props-dependent Switch color in Material-UI - javascript

The following code, adapted from Material-UI docs for customizing Switch allows to set the switch color to blue:
import React from 'react'
import Switch from '#material-ui/core/Switch'
import {withStyles} from '#material-ui/core/styles'
const ColoredSwitch = withStyles({
switchBase: {
'&$checked': {
color: 'blue',
},
},
checked: {},
track: {},
})(Switch)
But when trying to adapt it so that the color can be set by component properties, it just doesn't work. Event the following code (which is only pseudo-dynamic) renders to a default switch:
const ColoredSwitch = withStyles({
switchBase: {
'&$checked': {
color: props => 'blue',
},
},
checked: {},
track: {},
})(Switch)
I guess I must be doing something wrong but can't figure out what.

Follow this example for passing props if you must use withStyles HOC: https://material-ui.com/styles/basics/#adapting-the-higher-order-component-api
const ColoredSwitch = withStyles({
switchBase: {
"&.Mui-checked": {
color: (props) => props.customchecked
}
},
checked: {},
track: {}
})((props) => {
const { classes, ...other } = props;
return <Switch classes={{ switchBase: classes.switchBase }} {...other} />;
});
You can also use makeStyles
const useStyles = makeStyles({
switchBaseChecked: {
"&.Mui-checked": {
color: (props) => props.color
}
}
});
export default function Switches() {
const props = { color: "green" };
const classes = useStyles(props);
return (
<Switch
color="primary"
classes={{
checked: classes.switchBaseChecked
}}
/>
);
}

Related

How to change prop of Material UIs Button component based on screen size breakpoint when using a class component

