Как создать компонент списка с помощью Emotion

Как создать компонент списка с помощью Emotion

Sentry и заметил, что в нем нет общего компонента списка, который можно было бы использовать в проектах и ​​возможностях. Итак, я начал создавать его, но вот загвоздка: мы стилизуем вещи в Sentry при помощи Emotion, с которым у меня лишь мимолетный опыт и который описан в документации следующим образом:

[…] Библиотека, предназначенная для написания стилей CSS при помощи JavaScript. Она обеспечивает мощную и предсказуемую композицию стилей в дополнение к отличному опыту разработки с такими возможностями, как исходные карты, метки и утилиты для тестирования. Поддерживаются как строковые, так и объектные стили.

Если вы никогда не слышали об Emotion, общая идея такова: когда мы работаем над большими базами кода с большим числом компонентов, мы хотим убедиться, что можем контролировать каскад CSS. Итак, допустим, что у вас в одном файле есть класс .active, и вы планируете убедиться, что он не влияет на стили в полном объеме отдельного компонента, прописанного в ином файле, который также имеет в себя класс .active.

Emotion решает эту проблему, добавляя настраиваемые строки в имена классов, чтобы они не конфликтовали с иными компонентами. Ниже приведен пример HTML-код, который он может вывести.

<div class="css-1tfy8g7-List e13k4qzl9"></div>

Довольно аккуратно, да? Есть много иных инструментов и рабочих процессов, которые делают нечто похожее, к примеру, CSS-модули.

Шаг 1

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

import React from 'react';import styled from '@emotion/styled';export const List = styled('ul')` list-style: none; padding: 0;`;

Мне это кажется довольно странным, так как мы не только пишем стили для элемента <ul>, но и определяем, что компонент также должен выводить <ul>. Комбинировать разметку и стили в одном месте кажется странным, но мне нравится, насколько это просто. Это противоречит только моей ментальной модели и разделению задач между HTML, CSS и JavaScript.

В ином компоненте мы можем импортировать <List> и использовать его следующим образом:

import List from 'components/list';<List>This is a list item.</List>

Стили, которые мы добавили в компонент списка, далее будут преобразованы в имя класса, к примеру, .oefioaueg, а далее добавлены к элементу <ul>, который мы определили в компоненте.

Шаг 2

Но мы ещё не закончили! Надо также иметь функция визуализировать <ul> и <ol> с одним и тем же компонентом. Необходима также версия, которая позволяла бы помещать значок в каждый элемент списка. А:

Как создать компонент списка при помощи EmotionКрутая(а также странная) функция в Emotion заключается в том, что мы можем использовать атрибут as, чтобы выбрать, какой HTML-элемент надо отобразить при импорте компонента. Мы можем использовать этот атрибут для создания <ol> без необходимости создавать пользовательское свойство type или что-то в этом роде. Все это выглядит примерно так:

<List>This will render a ul.</List><List as="ol">This will render an ol.</List>

Это странно, правда? Однако это удобно, так как это означает, что нам не необходимо выполнять какую-либо причудливую логику в самом компоненте только для того, чтобы настроить разметку.

Именно в этот момент я начал записывать то, как может выглядеть идеальный API для этого компонента. Вот что я себе представлял:

<List> <ListItem>Item 1</ListItem> <ListItem>Item 2</ListItem> <ListItem>Item 3</ListItem></List><List> <ListItem icon={<IconBusiness color="orange400" size="sm" />}>Item 1</ListItem> <ListItem icon={<IconBusiness color="orange400" size="sm" />}>Item 2</ListItem> <ListItem icon={<IconBusiness color="orange400" size="sm" />}>Item 3</ListItem></List><List as="ol"> <ListItem>Item 1</ListItem> <ListItem>Item 2</ListItem> <ListItem>Item 3</ListItem></List>

Шаг 3

Итак, после создания этого наброска я знал, что нам понадобится два компонента, а также функция вкладывать подкомпоненты значков в <ListItem>. Начать можно так:

import React from 'react';import styled from '@emotion/styled';export const List = styled('ul')` list-style: none; padding: 0; margin-bottom: 20px; ol& { counter-reset: numberedList; }`;

