@sberbusiness/triplex-next-mcp-server 1.40.0-beta → 1.42.0-beta
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bundle/components/AlertProcess.md +460 -0
- package/bundle/components/Amount.md +145 -169
- package/bundle/components/Badge.md +155 -219
- package/bundle/components/Body.md +108 -116
- package/bundle/components/Calendar.md +183 -368
- package/bundle/components/CarouselExtended.md +355 -194
- package/bundle/components/Chip.md +310 -314
- package/bundle/components/ComposedKeyDownListener.md +323 -0
- package/bundle/components/FooterPage.md +2 -1
- package/bundle/components/FormField.md +468 -232
- package/bundle/components/HeaderPage.md +2 -1
- package/bundle/components/IconWrapper.md +142 -89
- package/bundle/components/Island.md +303 -123
- package/bundle/components/KeyDownListener.md +152 -100
- package/bundle/components/LoaderMiddle.md +265 -0
- package/bundle/components/LoaderScreen.md +178 -81
- package/bundle/components/LoaderSmall.md +230 -0
- package/bundle/components/MaskedField.md +481 -0
- package/bundle/components/MediaWidth.md +246 -108
- package/bundle/components/MobileView.md +192 -68
- package/bundle/components/ModalWindow.md +3 -1
- package/bundle/components/Page.md +3 -1
- package/bundle/components/Pagination.md +20 -0
- package/bundle/components/SelectExtendedField.md +691 -236
- package/bundle/components/ThemeProvider.md +305 -117
- package/bundle/components/Tooltip.md +421 -181
- package/bundle/guides/commits.md +54 -9
- package/bundle/guides/context.md +26 -0
- package/bundle/guides/stories.md +2 -0
- package/bundle/guides/template.md +1 -1
- package/bundle/manifest.json +63 -18
- package/bundle/release-notes/1.41.0.mdx +76 -0
- package/bundle/release-notes/1.41.1.mdx +18 -0
- package/bundle/release-notes/1.42.0.mdx +88 -0
- package/bundle/release-notes/1.43.0.mdx +7 -0
- package/package.json +1 -1
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
---
|
|
2
2
|
component: LoaderScreen
|
|
3
|
-
category:
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
stories: stories/Loaders/LoaderScreen
|
|
3
|
+
category: Loaders
|
|
4
|
+
related: [LoaderSmall, LoaderMiddle, Gap, Text, ModalWindowContent, LightBoxContent, LightBoxSideOverlay, List, TableBasic, MultiselectField]
|
|
5
|
+
tokens:
|
|
6
|
+
- --triplex-next-LoaderScreen-Small_Backdrop_Default
|
|
7
|
+
- --triplex-next-LoaderScreen-Middle_Backdrop_Default
|
|
8
|
+
stories: stories/Loaders/LoaderScreen/LoaderScreen.stories.tsx
|
|
9
9
|
version: "1.0"
|
|
10
10
|
---
|
|
11
11
|
|
|
@@ -13,117 +13,168 @@ version: "1.0"
|
|
|
13
13
|
|
|
14
14
|
## Назначение
|
|
15
15
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
16
|
+
Виджет-загрузчик: абсолютно позиционированный `<div>`, который перекрывает область ближайшего
|
|
17
|
+
позиционированного родителя полупрозрачной подложкой и показывает по её центру лоадер
|
|
18
|
+
(`LoaderSmall` или `LoaderMiddle`), опциональное описание и опциональные кнопки.
|
|
19
|
+
|
|
20
|
+
Используй когда: нужно закрыть уже отрисованный блок на время загрузки — тело модального окна,
|
|
21
|
+
контент LightBox, список, таблицу, выпадающий список поля. Так пользователь видит, какая именно
|
|
22
|
+
часть интерфейса перезагружается, и не теряет контекст страницы.
|
|
23
|
+
|
|
24
|
+
Не используй когда:
|
|
25
|
+
- Нужен просто индикатор загрузки без перекрытия контента — возьми `LoaderSmall` или `LoaderMiddle`
|
|
26
|
+
напрямую.
|
|
27
|
+
- Нужен полноэкранный лоадер приложения — LoaderScreen перекрывает только своего позиционированного
|
|
28
|
+
родителя, а не вьюпорт.
|
|
29
|
+
- Нужно заблокировать взаимодействие со страницей — LoaderScreen не ловушка фокуса и не модальный
|
|
30
|
+
оверлей, см. «Accessibility». Для блокирующего сценария возьми `ModalWindowContent` или
|
|
31
|
+
`LightBoxContent` с их встроенным `isLoading` — prop объявлен именно на контентных
|
|
32
|
+
субкомпонентах, а не на корневых `ModalWindow` / `LightBox`. У `LightBox` есть свой `isLoading`,
|
|
33
|
+
но он только отключает focus trap (`active={!isLoading}`) и лоадер не рендерит.
|
|
24
34
|
|
|
25
35
|
---
|
|
26
36
|
|
|
27
|
-
##
|
|
28
|
-
|
|
29
|
-
```text
|
|
30
|
-
src/components/LoaderScreen/
|
|
31
|
-
├── LoaderScreen.tsx # FC, <div> + lоадер
|
|
32
|
-
├── index.ts # Barrel
|
|
33
|
-
├── styles/LoaderScreen.module.less
|
|
34
|
-
└── __tests__/LoaderScreen.test.tsx
|
|
35
|
-
```
|
|
37
|
+
## Варианты и props
|
|
36
38
|
|
|
37
|
-
|
|
39
|
+
### Обязательные props
|
|
38
40
|
|
|
39
|
-
|
|
41
|
+
| Prop | Тип | Описание |
|
|
42
|
+
|---|---|---|
|
|
43
|
+
| `type` | `"small" \| "middle"` | Какой лоадер рендерится внутри и какой токен подложки используется. `small` → `LoaderSmall` с темой `ELoaderSmallTheme.BRAND`, `middle` → `LoaderMiddle` |
|
|
40
44
|
|
|
41
|
-
###
|
|
45
|
+
### Опциональные props
|
|
42
46
|
|
|
43
|
-
| Prop | Тип |
|
|
47
|
+
| Prop | Тип | По умолчанию | Описание |
|
|
44
48
|
|---|---|---|---|
|
|
45
|
-
| `
|
|
46
|
-
| `
|
|
47
|
-
| `
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
49
|
+
| `size` | `EComponentSize` | `EComponentSize.MD` | Размер лоадера. **Учитывается только при `type="small"`** — `LoaderMiddle` размеров не имеет и prop молча игнорируется |
|
|
50
|
+
| `description` | `React.ReactNode` | — | Текст под лоадером. Оборачивается в `Text` (`tag="div"`, `size={ETextSize.B2}`) с выравниванием по центру |
|
|
51
|
+
| `controls` | `React.ReactNode` | — | Кнопки под лоадером (например, «Отмена»). Рендерятся в простом `<div>` без раскладки — flex/gap задаёт потребитель |
|
|
52
|
+
|
|
53
|
+
Компонент расширяет `React.HTMLAttributes<HTMLDivElement>` — все стандартные атрибуты `<div>`
|
|
54
|
+
(`className`, `style`, `role`, `aria-*`, `data-*`, обработчики событий) попадают на корневой элемент.
|
|
55
|
+
|
|
56
|
+
### `ILoaderScreenMiddleProps`
|
|
57
|
+
|
|
58
|
+
`Omit<ILoaderScreenProps, "type" | "size">` — экспортируемый тип для компонентов, которые встраивают
|
|
59
|
+
LoaderScreen и сами фиксируют `type="middle"`. Потребителю остаются `description`, `controls`,
|
|
60
|
+
`className` и остальные атрибуты `<div>`. Используется в `ModalWindowContent`, `LightBoxContent`,
|
|
61
|
+
`LightBoxSideOverlay` как тип prop'а `loaderScreenProps`.
|
|
62
|
+
|
|
63
|
+
### Ограничения раскладки
|
|
64
|
+
|
|
65
|
+
- **Родитель обязан быть позиционированным** (`position: relative` / `absolute` / `fixed`).
|
|
66
|
+
Сам LoaderScreen — `position: absolute` с `left/top/right/bottom: 0`; при статическом родителе он
|
|
67
|
+
растянется по ближайшему позиционированному предку выше или по initial containing block.
|
|
68
|
+
Во всех stories и примерах обёртка явно получает `position: relative`.
|
|
69
|
+
- **Ширина контента ограничена 380px** (`.loaderContent`), содержимое центрируется по обеим осям.
|
|
70
|
+
Длинное `description` переносится, а не расширяет блок.
|
|
71
|
+
- **Отступы фиксированы:** `Gap size={24}` между лоадером и описанием и между описанием и кнопками.
|
|
72
|
+
Настроить их через props нельзя.
|
|
73
|
+
- **`z-index: @z-index-loader-screen`** (10100 из `src/helpers/less/z-indexes.less`). Значение выше
|
|
74
|
+
тултипа; `ModalWindowClose` намеренно поднимается ещё выше (`@z-index-loader-screen + 50`), чтобы
|
|
75
|
+
крестик оставался кликабельным поверх лоадера.
|
|
76
|
+
В обратную сторону: потребители `List` (`List.module.less`) и `TableBasic`
|
|
77
|
+
(`TableBasic.module.less`) **понижают** `z-index` до `@z-index-step` через собственный класс
|
|
78
|
+
(`.list .listLoaderScreen`, `.tableBasic .spinnerWrapper .tableLoaderScreen`), чтобы лоадер
|
|
79
|
+
перекрывал только своё содержимое, а не `LightBox` / `ModalWindow`. Держится это на специфичности
|
|
80
|
+
вложенного селектора (0,2,0 и выше против 0,1,0 у `.loaderScreen`) — **не повышай специфичность правил
|
|
81
|
+
`.loaderScreen`** в `LoaderScreen.module.less`, иначе оба потребителя молча сломаются, и
|
|
82
|
+
unit-тесты этого не поймают (ловит только visual regression).
|
|
51
83
|
|
|
52
84
|
---
|
|
53
85
|
|
|
54
|
-
##
|
|
55
|
-
|
|
56
|
-
### Два backdrop-класса по типу
|
|
86
|
+
## Дизайн-токены
|
|
57
87
|
|
|
58
|
-
```tsx
|
|
59
|
-
{
|
|
60
|
-
[styles.loaderSmallBackdrop]: type === "small",
|
|
61
|
-
[styles.loaderMiddleBackdrop]: type === "middle",
|
|
62
|
-
}
|
|
63
88
|
```
|
|
64
|
-
|
|
65
|
-
-
|
|
66
|
-
|
|
67
|
-
### Middle обёрнут в дополнительный `<div>`
|
|
68
|
-
|
|
69
|
-
```tsx
|
|
70
|
-
<div className={styles.loaderMiddleBackground}>
|
|
71
|
-
<LoaderMiddle />
|
|
72
|
-
</div>
|
|
89
|
+
--triplex-next-LoaderScreen-Small_Backdrop_Default (подложка при type="small")
|
|
90
|
+
--triplex-next-LoaderScreen-Middle_Backdrop_Default (подложка при type="middle")
|
|
73
91
|
```
|
|
74
92
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
### Игнорирование `theme`/`size` для `type="middle"`
|
|
93
|
+
Определены в `src/components/DesignTokens/components/LoaderScreen.ts`. Оба ссылаются на
|
|
94
|
+
`ColorNeutralAlpha.30` (в тёмной теме — `ColorDarkNeutralAlpha.30`), то есть визуально совпадают, но
|
|
95
|
+
разведены по типам намеренно — чтобы дизайн мог развести их независимо.
|
|
79
96
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
97
|
+
Там же объявлен третий токен `--triplex-next-LoaderScreen-Middle_Background_Default`, который стилями
|
|
98
|
+
LoaderScreen **не используется**: фон плашки `LoaderMiddle` берётся из
|
|
99
|
+
`--triplex-next-Loader-Middle_Background_Default`, а точки — из
|
|
100
|
+
`--triplex-next-Loader-Element_Background_Brand`. Токен остаётся в публичном наборе (его удаление —
|
|
101
|
+
breaking change для темизации), но опираться на него не нужно.
|
|
83
102
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
Компонент — `React.FC`, не `forwardRef`. Это отступление от общего правила
|
|
87
|
-
библиотеки (`forwardRef обязателен`). Известный legacy-момент; для нового
|
|
88
|
-
API-использования нужен ref — оборачивай снаружи.
|
|
103
|
+
Собственных цветов текста у компонента нет — они приходят из `Text` (`Typography`).
|
|
89
104
|
|
|
90
105
|
---
|
|
91
106
|
|
|
92
107
|
## Инварианты
|
|
93
108
|
|
|
94
|
-
- **`
|
|
95
|
-
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
-
|
|
109
|
+
- **`forwardRef`** — обязателен, `ref` идёт на корневой `<div>` подложки (не на внутренний лоадер).
|
|
110
|
+
- **Публичный API:** `LoaderScreen`, `ILoaderScreenProps`, `ILoaderScreenMiddleProps` экспортируются
|
|
111
|
+
через `src/components/LoaderScreen/index.ts`. `type` — строковый union `"small" | "middle"`,
|
|
112
|
+
а не enum; замена на enum — breaking change.
|
|
113
|
+
- **`ILoaderScreenMiddleProps` не мёртвый тип** — на нём держатся `loaderScreenProps` в
|
|
114
|
+
`ModalWindowContent`, `LightBoxContent` и `LightBoxSideOverlay`. Удалять нельзя.
|
|
115
|
+
- **`type` управляет и лоадером, и подложкой одновременно.** Разделять их на два props без задачи
|
|
116
|
+
не нужно: `TYPE_TO_BACKDROP_CLASS_NAME_MAP` в `LoaderScreen.tsx` держит это соответствие в одном месте.
|
|
117
|
+
- **Тема `LoaderSmall` захардкожена как `ELoaderSmallTheme.BRAND`** — `NEUTRAL` через LoaderScreen
|
|
118
|
+
недоступна. Пробрасывание темы наружу — расширение публичного API.
|
|
119
|
+
- **CSS-классы `loaderScreen`, `loaderSmallBackdrop`, `loaderMiddleBackdrop`** проверяются в
|
|
120
|
+
unit-тестах — переименование поймается прогоном. `loaderContent` и `description` тестами не
|
|
121
|
+
покрыты: они задают раскладку (ограничение ширины 380px и центрирование текста, см.
|
|
122
|
+
«Ограничения раскладки»), их переименование безопасно, но требует синхронной правки
|
|
123
|
+
`LoaderScreen.module.less`. `className` потребителя добавляется последним и базовые классы
|
|
124
|
+
не затирает.
|
|
125
|
+
- **Корневой элемент — `<div>`**: смена тега ломает абсолютное позиционирование потребителей.
|
|
99
126
|
|
|
100
127
|
---
|
|
101
128
|
|
|
102
129
|
## Accessibility
|
|
103
130
|
|
|
104
|
-
- `
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
131
|
+
- **Роль `status` живёт на внутреннем лоадере**, а не на корневом `<div>`: `LoaderSmall` и
|
|
132
|
+
`LoaderMiddle` сами выставляют `role="status"` и `aria-label="loading"`. Корневая подложка —
|
|
133
|
+
презентационный контейнер без роли.
|
|
134
|
+
- **`aria-label="loading"` захардкожен в компонентах семейства Loader** — это отклонение от правила
|
|
135
|
+
«не хардкодь aria-текст» (`docs/ai/codestyle.md`). Локализовать его через LoaderScreen нельзя;
|
|
136
|
+
фикс относится к `src/components/Loader/`.
|
|
137
|
+
- **Фокус не перехватывается и не возвращается.** LoaderScreen не focus trap: элементы под подложкой
|
|
138
|
+
остаются в tab-порядке и доступны с клавиатуры, хотя визуально закрыты. Если взаимодействие нужно
|
|
139
|
+
запретить по-настоящему — делай это на уровне потребителя (`disabled`, `inert`, снятие контента
|
|
140
|
+
из DOM) или используй модальные компоненты.
|
|
141
|
+
- **Мышь блокируется, клавиатура — нет.** Подложка перекрывает клики по контенту под собой просто
|
|
142
|
+
потому, что лежит сверху и имеет фон.
|
|
143
|
+
- Потребитель может передать `role`, `aria-live`, `aria-busy` и другие атрибуты через spread на
|
|
144
|
+
корневой `<div>` — компонент их не переопределяет.
|
|
145
|
+
- Анимация лоадеров непрерывная; `prefers-reduced-motion` семейством Loader не обрабатывается.
|
|
113
146
|
|
|
114
147
|
---
|
|
115
148
|
|
|
116
149
|
## Связанные компоненты
|
|
117
150
|
|
|
118
|
-
- `
|
|
119
|
-
- `
|
|
120
|
-
- `
|
|
151
|
+
- `LoaderSmall` — рендерится при `type="small"` с темой `BRAND` и переданным `size`.
|
|
152
|
+
- `LoaderMiddle` — рендерится при `type="middle"`; своих props не имеет.
|
|
153
|
+
- `Gap` — фиксированные отступы 24px между лоадером, описанием и кнопками.
|
|
154
|
+
- `Text` (`Typography`) — обёртка `description` (`ETextSize.B2`, выравнивание по центру).
|
|
155
|
+
- `ModalWindowContent` — `isLoading` + `loaderScreenProps?: ILoaderScreenMiddleProps`.
|
|
156
|
+
- `LightBoxContent`, `LightBoxSideOverlay` — то же через свои `loaderScreenProps`.
|
|
157
|
+
- `List` — при `loading` кладёт `LoaderScreen type="middle"` поверх списка.
|
|
158
|
+
- `TableBasic` — лоадер поверх тела таблицы.
|
|
159
|
+
- `MultiselectField` — `LoaderScreen type="small"` внутри выпадающего списка.
|
|
121
160
|
|
|
122
161
|
---
|
|
123
162
|
|
|
124
163
|
## Stories
|
|
125
164
|
|
|
126
|
-
`stories/Loaders/LoaderScreen
|
|
165
|
+
Основные истории: `stories/Loaders/LoaderScreen/LoaderScreen.stories.tsx`
|
|
166
|
+
Файлы примеров: `stories/Loaders/LoaderScreen/examples/`
|
|
167
|
+
|
|
168
|
+
| Story | Example file | Что демонстрирует |
|
|
169
|
+
|---|---|---|
|
|
170
|
+
| `Playground` | — | Интерактивный контроль `type`, `size`, `description` и наличия кнопок |
|
|
171
|
+
| `Default` | `DefaultExample.tsx` | Минимальный вариант: `type="small"`, `size="md"` в relative-обёртке |
|
|
172
|
+
| `Types` | `TypesExample.tsx` | `small` и `middle` рядом |
|
|
173
|
+
| `VisualTests` | — | Скриншот-состояния: `small` без текста, `middle` с описанием и кнопками, `middle` только с кнопками (анимации отключены) |
|
|
174
|
+
|
|
175
|
+
`VisualTests` помечена `tags: ["!autodocs", "!dev"]` — скрыта из docs и из сайдбара, как у
|
|
176
|
+
`LoaderMiddle` и `LoaderSmall`. Story ID (`loaders-loaderscreen--visual-tests`) при этом сохраняется,
|
|
177
|
+
скриншот-регрессия по ней продолжает прогоняться.
|
|
127
178
|
|
|
128
179
|
---
|
|
129
180
|
|
|
@@ -131,4 +182,50 @@ API-использования нужен ref — оборачивай снар
|
|
|
131
182
|
|
|
132
183
|
| Дата | Изменение |
|
|
133
184
|
|---|---|
|
|
134
|
-
| 2026-
|
|
185
|
+
| 2026-08-06 | Создан документ. AI-рефакторинг: порядок импортов по `codestyle.md`, JSDoc на компоненте, props и `ILoaderScreenMiddleProps` (включая значение по умолчанию для `size`), подложка вынесена в `TYPE_TO_BACKDROP_CLASS_NAME_MAP`, `className` в `clsx` перенесён в конец. Unit-тесты расширены: все значения `EComponentSize`, игнорирование `size` при `type="middle"`, класс подложки по типу, отсутствие `description` / `controls`, мердж `className`, spread rest-props, `forwardRef`. Публичный API, DOM и стили не изменены. |
|
|
186
|
+
|
|
187
|
+
---
|
|
188
|
+
|
|
189
|
+
## Примеры использования
|
|
190
|
+
|
|
191
|
+
### Default
|
|
192
|
+
|
|
193
|
+
`stories/Loaders/LoaderScreen/examples/DefaultExample.tsx`
|
|
194
|
+
|
|
195
|
+
```tsx
|
|
196
|
+
import React from "react";
|
|
197
|
+
import { LoaderScreen, EComponentSize } from "@sberbusiness/triplex-next";
|
|
198
|
+
|
|
199
|
+
export const DefaultExample = () => (
|
|
200
|
+
<div style={{ position: "relative", height: "200px", width: "300px" }}>
|
|
201
|
+
<LoaderScreen type="small" size={EComponentSize.MD} />
|
|
202
|
+
</div>
|
|
203
|
+
);
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
### Types
|
|
207
|
+
|
|
208
|
+
`stories/Loaders/LoaderScreen/examples/TypesExample.tsx`
|
|
209
|
+
|
|
210
|
+
```tsx
|
|
211
|
+
import React from "react";
|
|
212
|
+
import { LoaderScreen, EComponentSize } from "@sberbusiness/triplex-next";
|
|
213
|
+
|
|
214
|
+
export const TypesExample = () => (
|
|
215
|
+
<div style={{ display: "flex", gap: "32px", flexWrap: "wrap" }}>
|
|
216
|
+
<div>
|
|
217
|
+
<div style={{ marginBottom: "8px", fontSize: "16px", fontWeight: "700" }}>small</div>
|
|
218
|
+
<div style={{ position: "relative", height: "200px", width: "300px" }}>
|
|
219
|
+
<LoaderScreen type="small" size={EComponentSize.MD} />
|
|
220
|
+
</div>
|
|
221
|
+
</div>
|
|
222
|
+
<div>
|
|
223
|
+
<div style={{ marginBottom: "8px", fontSize: "16px", fontWeight: "700" }}>middle</div>
|
|
224
|
+
<div style={{ position: "relative", height: "200px", width: "300px" }}>
|
|
225
|
+
<LoaderScreen type="middle" />
|
|
226
|
+
</div>
|
|
227
|
+
</div>
|
|
228
|
+
</div>
|
|
229
|
+
);
|
|
230
|
+
```
|
|
231
|
+
|
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
---
|
|
2
|
+
component: LoaderSmall
|
|
3
|
+
category: Loaders
|
|
4
|
+
related: [LoaderMiddle, LoaderScreen, Button, Dropdown, ListItemLoading, SuggestField, SelectExtendedField]
|
|
5
|
+
tokens:
|
|
6
|
+
- --triplex-next-Loader-Element_Background_Brand
|
|
7
|
+
- --triplex-next-Loader-Element_Background_Neutral
|
|
8
|
+
stories: stories/Loaders/LoaderSmall/LoaderSmall.stories.tsx
|
|
9
|
+
version: "1.0"
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
# LoaderSmall
|
|
13
|
+
|
|
14
|
+
## Назначение
|
|
15
|
+
|
|
16
|
+
Компактный индикатор загрузки: три точки в ряд, гаснущие по очереди. Размер задаётся
|
|
17
|
+
общим для библиотеки `EComponentSize` (16 / 24 / 32 px по стороне), цвет — темой
|
|
18
|
+
(`ELoaderSmallTheme`). Анимация целиком на CSS, внутреннего состояния у компонента нет.
|
|
19
|
+
|
|
20
|
+
Используй когда: нужно показать загрузку внутри уже существующего элемента управления —
|
|
21
|
+
кнопки, поля с подсказками, выпадающего списка, строки списка.
|
|
22
|
+
|
|
23
|
+
Не используй когда:
|
|
24
|
+
- Грузится целая область или страница и место под крупный индикатор есть — возьми
|
|
25
|
+
`LoaderMiddle` (квадрат 64×64).
|
|
26
|
+
- Нужны подложка поверх контента, подпись и кнопки отмены — возьми `LoaderScreen`
|
|
27
|
+
(`type="small"` рендерит внутри себя именно `LoaderSmall`).
|
|
28
|
+
|
|
29
|
+
---
|
|
30
|
+
|
|
31
|
+
## Варианты и props
|
|
32
|
+
|
|
33
|
+
Компонент расширяет `React.HTMLAttributes<HTMLSpanElement>`: любой стандартный атрибут
|
|
34
|
+
`<span>` (`id`, `data-*`, `style`, обработчики) уходит на корневой элемент через `...rest`.
|
|
35
|
+
|
|
36
|
+
### Обязательные props
|
|
37
|
+
|
|
38
|
+
| Prop | Тип | Описание |
|
|
39
|
+
|---|---|---|
|
|
40
|
+
| `theme` | `ELoaderSmallTheme` | `BRAND` — брендовый цвет точек для светлых поверхностей; `NEUTRAL` — светлые точки для тёмных и цветных поверхностей (тёмная кнопка, цветной блок) |
|
|
41
|
+
| `size` | `EComponentSize` | `SM` — 16×16 (точки 4px), `MD` — 24×24 (6px), `LG` — 32×32 (8px) |
|
|
42
|
+
|
|
43
|
+
Оба props обязательны — значений по умолчанию нет, вызов `<LoaderSmall />` не типизируется.
|
|
44
|
+
Как правило `size` пробрасывается из размера родительского контрола: так делают `Button`,
|
|
45
|
+
`Dropdown`, `SuggestField`, `SelectExtendedField`.
|
|
46
|
+
|
|
47
|
+
### Опциональные props
|
|
48
|
+
|
|
49
|
+
Собственных опциональных props нет. `className` объединяется с базовыми классами
|
|
50
|
+
(`clsx`), а не заменяет их.
|
|
51
|
+
|
|
52
|
+
### Особенности API
|
|
53
|
+
|
|
54
|
+
- `ref` на корневой `<span>` **не пробрасывается** — компонент объявлен как `React.FC`
|
|
55
|
+
без `forwardRef`. Так же объявлен сосед `LoaderMiddle`. Если нужен доступ к DOM,
|
|
56
|
+
измеряй элемент-обёртку.
|
|
57
|
+
- Из barrel экспортируется enum `ELoaderSmallSize` (`sm` / `md` / `lg`). Компонентом он
|
|
58
|
+
**не используется** — размер задаётся `EComponentSize`. Enum остаётся в публичном API
|
|
59
|
+
ради обратной совместимости; в новом коде не применяй.
|
|
60
|
+
|
|
61
|
+
---
|
|
62
|
+
|
|
63
|
+
## Дизайн-токены
|
|
64
|
+
|
|
65
|
+
```text
|
|
66
|
+
--triplex-next-Loader-Element_Background_Brand
|
|
67
|
+
--triplex-next-Loader-Element_Background_Neutral
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
- `Element_Background_Brand` — цвет точек темы `BRAND` (`ColorBrand.50` в обеих темах).
|
|
71
|
+
- `Element_Background_Neutral` — цвет точек темы `NEUTRAL` (`ColorNeutralAlpha.0`).
|
|
72
|
+
|
|
73
|
+
Токены объявлены в `src/components/DesignTokens/components/Loader.ts` и общие для семейства
|
|
74
|
+
Loader: `Element_Background_Brand` использует также `LoaderMiddle`. Геометрия (размеры,
|
|
75
|
+
`gap`, радиус) и тайминги анимации (цикл 0.9s, сдвиг фазы 0.15s) заданы в
|
|
76
|
+
`styles/LoaderSmall.module.less` и снаружи не настраиваются.
|
|
77
|
+
|
|
78
|
+
---
|
|
79
|
+
|
|
80
|
+
## Инварианты
|
|
81
|
+
|
|
82
|
+
- **Публичный контракт — `React.FC` без `forwardRef`.** Добавление ref-проброса расширяет
|
|
83
|
+
публичный API и требует отдельной задачи и записи в release notes.
|
|
84
|
+
- **Корневой элемент — `<span role="status" aria-label="loading">`.** На эту связку опираются
|
|
85
|
+
`LoaderSmall.test.tsx` и `LoaderScreen.test.tsx` (кейсы `type="small"`) через
|
|
86
|
+
`getByRole("status", { name: "loading" })`, а `Button.test.tsx` — через
|
|
87
|
+
`getByRole("status")` без фильтра по имени.
|
|
88
|
+
- **Разметка внутренностей фиксирована:** ровно три `<span class="dot dot1|dot2|dot3">`.
|
|
89
|
+
Порядок и количество завязаны на задержки анимации (`.dot2` — 0.15s, `.dot3` — 0.3s);
|
|
90
|
+
удаление или перестановка ломают визуальную последовательность.
|
|
91
|
+
- **`...rest` разливается после `role` и `aria-label`** — потребитель может их переопределить.
|
|
92
|
+
Порядок атрибутов в JSX менять не нужно: это осознанная точка расширения.
|
|
93
|
+
- **`displayName = "LoaderSmall"`** — используется в React DevTools.
|
|
94
|
+
- **Значения `ELoaderSmallTheme`** (`brand` / `neutral`) и наличие `ELoaderSmallSize` в barrel —
|
|
95
|
+
часть публичного API, удаление или переименование — breaking change.
|
|
96
|
+
- **`ILoaderSmallProps` — не только props этого компонента.** `DropdownMobileLoader` строит из
|
|
97
|
+
него свой публичный интерфейс: `IDropownMobileLoaderProps extends Omit<ILoaderSmallProps,
|
|
98
|
+
"theme" | "size">` (`src/components/Dropdown/mobile/DropdownMobileLoader.tsx:8`). Любое
|
|
99
|
+
изменение формы `ILoaderSmallProps` автоматически меняет публичный API и этого компонента.
|
|
100
|
+
- **Токены `Loader-*` общие для семейства** — переименование ломает темизацию у потребителей
|
|
101
|
+
и затрагивает `LoaderMiddle`.
|
|
102
|
+
|
|
103
|
+
---
|
|
104
|
+
|
|
105
|
+
## Accessibility
|
|
106
|
+
|
|
107
|
+
- Корневой элемент имеет `role="status"` — неявная `aria-live="polite"` область: скринридер
|
|
108
|
+
сообщит о появлении лоадера, не прерывая текущее чтение.
|
|
109
|
+
- `aria-label="loading"` захардкожен в компоненте. Это отступление от общего правила
|
|
110
|
+
библиотеки «не хардкодить язык в aria-атрибутах»; менять нельзя без согласования — строка
|
|
111
|
+
входит в доступное имя, по которому лоадер находят тесты потребителей. Локализация
|
|
112
|
+
доступного имени — предмет отдельной задачи по всему семейству Loader. Потребитель может
|
|
113
|
+
перекрыть значение, передав свой `aria-label` (уйдёт в `...rest`).
|
|
114
|
+
- Компонент неинтерактивен: не получает фокус, не обрабатывает клавиатуру, текста не содержит.
|
|
115
|
+
Подпись к загрузке даёт родитель (например `LoaderScreen` через `description`).
|
|
116
|
+
- Анимация непрерывная и не отключается по `prefers-reduced-motion` — известное ограничение
|
|
117
|
+
семейства Loader.
|
|
118
|
+
|
|
119
|
+
---
|
|
120
|
+
|
|
121
|
+
## Связанные компоненты
|
|
122
|
+
|
|
123
|
+
- `LoaderMiddle` (`src/components/Loader/LoaderMiddle/LoaderMiddle.tsx`) — крупный квадратный
|
|
124
|
+
вариант без props, из того же barrel `src/components/Loader/index.ts`.
|
|
125
|
+
- `LoaderScreen` (`src/components/LoaderScreen/LoaderScreen.tsx`) — оверлей с лоадером,
|
|
126
|
+
подписью и кнопками; при `type="small"` рендерит `LoaderSmall` с темой `BRAND`.
|
|
127
|
+
- `Button` (`src/components/Button/Button.tsx`) — рендерит `LoaderSmall` **всегда**, а вне
|
|
128
|
+
состояния `loading` прячет обёртку классом (`Button.tsx:156`), поэтому `role="status"`
|
|
129
|
+
доступен и у обычной кнопки — не пиши тест вида `queryByRole("status")` → `null`.
|
|
130
|
+
Тему выбирает по теме кнопки (`SECONDARY` / `SECONDARY_LIGHT` → `BRAND`,
|
|
131
|
+
остальные → `NEUTRAL`), размер — по размеру кнопки.
|
|
132
|
+
- `Dropdown` (`DropdownList`, `DropdownMobileList`, `DropdownMobileLoader`), `SuggestField`,
|
|
133
|
+
`ChipSuggest`, `SelectExtendedField`, `ListItemLoading` — показывают `LoaderSmall` с темой
|
|
134
|
+
`BRAND` во время подгрузки данных.
|
|
135
|
+
|
|
136
|
+
---
|
|
137
|
+
|
|
138
|
+
## Stories
|
|
139
|
+
|
|
140
|
+
Основные истории: `stories/Loaders/LoaderSmall/LoaderSmall.stories.tsx`
|
|
141
|
+
Файлы примеров: `stories/Loaders/LoaderSmall/examples/`
|
|
142
|
+
|
|
143
|
+
| Story | Example file | Что демонстрирует |
|
|
144
|
+
|---|---|---|
|
|
145
|
+
| `Playground` | — | Интерактивный подбор `theme` и `size` |
|
|
146
|
+
| `Default` | `DefaultExample.tsx` | Минимальный вызов: тема `BRAND`, размер `MD` |
|
|
147
|
+
| `Themes` | `ThemesExample.tsx` | `BRAND` на светлом фоне и `NEUTRAL` на тёмной подложке |
|
|
148
|
+
| `Sizes` | `SizesExample.tsx` | Размеры `SM` / `MD` / `LG` в обеих темах |
|
|
149
|
+
| `VisualTests` | — | Скриншот-регрессия: сетка размеров × тем, анимации заглушены декоратором |
|
|
150
|
+
|
|
151
|
+
Файлы примеров сохраняют исторический постфикс `Example` (`DefaultExample.tsx`) —
|
|
152
|
+
переименование ломало бы ссылки на них без пользы, миграция именования не входит в задачу
|
|
153
|
+
(`docs/ai/stories-guide.md`).
|
|
154
|
+
|
|
155
|
+
Стори `Default`, `Themes` и `Sizes` исключены из скриншот-тестов (`testRunner: { skip: true }`):
|
|
156
|
+
их содержимое целиком повторяется в `VisualTests`, дублирующие снимки не нужны. Скриншоты
|
|
157
|
+
снимаются только со стори `VisualTests` (`loaders-loadersmall--visual-tests--xs|xl.png`).
|
|
158
|
+
|
|
159
|
+
---
|
|
160
|
+
|
|
161
|
+
## История изменений
|
|
162
|
+
|
|
163
|
+
| Дата | Изменение |
|
|
164
|
+
|---|---|
|
|
165
|
+
| 2026-08-06 | Создан документ. AI-рефакторинг: JSDoc на компоненте, props и значениях `ELoaderSmallTheme`, документирован неиспользуемый `ELoaderSmallSize`, порядок импортов приведён к codestyle, константы-маппинги переименованы в `THEME_TO_CLASS_NAME_MAP` / `SIZE_TO_CLASS_NAME_MAP`, в стилях объединено дублирующееся объявление анимации точек — сама анимация задана один раз на `.dot`, а у `.dot1` / `.dot2` / `.dot3` осталась только `animation-delay` (правило `.dot1` с нулевой задержкой обязательно: без него класс исчезнет из CSS-модуля и из разметки), unit-тесты расширены с 3 до 6 кейсов. В stories `controls: { disable: true }` перенесён на верхний уровень `parameters` — внутри `docs` он не применялся. Публичный API, DOM и визуальное поведение не изменены. |
|
|
166
|
+
|
|
167
|
+
---
|
|
168
|
+
|
|
169
|
+
## Примеры использования
|
|
170
|
+
|
|
171
|
+
### Default
|
|
172
|
+
|
|
173
|
+
`stories/Loaders/LoaderSmall/examples/DefaultExample.tsx`
|
|
174
|
+
|
|
175
|
+
```tsx
|
|
176
|
+
import React from "react";
|
|
177
|
+
import { LoaderSmall, ELoaderSmallTheme, EComponentSize } from "@sberbusiness/triplex-next";
|
|
178
|
+
|
|
179
|
+
export const DefaultExample = () => <LoaderSmall theme={ELoaderSmallTheme.BRAND} size={EComponentSize.MD} />;
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
### Themes
|
|
183
|
+
|
|
184
|
+
`stories/Loaders/LoaderSmall/examples/ThemesExample.tsx`
|
|
185
|
+
|
|
186
|
+
```tsx
|
|
187
|
+
import React from "react";
|
|
188
|
+
import { LoaderSmall, ELoaderSmallTheme, EComponentSize } from "@sberbusiness/triplex-next";
|
|
189
|
+
|
|
190
|
+
export const ThemesExample = () => (
|
|
191
|
+
<div style={{ display: "flex", gap: "32px", alignItems: "center" }}>
|
|
192
|
+
<div>
|
|
193
|
+
<div style={{ marginBottom: "8px", fontSize: "16px", fontWeight: "700" }}>BRAND</div>
|
|
194
|
+
<LoaderSmall theme={ELoaderSmallTheme.BRAND} size={EComponentSize.MD} />
|
|
195
|
+
</div>
|
|
196
|
+
<div>
|
|
197
|
+
<div style={{ marginBottom: "8px", fontSize: "16px", fontWeight: "700" }}>NEUTRAL</div>
|
|
198
|
+
<div style={{ display: "inline-flex", padding: "8px", borderRadius: "8px", background: "#1C1C1E" }}>
|
|
199
|
+
<LoaderSmall theme={ELoaderSmallTheme.NEUTRAL} size={EComponentSize.MD} />
|
|
200
|
+
</div>
|
|
201
|
+
</div>
|
|
202
|
+
</div>
|
|
203
|
+
);
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
### Sizes
|
|
207
|
+
|
|
208
|
+
`stories/Loaders/LoaderSmall/examples/SizesExample.tsx`
|
|
209
|
+
|
|
210
|
+
```tsx
|
|
211
|
+
import React from "react";
|
|
212
|
+
import { LoaderSmall, ELoaderSmallTheme, EComponentSize } from "@sberbusiness/triplex-next";
|
|
213
|
+
|
|
214
|
+
const SIZES = Object.values(EComponentSize);
|
|
215
|
+
|
|
216
|
+
export const SizesExample = () => (
|
|
217
|
+
<div style={{ display: "flex", flexDirection: "column", gap: "24px" }}>
|
|
218
|
+
{SIZES.map((size) => (
|
|
219
|
+
<div key={size} style={{ display: "flex", alignItems: "center", gap: "32px" }}>
|
|
220
|
+
<div style={{ width: "32px", fontSize: "16px", fontWeight: "700" }}>{size.toUpperCase()}</div>
|
|
221
|
+
<LoaderSmall theme={ELoaderSmallTheme.BRAND} size={size} />
|
|
222
|
+
<div style={{ display: "inline-flex", padding: "8px", borderRadius: "8px", background: "#1C1C1E" }}>
|
|
223
|
+
<LoaderSmall theme={ELoaderSmallTheme.NEUTRAL} size={size} />
|
|
224
|
+
</div>
|
|
225
|
+
</div>
|
|
226
|
+
))}
|
|
227
|
+
</div>
|
|
228
|
+
);
|
|
229
|
+
```
|
|
230
|
+
|