Распространенной метафорой пользовательского интерфейса является ненавязчивое всплывающее сообщение с предупреждением в нижней части экрана, чтобы информировать пользователей о статусе приложения и его процессов. Я только что создал эту функцию в учебном проекте, поэтому решил поделиться своим кодом. В проекте используются Material for UI/UX, React и MobX для обработки состояний и переходов состояний. Весь код написан на машинописном языке.

Материал определяет набор руководств и компонентов, поддерживающих лучшие практики проектирования пользовательского интерфейса. В этом примере мы будем использовать реализацию компонентов Material в React material-ui.

Snackbar — это концепция дизайна уведомлений, которая исходит от Material. Он предоставляет краткие сообщения о состоянии приложения в нижней части экрана.

В Material-ui уже есть компонент Snackbar, который управляет темами и анимацией компонента. Вот пример компонента Snackbar в действии.

В соответствии с руководящими принципами материалов, мы должны избегать штабелирования закусочных. Таким образом, реализуемая нами логика будет скрывать текущий Snackbar перед отображением следующего сообщения Snackbar.

Определение типов

Мы будем использовать Typescript, поэтому наша первая задача — определить некоторые типы для наших предупреждений.

Строки 1–4:определяется базовый тип для всех остальных предупреждений. Все предупреждения имеют сообщение и логическое значение, которое переключает, следует ли отображать предупреждение.

Строки 6–20: определяется каждый из отдельных типов предупреждений. И каждому типу оповещения присваивается отличительное поле: kind. Типы предупреждений здесь: success, info, warn и error.

Строка 22. Тип Alert определяется как размеченное объединение отдельных типов, которые мы уже определили.

Строки 24–29: Наконец, у нас есть функция для создания оповещения с именем alert. Все оповещения, созданные этим конструктором, по умолчанию отображаются.

Создание магазина

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

Давайте пройдемся по этому коду.

  1. Это наш импорт. Maybeasy обеспечивает поддержку необязательных структур данных, и мы импортируем несколько декораторов из MobX.
  2. Теперь мы в классе AlertsStore. Мы определяем свойство для хранения массива предупреждений. Мы украшаем это свойство как observable. Это делает массив предупреждений реактивным (о том, как это работает, можно написать в другом посте в блоге).
  3. Здесь мы определяем свойство только для чтения с именем current, которое возвращает предупреждение, которое должно отображаться. У нас не всегда может быть текущее предупреждение, так как массив может быть пустым, поэтому мы возвращаем Maybe<Alert> из этого свойства. Это поле также оформлено как значение computed. Вычисленные значения реагируют на изменения наблюдаемых, вычисляют новое значение, а затем значение запоминается.
  4. hide это наше первое действие. Действия в MobX — это подпрограммы, которым разрешено менять состояние. Этот метод скрывает текущее оповещение, изменяя значение поля отображения в первом (текущем) оповещении. Обратите внимание, что мы определяем наши действия как стрелочные функции. Это всплывет позже.
  5. process — это действие, которое удаляет первый элемент из очереди предупреждений.
  6. push добавляет оповещение в очередь оповещений. Обратите внимание, что помещение оповещения в очередь также скрывает текущее оповещение. Это соответствует рекомендации Материала по скрытию текущего оповещения перед отображением нового оповещения.
  7. Вот и весь функционал нашего AlertsStore. Это хранилище, которое, скорее всего, будет использоваться в любом месте приложения, поэтому мы также экспортируем одноэлементный экземпляр класса AlertsStore, чтобы не передавать его каждому отдельному компоненту, которому он может понадобиться.

Представление предупреждений

Это для логики. В следующем примере кода показано, как мы можем использовать AlertsStore для отображения предупреждения Snackbar.

