@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.
Files changed (36) hide show
  1. package/bundle/components/AlertProcess.md +460 -0
  2. package/bundle/components/Amount.md +145 -169
  3. package/bundle/components/Badge.md +155 -219
  4. package/bundle/components/Body.md +108 -116
  5. package/bundle/components/Calendar.md +183 -368
  6. package/bundle/components/CarouselExtended.md +355 -194
  7. package/bundle/components/Chip.md +310 -314
  8. package/bundle/components/ComposedKeyDownListener.md +323 -0
  9. package/bundle/components/FooterPage.md +2 -1
  10. package/bundle/components/FormField.md +468 -232
  11. package/bundle/components/HeaderPage.md +2 -1
  12. package/bundle/components/IconWrapper.md +142 -89
  13. package/bundle/components/Island.md +303 -123
  14. package/bundle/components/KeyDownListener.md +152 -100
  15. package/bundle/components/LoaderMiddle.md +265 -0
  16. package/bundle/components/LoaderScreen.md +178 -81
  17. package/bundle/components/LoaderSmall.md +230 -0
  18. package/bundle/components/MaskedField.md +481 -0
  19. package/bundle/components/MediaWidth.md +246 -108
  20. package/bundle/components/MobileView.md +192 -68
  21. package/bundle/components/ModalWindow.md +3 -1
  22. package/bundle/components/Page.md +3 -1
  23. package/bundle/components/Pagination.md +20 -0
  24. package/bundle/components/SelectExtendedField.md +691 -236
  25. package/bundle/components/ThemeProvider.md +305 -117
  26. package/bundle/components/Tooltip.md +421 -181
  27. package/bundle/guides/commits.md +54 -9
  28. package/bundle/guides/context.md +26 -0
  29. package/bundle/guides/stories.md +2 -0
  30. package/bundle/guides/template.md +1 -1
  31. package/bundle/manifest.json +63 -18
  32. package/bundle/release-notes/1.41.0.mdx +76 -0
  33. package/bundle/release-notes/1.41.1.mdx +18 -0
  34. package/bundle/release-notes/1.42.0.mdx +88 -0
  35. package/bundle/release-notes/1.43.0.mdx +7 -0
  36. package/package.json +1 -1
