В этой статье мы собираемся создать тумблер в стиле iOS, используя React. Это будет небольшой автономный компонент, который вы сможете повторно использовать в будущих проектах. По мере продвижения мы также создадим простое демонстрационное приложение React, в котором будет использоваться наш пользовательский компонент-переключатель.
Мы могли бы использовать для этого сторонние библиотеки, но сборка с нуля позволяет нам лучше понять, как работает наш код, и позволяет нам полностью настроить наш компонент.
Формы предоставляют основные средства для взаимодействия с пользователем. Флажок традиционно используется для сбора двоичных данных, таких как да или нет, истина или ложь, включение или выключение, включение или выключение и т. д. Хотя некоторые современные интерфейсы избегают использования полей формы при создании тумблеров, я буду придерживаться их здесь из-за их большей доступности.
Вот скриншот компонента, который мы будем создавать:
Два маленьких и два больших тумблера в проверенном и непроверенном состоянии
Начиная
Давайте воспользуемся Create React App, чтобы быстро запустить и запустить приложение React. Если вы не знакомы с приложением Create React, ознакомьтесь с нашим руководством по началу работы.
create-react-app toggleswitch
Как только все будет установлено, перейдите во вновь созданный каталог и запустите сервер с помощью yarn start (или npm start, если хотите). Это запустит сервер разработки по адресу http: //localhost:3000.
Далее создайте ToggleSwitchкаталог в srcкаталоге. Здесь мы создадим наш компонент:
mkdir src/ToggleSwitch
В этом каталоге создайте два файла: ToggleSwitch.jsи ToggleSwitch.scss:
touch ToggleSwitch.js ToggleSwitch.scss
Наконец, измените App.jsследующим образом:
import React from ‘react’;
import ToggleSwitch from '. /ToggleSwitch/ToggleSwitch’
function App () {
return (
) ;
}
export default App;
Разметка
Мы можем начать с базового элемента формы ввода HTML-флажка с набором необходимых свойств:
Чтобы построить вокруг него, нам может понадобиться вложение
с class, а
Преобразование в компонент React
Теперь, когда мы знаем, что нужно включить в HTML, все, что нам нужно сделать, это преобразовать HTML в компонент React. Давайте начнем с основного компонента здесь. Мы сделаем это компонентом класса, а затем преобразуем его в хуки, так как новым разработчикам легче следовать, stateчем useState.
На данный момент невозможно иметь несколько ползунков-переключателей на одном и том же представлении или на одной странице из-за повторения ids. Мы могли бы использовать способ компонентизации React здесь, но в этом случае мы будем использовать propsдля динамического заполнения значений:
import React, { Component } from ‘react’;
class ToggleSwitch extends Component {
render () {
return (
<input
type="checkbox"
className="toggle-switch-checkbox"
name={this.props.Name}
id={this.props.Name}
/>
) ;
}
}
export default ToggleSwitch;
Он this.props.Nameбудет динамически заполнять значения idи (обратите внимание name, forчто это htmlForв React JS), чтобы вы могли передавать разные значения компоненту и иметь несколько экземпляров на одной странице. Также обратите внимание, что у тега нет закрывающего тега. Вместо этого он закрывается в начальном теге вроде , и с точки зрения JSX это совершенно нормально.
Проверьте это, изменив содержимое App.jsследующим образом:
function App () {
return (
<>
</>
) ;
}
Проверьте результирующий вывод по адресу http: //localhost:3000/ (возможно, с помощью инструментов разработчика вашего браузера) и убедитесь, что все работает правильно.
Стилизация и SCSS
Недавно я писал о стилизации React Components, где сравнивал различные способы, которыми это возможно. В этой статье я пришел к выводу, что SCSS — лучший метод, и именно его мы будем использовать здесь.
Чтобы SCSS работал с приложением Create React, вам необходимо установить пакет node-sass:
yarn add node-sass
Нам также нужно импортировать правильный файл в наш компонент:
// ToggleSwitch.js
import React, { Component } from ‘react’;
import '. /ToggleSwitch.scss’;
...
Теперь о стиле. Это грубый набросок того, что нам нужно:
По умолчанию переключатель будет только 75pxшироким и выровненным по вертикали inline-block, чтобы он был встроен в текст и не вызывал проблем с макетом.
Мы позаботимся о том, чтобы элемент управления нельзя было выбрать, чтобы пользователи не могли его перетаскивать.
Мы будем скрывать исходный ввод флажка.
Псевдоэлементы: afterи: beforeдолжны быть стилизованы и превращены в элементы, чтобы поместить их в DOM и стилизовать их.
Мы также добавим несколько переходов CSS для классного анимированного эффекта.
И вот как это выглядит в SCSS. Добавьте следующее src/ToggleSwitch/ToggleSwitch.scss:
.toggle-switch {
position: relative;
width: 75px;
display: inline-block;
vertical-align: middle;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
text-align: left;
&-checkbox {
display: none;
}
&-label {
display: block;
overflow: hidden;
cursor: pointer;
border: 0 solid #bbb;
border-radius: 20px;
margin: 0;
}
&-inner {
display: block;
width: 200%;
margin-left: -100%;
transition: margin 0.3s ease-in 0s;
&: before,
&: after {
display: block;
float: left;
width: 50%;
height: 34px;
padding: 0;
line-height: 34px;
font-size: 14px;
color: white;
font-weight: bold;
box-sizing: border-box;
}
&: before {
content: «Yes»;
text-transform: uppercase;
padding-left: 10px;
background-color: #f90;
color: #fff;
}
}
&-disabled {
background-color: #ddd;
cursor: not-allowed;
&: before {
background-color: #ddd;
cursor: not-allowed;
}
}
&-inner: after {
content: «No»;
text-transform: uppercase;
padding-right: 10px;
background-color: #bbb;
color: #fff;
text-align: right;
}
&-switch {
display: block;
width: 24px;
margin: 5px;
background: #fff;
position: absolute;
top: 0;
bottom: 0;
right: 40px;
border: 0 solid #bbb;
border-radius: 20px;
transition: all 0.3s ease-in 0s;
}
&-checkbox: checked + &-label {
.toggle-switch-inner {
margin-left: 0;
}
.toggle-switch-switch {
right: 0px;
}
}
}
Предполагая, что вы следуете всем инструкциям, если вы перейдете на сервер разработки по адресу http: //localhost:3000/, вы увидите четыре красиво оформленных переключателя. Попробуйте переключить их; они все должны работать.
Также найдите время, чтобы пройти код выше. Если вы в чем-то не уверены, вы можете обратиться к документации по Sass или зайти на форумы SitePoint и задать вопрос.
Динамические метки
В настоящее время параметры переключения жестко закодированы:
.toggle-switch {
...
&-inner {
...
&: before {
content: «Yes»;
...
}
}
...
&-inner: after {
content: «No»;
...
}
...
}
Чтобы сделать компонент более гибким, мы можем получить их динамически из элемента управления, используя атрибуты данных HTML5:
&: before {
content: attr (data-yes) ;
...
}
&-inner: after {
content: attr (data-no) ;
...
}
Мы жестко закодируем атрибуты данных для тестирования, но в окончательной версии сделаем это более гибким:
// ToggleSwitch.js
class ToggleSwitch extends Component {
render () {
return (
...
data-yes="Ja" data-no="Nein"/>
) ;
}
}
Меньшая версия компонента
Кроме того, для небольших экранов было бы неплохо использовать уменьшенную версию переключателя без текста. Итак, давайте добавим для него стиль с минимальными размерами и удалением текста:
.toggle-switch {
...
&.small-switch {
width: 40px;
.toggle-switch-inner {
&: after,
&: before {
content: «»;
height: 20px;
line-height: 20px;
}
}
.toggle-switch-switch {
width: 16px;
right: 20px;
margin: 2px;
}
}
}
Что касается отзывчивости, мы должны изменить полный размер, поэтому давайте воспользуемся функцией масштабирования CSS. Здесь мы рассмотрели все адаптивные ширины устройств на основе Bootstrap:
.toggle-switch {
...
@media screen and (max-width: 991px) {
transform: scale (0.9) ;
}
@media screen and (max-width: 767px) {
transform: scale (0.825) ;
}
@media screen and (max-width: 575px) {
transform: scale (0.75) ;
}
}
Вы можете проверить это, добавив small-switchкласс к родительскому
элементу в ToggleSwitch.js:
class ToggleSwitch extends Component {
render () {
return (
...
) ;
}
}
Вернитесь на сервер разработки и проверьте свои изменения. Если вы хотите сравнить то, что у вас есть, с готовым файлом SCSS, вы можете найти его здесь.
Тематика в SCSS
Поскольку мы можем использовать переменные в SCSS, добавление поддержки нескольких цветовых тем в наше приложение упрощается. Подробнее об этом можно прочитать в «Sass Theming: The Never Ending Story «. Здесь мы будем использовать некоторые цветовые темы и изменим все необработанные цвета на переменные. Первые три строки представляют собой настраиваемый набор цветов, который помогает нам оформить наш маленький элемент управления:
// Colors
$label-colour: #bbb;
$disabled-colour: #ddd;
$toggle-colour: #2F855A;
$white: #fff;
// Styles
.toggle-switch {
...
&-label {
...
border: 0 solid $label-colour;
}
&-inner {
...
&: before {
...
background-color: $toggle-colour;
color: $white;
}
}
&-disabled {
background-color: $disabled-colour;
cursor: not-allowed;
&: before {
background-color: $disabled-colour;
cursor: not-allowed;
}
}
&-inner: after {
...
background-color: $label-colour;
color: $white;
}
&-switch {
...
background: $white;
border: 0 solid $label-colour;
}
...
}
И это все со стилем. Теперь давайте добавим немного интерактивности.
Взаимодействия и JavaScript
Обратите внимание, что следующий раздел содержит только демонстрационный код для объяснения концепций. Вы не должны обновлять свой фактический ToggleSwitchкомпонент в этом разделе.
Нашим базовым компонентом будет простой компонент (также известный как компонент представления), состояние которого будет контролироваться родительским компонентом или контейнером, таким как файл form. Что мы подразумеваем под контролем? Что ж, давайте сначала посмотрим на неконтролируемую версию:
import React from ‘react’;
const ToggleSwitch = () => (
<input
type="checkbox"
className="toggle-switch-checkbox"
/>
) ;
export default ToggleSwitch;
Когда пользователи взаимодействуют с приведенным выше вводом флажка, он будет переключаться между проверенным и непроверенным состоянием по собственному желанию, без необходимости писать какой-либо JavaScript. Элементы ввода HTML способны управлять своим собственным внутренним состоянием, и они делают это, напрямую обновляя DOM.
Однако в React рекомендуется использовать контролируемые компоненты, как показано в следующем примере:
import React from ‘react’;
const ToggleSwitch = ({checked}) => (
<input
type="checkbox"
className="toggle-switch-checkbox"
checked={checked}
/>
) ;
export default ToggleSwitch;
Здесь React контролирует состояние ввода флажка. Все взаимодействия с этим входом должны проходить через виртуальный DOM. Если вы попытаетесь взаимодействовать с компонентом как есть, ничего не произойдет, так как мы не определили какой-либо код JavaScript, который может изменить значение свойства, которое checkedмы передаем.
Чтобы исправить это, мы можем передать onChangeсвойство — функцию, которая будет вызываться всякий раз, когда нажимается флажок:
import React from ‘react’;
const ToggleSwitch = ({checked, onChange}) => (
<input
type="checkbox"
className="toggle-switch-checkbox"
checked={checked}
onChange={e => onChange (e.target.checked) }
/>
) ;
export default ToggleSwitch;
Теперь ввод флажка интерактивен. Пользователи могут включать и выключать компонент, как и раньше. Единственная разница здесь в том, что состояние контролируется React, в отличие от более ранней неконтролируемой версии. Делая это таким образом, мы можем легко получить доступ к состоянию нашего компонента в любой момент времени через JavaScript. Мы также можем легко определить начальное значение при объявлении компонента.
Теперь давайте посмотрим, как использовать ToggleSwitchкомпонент. Ниже приведен упрощенный пример на основе классов:
import React, { Component } from ‘react’;
class Form extends Component {
state = { checked: false }
onChange = newValue => {
this.setState ({ checked: newValue }) ;
}
render () {
return (
) ;
}
}
export default Form;
Теперь давайте преобразуем компонент на основе классов в функциональный компонент с помощью хуков:
import React, { useState } from ‘react’;
export default function Form () {
let [checked, setChecked] = useState (false) ;
return (
)
}
Как видите, мы резко сократили количество строк, используя функциональную составляющую методом создания хуков.
Если хуки в React для вас новы, ознакомьтесь с нашим руководством «React Hooks: как начать работу и создать свой собственный «.
Завершение работы над компонентом ToggleSwitch
Теперь вернемся к нашему ToggleSwitchкомпоненту. Нам понадобится следующий реквизит:
id (обязательно): это то id, что будет передано элементу управления вводом флажка. Без этого компонент не будет отображаться.
checked (обязательно): это будет содержать текущее состояние, которое будет логическим значением.
onChange (обязательно): эта функция будет вызываться при onChangeсрабатывании обработчика событий входа.
name (необязательно): это будет текст метки флажка, но обычно мы не будем его использовать.
small (необязательно): это логическое значение, которое отображает тумблер в маленьком режиме, где текст не отображается.
optionLabels (необязательно): если вы не используете smallверсию элемента управления, вам может потребоваться передать это в тумблер в виде массива из двух значений, которые обозначают текст для True и False. Примером будет Text={[»Yes», «No«]}.
disabled (необязательно): это будет напрямую передано в .
Если smallверсия не используется, по optionLabelsумолчанию будет использоваться следующий текст:
// Set optionLabels for rendering.
ToggleSwitch.defaultProps = {
optionLabels: [»Yes», «No»],
};
Поскольку большинство реквизитов должны быть установлены пользователем, и мы не можем использовать произвольные значения, всегда лучше остановить рендеринг, если требуемые реквизиты не переданы. Это можно сделать с помощью простого ifоператора JavaScript или тернарного оператора. с использованием?: или короткозамкнутым &&:
{this.props.id? (
): null}
По мере роста нашего приложения мы можем отловить много ошибок с проверкой типов. React имеет некоторые встроенные возможности проверки типов. Чтобы запустить проверку типа реквизита для компонента, вы можете назначить специальное propTypesсвойство. Мы можем применить приведенный выше список реквизитов, используя библиотеку React PropType, которая представляет собой отдельную библиотеку, которая экспортирует ряд валидаторов, которые можно использовать для проверки правильности получаемых вами данных.
Вы можете установить его так:
yarn add prop-types
Затем импортируйте библиотеку PropTypes, используя:
// ToggleSwitch.js
import PropTypes from «prop-types»;
Мы определим PropTypes следующим образом:
ToggleSwitch.propTypes = {
id: PropTypes.string.isRequired,
checked: PropTypes.bool.isRequired,
onChange: PropTypes.func.isRequired,
name: PropTypes.string,
optionLabels: PropTypes.array,
small: PropTypes.bool,
disabled: PropTypes.bool
};
В порядке пояснения:
PropTypes.string.isRequired: это строковое значение, обязательное и обязательное.
PropTypes.string: это строковое значение, но оно не обязательно.
PropTypes.func: это реквизит, который принимает функцию в качестве значения, но не является обязательным.
PropTypes.bool: это логическое значение, но оно не является обязательным.
PropTypes.array: это значение массива, но оно не обязательно.
Теперь мы можем продолжить работу с ToggleSwitchкомпонентом. Замените содержимое src/ToggleSwitch/ToggleSwitch.jsна следующее:
import React from «react»;
import PropTypes from «prop-types»;
import '. /ToggleSwitch.scss’;
/*
Toggle Switch Component
Note: id, checked and onChange are required for ToggleSwitch component to function.
The props name, small, disabled and optionLabels are optional.
Наконец, чтобы протестировать компонент, измените его App.jsследующим образом:
import React, { useState } from ‘react’;
import ToggleSwitch from '. /ToggleSwitch/ToggleSwitch’
function App () {
let [newsletter, setNewsletter] = useState (false) ;
const onNewsletterChange = (checked) => {
setNewsletter (checked) ;
}
return (
<>
Subscribe to our Newsletter
</>
) ;
}
export default App;
Теперь, когда вы перейдете на http: //localhost:3000/, вы должны увидеть рабочий переключатель.
Обеспечение доступности компонентной клавиатуры
Последний шаг — сделать клавиатуру нашего компонента доступной. Для этого сначала измените метку следующим образом:
// ToggleSwitch.js
<label className="toggle-switch-label"
htmlFor={id}
tabIndex={ disabled? -1: 1 }
onKeyDown={ e => handleKeyPress (e) }>
...
Как вы можете видеть, мы добавили tabIndexсвойство, которое мы устанавливаем в 1 (фокусируемое) или -1 (не фокусируемое) в зависимости от того, отключен ли компонент в данный момент.
Мы также объявили handleKeyPressфункцию для обработки ввода с клавиатуры:
function handleKeyPress (e) {
if (e.keyCode≠= 32) return;
e.preventDefault () ;
onChange (! checked)
}
Это проверяет, является ли нажатая клавиша пробелом. Если это так, он предотвращает действие браузера по умолчанию (в данном случае прокрутка страницы) и переключает состояние компонента.
И это, по сути, все, что вам нужно. Компонент теперь доступен с клавиатуры.
Однако есть небольшая проблема. Если щелкнуть ToggleSwitchкомпонент, теперь вы получите контур вокруг всего компонента, что, вероятно, нежелательно. Чтобы бороться с этим, мы можем немного изменить вещи, чтобы убедиться, что он получает контур, когда он сфокусирован с помощью клавиатуры, но не при нажатии:
// ToggleSwitch.js
<span
className={
disabled
? «toggle-switch-innertoggle-switch-disabled»
: «toggle-switch-inner»
}
data-yes={optionLabels[0]}
data-no={optionLabels[1]}
tabIndex={-1}
/>
<span
className={
disabled
? «toggle-switch-switchtoggle-switch-disabled»
: «toggle-switch-switch»
}
tabIndex={-1}
/>
Здесь мы добавили tabIndexсвойство к обоим внутренним элементам, чтобы гарантировать, что они не могут получить фокус.
Затем в ToggleSwitch.scss:
$focus-color: #ff0;
.toggle-switch {
...
&-label {
...
&: focus {
outline: none;
> span {
box-shadow: 0 0 2px 5px $focus-color;
}
}
> span: focus {
outline: none;
}
}
...
}
Это применит стиль к ToggleSwitchвнутреннему элементу, когда он сфокусирован с помощью клавиатуры, но не при нажатии. Подробнее об этой технике можно прочитать здесь. Он немного хакерский, и от него следует отказаться в пользу использования: focus-visible, как только он получит достаточно широкую поддержку браузеров.
Более полный пример
В завершение я хотел бы продемонстрировать более полный пример использования ToggleSwitchкомпонента в следующем CodeSandbox.
В этой демонстрации используется несколько ToggleSwitchкомпонентов на одной странице. Состояние последних трех переключателей зависит от состояния первого. То есть вам нужно принимать маркетинговые письма, прежде чем вы сможете уточнить свой выбор, какие из них получать.