Im using Material UI's Button component and would like to conditionally determine the variant of the button. That is, on screens medium and up, the variant should be 'outlined' and when the screen is smaller than medium, the variant type should be none. I am using a class component. I have done my research to see what others have done. I have seen the method of using useMediaQuery method but that would require me to change the component to a functional component. Im not sure if I know if that is a good idea because the component is rather a large one and that means will be a bit confusing to convert. I also tried using a ternary operator like this:
const variantType = theme.breakpoints.down("md") ? '' : 'outline';
<Button variant={variantType}>
Food Pick
</Button>
But this didnt do the job. Is there any other way to accomplish this?
Update: Here is my code after trying to wrap the component but in a functional component but its giving an error:
import { withStyles, withTheme, useTheme } from '#material-ui/core';
import PropTypes from 'prop-types';
import Button from '#material-ui/core/Button';
import Typography from '#material-ui/core/Typography';
import Grid from '#material-ui/core/Grid';
import { PulseLoader } from 'react-spinners';
import Icon from '#material-ui/core/Icon';
import Chip from '#material-ui/core/Chip';
import useMediaQuery from '#material-ui/core/useMediaQuery';
const styles = theme => ({
beta: {
height: '17px',
fontSize: '12px',
marginLeft: '10px'
},
scrollContainer: {
flex: 1,
maxHeight: '400px',
minHeight: '300px',
overflowY: 'auto'
},
progressContainer: {
height: '350px',
},
modalDescription: {
color: theme.palette.text.secondary,
marginTop: '20px',
},
button: {
marginTop: '20px',
marginLeft: '10px',
marginRight: '10px',
},
smartSuggestContainer: {
textAlign: 'right',
paddingRight: '35px',
[theme.breakpoints.down('xs')]: {
display: 'flex',
justifyContent: 'center',
flexDirection: 'row',
margin: '40px 0'
},
},
});
export default function MyComponentWrapper({ ...rest }) {
const theme = useTheme();
const mediumScreen = useMediaQuery(theme.breakpoints.down('md'));
return <FoodDescriptions {...rest} mediumScreen={mediumScreen} />;
}
class FoodDescriptions extends Component {
static PAGE_SIZE = 25;
constructor(props) {
super(props);
this.state = {
showFoodDescriptionsModal: false,
dataLoaded: false,
data: [],
page: 0,
sortBy: 'return',
sortDir: 'desc',
};
}
componentDidMount() {
document.addEventListener('keydown', this.handleKeypress);
}
componentWillUnmount() {
document.removeEventListener('keydown', this.handleKeypress);
}
fetchData = async (page, sortBy, sortDir) => {
const { currentBotId } = this.props;
const offset = page * FoodDescriptions.PAGE_SIZE;
const data = await getFoodDescriptions(currentBotId, sortBy, sortDir, FoodDescriptions.PAGE_SIZE, offset);
this.setState({
dataLoaded: true,
data,
});
};
handleKeypress = (e) => {
const { showSnartConfigsModal } = this.state;
const { key } = e;
if (key === 'Escape' && showSnartConfigsModal) {
this.closeModal();
}
};
applyConfig = (botId, params) => {
const { updateConfig, botConfig, actions } = this.props;
updateConfig({ name: botConfig.name, config: Object.assign(botConfig.config, params) });
this.closeModal();
actions.showNotification({ data: 'Configuration has been applied' });
};
openModal = () => {
const { page, sortBy, sortDir } = this.state;
this.fetchData(page, sortBy, sortDir);
this.setState({
showFoodDescriptionsModal: true,
});
};
closeModal = () => {
this.setState({
showFoodDescriptionsModal: false,
});
};
changePage = (page) => {
const { sortBy, sortDir } = this.state;
this.setState({
page,
dataLoaded: false
}, () => this.fetchData(page, sortBy, sortDir));
};
changeSortBy = (sortBy) => {
/* eslint-disable-next-line prefer-destructuring */
let sortDir = this.state.sortDir;
if (sortBy === this.state.sortBy) {
sortDir = (this.state.sortDir === 'desc') ? 'asc' : 'desc';
}
this.setState({
sortBy,
sortDir,
dataLoaded: false,
}, () => this.fetchData(this.state.page, sortBy, sortDir));
};
renderEmptyState() {
const { classes } = this.props;
return (
<Grid container alignItems="center" justify="center" className={classes.progressContainer}>
<Typography className={classes.noCoinsText}>No configurations found</Typography>
</Grid>
);
}
renderLoader() {
const { classes } = this.props;
return (
<Grid container alignItems="center" justify="center" className={classes.progressContainer}>
<PulseLoader size={6} color="#52B0B0" loading />
</Grid>
);
}
renderTable() {
const { data, page } = this.state;
return (
<StrategiesTable
strategies={data}
onClickCopy={this.applyConfig}
page={page}
changePage={this.changePage}
sortBy={this.changeSortBy} />
);
}
render() {
const {
classes, userFeatures, botConfig, theme
} = this.props;
const { showFoodDescriptionsModal, dataLoaded } = this.state;
if (!botConfig) {
return null;
}
return (
<Fragment>
<div className={classes.smartSuggestContainer}>
<Button
name="discoverConfigs"
variant={theme.breakpoints.down(600) ? '' : 'outlined'}
color="primary"
size="small"
disabled={!userFeatures['smart_suggest_backtests'.toUpperCase()] || botConfig.status.toLowerCase() === STATUSES.RUNNING}
onClick={this.openModal}>
Food Buy
</Button>
</div>
</Fragment>
);
}
}
function mapStateToProps(state) {
return {
userFeatures: state.global.paywall.features,
};
}
function mapDispatchToProps(dispatcher) {
return {
actions: {
...bindActionCreators({
showNotification,
}, dispatcher)
}
};
}
connect(mapStateToProps, mapDispatchToProps)(withTheme()(withStyles(styles)(FoodDescriptions)));
Why don't you just create a functional component using useMediaQuery and returning the responsive Button ?

use withStyles with named export React Class Component