Material-ui выполняет большую часть тяжелой работы по реализации представления Snackbar.

  1. Обратите внимание на импорт: компонент Snackbar исходит из material-ui, наблюдатель исходит из mobx-react (подробнее об этом позже), и мы импортируем только что созданный экземпляр alertsStore в модуль store.
  2. Ранее мы указывали, что текущее оповещение относится к типу «Возможно». Методcata — сокращение от катаморфизма — позволяет нам сложить различные возможные варианты, которые могут существовать, и вернуть одно значение. Это как switch, только как выражение, а не как утверждение.
  3. Давайте перейдем к случаю, когда у нас нет текущего оповещения (у нас ничего нет). В этом случае мы ничего не будем рендерить, что здесь обозначено возвратом пустого фрагмента React.
  4. В случае Just у нас есть предупреждение, поэтому мы отрисуем компонент Snackbar.
  5. Свойство open принимает логическое значение display из текущего оповещения. Мы передаем действия hide и process непосредственно соответствующим обработчикам событий — нет необходимости в анонимной функции-оболочке, поскольку мы определили их как стрелочные функции. Наконец, мы позволяем компоненту Snackbar обрабатывать логику увольнения по времени (здесь установлено значение 6 секунд).
  6. Мы передаем предупреждение компоненту AlertContent, который описан в другом месте.
  7. Компонент Alerts — это компонент React без состояния (функция); ничего особенного. Но оборачиваем компонент функцией observer из mobx-react. Это заставляет компонент автоматически реагировать на изменения в наблюдаемых и вычисляемых состояниях AlertsStore.

Тематика и отображение содержимого предупреждений

Компонент Snackbar содержит логику отображения и анимации, но для отображения чего-либо на экране ему нужен компонент SnackbarContent в качестве дочернего элемента. В этом примере я завернул SnackbarContent в компонент AlertContent, чтобы иметь возможность работать со стилями и темами ближе к отображаемому содержимому.

Большая часть логики этого компонента связана с API-интерфейсом material-ui для стилей и поддержанием согласованной темы.

  1. Мы много чего импортируем сюда, в основном из библиотек материалов. Обратите внимание, что мы также импортируем экземпляр alertsStore непосредственно в этот компонент.
  2. Здесь мы создаем функцию для передачи компоненту более высокого порядка withStyles (который исходит из material-ui). Это то, что позволяет нам поддерживать единую тему для наших компонентов, даже если мы некомпетентный дизайнер (как я).
  3. Здесь мы устанавливаем, что мы создаем стили только специально для наших типов предупреждений. На самом деле это довольно приятная функция. Если мы изменим наши типы предупреждений, компилятор напомнит нам, что мы должны прийти сюда и обновить стили.
  4. Мы не можем отображать контент без предупреждения, поэтому мы делаем это свойство обязательным. Вы *могли* снова развернуть текущее оповещение из alertsStore, но я предпочитаю не повторять эту логику здесь.
  5. Классы будут переданы из оболочки withStyles. На самом деле это необязательно, но поскольку этот реквизит становится частью общедоступного интерфейса компонента, я сделал его необязательным, чтобы пользователям компонента не нужно было явно указывать объект классов.
  6. Здесь я указываю значение по умолчанию для классов. На практике классы всегда будут предоставляться через material-ui.
  7. className зависит от типа оповещения.
  8. Фактический компонент, который будет отображаться для этого предупреждения, — это div, обертывающий сообщение предупреждения.
  9. Этот код предоставляет кнопку X для явного закрытия оповещения. Опять же, обработчик onClick получает действие hide непосредственно от alertsStore. Нет необходимости в анонимной функции-оболочке или bind.
  10. Наконец, мы открываем наш компонент контента, обернутый в тему material-ui, предоставленную withStyles HOC.

Запуск оповещений

И, наконец, мы можем запускать оповещения, импортируя экземпляр alertsStore, а затем push добавляя в него новые оповещения из любого места нашего приложения.

Подведение итогов

Мы создали полностью функциональный и многоразовый компонент Snackbar. Мы использовали MobX, чтобы упростить логику управления оповещениями, но при этом соответствовать рекомендациям Руководства по материалам. А с помощью Typescript мы снизили риск того, что мы доставим код, который выйдет из строя во время выполнения или будет вести себя непредвиденным образом.