bem / bem-react

A set of tools for developing user interfaces using the BEM methodology in React
http://bem.github.io/bem-react
Other
440 stars 64 forks source link

@bem-react/classname: Support css-modules [RFC] #545

Open yarastqt opened 4 years ago

yarastqt commented 4 years ago

Пример использования

Подключаем хелпер и передаем в него название компонента а так же объект со стилями:

// src/components/Button/Button.js
import React from 'react'
import { createCn } from '@bem-react/classname'
import styles from './Button.module.css'

export const { merge, cn } = createCn('Button', styles)

export const Button = ({ children }) => {
  return (
    <button className={cn()}>
      <span className={cn('Text')}>{children}</span>
    </button>
  )
}

Далее на уровне проекта или эксперимента мы можем переопределить нужные нам стили:

// src/experiments/Button/Button.js
import { merge, cn, Button } from '../../components/Button/Button'
import styles from './Button.module.css'

merge(styles)

export { merge, cn, Button }

Пример реализации

Используем фабрику для генерации классов, в замыкании храним доопределения для компонента и его элементов (в данной реализации не поддержаны модификаторы и миксы):

export function createCn(component, styles) {
  const styleOverrides = []
  return {
    merge(overrides) {
      styleOverrides.push(overrides)
    },
    cn(element) {
      const target = element ? `${component}-${element}` : component
      const className = [styles]
        .concat(styleOverrides)
        .map(shape => shape[target])
        .join(' ')
      return className
    }
  };
}

В таком кейсе нам не нужно будет докидывать микс на элементы снаружи, т.к. вся логика будет инкапсулирована внутри merge функции.

yarastqt commented 4 years ago

Возможно стоит делать это как новую фабрику, а не патчить существующую, в таком случае можно будет не нести эту логику в бандл, если она не нужна, т.к. будет три-шейкинг.

Нужно ещё продумать различные кейсы использования, возможно что-то не учли.

yarastqt commented 4 years ago

После реализации можно будет постепенно компоненты переводить на css-модули.

yarastqt commented 4 years ago

Для createCn можно использовать тапл, чтобы для каждого компонента можно было назвать cn уникально.

export const [cn, merge] = createCn('Button', styles)
yarastqt commented 4 years ago

Так же нужно подумать про то, как это будет работать с платформами (desktop, touch) при SSR, не будет ли проблем с тем, что у нас общая память.

stenin-nikita commented 4 years ago

Глобально мержить может быть не совсем хорошая идея - стили могут начать применяться там, где не нужно. Я поэкспериментировал и получилось примерно следующее Из плюсов здесь то, что такой подход может позволить гибко переопределять библиотечные стили. А библиотеки компонентов можно будет писать вообще без стилей, а при использовании можно будет прокидывать стили с разными темами и т.д.

В данном случае, в @bem-react/classname достаточно будет реализовать фабрику, которая принимает на входе мапу с классами:

const Button: FC<ButtonProps> = (props) => {
  const { className, styles, size, disabled, ...other } = props;
  const cn = useMemo(() => createCn('Block', props.styles), [props.styles]);

  return <button className={cn({ styles, disabled }, [className])}  {...other} />
}