This component is not a default export component. I'm trying to set some styles for it but not sure how to wrap this in an HOC here. So right now, it doesn't know what classes is.
import { withStyles } from '#material-ui/core/styles';
const styles = (theme) => ({
root: {
flexGrow: 1,
},
control: {
padding: theme.spacing(2),
},
});
export class FeaturedCompanyGroup extends Component<{ formattedFeaturedCompanies: Array<JSX.Element> }> {
render() {
const { formattedFeaturedCompanies } = this.props;
const { classes } = this.props;
return (
<Grid alignItems="center" className={classes.root} container direction="row">
{ formattedFeaturedCompanies }
</Grid>
);
}
}
You have to do something like this:
import { withStyles } from '#material-ui/core/styles';
const styles = (theme) => ({
root: {
flexGrow: 1,
},
control: {
padding: theme.spacing(2),
},
});
class FeaturedCompanyGroup extends Component<{ formattedFeaturedCompanies: Array<JSX.Element> }> {
render() {
const { formattedFeaturedCompanies } = this.props;
const { classes } = this.props;
return (
<Grid alignItems="center" className={classes.root} container direction="row">
{ formattedFeaturedCompanies }
</Grid>
);
}
}
export const withFeaturedCompanyGroup = withStyles(styles)(FeaturedCompanyGroup);

React Native Prevent Context Dependency Rerender All Components

I have three buttons. When I click one button, it fires Context's memo function to add an item into Context's items. I see that clicking one button re-renders all the three buttons, which is not expected. I need to re-render only the clicked button. Please advice.
CounterContext.js
import { createContext } from "react";
export const CounterContext = createContext(null);
App.js
import React from "react";
import { Text, StyleSheet } from "react-native";
import MyButton from "./MyButton";
import { CounterContext } from "./CounterContext";
function counterReducer(prevState, action) {
switch (action.type) {
case "ADD_ITEM":
let items = [...prevState.items, action.item];
return {
items: items,
count: items.length
};
default:
console.log("No action type");
break;
}
}
const buttons = ["1", "2", "3"];
export default function App() {
const [counterState, counterDispatch] = React.useReducer(counterReducer, {
items: [],
count: 0
});
const counterContext = React.useMemo(
() => ({
addItem: item => {
counterDispatch({
type: "ADD_ITEM",
item: item
});
},
items: counterState.items,
itemsCount: counterState.items.length
}),
[counterState.items]
);
return (
<CounterContext.Provider value={counterContext}>
{buttons.map((button, index) => {
return <MyButton key={index} id={button} />;
})}
<Text style={styles.counter}>{counterContext.itemsCount}</Text>
<Text style={styles.items}>{JSON.stringify(counterContext.items)}</Text>
</CounterContext.Provider>
);
}
const styles = StyleSheet.create({
counter: {
margin: 10,
fontSize: 30
},
items: {
margin: 10,
fontSize: 18
}
});
MyButton.js
import React from "react";
import { TouchableOpacity, Text, StyleSheet } from "react-native";
import { CounterContext } from "./CounterContext";
export default function MyButton(props) {
const { addItem } = React.useContext(CounterContext);
let time = new Date().toLocaleTimeString();
console.log(time + " - Rendering MyButton #" + props.id + " ...");
return (
<TouchableOpacity
style={styles.myButton}
onPress={() => {
addItem({
name: "Item #" + props.id
});
}}
>
<Text>Add</Text>
</TouchableOpacity>
);
}
const styles = StyleSheet.create({
myButton: {
width: 100,
margin: 10,
padding: 10,
textAlign: "center",
borderWidth: 1,
borderColor: "#ccc",
backgroundColor: "#e0e0e0",
borderRadius: 5
}
});
Here is the Codesandbox codes: https://codesandbox.io/s/usecontext-dependency-fpcep?file=/src/App.js
Turns out that I have to split Context into two, one that doesn’t have dependency, only to run the functions, and one that shows any value(s) real-time by adding the dependency. The same with the consumer component(s), take it out as new component and put the useContext in there, so parent component won’t be re-rendered.

CSS in JS, how to make props.className the priority class