Этот своеобразный синтаксис ol& — это то, как мы сообщаем Emotion, что данные стили применяются только к элементу, когда он выводится как <ol>. Часто бывает хорошей идеей просто добавить к данному элементу background: red;, чтобы убедиться, что компонент выводится правильно.

Шаг 4

Далее идет подкомпонент <ListItem>. Важно отметить, что в Sentry также используется TypeScript, так что, прежде чем мы определим компонент <ListItem>, надо изменить свойства:

type ListItemProps = { icon?: React.ReactNode; children?: string | React.ReactNode; className?: string;};

Сейчас мы можем добавить компонент <IconWrapper>, который будет определять размер компонента <Icon> в ListItem. Как вы помните из приведенного выше примера, я хотел, чтобы он выглядел примерно следующим образом:

<List> <ListItem icon={<IconBusiness color="orange400" size="sm" />}>Item 1</ListItem> <ListItem icon={<IconBusiness color="orange400" size="sm" />}>Item 2</ListItem> <ListItem icon={<IconBusiness color="orange400" size="sm" />}>Item 3</ListItem></List>

Этот компонент IconBusiness будет существующим, и мы хотим обернуть его в span, чтобы стилизовать. К счастью, нам понадобится совсем CSS, чтобы правильно выровнять значок с текстом, и все это <IconWrapper> может сделать за нас:

type ListItemProps = {  icon?: React.ReactNode;  children?: string | React.ReactNode;  className?: string;};const IconWrapper = styled('span')`  display: flex;  margin-right: 15px;  height: 16px;  align-items: center;`;

Шаг 5

Когда это сделано, мы наконец-то можем добавить компонент <ListItem> под данными двумя, хотя он значительно сложнее. Нам необходимо будет добавить свойства, далее мы сможем отобразить упомянутый выше <IconWrapper>, когда свойство icon существует, и отобразить компонент иконки, который также передан в него. Я также добавил все приведенные ниже стили, чтобы вы могли видеть, как я стилизую каждый из данных вариантов:

export const ListItem = styled(({icon, className, children}: ListItemProps) =>(  <li className={className}>    {icon &&(      <IconWrapper>        {icon}      </IconWrapper>)}    {children}  </li>))<ListItemProps>`  display: flex;  align-items: center;  position: relative;  padding-left: 34px;  margin-bottom: 20px;  /* Позиционирование маленького кружка и значка */  &:before,& > ${IconWrapper} {    position: absolute;    left: 0;  }  ul & {    color: #aaa;    /* Этот псевдо-элемент – это маленький кружок для элементов ul */     &:before {      content: '';      width: 6px;      height: 6px;      border-radius: 50%;      margin-right: 15px;      border: 1px solid #aaa;      background-color: transparent;      left: 5px;      top: 10px;    }    /* Icon styles */    ${p =>      p.icon &&      `      span {        top: 4px;      }      /* Удаляем маленький кружок, если присутствует значок */      &:before {        content: none;      }    `}  }  /* Когда список выводится, как <ol> */  ol & {    &:before {      counter-increment: numberedList;      content: counter(numberedList);      top: 3px;      display: flex;      align-items: center;      justify-content: center;      text-align: center;      width: 18px;      height: 18px;      font-size: 10px;      font-weight: 600;      border: 1px solid #aaa;      border-radius: 50%;      background-color: transparent;      margin-right: 20px;    }  }`;

И вот оно! Относительно простой компонент <List>, созданный при помощи Emotion. Хотя после выполнения этого упражнения я все ещё не уверен, что мне нравится синтаксис. Я считаю, что это делает простые вещи действительно простыми, но компоненты среднего размера гораздо сложнее, чем они должны быть. К тому же, это может быть чертовски запутанным для новичка, и меня это беспокоит.

Но я полагаю, что все это требует изучения. В любом случае, я рад, что у меня была функция поработать над данным крошечным компонентом, так как он научил меня нескольким хорошим вещам, связанным с TypeScript, React и попытками сделать наши стили более удобочитаемыми.

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *