# @skbkontur/react-ui
> A comprehensive React component library providing UI components, validation tools, and design utilities for building user interfaces.
> Version: 5.x | Homepage: https://tech.skbkontur.ru/kontur-ui | npm: @skbkontur/react-ui
## Installation
```bash
npm install @skbkontur/react-ui
# or
yarn add @skbkontur/react-ui
```
Import components directly:
```jsx
import { Button, Input, Modal } from '@skbkontur/react-ui';
```
---
# DataTids
Аттрибуты data-tid экспортируются для компонентов в виде объекта.
При необходимости добавления аттрибута data-tid необходимо создать пулл-реквест.
У всех компонентов также есть проп data-tid, которым можно переопределять корневые дата-тиды.
# Feature flags
Фича-флаги позволяют заранее применять будущие **breaking changes** (ломающие изменения) до выхода очередного мажорного релиза библиотеки.
При выходе очередной мажорной версии, например, `5.x.x` → `6.x.x`, будет применено новое поведение, а сами фича-флаги будут удалены.
## Подключение фича-флагов
Чтобы применить доработку/исправление нужно указать фича-флаги в ``
```jsx static
import { ReactUIFeatureFlagsContext } from '@skbkontur/react-ui';
function ExampleComponent() {
return (
{/* Внутри будут работать все фича-флаги с true */}
);
}
```
Механизм работы: новая функциональность применяется или не применяется в зависимости от того, был ли передан со значением true соответствующий флаг или нет.
Флаги задаются с помощью `ValidationsFeatureFlagsContext.Provider`.
```jsx static
import { ReactUIFeatureFlagsContext } from '@skbkontur/react-ui';
{/* ... */} ;
```
## Объект со всеми флагами
Для получения объекта со списком всех фича-флагов необходимо применить вспомогательные функции к объектам заданных флагов
```typescript static
import { getFullReactUIFlagsContext } from '@skbkontur/react-ui';
getFullReactUIFlagsContext(useContext(ReactUIFeatureFlagsContext));
```
### Выпадающие элементы и несколько React root
React позволяет создавать root внутри root, но контекст между ними не прокидывается. Это вызывает проблемы в работе
различных выпадашек, типа `Hint`, `Tooltip`, `Select`, `Modal` и других.
В версии `4.26.0` появился мехнизм, который решает большинство этих проблем. Если вложенный root является виджетом,
то будет достаточно обновить библиотеку только в нём.
Однако, при удалении HTML-элемента, который был root, его необходимо предварительно явно размонтировать:
```tsx static
React.useLayoutEffect(
() => () => {
if (Number(React.version) === 17) {
rootRef.current && ReactDOM.unmountComponentAtNode(rootRef.current);
} else if (Number(React.version) >= 18) {
setTimeout(() => reactRoot.current?.unmount());
}
},
[],
);
```
## Отключение анимаций во время тестирования
Анимации в компонентах отключаются любой из следующих глобальных переменных:
```static
REACT_UI_TEST
process.env.NODE_ENV === 'test'
process.env.REACT_UI_TEST
process.env.REACT_APP_REACT_UI_TEST
process.env.STORYBOOK_REACT_UI_TEST
```
## Прокидывание className и style компонентам
Начиная с версии 2.14.0, стало возможным передавать в компоненты свои css-классы для дополнительной стилизации. Однако,
не стоит пользоваться этой возможностью для вмешательства во внутренние стили компонентов. Верстка компонентов может
быть изменена в любой момент без предупреждения, что приведет к поломке ваших переопределенных стилей.
# StrictMode
Начиная с версий `@skbkontur/react-ui@3.10.0` и `@skbkontur/react-ui-validations@1.7.0` поддерживается `React.StrictMode`.
Некоторым компонентам библиотеки необходимо иметь доступ до корневой DOM-ноды своих children. Ранее для этого
использовался метод `findDomNode`, который в `StrictMode` запрещён. Теперь получение DOM-ноды реализовано в библиотеке через
`ref`, из-за чего появились некоторые требования к компонентам, передаваемым в `Hint`, `Tooltip`, `Popup` или `Tab`:
## Использование с forwardRef
При передаче функциональных компонентов в `react <= 18`, они должны использовать `React.forwardRef`:
## Использование с useImperativeHandle
При использовании хука `useImperativeHandle`, возвращаемый объект должен реализовывать метод `getRootNode`, возвращающий DOM-ноду:
## Использование с классовыми компонентами
В классовых компонентах инстанс должен реализовывать метод `getRootNode`, возвращающий DOM-ноду:
## Использование с компонентами React UI
При оборачивании компонентов React UI нужно брать DOM-ноду через `getRootNode` у инстанса компонента:
## Без StrictMode
Без `StrictMode` будет применяться метод `findDomNode`, который несовместим со `StrictMode`.
[Подробнее в pull request →](https://github.com/skbkontur/retail-ui/pull/2518)
# Локализации компонентов через контекст `React.Context`
```typescript static
interface LocaleContextProps {
locale?: LocaleControls;
langCode?: LangCodes;
}
```
## Доступные языки
```typescript static
enum LangCodes {
ru_RU = 'ru_RU',
en_GB = 'en_GB',
}
```
## LocaleControls
```typescript static
interface LocaleControls {
[key: string]: any;
TokenInput?: Partial;
Token?: Partial;
ComboBox?: Partial;
Select?: Partial;
Paging?: Partial;
DatePicker?: Partial;
Calendar?: Partial;
FileUploader?: Partial;
PasswordInput?: Partial;
SidePage?: Partial;
}
```
## Использование
Дефолтная локализация ` `
Кастомная локализация ` `
Некоторые контролы используют компоненты других контролов.
Для инкапсуляции локализации можно использовать несколько контекстов.
Локализация функционального компонента через useContext.
## Локализация @skbkontur/react-ui-addons
Компоненты `@skbkontur/react-ui-addons` так же поддерживают локализацию через `LocaleContext` из `@skbkontur/react-ui`.
См. пример с кастомной локализацией. Дефолтные локали описаны для каждого компонента в [докумендации аддонов](https://ui.gitlab-pages.kontur.host/docs/#/react-ui-addons).
# Server Side Rendering
Контролы поддерживают рендеринг на стороне сервера начиная с версии `2.12.0`.
Но для предварительного рендеринга стилей понадобится установить пакеты `@emotion/react` и `@emotion/server`. И импортировать `cache` из библиотеки контролов.
Подробнее - [https://emotion.sh](https://emotion.sh/docs/ssr)
Условный файл `server.js` будет выглядеть примерно так:
```typescript static
import React from 'react';
import { renderToString } from 'react-dom/server';
import { CacheProvider } from '@emotion/react';
import createEmotionServer, { EmotionCritical } from '@emotion/server/create-instance';
import { cache } from '@skbkontur/react-ui/lib/theming/Emotion';
import { App } from './App';
const { extractCritical } = createEmotionServer(cache);
const element = (
);
const { html, css, ids } = extractCritical(renderToString(element));
res.status(200).header('Content-Type', 'text/html').send(`
${html}
`);
```
`client.js`
```typescript static
import React from 'react';
import ReactDOM from 'react-dom';
import { CacheProvider } from '@emotion/react';
import { cache } from '@skbkontur/react-ui/lib/theming/Emotion';
import { App } from './App';
ReactDOM.hydrate(
,
document.getElementById('root'),
);
```
Для `react-ui` версии `2.x` актуален [старый пример](https://github.com/skbkontur/retail-ui/blob/2.x/packages/react-ui/SSR.md) с Emotion 10.
# SizeControl
Контекст для управления размерами контролов `React.Context`, позволяет единообразно задать дефолтный размер для большинства компонентов библиотеки. Это:
- упрощает конфигурирование размеров на уровне экрана/модуля;
- избавляет от дублирования пропа `size` у каждого компонента;
- помогает быстро переключать плотность интерфейса (например, small ↔ medium ↔ large)
Если компонент принимает проп `size`, он использует значение из контекста по умолчанию. При необходимости размер можно переопределить пропом.
## Подключение
```tsx static
import { SizeControlContext } from '@skbkontur/react-ui';
export function Page() {
return (
{/* Внутри провайдера все компоненты по умолчанию будут medium */}
);
}
```
## Поддерживаемые размеры
```ts static
type SizeProp = 'small' | 'medium' | 'large';
```
## Быстрый пример
В примере ниже компонент внутри провайдера автоматически принимают размер из контекста.
## Интерактивный переключатель размера
Потестируйте разные значения `size` и посмотрите, как меняются компоненты.
## Набор типовых компонентов
Пример показывает, как единый размер влияет на разные компоненты одновременно.
## Переопределение размера пропом
Даже внутри провайдера можно задать конкретный размер компоненту через проп `size`. Это удобно для редких исключений.
# Theme
Темы в React UI позволяют переключаться между светлой и темной темами, а также кастомизировать внешний вид компонентов.
Например, для гибкой настройки цветов, размеров, отступов, теней и др. параметров.
---
Для кастомизации компонентов используется ``. Динамические стили генерируются в зависимости от темы в процессе render'а с помощью @emotion/css , полученные классы добавляются в `className` соответствующих элементов.
Тема задается с помощью ``:
```jsx static
import { ThemeContext } from '@skbkontur/react-ui';
import { DARK_THEME } from '@skbkontur/react-ui/lib/theming/themes/DarkTheme';
{/* ... */} ;
```
Объект со значениями темы доступен через ``:
```jsx static
import { ThemeContext, Button } from '@skbkontur/react-ui';
{theme => /* ... */ }
```
В функцониальных компонентах применяется хук `useContext`
```typescript static
const theme = useContext(ThemeContext);
```
В классовых компонентах объект с темой доступен через `contextType`
```typescript static
public static contextType = ThemeContext;
public context: Theme = this.context;
```
## Создание/редактирование темы
Кастомные значения нужно передать в `ThemeFactory.create` и получившуюся тему можно использовать в `ThemeContext.Provider`. `ThemeFactory` расширяет переданный объект, задавая в качестве прототипа объект темы по умолчанию.
Вторым аргументом `ThemeFactory.create` может принимать объект, который будет использован в качестве базовой темы.
```jsx static
import { ThemeFactory, LIGHT_THEME } from '@skbkontur/react-ui';
const myFlatTheme = ThemeFactory.create({ btnBorderRadiusSmall: '10px' }, LIGHT_THEME);
```
## Добавление своих переменных
Для дополнения существующей темы новыми переменными для своих компонентов, можно создать отдельный контекст с расширенным объектом темы
## Кастомизация legacy-приложений
В случае, если контролы рендерятся через какую-то общую обертку, достаточно добавить в нее `ThemeContext.Provider` с вашей темой. В противном случае, вам подойдет метод `ThemeFactory.overrideDefaultTheme()`.
```typescript static
import theme from './theme/theme';
import { ThemeFactory } from '@skbkontur/react-ui/lib/theming/ThemeFactory';
ThemeFactory.overrideDefaultTheme(theme);
```
## Несколько тем вместе
```jsx harmony
import { ThemeContext, LIGHT_THEME, DARK_THEME } from '@skbkontur/react-ui';
import { ShowcaseGroup } from '@skbkontur/react-ui/internal/ThemePlayground/ShowcaseGroup';
const CombinedComponents = () => (
<>
>
);
;
```
## Темы @skbkontur/react-ui-addons
Компоненты `@skbkontur/react-ui-addons` так же поддерживают кастомизацию через `ThemeContext` из `@skbkontur/react-ui`. Достаточно переопределить нужные переменные, которые перечислены на страницах компонентов в [докумендации аддонов](https://ui.gitlab-pages.kontur.host/docs/#/react-ui-addons).
```tsx static
import { ThemeContext, ThemeFactory } from '@skbkontur/react-ui';
import { Logotype, AddonsThemeIn } from '@skbkontur/react-ui-addons';
const myTheme = ThemeFactory.create({
logoColor: 'black',
});
;
```
## Дополнительно: утилиты для цветов и размеров
В библиотеке есть несколько утилит для работы с цветами при создании своих тем
```typescript static
import { ColorFunctions } from '@skbkontur/react-ui'
ColorFunctions.lighten(colorString: string, amount: number | string, method?: 'absolute' | 'relative'): string
ColorFunctions.darken(colorString: string, amount: number | string, method?: 'absolute' | 'relative'): string
ColorFunctions.contrast(colorString: string, darkString?: string, lightString?: string, threshold: number = 0.43): string
ColorFunctions.red(colorString: string): string
ColorFunctions.green(colorString: string): string
ColorFunctions.blue(colorString: string): string
ColorFunctions.alpha(colorString: string): string
ColorFunctions.isValid(colorString: string): boolean // проверяет, можно ли распарсить строку в цвет
```
Документацию по их работе можно найти на сайте [less](http://lesscss.org/functions/#color-operations).
В качестве colorString можно передать цвет в одном из форматов: `keyword`, `hex`, `rgb(r, g, b)`, `rgba(r, g, b, a)`, `hsl(h, s, l)`, `hsla(h, s, l, a)`.
В качестве `amount` можно передать строку вида 'N%' или число от 0 до 1.
Все значения больше или меньше возможных обрезаются. Например, для `rgba(300, -100, 123, 20)` `r=255, g=0, b=123, a=1`.
Если распарсить `colorString` не получилось - выбрасывается исключение.
Если это возможно, результат возвращается в том же виде, что и входная строка:
```typescript static
import { ColorFunctions } from '@skbkontur/react-ui';
ColorFunctions.lighten('hsl(90, 0.8, 0.2)', '20%') === 'hsl(90, 0.8, 0.4)';
ColorFunctions.lighten('rgba(50, 50, 50, 0.2)', '20%') === 'rgba(102, 102, 102, 0.2)';
ColorFunctions.lighten('#80e619', 0.2) === '#b3f075';
ColorFunctions.lighten('crimson', '20%') === '#f16581';
```
Для работы с размерами предусмотрена одна функция (_DimensionFunctions.ts_):
```typescript static
shift(value: string, shift: string): string
// пример
import { DimensionFunctions } from '@skbkontur/react-ui'
DimensionFunctions.shift('100%', '-20') === '80%'
DimensionFunctions.shift('2em', '+2') === '4em'
DimensionFunctions.shift('12', '+1') === '13px' //если единица измерения не указана - используется px
DimensionFunctions.shift('10.2', '12.333451') === '22.5335px' //дробная часть округляется до 4 знаков
```
## Версии тем
С версии 5.0 в библиотеке появилось версионирование тем для облегчения обновления библиотеки. Версии тем позволяют откатывать визуальные изменения компоенентов к предыдущим минорным версиям, отложив их на будущее .
Помимо `LIGHT_THEME` и `DARK_THEME` с актуальным фирменным стилем, в пакете содержатся темы с фиксированными минорными версиями (например, `LIGHT_THEME_5_1` или `DARK_THEME_5_2`).
```
import { ThemeContext } from '@skbkontur/react-ui/lib/theming/ThemeContext';
import { LIGHT_THEME, LIGHT_THEME_5_2 } from '@skbkontur/react-ui/lib/theming/themes/LightTheme';
// Тема последней версии библиотеки
{/* ... */}
// Тема фиксированной версии библиотеки 5.2
{/* ... */}
```
⚠️ Вы можете использовать промежуточную версию темы неограниченно долго в рамках одной мажорной версии библиотеки. Однако, в момент выхода новой мажорной версии, все предыдущие промежуточные версии тем из нее удаляются. Это значит, что при миграции на новую мажорную версию, придется применить все отложенные визуальные изменения.
Подробнее о версиях
Представим, что текущая версия библиотеки пользователя: `5.5.0`. Он решает обновить библиотеку до `5.10.0`, в которой есть визуальные изменения по сравнению с его текущей темой, но которые он пока не хочет применять. При этом, он не пользовался версионированием тем до этого момента, используя просто `LIGHT_THEME`. В версии `5.10.0` набор тем может выглядеть примерно так:
- LIGHT_THEME
- LIGHT_THEME_5_0
- LIGHT_THEME_5_4
- LIGHT_THEME_5_8
- LIGHT_THEME_5_10
Чтобы исключить все визуальные изменения, которых у пользователя нет на момент обновления, ему следует выбрать версию темы, наиболее близкую, но не превышающую ту, что он использовал до обновления. Т.е. на момент использования им версии `5.5.0` и дефолтной `LIGHT_THEME` внутри нее, он по факту использует `LIGHT_THEME_5_4`. И ему следует переключиться именно на нее, чтобы отложить применение визуальных изменений после обновления на `5.10.0`.
# ThemePlayground
На странице собраны все переменные для кастомизации компонентов
# Адаптивность компонентов
Начиная с версии 4.0 многие компоненты, такие как
{/* prettier-ignore-start */}
стали адаптивными, получив новую верстку, которая активируются на мобильных устройствах.
{/* prettier-ignore-end */}
Механизм адаптивности основан на [медиазапросах](https://developer.mozilla.org/en-US/docs/Web/CSS/Media_Queries) и управляется
переменной темы `mobileMediaQuery`. В зависимости от того, удовлетворяет текущее устройство медиазапросу из переменной
или нет, компоненты переходят в мобильный режим и обратно. По умолчанию, значение переменной равно `(max-width: 576px)`. Так же можно передавать свои
медиазапросы в хук `useResponsiveLayout` или через проп `options` в ResponsiveLayout.
## Настройка адаптивности
Управлять значением переменной `mobileMediaQuery` можно через `ThemeContext`.
```jsx static
import { ThemeContext, ThemeFactory } from '@skbkontur/react-ui';
/* ... */
;
```
### Отключение адаптивности
Чтобы полностью отключить адаптивность компонентов достаточно изменить значение переменной на `not all`.
## Определение текущего режима
Для определения текущего режима предусмотрен хук `useResponsiveLayout`.
Он возвращает объект с флагами активности текущих режимов, по умолчанию пока реализован только мобильный флаг.
```ts static
interface ResponsiveLayoutFlags {
isMobile: boolean;
}
jsx static
import { useResponsiveLayout } from '@skbkontur/react-ui';
function SomeComponent(props) {
const { isMobile } = useResponsiveLayout();
if (isMobile) {
return; /* ... */
} else {
return; /* ... */
}
}
```
Так же можно передать в него параметр `options: ResponsiveLayoutOptions`, значения из которого дополнят список флагов в `ResponsiveLayoutFlags`:
```ts static
export type MediaQueriesType = Record;
export interface ResponsiveLayoutOptions {
customMediaQueries?: MQ;
}
```
На основе `useResponsiveLayout` можно создать свой специфичный хук:
```jsx static
import { useResponsiveLayout as useResponsiveLayoutOrigin } from '@skbkontur/react-ui';
const customMediaQueries = {
isTablet: '(min-width: 577px)',
isDesktop: '(min-width: 1280px)',
};
const useResponsiveLayout = () => {
return useResponsiveLayoutOrigin({ customMediaQueries });
};
function SomeComponent(props) {
const { isMobile, isTablet, isDesktop } = useResponsiveLayout();
if (isMobile) {
return; /* ... */
} else if (isTablet) {
return; /* ... */
} else {
return; /* ... */
}
}
```
Как альтернативу можно использовать отдельный компонент ResponsiveLayout.
---
## Autocomplete
```jsx
import { Autocomplete } from '@skbkontur/react-ui';
```
### Props
- **renderItem?**: Отрисовывает элементы результата поиска.
- **source?**: Задаёт функцию поиска элементов, которая должна возвращать Promise с массивом значений.
- **disablePortal?**: По умолчанию выпадающий список рендерится через [паттерн Portal](https://react.dev/reference/react-dom/createPortal). Проп отключает использование Portal и список рендерится как обычный блок с абсолютным позиционированием внутри компонента.
- **hasShadow?**: Определяет, нужно ли показывать тень у выпадающего списка.
- **menuAlign?**: Выравнивание выпадающего списка.
- **menuMaxHeight?**: Максимальная высота выпадающего списка.
- **menuWidth?**: Ширина выпадающего списка.
- **preventWindowScroll?**: Отключает скролл окна, когда выпадающий список раскрыт.
- **onValueChange**: Событие изменения `value`.
- **onBlur?**: Событие потери автокомплитом фокуса.
- **size?**: Размер автокомплита.
- **value**: Значение автокомплита.
- **mobileMenuHeaderText?**: Текст заголовка выпадающего списка в мобильной версии.
- **menuPos?**: Расположение выпадающего списка — над или под полем.
### Usage
```jsx
import { Autocomplete } from '@skbkontur/react-ui';
```
## Использование
В отличие от [комбобокса](https://tech.skbkontur.ru/kontur-ui/?path=/docs/react-ui_input-data-combobox--docs) у поля нет стрелки и список со значениями нельзя открыть вручную. Подробнее об отличиях читайте в [Гайде](https://guides.kontur.ru/components/input-fields/combobox).
Подсказки определяются в пропе `source` — задаёт функцию поиска элементов, которая должна возвращать `Promise` с массивом значений.
## Доступность
Компонент поддерживает aria-атрибуты, если вам необходимо переопределить стандартное поведение.
## Валидация
С помощью пакета [React UI Validations](https://tech.skbkontur.ru/kontur-ui/?path=/docs/react-ui-validations_api-reference) можно добавить валидацию для компонента.
Подробнее о том, как настроить тип, уровень валидации, формат сообщения об ошибке и другие параметры поведения, смотрите в документации пакета [React UI Validations](https://tech.skbkontur.ru/kontur-ui/?path=/docs/react-ui-validations_displaying-getting-started--docs).
## Адаптивность
Автокомплит адаптивен: на мобильных устройствах поле с выпадающим список открывается на весь экран устройства и становится фокусным активным элементом, что упрощает работу с ним. Мобильный режим активируется при ширине вьюпорта `(max-width: 576px)` и наличии сенсорного экрана `(pointer: coarse)`. Вы можете передавать свои медиазапросы, больше о настройке адаптивности читайте в статье [Адаптивность компонентов](https://tech.skbkontur.ru/kontur-ui/?path=/docs/react-ui_information-mobiles--docs).
### Examples
```tsx
const items = ['Абакан', 'Алексин', 'Алматы', 'Альметьевск', 'Алтайский край', 'Амурская область'];
const [value, setValue] = React.useState('');
return ;
```
**ExampleSize**: Размер автокомплита задаётся пропом `size`. По умолчанию `"small"`.
```tsx
const items = ['Маленький', 'Средний', 'Большой'];
const [valueSmall, setValueSmall] = React.useState('Маленький');
const [valueMedium, setValueMedium] = React.useState('Средний');
const [valueLarge, setValueLarge] = React.useState('Большой');
return (
);
```
**ExampleMenuWidth**: Проп `menuWidth` задаёт ширину выпадающего списка.
```tsx
const items = ['Абакан', 'Алексин', 'Алматы', 'Альметьевск', 'Алтайский край', 'Амурская область'];
const [valuePercent, setValuePercent] = React.useState('');
const [valueNumber, setValueNumber] = React.useState('');
return (
);
```
**ExampleMenuMaxHeight**: Проп `menuMaxHeight` фиксирует максимальную высоту выпадающего списка.
```tsx
const items = ['Абакан', 'Алексин', 'Алматы', 'Альметьевск', 'Алтайский край', 'Амурская область'];
const [value, setValue] = React.useState('');
return (
);
```
**ExampleMenuPos**: По умолчанию список раскрывается под полем, а если список находится близко к нижней границе страницы, то он динамически меняет расположение и раскрывается над полем. Расположение выпадающего списка можно зафиксировать вручную через проп `menuPos`. Оно может быть под полем — `"bottom"` или над полем — `"top"`. В таком случае расположение будет зафиксировано и не будет меняться при близости к границе страницы.
```tsx
const items = ['Абакан', 'Алексин', 'Алматы', 'Альметьевск', 'Алтайский край', 'Амурская область'];
const [value, setValue] = React.useState('');
return (
);
```
**ExampleMenuAlign**: Проп `menuAlign` выравнивает выпадающий список. Выпадающий список может быть прикреплен к левому краю — "left" или к правому — "right".
```tsx
const items = ['Выпадающее меню выравнивается по левому краю', 'Выпадающее меню выравнивается по правому краю'];
const [valueLeft, setValueLeft] = React.useState('Выпадающее меню выравнивается по левому краю');
const [valueRight, setValueRight] = React.useState('Выпадающее меню выравнивается по правому краю');
return (
);
```
**ExampleBorderless**: Проп `borderless` убирает обводку у поля.
```tsx
const items = ['Абакан', 'Алексин', 'Алматы', 'Альметьевск', 'Алтайский край', 'Амурская область'];
const [value, setValue] = React.useState('Амурская область');
return (
);
```
**ExampleAlign**: Проп `align` задаёт выравнивание текста в поле. Выравнивается только текст внутри поля, проп не влияет на выравнивание значений в выпадающем списке.
```tsx
const items = ['Слева', 'По центру', 'Справа'];
const [valueSmall, setValueSmall] = React.useState('Слева');
const [valueMedium, setValueMedium] = React.useState('По центру');
const [valueLarge, setValueLarge] = React.useState('Справа');
return (
);
```
**ExampleMask**: Для автокомплита может быть задана маска. Автокомплит наследует от [MaskedInput](https://tech.skbkontur.ru/kontur-ui/?path=/docs/react-ui_input-data-maskedinput--docs) пропсы: - `mask` — определяет шаблон маски, используемый для форматирования и проверки корректности вводимых данных в поле. - `maskChar` — задаёт cимвол маски. Он отображается в шаблоне маски в качестве плейсхолдера - `formatChars` — задаёт словарь символов-регулярок. С помощью него вы можете настроить собственный словарь символов.
```tsx
const [value, setValue] = React.useState('');
const getOnlyDigits = (value: string) => value.match(/\d+/g)?.join('') || '';
const items: string[] = ['+7 912 043-98-27', '+7 912 999-11-22', '+7 912 444-55-99'];
return (
{
const numbers = getOnlyDigits(pattern);
return new Promise((resolve) => {
resolve(items.filter((item) => getOnlyDigits(item).startsWith(numbers)));
});
}}
onValueChange={setValue}
/>
);
```
**ExampleSelectAllOnFocus**: Проп `selectAllOnFocus` добавляет автовыделение значения: всё значение внутри поля выделяется при фокусе на нем. Может быть полезно для полей, в которых пользователи могут часто копировать значение.
```tsx
const items = ['Абакан', 'Алексин', 'Алматы', 'Альметьевск', 'Алтайский край', 'Амурская область'];
const [value, setValue] = React.useState('Амурская область');
return ;
```
**ExampleIcon**: В поле можно передать иконку. Иконка может находиться в начале поля — проп `leftIcon`, в конце — проп `rightIcon`. Под разный размер полей используйте подходящие начертания и размер иконок: - Small — Light 16 - Medium — Light 20 - Large — Regular 24
```tsx
const items = ['Абакан', 'Алексин', 'Алматы', 'Альметьевск', 'Алтайский край', 'Амурская область'];
const [valueLeft, setValueLeft] = React.useState('');
const [valueRight, setValueRight] = React.useState('');
return (
}
/>
}
/>
);
```
**ExampleClear**: Очистить значение в поле можно только с помощью пустой строки.
```tsx
const items = ['Абакан', 'Алексин', 'Алматы', 'Альметьевск', 'Алтайский край', 'Амурская область'];
const [value, setValue] = React.useState('Алматы');
return (
setValue('')}>Передать пустое значение
);
```
**ExampleShowClearIcon**: Проп `showClearIcon` добавляет в поле иконку очистки, по нажатию на иконку поле будет очищаться. Доступные значения: - `"always"` — всегда показывает иконку очистки в заполненном поле, при очистке значения возвращается стрелка для раскрытия списка; - `"auto"` — показывает иконку в заполненном поле, только когда оно в состоянии hover или focus; - `"never"` (по умолчанию) — не показывает иконку очистки. При одновременной настройке `rightIcon` и `showClearIcon` иконка очистки заменит иконку справа.
```tsx
const items = [
'showClearIcon="auto"',
'showClearIcon="always"',
'showClearIcon="never"',
'showClearIcon="auto" + rightIcon',
];
const [valueAlways, setValueAlways] = React.useState(items[0]);
const [valueAuto, setValueAuto] = React.useState(items[1]);
const [valueNever, setValueNever] = React.useState(items[2]);
const [valueWithIcon, setValueWithIcon] = React.useState(items[3]);
return (
}
/>
);
```
---
## Button
```jsx
import { Button } from '@skbkontur/react-ui';
```
### Usage
```jsx
import { Button } from '@skbkontur/react-ui';
```
## Использование
По умолчанию кнопка принимает все стандартные атрибуты и обработчики, которые доступны для обычного `HTMLButtonElement`. То есть вы можете передавать в этот компонент те же пропсы, что и в обычный `` — они будут корректно работать.
Кнопка может рендериться как ссылка, например, если она используется как переход на другую страницу, при этом визуально все останется как есть — смотрите «Кнопка-ссылка» в примерах ниже.
**Альтернативы или дополнения**
- [Link](https://tech.skbkontur.ru/kontur-ui/?path=/docs/react-ui_button-link--docs) — если необходима кнопка, выглядящая как ссылка, можно переопределить корневой элемент на `component=button`.
- [Group](https://tech.skbkontur.ru/kontur-ui/?path=/docs/react-ui_layout-group--docs) — если необходимы кнопки в составе группы элементов по горизонтали.
## Доступность
Используйте кнопку, когда действие происходит на текущей странице. Если при клике на элемент происходит переход на другую страницу или на другую часть страницы, используйте ссылку — тег .
Компонент поддерживает aria-атрибуты, если вам необходимо переопределить стандартное поведение.
**Фокус кнопки**
Не отключайте стандартный вид фокуса кнопки — при фокусе с клавиатуры всегда должен отображаться видимый клавиатурный фокус. Цвет фокуса кнопки должен быть таким же, как во всём продукте.
**Событие по кнопке**
Указывайте тип кнопки `type` в зависимости от того, что происходит при нажатии:
- `submit` (значение по умолчанию): если отправляет данные на сервер. Всегда учитывайте, что при таком типе поля формы отправляются при нажатии на Enter. Проверьте, что вы предусмотрели защиту от случайной отправки непроверенных данных. Если кнопка находится вне формы, добавьте к кнопке атрибут `form` со значением `”id”` формы.
- `reset`: если удаляет введённые данные из формы.
- `button`: обычная кнопка с другими действиями, их можно задать через скрипты.
Если кнопка открывает модальное окно, сайдпейдж или выпадающий список, добавьте атрибуты `aria-expanded` и `aria-haspopup`:
- `aria-expanded` — значение атрибута меняется в зависимости от того, открыты ли модальное окно или сайдпейдж. `”true”` если открыт, и `”false”` — закрыт.
- `aria-haspopup` — значение атрибута зависит от того, какой тип компонента открывается при нажатии:
- `”true”` или `”menu”` — попап с ролью подменю `menu`;
- `”dialog”` — попап с ролью модального окна `dialog`;
- `”listbox”` — попап с ролью выпадающего списка `listbox`.
**Заблокированная кнопка**
За блокировку кнопки отвечает специальный атрибут `disabled`. Однако этот атрибут скрывает кнопку от скринридеров — а это не подходит в случаях, когда кнопка разблокируется при заполнении обязательных полей. Для такого случая добавьте атрибуты `aria-disabled=”true”`.
**Кнопка-иконка**
В кнопку-иконку добавляйте атрибут `aria-label` с коротким и ёмким названием кнопки, его лучше запросить у дизайнера. Это нужно, чтобы скринридер прочитал название кнопки.
```jsx static
} aria-label="Добавить">
```
**Адаптивная верстка**
В адаптивной верстке для кнопок, в особенности кнопок-иконок, используйте размеры Medium и Large. Если все же используете компактный размер, увеличивайте кликабельную область. [Подробнее в Контур.Гайдах](https://guides.kontur.ru/principles/base/adaptivity/#32).
ℹ️ **Полезно:** [Чек-лист доступности](https://tech.skbkontur.ru/kontur-ui/?path=/docs/accessibility--docs)
## Адаптивность
По умолчанию кнопка не меняет свой вид и поведение на мобильных устройствах.
ℹ️ **Полезно:** [Адаптивность компонентов](https://tech.skbkontur.ru/kontur-ui/?path=/docs/react-ui_information-mobiles--docs)
### Examples
```tsx
return Кнопка ;
```
**ExampleStyles**: Проп `use` задаёт стиль кнопки. Список доступных: - outline — кнопка второстепенного действия с обводкой, но без заливки - fill — кнопка второстепенного действия с заливкой, но без обводки - text — кнопка второстепенного действия без заливки и обводки - accent — кнопка основного действия - danger — кнопка деструктивного действия - success — кнопка позитивного действия - pay — кнопка, связанная с оплатой ⚠️ Deprecated-стили, будут удалены в следующих мажорных версиях: - use="primary" → use="accent" - use="backless" → use="outline" - use="link" → <Link component="button"> - use="default" → use="outline" для границ или use="fill" для фона
```tsx
return (
Outline
Fill
Text
Accent
Danger
Success
Pay
);
```
**ExampleSize**: Проп `size` задаёт размер. По умолчанию: `'small'`.
```tsx
return (
Small
Medium
Large
);
```
**ExampleWidth**: Пропом `width` можно задать свою ширину кнопки. Может принимать как абсолютные значения — например, 150, так и относительные — например, 50%.
```tsx
return Закрыть ;
```
**ExampleAlign**: Задаётся пропом `align`.
```tsx
return (
Left
Center
Right
);
```
**ExampleIcon**: В кнопку можно передать иконку. Иконка может находиться слева от текста кнопки — проп `icon`, справа — проп `rightIcon`, в обоих позициях одновременно. Если не передавать текст кнопки, будет отображаться кнопка-иконка. Для кнопки-иконки есть рекомендации по соблюдению доступности, изучите раздел Доступность . Под разный размер кнопок используйте подходящие начертания и размер иконок: - Small — Light 16 - Medium — Light 20 - Large — Regular 24
```tsx
return (
}>
}>Создать
} rightIcon={ }>
Создать
}>
Создать
);
```
**ExampleArrow**: Проп `arrow` создаёт кнопку со стрелкой. Рекомендуется использовать для пошаговых мастеров. При указании `arrow` без значения будет добавлена кнопка со стрелкой вправо, для кнопки со стрелкой влево укажите значение `arrow="left"`. При изменении ширины кнопки стрелка будет оставаться прикреплена к внешнему краю кнопки, а не к тексту, как это происходит при добавлении отдельной иконки.
```tsx
return (
Назад
Далее
Назад
Далее
);
```
**ExampleLoading**: Проп `loading` переводит кнопку в состояние загрузки. Кнопка на время загрузки отключается. Если в кнопке есть иконка, на время загрузки иконка заменяется на спиннер, если иконки нет — весь контент кнопки заменяется на спиннер. Когда иконки две — заменяется только левая.
```tsx
const [loading, setLoading] = React.useState(false);
const delay = (time: number) => (args?: unknown) => new Promise((resolve) => setTimeout(resolve, time, args));
const handleLoadingStart = () => {
delay(2000)().then(() => {
setLoading(false);
});
};
const handleClick = () => {
setLoading(true);
handleLoadingStart();
};
return (
Удалить
} width={150} onClick={handleClick} loading={loading}>
Удалить
} width={150} onClick={handleClick} loading={loading}>
Удалить
}
rightIcon={ }
width={150}
onClick={handleClick}
loading={loading}
>
Удалить
);
```
**ExampleDisabled**: Проп `disabled` блокирует кнопку. Кнопка меняет цвет на серый и становится недоступна для нажатия.
```tsx
return Кнопка ;
```
**ExampleLink**: Кнопка может рендерить ссылку в качестве корневого элемента. Переопределить корневой элемент можно c помощью пропа `component`. Кнопка принимает все пропсы переданного компонента.
```tsx
return (
Ссылка, но выглядит как кнопка
);
```
**ExampleTheme**: Проп `theme` позволяет кастомизировать кнопку через свойства темы. Заданные переменные будут объединены с темой из ``. Общие переменные темы и переменные для кнопки (с префиксом `btn`) смотрите на странице [ThemePlayground](https://tech.skbkontur.ru/kontur-ui/?path=/docs/react-ui_information-themeplayground--docs) .
```tsx
return (
Открыть
Открыть
Открыть
Открыть
Открыть
);
tsx
return (
}
theme={{ btnTextHoverBg: '#ED3F3F', btnTextActiveBg: '#DD3333', btnTextHoverTextColor: '#FFF' }}
>
Удалить
);
```
---
## Calendar
```jsx
import { Calendar } from '@skbkontur/react-ui';
```
### Props
- **onValueChange?**: Задает функцию, которая вызывается при изменении value.
- **value**: Задает текущую дату в формате `dd.mm.yyyy`.
- **maxDate?**: Задает максимальную возможную дату в формате `dd.mm.yyyy`.
- **minDate?**: Задает минимальную возможную дату в формате `dd.mm.yyyy`.
- **periodStartDate?**: Задает начальную дату периода в формате `dd.mm.yyyy`.
- **periodEndDate?**: Задает конечную дату периода в формате `dd.mm.yyyy`.
- **isHoliday?**: Задает функцию для определения праздничных дней. (default: `(_day, isWeekend) => isWeekend.`)
- **initialMonth?**: Задает начальный месяц.
- **initialYear?**: Задает начальный год.
- **renderDay?**: Задает метод отрисовки дат в календаре. (default: `(props: CalendarDayProps) => `)
- **onMonthChange?**: Задает функцию, которая вызывается при каждом изменении месяца. `month: number` - номер текущего отображаемого месяца от 1 до 12, `year: number` - отображаемый год.
### Examples
```tsx
const [date, setDate] = React.useState('01.11.2021');
return ;
```
**Example2**: Вне зависимости от того, какая дата выбрана в календаре в данный момент - можно изменить отображение начального года и месяца с помощью пропсов `initialMonth` и `initialYear`
```tsx
const [date, setDate] = React.useState('11.12.2021');
const initialMonth = 7;
const initialYear = 2000;
return (
Выбранная дата: {date}
Начальный месяц: {initialMonth}
Начальный год: {initialYear}
);
```
**Example3**: В компонент можно передать функцию `isHoliday`, которая будет получать день строкой формата `dd.mm.yyyy` и флаг `isWeekend`, и должна вернуть `true` для выходного и `false` для рабочего дня.
```tsx
const [date, setDate] = React.useState();
const createRandomHolidays = () => {
const holidays = new Array(10);
const today = new Date();
for (let index = 0; index < holidays.length; index++) {
const day = new Date(today.setDate(today.getDate() + 1 + index).valueOf());
const holiday = {
date: day.getDate(),
month: day.getMonth(),
year: day.getFullYear(),
};
holidays[index] = DatePickerHelpers.formatDate(holiday);
}
return holidays;
};
const holidays = createRandomHolidays();
const isHoliday = (day: string, isWeekend: boolean) => {
if (holidays.includes(day)) {
return !isWeekend;
}
return isWeekend;
};
return ;
```
**Example4**: Календарю можно задать кастомную высоту с помощью переменной темы `calendarWrapperHeight` - Базовая высота календаря - `330px` - Максимальная высота календаря - `450px`
```tsx
const [date, setDate] = React.useState('01.11.2021');
const theme = React.useContext(ThemeContext);
return (
);
```
**Example5**: Для кастомнизации дней в календаре используется проп `renderDay`. Корректная работа подразумевает обязательное использование компонента ` ` и передачу в него всех приходящих в функцию пропсов: ` } />`.
```tsx
const initialValue = '02.09.2023';
const [value, setValue] = React.useState(initialValue);
const renderDay = (props: CalendarDayProps) => {
const [date, month] = props.date.split('.').map(Number);
if (month === 9 && date > 12 && date < 16) {
return (
'Кастомный день'}>
);
}
if (month === 8 && date === 20) {
return (
#
);
}
return ;
};
return ;
```
**Example6**: Пример с кастомизацией темы и кастомным рендером дня.
```tsx
const theme = React.useContext(ThemeContext);
function renderDay(props: CalendarDayProps) {
const [date, month] = props.date.split('.').map(Number);
const randomDay = date % 6 === 0 || date % 7 === 0 || date % 8 === 0;
const randomPrice = Math.round((date / month) * 1000);
return (
{date}
{randomDay ? <>{randomPrice} ₽> : — }
);
}
const [value, setValue] = React.useState(null);
return (
);
tsx
const initialValue = '02.09.2023';
const [value, setValue] = React.useState(initialValue);
const calendarRef = React.useRef(null);
return (
<>
calendarRef.current?.scrollToMonth(1, 2023)}>I квартал
calendarRef.current?.scrollToMonth(4, 2023)}>II квартал
calendarRef.current?.scrollToMonth(7, 2023)}>III квартал
calendarRef.current?.scrollToMonth(10, 2023)}>IV квартал
>
);
```
---
## Center
```jsx
import { Center } from '@skbkontur/react-ui';
```
### Props
- **align?**: Задает выравнивание контента по горизонтали
### Examples
```tsx
const [alignAt, setAlignAt] = React.useState<'left' | 'center' | 'right'>('center');
return (
setAlignAt(value as 'left' | 'center' | 'right')}
/>
);
```
---
## Checkbox
```jsx
import { Checkbox } from '@skbkontur/react-ui';
```
### Props
- **error?**: Переводит контрол в состояние валидации "ошибка".
- **warning?**: Переводит контрол в состояние валидации "предупреждение".
- **size?**: Задает размер.
- **onValueChange?**: Задает функцию, вызывающуюся при изменении value.
- **initialIndeterminate?**: Устанавливает начальное [неопределенное состояние чекбокса](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/checkbox#attr-indeterminate).
### Examples
```tsx
const [checked, setChecked] = React.useState(false);
return (
Обычный чекбокс
);
tsx
const CheckboxWithState = ({ children, ...props }: { children: React.ReactNode; [key: string]: unknown }) => {
const [checked, setChecked] = React.useState(false);
return (
{children}
);
};
return (
Обычный чекбокс
Чекбокс в состоянии ошибки
Чекбокс в состоянии предупреждения
);
tsx
return (
Маленький
Средний
Большой
);
tsx
const [checked, setChecked] = React.useState(false);
const checkboxInstance = React.useRef(null);
return (
Пример чекбокса с программным фокусом
{
checkboxInstance.current?.focus();
}}
>
Дать фокус
{
checkboxInstance.current?.blur();
}}
>
Забрать фокус
);
```
**Example5**: Чекбокс может находится в неопределённом состоянии. Это состояние полностью копирует поведение состояния `indeterminate` ([подробнее](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/checkbox#indeterminate_state_checkboxes)) из HTML. Это состояние влияет только на внешний вид и не влияет на состояние `checked`.
```tsx
const [checked, setChecked] = React.useState(false);
const checkboxInstance = React.useRef(null);
return (
{
checkboxInstance.current = el;
}}
>
Неопределённый чекбокс
checkboxInstance.current?.setIndeterminate()}>
Перевести в неопределённое состояние
checkboxInstance.current?.resetIndeterminate()}>
Сбросить неопределённое состояние
);
tsx
const [checkedSiblings, setCheckedSiblings] = React.useState([]);
const siblingCheckboxes = [1, 2];
const parentCheckboxRef = React.useRef(null);
React.useEffect(() => {
if (checkedSiblings.length === 0 || checkedSiblings.length === siblingCheckboxes.length) {
parentCheckboxRef.current?.resetIndeterminate();
} else if (checkedSiblings.length !== 0) {
parentCheckboxRef.current?.setIndeterminate();
}
}, [JSON.stringify(checkedSiblings)]);
return (
<>
{
if (checkedSiblings.length === siblingCheckboxes.length) {
setCheckedSiblings(() => []);
} else {
setCheckedSiblings(() => [...siblingCheckboxes]);
}
}}
>
Родитель
{siblingCheckboxes.map((id) => {
return (
{
const siblingIndex = checkedSiblings.indexOf(id);
if (siblingIndex === -1) {
setCheckedSiblings((prev) => [...prev, id]);
} else {
setCheckedSiblings((prev) =>
prev.filter((siblingId) => {
return siblingId !== id;
}),
);
}
}}
>
Ребёнок ({id})
);
})}
>
);
```
---
## ComboBox
```jsx
import { ComboBox } from '@skbkontur/react-ui';
```
### Props
- **showClearIcon?**: Показывает иконку очистки значения в заполненном поле. (default: `never`)
- **align?**: Ввыравнивание текста в поле.
- **searchOnFocus?**: Вызывает функцию поиска `getItems` при фокусе и очистке поля ввода.
- **drawArrow?**: Отображает справа иконку в виде стрелки.
- **autoFocus?**: Устанавливает фокус на комбобоксе после окончания загрузки страницы.
- **borderless?**: Убирает обводку поля.
- **disablePortal?**: По умолчанию выпадающий список рендерится через [паттерн Portal](https://react.dev/reference/react-dom/createPortal). Проп отключает использование Portal и список рендерится как обычный блок с абсолютным позиционированием внутри компонента.
- **disabled?**: Поле становится недоступно для редактирования.
- **error?**: Меняет визуальное отображение поля на состояние «ошибка».
- **leftIcon?**: Добавляет иконку слева. При использовании `ReactNode` применяются дефолтные стили для иконки. При использовании `() => ReactNode` применяются только стили для позиционирования.
- **rightIcon?**: Добавляет иконку справа. При использовании `ReactNode` применяются дефолтные стили для иконки. При использовании `() => ReactNode` применяются только стили для позиционирования.
- **getItems**: Задаёт функцию поиска элементов, которая должна возвращать Promise с массивом значений. По умолчанию ожидаются объекты с типом `{ value: string, label: string }`. Элементы могут быть любого типа. В этом случае необходимо определить свойства `itemToId`, `renderValue`, `renderItem`, `valueToString`.
- **itemToId?**: Сравнивает полученные результаты с `value`.
- **maxLength?**: Mаксимальная длина значения, которое пользователь может ввести в поле.
- **menuPos?**: Расположение выпадающего списка — над или под полем.
- **menuAlign?**: Выравнивание выпадающего меню.
- **onBlur?**: Событие потери комбобоксом фокуса.
- **onValueChange?**: Событие изменения значения (`value`) в поле.
- **onFocus?**: Событие получения комбобоксом фокуса.
- **onInputValueChange?**: Событие, которое вызывается при изменении текста в поле ввода, если результатом функции будет строка, то она станет следующим состоянием полем ввода. **Не рекомендуется менять значение, передаваемое в проп `value`, внутри этой функции. Используйте для этих целей `onValueChange` или `onUnexpectedInput`. Иначе возможно неожиданное поведение компонента.**
- **onUnexpectedInput?**: Событие обработки ввода строки в поле ввода и последующей потерей фокуса компонентом. Функция срабатывает с аргументом поля строки. Если при потере фокуса в выпадающем списке будет только один элемент и результат `valueToString` с этим элементом будет совпадать со значение в текстовом поле, то сработает `onValueChange` со значением данного элемента. Сама функция также может вернуть значение, не равное undefined, с которым будет вызван `onValueChange`. Если возвращаемое значение будет равно null, то сработает очистка текущего значения поля, а в режиме редактирования токен будет удален.
- **placeholder?**: Текст, который отображается если не введено никакое значение.
- **renderItem?**: Отрисовывает элементы результата поиска. Не применяется, если элемент является функцией или React-элементом. (default: `item => item.label`)
- **itemWrapper?**: Устанавливает компонент, заменяющий собой обёртку элементов результата поиска. По умолчанию все элементы результата поиска оборачиваются в тег . itemWrapper={(item) => { if (item.value === 3) { return (props) => { return } } }}
- **renderNotFound?**: Отображает сообщение о пустом результате поиска. При `renderAddButton` не работает.
- **renderTotalCount?**: Отображает сообщение об общем количестве элементов.
- **renderValue?**: Отображает выбранное значение. (default: `item => item.label`)
- **renderAddButton?**: Отрисовывает кнопку добавления в выпадающем списке.
- **totalCount?**: Определяет общее количество элементов. Необходим для работы renderTotalCount.
- **value?**: Устанавливает выбранное в комбобоксе значение. Тип `value` совпадает с типом элементов в массиве, возвращаемом в `getItems`.
- **valueToString?**: Возвращает строковое представление `value`. Необходимо при фокусировке.
- **size?**: Размер комбобокса.
- **warning?**: Меняет визуальное отображение поля на состояние «предупреждение».
- **width?**: Ширина комбобокса.
- **maxMenuHeight?**: Максимальная высота выпадающего списка.
- **onMouseEnter?**: Событие наведения мышкой (событие `onmouseenter`). Смотрите разницу с `onMouseOver` в [документации](https://learn.javascript.ru/mousemove-mouseover-mouseout-mouseenter-mouseleave)
- **onMouseOver?**: Событие наведения мышкой (событие `onmouseover`).
- **onMouseLeave?**: Событие ухода мышки с объекта (событие `onmouseleave`).
- **onInputKeyDown?**: Событие нажатия кнопки на клавиатуре.
- **inputMode?**: Задаёт типы вводимых данных.
- **onBeforePasteInMask?**: Событие вставки значения в поле с маской.
- **viewMode?**: Режим отображения комбобокса: - `"singleline"` — однострочное поле; - `"multiline"` — многострочное поле; - `"multiline-editing"` — поле становится многострочным только при редактировании. Многострочные режимы не работают, если указан проп `mask`. В таком случае будет отображаться однострочное поле. (default: `singleline`)
- **maxRows?**: Максимальное количество отображаемых строк, если для поля добавлен проп многострочного режима — `"multiline"` или `"multiline-editing"`.
### Usage
```jsx
import { ComboBox } from '@skbkontur/react-ui';
```
## Использование
В комбобоксе пользователь может сразу раскрыть весь список значений из справочника и выбрать нужное. Когда пользователь начинает вводить значение, список динамически фильтруется. Можно также разрешить пользователям добавлять свои значения в справочник, если их ещё там нет.
Значения определяются в пропе `getItems` — задаёт функцию поиска элементов, которая должна возвращать `Promise` с массивом значений. По умолчанию ожидаются объекты с типом `{ value: string, label: string }`.
**Альтернативы и дополнения**
- [Autocomplete](https://tech.skbkontur.ru/kontur-ui/?path=/docs/react-ui_input-data-autocomplete--docs) — поле ввода со списком подсказок. Отличается от комбобокса тем, что в нём выпадающий список появляется только после ввода первого символа или изменении уже введенного значения, а в поле нет стрелки для раскрытия списка.
- [Select](https://tech.skbkontur.ru/kontur-ui/?path=/docs/react-ui_input-data-select--docs) — используйте раскрывающийся список, если среди значений не больше 25 вариантов.
- [RadioGroup](https://tech.skbkontur.ru/kontur-ui/?path=/docs/react-ui_input-data-radiogroup--docs) — используйте группу радиокнопок, если значений не больше 5.
## Доступность
Компонент поддерживает aria-атрибуты, если вам необходимо переопределить стандартное поведение.
## Валидация
С помощью пакета [React UI Validations](https://tech.skbkontur.ru/kontur-ui/?path=/docs/react-ui-validations_api-reference) можно добавить валидацию для компонента.
Подробнее о том, как настроить тип, уровень валидации, формат сообщения об ошибке и другие параметры поведения, смотрите в документации пакета [React UI Validations](https://tech.skbkontur.ru/kontur-ui/?path=/docs/react-ui-validations_displaying-getting-started--docs).
## Адаптивность
Комбобокс адаптивен: на мобильных устройствах поле с выпадающим списком открывается на весь экран устройства и становится фокусным активным элементом, что упрощает работу с ним. Мобильный режим активируется при ширине вьюпорта `(max-width: 576px)` и наличии сенсорного экрана `(pointer: coarse)`. Вы можете передавать свои медиазапросы, больше о настройке адаптивности читайте в статье [Адаптивность компонентов](https://tech.skbkontur.ru/kontur-ui/?path=/docs/react-ui_information-mobiles--docs).
### Examples
```tsx
const delay =
(time: number) =>
(args: Selected[]): Promise =>
new Promise((resolve) => setTimeout(() => resolve(args), time));
const maybeReject = (x: Selected[]) => (Math.random() * 3 < 1 ? Promise.reject() : Promise.resolve(x));
const getItems = (q: string): Promise =>
Promise.resolve(
[
{ value: 1, label: 'Абакан' },
{ value: 2, label: 'Алексин' },
{ value: 3, label: 'Алматы' },
{ value: 4, label: 'Альметьевск' },
{ value: 5, label: 'Алтайский край' },
{ value: 6, label: 'Амурская область' },
{ value: 7, label: 'Анадырь' },
{ value: 8, label: 'Анапа' },
{ value: 9, label: 'Архангельск' },
{ value: 10, label: 'Архангельская область' },
{ value: 11, label: 'Астраханская область' },
{ value: 12, label: 'Астрахань' },
].filter((x) => x.label.toLowerCase().includes(q.toLowerCase()) || x.value.toString(10) === q),
)
.then(delay(500))
.then(maybeReject);
const [selected, setSelected] = React.useState>({ value: 3, label: 'Third' });
const [error, setError] = React.useState(false);
const handleValueChange = (value: Selected) => {
setSelected(value);
setError(false);
};
const handleUnexpectedInput = () => {
setSelected(null);
setError(true);
};
const handleFocus = () => setError(false);
return (
'Выберите значение из списка'} trigger={error ? 'opened' : 'closed'}>
);
```
**ExampleSize**: Проп `size` задаёт размер комбобокса. По умолчанию `"small"`.
```tsx
const getItems = (q: string) => {
return Promise.resolve(
[
{ value: 1, label: 'Маленький' },
{ value: 2, label: 'Средний' },
{ value: 3, label: 'Большой' },
].filter((x) => x.label.toLowerCase().includes(q.toLowerCase()) || x.value.toString(10) === q),
);
};
const [valueSmall, setValueSmall] = React.useState({ value: 1, label: 'Маленький' });
const [valueMedium, setValueMedium] = React.useState({ value: 2, label: 'Средний' });
const [valueLarge, setValueLarge] = React.useState({ value: 3, label: 'Большой' });
return (
);
```
**ExampleWidth**: Проп `width` задаёт ширину комбобокса. Может быть в пикселях, процентах или других конкретных единицах. Заданная ширина применяется к полю и выпадающему списку.
```tsx
const getItems = (q: string) => {
return Promise.resolve(
[
{ value: 1, label: 'Абакан' },
{ value: 2, label: 'Алексин' },
{ value: 3, label: 'Алматы' },
{ value: 4, label: 'Альметьевск' },
{ value: 5, label: 'Алтайский край' },
{ value: 6, label: 'Амурская область' },
].filter((x) => x.label.toLowerCase().includes(q.toLowerCase()) || x.value.toString(10) === q),
);
};
const [valuePercent, setValuePercent] = React.useState({ value: 1, label: 'Абакан' });
const [valueNumber, setValueNumber] = React.useState({ value: 2, label: 'Амурская область' });
return (
);
```
**ExampleViewMode**: Проп `viewMode` позволяет сделать поле многострочным. Доступные значения: - `"singleline"` (по умолчанию) — однострочное поле; - `"multiline"` — многострочное поле; - `"multiline-editing"` — поле становится многострочным только при редактировании. Для многострочных полей можно дополнительно ограничить количество отображаемых строк через проп `maxRows`. Многострочные режимы не работают, если указан проп `mask`. В таком случае будет отображаться однострочное поле.
```tsx
const items: Record = {
1: 'Начос кукурузные ДЕЛИКАДОС с кусочками оливок и паприкой 150 г',
2: 'Плетенка с изюмом ИВАНОВСКАЯ с корицей и лимонной цедрой 300 г',
3: `Булочка с корицей ПЕКАРНЯ АРОМА с глазурью и изюмом, нежная внутри и с тонким ароматом корицы, идеально подходит к чаю или кофе 90 г`,
};
const getItems = (q: string) => {
return Promise.resolve(Object.keys(items).filter((key) => items[key].toLowerCase().includes(q.toLowerCase())));
};
const render = (v: string) => items[v];
const [valueMultilineEditing, setValueMultilineEditing] = React.useState('1');
const [valueMultiline, setValueMultiline] = React.useState('2');
const [valueMultilineRestricted, setValueMultilineRestricted] = React.useState('3');
return (
viewMode="multiline-editing"
viewMode="multiline"
viewMode="multiline" maxRows={3}
);
```
**ExampleMenuHeight**: Проп `maxMenuHeight` фиксирует максимальную высоту выпадающего списка.
```tsx
const getItems = (q: string) => {
return Promise.resolve(
[
{ value: 1, label: 'Абакан' },
{ value: 2, label: 'Алексин' },
{ value: 3, label: 'Алматы' },
{ value: 4, label: 'Альметьевск' },
{ value: 5, label: 'Алтайский край' },
{ value: 6, label: 'Амурская область' },
].filter((x) => x.label.toLowerCase().includes(q.toLowerCase()) || x.value.toString(10) === q),
);
};
const [value, setValue] = React.useState({ value: 2, label: 'Амурская область' });
return (
);
```
**ExampleMenuPos**: Проп `menuPos` фиксирует расположение выпадающего списка. Оно может быть под полем — `"bottom"` или над полем — `"top"`. По умолчанию список раскрывается под полем, а если список находится близко к нижней границе страницы, то он динамически меняет расположение и раскрывается над полем.
```tsx
const delay =
(time: number) =>
(args: Selected[]): Promise =>
new Promise((resolve) => setTimeout(() => resolve(args), time));
const maybeReject = (x: Selected[]) => (Math.random() * 3 < 1 ? Promise.reject() : Promise.resolve(x));
const getItems = (q: string) =>
Promise.resolve(
[
{ value: 1, label: 'Абакан' },
{ value: 2, label: 'Алексин' },
{ value: 3, label: 'Алматы' },
{ value: 4, label: 'Альметьевск' },
{ value: 5, label: 'Алтайский край' },
].filter((x) => x.label.toLowerCase().includes(q.toLowerCase()) || x.value.toString(10) === q),
)
.then(delay(500))
.then(maybeReject);
const [selected, setSelected] = React.useState>();
const [error, setError] = React.useState(false);
const handleValueChange = (value: Selected) => {
setSelected(value);
setError(false);
};
const handleUnexpectedInput = () => {
setSelected(null);
setError(true);
};
const handleFocus = () => setError(false);
return (
'Выберите значение из списка'} trigger={error ? 'opened' : 'closed'}>
);
```
**ExampleMenuAlign**: Проп `menuAlign` выравнивает выпадающий список. Выпадающий список может быть прикреплен к левому краю — `"left"` или к правому — `"right"`.
```tsx
const getItems = (q: string) => {
return Promise.resolve(
[
{ value: 1, label: 'Булочка с корицей ПЕКАРНЯ АРОМА с глазурью и изюмом 90 г' },
{ value: 2, label: 'Плетенка с изюмом ИВАНОВСКАЯ с корицей и лимонной цедрой 300 г' },
].filter((x) => x.label.toLowerCase().includes(q.toLowerCase()) || x.value.toString(10) === q),
);
};
const [valueLeft, setValueLeft] = React.useState({
value: 1,
label: 'Булочка с корицей ПЕКАРНЯ АРОМА с глазурью и изюмом 90 г',
});
const [valueRight, setValueRight] = React.useState({
value: 2,
label: 'Плетенка с изюмом ИВАНОВСКАЯ с корицей и лимонной цедрой 300 г',
});
return (
);
```
**ExampleBorderless**: Проп `borderless` убирает обводку у поля.
```tsx
const delay =
(time: number) =>
(args: Selected[]): Promise =>
new Promise((resolve) => setTimeout(() => resolve(args), time));
const maybeReject = (x: Selected[]) => (Math.random() * 3 < 1 ? Promise.reject() : Promise.resolve(x));
const getItems = (q: string) =>
Promise.resolve(
[
{ value: 1, label: 'Абакан' },
{ value: 2, label: 'Алексин' },
{ value: 3, label: 'Алматы' },
{ value: 4, label: 'Альметьевск' },
{ value: 5, label: 'Алтайский край' },
].filter((x) => x.label.toLowerCase().includes(q.toLowerCase()) || x.value.toString(10) === q),
)
.then(delay(500))
.then(maybeReject);
const [selected, setSelected] = React.useState>();
const [error, setError] = React.useState(false);
const handleValueChange = (value: Selected) => {
setSelected(value);
setError(false);
};
const handleUnexpectedInput = () => {
setSelected(null);
setError(true);
};
const handleFocus = () => setError(false);
return (
'Выберите значение из списка'} trigger={error ? 'opened' : 'closed'}>
);
tsx
const getItems = (q: string) => {
return Promise.resolve(
[
{ value: 1, label: 'Абакан' },
{ value: 2, label: 'Алексин' },
{ value: 3, label: 'Алматы' },
{ value: 4, label: 'Альметьевск' },
{ value: 5, label: 'Алтайский край' },
{ value: 6, label: 'Амурская область' },
].filter((x) => x.label.toLowerCase().includes(q.toLowerCase()) || x.value.toString(10) === q),
);
};
const [selected, setSelected] = React.useState>();
return (
}
/>
);
```
**ExampleAlign**: Проп `align` задаёт выравнивание текста в поле. Выравнивается только текст внутри поля, проп не влияет на выравнивание значений в выпадающем списке.
```tsx
const getItems = (q: string) => {
return Promise.resolve(
[
{ value: 1, label: 'Слева' },
{ value: 2, label: 'По центру' },
{ value: 3, label: 'Справа' },
].filter((x) => x.label.toLowerCase().includes(q.toLowerCase()) || x.value.toString(10) === q),
);
};
const [valueLeft, setValueLeft] = React.useState({ value: 1, label: 'Слева' });
const [valueCenter, setValueCenter] = React.useState({ value: 2, label: 'По центру' });
const [valueRight, setValueRight] = React.useState({ value: 3, label: 'Справа' });
return (
);
```
**ExampleMaxLength**: Проп `maxLength` задаёт максимальную длину значения, которое пользователь может ввести в поле. Значение пропа должно быть целым числом.
```tsx
const getItems = (q: string) => {
return Promise.resolve(
[
{ value: 1, label: 'Абакан' },
{ value: 2, label: 'Алексин' },
{ value: 3, label: 'Алматы' },
{ value: 4, label: 'Альметьевск' },
{ value: 5, label: 'Алтайский край' },
{ value: 6, label: 'Амурская область' },
].filter((x) => x.label.toLowerCase().includes(q.toLowerCase()) || x.value.toString(10) === q),
);
};
const [value, setValue] = React.useState>();
return (
);
```
**ExampleMask**: Для комбобокса может быть задана маска. Комобобокс наследует от [MaskedInput](https://tech.skbkontur.ru/kontur-ui/?path=/docs/react-ui_input-data-maskedinput--docs) пропсы: - `mask` — определяет шаблон маски, используемый для форматирования и проверки корректности вводимых данных в поле. - `maskChar` — задаёт cимвол маски. Он отображается в шаблоне маски в качестве плейсхолдера - `formatChars` — задаёт словарь символов-регулярок. С помощью него вы можете настроить собственный словарь символов.
```tsx
const [value, setValue] = React.useState<{ value: number; label: string } | null>(null);
const getOnlyDigits = (value: string) => value.match(/\d+/g)?.join('') || '';
const getItems = (q: string) => {
const numbers = getOnlyDigits(q);
return Promise.resolve(
[
{
value: 79120439827,
label: '+7 912 043-98-27',
},
{
value: 79120432228,
label: '+7 912 043-22-28',
},
].filter((x) => x.value.toString().startsWith(numbers)),
);
};
return (
);
```
**ExampleAddButton**: Проп `renderAddButton` позволяет разрешить пользователю добавлять свои значения в список. Используйте проп `renderAddButton`, в котором задаётся функция отрисовки кнопки добавления в выпадающем списке.
```tsx
const delay =
(time: number) =>
(args: Selected[]): Promise =>
new Promise((resolve) => setTimeout(() => resolve(args), time));
interface ComboboxExampleState {
items: Selected[];
query: string;
selected: Nullable;
error: boolean;
shouldRenderAddButton: boolean;
}
class ComboboxExample extends React.Component, ComboboxExampleState> {
comboBoxElement: ComboBox> | null = null;
constructor(props: Record) {
super(props);
this.state = {
items: [
{ value: 1, label: 'Абакан' },
{ value: 2, label: 'Алексин' },
{ value: 3, label: 'Алматы' },
{ value: 4, label: 'Альметьевск' },
{ value: 5, label: 'Алтайский край' },
{ value: 6, label: 'Амурская область' },
],
query: '',
selected: { value: 3, label: 'Алматы' },
error: false,
shouldRenderAddButton: false,
};
this.comboBoxElement = null;
this.getItems = this.getItems.bind(this);
this.handleValueChange = this.handleValueChange.bind(this);
this.handleFocus = this.handleFocus.bind(this);
this.handleInputValueChange = this.handleInputValueChange.bind(this);
this.renderAddButton = this.renderAddButton.bind(this);
this.refComboBox = this.refComboBox.bind(this);
this.addItem = this.addItem.bind(this);
}
render() {
return (
);
}
getItems(q: string): Promise {
return Promise.resolve(
this.state.items.filter(
(x: Selected) => x.label.toLowerCase().includes(q.toLowerCase()) || x.value.toString(10) === q,
),
).then(delay(500));
}
handleInputValueChange(query: string) {
const isItemExists = this.state.items.find((x) => x.label.toLowerCase() === query.toLowerCase());
this.setState({ query, shouldRenderAddButton: !isItemExists });
}
handleValueChange(value: Nullable) {
this.setState({ selected: value, error: false, shouldRenderAddButton: false });
}
handleFocus() {
this.setState({ error: false });
}
renderAddButton() {
if (!this.state.shouldRenderAddButton) {
return null;
}
return + Добавить "{this.state.query}" ;
}
refComboBox(element: ComboBox> | null) {
this.comboBoxElement = element;
}
addItem() {
this.setState((currentState) => {
const newItem = {
value: Math.max(...currentState.items.map(({ value }) => value)) + 1,
label: currentState.query,
};
return {
items: [...currentState.items, newItem],
selected: newItem,
error: false,
shouldRenderAddButton: false,
};
});
}
}
return ;
```
**ExampleClear**: Очистить значение в поле можно с помощью пустой строки, `null` или `undefined`.
```tsx
const [value, setValue] = React.useState>({ value: 2, label: 'Алексин' });
const getItems = (q: string) => {
return Promise.resolve(
[
{ value: 1, label: 'Абакан' },
{ value: 2, label: 'Алексин' },
{ value: 3, label: 'Алматы' },
{ value: 4, label: 'Альметьевск' },
{ value: 5, label: 'Алтайский край' },
{ value: 6, label: 'Амурская область' },
].filter((x) => x.label.toLowerCase().includes(q.toLowerCase()) || x.value.toString(10) === q),
);
};
return (
Передать:
setValue(null)}>Null
setValue(undefined)}>Undefined
setValue('')}>Пустая строка
);
```
**ExampleShowClearIcon**: Проп `showClearIcon` добавляет в поле иконку очистки, по нажатию на иконку поле будет очищаться. Доступные значения: - `"never"` (по умолчанию) — не показывает иконку очистки; - `"always"` — всегда показывает иконку очистки в заполненном поле, при очистке значения возвращается стрелка для раскрытия списка; - `"auto"` — показывает иконку в заполненном поле, только когда оно в состоянии hover или focus.
```tsx
const items = [
{ value: 'always', label: 'showClearIcon="always"' },
{ value: 'auto', label: 'showClearIcon="auto"' },
{ value: 'never', label: 'showClearIcon="never"' },
];
const [valueAlways, setValueAlways] = React.useState(items[0]);
const [valueAuto, setValueAuto] = React.useState(items[1]);
const [valueNever, setValueNever] = React.useState(items[2]);
const getItems = (q: string) => Promise.resolve(items.filter((x) => x.label.toLowerCase().includes(q.toLowerCase())));
return (
);
```
**ExampleReset**: Метод `reset()` позволяет сбросить введённое пользователем вручную значение без изменения `value`.
```tsx
const [selected, setSelected] = React.useState({ value: 2, label: 'Алексин' });
const ref = React.useRef>(null);
const handleReset = () => {
if (ref.current) {
ref.current.reset();
}
};
const getItems = (q: string) =>
Promise.resolve(
[
{ value: 1, label: 'Абакан' },
{ value: 2, label: 'Алексин' },
{ value: 3, label: 'Алматы' },
{ value: 4, label: 'Альметьевск' },
{ value: 5, label: 'Алтайский край' },
{ value: 6, label: 'Амурская область' },
].filter((x) => x.label.toLowerCase().includes(q.toLowerCase()) || x.value.toString(10) === q),
);
return (
Вызвать Reset
);
```
**ExampleDisabled**: Проп `disabled` переводит комбообокс в состояние блокировки. Поле визуально приглушается и становится недоступно для редактирования.
```tsx
const getItems = (q: string) => {
return Promise.resolve(
[
{ value: 1, label: 'Абакан' },
{ value: 2, label: 'Алексин' },
{ value: 3, label: 'Алматы' },
{ value: 4, label: 'Альметьевск' },
{ value: 5, label: 'Алтайский край' },
{ value: 6, label: 'Амурская область' },
].filter((x) => x.label.toLowerCase().includes(q.toLowerCase()) || x.value.toString(10) === q),
);
};
const [value, setValue] = React.useState>();
return (
);
```
**ExampleError**: Проп `error` переводит комбобокс в состояние ошибки.
```tsx
const delay =
(time: number) =>
(args: Selected[]): Promise =>
new Promise((resolve) => setTimeout(() => resolve(args), time));
const maybeReject = (x: Selected[]) => (Math.random() * 3 < 1 ? Promise.reject() : Promise.resolve(x));
const getItems = (q: string) =>
Promise.resolve(
[
{ value: 1, label: 'Абакан' },
{ value: 2, label: 'Алексин' },
{ value: 3, label: 'Алматы' },
{ value: 4, label: 'Альметьевск' },
{ value: 5, label: 'Алтайский край' },
{ value: 6, label: 'Амурская область' },
{ value: 7, label: 'Анадырь' },
{ value: 8, label: 'Анапа' },
{ value: 9, label: 'Архангельск' },
{ value: 10, label: 'Архангельская область' },
{ value: 11, label: 'Астраханская область' },
{ value: 12, label: 'Астрахань' },
].filter((x) => x.label.toLowerCase().includes(q.toLowerCase()) || x.value.toString(10) === q),
)
.then(delay(500))
.then(maybeReject);
const [selected, setSelected] = React.useState>();
const [error, setError] = React.useState(true);
const handleValueChange = (value: Selected) => {
setSelected(value);
setError(false);
};
const handleUnexpectedInput = () => {
setSelected(null);
setError(true);
};
const handleFocus = () => setError(false);
return (
'Выберите значение из списка'} trigger={error ? 'opened' : 'closed'}>
);
```
**ExampleCounter**: Пропсы `totalCount` и `renderTotalCount` позволяют добавить в выпадающий список счётчик найденных значений. - `renderTotalCount` — задаёт функцию, которая отображает сообщение о количестве значений. - `totalCount` — определяет общее количество значений.
```tsx
const [foundLength, setFoundLength] = React.useState(0);
const items: ComboBoxItem[] = [];
for (const key in document.body.style) {
if (Object.hasOwnProperty.call(document.body.style, key)) {
items.push({
value: key,
label: key,
});
}
}
const getItems = (query: string) => {
const found: Array> = items.filter(({ value }) =>
value.toLowerCase().includes(query.toLowerCase()),
);
const filtered = found.slice(0, 5);
setFoundLength(found.length);
return Promise.resolve(filtered);
};
return (
`Показано ${found} из ${total} найденных свойств`}
/>
);
```
**ExampleExtendedItems**: В массиве, возвращаемом `getItems`, могут быть переданы React-компоненты: ``, ``, ` ` и любые другие. В таких случаях поиск необходимо контролировать дополнительно.
```tsx
interface Item {
value: number;
label: string;
}
const delay =
(time: number) =>
(args: T): Promise =>
new Promise((resolve) => setTimeout(resolve, time, args));
const maybeReject = (x: T) => (Math.random() * 3 < 1 ? Promise.reject() : Promise.resolve(x));
const getItems = (q: string): Promise>> =>
Promise.resolve>>(
[
MenuHeader ,
{ value: 1, label: 'Абакан' },
{ value: 2, label: 'Алексин' },
{ value: 3, label: 'Алматы' },
{ value: 4, label: 'Альметьевск' },
{ value: 5, label: 'Алтайский край' },
{ value: 6, label: 'Амурская область' },
,
{ value: 7, label: 'Анадырь' },
{ value: 8, label: 'Анапа' },
{ value: 9, label: 'Архангельск' },
{ value: 10, label: 'Архангельская область' },
{ value: 11, label: 'Астраханская область' },
{ value: 12, label: 'Астрахань' },
MenuFooter ,
].filter((x) => ('label' in x ? x.label.toLowerCase().includes(q.toLowerCase()) : q === '')),
)
.then(delay(500))
.then(maybeReject);
const [selected, setSelected] = React.useState- (null);
const [error, setError] = React.useState(false);
const handleValueChange = (value: Item | null) => {
setSelected(value);
setError(false);
};
const handleUnexpectedInput = () => {
setSelected(null);
setError(true);
};
const handleFocus = () => setError(false);
return (
'Выберите значение из списка'} trigger={error ? 'opened' : 'closed'}>
error={error}
getItems={getItems}
onValueChange={handleValueChange}
onFocus={handleFocus}
onUnexpectedInput={handleUnexpectedInput}
placeholder="Введите или выберите из списка"
value={selected}
/>
);
```
**ExampleCustom**: В примере с помощью пропсов `renderValue`, `renderItem` и `itemWrapper` переопределён внешний вид элементов списка и выбранного значения, задано отображение галочки для одобренных элементов.
```tsx
type ExtendedSelected = Selected & {
approved: boolean;
label: string;
email: string;
};
const delay =
(time: number) =>
(args: ExtendedSelected[]): Promise =>
new Promise((resolve) => setTimeout(() => resolve(args), time));
const getItems = (q: string): Promise =>
Promise.resolve(
[
{ approved: true, value: 1, label: 'Леонид Долецкий', email: 'first@skbkontur.ru' },
{ approved: true, value: 2, label: 'Владислав Нашкодивший', email: 'second@skbkontur.ru' },
{ approved: false, value: 3, label: 'Розенкранц Харитонов', email: 'third@skbkontur.ru' },
{ approved: false, value: 4, label: 'Надежда Дубова', email: 'fourth@skbkontur.ru' },
{ approved: true, value: 5, label: 'Владислав Сташкеевич', email: 'fifth@skbkontur.ru' },
{ approved: true, value: 6, label: 'Василиса Александровна Поволоцкая', email: 'sixth@skbkontur.ru' },
].filter((x) => x.label.toLowerCase().includes(q.toLowerCase()) || x.value.toString(10) === q),
).then(delay(500));
const [selected, setSelected] = React.useState>({
approved: false,
value: 3,
label: 'Розенкранц Харитонов',
email: 'third@skbkontur.ru',
});
const [error, setError] = React.useState(false);
const handleValueChange = (value: ExtendedSelected) => {
setSelected(value);
setError(false);
};
const handleUnexpectedInput = () => {
setSelected(null);
setError(true);
};
const handleFocus = () => setError(false);
const customRenderItem = (item: ExtendedSelected) => (
{item.approved ? : null}
{item.label}
{item.email}
);
const customItemWrapper = (item: { value: number }) => {
if (item.value === 3) {
return (props: React.HTMLAttributes) =>
;
}
return (props: React.HTMLAttributes) => ;
};
const customRenderValue = (item: { label: string; email: string }) => (
{item.label}
{item.email}
);
return (
'Item must be selected!'} trigger={error ? 'opened' : 'closed'}>
error={error}
getItems={getItems}
onValueChange={handleValueChange}
onFocus={handleFocus}
onUnexpectedInput={handleUnexpectedInput}
placeholder="Введите или выберите значение"
value={selected}
renderItem={customRenderItem}
itemWrapper={customItemWrapper}
renderValue={customRenderValue}
width="400px"
/>
);
```
**ExampleHighlightedLabel**: В примере настроено кастомное поведение, которое при поиске подсвечивает совпадающие части в значениях.
```tsx
const delay =
(time: number) =>
(args: Selected[]): Promise =>
new Promise((resolve) => setTimeout(() => resolve(args), time));
const getItems = (query: string): Promise =>
Promise.resolve(
[
{ value: 1, label: 'Абакан' },
{ value: 2, label: 'Алексин' },
{ value: 3, label: 'Алматы' },
{ value: 4, label: 'Альметьевск' },
{ value: 5, label: 'Алтайский край' },
{ value: 6, label: 'Амурская область' },
{ value: 7, label: 'Анадырь' },
{ value: 8, label: 'Анапа' },
{ value: 9, label: 'Архангельск' },
{ value: 10, label: 'Архангельская область' },
{ value: 11, label: 'Астраханская область' },
{ value: 12, label: 'Астрахань' },
]
.filter((x) => x.label.toLowerCase().includes(query.toLowerCase()) || x.value.toString(10) === query)
.map(({ label, ...rest }) => {
const start = label.toLowerCase().indexOf(query.toLowerCase());
const end = start + query.length;
return {
...rest,
label,
highlightedLabel:
start >= 0 ? (
{label.substring(0, start)}
{label.substring(start, end)}
{label.substring(end)}
) : null,
};
}),
).then(delay(500));
const [selected, setSelected] = React.useState>();
const [error, setError] = React.useState(false);
const handleValueChange = (value: Nullable) => {
setSelected(value);
setError(false);
};
const handleUnexpectedInput = () => {
setSelected(null);
setError(true);
};
const handleFocus = () => setError(false);
const renderItem = (item: Selected & { highlightedLabel?: React.ReactNode }) => {
if (item.highlightedLabel) {
return item.highlightedLabel;
}
return item.label;
};
return (
'Выберите значение'} trigger={error ? 'opened' : 'closed'}>
);
```
---
## CurrencyInput
```jsx
import { CurrencyInput } from '@skbkontur/react-ui';
```
### Props
- **value?**: Значение поля.
- **hideTrailingZeros?**: Убирает лишние нули после запятой.
- **fractionDigits?**: Устанавливает минимальное количество отображаемых знаков после запятой. Если fractionDigits=15, то в целой части допускается только **0**.
- **signed?**: Разрешает вводить в поле символ минуса для отрицательных значений.
- **integerDigits?**: Допустимое количество цифр целой части до запятой. Если передан **0**, то в целой части допускается только **0**.
- **onValueChange**: Событие изменения `value`.
- **onSubmit?**: Событие отправки формы.
### Usage
```jsx
import { CurrencyInput } from '@skbkontur/react-ui';
```
## Использование
Используйте такое поле, когда нужно вводить значение в рублях, долларах или другой валюте.
Оно форматирует введенное значение и помогает пользователю не ошибиться при вводе.
По умолчанию значение в поле — десятичная дробь c запятой в качестве разделителя. Если ввести точку, она автоматически заменится на запятую. Максимальная длина числа — 15 цифр с разделителем в любом месте, мы опираемся на рекомендации [MDN web docs](https://developer.mozilla.org/ru/docs/Web/JavaScript/Reference/Global_Objects/Number/MAX_SAFE_INTEGER).
Количество знаков до и после запятой регулируются пропсами `fractionDigits` и `integerDigits`, с помощью пропа `fractionDigits` можно запретить знаки после запятой и разрешить только целые числа.
Компонент наследует часть базовых пропcов (размер, ширина, иконка в поле и т.д.) от компонента Input, они включены в таблицу пропсов . Примеры для базовых пропсов вы можете посмотреть на странице [Input](https://tech.skbkontur.ru/kontur-ui/?path=/docs/react-ui_input-data-input--docs).
## Доступность
Для компонента действуют все те же рекомендации по доступности, что и для компонента [Input](https://tech.skbkontur.ru/kontur-ui/?path=/docs/react-ui_input-data-input--docs).
## Адаптивность
По умолчанию поле ввода не меняет свой вид и поведение на мобильных устройствах.
ℹ️ Полезно: [Адаптивность компонентов](https://tech.skbkontur.ru/kontur-ui/?path=/docs/react-ui_information-mobiles--docs)
### Examples
```tsx
const [value, setValue] = React.useState();
return ;
```
**ExampleSize**: Проп `size` задаёт размер поля. По умолчанию `"small"`.
```tsx
const [value, setValue] = React.useState();
return (
);
```
**ExampleAlign**: Проп `align` задаёт выравнивание текста. По умолчанию `"right"`. Меняйте правое выравнивание на левое, если значения других полей в колонке выровнены по левому краю.
```tsx
const [valueLeft, setValueLeft] = React.useState('10');
const [valueRight, setValueRight] = React.useState('10');
return (
);
```
**ExampleFractionDigits**: Проп `fractionDigits` задаёт количество знаков после запятой. По умолчанию `{2}`. Если задать максимальное значение `fractionDigits={15}`, то в целой части допускается **0**. Чтобы поле могло принимать только целое число, установите `fractionDigits={0}`.
```tsx
const [value, setValue] = React.useState();
return ;
```
**ExampleIntegerDigits**: Проп `integerDigits` задаёт количество знаков до запятой. По умолчанию `{2}`. Если задать значение `integerDigits=0`, то в целой части допускается только **0**.
```tsx
const [value, setValue] = React.useState();
return ;
```
**ExampleHideTrailingZeros**: Проп `hideTrailingZeros` позволяет убрать лишние нули после запятой. Будет убирать лишние нули после запятой при потере фокуса с поля.
```tsx
const [value, setValue] = React.useState();
return ;
```
**ExampleSigned**: Проп `signed` разрешает ввод отрицательного значения. В поле можно ввести символ минуса (−). При вводе дефис (-), короткое тире (–) или тире (—) автоматически заменятся на верный символ минуса.
```tsx
const [value, setValue] = React.useState();
return ;
```
**ExampleClear**: Очистить значение в `CurrencyInput` можно с помощью `null` или `undefined`
```tsx
const [value, setValue] = React.useState();
return (
setValue(null)}>Передать null
setValue(undefined)}>Передать undefined
);
```
**ExampleCurrency**: Знак валюты можно прокидывать как внутрь поля с помощью пропа `rightIcon`, так и вне поля с помощью элемента `label`.
```tsx
const [value, setValue] = React.useState();
return (
₽
);
```
---
## CurrencyLabel
```jsx
import { CurrencyLabel } from '@skbkontur/react-ui';
```
### Props
- **fractionDigits?**: Минимальное количество отображаемых знаков после запятой. (default: `2`)
- **value**: Значение.
- **currencySymbol?**: Символ валюты.
- **hideTrailingZeros?**: Убирает лишние нули после запятой.
### Usage
```jsx
import { CurrencyLabel } from '@skbkontur/react-ui';
```
### Examples
```tsx
return ;
```
**ExampleCurrencySymbol**: В пропе `currencySymbol` указывается нужный символ валюты.
```tsx
return (
);
```
**ExampleFractionDigits**: Проп `fractionDigits` устанавливает минимальное количество знаков после запятой.
```tsx
return ;
```
**ExampleHideTrailingZeros**: Проп `hideTrailingZeros` удаляет лишние нули, если они есть в конце значения.
```tsx
return ;
```
---
## DateInput
```jsx
import { DateInput } from '@skbkontur/react-ui';
```
### Props
- **autoFocus?**: Устанавливает фокус на контроле после окончания загрузки страницы.
- **value?**: Устанавливает значение датаинпута.
- **error?**: Переводит контрол в состояние валидации "ошибка".
- **warning?**: Переводит контрол в состояние валидации "предупреждение".
- **disabled?**: Делает компонент недоступным.
- **minDate?**: Задает минимальную возможную дату в формате `dd.mm.yyyy`.
- **maxDate?**: Задает максимальную возможную дату в формате `dd.mm.yyyy`
- **width?**: Задает ширину поля.
- **withIcon?**: Добавляет иконку календаря.
- **size?**: Задает размер поля.
- **onBlur?**: Задает функцию, которая вызывается при потере датаинпутом фокуса.
- **onClick?**: Задает функцию, которая вызывается при клике на датаинпут.
- **onFocus?**: Задает функцию, которая вызывается при получении датаинпутом фокуса.
- **onValueChange?**: Задает функцию, которая вызывается при изменении value.
- **onKeyDown?**: Задает функцию, которая вызывается при нажатии кнопки на клавиатуре.
### Examples
```tsx
return ;
tsx
const [value, setValue] = React.useState();
return ;
tsx
return ;
tsx
class DateInputFormatting2 extends React.Component, { langCode: LangCodes; value: string }> {
constructor(props: Record) {
super(props);
this.state = {
langCode: LangCodes.ru_RU,
value: '21.12.2012',
};
}
render() {
return (
Локаль (LangCodes)
this.setState({ langCode })}
/>
this.setState({ value })} value={this.state.value} />
);
}
}
return ;
tsx
class DateInputFormatting extends React.Component<
Record,
{ order: DateOrder; separator: keyof typeof DateSeparator; value: string }
> {
constructor(props: Record) {
super(props);
this.state = {
order: DateOrder.YMD,
separator: 'Dot',
value: '21.12.2012',
};
}
render() {
return (
Порядок компонентов (DateOrder)
this.setState({ order })}
/>
Разделитель (DateSeparator)
this.setState({ separator })}
/>
this.setState({ value })} value={this.state.value} />
);
}
}
return ;
```
---
## DatePicker
```jsx
import { DatePicker } from '@skbkontur/react-ui';
```
### Props
- **autoFocus?**: Устанавливает фокус на контроле после окончания загрузки страницы.
- **disabled?**: Делает компонент недоступным.
- **enableTodayLink?**: Отображает кнопку "Сегодня" в календаре.
- **error?**: Переводит контрол в состояние валидации "ошибка".
- **menuPos?**: Задает nекущую позицию выпадающего окна вручную.
- **menuAlign?**: Задает выравнивание меню.
- **size?**: Задает размер контрола.
- **value?**: Строка формата `dd.mm.yyyy` Задает значение автокомплита.
- **warning?**: Переводит контрол в состояние валидации "предупреждение".
- **width?**: Задает ширину автокомплита.
- **onBlur?**: Задает функцию, которая вызывается при потере датапикером фокуса.
- **onValueChange**: Задает функцию, вызывающуюся при изменении value.
- **onFocus?**: Задает функцию, которая вызывается при получении датапикером фокуса.
- **onKeyDown?**: Задает функцию, которая вызывается при нажатии кнопки на клавиатуре.
- **onMouseEnter?**: Задает функцию, которая вызывается при наведении мышкой (событие `onmouseenter`). См разницу с onMouseOver в [документации](https://learn.javascript.ru/mousemove-mouseover-mouseout-mouseenter-mouseleave)
- **onMouseLeave?**: Задает функцию, которая вызывается при уходе мышки с объекта (событие `onmouseleave`).
- **onMouseOver?**: Задает функцию, которая вызывается при наведении мышкой (событие `onmouseover`).
- **useMobileNativeDatePicker?**: Позволяет использовать на мобильных устройствах нативный календарь для выбора дат. На iOS нативный календарь не умеет работать с minDate и maxDate.
### Examples
**Example1**: Пример с обработкой ошибок, когда пользователь ввел невалидную дату.
```tsx
const [value, setValue] = React.useState('');
const [error, setError] = React.useState(false);
const [tooltip, setTooltip] = React.useState(false);
const minDate = '22.12.2012';
const maxDate = '02.05.2018';
const unvalidate = () => {
setError(false);
setTooltip(false);
};
const validate = () => {
const errorNew = !!value && !DatePicker.validate(value, { minDate, maxDate });
setError(errorNew);
setTooltip(errorNew);
};
const removeTooltip = () => setTooltip(false);
return (
minDate = {minDate}
maxDate = {maxDate}
'Невалидная дата'} onCloseClick={removeTooltip}>
);
```
**Example2**: В компонент можно передать функцию `isHoliday`, которая будет получать день строкой формата `dd.mm.yyyy` и флаг `isWeekend`, и должна вернуть `true` для выходного и `false` для рабочего дня.
```tsx
const [value, setValue] = React.useState('');
const createRandomHolidays = () => {
const holidays = new Array(10);
const today = new Date();
for (let index = 0; index < holidays.length; index++) {
const day = new Date(today.setDate(today.getDate() + 1 + index).valueOf());
const holiday = {
date: day.getDate(),
month: day.getMonth(),
year: day.getFullYear(),
};
holidays[index] = DatePickerHelpers.formatDate(holiday);
}
return holidays;
};
const holidays = createRandomHolidays();
const isHoliday = (day: string, isWeekend: boolean) => {
if (holidays.includes(day)) {
return !isWeekend;
}
return isWeekend;
};
return ;
tsx
class DatePickerFormatting extends React.Component {
constructor(props: Record) {
super(props);
this.state = {
order: DateOrder.YMD,
separator: 'Dot',
value: '21.12.2012',
};
}
render() {
return (
Порядок компонентов (DateOrder)
this.setState({ order })}
/>
Разделитель (DateSeparator)
this.setState({ separator })}
/>
this.setState({ value })} value={this.state.value} />
);
}
}
return ;
```
**Example5**: Подробный пример в компоненте Calendar.
```tsx
const [value, setValue] = React.useState('12.05.2022');
const renderDay = (props: CalendarDayProps) => {
const [date] = props.date.split('.').map(Number);
const isEven = date % 2 === 0;
if (isEven) {
return ;
}
return ;
};
return ;
```
---
## DateRangePicker
```jsx
import { DateRangePicker } from '@skbkontur/react-ui';
```
### Props
- **menuAnchorElement?**: Элемент относительно которого открывается календарь, Если передать значение `focused` - меню будет открываться у зафокусированного элемента. Если передать ссылку на DOM элемент или ref - меню откроется относительно переданного элемента.
- **children**: Элементы DateRangePicker: ` ` ` ` ` `
### Usage
Компонент выбора периода состоит из 3-х дочерних компонентов:
- `` — начало периода с настройками как у `DateInput`
- `` — окончание периода с настройками как у `DateInput`
- `` — разделитель между полями
Значения задается через пропсы у компонентов `Start` и `End`:
- 2 значения указываются в пропах `value={'dd.mm.yyyy'}` (формат настраивается)
- Пустыми значениями считаются `""`, `null` и `undefined`
- Минимальная дата задаётся в `Start` через `minDate`, максимальная — в `End` через `maxDate`
## Адаптивность
На мобильных устройствах доступны 2 варианта отображения:
- Адаптивная версия в попапе (по умолчанию)
- 2 нативных календаря через `useMobileNativeDatePicker`. Ограничение: [календарь в iOS не поддерживает параметры min и max](https://bugs.webkit.org/show_bug.cgi?id=225639)
## Локали по умолчанию
```typescript static
interface DateRangePickerLocale {
today?: string;
months?: string[];
order?: DateOrder;
separator?: DateSeparator;
todayAriaLabel?: string;
selectMonthAriaLabel?: string;
selectYearAriaLabel?: string;
selectChosenAriaLabel?: string;
dayCellChooseDateAriaLabel?: string;
startDateLabel?: string;
endDateLabel?: string;
startDateEmpty?: string;
endDateEmpty?: string;
}
const ru_RU = {
today: 'Сегодня',
months: [
'Январь',
'Февраль',
'Март',
'Апрель',
'Май',
'Июнь',
'Июль',
'Август',
'Сентябрь',
'Октябрь',
'Ноябрь',
'Декабрь',
],
order: DateOrder.DMY,
separator: DateSeparator.Dot,
todayAriaLabel: 'Перейти к сегодняшней дате',
selectMonthAriaLabel: 'месяц',
selectYearAriaLabel: 'год',
selectChosenAriaLabel: 'Выбранный',
dayCellChooseDateAriaLabel: 'Выбрать дату',
startDateLabel: 'Дата начала',
endDateLabel: 'Дата окончания',
startDateEmpty: 'Без первой даты',
endDateEmpty: 'Без второй даты',
};
const en_GB = {
today: 'Endday',
months: [
'January',
'February',
'March',
'April',
'May',
'June',
'July',
'August',
'September',
'October',
'November',
'December',
],
order: DateOrder.MDY,
separator: DateSeparator.Slash,
todayAriaLabel: "Go end today's date",
selectMonthAriaLabel: 'month',
selectYearAriaLabel: 'year',
selectChosenAriaLabel: 'Chosen',
dayCellChooseDateAriaLabel: 'Choose date',
startDateLabel: 'Start date',
endDateLabel: 'End date',
startDateEmpty: 'No start date',
endDateEmpty: 'No end date',
};
```
### Examples
**ExamplePrices**: Для валидаций используйте `DateRangePicker.validate(startValue, endValue, options)`, который принимает: - `startValue` и `endValue` — проверяемые значения `'dd.mm.yyyy'` - `options` — объект с настройками `{ startOptional, endOptional, minDate, maxDate }` Возвращается валидация полей `Start` и `End` в формате `[true, false]` / export const Validations = ValidationsStory; Validations.storyName = 'Валидации'; /** Пример с кастомизацией темы и кастомным рендером дня
```tsx
const theme = React.useContext(ThemeContext);
function renderDay(props: CalendarDayProps) {
const [date, month] = props.date.split('.').map(Number);
const randomDay = date % 6 === 0 || date % 7 === 0 || date % 8 === 0;
const randomPrice = Math.round((date / month) * 1000);
return (
{date}
{randomDay ? <>{randomPrice} ₽> : — }
);
}
const [valueStart, setValueStart] = React.useState('');
const [valueEnd, setValueEnd] = React.useState('');
const minDate = '08.07.2024';
const maxDate = '18.08.2024';
return (
);
```
**ExampleCustomOptional**: Для полей достпен параметр `optional`, чтобы указывать открытые диапазоны
```tsx
const [valueStart, setValueStart] = React.useState('');
const [valueEnd, setValueEnd] = React.useState('');
return (
);
tsx
const [valueStart, setValueStart] = React.useState('');
const [valueEnd, setValueEnd] = React.useState('');
const [order, setOrder] = React.useState(DateOrder.YMD);
const [separator, setSeparator] = React.useState(
Object.keys(DateSeparator)[0] as keyof typeof DateSeparator,
);
return (
Порядок компонентов (DateOrder )
setOrder(order)} />
Разделитель (DateSeparator )
setSeparator(separator)}
/>
);
tsx
const customRef = React.createRef();
const [valueStart, setValueStart] = React.useState('');
const [valueEnd, setValueEnd] = React.useState('');
return (
menuAnchorElement="focused" меню для выбора даты будет открываться у зафокусированного элемента
customRef
menuAnchorElement="customRef" меню для выбора даты будет открываться у элемента "customRef"
);
```
---
## Dropdown
```jsx
import { Dropdown } from '@skbkontur/react-ui';
```
### Props
- **caption**: Текст кнопки-меню.
- **icon?**: Добавляет иконку слева от текста кнопки.
- **width?**: Ширина кнопки-меню. Если `menuWidth` не задан, такая же минимальная ширина применяется к раскрывающемуся меню.
- **disablePortal?**: Отключает использование портала.
- **disabled?**: Блокирует компонент.
- **error?**: Показывает состояние ошибки.
- **warning?**: Показывает состояние предупреждения.
- **maxMenuHeight?**: Ограничивает максимальную высоту раскрывающегося меню.
- **menuPos?**: Фиксирует положение раскрывающегося меню относительно кнопки-меню.
- **menuAlign?**: Выравнивает раскрывающееся меню относительно кнопки-меню.
- **menuWidth?**: Ширина раскрывающегося меню.
- **size?**: Размер кнопки-меню.
- **use?**: Визуальный стиль кнопки-меню.
- **onClose?**: Вызывается при закрытии раскрывающегося меню.
- **onOpen?**: Вызывается при открытии раскрывающегося меню.
- **onMouseEnter?**: Вызывается при наведении курсора (событие `onmouseenter`). Разницу с `onMouseOver` смотрите в [документации](https://learn.javascript.ru/mousemove-mouseover-mouseout-mouseenter-mouseleave).
- **onMouseLeave?**: Вызывается при уходе курсора с элемента (событие `onmouseleave`).
- **onMouseOver?**: Вызывается при движении курсора над элементом (событие `onmouseover`).
### Usage
```jsx
import { Dropdown } from '@skbkontur/react-ui';
```
Кнопка-меню `Dropdown` объединяет действия и открывает раскрывающееся меню.
## Использование
Используйте кнопку-меню:
- когда не хватает места для нескольких кнопок;
- когда названия действий очень длинные;
- когда действия редко используются или объединены по смыслу.
## Альтернативы и дополнения
- [DropdownMenu](https://tech.skbkontur.ru/kontur-ui/?path=/docs/react-ui_menu-dropdownmenu--docs) — выбирайте, когда нужен более гибкий сценарий:
произвольный `caption` (в том числе render-function), кастомные `header`/`footer`, управление `positions`.
- [Select](https://tech.skbkontur.ru/kontur-ui/?path=/docs/react-ui_input-data-select--docs) — используйте для выбора значения из набора вариантов.
## Доступность
Компонент поддерживает aria-атрибуты, если вам необходимо переопределить стандартное поведение.
Рекомендации:
- задавайте понятный текст в `caption`, чтобы действие кнопки-меню было очевидно;
- если подпись недостаточно описательна, добавляйте `aria-label`;
- контролируйте фокус после открытия/закрытия меню в сложных сценариях;
- для полного отключения используйте проп `disabled`: компонент становится недоступен для взаимодействия и клавиатурной навигации;
- если важно сохранить элемент в потоке озвучивания скринридером, используйте `aria-disabled="true"` и блокируйте действия логически.
ℹ️ **Полезно:** [Чек-лист доступности](https://tech.skbkontur.ru/kontur-ui/?path=/docs/accessibility--docs)
## Адаптивность
`Dropdown` адаптивен: на мобильных устройствах раскрывающееся меню открывается в мобильном попапе (`MobilePopup`). Поведение включается настройками адаптивности React UI.
Если нужно настроить пороги переключения между десктопным и мобильным режимами, используйте настройки из раздела [Адаптивность компонентов](https://tech.skbkontur.ru/kontur-ui/?path=/docs/react-ui_information-mobiles--docs).
### Examples
**ExampleBasic**: Базовый пример с подписью и элементами меню в `children`.
```tsx
return (
Действия
Пункт 1
Пункт 2
Пункт 3
);
```
**ExampleSize**: Проп `size` задаёт размер кнопки-меню.
```tsx
return (
Small 1
Small 2
Medium 1
Medium 2
Large 1
Large 2
);
```
**ExampleUse**: Проп `use` задаёт визуальный стиль кнопки-меню. Доступные стили соответствуют кнопке, подробнее в [Button](https://tech.skbkontur.ru/kontur-ui/?path=/docs/react-ui_button-button--docs#%D1%81%D1%82%D0%B8%D0%BB%D1%8C).
```tsx
return (
Default 1
Default 2
Primary 1
Primary 2
Danger 1
Danger 2
);
```
**ExampleIcon**: Проп `icon` добавляет иконку слева от подписи кнопки.
```tsx
return (
}>
Icon 1
Icon 2
);
```
**ExampleDisabled**: Проп `disabled` переводит компонент в недоступное состояние.
```tsx
return (
Disabled 1
Disabled 2
);
```
**ExampleError**: Проп `error` показывает состояние ошибки.
```tsx
return (
Error 1
Error 2
);
```
**ExampleWarning**: Проп `warning` показывает состояние предупреждения.
```tsx
return (
Warning 1
Warning 2
);
```
**ExampleWidth**: Проп `width` задаёт ширину кнопки-меню. Может быть в пикселях, процентах или других конкретных единицах. Заданная ширина применяется к полю и выпадающему списку.
```tsx
return (
Width 1
Width 2
Width 1
Width 2
);
```
**ExampleMenuWidth**: Проп `menuWidth` задаёт ширину выпадающего меню.
```tsx
return (
MenuWidth 1
MenuWidth 2
MenuWidth 1
MenuWidth 2
);
```
**ExampleMaxMenuHeight**: Проп `maxMenuHeight` ограничивает максимальную высоту меню.
```tsx
return (
Пункт 1
Пункт 2
Пункт 3
Пункт 4
Пункт 5
Пункт 6
Пункт 7
Пункт 8
);
```
**ExampleMenuPos**: Проп `menuPos` фиксирует расположение выпадающего меню. Оно может быть под полем — `"bottom"` или над полем — `"top"`. По умолчанию меню раскрывается под полем, а если меню находится близко к нижней границе страницы, то оно динамически меняет расположение и раскрывается над полем.
```tsx
return (
Bottom 1
Bottom 2
Top 1
Top 2
);
```
**ExampleMenuAlign**: Проп `menuAlign` выравнивает выпадающий список. Выпадающий список может быть прикреплен к левому краю — `"left"` или к правому — `"right"`.
```tsx
return (
Left 1
Left 2
Right 1
Right 2
);
```
**ExampleDisablePortal**: Проп `disablePortal` отключает рендер через портал.
```tsx
return (
Portal 1
Portal 2
);
```
**ExampleOpenCloseCallbacks**: Коллбеки `onOpen` и `onClose` вызываются при открытии и закрытии меню.
```tsx
const [status, setStatus] = React.useState('Закрыто');
return (
setStatus('Открыто')} onClose={() => setStatus('Закрыто')}>
Callbacks 1
Callbacks 2
{`Статус меню: ${status}`}
);
```
**ExampleRefMethods**: Публичные методы `open()` и `close()` доступны через `ref`.
```tsx
const dropdownRef = React.useRef(null);
return (
dropdownRef.current?.open()}>
Открыть через ref
dropdownRef.current?.close()}>
Закрыть через ref
Ref 1
Ref 2
);
```
---
## DropdownMenu
```jsx
import { DropdownMenu } from '@skbkontur/react-ui';
```
### Props
- **menuMaxHeight?**: Максимальная высота меню
- **menuWidth?**: Ширина меню
- **width?**: Ширина caption
- **caption**: Элемент или функция возвращающая элемент, если передана, используется вместо `caption`, в таком случае управлять открытием и закрытием меню придется в этой функции
- **header?**: Произвольный элемент, который будет отрендерен в шапке меню. _Примечание_: контрол MenuHeader передаётся только в `children` меню-контролов. Не стоит передавать `MenuHeader` в `header`.
- **footer?**: Произвольный элемент, который будет отрендерен в подвале меню. Перед элементом переданным в `footer` будет отрендерен MenuSeparator.
- **positions?**: Список позиций доступных для расположения выпадашки относительно `caption`. Если во всех позициях выпадашка вылезает за пределы `viewport`, будет использована первая из этого списка. **Возможные значения**: `top left`, `top center`, `top right`, `right top`, `right middle`, `right bottom`, `bottom left`, `bottom center`, `bottom right`, `left top`, `left middle`, `left bottom` (default: `['bottom left', 'bottom right', 'top left', 'top right']`)
- **disableAnimations?**: Не показывать анимацию
### Examples
```tsx
return (
Открыть меню}>
Заголовок меню
Раз
Два
Три
Раз
Два
Три
);
tsx
return (
Открыть меню c заданной шириной} menuWidth={350}>
Заголовок меню
Раз
Два
Три
Раз
Два
Три
);
tsx
return (
Открыть меню c заданной высотой} menuMaxHeight={150}>
Заголовок меню
Раз
Два
Три
Раз
Два
Три
);
tsx
return (
Открыть меню}>
Заголовок меню
Раз
Два
Три
Раз
Два
Три
);
tsx
return (
Это шапка в виде обычного текста
}
footer={А это подвал в виде кнопки }
caption={Открыть меню }
>
Раз
Два
Три
);
tsx
return (
Открыть меню}>
MenuHeader
}>MenuItem1
}>MenuItem2
MenuItem3
);
tsx
return (
Открыть меню}>
MenuHeader
}>MenuItem1
}>MenuItem2
MenuItem3
);
tsx
return (
Открыть меню без анимации}>
Заголовок меню
Раз
Два
Три
);
```
**Example9**: В `caption` можно передать любой элемент.
```tsx
return (
}
menuWidth="300px"
>
Раз
Два
Три
);
tsx
const [checked, setChecked] = React.useState(false);
let close: () => void;
const renderCaption = ({ openMenu, closeMenu }: { openMenu: () => void; closeMenu: () => void }) => {
close = closeMenu;
return (
Открыть меню
);
};
return (
e.preventDefault()}>Просто пункт
{
e.preventDefault();
setChecked(!checked);
}}
>
с чекбоксом
{
e.preventDefault();
close();
}}
>
Закрыть
);
```
**Example11**: (с сохранением поведения MenuItem)
```tsx
const [showItems, setShowItems] = React.useState(false);
const hiddenItems = [
,
А я скрываюсь ,
И я ,
Я с вами ,
];
return (
setShowItems(!showItems)}>{showItems ? 'Спрятать' : 'Показать'} элементы
Открыть меню}>
Меня видно всегда
Меня тоже
Ага, и меня!
{showItems && hiddenItems}
);
```
---
## FileUploader
`FileUploader` — контрол для выбора пользователем файла на компьютере и отображения статуса его отправки на сервер. Можно использовать для синхронной отправки данных, например, в форме. Или же можно использовать в асинхронном режиме.
```jsx
import { FileUploader } from '@skbkontur/react-ui';
```
### Props
- **error?**: Переводит контрол в состояние валидации "ошибка". */ /** Состояние ошибки всего контрола
- **warning?**: Переводит контрол в состояние валидации "предупреждение"
- **validationTooltipPosition?**: Задает приоритетное расположение подсказки относительно контрола (default: `'top left'`)
- **withValidationTooltip?**: Использовать тултип для отображения валидации (default: `false`)
- **withWarningIcon?**: Использовать иконку для ворнинга (восклицательный знак) (default: `false`)
- **width?**: Задает длину компонента.
- **size?**: Задаёт размер контрола. (default: `small`)
- **hideFiles?**: Скрывает отображение файлов. (default: `false`)
- **uploaderText?**: Пользовательский текст для загрузки файла (default: `'Загрузить файл'`)
- **uploaderIcon?**: Пользовательская иконка для загрузки файла (default: `UploadIcon`)
- **view?**: Вид компонента - `row` — строчный вид - `tile` — плиточный вид (default: `row`)
- **validationSummary?**: Отображать ли саммари с детализацией ошибок. Работает с версией темы >= 5_5. - `auto` — саммари отображается, если количество загруженных файлов >= validationSummaryStart - `enabled` — всегда включено - `disabled` — всегда отключено (default: `auto`)
- **validationSummaryStart?**: Количество файлов, от которого показываем саммари (при validationSummary = `auto`) (default: `5`)
- **uploadButtonPosition?**: Позиционирование области загрузки файла (default: `start`)
- **request?**: Задает функцию, через которую отправляются файлы. Используется для отслеживания статуса загрузки файла.
- **onRequestSuccess?**: Задает функцию, которая вызывается при удачной попытке отправки через request.
- **onRequestError?**: Задает функцию, которая вызывается при неудачной попытке отправки через request.
- **validateBeforeUpload?**: Определяет функцию валидации каждого файла. Срабатывает после выбора файлов и перед попыткой отправить в request. Чтобы вывести валидацию ошибки, промис должен вернуть строку или объект с ошибкой.
- **renderFile?**: Задает функцию, которая позволяет кастомизировать файлы. Через нее можно вешать кастомные валидации на каждый файл. (default: `(props: FileUploaderFileProps) => `)
## Локали по умолчанию (см. [LocaleContext](https://tech.skbkontur.ru/kontur-ui/?path=/docs/react-ui_information-locale--localecontext))
Любой текст в контроле можно поменять через `LocaleContext`.
```typescript static
interface FileUploaderLocale {
chooseFile: string;
choosedFile: string;
orDragHere: string;
requestErrorText: string;
errors: string[];
warnings: string[];
}
const ru_RU = {
chooseFile: 'Выберите файл',
choosedFile: 'Выбран файл',
orDragHere: 'или перетащите сюда',
requestErrorText: 'Файл не удалось загрузить на сервер, повторите попытку позже',
errors: ['ошибка', 'ошибки', 'ошибок'],
warnings: ['предупреждение', 'предупреждения', 'предупреждений'],
};
const en_GB = {
chooseFile: 'Select a file',
choosedFile: 'File selected',
orDragHere: 'or drag here',
requestErrorText: 'The file could not be uploaded to the server, please try again later',
errors: ['error', 'errors', 'errors'],
warnings: ['warning', 'warnings', 'warnings'],
};
```
### Examples
```tsx
return ;
tsx
const request = () => Promise.resolve();
return ;
tsx
const request = () => Promise.reject();
return ;
tsx
const initialFiles = [createFile('test1.txt'), createFile('test2.txt')];
return ;
```
**Example5**: Для кастомизации отображения файлов можно использовать проп `renderFile`. Более подробные примеры кастомизации отображения файлов можно посмотреть на [странице](https://tech.skbkontur.ru/kontur-ui/?path=/docs/input-data-fileuploader-fileuploaderfile--docs) компонента `FileUploaderFile`.
```tsx
const initialFiles = [createFile('test1.txt'), createFile('test2.txt')];
return (
}
/>
);
tsx
return ;
tsx
return (
{
return Promise.resolve(`У файла ${originalFile.name} неверный формат`);
}}
/>
);
tsx
return (
{
return Promise.resolve(`У файла ${originalFile.name} неверный формат`);
}}
/>
);
```
**Example9**: Чтобы указать на ошибку загрузки файла на сервер, функция `request` должна вернуть `Promise` в состоянии `rejected`. Тогда рядом с файлом появится иконка ошибки. Проп `onRequestError` можно использовать для переключения состояния ошибки всей формы.
```tsx
const [error, setError] = React.useState(false);
const [showError, setShowError] = React.useState(false);
const request = () =>
new Promise((resolve, reject) => {
setTimeout(() => (showError ? reject() : resolve()), 1000);
});
const reject = () => setError(true);
return (
setError(false)} request={request} onRequestError={reject} />
Показывать ошибку загрузки
);
tsx
return ;
```
**Example11**: В компоненте есть возможность скрыть дефолтный список файлов и нарисовать свой, используя пропы `hideFiles`, `onAttach`, `onRemove` или `onValueChange`. Если требуется удалить файлы вручную, можно использовать метод `removeFile` из `ref`. При его вызове автоматически вызываются колбэки `onValueChange` и `onRemove`.
```tsx
const fileUploaderRef = React.useRef(null);
const [fileList, setFileList] = React.useState([]);
return (
setFileList(files)} />
{fileList.map((file) => {
return (
fileUploaderRef.current?.removeFile(file.id)}>
Delete file {file.originalFile.name}
);
})}
);
```
**Example14**: Саммари будет показано автоматически, если загружено больше 5 файлов (по умолчанию). Работает с версией темы >= 5_5. Попробуй загрузить несколько файлов, в примере уже дописан обработчик на валидацию файлов.
```tsx
function containsCyrillicLetters(str: string): boolean {
// Регулярное выражение для проверки наличия символов кириллицы
const regex = /[\u0400-\u04FF]/;
return regex.test(str);
}
function containsDigits(str: string): boolean {
// Регулярное выражение для проверки наличия цифр
const regex = /\d/;
return regex.test(str);
}
const validateBeforeUpload = (file: FileUploaderAttachedFile) => {
let status = FileUploaderFileStatus.Uploaded;
let message = '';
const { name } = file.originalFile;
if (containsCyrillicLetters(name)) {
status = FileUploaderFileStatus.Error;
message = 'Имя файла содержит кириллицу';
} else if (containsDigits(name)) {
status = FileUploaderFileStatus.Warning;
message = 'Имя файла содержит цифры';
}
return Promise.resolve(status !== FileUploaderFileStatus.Uploaded ? { message, status } : message);
};
return (
);
```
**Customization**: Обновленное отображение плиткой. При мультизагрузке можно настроить позиционирование для области загрузки файла - добавить на старт или в конец / export const Example15 = () => { const initialFiles = [createFile('test1.txt'), createFile('test2.txt')]; return ; }; Example15.storyName = 'Отображение файлов плиткой'; /** Внешний вид контрола, в том числе цвет текстов, можно настроить через `ThemeContext`. Заменить текст можно через `uploaderText`, а иконку через `uploaderIcon`
```tsx
const initialFiles = [createFile('test1.txt'), createFile('test2.txt')];
return (
);
```
---
## FxInput
```jsx
import { FxInput } from '@skbkontur/react-ui';
```
### Props
- **auto?**: Управляет видимостью кнопки Restore: - true — кнопка Restore не отображается. Значение в поле является автоматически рассчитанным. - false — кнопка Restore отображается в поле. Значение в поле считается отредактированным.
- **type?**: Тип поля.
- **onRestore?**: Событие нажатия на кнопку Restore.
- **onValueChange**: Событие изменения value.
- **value?**: Значение поля.
- **refInput?**: Задаёт ref поля.
- **hideTrailingZeros?**: Убирает лишние нули после запятой.
- **buttonAriaLabel?**: Атрибут aria-label кнопке Restore.
### Usage
```jsx
import { FxInput } from '@skbkontur/react-ui';
```
## Использование
Компонент наследует часть базовых пропcов (размер, ширина, количество символов до/после запятой и т.д.) от компонентов Input и CurrencyInput, они включены в таблицу пропсов .
Примеры для базовых пропсов вы можете посмотреть на странице [Input](https://tech.skbkontur.ru/kontur-ui/?path=/docs/react-ui_input-data-input--docs) и [CurrencyInput](https://tech.skbkontur.ru/kontur-ui/?path=/docs/react-ui_input-data-currencyinput--docs).
Пропсы для добавления маски в поле наследуются от [MaskedInput](https://tech.skbkontur.ru/kontur-ui/?path=/docs/react-ui_input-data-maskedinput--docs).
## Доступность
Для компонента действуют все те же рекомендации по доступности, что и для компонента [Input](https://tech.skbkontur.ru/kontur-ui/?path=/docs/react-ui_input-data-input--docs).
## Адаптивность
По умолчанию поле ввода не меняет свой вид и поведение на мобильных устройствах.
ℹ️ Полезно: [Адаптивность компонентов](https://tech.skbkontur.ru/kontur-ui/?path=/docs/react-ui_information-mobiles--docs)
### Examples
```tsx
const FxValue = 100500;
const [auto, setAuto] = React.useState(true);
const [value, setValue] = React.useState(FxValue);
function handleValueChange(value: number) {
setAuto(false);
setValue(value);
}
function handleRestore() {
setAuto(true);
setValue(FxValue);
}
return ;
```
**ExampleRestore**: У компонента нет заложенной по умолчанию логики по нажатию на кнопку Restore, задайте её самостоятельно. На видимость кнопки Restore влияет проп `auto`. Если передано: - `true` — кнопка Restore не отображается. Значение в поле считается автоматически рассчитанным. - `false` — кнопка Restore отображается в поле. Значение в поле считается отредактированным. Вернуть автоматически рассчитанное значение можно в обработчике `onRestore` после нажатия на кнопку. Чтобы кнопка Restore пропала после нажатия, верните проп `auto` в значение `true`.
```tsx
const [auto, setAuto] = React.useState(true);
const [value, setValue] = React.useState('');
function handleValueChange(value: number | string) {
setAuto(false);
setValue(value);
}
function handleRestore() {
setAuto(true);
setValue('');
}
return ;
```
**ExampleClear**: Очистить значение в автополе можно с помощью пустой строки или `undefined`.
```tsx
const [value, setValue] = React.useState(12345);
return (
setValue(undefined)}>Передать undefined
setValue('')}>Передать пустое значение
);
```
**ExampleCurrency**: Знак валюты, процент или другие единицы измерения можно прокидывать как внутрь поля с помощью пропа `rightIcon`, так и вне поля с помощью обычного `label`.
```tsx
const [value, setValue] = React.useState('100500');
return (
₽
);
```
**ExampleMask**: Проп `mask` задаёт маску для поля.
```tsx
const [auto, setAuto] = React.useState(true);
const [value, setValue] = React.useState('');
function handleValueChange(value: number) {
setAuto(false);
setValue(value);
}
function handleRestore() {
setAuto(true);
setValue('');
}
return (
);
```
---
## Gapped
```jsx
import { Gapped } from '@skbkontur/react-ui';
```
### Props
- **gap?**: Задает расстояние между элементами в пикселях.
- **verticalAlign?**: Задает вертикальное выравнивание.
- **vertical?**: Располагает элементы по вертикали.
- **wrap?**: Переносит элементы на новую строку при горизонтальном расположении.
### Examples
```tsx
return (
Сохранить
Отмена
);
tsx
return (
Write todos
Work in progress
Make things done
);
```
**Example3**: По умолчанию `Gapped` выстраивает элементы в одну строчку, но с помощью свойства `wrap` можно включить перенос элементов на новую строку. При этом между строчками будет отступ равный значению свойста `gap` В таком случае из-за особенности верстки `Gapped` может перекрывать элементы расположенные сверху-слева.
```tsx
return (
<>
{"U Can't Touch This! => "}
Сохранить
Отмена
Я не робот
>
);
```
---
## GlobalLoader
```jsx
import { GlobalLoader } from '@skbkontur/react-ui';
```
### Props
- **delayBeforeShow?**: Задержка до появления лоадера в миллисекундах.
- **delayBeforeHide?**: Задержка до исчезновения лоадера в миллисекундах.
- **expectedResponseTime?**: Ожидаемое время(ms) ответа сервера.
- **rejected?**: Показывать лоадер в виде бегающей полоски.
- **active?**: Показывать лоадер.
- **disableAnimations?**: Отключить анимацию.
### Usage
```jsx
import { GlobalLoader } from '@skbkontur/react-ui';
```
## Использование
Используйте глобальный лоадер для индикации длительных операций обмена данными с сервером, таких как загрузка данных, сохранение формы, отправка запросов и т.д.
**Когда использовать:**
- Для индикации загрузки данных с сервера.
- Для отображения прогресса выполнения длительных операций.
- Для информирования пользователя о состоянии соединения с сервером.
**Когда не использовать:**
- Для локальных операций, которые выполняются мгновенно.
- Для индикации загрузки отдельных элементов интерфейса. В этом случае используйте ['Loader'](https://tech.skbkontur.ru/kontur-ui/?path=/docs/react-ui_display-data-loader--docs).
- Если в приложении уже есть другой глобальный индикатор загрузки.
### {GlobalLoaderStories.ExampleStaticMethods.storyName}
У компонента `GlobalLoader` есть набор статических методов: `GlobalLoader.start()`, `GlobalLoader.done()`, `GlobalLoader.reject()`, `GlobalLoader.accept()`.
Запустить лоадер ("start") — активирует анимацию загрузки
Завершить ("done") — переведёт глобальный лоадер в успешное завершённое состояние
Эмулировать проблему ("reject") — переведёт глобальный лоадер в состояние с ошибкой
Вернуться в состояние загрузки ("accept") — запустит глобальный лоадер с момента остановки при ошибке
### {GlobalLoaderStories.ExampleMount.storyName}
Вместо статических методов можно воспользоваться управлением через пропсы.
### {GlobalLoaderStories.ExampleInModal.storyName}
Глобальный лоадер можно запустить в модальном окне. Тогда он перекроет вуаль.
### {GlobalLoaderStories.ExampleCustomizeColor.storyName}
Через переменные темы можно изменять параметры глобального лоадера. Например, цвет индикатора загрузки.
## Доступность
Компонент поддерживает aria-атрибуты, если вам необходимо переопределить стандартное поведение.
Глобальный лоадер визуально отображает состояние загрузки, но для пользователей скринридеров рекомендуется дополнительно информировать о состоянии операции через регионы `aria-live` или другие доступные механизмы.
**Рекомендации:**
- Используйте `aria-live="polite"` или `aria-live="assertive"` для объявления изменений состояния загрузки.
- Предоставляйте текстовые альтернативы для индикации состояния операции.
- Убедитесь, что пользователи могут понять, что происходит загрузка, даже без визуального индикатора.
ℹ️ **Полезно:** [Чек-лист доступности](https://tech.skbkontur.ru/kontur-ui/?path=/docs/accessibility--docs)
## Адаптивность
По умолчанию глобальный лоадер не меняет свой вид и поведение на мобильных устройствах. Лоадер отображается у верхней границы экрана независимо от размера вьюпорта.
ℹ️ **Полезно:** [Адаптивность компонентов](https://tech.skbkontur.ru/kontur-ui/?path=/docs/react-ui_information-mobiles--docs)
### Examples
```tsx
return (
GlobalLoader.start()} use="primary">
Запустить лоадер
GlobalLoader.done()}>Завершить
);
tsx
return (
GlobalLoader.start()} use="primary">
Запустить лоадер
GlobalLoader.done()}>Завершить
GlobalLoader.reject()}>Эмулировать проблему
GlobalLoader.accept()}>Вернуться в состояние загрузки
);
tsx
const [manually, setManually] = React.useState(false);
const [active, setActive] = React.useState(false);
const [error, setError] = React.useState(false);
const reset = () => {
if (manually) {
setManually(false);
setError(false);
setActive(false);
} else {
setManually(true);
}
};
return (
Активировать управление пропсами
Запустить лоадер
Эмулировать ошибку
{manually && (
console.log('start')}
onDone={() => console.log('done')}
onReject={() => console.log('reject')}
onAccept={() => console.log('accept')}
/>
)}
);
tsx
const [opened, setOpened] = React.useState(false);
function renderModal() {
return (
Заголовок
GlobalLoader.start()} use="primary">
Запустить лоадер
GlobalLoader.done()}>Завершить
);
}
function open() {
setOpened(true);
}
function close() {
setOpened(false);
}
return (
{opened && renderModal()}
Открыть модальное окно
);
tsx
const [enableTheme, setEnableTheme] = React.useState(false);
const [active, setActive] = React.useState(false);
const globalLoaderCustomTheme = ThemeFactory.create(
enableTheme
? {
globalLoaderColor: 'cyan',
}
: {},
);
return (
Изменить цвет индикатора загрузки на 'cyan'
setActive(true)} use="primary">
Запустить лоадер
setActive(false)}>Завершить
);
```
---
## Group
```jsx
import { Group } from '@skbkontur/react-ui';
```
### Props
- **width?**: Задает длину компонента Group.
### Examples
```tsx
const [value, setValue] = React.useState('Foo');
return (
Foo
);
```
**UsageInDateRangePicker**: Пример с группой полей без разделителя в `DateRangePicker`.
```tsx
const [valueStart, setValueStart] = React.useState('');
const [valueEnd, setValueEnd] = React.useState('');
return (
);
tsx
const [value, setValue] = React.useState('Foo');
return (
'Hi!'} trigger="opened" pos="bottom">
Hover me
);
```
---
## Hint
```jsx
import { Hint } from '@skbkontur/react-ui';
```
### Props
- **manual?**: Переводит отображение подсказки в _"ручной режим"_. В _"ручном режиме"_ подсказку можно активировать только задав значение пропу `opened`.
- **maxWidth?**: Задает максимальную ширину подсказки.
- **onMouseEnter?**: Задает функцию, которая вызывается при наведении мышкой (событие `onmouseenter`).
- **onMouseLeave?**: Задает функцию, которая вызывается при уходе мышки с объекта (событие `onmouseleave`).
- **opened?**: Открывает подсказку. Работает только при `manual=true`.
- **pos?**: Задает приоритетное расположение подсказки относительно текста.
- **text**: Задает текст подсказки.
- **allowedPositions?**: Задает список позиций, которые будет занимать хинт. Если положение хинта в определенной позиции будет выходить за край экрана, то будет выбрана следующая позиция. Обязательно должен включать позицию указанную в `pos`.
- **disableAnimations?**: Отключает анимацию.
- **useWrapper?**: Явно указывает, что вложенные элементы должны быть обёрнуты в ` `. Используется для корректного позиционирования хинта при двух и более вложенных элементах. _Примечание_: при **двух и более** вложенных элементах обёртка будет добавлена автоматически.
### Examples
```tsx
return Базовая ;
tsx
return (
);
tsx
return (
Всегда всплывает слева
);
tsx
return (
Очень непонятный элемент
);
tsx
const [isOpen, setIsOpen] = React.useState(false);
return (
setIsOpen(true)} onBlur={() => setIsOpen(false)}>
{isOpen ? 'Закрыть подсказку' : 'Открыть подсказку'}
);
tsx
return (
Есть анимация?
);
```
**Example7**: Подсказка должна отображаться даже на отключённых компонентах. Из коробки это работает только с контролами `react-ui`. Нативные элементы, поддерживающие атрибут `disabled`, отключают необходимые события мыши. В подобных случаях следуют использовать проп `useWrapper`:
```tsx
return (
native button
);
```
**Example8**: Т.к. встроённая обёртка это `` без стилей, то она может работать некорректно в определённых ситуациях. В таких случаях стоит использовать собственную обётку:
```tsx
return (
<>
useWrapper prop
custom wrapper
>
);
```
---
## Input
```jsx
import { Input } from '@skbkontur/react-ui';
```
### Props
- **showClearIcon?**: Показывает иконку очистки значения в заполненном поле. (default: `never`)
- **leftIcon?**: Добавляет иконку слева. При использовании `ReactNode` применяются дефолтные стили для иконки. При использовании `() => ReactNode` применяются только стили для позиционирования.
- **rightIcon?**: Добавляет иконку справа. При использовании `ReactNode` применяются дефолтные стили для иконки. При использовании `() => ReactNode` применяются только стили для позиционирования.
- **error?**: Меняет визуальное отображение поля на состояние «ошибка». Может быть полезен при разработке собственной валидации, если вы не используете пакет [React UI Validations](https://tech.skbkontur.ru/kontur-ui/?path=/docs/react-ui-validations_displaying-getting-started--docs).
- **warning?**: Меняет визуальное отображение поля на состояние «предупреждение». Может быть полезен при разработке собственной валидации, если вы не используете пакет [React UI Validations](https://tech.skbkontur.ru/kontur-ui/?path=/docs/react-ui-validations_displaying-getting-started--docs).
- **borderless?**: Убирает обводку поля.
- **align?**: Выравнивает контент внутри поля.
- **size?**: Размер поля. (default: `small`)
- **onValueChange?**: Событие изменения значения `value` в поле.
- **type?**: Тип поля ввода.
- **value?**: Значение внутри поля.
- **prefix?**: Устанавливает префикс `ReactNode` перед значением, но после иконки.
- **suffix?**: Устанавливает суффикс `ReactNode` после значения, но перед правой иконкой.
- **selectAllOnFocus?**: Выделяет введённое значение при фокусе в поле. Работает с типами `text`, `password`, `tel`, `search`, `url`.
- **onUnexpectedInput?**: Устанавливает обработчик на случай некорректного ввода. Если передан onUnexpectedInput, он будет вызван при ошибке, а эффект мигания можно запустить вручную через публичный метод blink.
- **element?**: Устанавливает элемент, заменяющий нативный input. Должен иметь пропсы `InputElementProps` и тип `InputElement`.
### Usage
```jsx
import { Input } from '@skbkontur/react-ui';
```
## Использование
Используйте поле ввода для коротких текстовых или цифровых значений без предсказуемого формата — не больше 5 слов.
По умолчанию поле ввода принимает все стандартные атрибуты, которые доступны для обычного [HTMLInputElement](https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Elements/input).
**Альтернативы и дополнения**
Для полей с длинными текстовыми комментариями используйте [Textarea](https://tech.skbkontur.ru/kontur-ui/?path=/docs/react-ui_input-data-textarea--docs).
Для ввода значений в определенном формате, используйте специальную версию поля:
- [PasswordInput](https://tech.skbkontur.ru/kontur-ui/?path=/docs/react-ui_input-data-passwordinput--docs) — поле для ввода пароля, в котором символы заменяются на точки.
- [CurrencyInput](https://tech.skbkontur.ru/kontur-ui/?path=/docs/react-ui_input-data-currencyinput--docs) — поле для ввода денежных сумм в разной валюте.
- [MaskedInput](https://tech.skbkontur.ru/kontur-ui/?path=/docs/react-ui_input-data-maskedinput--docs) — поле с маской.
- [FxInput](https://tech.skbkontur.ru/kontur-ui/?path=/docs/react-ui_input-data-fxinput--docs) — автополе.
Если же список значений заранее определен и должен отображаться пользователю для выбора, подойдут поля с подсказками:
- [Autocomplete](https://tech.skbkontur.ru/kontur-ui/?path=/docs/react-ui_input-data-autocomplete--docs)
- [Combobox](https://tech.skbkontur.ru/kontur-ui/?path=/docs/react-ui_input-data-combobox--docs)
- [TokenInput](https://tech.skbkontur.ru/kontur-ui/?path=/docs/react-ui_input-data-tokeninput-tokeninput--docs)
## Доступность
Компонент поддерживает стандартные aria-атрибуты, если вам необходимо переопределить его поведение.
**Рекомендации**
- Указывайте `` для каждого поля формы. Если поле нельзя обернуть в ``, используйте атрибуты `htmlFor` и `id`:
```html
Имя Фамилия
```
- Если у поля ввода нет лейбла, задайте его при помощи `aria-label`:
```html
```
- Если нет возможности обернуть ` ` в ``, используйте `aria-labeledby`:
```html
Лейбл
```
- Для блокировки поля ввода используйте `aria-disabled`, а визуально и интерактивно блокируйте элемент при помощи стилей и JS. Скринридер остановится на этом элементе, тогда как атрибут `disabled` скринридер проигнорирует:
```html
```
## Валидация
С помощью пакета [React UI Validations](https://tech.skbkontur.ru/kontur-ui/?path=/docs/react-ui-validations_api-reference) можно добавить валидацию для компонента.
Подробнее о том, как настроить тип, уровень валидации, формат сообщения об ошибке и другие параметры поведения, смотрите в документации пакета [React UI Validations](https://tech.skbkontur.ru/kontur-ui/?path=/docs/react-ui-validations_displaying-getting-started--docs).
## Адаптивность
По умолчанию поле ввода не меняет свой вид и поведение на мобильных устройствах.
ℹ️ Полезно: [Адаптивность компонентов](https://tech.skbkontur.ru/kontur-ui/?path=/docs/react-ui_information-mobiles--docs)
### Examples
```tsx
return ;
```
**ExampleLabel**: Название к полю можно добавить через `label`. Для названия поля есть рекомендации по соблюдению доступности, изучите раздел Доступность .
```tsx
return (
Название поля
);
```
**ExampleSize**: Размер поля задаётся пропом `size`. По умолчанию `"small"`.
```tsx
return (
);
```
**ExampleWidth**: Ширину поля можно задать с помощью пропа `width`. Может принимать как абсолютные значения — например, 150, так и относительные — например, 50%.
```tsx
return (
);
```
**ExamplePlaceholder**: Добавить плейсхолдер можно через `placeholder`. Добавляет подсказку, которая отображается внутри поля, пока оно не заполнено.
```tsx
return ;
```
**ExampleAlign**: Выравнивание текста задаётся пропом `align`.
```tsx
const [valueLeft, setValueLeft] = React.useState('Left');
const [valueCenter, setValueCenter] = React.useState('Center');
const [valueRight, setValueRight] = React.useState('Right');
return (
);
```
**ExampleClear**: Очистить значение в поле можно только с помощью пустого значения.
```tsx
const [value, setValue] = React.useState('Нажмите кнопку');
return (
setValue('')}>Передать пустое значение
);
```
**ExampleIcon**: В поле ввода можно передать иконку. Иконка может находиться в начале поля — проп `leftIcon`, в конце — проп `rightIcon`. Под разный размер полей используйте подходящие начертания и размер иконок: - Small — Light 16 - Medium — Light 20 - Large — Regular 24
```tsx
return (
} />
} />
} />
} />
} />
} />
);
```
**ExampleShowClearIcon**: По умолчанию очистить поле ввода можно с помощью пустой строки. Дополнительно можно добавить проп `showClearIcon`, по нажатию на иконку очистки поле будет очищаться. По умолчанию `"never"`. Может отображаться в разных вариантах: - `"always"` — всегда показывает иконку очистки в заполненном поле; - `"auto"` — показывает иконку в заполненном поле, когда поле ввода в состоянии hover или focus; - `"never"` (по умолчанию) — не показывает. При одновременной настройке `rightIcon` и `showClearIcon` иконка очистки заменит иконку справа. В таких ситуациях не используйте для `rightIcon` интерактивную иконку, а значение для `showClearIcon` выбирайте `"auto"`.
```tsx
const [valueAlways, setValueAlways] = React.useState('showClearIcon="always"');
const [valueAuto, setValueAuto] = React.useState('showClearIcon="auto"');
const [valueNever, setValueNever] = React.useState('showClearIcon="never"');
const [valueWithIcon, setValueWithIcon] = React.useState('showClearIcon="auto" + rightIcon');
return (
}
/>
);
```
**ExampleSelectAllOnFocus**: Чтобы значение внутри поля выделялось при фокусе на нем, добавьте проп `selectAllOnFocus`. Может быть полезно для полей, в которых пользователи могут часто копировать значение, или для полей только для чтения.
```tsx
const [valueAlways, setValueAlways] = React.useState('Обычное поле');
const [valueAuto, setValueAuto] = React.useState('Поле с автовыделением');
return (
);
```
**ExamplePrefixSuffix**: Внутри поля можно отобразить префикс — через проп `prefix` или суфикс — `suffix`. Префикс отображает контент слева от вводимого текста, суфикс — справа.
```tsx
return (
} />
);
```
**ExampleBorderless**: Поле без обводки задаётся пропом `borderless`.
```tsx
const [valueAlways, setValueAlways] = React.useState('Обычное поле');
const [valueAuto, setValueAuto] = React.useState('Поле без обводки');
return (
);
```
**ExampleType**: Тип поля задаётся пропом `type`. По умолчанию `"text"`. Это стандартные типы поля ввода в HTML. Тип наделяет компонент нативными свойствами, может влиять на отображение подсказок, валидацию, автоматическое переключение раскладки клавиатуры на мобильных устройствах и другие свойства поведения. Подробнее смотрите в [Справке по HTML](https://developer.mozilla.org/ru/docs/Web/HTML/Reference/Elements/input#type). В сервисах Контура допустимо использовать только типы text и password, для остальных используйте компоненты из React UI.
```tsx
return (
type = "text"
type = "number"
type = "tel"
type = "search"
type = "time"
type = "date"
type = "url"
type = "email"
);
```
**ExampleOnUnexpectedInput**: Проп `onUnexpectedInput` устанавливает обработчик на случай некорректного ввода. В примере ошибка возникнет при попытке ввести больше 3 символов.
```tsx
const [unexpectedInput, setUnexpectedInput] = React.useState('');
return (
{unexpectedInput ? <>Некорректный ввод: {unexpectedInput}> : <>Данные в поле валидны>}
);
tsx
const maxLength = 15;
const [value, setValue] = React.useState('Очень длинная строка');
const theme = React.useContext(ThemeContext);
const charsRemaining = maxLength - value.length;
const suffixColor = charsRemaining >= 0 ? theme.gray : theme.errorText;
return (
setValue(e.target.value)}
suffix={
{String(charsRemaining).replace('-', '−')}
}
/>
);
```
---
## Kebab
```jsx
import { Kebab } from '@skbkontur/react-ui';
```
### Props
- **size?**: Размер кнопки.
- **positions?**: Список доступных позиций выпадающего меню относительно кнопки. (default: `['bottom left', 'bottom right', 'top left', 'top right'].`)
- **menuMaxHeight?**: Максимальная высота меню.
- **disabled?**: Блокирует кнопку.
- **disableAnimations?**: Отключает анимацию выпадающего меню.
- **icon?**: Заменяет иконку кебаба у кнопки.
### Usage
```jsx
import { Kebab } from '@skbkontur/react-ui';
```
## Использование
Используйте кебаб-меню, чтобы сэкономить место и скрыть малоиспользуемые ссылки и действия.
Не убирайте в кебаб-меню важные и часто используемые действия.
**Альтернативы и дополнения**
- [Dropdown](https://tech.skbkontur.ru/kontur-ui/?path=/docs/react-ui_menu-dropdown--docs) — если необходима кастомизируемая кнопка вместо простой иконки кебаба.
- [DropdownMenu](https://tech.skbkontur.ru/kontur-ui/?path=/docs/react-ui_menu-dropdownmenu--docs) — если необходим произвольный элемент `caption` вместо кнопки.
## Адаптивность
`Kebab` адаптивен: на мобильных устройствах выпадающее меню открывается модально по центру экрана. Сама кнопка не меняет внешний вид и поведение.
ℹ️ **Полезно:** [Адаптивность компонентов](https://tech.skbkontur.ru/kontur-ui/?path=/docs/react-ui_information-mobiles--docs)
### Examples
```tsx
return (
Действие 1
Действие 2
Действие 3
);
```
**ExampleSize**: Проп `size` задаёт размер кнопки. По умолчанию: `'small'`.
```tsx
return (
Действие 1
Действие 2
Действие 3
Действие 1
Действие 2
Действие 3
Действие 1
Действие 2
Действие 3
);
```
**ExamplePositions**: Проп `positions` задаёт список доступных позиций выпадающего меню относительно кнопки. По умолчанию: `['bottom left', 'bottom right', 'top left', 'top right']`. Если во всех позициях в списке выпадающее меню вылезает за пределы `viewport`, будет использована первая.
```tsx
return (
bottom left
Действие 1
Действие 2
Действие 3
top center
Действие 1
Действие 2
Действие 3
right bottom
Действие 1
Действие 2
Действие 3
);
```
**ExampleMaxHeight**: Пропом `menuMaxHeight` можно задать максимальную высоту выпадающего меню в пикселях.
```tsx
return (
Действие 1
Действие 2
Действие 3
Действие 4
Действие 5
Действие 6
Действие 7
Действие 8
Действие 9
);
```
**ExampleIconsOffset**: Проп `preventIconsOffset` отключает выравнивание текста пунктов меню относительно иконок в других пунктах.
```tsx
return (
preventIconsOffset="false"
Заголовок
}>Действие 1
Действие 2
Действие 3
preventIconsOffset="true"
Заголовок
}>Действие 1
Действие 2
Действие 3
);
```
**ExampleIcon**: Пропом `icon` можно задать свою иконку кнопке.
```tsx
return (
}>
Действие 1
Действие 2
Действие 3
);
```
**ExampleDisabled**: Проп `disabled` блокирует кнопку, делая её недоступной для нажатия.
```tsx
return (
Действие 1
Действие 2
Действие 3
);
```
**ExampleDisableAnimations**: Проп `disableAnimations` отключает анимацию выпадающего меню.
```tsx
return (
Действие 1
Действие 2
Действие 3
);
```
**ExampleOnOpenAndClose**: Коллбеки `onOpen` и `onClose` вызываются при открытии и закрытии кебаб-меню.
```tsx
const [isOpened, setIsOpened] = React.useState(false);
return (
Кебаб-меню {isOpened ? 'открыто' : 'закрыто'}.
setIsOpened(true)} onClose={() => setIsOpened(false)}>
Действие 1
Действие 2
Действие 3
);
```
---
## Link
```jsx
import { Link } from '@skbkontur/react-ui';
```
### Usage
```jsx
import { Link } from '@skbkontur/react-ui';
```
## Использование
Ссылка используется для навигации на другие разделы или внешние страницы.
Не используйте ссылку для действий на странице, для этого подойдёт [кнопка](https://tech.skbkontur.ru/kontur-ui/?path=/docs/react-ui_button-button--docs), в том числе кнопка в стиле Text, если нужна более легкая версия кнопки.
Если же вам нужно сохранить нативные свойства ссылки, но визуально она должна выглядеть как кнопка, вы можете переопределить корневой элемент ссылки, см. пример [Управление корневым элементом](https://tech.skbkontur.ru/kontur-ui/?path=/docs/react-ui_button-link--docs#управление-корневым-элементом).
Для ссылки нет отдельных пропсов для размерности. На размер отображаемой ссылки влияет размер шрифта (font-size) и высота строки (line-height).
## Доступность
Компонент поддерживает aria-атрибуты, если вам необходимо переопределить стандартное поведение.
ℹ️ **Полезно:** [Чек-лист доступности](https://tech.skbkontur.ru/kontur-ui/?path=/docs/accessibility--docs)
## Адаптивность
По умолчанию ссылка не меняет свой вид и поведение на мобильных устройствах.
ℹ️ **Полезно:** [Адаптивность компонентов](https://tech.skbkontur.ru/kontur-ui/?path=/docs/react-ui_information-mobiles--docs)
### Examples
```tsx
return (
Обычная ссылка
);
```
**ExampleStyle**: Проп `use` задаёт стиль ссылки. Стиль влияет на внешний вид cсылки. По умолчанию: `'default'`. Доступны стили: - Default — чёрная ссылка. - Grayed — серая ссылка. - Success — зелёная ссылка на действие с положительной окраской. - Danger — красная ссылка для необратимых или негативных по смыслу действий.
```tsx
return (
Перейти
Перейти
Принять
Удалить
);
```
**ExampleIcon**: В ссылку можно передать иконку. Иконка может находиться слева от ссылки — проп `icon`, справа — проп `rightIcon`.
```tsx
return (
}>Ссылка с иконкой слева
}>Ссылка с иконкой справа
);
```
**ExampleExternal**: По умолчанию ссылка открывается в текущей открытой вкладке. Переопределить это поведение можно с помощью нативного атрибута `target="_blank"` — ссылка будет открываться в новой вкладке. Если ссылка ведёт на внешний URL, компонент автоматически добавит атрибут `rel` с необходимым значением, чтобы защитить от проблем безопасности и утечки реферера. При этом не будет меняться или добавляться атрибут `target`.
```tsx
return (
Откроется в этой вкладке
Откроется в новой вкладке
);
```
**ExampleDisabled**: Проп `disabled` переводит ссылку в состояние блокировки. Ссылка меняет цвет на серый и становится недоступна для нажатия.
```tsx
return Заблокированная ссылка;
```
**ExampleSpinner**: Проп `loading` переводит ссылку в состояние загрузки. При загрузке ссылка переходит в состояние блокировки и становится серой. Если у ссылки есть иконка, она заменяется на спиннер.
```tsx
const [isLoading, setIsLoading] = React.useState(false);
return (
Обычная ссылка
}>
С иконкой слева
}>
С иконкой справа
setIsLoading(!isLoading)}>{isLoading ? 'Остановить загрузку' : 'Начать загрузку'}
);
```
**ExampleError**: Проп `error` переводит сслыку в состояние ошибки.
```tsx
return (
}>
Заполнить адрес
);
```
**ExampleButton**: Проп `component` позволяет переопределить корневой элемент. Примеры: - Ссылка может рендерить кнопку в качестве корневого элемента. Ссылка принимает все пропсы переданного компонента. Рекомендуется к использованию вместо кнопки с `use=link`, если нужна кнопка с визуалом ссылки. - В `component` можно передать [Link из библиотеки React Router](https://reactrouter.com/api/components/Link), если нужен переход внутри одностраничного приложения без полной перезагрузки страницы.
```tsx
return Кнопка, но выглядит как ссылка;
```
**ExampleClickCustom**: Вы можете управлять тем, какое именно действие присходит при нажатии на ссылку.
```tsx
return SingleToast.push('Ты нажал на ссылку')}>Ссылка с кастомным действием;
```
**ExampleTheme**: Ссылки допускается выделять фирменным цветом продукта. Сделать это можно, изменяя свойства темы через проп `theme`. Заданные переменные будут объединены с темой из ``. Общие переменные темы и переменные для ссылки (с префиксом `link`) смотрите на странице [ThemePlayground](https://tech.skbkontur.ru/kontur-ui/?path=/docs/react-ui_information-themeplayground--docs).
```tsx
return (
Ссылка другого цвета
);
tsx
const textDecorationStyles = {
linkTextUnderlineOffset: '1px',
};
const underlineOnHoverStyles = {
linkTextDecorationColor: 'transparent',
};
const differentColorStyles = {
linkColor: '#1874CF',
linkHoverColor: '#1874CF',
linkActiveColor: '#1874CF',
};
const stringify = (styles: Record) => {
return `${Object.entries(styles)
.map(([key, value]) => `${key}: "${value}"`)
.join(', ')}`;
};
const copyStyles = (styles: Record) => {
navigator.clipboard.writeText(stringify(styles));
SingleToast.push('Copied');
};
const tableStyle: React.CSSProperties = {
borderCollapse: 'collapse',
width: '100%',
};
const tdStyle = {
border: '1px solid',
padding: '8px',
};
const renderExampleRow = (title: string, styles: Record) => {
return (
{title}
Link
{stringify(styles).replace(/, /g, '\n')}
} use={'text'} onClick={() => copyStyles(styles)} />
);
};
return (
Пример
Переменные темы
{renderExampleRow('Ссылка с подчеркиванием без отступа', textDecorationStyles)}
{renderExampleRow('Ссылка с подчеркиванием при наведении', underlineOnHoverStyles)}
{renderExampleRow('Изменение цвета ссылки', differentColorStyles)}
);
```
---
## Loader
```jsx
import { Loader } from '@skbkontur/react-ui';
```
### Props
- **active?**: Показывает лоадер. (default: `false`)
- **caption?**: Подпись под спиннером. (default: `""`)
- **component?**: Задает компонент, заменяющий спиннер.
- **type?**: Размер спиннера и текста. (default: `normal.`)
- **delayBeforeSpinnerShow?**: Время в миллисекундах для показа вуали без спиннера. (default: `300`)
- **minimalDelayBeforeSpinnerHide?**: Минимальное время в миллисекундах для показа спиннера. (default: `1000.`)
## Использование
**Когда использовать**
- Для индикации загрузки отдельных элементов интерфейса. Контрол позволяет настраивать внешний вид и управлять времем показа над заданным компонентом или контентом.
**Когда не использовать**
- Для индикации длительных операций обмена данными с сервером, таких как загрузка данных, сохранение формы, отправка запросов и др. используйте [Глобальный лоадер](https://tech.skbkontur.ru/kontur-ui/?path=/docs/react-ui_display-data-globalloader--docs).
- Для инлайн-встраивания или более гибкой настройки внешнего вида (цвет, толщина, затемнённый режим) используйте [Спиннер](https://tech.skbkontur.ru/kontur-ui/?path=/docs/react-ui_display-data-spinner--docs).
## Доступность
Лоадер визуально отображает состояние загрузки.
**Рекомендации:**
- Предоставляйте текстовые альтернативы для индикации состояния операции.
- Убедитесь, что пользователи могут понять, что происходит загрузка, даже без визуального индикатора.
ℹ️ **Полезно:** [Чек-лист доступности](https://tech.skbkontur.ru/kontur-ui/?path=/docs/accessibility--docs)
## Адаптивность
По умолчанию лоадер не меняет свой вид и поведение на мобильных устройствах.
ℹ️ **Полезно:** [Адаптивность компонентов](https://tech.skbkontur.ru/kontur-ui/?path=/docs/react-ui_information-mobiles--docs)
### Examples
```tsx
const [isActive, setIsActive] = React.useState(true);
return (
<>
setIsActive(!isActive)}>{isActive ? 'Скрыть лоадер' : 'Показать лоадер'}
Заполнение бумажных платежных поручений традиционно требует колоссального внимания и предельной концентрации,
так как малейшая ошибка в любой цифре может привести к серьезным финансовым последствиям или задержкам в
зачислении средств. Современная система автоматизации позволяет полностью исключить человеческий фактор,
формируя электронные платежки в автоматическом режиме. Теперь вам не нужно вручную вводить сложные коды
бюджетной классификации, реквизиты плательщика или данные налоговой инспекции — все необходимые поля
заполняются системой самостоятельно на основе актуальных данных. Вы можете гибко выбирать наиболее подходящий
формат документа, будь то вариант для внутреннего согласования с директором, файл для отправки в банковскую
систему или версия для оплаты через банкомат.
>
);
```
**ExampleCaption**: Проп `caption` задаёт подпись для лоадера. Примеры наиболее подходящих формулировок перечислены в [Гайде](https://guides.kontur.ru/components/progress-indicators/spinner/).
```tsx
const [isActive, setIsActive] = React.useState(true);
return (
<>
setIsActive(!isActive)}>{isActive ? 'Скрыть лоадер' : 'Показать лоадер'}
Заполнение бумажных платежных поручений традиционно требует колоссального внимания и предельной концентрации,
так как малейшая ошибка в любой цифре может привести к серьезным финансовым последствиям или задержкам в
зачислении средств. Современная система автоматизации позволяет полностью исключить человеческий фактор,
формируя электронные платежки в автоматическом режиме. Теперь вам не нужно вручную вводить сложные коды
бюджетной классификации, реквизиты плательщика или данные налоговой инспекции — все необходимые поля
заполняются системой самостоятельно на основе актуальных данных. Вы можете гибко выбирать наиболее подходящий
формат документа, будь то вариант для внутреннего согласования с директором, файл для отправки в банковскую
систему или версия для оплаты через банкомат.
>
);
```
**ExampleSize**: Проп `type` задаёт размер лоадера. Доступные размеры: - big — для использования в рамках всей страницы. - normal (по умолчанию) — для показа в модальных окнах и компонентах среднего размера. - mini — для встраивания в строку или небольшой контрол.
```tsx
const [isActive, setIsActive] = React.useState(true);
const Content = () => (
<>
Заполнение бумажных платежных поручений традиционно требует колоссального внимания и предельной концентрации,
так как малейшая ошибка в любой цифре может привести к серьезным финансовым последствиям или задержкам в
зачислении средств. Современная система автоматизации позволяет полностью исключить человеческий фактор,
формируя электронные платежки в автоматическом режиме. Теперь вам не нужно вручную вводить сложные коды
бюджетной классификации, реквизиты плательщика или данные налоговой инспекции — все необходимые поля заполняются
системой самостоятельно.
>
);
return (
<>
setIsActive(!isActive)}>{isActive ? 'Скрыть лоадер' : 'Показать лоадер'}
>
);
```
**ExampleTime**: Для того чтобы спиннер не мигал, применяются 2 пропса: - `delayBeforeSpinnerShow` — задержка перед показом лоадера (по умолчанию 300 миллисекунд) - `minimalDelayBeforeSpinnerHide` — минимальное время показа лоадера (по умолчанию 1 секунда)
```tsx
const [loading, setLoading] = React.useState(false);
const [delayBeforeShow, setDelayBeforeShow] = React.useState(300);
const [minDisplayTime, setMinDisplayTime] = React.useState(1000);
return (
<>
delayBeforeSpinnerShow
setDelayBeforeShow(Number(v) || 0)}
/>
minimalDelayBeforeSpinnerHide
setMinDisplayTime(Number(v) || 0)}
/>
setLoading(!loading)}>{loading ? 'Остановить' : 'Показать лоадер'}
Заполнение бумажных платежных поручений требует колоссального внимания. Автоматизация исключает
человеческий фактор: система самостоятельно заполняет КБК и реквизиты на основе актуальных данных.
>
);
```
---
## MaskedInput
Поле ввода, которое ограничивает формат вводимого значения по заданной маске. Такое поле облегчает пользователю ввод и снижает количество ошибок.
```jsx
import { MaskedInput } from '@skbkontur/react-ui';
```
### Props
- **mask**: Шаблон ввода, определяющий допустимые символы.
- **maskChar?**: Плейсхолдер, который отображается на месте ещё не введённых пользователем символов. (default: `_`)
- **formatChars?**: Словарь правил для настройки маски, где: ключ — символ для использования в маске; значение — регулярка-правило. (default: `{ '9': '[0-9]', 'a': '[A-Za-z]', '*': '[A-Za-z0-9]' }`)
- **alwaysShowMask?**: Всегда показывать символы маски, независимо от фокуса в поле. (default: `false`)
- **onUnexpectedInput?**: Событие некорректного ввода. Вторым аргументом передаётся метод вспыхивания рамки поля. Если обработчик не задан, то при событии рамка всегда вспыхивает.
- **onBeforePasteValue?**: Событие перед вставкой текста в поле. Вызывается с аргументом value — текст из буфера. Обработчик должен вернуть текст — он попадёт в поле.
- **unmask?**: Убирает из value символы маски, которые пользователь не вводил. (default: `false`)
### Usage
```jsx
import { MaskedInput } from '@skbkontur/react-ui';
```
## Использование
Используйте поле с маской, когда нужно гарантировать строгий формат данных от пользователя, например: номер телефона, номер карты, стандартные идентификаторы.
Не используйте для свободного текстового ввода или сложных вариантов с большим количеством исключений.
Компонент наследует часть базовых пропcов от компонента Input — размер, ширина, иконка в поле и так далее. Они включены в таблицу пропсов выше.
Примеры для базовых пропсов вы можете посмотреть на странице [Input](https://tech.skbkontur.ru/kontur-ui/?path=/docs/react-ui_input-data-input--docs).
Из пропсов Input исключены некоторые неприменимые к полю с маской пропсы и сокращен список возможных значений для пропа `type`.
## Доступность
Для компонента действуют все те же рекомендации по доступности, что и для компонента [Input](https://tech.skbkontur.ru/kontur-ui/?path=/docs/react-ui_input-data-input--docs).
## Валидация
С помощью пакета [React UI Validations](https://tech.skbkontur.ru/kontur-ui/?path=/docs/react-ui-validations_api-reference) можно добавить валидацию для компонента.
Подробнее о том, как настроить тип, уровень валидации, формат сообщения об ошибке и другие параметры поведения, смотрите в документации пакета [React UI Validations](https://tech.skbkontur.ru/kontur-ui/?path=/docs/react-ui-validations_displaying-getting-started--docs).
## Адаптивность
По умолчанию поле ввода не меняет свой вид и поведение на мобильных устройствах.
ℹ️ Полезно: [Адаптивность компонентов](https://tech.skbkontur.ru/kontur-ui/?path=/docs/react-ui_information-mobiles--docs)
### Examples
```tsx
const [value, setValue] = React.useState('');
return (
Номер телефона
);
```
**ExampleMask**: Проп `mask` определяет шаблон маски, используемый для форматирования и проверки корректности вводимых данных в поле.
```tsx
const [valueLetter, setValueLetter] = React.useState('');
const [valueNumber, setValueNumber] = React.useState('');
const [valueAny, setValueAny] = React.useState('');
return (
);
```
**ExampleMaskChar**: Проп `maskChar` задаёт символ маски. Он отображается в шаблоне маски в качестве плейсхолдера. Символом маски может быть любой символ.
```tsx
const [value, setValue] = React.useState('');
return (
);
```
**ExampleFormatChars**: Проп `formatChars` задаёт словарь символов-регулярок. Вы можете настроить собственный словарь символов. Каждая запись описывает один токен маски: допустимые символы или регулярное выражение.
```tsx
const [value, setValue] = React.useState('');
return (
);
```
**ExampleType**: Проп `type` задаёт тип. Это стандартные типы поля ввода в HTML. Тип наделяет компонент нативными свойствами, может влиять на отображение подсказок, валидацию, автоматическое переключение раскладки клавиатуры на мобильных устройствах и другие свойства поведения. Подробнее смотрите в [Справке по HTML](https://developer.mozilla.org/ru/docs/Web/HTML/Reference/Elements/input#type). Полный список значений для типа смотрите в таблице пропсов.
```tsx
const [valueTel, setValueTel] = React.useState('');
const [valueLetter, setValueLetter] = React.useState('');
return (
type = "text"
type = "tel"
);
```
**ExampleAlwaysShowMask**: По умолчанию маска показывается только после того, как поле получает фокус. Это поведение рекомендуемое и закреплено в [Гайдах](https://guides.kontur.ru/components/input-fields/mask/#Opisanie_raboti). Но если вам необходимо переопределить стандартное поведение, используйте проп `alwaysShowMask`. Маска будет отображаться независимо от фокуса в поле.
```tsx
return ;
```
**ExampleUnMask**: Проп `unmask` позволяет сразу получать value, в котором будет только введённое пользователем значение, без символов маски.
```tsx
const [value, setValue] = React.useState('');
const [valueUnMask, setValueUnMask] = React.useState('');
return (
value по умолчанию: "{value}"
value c unmask: "{valueUnMask}"
);
```
**ExampleUnMaskPlus**: Проп `unmask` позволяет выбрать, какие символы из маски должны быть переданы в `value`. Для этого в маске оберните в фигурные скобки нужные символы.
```tsx
const [value, setValue] = React.useState('');
return (
value: "{value}"
);
```
**ExampleonBeforePasteValue**: Проп `onBeforePasteValue` вызывает обработчик при вставке значения. В него передаётся текст из буфера, а то, что он вернёт — попадёт в поле. Используйте для очистки или фильтрации вставки. В примере при вставке удалятся символы, не являющиеся цифрами, и первый символ полученной строки.
```tsx
const [value, setValue] = React.useState('');
return (
value.replace(/\D/g, '').slice(1)}
onValueChange={setValue}
/>
);
```
---
## MenuFooter
```jsx
import { MenuFooter } from '@skbkontur/react-ui';
```
### Props
- **_enableIconPadding?**: Добавляет отступ иконке.
- **size?**: Задает размер.
### Examples
```tsx
return (
Сотрудники компании}>
Вася
Петя
Маша
Всего 3 человека
);
tsx
return (
Маленький
Средний
Большой
);
```
---
## MenuHeader
```jsx
import { MenuHeader } from '@skbkontur/react-ui';
```
### Props
- **_enableIconPadding?**: Добавляет отступ иконке.
- **size?**: Задает размер.
### Examples
```tsx
return (
Сотрудники компании}>
Разработчики
Вася
Петя
Маша
Дизайнеры
Галя
Гриша
Гена
Продакты
Валя
Аля
Артём
);
tsx
return (
Маленький
Средний
Большой
);
```
---
## MenuItem
```jsx
import { MenuItem } from '@skbkontur/react-ui';
```
### Props
- **comment?**: Добавляет описание для элемента меню.
- **disabled?**: Делает компонент недоступным.
- **icon?**: Добавляет иконку элементу меню.
- **size?**: Задает размер контрола.
- **onClick?**: Задает функцию, которая вызывается при клике.
- **onMouseEnter?**: Задает функцию, которая вызывается при наведении мышкой (событие `onmouseenter`).
- **onMouseLeave?**: Задает функцию, которая вызывается при уходе мышки с объекта (событие `onmouseleave`).
- **target?**: Задает HTML-атрибут `target`.
- **title?**: Задает HTML-атрибут `title`.
- **href?**: Задает HTML-атрибут `href` - адрес, на который следует перейти.
- **rel?**: Задает HTML-атрибут `rel`. Для внешних ссылок аттрибут rel по умолчанию равен "noopener noreferrer".
- **component?**: Заменяет корневой элемент, на компонент переданный в проп. По умолчанию корневой элемент рендерится как `button`. Если передан `href`, то вместо `button` рендерится `a`.
- **isNotSelectable?**: Запрещает выделение и выбор данного пункта меню.
- **isMobile?**: Устанавливает стиль для отображения в мобильной версии.
### Examples
```tsx
return (
Открыть меню с базовыми элементами меню}>
Базовый элемент меню
Ещё один базовый элемент меню
И ещё один
);
tsx
return (
Открыть меню с базовыми и заблокированными элементами}>
Это базовый элемент меню
А это заблокированный
А это снова базовый
И снова заблокированный
И вот ещё один заблокированный
);
```
**Example3**: В пункты меню можно передать проп `isNotSelectable`, чтобы запретить выделение и выбор этого пункта меню
```tsx
return (
Открыть меню с базовыми и отключёнными элементами}>
Это базовый элемент меню
А это отключённый
А это снова базовый
И снова отключённый
И вот ещё один отключённый
);
tsx
return (
Открыть меню с причастными к Pied Piper}>
Bertram Gilfoyle
Gavin Belson
Dinesh Chugtai
Richard Hendricks
Erlich Bachman
);
tsx
return (
Открыть меню с иконками}>
}>Базовый элемент меню c иконкой
}>
Отключённый элемент меню с иконкой
} comment="А слева вы можете видеть икону 21-го века">
Элемент меню с описанием и иконкой
);
```
**Example6**: В элементы меню можно передавать проп `href`, чтобы превратить их в ссылку. Лучше выделять такие элементы иконками.
```tsx
return (
Открыть меню с ссылками}>
Начало документации
Список прекрасных людей
}
href="https://guides.kontur.ru/"
target="_blank"
rel="noopener noreferrer"
>
Подробнее в Контур Гайдах
);
tsx
return (
Маленький
Средний
Большой
);
```
---
## MenuSeparator
```jsx
import { MenuSeparator } from '@skbkontur/react-ui';
```
### Examples
```tsx
return (
Открыть меню с разделителями}>
У меня есть разделитель
У меня тоже!
А у меня нет :(
Как и у меня :(
);
```
---
## MiniModal
`MiniModal` — модальное диалоговое окно, которое предполагает обязательный отклик пользователя по одному из доступных действий. Обёртка над Modal. Закрытие окна по клику на фон или "крестик" не рекомендуется, т.к. у этих действий нет однозначного описания в отличие от кнопок с названиями, наподобие "Сохранить", "Подтвердить" и т.п. По макету предполагается, что все кнопки должны быть среднего размера `size = medium`.
```jsx
import { MiniModal } from '@skbkontur/react-ui';
```
### Examples
**Example1**: Самый простой вариант использования:
```tsx
const PayNotifice = () => {
const [isOpened, setIsOpened] = React.useState(false);
const open = () => setIsOpened(true);
const close = () => setIsOpened(false);
return (
<>
{isOpened && (
Простое уведомление
Это простое, но достаточное важное уведомление, чтобы его показать в МиниМодалке
Понятно
)}
}>
Оплата
>
);
};
return ;
```
**Example2**: Иногда от пользователя требуется выбрать одно из доступных действий. Например, подтвердить важное действие или отклонить его:
```tsx
const ConfirmDelete = ({ name, handleDelete }: { name: string; handleDelete: () => void }) => {
const theme = React.useContext(ThemeContext);
const [isOpened, setIsOpened] = React.useState(false);
const open = () => setIsOpened(true);
const close = () => setIsOpened(false);
const mainAction = () => {
handleDelete();
close();
};
return (
<>
{isOpened && (
}>
Удалить "{name}"?
Удалить
Отменить
)}
} />
>
);
};
const list = ['Отчёт № 111', 'Отчёт № 222', 'Отчёт № 333'];
return (
{list.map((name) => (
{name}
alert(`${name} удалён`)} />
))}
);
```
**Example3**: Одно и то же диалоговое окно может вызываться в разных частях приложения. В таком случае стоит реализовать паттерн синглтона:
```tsx
const EnableNotification = React.forwardRef<
{ open: () => void; close: () => void },
{ setStatus: (status: string) => void }
>(({ setStatus }, ref) => {
const [isOpened, setIsOpened] = React.useState(false);
const open = () => setIsOpened(true);
const close = () => setIsOpened(false);
const handleAllowAll = () => {
setStatus('Разрешить все');
close();
};
const handleAllowBasic = () => {
setStatus('Разрешить только основные');
close();
};
const handleDenyAll = () => {
setStatus('Запретить');
close();
};
React.useImperativeHandle(ref, () => ({ open, close }), []);
return isOpened ? (
}>Разрешить уведомления?
Разрешить все
Разрешить только основные
Запретить
) : null;
});
const [status, setStatus] = React.useState('-не выбрано-');
const NotificationEnableRef = React.useRef>(null);
const NotificationEnableOpen = () => NotificationEnableRef.current && NotificationEnableRef.current.open();
return (
<>
Статус уведомлений: {status}
Разрешить уведомления?
Разрешить уведомления?
Разрешить уведомления?
>
);
```
**Example4**: Некоторые действия для корректного исполнения требуют блокировки других действий пользователя. В таких случаях можно, например, использовать проп `loading` для `Button`, и не позволять закрыть окно до конца исполнения:
```tsx
const WaitingUpdate = ({
handleUpdate,
setLastUpdated,
}: {
handleUpdate: () => Promise;
setLastUpdated: (date: Date) => void;
}) => {
const [isLoading, setIsLoading] = React.useState(false);
const [isOpened, setIsOpened] = React.useState(false);
const open = () => setIsOpened(true);
const close = () => setIsOpened(false);
const handleMainClick = () => {
setIsLoading(true);
handleUpdate().then(() => {
setIsLoading(false);
setIsOpened(false);
setLastUpdated(new Date());
});
};
return (
<>
{isOpened && (
Обновить?
После вашего подтверждения другие действия на странице будут заблокированы на несколько секунд.
Обновить
Отменить
)}
Обновить
>
);
};
const dateTimeFormat = new Intl.DateTimeFormat('nu', { hour: '2-digit', minute: '2-digit', second: '2-digit' });
const [lastUpdated, setLastUpdated] = React.useState(new Date());
const handleUpdate = () => new Promise((resolve) => setTimeout(resolve, 1500));
return (
handleUpdate()} setLastUpdated={setLastUpdated} />
Последнее обновление: {dateTimeFormat.format(lastUpdated)}
);
```
---
## Modal
```jsx
import { Modal } from '@skbkontur/react-ui';
```
### Props
- **disableClose?**: Отключает событие `onClose` и блокирует кнопку закрытия модального окна.
- **alignTop?**: Выравнивает модальное окно по верху страницы.
- **ignoreBackgroundClick?**: Оставляет модальное окно открытым, когда пользователь кликнул на фон вне модального окна.
- **noClose?**: Скрывает крестик для закрытия модального окна.
- **width?**: Ширина модального окна.
- **onClose?**: Событие закрытия модального окна. Вызывается при клике на фон или крестик, и при нажатии Esc.
- **disableFocusLock?**: Выключает ограничение на фокус. По умолчанию модальное окно не позволяет установить фокус за переделами своего содержимого.
- **theme?**: Объект с переменными темы. Он будет объединён с темой из ``. Общие переменные темы и переменные для модального окна (с префиксом modal) смотрите на странице [ThemePlayground](https://tech.skbkontur.ru/kontur-ui/?path=/docs/react-ui_information-themeplayground--docs).
- **mobileAppearance?**: Задаёт внешний вид модального окна в мобильном режиме. Работает с версией темы >= 5_2. - `"auto"` — если футера нет, модальное окно распологается в центре экрана, если есть — модальное окно растягивается на весь экран с отступами и закругленными краями. - `"top"` — модальное окно располагается сверху независимо от наличия футера. - `"center"` — модальное окно располагается в центре независимо от наличия футера. - `"bottom"` — модальное окно располагается снизу независимо от наличия футера. - `"fullscreen-spacing"` — модальное окно растягивается на весь экран с отступами и закругленными краями. - `"fullscreen"` — модальное окно растягивается на весь экран. (default: `auto`)
### Usage
```jsx
import { Modal } from '@skbkontur/react-ui';
```
## Доступность
Компонент поддерживает стандартные aria-атрибуты, если вам необходимо переопределить его поведение.
## Адаптивность
Мобильный режим модального окна активируется при ширине вьюпорта `(max-width: 576px)` и наличии сенсорного экрана `(pointer: coarse)`.
С помощью пропа `mobileAppearance` можно определять внешний вид модального окна в мобильном режиме. Работает с версией темы >= 5_2.
Доступные значения:
- `"auto"` — если футера нет, модальное окно распологается в центре экрана, если есть — модальное окно растягивается на весь экран с отступами и закругленными краями.
- `"top"` — модальное окно располагается сверху независимо от наличия футера.
- `"center"` — модальное окно располагается в центре независимо от наличия футера.
- `"bottom"` — модальное окно располагается снизу независимо от наличия футера.
- `"fullscreen-spacing"` — модальное окно растягивается на весь экран с отступами и закругленными краями.
- `"fullscreen"` — модальное окно растягивается на весь экран.
Для просмотра примера на десктопе активируйте мобильный режим в браузере через инструменты разработчика.
Вы можете передавать свои медиазапросы, больше о настройке адаптивности читайте в статье [Адаптивность компонентов](https://tech.skbkontur.ru/kontur-ui/?path=/docs/react-ui_information-mobiles--docs).
### Examples
```tsx
const [opened, setOpened] = React.useState(false);
function renderModal() {
return (
Заголовок
Контент-зона модального окна
Закрыть
);
}
function open() {
setOpened(true);
}
function close() {
setOpened(false);
}
return (
{opened && renderModal()}
Открыть модальное окно
);
```
**ExampleWidth**: Проп `width` задаёт ширину модального окна.
```tsx
const [opened, setOpened] = React.useState(false);
function renderModal() {
return (
Заголовок
Контент-зона модального окна
Закрыть
);
}
function open() {
setOpened(true);
}
function close() {
setOpened(false);
}
return (
{opened && renderModal()}
Открыть модальное окно
);
```
**ExamplePanel**: Проп `panel` для [Modal.Footer](https://tech.skbkontur.ru/kontur-ui/?path=/docs/react-ui_overlay-modal-modalfooter--docs) визуально отделяет футер от остальной части модального окна с помощью разделителя.
```tsx
const [opened, setOpened] = React.useState(false);
const [panel, setPanel] = React.useState(true);
function renderModal() {
return (
Заголовок
Контент-зона модального окна
"panel" {panel ? 'enabled' : 'disabled'}
Закрыть
);
}
function open() {
setOpened(true);
}
function close() {
setOpened(false);
}
return (
{opened && renderModal()}
Открыть модальное окно
);
```
**ExampleStickyHeader**: Проп `sticky` для [Modal.Header](https://tech.skbkontur.ru/kontur-ui/?path=/docs/react-ui_overlay-modal-modalheader--docs) закрепляет заголовок вверху модального окна.
```tsx
const [opened, setOpened] = React.useState(false);
const [sticky, setSticky] = React.useState(false);
function renderModal() {
return (
Заголовок
"sticky" {sticky ? 'true' : 'false'}
Закрыть
);
}
function open() {
setOpened(true);
}
function close() {
setOpened(false);
}
return (
{opened && renderModal()}
Открыть модальное окно
);
```
**ExampleCutHeader**: Проп `cutTitleOnStuck` для [Modal.Header](https://tech.skbkontur.ru/kontur-ui/?path=/docs/react-ui_overlay-modal-modalheader--docs) обрезает часть длинного заголовка, если включен проп закрепления — `sticky`
```tsx
const [opened, setOpened] = React.useState(false);
function renderModal() {
return (
Очень длинный заголовок в несколько строк
Закрыть
);
}
function open() {
setOpened(true);
}
function close() {
setOpened(false);
}
return (
{opened && renderModal()}
Открыть модальное окно
);
```
**ExampleStickyFooter**: Проп `sticky` для [Modal.Footer](https://tech.skbkontur.ru/kontur-ui/?path=/docs/react-ui_overlay-modal-modalfooter--docs) закрепляет футер снизу модального окна. На десктопе — `true`, на мобильных — `false`.
```tsx
const [opened, setOpened] = React.useState(false);
function renderModal() {
return (
Заголовок
Закрыть
);
}
function open() {
setOpened(true);
}
function close() {
setOpened(false);
}
return (
{opened && renderModal()}
Открыть модальное окно
);
```
**ExampleGap**: Проп `gap` задаёт расстояние между элементами футера в пикселях.
```tsx
const [opened, setOpened] = React.useState(false);
function renderModal() {
return (
Заголовок
Контент-зона модального окна
Отправить
Отменить
);
}
function open() {
setOpened(true);
}
function close() {
setOpened(false);
}
return (
{opened && renderModal()}
Открыть модальное окно
);
```
**ExampleAlignTop**: Проп `alignTop` перемещает модальное окно в верхнюю часть страницы.
```tsx
const [opened, setOpened] = React.useState(false);
function renderModal() {
return (
Заголовок
Контент-зона модального окна
Закрыть
);
}
function open() {
setOpened(true);
}
function close() {
setOpened(false);
}
return (
{opened && renderModal()}
Открыть модальное окно
);
```
**ExampleOnClose**: Cобытие `onClose` задаёт функцию, которая вызывается, когда пользователь запросил закрытие окна — нажал на фон, Escape или крестик.
```tsx
const [opened, setOpened] = React.useState(false);
function renderModal() {
return (
Заголовок
Контент-зона модального окна
Закрыть
);
}
function open() {
setOpened(true);
}
function close() {
setOpened(false);
}
return (
{opened && renderModal()}
Открыть модальное окно
);
```
**ExampleDisableClose**: Отключает событие `onClose` и блокирует кнопку закрытия модального окна.
```tsx
const [opened, setOpened] = React.useState(false);
function renderModal() {
return (
Заголовок
Контент-зона модального окна
Закрыть
);
}
function open() {
setOpened(true);
}
function close() {
setOpened(false);
}
return (
{opened && renderModal()}
Открыть модальное окно
);
```
**ExampleIgnoreBackgroundClick**: Проп `ignoreBackgroundClick` оставляет модальное окно открытым, когда пользователь кликнул на фон.
```tsx
const [opened, setOpened] = React.useState(false);
function renderModal() {
return (
Заголовок
Контент-зона модального окна
Закрыть
);
}
function open() {
setOpened(true);
}
function close() {
setOpened(false);
}
return (
{opened && renderModal()}
Открыть модальное окно
);
```
**ExampleNoClose**: Проп `noClose` скрывает крестик закрытия.
```tsx
const [opened, setOpened] = React.useState(false);
function renderModal() {
return (
Заголовок
Контент-зона модального окна
Закрыть
);
}
function open() {
setOpened(true);
}
function close() {
setOpened(false);
}
return (
{opened && renderModal()}
Открыть модальное окно
);
```
---
## Paging
```jsx
import { Paging } from '@skbkontur/react-ui';
```
### Props
- **pagesCount**: Количество страниц.
- **activePage**: Номер текущей страницы.
- **onPageChange**: Вызывается при переключении страницы.
- **size?**: Размер пейджинга. *Проп поддерживается начиная с версии 5.3.* (default: `'small'`)
- **disabled?**: Делает компонент недоступным.
- **caption?**: Подпись у кнопки перехода на следующую страницу.
- **component?**: Компонент обёртки страниц. (default: ` `)
- **withoutNavigationHint?**: Отключает навигационные подсказки. По умолчанию подсказки появляются, когда доступно управление с клавиатуры (либо контрол в фокусе, либо globalListeners === true).
- **useGlobalListener?**: Включает глобальный слушатель `keyDown`, для навигации клавишами без фокуса на контроле.
### Usage
```jsx
import { Paging } from '@skbkontur/react-ui';
```
## Использование
Пейджинг используют для навигации по длинным спискам, таблицам и другим наборам однотипных данных, разбитых на страницы.
Рекомендации:
- храните текущую страницу в состоянии и синхронизируйте её через `activePage` и `onPageChange`;
- используйте `caption`, если нужна явная подпись у кнопки перехода вперед;
- включайте `useGlobalListener`, только когда пользователю действительно нужна навигация стрелками без фокуса на компоненте.
## Доступность
Компонент поддерживает aria-атрибуты, если вам необходимо переопределить стандартное поведение.
Рекомендации:
- обеспечьте видимый фокус при навигации с клавиатуры;
- не отключайте подсказки клавиатурной навигации без необходимости;
- при кастомном `component` сохраняйте корректное поведение клавиатуры и `tabIndex`;
- для блокировки используйте проп `disabled`: переходы между страницами и взаимодействие с элементами становятся недоступны;
- если необходимо оставить элемент в доступности для скринридеров, используйте `aria-disabled="true"` и обрабатывайте блокировку действий вручную.
ℹ️ **Полезно:** [Чек-лист доступности](https://tech.skbkontur.ru/kontur-ui/?path=/docs/accessibility--docs)
## Адаптивность
Пейджинг адаптивен: на мобильных устройствах меняется отображение элементов управления и иконок. Мобильный режим работает при соответствующих медиазапросах адаптивного окружения React UI.
Если нужно скрывать «последнюю страницу» до определенного шага, используйте сценарий из примера «Пейджинг без последней страницы».
ℹ️ **Полезно:** [Адаптивность компонентов](https://tech.skbkontur.ru/kontur-ui/?path=/docs/react-ui_information-mobiles--docs)
### Examples
**ExampleBasic**: Базовый пример с обязательными пропсами `activePage`, `onPageChange` и `pagesCount`.
```tsx
const [activePage, setActivePage] = React.useState(1);
return ;
```
**ExampleSize**: Проп `size` задаёт размер пейджинга.
```tsx
const [activePage, setActivePage] = React.useState(1);
return (
);
```
**ExampleDisabled**: Проп `disabled` блокирует переключение страниц.
```tsx
const [activePage, setActivePage] = React.useState(3);
return ;
```
**ExampleCaption**: Проп `caption` задаёт подпись на кнопке перехода вперёд.
```tsx
const [activePage, setActivePage] = React.useState(1);
return ;
```
**ExampleWithoutNavigationHint**: Проп `withoutNavigationHint` отключает подсказку по навигации клавишами.
```tsx
const [activePage, setActivePage] = React.useState(7);
return ;
```
**ExampleUseGlobalListener**: Проп `useGlobalListener` включает глобальную навигацию по стрелкам без фокуса на компоненте.
```tsx
const [activePage, setActivePage] = React.useState(4);
return (
);
```
**ExampleWithoutLastPage**: Иногда у пейджинга не нужно показывать последнюю страницу заранее. [Ссылка на гайд](https://guides.kontur.ru/components/navigation/paging/#Opisanie_raboti/Peidzhing_bez_poslednei_stranitsi).
```tsx
const { isMobile } = useResponsiveLayout();
const [activePage, setActivePage] = React.useState(1);
const totalPagesCount = 30;
const shouldShowLastPage = activePage > totalPagesCount - (isMobile ? 2 : 4);
const visiblePagesCount = shouldShowLastPage ? totalPagesCount : Infinity;
return (
);
```
**ExampleCustomItemComponent**: Проп `component` позволяет переопределить компонент элемента пагинации.
```tsx
const [activePage, setActivePage] = React.useState(1);
const CustomPagingItem = ({ children, className, onClick, tabIndex }: ItemComponentProps) => {
return (
{children}
);
};
return ;
```
**ExampleIsForwardMethod**: Статический метод `Paging.isForward` определяет, является ли элемент кнопкой перехода вперед.
```tsx
const [activePage, setActivePage] = React.useState(1);
const CustomPagingItem = ({ children, pageNumber, className, onClick, tabIndex }: ItemComponentProps) => {
return (
{children}
);
};
return ;
```
---
## PasswordInput
```jsx
import { PasswordInput } from '@skbkontur/react-ui';
```
### Props
- **detectCapsLock?**: Визуально показывает, что активен CapsLock.
### Usage
```jsx
import { PasswordInput } from '@skbkontur/react-ui';
```
## Использование
Не используйте такое поле для ввода одноразовых СМС-кодов, так как пользователю нужно видеть вводимые цифры для самопроверки. Для этого лучше подойдет [MaskedInput](https://tech.skbkontur.ru/kontur-ui/?path=/docs/react-ui_input-data-maskedinput--docs).
Компонент наследует часть базовых пропcов (размер, ширина, иконка в поле и т.д.) от компонента Input, они включены в таблицу пропсов . Примеры для базовых пропсов вы можете посмотреть на странице [Input](https://tech.skbkontur.ru/kontur-ui/?path=/docs/react-ui_input-data-input--docs).
## Доступность
Для компонента действуют все те же рекомендации по доступности, что и для компонента [Input](https://tech.skbkontur.ru/kontur-ui/?path=/docs/react-ui_input-data-input--docs).
## Валидация
С помощью пакета [React UI Validations](https://tech.skbkontur.ru/kontur-ui/?path=/docs/react-ui-validations_displaying-getting-started--docs) можно добавить валидацию для поля.
## Адаптивность
По умолчанию поле ввода не меняет свой вид и поведение на мобильных устройствах.
ℹ️ Полезно: [Адаптивность компонентов](https://tech.skbkontur.ru/kontur-ui/?path=/docs/react-ui_information-mobiles--docs)
### Examples
```tsx
return ;
```
**ExampleDetectCapsLock**: Проп `detectCapsLock` визуально покажет пользователю, что клавиша CapsLock активна.
```tsx
return ;
```
---
## Radio
```jsx
import { Radio } from '@skbkontur/react-ui';
```
### Props
- **error?**: Меняет визуальное отображение поля на состояние «ошибка». Может быть полезен при разработке собственной валидации, если вы не используете пакет [React UI Validations](https://tech.skbkontur.ru/kontur-ui/?path=/docs/react-ui-validations_displaying-getting-started--docs).
- **warning?**: Меняет визуальное отображение поля на состояние «предупреждение». Может быть полезен при разработке собственной валидации, если вы не используете пакет [React UI Validations](https://tech.skbkontur.ru/kontur-ui/?path=/docs/react-ui-validations_displaying-getting-started--docs).
- **size?**: Размер радиокнопки.
- **focused?**: Задаёт состояние фокуса.
- **onValueChange?**: Задаёт функцию, которая вызывается при изменении `value`.
- **value**: Задаёт значение.
### Usage
```jsx
import { Radio } from '@skbkontur/react-ui';
```
## Доступность
Компонент поддерживает aria-атрибуты, если вам необходимо переопределить стандартное поведение.
ℹ️ **Полезно:** [Чек-лист доступности](https://tech.skbkontur.ru/kontur-ui/?path=/docs/accessibility--docs)
## Адаптивность
По умолчанию радиокнопка не меняет свой вид и поведение на мобильных устройствах.
ℹ️ **Полезно:** [Адаптивность компонентов](https://tech.skbkontur.ru/kontur-ui/?path=/docs/react-ui_information-mobiles--docs)
### Examples
```tsx
const [chosen, setChosen] = React.useState(null);
return (
Первый вариант
Второй вариант
);
```
**ExampleSize**: Проп `size` задаёт размер радиокнопки. По умолчанию: `'small'`.
```tsx
const [chosen, setChosen] = React.useState(null);
return (
Маленький
Средний
Большой
);
```
**ExampleMode**: У радиокнопки есть несколько пропсов состояний: - `disabled` — блокирует радиокнопку. - `checked` — делает радиокнопку контролируемым и выбранным по умолчанию элементом. - `focused` — задаёт состояние фокусировки. - `error` — задаёт состояние «Ошибка». - `warning` — задаёт состояние «Предупреждение». Радиокнопка может быть сразу в нескольких состояниях.
```tsx
const [chosen, setChosen] = React.useState(null);
return (
Обычная
Заблокированная
Отмеченная по умолчанию
В состоянии focused
В состоянии error
В состоянии warning
Заблокированная и отмеченная по умолчанию в состоянии warning
);
```
---
## RadioGroup
```jsx
import { RadioGroup } from '@skbkontur/react-ui';
```
### Props
- **defaultValue?**: Значение по умолчанию. Должно быть одним из значений дочерних радиокнопок или значений из `items`.
- **value?**: Значение группы радиокнопок. Должно быть одним из значений радиокнопок. Если не указано, то компонент будет работать как неконтролируемый.
- **items?**: Массив параметров радиокнопок. Может быть типа `Array` или `Array<[Value, Data]>`, где тип `Value` — значение радиокнопки, а `Data` — значение которое будет использовано вторым параметром в `renderItem`. Тип `Array` будет приведен к типу `Array<[Value, Value]>`. Может быть использовано, если не передан `children`.
- **name?**: Устанавливает атрибут `name` для дочерних радиокнопок. Если не указан, то сгенерируется случайное имя.
- **toKey?**: Получает уникальный ключ по элементу.
- **disabled?**: Блокирует все радиокнопки в группе.
- **warning?**: Меняет визуальное отображение всех радиокнопок на состояние «предупреждение».
- **error?**: Меняет визуальное отображение всех радиокнопок на состояние «ошибка».
- **inline?**: Размещает радиокнопки в строку. Работает только со значениями, переданными через `items`, не работает с `children`.
- **width?**: Ширина радиогруппы. Работает только со значениями, переданными через `items`, не работает с `children`.
- **renderItem?**: Отрисовывает контент радиокнопки. Работает только со значениями, переданными через `items`, не работает с `children`.
- **onBlur?**: Событие потери радиогруппой фокуса.
- **onMouseLeave?**: Событие ухода мышки с объекта (событие `onmouseleave`).
- **onMouseOver?**: Событие наведения мышкой (событие `onmouseover`).
- **onMouseEnter?**: Событие наведения мышкой (событие `onmouseenter`). Смотрите разницу с `onMouseOver` в [документации](https://learn.javascript.ru/mousemove-mouseover-mouseout-mouseenter-mouseleave)
### Usage
```jsx
import { RadioGroup } from '@skbkontur/react-ui';
```
## Использование
Группа радиокнопок может содержать любую разметку с радиокнопками. Значения могут быть переданы в `children`, либо через проп `items`.
Тип значений в группе и радиокнопках должен совпадать.
Значения активного элемента сравниваются по строгому равенству `===`.
## Доступность
Компонент поддерживает aria-атрибуты, если вам необходимо переопределить стандартное поведение.
ℹ️ **Полезно:** [Чек-лист доступности](https://tech.skbkontur.ru/kontur-ui/?path=/docs/accessibility--docs)
## Валидация
С помощью пакета [React UI Validations](https://tech.skbkontur.ru/kontur-ui/?path=/docs/react-ui-validations_api-reference) можно добавить валидацию для компонента.
Подробнее о том, как настроить тип, уровень валидации, формат сообщения об ошибке и другие параметры поведения, смотрите [в документации пакета React UI Validations](https://tech.skbkontur.ru/kontur-ui/?path=/docs/react-ui-validations_displaying-getting-started--docs).
## Адаптивность
По умолчанию группа радиокнопок не меняет свой вид и поведение на мобильных устройствах.
ℹ️ **Полезно:** [Адаптивность компонентов](https://tech.skbkontur.ru/kontur-ui/?path=/docs/react-ui_information-mobiles--docs)
### Examples
```tsx
const [chosen, setChosen] = React.useState(null);
return (
Первый вариант
Второй вариант
Третий вариант
Четвёртый вариант
);
```
**ExampleWidth**: Проп `width` задаёт максимальную ширину элементов группы. Работает только со значениями, переданными через `items`, не работает с `children`.
```tsx
const items = [
'Уведомлять обо всех изменениях',
'Уведомлять только о самых важных изменения',
'Никогда не уведомлять',
'Настроить свой вариант',
];
return ;
```
**ExampleMode**: У группы радиокнопок есть несколько пропсов состояний: - `disabled` — блокировка. - `error` — ошибка. - `warning` — предупреждение.
```tsx
const itemsDisabled = ['Первый вариант', 'Второй вариант', 'Третий вариант', 'Четвёртый вариант'];
const itemsError = ['Первый вариант', 'Второй вариант', 'Третий вариант', 'Четвёртый вариант'];
const itemsWarning = ['Первый вариант', 'Второй вариант', 'Третий вариант', 'Четвёртый вариант'];
const disabledRadioGroup = (
Заблокированная группа
);
const errorRadioGroup = (
В состоянии «Ошибка»
);
const warningRadioGroup = (
В состоянии «Предупреждение»
);
return (
{disabledRadioGroup}
{errorRadioGroup}
{warningRadioGroup}
);
```
**ExampleDefault**: Проп `defaultValue` задаёт значение по умолчанию. Должно быть одним из значений дочерних радиокнопок или значений из `items`.
```tsx
const items = ['Первый вариант', 'Второй вариант', 'Третий вариант', 'Четвёртый вариант'];
return ;
```
**ExampleInline**: Проп `inline` размещает радиокнопки в строку. Работает только со значениями, переданными через `items`, не работает с `children`.
```tsx
const items = ['Первый вариант', 'Второй вариант', 'Третий вариант', 'Четвёртый вариант'];
return ;
```
---
## ResponsiveLayout
Компонент `ResponsiveLayout` для определения текущего лэйаута.
```jsx
import { ResponsiveLayout } from '@skbkontur/react-ui';
```
### Props
- **onLayoutChange?**: Задает функцию, которая вызывается при изменении лейаута.
- **customMediaQueries?**: Позволяет кастомизировать возвращаемые флаги.
## Примеры
```ts static
interface ResponsiveLayoutFlags {
isMobile: boolean;
}
```
### Компонент ожидает в себя функцию, в которую аргументом передается объект с флагом лэйаута.
```jsx static
import { ResponsiveLayout } from '@skbkontur/react-ui';
class SomeComponent {
public render() {
return (
{
({ isMobile }) => {
/* ... */
}
}
)
}
}
```
### Также существует проп `onLayoutChange`, который вызывает переданный в него коллбэк при изменении лэйаута. Аргументом передается объект с флагом.
```jsx static
import { ResponsiveLayout } from '@skbkontur/react-ui';
class SomeComponent {
public render() {
return (
console.log(isMobile)} />
)
}
}
```
### В компонент можно передать проп `customMediaQueries: MediaQueriesType`, который позволяет кастомизировать возвращаемые флаги:
```ts static
type MediaQueriesType = Record;
```
### 3. При добавлении кастомного флага isMobile, значение дефолтного флага перезаписывается в пользу кастомного
```jsx static
import { ResponsiveLayout } from '@skbkontur/react-ui';
const customMediaQueries = {
isTablet: '(min-width: 577px)',
isDesktop: '(min-width: 1280px)',
};
class SomeComponent {
public render() {
return (
{
({ isMobile, isTablet, isDesktop }) => {
/* ... */
}
}
)
}
}
```
Как альтернативу можно использовать хук [useResponsiveLayout](#/Mobiles).
---
## ScrollContainer
```jsx
import { ScrollContainer } from '@skbkontur/react-ui';
```
### Props
- **invert?**: Инвертирует цвет скроллбара. (default: `false`)
- **maxHeight?**: Задает максимальную высоту.
- **maxWidth?**: Задает максимальную ширину.
- **preventWindowScroll?**: Отключает скролл окна, когда меню открыто. (default: `false`)
- **scrollBehaviour?**: Задает поведение скролла. (https://developer.mozilla.org/en-US/docs/Web/CSS/scroll-behavior) (default: `'auto'`)
- **onScrollStateChangeX?**: Задает функцию, которая вызывается при скроле по горизонтали.
- **onScrollStateChangeY?**: Задает функцию, которая вызывается при скроле по вертикали.
- **onScroll?**: Задает функцию, которая вызывается при скроле.
- **disabled?**: Отключает кастомный скролл.
- **offsetY?**: Задает смещение вертикального скроллбара.
- **offsetX?**: Задает смещение горизонтального скроллбара.
- **showScrollBar?**: Определяет, нужно ли показывать скроллбар.
- **hideScrollBarDelay?**: Устанавливает задержку в миллисекундах перед скрытием скроллбара. Работает только при hideScrollBar = true или showScrollBar = 'scroll' | 'hover'.
- **disableAnimations?**: Отключает анимацию.
### Examples
```tsx
function items(count: number) {
const items = [];
for (let i = 0; i < count; ++i) {
items.push(i);
}
return items;
}
const divStyle: React.CSSProperties = {
display: 'inline-block',
border: '1px solid #f99',
height: 200,
margin: 1,
position: 'relative',
verticalAlign: 'top',
width: 200,
};
const absStyle: React.CSSProperties = {
border: '1px solid',
boxSizing: 'border-box',
position: 'absolute',
width: '100%',
};
return (
{items(20).map((i) => (
{i}
))}
{items(20).map((i) => (
{i}
))}
{items(3).map((i) => (
{i}
))}
{items(30).map((i) => (
{i}
))}
);
tsx
const divStyle: React.CSSProperties = {
display: 'inline-block',
border: '1px solid #f99',
height: 200,
margin: 1,
position: 'relative',
verticalAlign: 'top',
width: 200,
};
const absStyle: React.CSSProperties = {
border: '1px solid',
boxSizing: 'border-box',
position: 'absolute',
width: '100%',
};
function items(count: number) {
const items = [];
for (let i = 0; i < count; ++i) {
items.push(i);
}
return items;
}
const innerStyle = {
width: 400,
};
return (
{items(5).map((i) => (
{i}
))}
{items(20).map((i) => (
{i}
))}
{items(3).map((i) => (
{i}
))}
{items(30).map((i) => (
{i}
))}
);
tsx
const containerStyle = {
display: 'inline-block',
border: '1px solid #f99',
height: 200,
margin: 1,
width: 200,
};
const offsetY = {
top: 8,
bottom: 8,
right: 8,
};
return (
{Array(30)
.fill(null)
.map((_, i) => (
{i}
))}
);
```
**Example4**: Проп `showScrollBar` со значением `scroll` скрывает скроллбар при отсутствии активности пользователя. Задержку на скрытие скроллбара можно регулировать пропом `hideScrollBarDelay` (по умолчанию 500ms)
```tsx
const divStyle: React.CSSProperties = {
display: 'inline-block',
border: '1px solid #f99',
height: 200,
margin: 1,
position: 'relative',
verticalAlign: 'top',
width: 200,
};
return (
{Array(30)
.fill(null)
.map((_, i) => (
{i}
))}
);
```
**Example5**: Проп `showScrollBar` со значением `hover` позволяет показывать скроллбар только когда курсор находится над скролл контейнером
```tsx
const divStyle: React.CSSProperties = {
display: 'inline-block',
border: '1px solid #f99',
height: 200,
margin: 1,
position: 'relative',
verticalAlign: 'top',
width: 200,
};
return (
{Array(30)
.fill(null)
.map((_, i) => (
{i}
))}
);
```
---
## Select
```jsx
import { Select } from '@skbkontur/react-ui';
```
### Props
- **_renderButton?**: Отрисовывает кнопку.
- **defaultValue?**: Значение по умолчанию.
- **menuOffset?**: Смещение списка относительно поля по горизонтали. При нуле смещения нет, при положительном значении — список сдвигается вправо, при отрицательном — влево. (default: `0`)
- **disablePortal?**: По умолчанию список рендерится через [паттерн Portal](https://react.dev/reference/react-dom/createPortal). Проп отключает использование Portal и список рендерится как обычный блок с абсолютным позиционированием внутри компонента.
- **disabled?**: Блокирует раскрывающийся список.
- **error?**: Меняет визуальное отображение раскрывающегося списка на состояние ошибки. Может быть полезен при разработке собственной валидации, если вы не используете пакет [React UI Validations](https://tech.skbkontur.ru/kontur-ui/?path=/docs/react-ui-validations_displaying-getting-started--docs).
- **filterItem?**: Отфильтровывает элементы по заданному паттерну.
- **items?**: Набор значений. Поддерживаются любые перечисляемые типы, в том числе `Array`, `Map`, `Immutable.Map`. Элементы воспринимаются следующим образом: - если элемент — это массив, то первый элемент является значением, второй — отображается в списке, а третий – комментарий; - если элемент не является массивом, то он используется и для отображения, и для значения. Для вставки разделителя между значениями можно использовать `Select.SEP`. Вставить невыделяемый элемент со своей разметкой можно так: ``` My Element
)]} /> ``` Чтобы добавить стандартный отступ для статического элемента: ``` My Element ```
- **maxMenuHeight?**: Максимальная высота раскрывающегося списка.
- **maxWidth?**: Максимальная ширина кнопки.
- **positions?**: Список значений, определяющих расположение списка относительно кнопки. Если во всех позициях список выходит за пределы `viewport`, будет использована первая из этого списка. `"top left"`, `"top center"`, `"top right"`, `"right top"`, `"right middle"`, `"right bottom"`, `"bottom left"`, `"bottom center"`, `"bottom right"`, `"left top"`, `"left middle"`, `"left bottom"`. (default: `['bottom left', 'bottom right', 'top left', 'top right']`)
- **menuPos?**: Фиксирует расположение списка относительно кнопки.
- **menuAlign?**: Выравнивание списка.
- **menuWidth?**: Ширина списка.
- **onValueChange?**: Событие изменения значения (`value`) в поле.
- **onClose?**: Событие закрытия меню.
- **onMouseEnter?**: Событие наведения мышкой (событие `onmouseenter`). Смотрите разницу с `onMouseOver` в [документации](https://learn.javascript.ru/mousemove-mouseover-mouseout-mouseenter-mouseleave).
- **onMouseLeave?**: Событие ухода мышки с объекта (событие `onmouseleave`).
- **onMouseOver?**: Событие наведения мышкой (событие `onmouseover`).
- **onKeyDown?**: Событие нажатия кнопки на клавиатуре.
- **onOpen?**: Событие открытия меню.
- **placeholder?**: Текст, который отображается если не введено никакое значение.
- **renderItem?**: Отрисовывает элементы в списке.
- **renderValue?**: Отрисовывает выбранный элемент.
- **areValuesEqual?**: Сравнивает `value` с элементом из `items`.
- **search?**: Отображает строчку поиска в списке значений.
- **value?**: Задаёт значение.
- **theme?**: Объект с переменными темы.
- **width?**: Ширина раскрывающегося списка.
- **warning?**: Меняет визуальное отображение поля на состояние предупреждения. Может быть полезен при разработке собственной валидации, если вы не используете пакет [React UI Validations](https://tech.skbkontur.ru/kontur-ui/?path=/docs/react-ui-validations_displaying-getting-started--docs).
- **use?**: Стиль кнопки. Примеры стилей смотрите [в документации компонента Button](https://tech.skbkontur.ru/kontur-ui/?path=/docs/react-ui_button-button--docs).
- **size?**: Размер раскрывающегося списка. (default: `small`)
- **onFocus?**: HTML-событие `onfocus`.
- **onBlur?**: HTML-событие `onblur`.
- **mobileMenuHeaderText?**: Текст заголовка списка в мобильной версии.
## Использование
Используйте раскрывающийся список при:
- заполнении форм, например, для выбора месяца;
- переключении состояний, например, фильтра;
- выборе предустановленных настроек, например, частоты уведомлений или часового пояса.
Не используйте раскрывающийся список для выбора элементов меню. В таком случае воспользуйтесь компонентом [Dropdown](https://tech.skbkontur.ru/kontur-ui/?path=/docs/react-ui_menu-dropdown--docs).
## Доступность
Компонент поддерживает стандартные aria-атрибуты, если вам необходимо переопределить его поведение.
## Адаптивность
Раскрывающийся список адаптивен: на мобильных устройствах он открывается на весь экран устройства и становится фокусным активным элементом, что упрощает работу с ним. Мобильный режим активируется при ширине вьюпорта `(max-width: 576px)` и наличии сенсорного экрана `(pointer: coarse)`.
В мобильном режиме у раскрывающегося списка появляется заголовок, который необходимо передать через проп `mobileMenuHeaderText`. Полное описание адаптивного вида и поведения компонента смотрите в [Гайде](https://guides.kontur.ru/components/selection-elements/select/#Аdaptivnost').
Вы можете передавать свои медиазапросы, больше о настройке адаптивности читайте в статье [Адаптивность компонентов](https://tech.skbkontur.ru/kontur-ui/?path=/docs/react-ui_information-mobiles--docs).
## Валидация
С помощью пакета [React UI Validations](https://tech.skbkontur.ru/kontur-ui/?path=/docs/react-ui-validations_api-reference) можно добавить валидацию для компонента.
Подробнее о том, как настроить тип, уровень валидации, формат сообщения об ошибке и другие параметры поведения, смотрите в документации пакета [React UI Validations](https://tech.skbkontur.ru/kontur-ui/?path=/docs/react-ui-validations_displaying-getting-started--docs).
### Examples
```tsx
const [value, setValue] = React.useState();
const items = ['Счёт-фактура', 'Акт', 'Накладная', 'Договор'];
return ;
```
**ExampleSize**: Проп `size` задаёт размер раскрывающегося списка. По умолчанию `"small"`.
```tsx
const [valueSmall, setValueSmall] = React.useState('Маленький');
const [valueMedium, setValueMedium] = React.useState('Средний');
const [valueLarge, setValueLarge] = React.useState('Большой');
const items = ['Маленький', 'Средний', 'Большой'];
return (
);
```
**ExampleWidth**: Проп `width` задаёт фиксированную ширину раскрывающегося списка, в том числе ширину кнопки.
```tsx
const [value, setValue] = React.useState();
const items = ['Счёт-фактура', 'Акт', 'Накладная', 'Договор'];
return ;
```
**ExampleMaxWidth**: Проп `maxWidth` задаёт максимальную ширину кнопки, не влияет на ширину списка.
```tsx
const [value, setValue] = React.useState();
const items = ['Счёт-фактура', 'Акт', 'Накладная', 'Договор', 'Универсальный передаточный документ (УПД)'];
return ;
```
**ExampleMaxMenuHeight**: Проп `maxMenuHeight` задаёт максимальную высоту списка.
```tsx
const [value, setValue] = React.useState();
const items = ['Счёт-фактура', 'Акт', 'Накладная', 'Договор'];
return ;
```
**ExampleMenuWidth**: Проп `menuWidth` задаёт фиксированную ширину списка, не влияет на ширину кнопки.
```tsx
const [value, setValue] = React.useState();
const items = ['Счёт-фактура', 'Акт', 'Накладная', 'Договор'];
return ;
```
**ExampleSelectSep**: Добавить визуальный разделитель между значениями можно через константу `Select.SEP` — рендерится как линия. Размещать разделитель можно в любом месте передаваемого массива.
```tsx
const [value, setValue] = React.useState();
const items = ['Любой', Select.SEP, 'Счёт-фактура', 'Акт', 'Накладная', 'Договор'];
return ;
```
**ExampleMenuPos**: Проп `menuPos` фиксирует расположение выпадающего списка. Он может быть под кнопкой — `"bottom"`, над ней — `"top"` или посередине — `"middle"`. По умолчанию список отображается под кнопкой, а если не хватает места, то динамически меняет расположение и показывается над кнопкой.
```tsx
const [value, setValue] = React.useState();
const items = ['Счёт-фактура', 'Акт', 'Накладная', 'Договор'];
return ;
```
**ExampleMenuAlign**: Проп `menuAlign` выравнивает список. Список может быть прикреплен к левому краю кнопки — "left" или к правому — "right".
```tsx
const [value, setValue] = React.useState();
const items = ['Счёт-фактура', 'Акт', 'Накладная', 'Договор'];
return (
"left"
"right"
);
```
**ExamplePositions**: Проп `positions` задаёт расположение и выравнивание списка относительно кнопки, можно передать одно значение или список значений — если во всех позициях список выходит за пределы `viewport`, будет использована первая из этого списка. Возможные значения: `"top left"`, `"top center"`, `"top right"`, `"right top"`, `"right middle"`, `"right bottom"`, `"bottom left"`, `"bottom center"`, `"bottom right"`, `"left top"`, `"left middle"`, `"left bottom"`.
```tsx
const [value, setValue] = React.useState();
const items = ['Счёт-фактура', 'Акт', 'Накладная', 'Договор'];
return ;
```
**ExampleMenuOffset**: Проп `menuOffset` смещает список относительно кнопки по горизонтали. При 0 — смещения нет, при положительном значении — список сдвигается влево, при отрицательном — вправо.
```tsx
const [value, setValue] = React.useState();
const items = ['Счёт-фактура', 'Акт', 'Накладная', 'Договор'];
return ;
```
**ExamplePlaceholder**: Проп `placeholder` переопределяет текст, который отображается если не введено никакое значение.
```tsx
const [value, setValue] = React.useState();
const items = ['Счёт-фактура', 'Акт', 'Накладная', 'Договор'];
return ;
```
**ExampleSearch**: Проп `search` отображает строчку поиска в списке значений. Дополнительно через проп `filterItem` можно фильтровать элементы по заданному паттерну.
```tsx
const [value, setValue] = React.useState();
const items = ['Счёт-фактура', 'Акт', 'Накладная', 'Договор'];
const filterItem = (value: string | undefined, item: string, pattern: string) => {
console.log({ value, pattern, item });
if (!pattern) {
return true;
}
const normalize = (s: string) =>
String(s)
.toLowerCase()
.replace(/ё/g, 'е')
.replace(/[^а-яa-z0-9]+/g, '');
return normalize(item).includes(normalize(pattern));
};
return ;
```
**ExampleClear**: Очистить выбранное значение в раскрывающемся списке можно только с помощью `null`.
```tsx
const [value, setValue] = React.useState();
const items = ['Счёт-фактура', 'Акт', 'Накладная', 'Договор'];
return (
setValue(null)}>Передать null
);
```
**ExampleIsNotSelectable**: Проп `isNotSelectable` для Select.Item запрещает выделение и выбор этого значения из списка.
```tsx
const [value, setValue] = React.useState();
const items = [
Невыбираемое значение ,
'Счёт-фактура',
'Акт',
'Накладная',
'Договор',
];
return ;
```
**ExampleDisabled**: Проп `disabled` блокирует раскрывающийся список.
```tsx
const [value, setValue] = React.useState();
const items = ['Любой', 'Счёт-фактура', 'Акт', 'Накладная', 'Договор'];
return ;
```
**ExampleError**: Проп `error` переводит раскрывающийся список в состояние ошибки.
```tsx
const [value, setValue] = React.useState();
const items = ['Любой', 'Счёт-фактура', 'Акт', 'Накладная', 'Договор'];
return ;
```
**ExampleCustomValue**: Если элемент в пропе `items` имеет кастомный тип, то компоненту нужны подсказки по работе с ним.
```tsx
const [value, setValue] = React.useState<{ color: string; hex: string }>({
color: 'Красный',
hex: '#f00',
});
return (
items={[
{ color: 'Красный', hex: '#f00' },
{ color: 'Синий', hex: '#00f' },
{ color: 'Зелёный', hex: '#0f0' },
]}
value={value}
onValueChange={setValue}
areValuesEqual={(a, b) => a.hex === b.hex}
renderValue={(item) => item.color}
renderItem={(item) => (
<>
{item.color} ◼
>
)}
/>
);
```
**ExampleRenderButton**: Проп `_renderButton` позволяет задать функцию отрисовки кнопки. В примере заданы параметры для отображения кнопки-ссылки с иконкой.
```tsx
const [value, setValue] = React.useState();
const items = ['Любой', 'Счёт-фактура', 'Акт', 'Накладная', 'Договор'];
const renderLinkButton = (params: ButtonParams) => {
const linkProps = {
disabled: params.disabled,
icon: ,
_button: true,
_buttonOpened: params.opened,
onClick: params.onClick,
onKeyDown: params.onKeyDown,
};
return {params.label};
};
return ;
```
---
## SidePage
```jsx
import { SidePage } from '@skbkontur/react-ui';
```
### Props
- **blockBackground?**: Добавляет блокирующий фон, когда сайдпейдж открыт.
- **disableClose?**: Отключает событие onClose, также дизейблит кнопку закрытия сайдпейджа.
- **ignoreBackgroundClick?**: Оставляет окно открытым при клике на фон.
- **ignoreOutsideClick?**: Оставляет окно открытым при клике на фон.
- **width?**: Задает ширину сайдпейджа.
- **mobileWidth?**: Задает ширину сайдпейджаю на мобилке. По умолчанию ширина во весь экран.
- **onClose?**: Задает функцию, которая вызывается при запросе закрытия сайдпейджа пользователем (нажал на фон, на Escape или на крестик).
- **onOpened?**: Задает функцию, которая вызывается при завершении анимации открытия сайдпейджа.
- **fromLeft?**: Отображает сайдпэйдж слева.
- **disableAnimations?**: Отключает анимацию.
- **disableFocusLock?**: Отключает фокус-лок внутри сайдпейджа. Работает только при заблокированном фоне: `blockBackground = true`.
- **offset?**: Задает отступ от края экрана.
- **onOutsideClick?**: Задаёт функцию, которая вызывается при клике на фон.
### Examples
```tsx
const [opened, setOpened] = React.useState(false);
function renderSidePage() {
return (
Title
Use rxjs operators with react hooks
Close
);
}
function open() {
setOpened(true);
}
function close() {
setOpened(false);
}
return (
{opened && renderSidePage()}
Open
);
```
**Example2**: Проп `cutTitleOnStuck` позволяет регулировать - нужно ли обрезать длинное название в шапке ` ` при прокрутке содержимого.
```tsx
const [opened, setOpened] = React.useState(false);
const [cutTitleOnStuck, setСutTitleOnStuck] = React.useState(false);
function renderSidePage() {
return (
Very very very very very very very very very very very very very very very very very very very very long title
Close
);
}
function open(cutTitleOnStuck: boolean) {
setOpened(true);
setСutTitleOnStuck(cutTitleOnStuck);
}
function close() {
setOpened(false);
}
return (
{opened && renderSidePage()}
open(true)}>With Title Cutting
open(false)}>Without Title Cutting
);
```
**Example3**: При помощи пропа `sticky` можно регулировать - будет ли залипать ` ` при прокрутке содержимого.
```tsx
const [opened, setOpened] = React.useState(false);
function renderSidePage() {
return (
Title
Close
);
}
function open() {
setOpened(true);
}
function close() {
setOpened(false);
}
return (
{opened && renderSidePage()}
Open
);
```
**Example4**: При помощи пропа `onOutsideClick` можно управлять поведением при клике по фону. Например, не закрывать при клике на определённый элемент.
```tsx
const [opened, setOpened] = React.useState(false);
function renderSidePage() {
return (
Голова
Туловище
Close
);
}
function open() {
setOpened(true);
}
function close() {
setOpened(false);
}
function handleIgnoredElementClick(e: Event) {
if (e.target instanceof HTMLElement) {
const ignoredElement = e.target.closest('#bg-ignore');
if (ignoredElement) {
e.preventDefault();
}
}
}
return (
{opened && renderSidePage()}
{opened ? `Will not close` : 'Open'}
);
```
---
## SingleToast
`SingleToast` — это короткое немодальное уведомление, которое сообщает пользователю о результате выполнения его команды. Результат может быть положительным, отрицательным или нейтральным. Позволяет вызывать тосты с помощью статических методов. ##### Особенности компонента SingleToast Для корректной работы ` ` должен быть отрисован только **один раз** на странице. После чего его можно вызывать из любого места приложения методом `SingleToast.push()`. Однако, переданные в компонент пропсы, такие как `theme`, `onPush` и остальные, будут применяться ко всем вызовам.
```jsx
import { SingleToast } from '@skbkontur/react-ui';
```
### Examples
```tsx
const customTheme = {
toastBg: '#00BEA2',
toastColor: '#ffff',
};
const [isCustomTheme, setIsCustomTheme] = React.useState(false);
return (
<>
SingleToast.push('Тост')}>Показать тост
Кастомная тема
>
);
tsx
return (
SingleToast.push('Тост с кнопкой действия', {
action: { label: 'Cancel', handler: () => alert('Действие было отменено') },
})
}
>
Тост c кнопкой действия
);
```
---
## Spinner
```jsx
import { Spinner } from '@skbkontur/react-ui';
```
### Props
- **caption?**: Подпись спиннера
- **type?**: Размер индикатора и текста (default: `normal`)
- **color?**: Одноцветный режим. Удобная альтернатива пропа `dimmed`
- **inline?**: Уменьшает размер индикатора для работы в строках. Если задан, то размер индикитора из `type` игнорируется (default: `false`)
- **width?**: Толщина индикатора в пикселях. (default: `2`)
- **dimmed?**: Одноцветный режим. Цвет спиннера не переливается. Можно кастомизировать переменной `spinnerDimmedColor` (default: `false`)
### Usage
```jsx
import { Spinner } from '@skbkontur/react-ui';
```
## Использование
Используйте `Spinner`, чтобы показать, что система выполняет команду, которую дал пользователь.
Не применяйте `Spinner` для заполнения паузы при загрузке контента, для этого предназначен [GlobalLoader](https://tech.skbkontur.ru/kontur-ui/?path=/docs/react-ui_display-data-globalloader--docs).
## Доступность
Для пользователей скринридеров можно использовать `live regions`.
Обычное использование cпиннера предполагает, что он удаляется, когда не нужен.
Но, чтобы информировать пользователя об отсутствии спиннера, необходим текст доступный для скринридера.
Пример ниже демонстрирует, как доступность можно реализовать через атрибут `aria-live="polite"` и некоторые приёмы вёрстки.
```jsx
const [active, setActive] = React.useState(false);
<>
setActive(!active)}>Spinner
Загрузка {active ? ' началась' : ' закончилась'}
{active && }
>;
```
ℹ️ **Полезно:** [Чек-лист доступности](https://tech.skbkontur.ru/kontur-ui/?path=/docs/accessibility--docs), [ARIA live regions](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Guides/Live_regions)
## Адаптивность
По умолчанию лоадер не меняет свой вид и поведение на мобильных устройствах.
ℹ️ **Полезно:** [Адаптивность компонентов](https://tech.skbkontur.ru/kontur-ui/?path=/docs/react-ui_information-mobiles--docs)
### Examples
**ExampleType**: Проп `type` меняет размер индикатора и текста.
```tsx
return (
);
```
**ExampleCaption**: Проп `width` задаёт толщину индикатора в пикселях. / export const ExampleWidth: Story = () => ; ExampleWidth.storyName = 'Толщина индикатора'; /** Проп `caption` добавляет текст рядом с индикатором.
```tsx
return (
);
```
**ExampleInline**: Проп `inline` уменьшает размер индикатора для работы в строках.
```tsx
return (
span
span
);
```
**ExampleColor**: Проп `color` делает индикатор одноцветным, позволяя напрямую задать цвет. Альтернативой является проп `dimmed`, который включает одноцветный режим. По умолчанию цвета становятся приглушёнными, но их можно изменить через тему.
```tsx
return (
);
```
---
## Sticky
```jsx
import { Sticky } from '@skbkontur/react-ui';
```
### Props
- **side**: Задает сторону залипания.
- **offset?**: Задает отступ от края экрана в пикселях, на который сдвигается элемент в залипшем состоянии. (default: `0`)
- **getStop?**: Задает функцию, которая возвращает DOM-элемент, который нельзя пересекать.
### Examples
```tsx
const style = {
padding: 10,
background: '#f0f0f0',
};
let stop: HTMLElement | null = null;
return (
stop}>
{(fixed) => (
Header
fixed: {String(fixed)}
)}
Content
{
stop = el;
}}
style={{ borderTop: '1px solid' }}
/>
stop} offset={20}>
{(fixed) => (
Footer
fixed: {String(fixed)}
)}
);
```
---
## Switcher
```jsx
import { Switcher } from '@skbkontur/react-ui';
```
### Props
- **items**: Задает список элементов в свитчере. Это массив строк или объектов типа `{ label: string, value: string, buttonProps?: Partial
}`
- **value?**: Устанавливает значение свитчера.
- **onValueChange?**: Задает функцию, которая вызывается при изменении значения свитчера (value).
- **caption?**: Задает подпись около свитчера.
- **error?**: Переводит контрол в состояние валидации "ошибка".
- **size?**: Задает размер контрола.
- **width?**: Задает ширину контрола. С этим пропом элементы внутри автоматически равномерно растянутся.
- **disabled?**: Делает компонент недоступным.
- **renderItem?**: Задает функцию отрисовки элемента. Параметр `renderDefault` - это встроенная дефолтная функция отрисовки элемента, которую можно вызывать в `renderItem`.
### Examples
```tsx
const [value, setValue] = React.useState('');
return (
);
```
**WithItemsAsObjects**: Случай, когда `items` принимает объект типа `{ label: string, value: string }`
```tsx
const [value, setValue] = React.useState('');
const items: SwitcherItems[] = [
{
label: '1',
value: '1',
},
{
label: '2',
value: '2',
},
{
label: '3',
value: '3',
},
{
label: '4',
value: '4',
},
];
return (
value: {value}
);
```
**WithCustomButtonProps**: Вариант `items` с полем `buttonProps`, который позволяет кастомизировать кнопку
```tsx
const [value, setValue] = React.useState('system');
const items: SwitcherItems[] = [
{
label: '',
value: 'light',
buttonProps: {
icon: ,
},
},
{
label: '',
value: 'system',
buttonProps: {
icon: ,
},
},
{
label: '',
value: 'dark',
buttonProps: {
icon: ,
},
},
];
return ;
```
**WithCustomRenderItem**: Пример с методом `renderItem` для кастомизации `items`:
```tsx
const [value, setValue] = React.useState('');
const items = ['Самовывоз', 'Постамат', 'Курьер'];
const renderItem = (label: string, value: string, buttonProps: ButtonProps, renderDefault: () => React.ReactNode) => {
if (value === 'Постамат') {
return (
{renderDefault()}
);
}
if (value === 'Курьер') {
return (
(
Из-за повышенного объема заказов
возможно длительное ожидание
)}
>
{renderDefault()}
);
}
return renderDefault();
};
return (
);
```
**WithCustomWidth**: Пример с разными значениями пропа `width`:
```tsx
const [valueFirst, setValueFirst] = React.useState('');
const [valueSecond, setValueSecond] = React.useState('');
const [valueThird, setValueThird] = React.useState('');
const items = ['Первый', 'Второй', 'Третий'];
return (
);
```
---
## Tabs
```jsx
import { Tabs } from '@skbkontur/react-ui';
```
### Props
- **indicatorClassName?**: Кастомный класс для индикатора (подчёркивания) активного таба.
- **size?**: Размер табов.
- **value**: Идентификатор активного таба. Совпадает с `id` выбранного ` `.
- **onValueChange?**: Вызывается при смене активного таба.
- **vertical?**: Располагает табы вертикально. (default: `false`)
- **width?**: Ширина компонента.
### Usage
```jsx
import { Tabs } from '@skbkontur/react-ui';
```
## Использование
Используйте `Tabs` для второстепенной навигации, для группировки или фильтрации контента.
**Когда не использовать:**
- Для основной навигации. Для этого лучше подходит главное меню на цветной плашке — оно более заметно на странице.
- Для состояний — для этого есть [RadioGroup](https://tech.skbkontur.ru/kontur-ui/?path=/docs/react-ui_input-data-radiogroup--docs), [Toggle](https://tech.skbkontur.ru/kontur-ui/?path=/docs/react-ui_input-data-toggle--docs) и [Switcher](https://tech.skbkontur.ru/kontur-ui/?path=/docs/react-ui_button-switcher--docs).
## Доступность
Компонент поддерживает управление с клавиатуры - при переходе к группе табов клавишей `Tab` первый таб получает фокус. Стрелками можно перемещать фокус между табами. При нажатии клавиши `Enter` таб с фокусом становится активным.
**Рекомендации**
- Используйте `aria-describedby` для связи группы табов с элементом, содержащим дополнительное, развёрнутое описание.
ℹ️ **Полезно:** [Чек-лист доступности](https://tech.skbkontur.ru/kontur-ui/?path=/docs/accessibility--docs)
## Адаптивность
По умолчанию группа табов не меняет свой вид и поведение на мобильных устройствах.
ℹ️ **Полезно:** [Адаптивность компонентов](https://tech.skbkontur.ru/kontur-ui/?path=/docs/react-ui_information-mobiles--docs)
### Examples
```tsx
const [active, setActive] = React.useState('inbox');
return (
Входящие
Отправленные
);
```
**SizeExample**: Проп `size` задаёт размер табов в группе. По умолчанию: `'large'`.
```tsx
const [active, setActive] = React.useState('inbox');
const renderCaption = (caption: string) => {caption} ;
return (
{renderCaption('small')}
Входящие
Отправленные
{renderCaption('medium')}
Входящие
Отправленные
{renderCaption('large')}
Входящие
Отправленные
);
```
**AlignmentExample**: Компонент может отображать табы двумя способами: горизонтально (по умолчанию) и вертикально.
```tsx
const [active, setActive] = React.useState('inbox');
return (
Входящие
Отправленные
);
```
**WidthExample**: Проп `width` задаёт ширину группы табов.
```tsx
const [active, setActive] = React.useState('inbox');
return (
Входящие
Отправленные
);
```
**CustomizationExample**: Проп `indicatorClassName` задаёт кастомный класс подчёркивания табов.
```tsx
const [active, setActive] = React.useState('inbox');
const emotion = useEmotion();
return (
Входящие
Отправленные
);
```
---
## Textarea
```jsx
import { Textarea } from '@skbkontur/react-ui';
```
### Props
- **error?**: Меняет визуальное отображение поля на состояние ошибки.
- **warning?**: Меняет визуальное отображение поля на состояние предупреждения.
- **disabled?**: Блокирует поле.
- **size?**: Размер многострочного поля.
- **autoResize?**: Выполняет автоматический ресайз в зависимости от количества текста в поле. Связан с пропом `extraRow`, который всегда добавляет дополнительную пустую строку.
- **rows?**: Высота поля — число видимых строк. При превышении этой высоты появляется скролл.
- **maxRows?**: Максимальное число видимых строк при автоматическом ресайзе.
- **resize?**: Направление ресайза поля. Попадает в `style`. Описание всех значений смотрите [в документации MDN](https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/Properties/resize).
- **width?**: Ширина многострочного поля.
- **onValueChange?**: Событие изменения `value`.
- **selectAllOnFocus?**: Выделяет введённое значение при фокусе в поле. Работает с типами `text`, `password`, `tel`, `search`, `url`.
- **showLengthCounter?**: Отображает счётчик введённых символов.
- **lengthCounter?**: Допустимое количество символов в поле. Отображается в счётчике символов. (default: `maxLength`)
- **counterHelp?**: Добавляет подсказку к счётчику символов. По умолчанию - тултип с содержимым из пропа, если передан `ReactNode`. Передав функцию, можно переопределить подсказку целиком, вместе с иконкой. ``` counterHelp={() => } ```
- **extraRow?**: Добавляет дополнительную свободную строку при автоматическом ресайзе.
- **disableAnimations?**: Отключает анимацию при автоматическом ресайзе. Автоматически отключается, когда в `extraRow` передан `false`.
- **align?**: Выравнивание текста в поле.
## Использование
Принимает все атрибуты `React.TextareaHTMLAttributes`.
Пропсы `className` и `style` игнорируются.
Используйте многострочное поле, если пользователю нужно ввести много слов. Если значение чаще всего состоит из 1–3 слов, следует использовать обычное поле ввода [Input](https://tech.skbkontur.ru/kontur-ui/?path=/docs/react-ui_input-data-input--docs).
## Доступность
Компонент поддерживает стандартные aria-атрибуты, если вам необходимо переопределить его поведение.
## Валидация
С помощью пакета [React UI Validations](https://tech.skbkontur.ru/kontur-ui/?path=/docs/react-ui-validations_api-reference) можно добавить валидацию для компонента.
Подробнее о том, как настроить тип, уровень валидации, формат сообщения об ошибке и другие параметры поведения, смотрите в документации пакета [React UI Validations](https://tech.skbkontur.ru/kontur-ui/?path=/docs/react-ui-validations_displaying-getting-started--docs).
## Адаптивность
По умолчанию многострочное поле не меняет свой вид и поведение на мобильных устройствах.
ℹ️ Полезно: [Адаптивность компонентов](https://tech.skbkontur.ru/kontur-ui/?path=/docs/react-ui_information-mobiles--docs)
### Examples
```tsx
const [value, setValue] = React.useState('');
return (
);
tsx
const [value, setValue] = React.useState('');
return (
);
tsx
const [valuePercent, setValuePercent] = React.useState('');
const [valueNumber, setValueNumber] = React.useState('');
return (
);
```
**ExampleRows**: Проп `rows` задаёт высоту поля, которая равна количеству видимых строк. При превышении этой высоты появляется скролл.
```tsx
const [value, setValue] = React.useState('');
return (
);
```
**ExampleResize**: Проп `resize` определяет, в какую сторону или стороны будет расширяться поле. По умолчанию многострочное поле может изменять свой размер, если потянуть за нижний правый угол. Основные значения: - none — размеры изменять нельзя; - both — размеры можно изменять по горизонтали и вертикали; - horizontal — размер можно изменять по горизонтали; - vertical — размер можно изменять по вертикали. Описания для экспериментальных значений смотрите [в документации MDN](https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/Properties/resize).
```tsx
const [value, setValue] = React.useState('');
return (
);
```
**ExampleAutoResize**: Проп `autoResize` автоматически увеличивает поле под его содержимое. Связан с пропом `extraRow`, который всегда добавляет дополнительную пустую строку, и пропом `maxRows` — максимальное число видимых строк.
```tsx
const [value, setValue] = React.useState('');
return (
);
```
**ExampleAlign**: Проп `align` выравнивает текст в поле.
```tsx
const [value, setValue] = React.useState('');
return (
);
```
**ExampleClear**: Очистить значение в поле можно только с помощью пустой строки.
```tsx
const [value, setValue] = React.useState('Значение');
return (
setValue('')}>Передать пустую строку
);
```
**ExampleCounter**: За отображение счётчика введённых символов отвечают пропсы: - `showLengthCounter` — отображает счётчик символов. - `lengthCounter` — допустимое количество символов в поле. Отображается в счётчике символов. - `counterHelp` — добавляет подсказку к счётчику символов.
```tsx
const [value, setValue] = React.useState('');
return (
);
```
**ExampleDisabled**: Проп `disabled` блокирует поле.
```tsx
const [value, setValue] = React.useState('');
return (
);
```
**ExampleError**: Пропсы `error` и `warning` используются для валидации.
```tsx
const [valid, setValid] = React.useState();
const [value, setValue] = React.useState('');
return (
);
```
---
## Toast
```jsx
import { Toast } from '@skbkontur/react-ui';
```
### Props
- **onPush?**: Задает функцию, которая вызывается при возникновении тоста.
- **onClose?**: Задает функцию, которая вызывается при закрытии тоста.
- **theme?**: Задает объект с переменными темы. Он будет объединён с темой из контекста.
### Examples
```tsx
const toastRef = React.useRef(null);
const showNotification = () => {
const { current: toast } = toastRef;
if (toast) {
toast.push('Default text');
}
};
return (
<>
Show notification
>
);
tsx
const toastRef = React.useRef(null);
const showNotification = () => {
const { current: toast } = toastRef;
if (toast) {
toast.push('Toast with action', {
action: { label: 'Cancel', handler: () => toast.push('Canceled') },
});
}
};
return (
<>
Show notification
>
);
tsx
const toastRef = React.useRef(null);
const showNotification = () => {
const { current: toast } = toastRef;
if (toast) {
toast.push('Toast with custom showTime', { showTime: 15_000 });
}
};
return (
<>
Show notification
>
);
tsx
class Toaster extends React.Component {
private notifier = React.createRef();
showNotification() {
this.notifier.current?.push('Successfully');
}
render() {
return (
this.showNotification()}>Show notification
);
}
}
return ;
```
---
## Toggle
```jsx
import { Toggle } from '@skbkontur/react-ui';
```
### Props
- **captionPosition?**: Расположение названия относительно переключателя. (default: `'right'`)
- **checked?**: Включает тогл. (default: `false`)
- **defaultChecked?**: Делает тогл включенным по умолчанию. Не работает, если задан проп `checked`.
- **disabled?**: Блокирует тогл.
- **onValueChange?**: Событие изменения значения.
- **onChange?**: Событие клика на тогл.
- **warning?**: Переводит тогл в состояние предупреждения. (default: `false`)
- **error?**: Переводит тогл в состояние ошибки. (default: `false`)
- **loading?**: Переводит тогл в состояние загрузки: добавляет стили для состояния `loading` и отключает тогл.
- **autoFocus?**: Устанавливает фокус на тогл после окончания загрузки страницы. (default: `false`)
- **size?**: Размер тогла.
- **onFocus?**: Событие получения тоглом фокуса.
- **onBlur?**: Событие потери тоглом фокуса.
- **disableAnimations?**: Отключает анимацию.
### Usage
```jsx
import { Toggle } from '@skbkontur/react-ui';
```
## Доступность
Компонент поддерживает aria-атрибуты, если вам необходимо переопределить стандартное поведение.
ℹ️ **Полезно:** [Чек-лист доступности](https://tech.skbkontur.ru/kontur-ui/?path=/docs/accessibility--docs)
## Валидация
Если тогл нельзя использовать, заблокируйте его. Валидация не нужна.
## Адаптивность
По умолчанию тогл не меняет свой вид и поведение на мобильных устройствах.
ℹ️ **Полезно:** [Адаптивность компонентов](https://tech.skbkontur.ru/kontur-ui/?path=/docs/react-ui_information-mobiles--docs)
### Examples
```tsx
const [checked, setChecked] = React.useState(false);
return (
{checked ? 'Включен' : 'Выключен'}
);
tsx
return (
Маленький
Средний
Большой
);
```
**ExampleCaptionPosition**: Проп `captionPosition` определяет, с какой стороны от переключателя находится его название.
```tsx
const [checked, setChecked] = React.useState(false);
return (
Название справа
Название слева
);
tsx
const [checked, setChecked] = React.useState(false);
return (
Внешний label
);
```
**ExampleDisabled**: Проп `disabled` переводит тогл в состояние блокировки.
```tsx
return Заблокированный ;
```
**ExampleLoading**: Проп `loading` переводит тогл в состояние загрузки.
```tsx
return Загрузка ;
```
**ExampleDisableAnimations**: Проп `disableAnimations` отключает анимацию при переключении тогла. По умолчанию тогл переключается с анимацией: круг плавно перемещается, фон движется за ним.
```tsx
return Без анимации ;
```
---
## Token
```jsx
import { Token } from '@skbkontur/react-ui';
```
### Props
- **isActive?**: Делает токен активным.
- **error?**: Меняет визуальное отображение токена на состояние «ошибка». Может быть полезен при разработке собственной валидации, если вы не используете пакет [React UI Validations](https://tech.skbkontur.ru/kontur-ui/?path=/docs/react-ui-validations_displaying-getting-started--docs).
- **warning?**: Меняет визуальное отображение токена на состояние «предупреждение». Может быть полезен при разработке собственной валидации, если вы не используете пакет [React UI Validations](https://tech.skbkontur.ru/kontur-ui/?path=/docs/react-ui-validations_displaying-getting-started--docs).
- **disabled?**: Блокирует токен.
- **size?**: Размер токена.
- **onClick?**: Задаёт функцию, которая вызывается при клике на токен.
- **onDoubleClick?**: Задаёт функцию, которая вызывается при двойном клике на токен.
- **onRemove?**: Задаёт функцию, которая вызывается, когда токен удаляется.
- **onMouseEnter?**: Задаёт функцию, которая вызывается при наведении мышкой (событие `onmouseenter`).
- **onMouseLeave?**: Задаёт функцию, которая вызывается при уходе мышки с объекта (событие `onmouseleave`).
- **onFocus?**: Задаёт функцию, которая вызывается, когда токен получает фокус.
- **onBlur?**: Задаёт функцию, которая вызывается, когда токен теряет фокус.
### Usage
```jsx
import { Token } from '@skbkontur/react-ui';
```
## Доступность
Компонент поддерживает aria-атрибуты, если вам необходимо переопределить стандартное поведение.
ℹ️ **Полезно:** [Чек-лист доступности](https://tech.skbkontur.ru/kontur-ui/?path=/docs/accessibility--docs)
## Адаптивность
По умолчанию токен не меняет свой вид и поведение на мобильных устройствах.
ℹ️ **Полезно:** [Адаптивность компонентов](https://tech.skbkontur.ru/kontur-ui/?path=/docs/react-ui_information-mobiles--docs)
### Examples
```tsx
return mail@example.ru ;
```
**ExampleSize**: Проп `size` задаёт размер токена. По умолчанию `"small"`.
```tsx
return (
Маленький
Средний
Большой
);
```
**ExampleIsActive**: Проп `isActive` переводит токен в активное состояние. По умолчанию, это происходит, когда токен находится в фокусе.
```tsx
return (
Обычный
Активный
);
```
**ExampleDisabled**: Проп `disabled` блокирует токен.
```tsx
return mail@example.ru ;
```
**ExampleErrorWarning**: Проп `error` меняет визуальное отображение поля на состояние ошибки, а `warning` — на предупреждение.
```tsx
return (
Обычный
С ошибкой
Предупреждающий
);
```
---
## TokenInput
```jsx
import { TokenInput } from '@skbkontur/react-ui';
```
### Props
- **selectedItems?**: Токены, которые будут отображаться в поле ввода.
- **onValueChange?**: Событие добавления нового токена.
- **onKeyDown?**: HTML-событие `onkeydown`.
- **autoFocus?**: Устанавливает фокус на поле с токенами после окончания загрузки страницы.
- **size?**: Размер поля с токенами.
- **type?**: Тип инпута. Возможные значения: - `TokenInputType.Combined` — в поле можно выбирать значения из справочника и добавлять свои значения. - `TokenInputType.WithReference` — можно только выбрать значения из справочника, но нельзя добавлять свои. - `TokenInputType.WithoutReference` — можно добавлять любые значения, но подсказок из справочника нет.
- **menuWidth?**: Ширина выпадающего списка.
- **menuAlign?**: Выравнивание выпадающего списка.
- **getItems?**: Задаёт функцию поиска значений, которая должна возвращать Promise с массивом значений. По умолчанию ожидаются строки. Элементы могут быть любого типа. В этом случае необходимо определить свойства `renderItem`, `valueToString`.
- **hideMenuIfEmptyInputValue?**: Ограничивает отображение выпадающего списка при фокусе на пустом поле: выпадающий список появится, только когда будет введён хотя бы один символ токена.
- **renderItem?**: Отрисовывает элемент списка.
- **renderValue?**: Отрисовывает выбранное значение.
- **valueToString?**: Возвращает строковое представление `value`. Необходимо при фокусировке. (default: `item => item`)
- **renderTotalCount?**: Отрисовывает сообщение об общем количестве элементов.
- **totalCount?**: Определяет общее количество элементов. Необходим для работы `renderTotalCount`.
- **renderNotFound?**: Отрисовывает сообщение о пустом результате поиска. При `renderAddButton` не работает.
- **valueToItem?**: Преобразует значение в элемент списка.
- **itemToId?**: Сравнивает полученные результаты с `value`.
- **placeholder?**: Текст, который отображается если не введено никакое значение.
- **delimiters?**: Символы, которые разделяют введённый текст на токены. По умолчанию — запятая.
- **error?**: Меняет визуальное отображение токена на состояние «ошибка». Может быть полезен при разработке собственной валидации, если вы не используете пакет [React UI Validations](https://tech.skbkontur.ru/kontur-ui/?path=/docs/react-ui-validations_displaying-getting-started--docs).
- **warning?**: Меняет визуальное отображение токена на состояние «предупреждение». Может быть полезен при разработке собственной валидации, если вы не используете пакет [React UI Validations](https://tech.skbkontur.ru/kontur-ui/?path=/docs/react-ui-validations_displaying-getting-started--docs).
- **disabled?**: Блокирует поле с токенами.
- **width?**: Ширина поля с токенами.
- **maxMenuHeight?**: Максимальная высота выпадающего списка.
- **renderToken?**: Отрисовывает токен и даёт возможность кастомизировать внешний вид и поведение токена.
- **onInputValueChange?**: Событие изменения текста в поле ввода, если результатом функции будет строка, то она станет следующим состоянием полем ввода.
- **renderAddButton?**: Отрисовывает кнопку добавления нового токена в выпадающем списке.
- **onUnexpectedInput?**: Событие обработки ввода строки в поле ввода и последующая потеря фокуса компонентом. Функция срабатывает с аргументом поля строки. Если при потере фокуса в выпадающем списке будет только один элемент и результат `valueToString` с этим элементом будет совпадать со значением в текстовом поле, то сработает `onValueChange` со значением данного элемента. Сама функция также может вернуть значение, не равное undefined, с которым будет вызван `onValueChange`. Если возвращаемое значение будет равно null, то сработает очистка текущего значения поля, а в режиме редактирования токен будет удален.
- **inputMode?**: Задаёт типы вводимых данных. Передаёт браузеру информацию о том, какой набор символов показать при вводе данных в конкретное поле на устройствах с экранной клавиатурой.
- **maxHeight?**: Максимальная высота компонента. При её достижении появится скроллбар
### Usage
```jsx
import { TokenInput } from '@skbkontur/react-ui';
```
## Использование
Поле с токенами похоже на [комбобокс](https://tech.skbkontur.ru/kontur-ui/?path=/docs/react-ui_input-data-combobox--docs), но используется в случаях, когда нужно указать сразу много однородных элементов — токенов.
Значения определяются в пропе `getItems` — задаёт функцию поиска элементов, которая должна возвращать `Promise` с массивом значений. По умолчанию ожидаются строки.
## Доступность
Компонент поддерживает aria-атрибуты, если вам необходимо переопределить стандартное поведение.
ℹ️ **Полезно:** [Чек-лист доступности](https://tech.skbkontur.ru/kontur-ui/?path=/docs/accessibility--docs)
## Валидация
С помощью пакета [React UI Validations](https://tech.skbkontur.ru/kontur-ui/?path=/docs/react-ui-validations_api-reference) можно добавить валидацию для компонента.
В примере добавлена комплексная валидация:
1. Валидация обязательности ввода.
2. Классическая валидация компонента: проверка пробелов в токене.
3. Валидация каждого токена через `createValidator` и `renderToken`.
Подробнее о том, как настроить тип, уровень валидации, формат сообщения об ошибке и другие параметры поведения, смотрите в документации пакета [React UI Validations](https://tech.skbkontur.ru/kontur-ui/?path=/docs/react-ui-validations_displaying-getting-started--docs).
## Адаптивность
По умолчанию поле с токенами не меняет свой вид и поведение на мобильных устройствах.
ℹ️ **Полезно:** [Адаптивность компонентов](https://tech.skbkontur.ru/kontur-ui/?path=/docs/react-ui_information-mobiles--docs)
### Examples
```tsx
const [selectedItems, setSelectedItems] = React.useState(['Красный', 'Синий']);
const delay =
(time: number) =>
(args?: string[]): Promise =>
new Promise((resolve) => setTimeout(resolve, time, args));
const getItems = (q: string) =>
Promise.resolve(
['Красный', 'Оранжевый', 'Жёлтый', 'Зелёный', 'Голубой', 'Синий', 'Фиолетовый'].filter(
(x) => x.toLowerCase().includes(q.toLowerCase()) || x === q,
),
).then(delay(500));
return (
(
{item}
)}
/>
);
```
**ExampleSize**: Проп `size` задаёт размер поля с токенами.
```tsx
const [selectedItemsSmall, setSelectedItemsSmall] = React.useState(['Маленький']);
const [selectedItemsMedium, setSelectedItemsMedium] = React.useState(['Средний']);
const [selectedItemsLarge, setSelectedItemsLarge] = React.useState(['Большой']);
const delay =
(time: number) =>
(args?: string[]): Promise =>
new Promise((resolve) => setTimeout(resolve, time, args));
const getItems = (q: string) =>
Promise.resolve(
['Маленький', 'Средний', 'Большой'].filter(
(x) => x.toLowerCase().includes(q.toLowerCase()) || x.toString() === q,
),
).then(delay(500));
return (
);
```
**ExampleWidth**: Проп `width` задаёт ширину поля с токенами.
```tsx
const [selectedItems, setSelectedItems] = React.useState([]);
const delay =
(time: number) =>
(args?: string[]): Promise =>
new Promise((resolve) => setTimeout(resolve, time, args));
const getItems = (q: string): Promise =>
Promise.resolve(
['Красный', 'Оранжевый', 'Жёлтый', 'Зелёный', 'Голубой', 'Синий', 'Фиолетовый'].filter(
(x) => x.toLowerCase().includes(q.toLowerCase()) || x.toString() === q,
),
).then(delay(500));
return (
);
```
**ExampleMaxHeight**: Проп `maxHeight` ограничивает высоту поля с токенами. При достижении этой высоты будет появляться скроллбар.
```tsx
const items = Array(30)
.fill('')
.map(
(t, i1) =>
i1 +
Array(5 + (i1 % 10))
.fill('')
.map((_, i2) => i2)
.join(''),
);
const [value, setValue] = React.useState(items);
const getItems = (query: string) => {
return Promise.resolve(items.filter((item) => item.includes(query)));
};
return (
maxHeight={200}
width={350}
type={TokenInputType.Combined}
getItems={getItems}
selectedItems={value}
onValueChange={setValue}
/>
);
```
**ExampleMenuWidth**: Проп `menuWidth` задаёт максимальную ширину выпадающего списка. Может быть `auto` — по ширине текста, в пикселях, процентах от ширины поля и других конкретных единицах. Проп зависит от другого пропа `menuAlign`. Ширина выпадающего списка всегда будет равна `"auto"`, когда 'menuAlign'='cursor' — для поля с токенами является значением по умолчанию.
```tsx
const [selectedItems, setSelectedItems] = React.useState([]);
const delay =
(time: number) =>
(args?: string[]): Promise =>
new Promise((resolve) => setTimeout(resolve, time, args));
const getItems = (q: string) =>
Promise.resolve(
['Красный', 'Оранжевый', 'Жёлтый', 'Зелёный', 'Голубой', 'Синий', 'Фиолетовый'].filter(
(x) => x.toLowerCase().includes(q.toLowerCase()) || x.toString() === q,
),
).then(delay(500));
return (
);
```
**ExampleMaxMenuHeight**: Проп `maxMenuHeight` задаёт максимальную высоту выпадающего списка.
```tsx
const [selectedItems, setSelectedItems] = React.useState([]);
const delay =
(time: number) =>
(args?: string[]): Promise =>
new Promise((resolve) => setTimeout(resolve, time, args));
const getItems = (q: string) =>
Promise.resolve(
['Красный', 'Оранжевый', 'Жёлтый', 'Зелёный', 'Голубой', 'Синий', 'Фиолетовый'].filter(
(x) => x.toLowerCase().includes(q.toLowerCase()) || x.toString() === q,
),
).then(delay(500));
return (
);
```
**ExampleMenuAlign**: Проп `menuAlign` выравнивает выпадающий список. По умолчанию `cursor` — выпадающий список отображается по линии текущего положения курсора в поле с токенами. Можно закрепить строго по левому краю через значение `"left"`.
```tsx
const [selectedItems, setSelectedItems] = React.useState(['Красный']);
const [selectedItemsLeft, setSelectedItemsLeft] = React.useState(['Красный']);
const delay =
(time: number) =>
(args?: string[]): Promise =>
new Promise((resolve) => setTimeout(resolve, time, args));
const getItems = (q: string) =>
Promise.resolve(
['Красный', 'Оранжевый', 'Жёлтый', 'Зелёный', 'Голубой', 'Синий', 'Фиолетовый'].filter(
(x) => x.toLowerCase().includes(q.toLowerCase()) || x.toString() === q,
),
).then(delay(500));
return (
menuAlign="cursor"
menuAlign="left"
);
```
**ExampleHideMenuIfEmptyInputValue**: По умолчанию выпадающий список с подсказками появляется сразу при фокусе в поле и продолжает отображаться всё время, пока пользователь вводит в поле токены. Проп `hideMenuIfEmptyInputValue` отключает это поведение. Такой режим похож на работу [автокомплита](https://tech.skbkontur.ru/kontur-ui/?path=/docs/react-ui_input-data-autocomplete--docs). Выпадающий список появляется, когда введён первый символ первого или последующего токена.
```tsx
const [selectedItems, setSelectedItems] = React.useState([]);
const delay =
(time: number) =>
(args?: string[]): Promise =>
new Promise((resolve) => setTimeout(resolve, time, args));
const getItems = (q: string) =>
Promise.resolve(
['Красный', 'Оранжевый', 'Жёлтый', 'Зелёный', 'Голубой', 'Синий', 'Фиолетовый'].filter(
(x) => x.toLowerCase().includes(q.toLowerCase()) || x === q,
),
).then(delay(500));
return (
Обычное поле
С пропом "hideMenuIfEmptyInputValue"
);
```
**ExampleType**: Проп `type` задаёт тип поля: - `TokenInputType.Combined` — можно и выбирать, и добавлять значения. - `TokenInputType.WithReference` — в поле можно ввести только значения из справочника, но нельзя добавлять свои. - `TokenInputType.WithoutReference` — можно добавлять любые значения, но подсказок из справочника нет.
```tsx
const [selectedItemsCombined, setSelectedItemsCombined] = React.useState([]);
const [selectedItemsWithReference, setSelectedItemsWithReference] = React.useState([]);
const [selectedItemsWithoutReference, setSelectedItemsWithoutReference] = React.useState([]);
const delay =
(time: number) =>
(args?: string[]): Promise =>
new Promise((resolve) => setTimeout(resolve, time, args));
const getItems = (q: string) =>
Promise.resolve(
['Маленький', 'Средний', 'Большой'].filter((x) => x.toLowerCase().includes(q.toLowerCase()) || x === q),
).then(delay(500));
return (
TokenInputType.Combined
TokenInputType.WithReference
TokenInputType.WithoutReference
);
```
**ExampleDelimiters**: Проп `delimiters` определяет знак разделителя токенов при вводе. По умолчанию запятая. Работает только с типами `TokenInputType.WithoutReference` и `TokenInputType.Combined`. При смене разделителей смените текст для подсказки добавления нового токена, по умолчанию — «Нажмите запятую».
```tsx
const [selectedItems, setSelectedItems] = React.useState(['Красный', 'Синий']);
const delay =
(time: number) =>
(args?: string[]): Promise =>
new Promise((resolve) => setTimeout(resolve, time, args));
const getItems = (q: string) =>
Promise.resolve(
['Красный', 'Оранжевый', 'Жёлтый', 'Зелёный', 'Голубой', 'Синий', 'Фиолетовый'].filter(
(x) => x.toLowerCase().includes(q.toLowerCase()) || x === q,
),
).then(delay(500));
const customLocale = {
TokenInput: {
addButtonComment: 'Нажмите точку',
},
};
return (
(
{item}
)}
/>
);
```
**ExampleDisabled**: Проп `disabled` блокирует поле с токенами. Поле меняет цвет на серый и становится недоступно для редактирования.
```tsx
const [selectedItems, setSelectedItems] = React.useState(['Красный', 'Синий']);
const delay =
(time: number) =>
(args?: string[]): Promise =>
new Promise((resolve) => setTimeout(resolve, time, args));
const getItems = (q: string): Promise =>
Promise.resolve(
['Красный', 'Оранжевый', 'Жёлтый', 'Зелёный', 'Голубой', 'Синий', 'Фиолетовый'].filter(
(x) => x.toLowerCase().includes(q.toLowerCase()) || x.toString() === q,
),
).then(delay(500));
return (
(
{item}
)}
/>
);
```
**ExampleError**: Проп `error` переводит поле с токенами в состояние ошибки. Поле подсвечивается красной рамкой.
```tsx
const [selectedItems, setSelectedItems] = React.useState(['Красный', 'Синий']);
const delay =
(time: number) =>
(args?: string[]): Promise =>
new Promise((resolve) => setTimeout(resolve, time, args));
const getItems = (q: string) =>
Promise.resolve(
['Красный', 'Оранжевый', 'Жёлтый', 'Зелёный', 'Голубой', 'Синий', 'Фиолетовый'].filter(
(x) => x.toLowerCase().includes(q.toLowerCase()) || x === q,
),
).then(delay(500));
return (
(
{item}
)}
/>
);
```
**ExampleRenderToken**: Проп `renderToken` задаёт функцию, которая отображает токен и даёт возможность кастомизировать внешний вид и поведение токена.
```tsx
const [selectedItems, setSelectedItems] = React.useState(['Красный', 'Синий', 'Зелёный']);
async function getItems(query) {
return ['Красный', 'Синий', 'Зелёный'].filter((s) => s.includes(query));
}
return (
(
{item}
)}
/>
);
```
**ExampleTotalCount**: Пропсы `totalCount` и `renderTotalCount` позволяют добавить в выпадающий список счётчик найденных значений. - `renderTotalCount` — задаёт функцию, которая отображает сообщение о количестве значений. - `totalCount` — определяет общее количество значений. В примере также настроено ограничение количества значений в результате поиска.
```tsx
const delay =
(time: number) =>
(args?: string[]): Promise =>
new Promise((resolve) => setTimeout(resolve, time, args));
const maxItems = 5;
const [totalCount, setTotalCount] = React.useState(cities.length);
const [value, setValue] = React.useState([]);
const getItems = (query: string): Promise => {
const items = cities
.map((x) => x.City)
.filter((x) => x.toLowerCase().includes(query.toLowerCase()) || x.toString() === query);
const result = items.slice(0, maxItems);
setTotalCount(items.length);
return Promise.resolve(result).then(delay(500));
};
const renderTotalCount = (foundCount: number, totalCount: number) => (
Показано {foundCount} из {totalCount} найденных городов
);
return (
);
```
**ExampleOnKeyDown**: Проп `onKeyDown` вызывает HTML-событие `onkeydown`. Вызывая `preventDefault` на его события можно нативно блокировать ввод конкретных символов.
```tsx
const [value, setValue] = React.useState([]);
const tokenInputRef = React.useRef(null);
const items = ['Красный', 'Оранжевый', 'Жёлтый', 'Зелёный', 'Голубой', 'Синий', 'Фиолетовый'];
const getItems = (query: string) => {
return Promise.resolve(items.filter((item) => item.includes(query)));
};
return (
ref={tokenInputRef}
selectedItems={value}
onValueChange={setValue}
getItems={getItems}
placeholder="Запрещён символ @"
onKeyDown={(e) => {
if (e.key === '@') {
e.preventDefault();
tokenInputRef.current?.blink();
}
}}
/>
);
```
**ExampleCustomItems**: В примере показано, как передать кастомный тип значений для справочника.
```tsx
const [selectedItems, setSelectedItems] = React.useState>([]);
const delay =
(time: number) =>
(args?: string[]): Promise =>
new Promise((resolve) => setTimeout(resolve, time, args));
const getGenericItems = () => [
{ id: '1', value: 'Красный' },
{ id: '2', value: 'Оранжевый' },
{ id: '3', value: 'Жёлтый' },
{ id: '4', value: 'Зелёный' },
];
const renderItem = (item: { id: string; value: string }) => item.value;
const renderValue = (value: { id: string; value: string }) => value.value;
const valueToItem = (item: string) => ({
id: Math.random().toString(),
value: item,
});
const getModelItems = async (query: string): Promise> => {
await delay(400);
return getGenericItems().filter((s) => s.value.includes(query));
};
return (
(
{item.value}
)}
/>
);
```
**ExampleExtendedItems**: В массиве, возвращаемом `getItems`, могут быть переданы React-компоненты: ``, ``, ` ` и любые другие. В таких случаях поиск необходимо контролировать дополнительно.
```tsx
const [selectedItems, setSelectedItems] = React.useState();
return (
[
MenuHeader ,
'Красный',
'Синий',
,
'Жёлтый',
'Зелёный',
MenuFooter ,
].filter((i) => (typeof i === 'string' ? i.toLowerCase().includes(q.toLowerCase()) : q === ''))
}
/>
);
```
**ExampleFuncDebounceAsync**: Функция `debounce` из lodash некорректно работает с `async/promise`, поэтому лучше использовать кастомную функцию, как в примере ниже.
```tsx
const [value, setValue] = React.useState([]);
function debounceAsync Promise>(callback: T, wait: number): T {
let timeoutId: NodeJS.Timeout | null = null;
return ((...args: string[]) => {
if (timeoutId) {
clearTimeout(timeoutId);
}
return new Promise((resolve) => {
const timeoutPromise = new Promise((resolve) => {
timeoutId = setTimeout(resolve, wait);
});
timeoutPromise.then(async () => {
resolve(await callback(...args));
});
});
}) as T;
}
const items = ['Красный', 'Оранжевый', 'Жёлтый', 'Зелёный', 'Голубой', 'Синий', 'Фиолетовый'];
const getItems = async (query: string): Promise => {
console.log('query: ', query);
return items.filter((item) => item.includes(query));
};
return (
);
```
---
## Tooltip
```jsx
import { Tooltip } from '@skbkontur/react-ui';
```
### Props
- **anchorElement?**: Указывает элемент, относительно которого позиционировать тултип.
- **className?**: Задает HTML-атрибут class.
- **closeButton?**: Отображает крестик для закрытия тултипа. По умолчанию крестик виден, если проп *trigger* не равен `hover` или `focus`.
- **render?**: Задает функцию, которая возвращает содержимое тултипа. Если функция вернула `null`, то тултип не показывается.
- **pos?**: Задает приоритетное расположение подсказки относительно текста.
- **size?**: Задает размер тултипа. (default: `'small'`)
- **trigger?**: Задает триггер открытия тултипа.
- **onCloseClick?**: Задает хендлер, который вызывается при клике по крестику.
- **onCloseRequest?**: Задает хендлер, который вызывается при клике по крестику или снаружи тултипа.
- **onClose?**: Задает хендлер, который вызывается при закрытии тултипа.
- **onOpen?**: Задает хендлер, который вызывается при открытии тултипа.
- **allowedPositions?**: Задает список позиций, которые тултип будет занимать. Если положение тултипа в определенной позиции будет выходить за край экрана, то будет выбрана следующая позиция. Обязательно должен включать позицию указанную в `pos`.
- **disableAnimations?**: Отключает анимацию. (default: `false`)
- **useWrapper?**: Явно указывает, что вложенные элементы должны быть обёрнуты в ` `. Используется для корректного позиционирования тултипа при двух и более вложенных элементах. _Примечание_: при **двух и более** вложенных элементах обёртка будет добавлена автоматически.
- **delayBeforeShow?**: Устанавливает задержку в миллисекундах до появления лоадера.
### Examples
```tsx
const tooltipContent = () => Подсказка, которая поможет понять сценарий
;
return (
);
tsx
const tooltipContent = () => Подсказка, которая поможет понять сценарий
;
return (
small
medium
large
);
tsx
const tooltipContent = () => (
Эта функциональность недоступна на вашем тарифе
Купить тариф Премиум
);
return (
}>
Что-то классное
);
```
**PositioningExample**: Тултип может располагаться в одной из 12 позиций.
```tsx
const S = 80;
const tooltipContent = (pos: string) => (
Позиция
'{pos}'
);
const absoluteAnchor = (top: number, left: number, pos: string) => (
tooltipContent(pos)} pos={pos} trigger="opened" closeButton={false}>
);
const blocks = [
{ top: S, left: S * 2, pos: 'top left' },
{ top: S, left: S * 4, pos: 'top center' },
{ top: S, left: S * 6, pos: 'top right' },
{ top: S * 2, left: S * 7, pos: 'right top' },
{ top: S * 4, left: S * 7, pos: 'right middle' },
{ top: S * 6, left: S * 7, pos: 'right bottom' },
{ top: S * 7, left: S * 6, pos: 'bottom right' },
{ top: S * 7, left: S * 4, pos: 'bottom center' },
{ top: S * 7, left: S * 2, pos: 'bottom left' },
{ top: S * 6, left: S, pos: 'left bottom' },
{ top: S * 4, left: S, pos: 'left middle' },
{ top: S * 2, left: S, pos: 'left top' },
];
return (
{blocks.map((block) => absoluteAnchor(block.top, block.left, block.pos))}
);
```
**ContextualHintsExample**: Пример реализации тултипа [контекстного обучения](https://guides.kontur.ru/principles/onbording/contextual-hints/).
```tsx
const [tooltipVisible, setTooltipVisible] = React.useState(false);
const tooltipTheme = ThemeFactory.create({
tooltipBg: '#2291FF',
tooltipTextColor: '#FFFFFF',
});
const tooltipContentTheme = ThemeFactory.create({
btnBacklessTextColor: '#FFFFFF',
btnBacklessBorderColor: '#FFFFFF8A',
btnBacklessHoverBg: '#FFFFFF0F',
btnBacklessActiveBg: '#FFFFFF1A',
});
const tooltipRef = React.useRef(null);
const tooltipContent = () => (
Заголовок контекстного обучения
Дополнительный текст в несколько строк, описывающий новую возможность
Понятно
);
const handleHideClick = () => {
if (tooltipRef.current) {
setTooltipVisible(false);
tooltipRef.current.hide();
}
};
const handleShowClick = () => {
if (tooltipRef.current) {
setTooltipVisible(true);
tooltipRef.current.show();
}
};
return (
} use="text">
Добавить запись
{tooltipVisible ? 'Скрыть' : 'Показать'} тултип
);
```
**CustomDelayExample**: Тултипы, которые открываются по наведению, появляются и скрываются с задержкой в 100 миллисекунд. Задержку перед появлением можно переопределить с помощью пропа `delayBeforeShow`.
```tsx
const [delay, setDelay] = React.useState(100);
const tooltipContent = () => `Я показался с задержкой в ${delay}мс`;
const handleDelayChange = (value: string) => {
const valueAsNumber = Number(value);
setDelay(isNaN(valueAsNumber) || valueAsNumber < 0 ? 0 : valueAsNumber);
};
return (
Задержка:
);
```
---
## TooltipMenu
```jsx
import { TooltipMenu } from '@skbkontur/react-ui';
```
### Props
- **menuMaxHeight?**: Задает максимальную высоту меню.
- **menuWidth?**: Задает ширину меню.
- **caption**: Задает элемент или функцию возвращающую элемент, которые используется вместо `caption`. В случае функции, внутри нее необходимо управлять открытием и закрытием меню.
- **header?**: Задает элемент, который будет отрендерен в шапке меню. _Примечание_: контрол MenuHeader передаётся только в `children` меню-контролов. Не стоит передавать `MenuHeader` в `header`.
- **footer?**: Задает элемент, который будет отрендерен в подвале меню. Перед элементом переданным в `footer` будет отрендерен MenuSeparator.
- **positions?**: Определяет список позиций, доступных для расположения выпадашки относительно caption. Если во всех позициях выпадашка вылезает за пределы `viewport`, будет использована первая из этого списка.
- **disableAnimations?**: Отключает анимацию.
### Examples
```tsx
return (
Открыть меню}>
Заголовок меню
Раз
Два
Три
Раз
Два
Три
);
```
**Example2**: В проп `caption` помимо компонента можно передать функцию, возвращающую компонент, с помощью которой можно управлять текущим состоянием тултип-меню.
```tsx
return (
{
return (
<>
Сейчас тултип-меню {opened ? 'окрыто' : 'закрыто'}
toggleMenu()}>Переключить меню
openMenu()}>Открыть меню
closeMenu()}>Закрыть меню
>
);
}}
>
Заголовок меню
Раз
Два
Три
Раз
Два
Три
);
tsx
return (
Открыть меню с заданной шириной} menuWidth={350}>
Заголовок меню
Раз
Два
Три
Раз
Два
Три
);
tsx
return (
Открыть меню с заданной высотой} menuMaxHeight={150}>
Заголовок меню
Раз
Два
Три
Раз
Два
Три
);
tsx
return (
Открыть меню без анимации}>
Заголовок меню
Раз
Два
Три
);
```
**Example6**: В `caption` можно передать любой элемент.
```tsx
return (
}
menuWidth="300px"
>
Раз
Два
Три
);
tsx
return (
}
menuWidth="300px"
positions={['right top', 'right middle', 'right bottom']}
>
Раз
Два
Три
);
tsx
return (
}
menuWidth="300px"
positions={['top right']}
>
Раз
Два
Три
);
tsx
return (
Это шапка в виде обычного текста}
footer={А это подвал в виде кнопки }
caption={Открыть меню }
>
Раз
Два
Три
);
tsx
return (
Открыть меню}>
MenuHeader
}>MenuItem1
}>MenuItem2
MenuItem3
);
tsx
return (
Открыть меню}>
MenuHeader
}>MenuItem1
}>MenuItem2
MenuItem3
);
```
**Example12**: (с сохранением поведения [MenuItem](#/Components/MenuItem)).
```tsx
const [showItems, setShowItems] = React.useState(false);
const hiddenItems = [
,
А я скрываюсь ,
И я ,
Я с вами ,
];
return (
setShowItems(!showItems)}>{showItems ? 'Спрятать' : 'Показать'} элементы
Открыть меню}>
Меня видно всегда
Меня тоже
Ага, и меня!
{showItems ? <>{hiddenItems}> : <>>}
);
```
---