We need components where the class passed as props should have more priority than the default class.
When passing classes as a prop, the component gives priority to the
class created in his own file.
Text.jsx
// this will be a component in another folder, it will be used in the whole app so it
// should haveDYNAMIC styling
function Text(props) {
const useStyles = makeStyles(theme => ({
default: {
fontSize: 18,
color: "black"
}
}));
const classes = useStyles();
return (
<div className={classNames(classes.default, props.className)}>
{props.children}
</div>
);
}
App.jsx
function App() {
const useStyles = makeStyles(theme => ({
title: {
fontSize: 80,
color: "red"
},
notGoodPractice: {
fontSize: "80px !important"
}
}));
const classes = useStyles();
return (
<div className="App">
<Text className={classes.title}>Text in here is 18px</Text>
<Text className={classes.notGoodPractice}>
Text in here is 80px
</Text>
</div>
);
}
React Snippet => CodeSandBox
You can prioritise classes passed as props this way.
Just make sure you don't apply makeStyles on it so that you can access them correctly in child.
import React from "react";
import ReactDOM from "react-dom";
import { makeStyles } from "#material-ui/core/styles";
// this is how we will use the Text component
function App() {
const style = {
title: {
fontSize: "80px",
color: "red",
"#media (max-width: 767px)": {
color: "green"
}
},
notGoodPractice: {
fontSize: "80px"
}
};
return (
<div className="App">
<Text class1={style.title}>Title should be with size 80px</Text>
<Text class1={style.notGoodPractice}>Title should be with size 80px</Text>
</div>
);
}
// this will be a component in another folder, it will be used throw the in app so it should be as DYNAMIC as possible
function Text(props) {
const useStyles = makeStyles(theme => ({
default1: {
fontSize: 18,
color: "black"
},
priorityClass: props => props.class1
}));
const { default1, priorityClass } = useStyles(props);
return <div className={`${default1} ${priorityClass}`}>{props.children}</div>;
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
Check out live sandbox https://codesandbox.io/s/zen-voice-mb60t

Weird antialias artefacting with react native on nested posed components

I am creating a component that animates a group of buttons in an elastic staggered way using react-native-pose. The buttons them selves use pose to animate the pressed state. I have almost got it looking how I want it although I'm not sure I'm doing things correctly.
This is what I want to achhieve...
... However the text looks awful like its got jpg artefacts on it :
App.js
import React, { Component } from 'react';
import styled from 'styled-components';
import posed from 'react-native-pose';
import Button from './src/components/Button';
const ScreenContainer = styled.View({
flex: 1,
padding: 20,
marginTop: 100
});
const Buttons = posed.View({
visible: {
staggerChildren: 100
},
hidden: {
staggerChildren: 100
}
});
export default class App extends Component {
state = {
buttonPose: 'hidden'
};
items = [
{ id: 0, label: 'One' },
{ id: 1, label: 'Two' },
{ id: 2, label: 'Three' }
];
componentDidMount = () => {
this.setState({
buttonPose: 'visible'
});
};
render() {
return (
<ScreenContainer>
<Buttons pose={this.state.buttonPose}>
{this.items.map(item => (
<Button label={item.label} key={item.id} />
))}
</Buttons>
</ScreenContainer>
);
}
}
Button.js
import React, { PureComponent } from 'react';
import { TouchableWithoutFeedback } from 'react-native';
import styled from 'styled-components';
import posed from 'react-native-pose';
const Container = styled(
posed.View({
visible: {
opacity: 1,
x: 0
},
hidden: {
opacity: 0,
x: -100
}
})
)({
marginBottom: 20
});
const Background = styled(
posed.View({
// If I comment out these poses the problem goes away
pressIn: {
scale: 1.1
},
pressOut: {
scale: 1
}
})
)({
padding: 20,
backgroundColor: '#f9415d',
borderRadius: 10
});
const Label = styled.Text({
fontSize: 18,
color: 'white',
textAlign: 'center'
});
export default class Button extends PureComponent {
state = {
buttonPose: 'pressOut'
};
onPressIn = () => {
this.setState({
buttonPose: 'pressIn'
});
};
onPressOut = () => {
this.setState({
buttonPose: 'pressOut'
});
};
componentDidMount = () => {};
render() {
const { onPressIn, onPressOut } = this;
const { buttonPose } = this.state;
const { label } = this.props;
return (
<Container>
<TouchableWithoutFeedback onPressIn={onPressIn} onPressOut={onPressOut}>
<Background pose={buttonPose} withParent={false}>
<Label>{label}</Label>
</Background>
</TouchableWithoutFeedback>
</Container>
);
}
}
Can anyone offer any insight into why the text and also the rounded corners look so artifacted and low res?

Categories

Resources