Создание пользовательских вариантов с помощью Fluent UI React v9

Бывают случаи, когда вы хотите создать вариант UI-компонента для обеспечения согласованного UX в продукте или команде инженеров. Довольно часто это происходит путем переопределения поведения по умолчанию компонентов пользовательского интерфейса, доступных в соответствующей библиотеке пользовательского интерфейса, чтобы не изобретать велосипед.

С Fluent UI React этот сценарий ничем не отличается. Мы разработали компоненты пользовательского интерфейса так, чтобы они позволяли переопределять стиль. Следующие примеры покажут вам различные способы использования CSS-in-JS для переопределения стандартного стиля UI-компонентов и создания пользовательских вариантов для вашего приложения.

Сценарий варианта: Кнопка запуска

Итак, для этого примера мы создадим «Кнопку запуска», которая будет иметь определенный набор стилей, отменяющий стандартный стиль Кнопки, предоставляемый Fluent UI React. Мы хотим сохранить всю встроенную функциональность Button и переопределить следующее:

  1. Всегда менять форму на круглую
  2. Всегда иметь значок ракеты
  3. Всегда менять цвет фона в состояниях покоя, наведения и нажатия
  4. Всегда иметь большой размер

Вот пример того, как мы хотим, чтобы выглядела «Кнопка запуска»:

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

В следующих разделах мы рассмотрим различные способы переопределения компонента Fluent UI React Button и способы его структурирования для совместного использования в рамках проекта.

Вариант 1: Создание переопределений стилей CSS и реквизитов компонента

Первым вариантом создания варианта будет создание переопределений стилей CSS-in-JS и настройка реквизитов компонента Button. Давайте рассмотрим определение переопределения стилей CSS-in-JS с помощью Griffle — движка CSS-in-JS по умолчанию в Fluent UI React:

const useLaunchButtonStyles = makeStyles({
  launchButton: {
    backgroundColor: "#ce000a",
    ...shorthands.borderColor("transparent"),
    color: tokens.colorBrandBackgroundInverted,
    ":hover": {
      backgroundColor: "#b10007",
      ...shorthands.borderColor("transparent"),
      color: tokens.colorBrandBackgroundInverted
    },
    ":hover:active": {
      backgroundColor: "#5d0b00",
      ...shorthands.borderColor("transparent"),
      color: tokens.colorNeutralForegroundOnBrand
    }
  }
});
Вход в полноэкранный режим Выход из полноэкранного режима

Мы создали определения стилей CSS-in-JS, которые переопределяют атрибуты backgound-color, border-color и color стиля Button. Мы также сделали это для состояния покоя, наведения и нажатия кнопки.

Далее мы воспользуемся созданным нами хуком стилей и применим класс к компоненту Button через реквизит className:

const l = useLaunchButtonStyles();

<Button className={l.launchButton}>Launch</Button>
Вход в полноэкранный режим Выйти из полноэкранного режима

Теперь, это только переопределяет цвет компонента Button, что означает, что нам нужно добавить значок ракеты и изменить форму и размер Button:

const l = useLaunchButtonStyles();

<Button className={l.launchButton}
  icon={<RocketRegular />}
  shape="circular"
  size="large">
  Launch
</Button>
Вход в полноэкранный режим Выход из полноэкранного режима

Итак, теперь у нас есть «Кнопка запуска». Недостатком является то, что мы должны сообщить любому потребителю, что ему необходимо настроить компонент Button с классом CSS, значком и формой. Это означает, что потенциальные потребители могут либо изменить дизайн, либо неправильно сконфигурировать компонент для обеспечения согласованного UX.

Давайте посмотрим, как мы можем решить эту проблему с помощью следующего варианта!

Вариант 2: Компонент-обертка с переопределением стиля и конфигурацией реквизитов

Для этого подхода мы сделаем то же самое, что и в варианте 1 выше, но обернем это в React-компонент.

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

Мы снова проделаем те же шаги, что и в варианте 1, но обернем это в функциональный компонент React:

const useLaunchButtonStyles = makeStyles({
  launchButton: {
    backgroundColor: "#ce000a",
    ...shorthands.borderColor("transparent"),
    color: tokens.colorBrandBackgroundInverted,
    ":hover": {
      backgroundColor: "#b10007",
      ...shorthands.borderColor("transparent"),
      color: tokens.colorBrandBackgroundInverted
    },
    ":hover:active": {
      backgroundColor: "#5d0b00",
      ...shorthands.borderColor("transparent"),
      color: tokens.colorNeutralForegroundOnBrand
    }
  }
});

