...接續前一篇
在上一篇中講解如何覆蓋 Material-UI 組件的樣式,現在,讓我們看看如何使這些覆蓋動態化。這裡有五種選擇;每個都有其優點和缺點。
這個方式是透過 withStyles() 的方式更改樣式,直接透過 styles 內的參數做更改。
// Like https://github.com/brunobertolini/styled-by
const styledBy = (property, mapping) => (props) => mapping[props[property]];
const styles = {
root: {
background: styledBy('color', {
default: 'linear-gradient(45deg, #FE6B8B 30%, #FF8E53 90%)',
blue: 'linear-gradient(45deg, #2196F3 30%, #21CBF3 90%)',
}),
borderRadius: 3,
border: 0,
color: 'white',
height: 48,
padding: '0 30px',
boxShadow: styledBy('color', {
default: '0 3px 5px 2px rgba(255, 105, 135, .3)',
blue: '0 3px 5px 2px rgba(33, 203, 243, .3)',
}),
},
};
const StyledButton = withStyles(styles)(({ classes, color, ...other }) => (
<Button className={classes.root} {...other} />
));
export default function DynamicCSS() {
const [color, setColor] = React.useState('default');
const handleChange = (event) => {
setColor(event.target.checked ? 'blue' : 'default');
};
return (
<React.Fragment>
<FormControlLabel
control={
<Switch
checked={color === 'blue'}
onChange={handleChange}
color="primary"
value="dynamic-class-name"
/>
}
label="Blue"
/>
<StyledButton color={color}>Dynamic CSS</StyledButton>
</React.Fragment>
);
}
這個方法就是常用的 makeStyles() 的做法。
const useStyles = makeStyles({
button: {
background: 'linear-gradient(45deg, #FE6B8B 30%, #FF8E53 90%)',
borderRadius: 3,
border: 0,
color: 'white',
height: 48,
padding: '0 30px',
boxShadow: '0 3px 5px 2px rgba(255, 105, 135, .3)',
},
buttonBlue: {
background: 'linear-gradient(45deg, #2196F3 30%, #21CBF3 90%)',
boxShadow: '0 3px 5px 2px rgba(33, 203, 243, .3)',
},
});
export default function DynamicClassName() {
const classes = useStyles();
const [color, setColor] = React.useState('default');
const handleChange = (event) => {
setColor(event.target.checked ? 'blue' : 'default');
};
return (
<React.Fragment>
<FormControlLabel
control={
<Switch
checked={color === 'blue'}
onChange={handleChange}
color="primary"
value="dynamic-class-name"
/>
}
label="Blue"
/>
<Button
className={clsx(classes.button, {
[classes.buttonBlue]: color === 'blue',
})}
>
{'Class name branch'}
</Button>
</React.Fragment>
);
}
者個方法跟上面差不多,但是是以 CSS 變數作為更改的值。
const useStyles = makeStyles({
button: {
background: 'linear-gradient(45deg, var(--background-start) 30%, var(--background-end) 90%)',
borderRadius: 3,
border: 0,
color: 'white',
height: 48,
padding: '0 30px',
boxShadow: '0 3px 5px 2px var(--box-shadow)',
},
});
const blue = {
'--background-start': '#2196F3',
'--background-end': '#21CBF3',
'--box-shadow': 'rgba(33, 203, 243, .3)',
};
const defaultColor = {
'--background-start': '#FE6B8B',
'--background-end': '#FF8E53',
'--box-shadow': 'rgba(255, 105, 135, .3)',
};
export default function DynamicCSSVariables() {
const classes = useStyles();
const [color, setColor] = React.useState(defaultColor);
// 和上面不同的是以變數更改而不是使用className
const handleChange = (event) => {
setColor(event.target.checked ? blue : defaultColor);
};
return (
<React.Fragment>
<FormControlLabel
control={
<Switch
checked={color === blue}
onChange={handleChange}
color="primary"
value="dynamic-class-name"
/>
}
label="Blue"
/>
<Button className={classes.button} style={color}>
{'CSS variables'}
</Button>
</React.Fragment>
);
}
這個方法會直接使用 inline-styles 去更改。
const styles = {
button: {
background: 'linear-gradient(45deg, #FE6B8B 30%, #FF8E53 90%)',
borderRadius: 3,
border: 0,
color: 'white',
height: 48,
padding: '0 30px',
boxShadow: '0 3px 5px 2px rgba(255, 105, 135, .3)',
},
buttonBlue: {
background: 'linear-gradient(45deg, #2196f3 30%, #21cbf3 90%)',
boxShadow: '0 3px 5px 2px rgba(33, 203, 243, .30)',
},
};
export default function DynamicInlineStyle() {
const [color, setColor] = React.useState('default');
const handleChange = (event) => {
setColor(event.target.checked ? 'blue' : 'default');
};
return (
<React.Fragment>
<FormControlLabel
control={
<Switch
checked={color === 'blue'}
onChange={handleChange}
color="primary"
value="dynamic-class-name"
/>
}
label="Blue"
/>
<Button
style={{
...styles.button,
...(color === 'blue' ? styles.buttonBlue : {}),
}}
>
{'dynamic inline-style'}
</Button>
</React.Fragment>
);
}
這個做法之前有講過了,透過不同的 theme provider 來更改。
export default function DynamicThemeNesting() {
const [color, setColor] = React.useState('default');
const handleChange = (event) => {
setColor(event.target.checked ? 'blue' : 'default');
};
const theme = React.useMemo(() => {
if (color === 'blue') {
return createTheme({
palette: {
secondary: {
main: blue[500],
contrastText: '#fff',
},
},
});
}
return createTheme();
}, [color]);
return (
<React.Fragment>
<FormControlLabel
control={
<Switch
checked={color === 'blue'}
onChange={handleChange}
color="primary"
value="dynamic-class-name"
/>
}
label="Blue"
/>
<ThemeProvider theme={theme}>
<Button variant="contained" color="secondary">
{'Theme nesting'}
</Button>
</ThemeProvider>
</React.Fragment>
);
}
你可能需要創建組件的變體並在不同的 context 中使用它,例如產品頁面上的彩色按鈕,但你可能希望保持代碼單純。
最好的方法是遵循方法 1,然後通過導出自定義組件以在任何需要的地方使用,從而利用 React 的組合能力。
Material Design 規範記錄了某些組件的不同變體,例如按鈕如何具有不同的形狀:text(以前的"flat")、contained的(以前的"raised")、FAB 等等。
詳細支援組件。
為了促進組件之間的一致性,並從整體上管理用戶界面外觀,Material-UI 提供了一種應用全局更改的機制。
const GlobalCss = withStyles({
// @global is handled by jss-plugin-global.
'@global': {
// 要指向 [class*="MuiButton-root"] 而不是 nest themes.
'.MuiButton-root': {
fontSize: '1rem',
},
},
})(() => null);
export default function GlobalCssOverride() {
return (
<React.Fragment>
<GlobalCss />
<Button>font-size: 1rem</Button>
</React.Fragment>
);
}
直接立一個新的 theme 去做覆蓋的動作,利用 theme 的 overrides 鍵來更改 Material-UI 注入到 DOM 中的每個樣式。
const theme = createTheme({
overrides: {
MuiButton: {
root: {
fontSize: '1rem',
},
},
},
});
export default function GlobalThemeOverride() {
return (
<ThemeProvider theme={theme}>
<Button>font-size: 1rem</Button>
</ThemeProvider>
);
}
以上就是今天的全部內容了,有在 follow 官方網站的朋友應該有發現最近已經發布 ver. 5 的版本了,我現在介紹和使用的是 ver.4,有機會會再出一個新版本更新的內容。