@sberbusiness/triplex-next-mcp-server 1.48.0-beta → 1.50.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/Amount.md +2 -1
- package/bundle/components/Badge.md +2 -1
- package/bundle/components/BodyPage.md +2 -1
- package/bundle/components/Button.md +5 -1
- package/bundle/components/ButtonDropdownExtended.md +3 -1
- package/bundle/components/ButtonIcon.md +3 -1
- package/bundle/components/Calendar.md +3 -1
- package/bundle/components/CardAction.md +1 -0
- package/bundle/components/CheckboxXGroup.md +2 -1
- package/bundle/components/Chip.md +2 -1
- package/bundle/components/ChipMonthYearPicker.md +2 -1
- package/bundle/components/ChipMultiselect.md +4 -1
- package/bundle/components/ChipOptions.md +2 -1
- package/bundle/components/ChipSort.md +4 -1
- package/bundle/components/CollapsibleTree.md +2 -1
- package/bundle/components/ComposedKeyDownListener.md +4 -3
- package/bundle/components/DateField.md +7 -1
- package/bundle/components/Divider.md +85 -138
- package/bundle/components/DocumentNumberEdit.md +173 -214
- package/bundle/components/Dropdown.md +11 -1
- package/bundle/components/Ellipsis.md +131 -147
- package/bundle/components/FooterPage.md +3 -1
- package/bundle/components/FormField.md +2 -1
- package/bundle/components/Gap.md +1 -1
- package/bundle/components/Header.md +1 -0
- package/bundle/components/HeaderPage.md +3 -1
- package/bundle/components/HelpBox.md +2 -1
- package/bundle/components/IconWrapper.md +10 -6
- package/bundle/components/ImageGallery.md +2 -1
- package/bundle/components/ImageGalleryExtended.md +2 -1
- package/bundle/components/IslandAccordion.md +6 -1
- package/bundle/components/KeyDownListener.md +3 -2
- package/bundle/components/LightBox.md +7 -1
- package/bundle/components/List.md +2 -1
- package/bundle/components/ListActionItem.md +2 -1
- package/bundle/components/ListItem.md +3 -1
- package/bundle/components/ListItemControlsButton.md +2 -1
- package/bundle/components/ListItemSelectable.md +2 -1
- package/bundle/components/ListMasterFooter.md +2 -1
- package/bundle/components/ListMasterHeader.md +2 -1
- package/bundle/components/ListSortable.md +2 -1
- package/bundle/components/ListSortableItem.md +2 -1
- package/bundle/components/ListTableItem.md +4 -1
- package/bundle/components/LoaderMiddle.md +5 -1
- package/bundle/components/LoaderScreen.md +7 -1
- package/bundle/components/LoaderSmall.md +10 -4
- package/bundle/components/MaskedField.md +3 -1
- package/bundle/components/MediaWidth.md +2 -1
- package/bundle/components/MobileView.md +2 -1
- package/bundle/components/ModalWindow.md +4 -1
- package/bundle/components/MonthYearField.md +4 -1
- package/bundle/components/Notification.md +1 -0
- package/bundle/components/Overlay.md +7 -2
- package/bundle/components/Pagination.md +1 -7
- package/bundle/components/Radio.md +238 -115
- package/bundle/components/RadioXGroup.md +259 -0
- package/bundle/components/RadioYGroup.md +266 -0
- package/bundle/components/SegmentedControl.md +505 -158
- package/bundle/components/SelectExtendedField.md +2 -1
- package/bundle/components/SelectField.md +4 -1
- package/bundle/components/Skeleton.md +140 -69
- package/bundle/components/Spoiler.md +245 -63
- package/bundle/components/Step.md +257 -68
- package/bundle/components/SuggestField.md +30 -18
- package/bundle/components/Tabs.md +2 -1
- package/bundle/components/TabsLine.md +2 -1
- package/bundle/components/TextField.md +8 -4
- package/bundle/components/TextareaField.md +2 -1
- package/bundle/components/ThemeProvider.md +2 -1
- package/bundle/components/Tooltip.md +6 -3
- package/bundle/guides/context.md +4 -1
- package/bundle/manifest.json +32 -10
- package/bundle/release-notes/1.49.0.mdx +78 -0
- package/bundle/release-notes/1.50.0.mdx +35 -0
- package/bundle/release-notes/1.51.0.mdx +7 -0
- package/bundle/tokens.json +161 -11
- package/package.json +1 -1
|
@@ -1,9 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
component: DocumentNumberEdit
|
|
3
|
-
category:
|
|
4
|
-
|
|
5
|
-
figma-file: ""
|
|
6
|
-
related: [SmallInput, Link, Typography]
|
|
3
|
+
category: TextFields
|
|
4
|
+
related: [SmallInput, Link, Text]
|
|
7
5
|
tokens: []
|
|
8
6
|
stories: stories/DocumentNumberEdit/DocumentNumberEdit.stories.tsx
|
|
9
7
|
version: "1.0"
|
|
@@ -13,263 +11,194 @@ version: "1.0"
|
|
|
13
11
|
|
|
14
12
|
## Назначение
|
|
15
13
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
14
|
+
Инлайн-редактирование номера документа: строка с подписью («Документ № 123456» или текст об
|
|
15
|
+
автоматическом присвоении) и ссылка-переключатель рядом. По клику по ссылке подпись сжимается до
|
|
16
|
+
`numberLabel`, а на её месте появляется `SmallInput` с автофокусом, который принимает только цифры.
|
|
17
|
+
Редактирование завершается по Enter или по потере фокуса. Значение компонент не хранит: он
|
|
18
|
+
контролируемый — `value` и `onChange` ведёт потребитель.
|
|
20
19
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
ссылка `emptyNumberButtonLabel` ("Задать номер").
|
|
24
|
-
2. **Есть значение + не в режиме edit** — текст `{numberLabel} {value}`
|
|
25
|
-
+ ссылка `buttonLabel` ("Изменить").
|
|
26
|
-
3. **В режиме edit** — текст `numberLabel` + SmallInput (цифры, ≤6
|
|
27
|
-
символов).
|
|
20
|
+
Используй когда: в шапке документа или в плотной вёрстке нужно показать номер и дать его
|
|
21
|
+
поправить на месте, без открытия формы и без модального окна.
|
|
28
22
|
|
|
29
|
-
|
|
30
|
-
Не используй для: произвольного числового поля — бери `SmallInput` или
|
|
31
|
-
`NumberField`.
|
|
23
|
+
Не используй когда:
|
|
32
24
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
└── styles/DocumentNumberEdit.module.less # .label (inline-block + 8px right), .inputEditWrapper (68px)
|
|
42
|
-
```
|
|
43
|
-
|
|
44
|
-
Особенности:
|
|
45
|
-
- **НЕ `forwardRef`** — FC. Известный AI-ready debt.
|
|
46
|
-
- **Внутренний state `editingMode`** — компонент сам управляет
|
|
47
|
-
переключением "просмотр ↔ редактирование". Внешнего управления нет.
|
|
48
|
-
- **`value`** — controlled-only (без defaultValue).
|
|
49
|
-
- **Нет `__tests__`** — покрытие через `SmallInput`-тесты косвенно.
|
|
50
|
-
- **Нет `data-tx`** — не ставит атрибут версии.
|
|
25
|
+
- Поле живёт в форме и ему нужны лейбл, статус (error/warning), описание или счётчик — возьми
|
|
26
|
+
`TextField`.
|
|
27
|
+
- Нужен просто компактный `<input>` без подписи и переключения режимов — возьми `SmallInput`,
|
|
28
|
+
на котором `DocumentNumberEdit` и построен.
|
|
29
|
+
- Номер длиннее 6 символов или содержит не только цифры (серия, префикс, разделители):
|
|
30
|
+
`maxLength` ограничен литералами `0…6`, а нецифровые символы компонент вырезает молча.
|
|
31
|
+
- Нужно управлять режимом редактирования снаружи (открыть программно, запретить выход,
|
|
32
|
+
показать кнопки «Сохранить»/«Отмена»): режим — внутренний state без props и колбэков.
|
|
51
33
|
|
|
52
34
|
---
|
|
53
35
|
|
|
54
36
|
## Варианты и props
|
|
55
37
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
**DocumentNumberEdit-specific:**
|
|
59
|
-
|
|
60
|
-
| Prop | Тип | Default | Описание |
|
|
61
|
-
|---|---|---|---|
|
|
62
|
-
| `value` | `React.ReactText` (`string \| number`) | — | Текущий номер. Falsy → empty-state. |
|
|
63
|
-
| `numberLabel` | `string` | — (обязателен) | Префикс текста в view-mode ("Документ №"). |
|
|
64
|
-
| `emptyNumberLabel` | `string` | — (обязателен) | Полный текст в empty-state ("Номер документа будет присвоен автоматически"). |
|
|
65
|
-
| `buttonLabel` | `string` | — (обязателен) | Текст ссылки в view-mode ("Изменить"). |
|
|
66
|
-
| `emptyNumberButtonLabel` | `string` | — (обязателен) | Текст ссылки в empty-state ("Задать номер"). |
|
|
67
|
-
| `maxLength` | `0 \| 1 \| 2 \| 3 \| 4 \| 5 \| 6` | `6` | Максимум цифр. Влияет на `placeholder` (`"0"*maxLength`) и `SmallInput.maxLength`. |
|
|
68
|
-
|
|
69
|
-
**Остальные:** все пропсы `ISmallInputProps` (включая `onChange`,
|
|
70
|
-
`onBlur`, `onKeyDown`, `className`, `...`).
|
|
71
|
-
|
|
72
|
-
### `labelText` — вычисляемый
|
|
73
|
-
|
|
74
|
-
```ts
|
|
75
|
-
const labelText = useMemo(() => {
|
|
76
|
-
if (editingMode) return numberLabel; // только "Документ №"
|
|
77
|
-
if (value) return `${numberLabel} ${value}`; // "Документ № 12345"
|
|
78
|
-
return emptyNumberLabel; // "Номер ... будет присвоен автоматически"
|
|
79
|
-
}, [value, numberLabel, emptyNumberLabel, editingMode]);
|
|
80
|
-
```
|
|
81
|
-
|
|
82
|
-
---
|
|
83
|
-
|
|
84
|
-
## Ключевые особенности реализации
|
|
85
|
-
|
|
86
|
-
### State `editingMode` — внутренний uncontrolled switch
|
|
87
|
-
|
|
88
|
-
```ts
|
|
89
|
-
const [editingMode, setEditingMode] = useState(false);
|
|
90
|
-
```
|
|
91
|
-
|
|
92
|
-
**Включается:** клик по ссылке (`handleButtonClick`, `preventDefault()`).
|
|
93
|
-
**Выключается:** Enter в input (`handleInputKeyDown`) или blur input
|
|
94
|
-
(`handleInputBlur`).
|
|
95
|
-
|
|
96
|
-
**Нет `controlled` варианта** — потребитель не может извне открыть
|
|
97
|
-
редактирование. Если нужно — оборачивать в свою логику.
|
|
98
|
-
|
|
99
|
-
### Фильтрация цифр + сохранение позиции курсора
|
|
100
|
-
|
|
101
|
-
```ts
|
|
102
|
-
const handleInputChange = (event) => {
|
|
103
|
-
const caret = getCaretPosition(event.target);
|
|
104
|
-
const value = getFilteredValue(event.target.value);
|
|
105
|
-
const caretShift = value.length - event.target.value.length;
|
|
106
|
-
|
|
107
|
-
event.target.value = value;
|
|
108
|
-
setCaretPosition(event.target, caret + caretShift);
|
|
109
|
-
|
|
110
|
-
onChange?.(event);
|
|
111
|
-
};
|
|
112
|
-
|
|
113
|
-
const getFilteredValue = (value: string) => {
|
|
114
|
-
const buffer: string[] = [];
|
|
115
|
-
for (let i = 0; i < value.length; i++) {
|
|
116
|
-
if (StringUtils.isDigit(value[i])) buffer.push(value[i]);
|
|
117
|
-
}
|
|
118
|
-
return buffer.join("");
|
|
119
|
-
};
|
|
120
|
-
```
|
|
121
|
-
|
|
122
|
-
**Зачем `caretShift`:** если пользователь вставил "12a3" → фильтр
|
|
123
|
-
вернёт "123", длина уменьшилась на 1. Курсор должен сдвинуться на `-1`
|
|
124
|
-
относительно момента вставки, чтобы остаться на правильном месте.
|
|
125
|
-
Без этого курсор "прыгнет" в конец.
|
|
126
|
-
|
|
127
|
-
**`StringUtils.isDigit`** — проверка по регулярке (0-9). Не
|
|
128
|
-
поддерживает юникод-цифры, только ASCII.
|
|
129
|
-
|
|
130
|
-
**Мутация `event.target.value` напрямую** — это анти-паттерн, но
|
|
131
|
-
React принимает это, так как мы работаем с uncontrolled SmallInput
|
|
132
|
-
под капотом (controlled через onChange). Вариант: принять внешний
|
|
133
|
-
onChange(filteredValue), но тогда `value` не обновится моментально
|
|
134
|
-
до ре-рендера.
|
|
38
|
+
`IDocumentNumberEditProps` наследует `ISmallInputProps` (то есть
|
|
39
|
+
`React.InputHTMLAttributes<HTMLInputElement>`), поэтому принимает все атрибуты `<input>`.
|
|
135
40
|
|
|
136
|
-
###
|
|
41
|
+
### Обязательные props
|
|
137
42
|
|
|
138
|
-
|
|
139
|
-
|
|
43
|
+
| Prop | Тип | Описание |
|
|
44
|
+
|---|---|---|
|
|
45
|
+
| `numberLabel` | `string` | Текст перед номером («Документ №»). В режиме просмотра склеивается с `value` через пробел, в режиме редактирования остаётся один |
|
|
46
|
+
| `buttonLabel` | `string` | Текст ссылки-переключателя, когда номер задан («Изменить») |
|
|
47
|
+
| `emptyNumberButtonLabel` | `string` | Текст ссылки-переключателя, когда номера нет («Задать номер») |
|
|
48
|
+
| `emptyNumberLabel` | `string` | Текст вместо подписи с номером, когда номера нет («Номер документа будет присвоен автоматически») |
|
|
140
49
|
|
|
141
|
-
|
|
50
|
+
Все четыре текста обязательны и **дефолтов не имеют** — библиотека мультиязычная, строки в неё
|
|
51
|
+
не зашиты. Значения в скобках — примеры из stories, а не значения по умолчанию.
|
|
142
52
|
|
|
143
|
-
|
|
144
|
-
<Link href="#" onClick={handleButtonClick}>
|
|
145
|
-
{value ? buttonLabel : emptyNumberButtonLabel}
|
|
146
|
-
</Link>
|
|
147
|
-
```
|
|
148
|
-
|
|
149
|
-
**`href="#"`** — чтобы ссылка была "настоящей" (правильный фокус,
|
|
150
|
-
подчёркивание). `preventDefault` в обработчике клика предотвращает
|
|
151
|
-
прыжок страницы наверх.
|
|
152
|
-
|
|
153
|
-
**Альтернатива — `<button>`**, но дизайн требует ссылочного вида.
|
|
154
|
-
Компромисс: семантика button + визуал Link.
|
|
53
|
+
### Опциональные props
|
|
155
54
|
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
55
|
+
| Prop | Тип | По умолчанию | Описание |
|
|
56
|
+
|---|---|---|---|
|
|
57
|
+
| `value` | `React.ReactText` (`string \| number`) | — | Номер документа. Тип сужен относительно `<input>`: `readonly string[]` не принимается |
|
|
58
|
+
| `maxLength` | `0 \| 1 \| 2 \| 3 \| 4 \| 5 \| 6` | `6` | Максимальная длина ввода. Из неё же строится плейсхолдер — маска из нулей той же длины (`6` → `000000`) |
|
|
59
|
+
| `className` | `string` | — | Мерджится через `clsx` на корневой `<div>` |
|
|
60
|
+
| `onChange` | `React.ChangeEventHandler<HTMLInputElement>` | — | Вызывается уже после фильтрации: в `event.target.value` лежат только цифры |
|
|
61
|
+
| `onKeyDown` | `React.KeyboardEventHandler<HTMLInputElement>` | — | Вызывается после внутренней обработки Enter |
|
|
62
|
+
| `onBlur` | `React.FocusEventHandler<HTMLInputElement>` | — | Вызывается после выхода из режима редактирования |
|
|
63
|
+
| `...InputHTMLAttributes` | — | — | Уходят на `SmallInput` через `...rest` — **только в режиме редактирования** |
|
|
162
64
|
|
|
163
|
-
|
|
164
|
-
сам обработать в `onKeyDown` и програмно вызвать blur.
|
|
65
|
+
### Что считается «номер задан»
|
|
165
66
|
|
|
166
|
-
|
|
67
|
+
Проверка — на правдивость `value`, а не на `undefined`: при `value={0}`, `value=""` и
|
|
68
|
+
`value={undefined}` компонент одинаково показывает `emptyNumberLabel` и
|
|
69
|
+
`emptyNumberButtonLabel`, а в поле ввода подставляет пустую строку. Числовой ноль как номер
|
|
70
|
+
документа не поддерживается.
|
|
167
71
|
|
|
168
|
-
|
|
169
|
-
Визуально показывает сколько символов ожидается. Меняется с maxLength.
|
|
72
|
+
### Фильтрация и курсор
|
|
170
73
|
|
|
171
|
-
|
|
74
|
+
`onChange` перехватывается: из значения вырезаются все нецифровые символы
|
|
75
|
+
(`StringUtils.isDigit`), отфильтрованное значение записывается прямо в `event.target.value`, а
|
|
76
|
+
курсор сдвигается на разницу длин — чтобы вставка `12ab34` в середину не уводила курсор в конец.
|
|
77
|
+
Потребитель получает уже очищенное значение, и любая своя валидация работает поверх цифр.
|
|
172
78
|
|
|
173
|
-
|
|
174
|
-
.inputEditWrapper { width: 68px; }
|
|
175
|
-
```
|
|
79
|
+
### Контролируемость
|
|
176
80
|
|
|
177
|
-
|
|
178
|
-
`
|
|
179
|
-
|
|
180
|
-
должны переопределять ширину через className/style на wrapper**
|
|
181
|
-
(но wrapper — внутренний, override невозможен без хака).
|
|
81
|
+
В поле ввода всегда уходит `value || ""`, то есть `<input>` контролируемый. Если потребитель не
|
|
82
|
+
обновляет `value` в `onChange`, ввод визуально не применяется. Остальные props для этого сценария
|
|
83
|
+
(`defaultValue`, `readOnly`) не запрещены, но противоречат схеме контролируемого поля.
|
|
182
84
|
|
|
183
|
-
|
|
85
|
+
### Режим редактирования
|
|
184
86
|
|
|
185
|
-
|
|
87
|
+
Режим — внутренний `useState`, наружу не выведен: нет ни prop'а, ни колбэка на вход/выход. Вход —
|
|
88
|
+
только клик по ссылке, выход — Enter (по `event.code`) или потеря фокуса. Escape не обрабатывается,
|
|
89
|
+
отката значения нет: всё, что потребитель успел записать в свой state, остаётся. `...rest`
|
|
90
|
+
доходит только до поля ввода, поэтому `disabled` ссылку-переключатель не выключает: вход в режим
|
|
91
|
+
редактирования остаётся доступен, а выйти из него уже нельзя — disabled-поле не получает фокус
|
|
92
|
+
(`autoFocus` становится no-op), поэтому ни `blur`, ни Enter не срабатывают, ссылка в этом режиме
|
|
93
|
+
не рендерится, и номер остаётся скрытым. Передавать `disabled` этому компоненту бессмысленно:
|
|
94
|
+
запрета редактирования он не даёт, а даёт тупиковое состояние.
|
|
186
95
|
|
|
187
|
-
|
|
188
|
-
.label { display: inline-block; margin-right: 8px; }
|
|
189
|
-
.inputEditWrapper { width: 68px; display: inline-block; }
|
|
190
|
-
```
|
|
96
|
+
---
|
|
191
97
|
|
|
192
|
-
|
|
193
|
-
grid/flex. Это требование дизайна — "в одну строку".
|
|
98
|
+
## Дизайн-токены
|
|
194
99
|
|
|
195
|
-
|
|
100
|
+
Собственных токенов у компонента нет: `styles/DocumentNumberEdit.module.less` задаёт только
|
|
101
|
+
раскладку — `display: inline-block` и `margin-right: 8px` на классе `.label` (он навешан и на
|
|
102
|
+
подпись, и на обёртку ссылки, поэтому правка отступа затронет оба элемента),
|
|
103
|
+
`display: inline-block` и `width: 68px` на обёртке поля (под шесть цифр). Самому корневому `<div>` стили ничего не
|
|
104
|
+
выставляют. Цвета и типографика приходят из вложенных компонентов и переопределяются через их
|
|
105
|
+
группы токенов (`ThemeProvider`, prop `tokens` — см. `ThemeProvider-ai.md`):
|
|
196
106
|
|
|
197
|
-
```
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
107
|
+
```text
|
|
108
|
+
SmallInput.Background
|
|
109
|
+
SmallInput.Color
|
|
110
|
+
SmallInput.PlaceholderColor
|
|
111
|
+
SmallInput.Shadow
|
|
112
|
+
Link.Text_Color_Default
|
|
113
|
+
Link.Text_Color_Hover
|
|
114
|
+
Link.Text_Color_Active
|
|
115
|
+
Link.BorderColor_Focus
|
|
202
116
|
```
|
|
203
117
|
|
|
204
|
-
|
|
118
|
+
Подпись и обёртку ссылки рендерит `Text` (`ETextSize.B3`, `EFontType.SECONDARY`): размер и цвет
|
|
119
|
+
подписи — из токенов типографики. Цвет самого текста ссылки задаёт `Link` своими токенами
|
|
120
|
+
(`Link.Text_Color_*` стоят на `.link` и перебивают унаследованный от `Text` цвет), поэтому
|
|
121
|
+
перекрашивать переключатель нужно через группу `Link`, а не через типографику.
|
|
205
122
|
|
|
206
123
|
---
|
|
207
124
|
|
|
208
125
|
## Инварианты
|
|
209
126
|
|
|
210
|
-
- **`
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
-
|
|
224
|
-
|
|
225
|
-
-
|
|
127
|
+
- **`forwardRef<HTMLDivElement>`** — не убирать. `ref` указывает на корневой
|
|
128
|
+
`<div class="documentNumberEdit">`, **не** на поле ввода: поле существует только в режиме
|
|
129
|
+
редактирования. Доступ к `<input>` по ref не предусмотрен.
|
|
130
|
+
- **Корневой элемент — `<div>`** с двумя детьми: подпись (`Text`) и либо обёртка поля
|
|
131
|
+
(`div.inputEditWrapper` → `SmallInput`), либо ссылка (`Text` → `Link`). Сам корень — обычный
|
|
132
|
+
блок и в строку текста не встраивается; в строку выстроены его дети (оба `inline-block`).
|
|
133
|
+
Смена тега сломает эту вёрстку и ref-контракт (`ref` типизирован как `HTMLDivElement`).
|
|
134
|
+
- **`displayName`** — `"DocumentNumberEdit"`. Не менять.
|
|
135
|
+
- **Имена `DocumentNumberEdit`, `IDocumentNumberEditProps`** и все четыре текстовых props —
|
|
136
|
+
публичный API, экспортируются из barrel `src/components/DocumentNumberEdit/index.ts`.
|
|
137
|
+
- **Обязательность `buttonLabel`, `emptyNumberButtonLabel`, `emptyNumberLabel`, `numberLabel`** —
|
|
138
|
+
часть публичного контракта. Сделать их опциональными с дефолтными русскими строками нельзя:
|
|
139
|
+
это и изменение API, и хардкод языка в мультиязычной библиотеке.
|
|
140
|
+
- **Тип `maxLength` — литеральный union `0…6`**, сужение относительно `number` у `<input>`.
|
|
141
|
+
Расширение или сужение диапазона — ломающее изменение.
|
|
142
|
+
- **Фильтрация до цифр и перезапись `event.target.value` до вызова `onChange`** — наблюдаемое
|
|
143
|
+
поведение, на него опираются потребители. Порядок «отфильтровать → выставить курсор →
|
|
144
|
+
вызвать `onChange`» менять нельзя.
|
|
145
|
+
- **Плейсхолдер выводится из `maxLength`** (`"0".repeat(maxLength)`), отдельного prop'а нет.
|
|
146
|
+
- **`autoFocus` на поле ввода** — обязателен: без него переход в режим редактирования требовал бы
|
|
147
|
+
второго клика.
|
|
148
|
+
- **Ширина `inputEditWrapper` 68px** рассчитана на шесть цифр шрифтом B3. При меньшем `maxLength`
|
|
149
|
+
поле не сжимается.
|
|
226
150
|
|
|
227
151
|
---
|
|
228
152
|
|
|
229
153
|
## Accessibility
|
|
230
154
|
|
|
231
|
-
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
-
|
|
235
|
-
через `...rest
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
---
|
|
246
|
-
|
|
247
|
-
## Дизайн-токены
|
|
248
|
-
|
|
249
|
-
**Нет собственных.** Все стили от `SmallInput`, `Link`, `Typography.Text`.
|
|
155
|
+
- Переключатель режима — `Link` (`<a href="#">` с `preventDefault`), то есть скринридер
|
|
156
|
+
объявляет его ссылкой, а не кнопкой. Заменить роль снаружи нельзя: props до `Link` не доходят.
|
|
157
|
+
С клавиатуры ссылка доступна (Tab + Enter), но это остаётся ссылкой-заглушкой.
|
|
158
|
+
- **Доступное имя поля ввода обязан задать потребитель** — `aria-label` или `aria-labelledby`
|
|
159
|
+
уходят на `SmallInput` через `...rest`. Видимая подпись «Документ №» с полем программно не
|
|
160
|
+
связана: `<label htmlFor>` компонент не рендерит и `id` не генерирует.
|
|
161
|
+
- Фокус при входе в режим редактирования уходит в поле (`autoFocus`). При выходе по Enter поле
|
|
162
|
+
размонтируется, и фокус **не возвращается** на ссылку — он оказывается на `<body>`. Если
|
|
163
|
+
компонент стоит в клавиатурном сценарии, верни фокус сам или ориентируй пользователя на выход
|
|
164
|
+
по Tab.
|
|
165
|
+
- Escape не обрабатывается: отменить правку клавиатурой нельзя, выход по Escape не предусмотрен.
|
|
166
|
+
- Смена режима ничего не анонсирует: `aria-live` области нет, изменение подписи скринридер сам
|
|
167
|
+
не озвучит.
|
|
168
|
+
- Компонент не хардкодит текстовые строки: все подписи и любые `aria-*` приходят из props.
|
|
250
169
|
|
|
251
170
|
---
|
|
252
171
|
|
|
253
172
|
## Связанные компоненты
|
|
254
173
|
|
|
255
|
-
- `SmallInput` (`src/components/SmallInput`) —
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
174
|
+
- `SmallInput` (`src/components/SmallInput/SmallInput.tsx`) — поле ввода, которое рендерится в
|
|
175
|
+
режиме редактирования, и источник типа props: `IDocumentNumberEditProps extends ISmallInputProps`.
|
|
176
|
+
`DocumentNumberEdit` фиксирует у него `value`, `placeholder`, `maxLength`, `autoFocus` и все три
|
|
177
|
+
обработчика — переопределить их через `...rest` нельзя, они стоят после спреда.
|
|
178
|
+
- `Link` (`src/components/Link/Link.tsx`) — ссылка-переключатель режима. `DocumentNumberEdit`
|
|
179
|
+
передаёт ей только `href="#"` и свой `onClick`; настроить её из props потребителя нельзя.
|
|
180
|
+
- `Text` (`src/components/Typography/Text.tsx`) — рендерит и подпись, и обёртку ссылки
|
|
181
|
+
(`tag="div"`, `ETextSize.B3`, `EFontType.SECONDARY`). Типографику компонента менять можно только
|
|
182
|
+
здесь.
|
|
183
|
+
- `TextField` — альтернатива для полноформатного поля в форме (лейбл, статусы, описание,
|
|
184
|
+
счётчик), когда инлайн-редактирования недостаточно.
|
|
260
185
|
|
|
261
186
|
---
|
|
262
187
|
|
|
263
188
|
## Stories
|
|
264
189
|
|
|
265
|
-
`stories/DocumentNumberEdit/DocumentNumberEdit.stories.tsx`
|
|
266
|
-
|
|
190
|
+
Основные истории: `stories/DocumentNumberEdit/DocumentNumberEdit.stories.tsx`
|
|
191
|
+
Файлы примеров: `stories/DocumentNumberEdit/examples/`
|
|
192
|
+
|
|
193
|
+
| Story | Example file | Что демонстрирует |
|
|
194
|
+
|---|---|---|
|
|
195
|
+
| `Playground` | `PlaygroundExample.tsx` | Интерактивный контроль всех четырёх текстов и `maxLength` на контролируемом значении |
|
|
196
|
+
| `Default` | `DefaultExample.tsx` | Минимальное использование: контролируемое значение в state потребителя |
|
|
197
|
+
| `VisualTests` | `VisualTestsExample.tsx` | Скриншот-регрессия: пустой номер и заполненный номер, у первого `play` открывает режим редактирования |
|
|
267
198
|
|
|
268
|
-
|
|
269
|
-
-
|
|
270
|
-
|
|
271
|
-
- `MaxLengths` — разные длины
|
|
272
|
-
- `Playground` — интерактив
|
|
199
|
+
Story ID участвуют в именах baseline-скриншотов (`documentnumberedit--default--xs.png`,
|
|
200
|
+
`documentnumberedit--visual-tests--xl.png`), поэтому переименование story требует перегенерации
|
|
201
|
+
baseline и удаления осиротевших файлов.
|
|
273
202
|
|
|
274
203
|
---
|
|
275
204
|
|
|
@@ -277,4 +206,34 @@ grid/flex. Это требование дизайна — "в одну стро
|
|
|
277
206
|
|
|
278
207
|
| Дата | Изменение |
|
|
279
208
|
|---|---|
|
|
280
|
-
| 2026-
|
|
209
|
+
| 2026-09-29 | Создан документ (TRI-34). AI-рефакторинг: компонент переведён с `React.FC` на `forwardRef` (`ref` идёт на корневой `<div>`), фильтрация цифр вынесена в модульную функцию, `renderInput`/`renderButton` заменены на JSX в `return`, снят лишний `useMemo` с подписи, JSDoc на props перестал обещать несуществующие дефолты. Добавлены unit-тесты (`__tests__/DocumentNumberEdit.test.tsx`). Публичный API не изменён |
|
|
210
|
+
|
|
211
|
+
---
|
|
212
|
+
|
|
213
|
+
## Примеры использования
|
|
214
|
+
|
|
215
|
+
### Default
|
|
216
|
+
|
|
217
|
+
`stories/DocumentNumberEdit/examples/DefaultExample.tsx`
|
|
218
|
+
|
|
219
|
+
```tsx
|
|
220
|
+
import React, { useState } from "react";
|
|
221
|
+
import { DocumentNumberEdit } from "@sberbusiness/triplex-next";
|
|
222
|
+
|
|
223
|
+
export const DefaultExample = () => {
|
|
224
|
+
const [value, setValue] = useState("");
|
|
225
|
+
const handleChange = (event: React.ChangeEvent<HTMLInputElement>) => setValue(event.target.value);
|
|
226
|
+
|
|
227
|
+
return (
|
|
228
|
+
<DocumentNumberEdit
|
|
229
|
+
value={value}
|
|
230
|
+
buttonLabel="Изменить"
|
|
231
|
+
emptyNumberButtonLabel="Задать номер"
|
|
232
|
+
emptyNumberLabel="Номер документа будет присвоен автоматически"
|
|
233
|
+
numberLabel="Документ №"
|
|
234
|
+
onChange={handleChange}
|
|
235
|
+
/>
|
|
236
|
+
);
|
|
237
|
+
};
|
|
238
|
+
```
|
|
239
|
+
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
component: Dropdown
|
|
3
3
|
category: Dropdown
|
|
4
|
-
related: [ButtonDropdown, ButtonDropdownExtended, Portal, MobileView,
|
|
4
|
+
related: [ButtonDropdown, ButtonDropdownExtended, Portal, MobileView, LoaderSmall]
|
|
5
5
|
tokens:
|
|
6
6
|
- Dropdown.Background
|
|
7
7
|
- Dropdown.Shadow
|
|
@@ -181,6 +181,14 @@ DropdownMobileList.Selected_Background
|
|
|
181
181
|
- `Portal`, `MobileView`, `OverlayBase` — инфраструктура рендера.
|
|
182
182
|
- `ButtonDropdown`, `ButtonDropdownExtended` — готовые кнопки с выпадающим блоком поверх `Dropdown`.
|
|
183
183
|
|
|
184
|
+
**Потребители внутри библиотеки** (в `related` не входят — по `docs/ai/CONTEXT.md` ссылка
|
|
185
|
+
стоит с их стороны, и она там уже есть):
|
|
186
|
+
|
|
187
|
+
- `SelectExtendedField` — база полей выбора: владеет состоянием открытости и рендерит `Dropdown`.
|
|
188
|
+
- `SelectField`, `MultiselectField` — поля выбора одного и нескольких значений, построены на `SelectExtendedField`.
|
|
189
|
+
- `SuggestField` — поле с поиском по вводу; собрано на `Dropdown` напрямую, а не на `SelectExtendedField`.
|
|
190
|
+
- `TabsLine` — прячет не поместившиеся вкладки в `Dropdown`.
|
|
191
|
+
|
|
184
192
|
---
|
|
185
193
|
|
|
186
194
|
## Stories
|
|
@@ -210,6 +218,8 @@ DropdownMobileList.Selected_Background
|
|
|
210
218
|
| 2026-07-31 | Создан документ AI-ready для `Dropdown` (TRI-35). |
|
|
211
219
|
| 2026-07-31 | AI-рефакторинг: `DropdownList` больше не создаёт ref через `React.createRef()` в рендере (из-за этого автопрокрутка к активному элементу не работала), сброс активного элемента при открытии переведён на корректировку state в рендере вместо `setState` в эффекте, прокрутка вынесена в `desktop/utils.ts`, в `DropdownListItem` починен устаревший обработчик клавиатуры, в `Dropdown` актуальные `onOpen` / `onClose` хранятся в ref. Публичный API не изменён. Добавлены unit-тесты (109 кейсов) и stories. |
|
|
212
220
|
| 2026-07-31 | Правки по ревью PR #491: эффект `onOpen` / `onClose` в `Dropdown` сравнивает предыдущее значение `opened` вместо флага «смонтирован» — колбэки больше не срабатывают ложно при двойном маунте в React 18 StrictMode (регрессионный тест `__tests__/Dropdown.strictMode.test.tsx`). Документационные stories стартуют закрытыми и раскрываются через `play`. Сброс активного элемента `DropdownList` сделан симметричным: при закрытии подсветка снимается вместе с `activeDescendant`. |
|
|
221
|
+
| 2026-09-28 | Токен `Dropdown.Shadow` ссылается на `Shadow.07` (светлая) и `DarkShadow.07` (тёмная) вместо литералов; в тёмной теме прозрачность 0.25 вместо 0.35. |
|
|
222
|
+
| 2026-09-29 | Аудит симметрии `related` (TRI-156): из `related` убраны имена без строки в `docs/ai/ROADMAP.md` (`OverlayBase`) и потребители `SelectField` / `SelectExtendedField` / `SuggestField` / `MultiselectField` / `TabsLine` — по `docs/ai/CONTEXT.md` ссылка стоит с их стороны; добавлен `LoaderSmall` — контракт по рендеру в верную сторону. Пояснения к убранным именам сохранены прозой в «Связанные компоненты»; публичный API и поведение не затронуты. |
|
|
213
223
|
|
|
214
224
|
---
|
|
215
225
|
|