@@ -0,0 +1,323 @@
1
+ ---
2
+ component: ComposedKeyDownListener
3
+ category: KeyDownListener
4
+ related: [KeyDownListener, TriggerClickOnKeyDownEvent, SelectExtendedField, SliderExtended]
5
+ tokens: []
6
+ stories: stories/ComposedKeyDownListener/ComposedKeyDownListener.stories.tsx
7
+ version: "1.0"
8
+ ---
9
+
10
+ # ComposedKeyDownListener
11
+
12
+ ## Назначение
13
+
14
+ Сервисный headless-компонент, композитор `KeyDownListener`. Принимает массив
15
+ конфигураций `keyDownListeners` и рендерит по одному `KeyDownListener` на элемент
16
+ массива — соседями к `children`. Каждый слушатель подписывается на `keydown` на
17
+ `window` независимо, поэтому компонент — это только способ описать несколько
18
+ горячих клавиш одним объявлением вместо ручного вложения нескольких
19
+ `KeyDownListener`.
20
+
21
+ Собственной разметки не рендерит: `children` выводятся как есть, без обёртки;
22
+ без `children` (и при любом наборе слушателей) DOM остаётся пустым — сами
23
+ `KeyDownListener` рендерят `null`.
24
+
25
+ Используй когда: у экрана или оверлея несколько глобальных горячих клавиш с
26
+ разными обработчиками — `Enter` подтверждает, `Esc` отменяет, стрелки листают.
27
+
28
+ Не используй когда:
29
+ - Клавиша всего одна — достаточно `KeyDownListener` напрямую.
30
+ - Клавиши разные, но обработчик один — у `KeyDownListener` `eventKeyCode`
31
+ принимает массив кодов; различить нажатую можно по `event.keyCode`.
32
+ - Клавиша должна обрабатываться только внутри конкретного элемента — навесь
33
+ `onKeyDown` на сам элемент: слушатели здесь глобальные и сработают при вводе в
34
+ любое поле на странице.
35
+ - Нужно кликнуть по конкретной кнопке по нажатию клавиши — есть готовый
36
+ `TriggerClickOnKeyDownEvent`.
37
+
38
+ ---
39
+
40
+ ## Варианты и props
41
+
42
+ ### Обязательные props
43
+
44
+ | Prop | Тип | Описание |
45
+ |---|---|---|
46
+ | `keyDownListeners` | `IKeyDownListenerProps[]` | Конфигурация слушателей. Из каждого элемента используются только `eventKeyCode` и `onMatch`; поле `children` элемента игнорируется — содержимое задаётся `children` самого композитора |
47
+
48
+ ### Опциональные props
49
+
50
+ | Prop | Тип | По умолчанию | Описание |
51
+ |---|---|---|---|
52
+ | `children` | `React.ReactNode` | — | Рендерится как есть, без обёртки. При пустом `keyDownListeners` компонент сводится к `children` |
53
+
54
+ ### Особенности поведения
55
+
56
+ - Слушатели рендерятся **соседями** к `children`
57
+ (`<>{listeners}{children}</>`), а не оборачивают их. Поэтому `children` не
58
+ зависят от длины и порядка массива: набор горячих клавиш можно менять между
59
+ рендерами, содержимое при этом не перемонтируется — DOM-узлы, значения
60
+ инпутов, фокус и анимации сохраняются.
61
+ - `onMatch` вызываются в порядке элементов `keyDownListeners`: слушатели
62
+ монтируются в порядке массива, а `window` вызывает обработчики в порядке
63
+ подписки. На нём завязан только порядок вызова — «перехватить» событие у
64
+ соседнего слушателя нельзя, все подписаны на `window` независимо.
65
+ - Несколько элементов массива могут слушать одну и ту же клавишу — сработают
66
+ все.
67
+ - Слушатели подписываются на `window` **до** `children`, потому что монтируются
68
+ раньше них. Если внутри `children` есть собственный глобальный
69
+ `keydown`-обработчик той же клавиши, `onMatch` сработает первым.
70
+ - Компонент ничего не мемоизирует: массив можно собирать инлайн в рендере.
71
+ Каждый `KeyDownListener` читает свежие `eventKeyCode` и `onMatch` из props в
72
+ момент события, переподписки на новые ссылки не происходит.
73
+ - `keyDownListeners` сопоставляются между рендерами по индексу (`key={index}`).
74
+ Для элементов конфигурации это безопасно: `KeyDownListener` не рендерит
75
+ разметку и не хранит внутренний state, поэтому переиспользование инстанса по
76
+ позиции наблюдаемого эффекта не даёт.
77
+
78
+ ---
79
+
80
+ ## Дизайн-токены
81
+
82
+ Собственных стилей и CSS-переменных нет — компонент не рендерит разметку.
83
+
84
+ ---
85
+
86
+ ## Инварианты
87
+
88
+ - **Имя prop `keyDownListeners`, `children` и интерфейс
89
+ `IComposedKeyDownListenerProps`** (экспортируется из barrel
90
+ `src/components/KeyDownListener/index.ts`) — публичное API, не менять.
91
+ - **Элементы `keyDownListeners` типизированы `IKeyDownListenerProps`** —
92
+ переиспользование типа `KeyDownListener` осознанное; не заводить отдельный тип
93
+ ради «более узкого» `Pick`, это сузит публичный контракт.
94
+ - **Плоский рендер слушателей** — не возвращаться к вложению
95
+ (`reduce` с оборачиванием `children`): вложенность делает глубину дерева
96
+ зависимой от длины массива и перемонтирует `children` при её изменении.
97
+ - **Собственной разметки нет** — не добавлять обёртку (`div`, `span`) вокруг
98
+ `children`: потребители рассчитывают, что компонент прозрачен для layout,
99
+ в том числе внутри flex/grid-контейнеров.
100
+ - **Без `forwardRef`** — осознанное исключение, как и у `KeyDownListener`:
101
+ компонент не рендерит собственный host-элемент, форвардить `ref` некуда.
102
+ - Реализация остаётся React 17-совместимой (функциональный компонент без
103
+ React 18-only API): код синхронизируется в `release-0`.
104
+
105
+ ---
106
+
107
+ ## Accessibility
108
+
109
+ - Собственной ARIA-семантики нет — компонент прозрачен для accessibility-дерева
110
+ и не рендерит фокусируемых элементов.
111
+ - Горячие клавиши глобальные: перехватываются независимо от позиции фокуса и не
112
+ анонсируются screen reader'ом. Действия, повешенные на клавиши, должны быть
113
+ доступны и обычным путём (кнопкой) — шорткат не единственный способ выполнить
114
+ действие.
115
+ - Компонент не проверяет, где находится фокус: `Enter` и `Esc` сработают и во
116
+ время ввода в поле. Если это нежелательно, фильтруй в `onMatch` по
117
+ `event.target`.
118
+
119
+ ---
120
+
121
+ ## Связанные компоненты
122
+
123
+ - `KeyDownListener` (`src/components/KeyDownListener/KeyDownListener.tsx`) —
124
+ элементарный слушатель, из которого собирается композитор; там же описано
125
+ сравнение по устаревшему `event.keyCode` и константы `EVENT_KEY_CODES`.
126
+ - `TriggerClickOnKeyDownEvent` (`src/components/Triggers/`) — по нажатию клавиши
127
+ вызывает `click` на элементе из `targetRef`.
128
+ - `SelectExtendedField`, `SliderExtendedDot`, `SliderExtendedTrack` — потребители
129
+ `KeyDownListener` внутри библиотеки.
130
+
131
+ ---
132
+
133
+ ## Stories
134
+
135
+ Основные истории: `stories/ComposedKeyDownListener/ComposedKeyDownListener.stories.tsx`
136
+ Файлы примеров: `stories/ComposedKeyDownListener/examples/`
137
+
138
+ | Story | Example file | Что демонстрирует |
139
+ |---|---|---|
140
+ | `Playground` | `Playground.tsx` | Набор клавиш выбирается чекбоксами: слушатели пересобираются, введённый текст сохраняется |
141
+ | `Default` | `Default.tsx` | Две клавиши с разными обработчиками: `Enter` подтверждает, `Esc` отменяет |
142
+ | `WithDynamicListeners` | `WithDynamicListeners.tsx` | Слушатель добавляется на лету — `children` не перемонтируются |
143
+ | `Example` | `Example.tsx` | Панель подтверждения: горячие клавиши смонтированы вместе с панелью, те же действия доступны кнопками |
144
+
145
+ Набор целиком исключён из скриншот-тестов (`testRunner: { skip: true }` в `meta`):
146
+ собственной разметки компонент не рендерит, на скриншот попадала бы только
147
+ обвязка примеров. Поведение слушателей покрыто unit-тестами.
148
+
149
+ ---
150
+
151
+ ## История изменений
152
+
153
+ | Дата | Изменение |
154
+ |---|---|
155
+ | 2026-08-11 | Создан документ (TRI-112). Слушатели рендерятся соседями к `children` вместо вложения — смена длины `keyDownListeners` больше не перемонтирует содержимое (покрыто unit-тестом). Добавлен собственный набор stories по modern pattern; демонстрационная story `Example` перенесена из набора `KeyDownListener`. Публичный API не изменён. |
156
+
157
+ ---
158
+
159
+ ## Примеры использования
160
+
161
+ ### Default
162
+
163
+ `stories/ComposedKeyDownListener/examples/Default.tsx`
164
+
165
+ ```tsx
166
+ import React, { useState } from "react";
167
+ import { ComposedKeyDownListener, EVENT_KEY_CODES } from "@sberbusiness/triplex-next";
168
+
169
+ export const Default = () => {
170
+ const [result, setResult] = useState("Ожидание нажатия клавиши");
171
+
172
+ const keyDownListeners = [
173
+ { eventKeyCode: EVENT_KEY_CODES.ENTER, onMatch: () => setResult("Подтверждено по Enter") },
174
+ { eventKeyCode: EVENT_KEY_CODES.ESCAPE, onMatch: () => setResult("Отменено по Esc") },
175
+ ];
176
+
177
+ return (
178
+ <ComposedKeyDownListener keyDownListeners={keyDownListeners}>
179
+ <div style={{ display: "flex", flexDirection: "column", gap: "16px", maxWidth: "400px" }}>
180
+ <div>Две горячие клавиши с разными обработчиками: Enter подтверждает, Esc отменяет.</div>
181
+ <div style={{ border: "1px dashed rgb(125, 131, 138)", borderRadius: "4px", padding: "16px" }}>
182
+ {result}
183
+ </div>
184
+ </div>
185
+ </ComposedKeyDownListener>
186
+ );
187
+ };
188
+ ```
189
+
190
+ ### WithDynamicListeners
191
+
192
+ `stories/ComposedKeyDownListener/examples/WithDynamicListeners.tsx`
193
+
194
+ ```tsx
195
+ import React, { useState } from "react";
196
+ import { ComposedKeyDownListener, EVENT_KEY_CODES } from "@sberbusiness/triplex-next";
197
+
198
+ export const WithDynamicListeners = () => {
199
+ const [arrowsEnabled, setArrowsEnabled] = useState(false);
200
+ const [result, setResult] = useState("Ожидание нажатия клавиши");
201
+
202
+ const keyDownListeners = [
203
+ { eventKeyCode: EVENT_KEY_CODES.ESCAPE, onMatch: () => setResult("Отменено по Esc") },
204
+ ...(arrowsEnabled
205
+ ? [
206
+ {
207
+ eventKeyCode: [EVENT_KEY_CODES.ARROW_LEFT, EVENT_KEY_CODES.ARROW_RIGHT],
208
+ onMatch: (event: KeyboardEvent) =>
209
+ setResult(event.keyCode === EVENT_KEY_CODES.ARROW_LEFT ? "Стрелка влево" : "Стрелка вправо"),
210
+ },
211
+ ]
212
+ : []),
213
+ ];
214
+
215
+ return (
216
+ <ComposedKeyDownListener keyDownListeners={keyDownListeners}>
217
+ <div style={{ display: "flex", flexDirection: "column", gap: "16px", maxWidth: "400px" }}>
218
+ <div>Набор слушателей меняется на лету — содержимое при этом не перемонтируется.</div>
219
+ <input type="text" placeholder="Введите текст" style={{ padding: "8px", width: "100%" }} />
220
+ <label style={{ display: "flex", alignItems: "center", gap: "8px" }}>
221
+ <input
222
+ type="checkbox"
223
+ checked={arrowsEnabled}
224
+ onChange={(event) => setArrowsEnabled(event.target.checked)}
225
+ />
226
+ Добавить слушатель стрелок — введённый текст и фокус сохранятся
227
+ </label>
228
+ <div style={{ border: "1px dashed rgb(125, 131, 138)", borderRadius: "4px", padding: "16px" }}>
229
+ {result}
230
+ </div>
231
+ </div>
232
+ </ComposedKeyDownListener>
233
+ );
234
+ };
235
+ ```
236
+
237
+ ### Example
238
+
239
+ `stories/ComposedKeyDownListener/examples/Example.tsx`
240
+
241
+ ```tsx
242
+ import React, { useRef, useState } from "react";
243
+ import {
244
+ Button,
245
+ ComposedKeyDownListener,
246
+ EButtonTheme,
247
+ EComponentSize,
248
+ EVENT_KEY_CODES,
249
+ } from "@sberbusiness/triplex-next";
250
+
251
+ export const Example = () => {
252
+ const [opened, setOpened] = useState(true);
253
+ const [status, setStatus] = useState("Черновик не сохранён");
254
+ const panelRef = useRef<HTMLDivElement>(null);
255
+
256
+ const handleSave = () => {
257
+ setStatus("Черновик сохранён");
258
+ setOpened(false);
259
+ };
260
+
261
+ const handleCancel = () => {
262
+ setStatus("Сохранение отменено");
263
+ setOpened(false);
264
+ };
265
+
266
+ // Слушатели глобальные, поэтому Enter на сфокусированной кнопке панели дошёл бы
267
+ // и до onMatch, и до её onClick. Отфильтровываем такие события по event.target —
268
+ // действие кнопки должно выигрывать у горячей клавиши.
269
+ const handleSaveOnEnter = (event: KeyboardEvent) => {
270
+ if (event.target instanceof HTMLButtonElement && panelRef.current?.contains(event.target)) {
271
+ return;
272
+ }
273
+
274
+ handleSave();
275
+ };
276
+
277
+ // Слушатели монтируются только вместе с панелью, поэтому горячие клавиши
278
+ // работают лишь пока она открыта.
279
+ const keyDownListeners = [
280
+ { eventKeyCode: EVENT_KEY_CODES.ENTER, onMatch: handleSaveOnEnter },
281
+ { eventKeyCode: EVENT_KEY_CODES.ESCAPE, onMatch: handleCancel },
282
+ ];
283
+
284
+ if (!opened) {
285
+ return (
286
+ <div style={{ display: "flex", flexDirection: "column", gap: "16px", maxWidth: "400px" }}>
287
+ <div>{status}</div>
288
+ <Button theme={EButtonTheme.SECONDARY} size={EComponentSize.MD} onClick={() => setOpened(true)}>
289
+ Открыть панель снова
290
+ </Button>
291
+ </div>
292
+ );
293
+ }
294
+
295
+ return (
296
+ <ComposedKeyDownListener keyDownListeners={keyDownListeners}>
297
+ <div
298
+ ref={panelRef}
299
+ style={{
300
+ display: "flex",
301
+ flexDirection: "column",
302
+ gap: "16px",
303
+ maxWidth: "400px",
304
+ border: "1px solid rgb(125, 131, 138)",
305
+ borderRadius: "8px",
306
+ padding: "16px",
307
+ }}
308
+ >
309
+ <div>Сохранить черновик? Enter — сохранить, Esc — отменить.</div>
310
+ <div style={{ display: "flex", gap: "8px" }}>
311
+ <Button theme={EButtonTheme.GENERAL} size={EComponentSize.MD} onClick={handleSave}>
312
+ Сохранить
313
+ </Button>
314
+ <Button theme={EButtonTheme.SECONDARY} size={EComponentSize.MD} onClick={handleCancel}>
315
+ Отменить
316
+ </Button>
317
+ </div>
318
+ </div>
319
+ </ComposedKeyDownListener>
320
+ );
321
+ };
322
+ ```
323
+
@@ -39,7 +39,7 @@ version: "1.0"
39
39
 
40
40
  | Prop | Тип | По умолчанию | Описание |
41
41
  |---|---|---|---|
42
- | `sticky` | `boolean` | `false` | Футер прилипает к нижней границе при скролле. **Только для `type=FIRST`** (внутри `LightBox`); для `type=SECOND` типизирован как `never`. |
42
+ | `sticky` | `boolean` | `false` | Футер прилипает к нижней границе при скролле, если ширина viewport от 992px, а высота больше 800px. **Только для `type=FIRST`**; для `type=SECOND` типизирован как `never`. |
43
43
  | `size` | `EComponentSize` | — | Размер острова (Island). **Доступен только для `type=FIRST`**; для `type=SECOND` типизирован как `never`. |
44
44
 
45
45
  ### Ограничения по типам
@@ -111,6 +111,7 @@ version: "1.0"
111
111
 
112
112
  | Дата | Изменение |
113
113
  |---|---|
114
+ | 2026-08-06 | Sticky-позиционирование доступно вне `LightBox` при ширине viewport от 992px и отключено при высоте viewport не больше 800px. |
114
115
  | 2026-06-19 | Создан документ. AI-рефакторинг (clsx order, JSDoc на props, displayName), unit-тесты, миграция stories на modern pattern. |
115
116
 
116
117
  ---