const LaunchButton: React.FC<LaunchButtonProps> = () => {
  const l = useLaunchButtonStyles();
  return (
    <Button
      icon={<RocketRegular />}
      className={classes}
      shape="circular"
      size="large"
    >
      Launch
    </Button>
  );
};

export default LaunchButton;
Вход в полноэкранный режим Выход из полноэкранного режима

Теперь, когда потребитель импортирует LaunchButton, он получает всю последовательность действий, завернутую в функциональный компонент:

<LaunchButton />
Вход в полноэкранный режим Выход из полноэкранного режима

Это отлично работает для обеспечения согласованного UX во всем продукте, но мы, по сути, лишили потребителей возможности дальнейшей настройки. Если это было вашим намерением — вы закончили. Ура! 🎉

Но давайте представим, что кто-то из вашей команды говорит: «Команда дизайнеров хочет, чтобы у этой функции была зеленая «кнопка запуска» в этой конкретной функции. Как мы можем это сделать?»

Ну, вы можете создать еще один компонент-обертку и назвать его LaunchButtonGreen. Однако, представляя себе все больше и больше цветовых запросов в будущем, вы понимаете, что это не масштабируемо. Поэтому давайте воспользуемся существующими возможностями стилизации в компонентах Fluent UI React и перенесем их в ваш вариант.

В следующем варианте мы рассмотрим возможность переопределения стиля CSS-in-JS, чтобы позволить потребителям контролировать цвет LaunchButton, но сохранить все остальное фиксированным.

Вариант 3: Компонент-обертка с переопределениями и разрешением переопределения стилей для потребителей

Теперь мы продолжим работу над вариантом 2, но позволим потребителям LaunchButton передавать переопределения стиля через свойство className (точно так же, как мы сделали это с Fluent UI React Button).

Интересным требованием, о котором нам нужно подумать, является порядок применения переопределений стилей. Мы воспользуемся API mergeClasses в Griffel, который объединяет и дедуплицирует атомарные классы, созданные API makeStyles.

А поскольку порядок важен для mergeClasses, мы сделаем так, чтобы потребительские стили, передаваемые через реквизит className, всегда выигрывали у стилей по умолчанию LaunchButton:

const useLaunchButtonStyles = makeStyles({
  launchButton: {
    backgroundColor: "#ce000a",
    ...shorthands.borderColor("transparent"),
    color: tokens.colorBrandBackgroundInverted,
    ":hover": {
      backgroundColor: "#b10007",
      ...shorthands.borderColor("transparent"),
      color: tokens.colorBrandBackgroundInverted
    },
    ":hover:active": {
      backgroundColor: "#5d0b00",
      ...shorthands.borderColor("transparent"),
      color: tokens.colorNeutralForegroundOnBrand
    }
  }
});

export type LaunchButtonProps = Pick<
  React.HTMLAttributes<HTMLElement>,
  "className"
>;

const LaunchButton: React.FC<LaunchButtonProps> = ({ className }) => {
  const l = useLaunchButtonStyles();
  const classes = mergeClasses(l.launchButton, className);

  return (
    <Button
      icon={<RocketRegular />}
      className={classes}
      shape="circular"
      size="large"
    >
      Launch
    </Button>
  );
};

export default LaunchButton;
Вход в полноэкранный режим Выход из полноэкранного режима

Вот пример использования потребителем, переопределяющим стили LaunchButton:

const useLaunchButtonOverrides = makeStyles({
  overrides: {
    backgroundColor: "green"
  }
});

const o = useLaunchButtonOverrides();

<LaunchButton className={o.overrides} />

Вход в полноэкранный режим Выход из полноэкранного режима

Теперь ваш вариант имеет такой же опыт разработчика, как и любой другой компонент Fluent UI React — прекрасная вещь! Это также дает потребителю хороший баланс между настройкой и контролем над качествами UX, которые должны быть согласованными и последовательными.

Хотите узнать больше? Посмотрите CodesandBox для полного образца:

Дайте нам знать, какой контент вы хотели бы увидеть от Fluent UI React, посетив нас на:

GitHub: https://github.com/microsoft/fluentui
Docs: https://react.fluentui.dev
Twitter: https://twitter.com/fluentui

Спасибо!

Оцените статью
devanswers.ru
Добавить комментарий