iT邦幫忙

2021 iThome 鐵人賽

DAY 28
0
自我挑戰組

Material UI in React系列 第 28

Material UI in React [ Day 28 ] Customization Component 自訂組件 (part1)

由於組件可以在不同的context中使用,有幾種方法可以解決這個問題,官方連結

  • 1.一次性情況的特定變化
  • 2.一次性情況的動態變化
  • 3.在不同context中重用的組件的特定變體
  • 4.Material Design 變體,例如按鈕組件
  • 5.Global theme

1. 一次性情況的特定變化

你可能需要為特定實現更改組件的樣式,為此可以使用以下解決方案:

用 className 覆蓋樣式

每個組件都提供一個 className 屬性,該屬性始終應用於根元素。

範例使用 withStyles() 將自定義樣式註入 DOM,並通過其 classes 屬性將類名傳遞給 ClassNames 組件。這裡可以選擇任何其他解決方案

import React from 'react';
import PropTypes from 'prop-types';
import clsx from 'clsx';
import Button from '@material-ui/core/Button';
import { withStyles } from '@material-ui/core/styles';

const styles = {
  root: {
    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)',
  },
};

function ClassNames(props) {
  const { classes, children, className, ...other } = props;

  return (
    <Button className={clsx(classes.root, className)} {...other}>
      {children || 'class names'}
    </Button>
  );
}

ClassNames.propTypes = {
  children: PropTypes.node,
  classes: PropTypes.object.isRequired,
  className: PropTypes.string,
};

export default withStyles(styles)(ClassNames);

關於Clsx,他可以減少我們用三元運算的方式撰寫 className。
本來的寫法:

// in return
<div
  className={`
    MuiButton-root
    ${disabled ? 'Mui-disabled' : ''}
    ${selected ? 'Mui-selected' : ''}
  `}
/>

引入之後:

<div
  className={clsx('MuiButton-root', {
    'Mui-disabled': disabled,
    'Mui-selected': selected,
  })}
/>

用 classes 覆蓋樣式

當 className 屬性不夠用時,並且需要訪問更深層次的元素,可以利用 classes 對象屬性來自定義 Material-UI 為給定組件注入的所有 CSS。

每個組件的classes列表記錄在組件 API 頁面中,可以透過瀏覽官方文件的 component API 或者直接透過 dev tools 查看,這個例子也使用了 withStyles()(如上文),但在這裡,ClassesNesting 使用 Button 的 classes 屬性來提供一個對象,該對象將要覆蓋的 classes 名稱(樣式規則)映射到要應用的 CSS class names(值)。組件的現有classes將繼續注入,因此只需提供想要添加或覆蓋的特定樣式。
請注意,除了按鈕樣式之外,按鈕標籤的大小寫也已更改:

const useStyles = makeStyles({
  root: {
    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)',
  },
  label: {
    textTransform: 'capitalize',
  },
});
// ...in export function
const classes = useStyles();
// ...in return
<Button
  classes={{
    root: classes.root, // class name, e.g. `classes-nesting-root-x`
    label: classes.label, // class name, e.g. `classes-nesting-label-x`
  }}
>
  classes nesting
</Button>

使用global class name覆蓋樣式

參考連結

透過 dev tools

瀏覽器開發工具可以節省大量時間。
Material-UI 的class names 遵循模式:
Mui[component name]-[style rule name]-[UUID]
使用開發工具,你知道你需要定位 Button 組件和標籤樣式規則

<Button classes={{ label: 'my-class-name' }} />

Shorthand

上面的範例可以通過使用與子組件相同的 CSS API 來壓縮。在這個例子中,withStyles() HOC 正在注入一個由 Button 組件使用的 classes 屬性。

const StyledButton = withStyles({
  root: {
    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)',
  },
  label: {
    textTransform: 'capitalize',
  },
})(Button);
// in retutn
<StyledButton>classes shorthand</StyledButton>

Pseudo-classes 偽類

組件的特殊狀態,例如 hover, focus, disabled 或 selected,特異性是應用於給定 CSS 聲明的權重。
為了覆蓋組件的特殊狀態,你需要增加特異性。

.Button {
  color: black;
}
.Button:disabled { /* Increase the specificity */
  color: white;
}
// in return
<Button disabled className="Button">

有時後不能使用偽類,因為平台中不存在狀態。以MenuItem 組件和選中狀態為例,除了訪問嵌套元素外,classes 屬性還可用於自定義 Material-UI 組件的特殊狀態:

.MenuItem {
  color: black;
}
.MenuItem.selected { /* Increase the specificity */
  color: blue;
}
// in return
<MenuItem selected classes={{ root: 'MenuItem', selected: 'selected' }}>

使用 $ruleName 引用同一樣式表中的規則

jss-nested 插件可以使增加特異性的過程更容易。

const styles = {
  root: {
    '&$disabled': {
      color: 'white',
    },
  },
  disabled: {},
};

需要將生成的兩個類名(root 和 disabled)應用於 DOM 以使其工作。

// in return
<Button
  disabled
  classes={{
    root: classes.root, // class name, e.g. `root-x`
    disabled: classes.disabled, // class name, e.g. `disabled-x`
  }}
>
  classes state
</Button>

以上就是今天的全部內容了,明天會接續 2~5 的方法講解。


上一篇
Material UI in React [ Day 27 ] Styles API (part 2)
下一篇
Material UI in React [ Day 29 ] Customization Component 自訂組件 (part2)
系列文
Material UI in React30

尚未有邦友留言

立即登入留言