Стили, перезаписываемые стилем Material-UI - PullRequest
1 голос
/ 12 февраля 2020

Предисловие

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

CodeSanbox Пример (обновлен для отражения принятого ответа)

Проблема:

Я бы хотел, чтобы любые внешние стили, передаваемые с помощью className, имели более высокую специфичность, чем мой собственный стиль внутренних компонентов. Таким образом, кто-то может использовать поля и отступы. Однако внутренний стиль по умолчанию для моих компонентов перезаписывает мой внешний стиль, и я бы хотел, чтобы он был наоборот.

You can see my external style is lower speceficity

Подробности :

Я создаю библиотеку пользовательских компонентов, созданную поверх материала-интерфейса. Я бы хотел, чтобы пользовательские компоненты API были похожи на @material-ui, чтобы нашим разработчикам было проще их использовать. У каждого компонента, который я создаю, есть свой собственный внутренний стиль, перезаписывающий стили материала-интерфейса по умолчанию, в этом случае он определяется как класс button. Кроме того, как @material-ui Я принимаю цветную опору <TestButton color={'default'}/>. Наконец, я бы хотел, чтобы мою пользовательскую кнопку можно было перезаписать внешними стилями, если когда-либо возникнет такая необходимость. Я использую библиотеку clsx для создания строк className.

Код:

import React, { useState } from "react";
import { makeStyles } from "@material-ui/styles";
import InputLabel from "@material-ui/core/InputLabel";
import MenuItem from "@material-ui/core/MenuItem";
import FormControl from "@material-ui/core/FormControl";
import { Button } from "@material-ui/core";
import clsx from "clsx";

const useAppStyles = makeStyles({
  gButton: { margin: "150px" }
});

export default function App() {
  const classes = useAppStyles();

  return (
    <div className={classes.example}>
      <div className={classes.separator}>
        <div>Buttons:</div>
        <TestButton
          className={classes.gButton}
          color={"default"}
        >
          Default
        </TestButton>
        <TestButton
          className={classes.gButton}
          color={"primary"}
        >
          Primary
        </TestButton>
    </div>
  );
}

function TestButton(props) {

  const classes = GrangeButtonStyles();
  let color = props.color === 'default' ? classes.default : classes.primary 

  const GrangeButtonStyles = makeStyles({
    button: {
     height: "45px",
     padding: "13px 30px 13px 30px",
     borderRadius: "5px",
     border: "none",
     margin: "15px",
    },
    default: {
     backgroundColor: "black",
     border: 'solid #2e7d32 1px',
     color: "white",
    },
    primary: {
     backgroundColor: 'white',
     color: 'black',
     fontFamily: 'Montserrat, sans-serif',
     border: 'solid black 1px',
    }
  });

  return (
    <Button
      className={clsx(classes.button, color, props.className)}
      variant="contained"
      disabled={props.disabled}
      disableElevation
    >
      {props.children}
    </Button>
  );
}

ПРИМЕЧАНИЕ:

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

Ответы [ 2 ]

1 голос
/ 12 февраля 2020

С https://developer.mozilla.org/en-US/docs/Web/CSS/Specificity:

Когда несколько объявлений имеют одинаковую специфичность, к элементу применяется последнее объявление, найденное в CSS.

Так что в вашем случае, когда вы определяете CSS классы в своем пользовательском компоненте (например, TestButton) и в коде, который использует этот компонент, специфичность определяется порядком, в котором эти классы CSS появляются внутри элемента <head>. Этот порядок определяется индексом , который устанавливается, когда makeStyles называется , поэтому классы, определенные последующими вызовами makeStyles, появятся позже в элементе <head> и, следовательно, будут иметь большую специфичность.

В вашем примере есть две проблемы:

  1. TestButton определяется после кода, который его использует, и, следовательно, после вызова makeStyles, определяющего стили, предназначенные для переопределения стилей. в TestButton. Поскольку вызов makeStyles для gButton происходит первым, соответствующий класс CSS будет первым в элементе <head>. Однако в реальных условиях TestButton (ваш пользовательский компонент) будет определен в отдельном файле и будет импортирован. Поскольку импорт должен быть наверху, любые makeStyles вызовы на верхнем уровне импортируемого файла будут выполняться до любых makeStyles вызовов в файле с использованием импортируемого компонента.

  2. Вызов makeStyles для TestButton - это , а не , выполняемый на верхнем уровне. Вместо этого это делается внутри функции TestButton, что означает, что она будет выполняться при визуализации TestButton вместо импорта TestButton. Вызовы makeStyles всегда должны быть на верхнем уровне, а не вложенными в функцию компонента. Еще одна незначительная проблема - это имя переменной, возвращаемой из makeStyles (т.е. GrangeButtonStyles в вашем примере). Так как makeStyles возвращает пользовательский хук , у вас всегда должно быть имя, начинающееся с «use» (например, useGrangeButtonStyles). Это гарантирует, что правила eslint для хуков распознают его как хук и предупреждают о любом неправильном использовании хуков.

Связанные ответы и ссылки:

0 голосов
/ 12 февраля 2020
<TestButton className={classes.gButton} color={"default"}>

// should be

<TestButton classes={{button:classes.gButton}} color={"default"}>

// and

<TestSelect className={classes.gSelect}

// should be

<TestSelect className={{dropdown:classes.gSelect}}

^ при работе со стилевым решением material-ui, не передавайте "className" компонентам (ставьте эту опору только на DOM-элементы !!!!)

и

function TestButton(props) {
  const classes = GrangeButtonStyles();
  ...

// should be


function TestButton(props) {
  const classes = GrangeButtonStyles(props);
  ...


^ Это приведет к объединению кнопки prop classes. (которая будет выглядеть как jss-1233) с классом кнопки, который выходит из GrangeButtonStyles, поэтому классы теперь будет выглядеть так:

{
  button: 'jss-7382 jss-1233' <- jss-1233 is the classname that got generated in the previous component
}

и

    <Button
      className={clsx(classes.button, color, props.className)}

// should be

    <Button
      classes={{root:classes.button)}

^ Смотрите документацию по материалам для кнопки

Это на самом деле к сожалению, Material-UI пересылает свои ссылки на элемент DOM без проверки на className , потому что это позволяет людям помещать className в компоненты Material-UI, и это «работает». Они должны действительно добавить предупреждения, чтобы использовать classes вместо. Спасибо за комментарий для исправления моей ошибки, мне все еще нравится многословие передачи classes вместо className, так как смешивание двух может привести к путанице!

РЕДАКТИРОВАТЬ:

Я также заметил, что вы путаете стили в TestSelect - всегда помните об этом - не передавайте className как реквизиты для компонентов Material-UI, только передавайте classes. Если вы хотите передать стили от родительского компонента к дочернему компоненту, вам нужно использовать тот же ключ:

Давайте попробуем пример, я знаю, этот материал сложно найти, но в конечном итоге он будет " нажмите ":

const useParentStyles = makeStyles({
   childStyles: { ... some jss }
   childStyles2: { ... some jss }
});

const Parent = props => {
   const classes = useParentStyles(props);
   return <Child classes={{root:classes.childStyles,someOtherKey:classes.childStyles2}}/> <- object with a keys of "root" and "someOtherKey"
}

const useChildStyles = makeStyles({
  root: { ... some jss } <- root
  someOtherKey: { ... some jss } <- someOtherKey 
});

const Child = props => {
   const classes = useChildStyles(props); <- classes have been merged together
   return <div className={classes.root}>...</div>
}
...