@sberbusiness/triplex-next-mcp-server 1.50.0-beta → 1.53.1
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/README.md +1 -0
- package/bundle/components/AlertProcess.md +6 -3
- package/bundle/components/AlertProcessSpoiler.md +216 -0
- package/bundle/components/Badge.md +4 -3
- package/bundle/components/ButtonDropdown.md +5 -3
- package/bundle/components/Carousel.md +671 -0
- package/bundle/components/CarouselExtended.md +7 -5
- package/bundle/components/CarouselIndicators.md +666 -0
- package/bundle/components/CarouselItem.md +579 -0
- package/bundle/components/CarouselNextButton.md +633 -0
- package/bundle/components/CarouselPrevButton.md +633 -0
- package/bundle/components/CarouselViewport.md +631 -0
- package/bundle/components/CheckboxTreeExtended.md +21 -7
- package/bundle/components/CheckboxTreeExtendedCheckbox.md +591 -0
- package/bundle/components/CheckboxTreeExtendedNode.md +577 -0
- package/bundle/components/Chip.md +1 -0
- package/bundle/components/ChipDatePicker.md +332 -0
- package/bundle/components/ChipDatePickerTarget.md +316 -0
- package/bundle/components/ChipMonthYearPicker.md +4 -3
- package/bundle/components/ChipMonthYearPickerTarget.md +318 -0
- package/bundle/components/ChipMultiselect.md +63 -73
- package/bundle/components/ChipSelect.md +385 -0
- package/bundle/components/ChipSelectTarget.md +388 -0
- package/bundle/components/ChipSuggest.md +10 -16
- package/bundle/components/ChipSuggestTarget.md +506 -0
- package/bundle/components/CollapsibleTree.md +2 -1
- package/bundle/components/CollapsibleTreeExtended.md +2 -1
- package/bundle/components/DateField.md +4 -2
- package/bundle/components/DatePickerExtended.md +16 -14
- package/bundle/components/DateRange.md +17 -10
- package/bundle/components/Dropdown.md +36 -63
- package/bundle/components/EmptyView.md +9 -4
- package/bundle/components/ExpandAnimation.md +9 -5
- package/bundle/components/FormField.md +1 -1
- package/bundle/components/ImageGallery.md +19 -52
- package/bundle/components/ImageGalleryExtended.md +123 -139
- package/bundle/components/ListItemControlsButtonDropdown.md +3 -2
- package/bundle/components/LoaderSmall.md +5 -4
- package/bundle/components/MonthYearRange.md +334 -0
- package/bundle/components/MultiselectField.md +81 -143
- package/bundle/components/Notification.md +2 -1
- package/bundle/components/NotificationGrouped.md +174 -0
- package/bundle/components/NumberField.md +259 -116
- package/bundle/components/OrderedList.md +149 -112
- package/bundle/components/Overlay.md +3 -2
- package/bundle/components/PageIndicators.md +306 -0
- package/bundle/components/Pagination.md +81 -3
- package/bundle/components/PaginationExtended.md +160 -0
- package/bundle/components/Portal.md +5 -2
- package/bundle/components/Puller.md +375 -0
- package/bundle/components/SMSField.md +277 -102
- package/bundle/components/SelectExtendedField.md +11 -7
- package/bundle/components/SelectField.md +7 -4
- package/bundle/components/SmartSpot.md +259 -0
- package/bundle/components/Step.md +2 -1
- package/bundle/components/Stepper.md +7 -7
- package/bundle/components/Suggest.md +7 -6
- package/bundle/components/SuggestField.md +8 -6
- package/bundle/components/TableBasic.md +12 -14
- package/bundle/components/Tooltip.md +9 -8
- package/bundle/guides/commits.md +4 -1
- package/bundle/guides/context.md +5 -5
- package/bundle/guides/stories.md +29 -21
- package/bundle/manifest.json +145 -13
- package/bundle/release-notes/1.50.0.mdx +4 -0
- package/bundle/release-notes/1.51.0.mdx +57 -0
- package/bundle/release-notes/1.52.0.mdx +55 -0
- package/bundle/release-notes/1.53.0.mdx +60 -0
- package/bundle/release-notes/1.54.0.mdx +45 -0
- package/bundle/release-notes/1.55.0.mdx +7 -0
- package/bundle/tokens.json +377 -222
- package/dist/index.js +39 -1
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
component: DatePickerExtended
|
|
3
3
|
category: Date components
|
|
4
|
-
related: [Calendar, Dropdown, DateField, MonthYearField, ChipDatePicker, ChipMonthYearPicker]
|
|
4
|
+
related: [Calendar, Dropdown, Puller, DateField, MonthYearField, ChipDatePicker, ChipMonthYearPicker]
|
|
5
5
|
tokens: []
|
|
6
6
|
stories: stories/DatePickerExtended/DatePickerExtended.stories.tsx
|
|
7
7
|
version: "1.0"
|
|
@@ -11,7 +11,7 @@ version: "1.0"
|
|
|
11
11
|
|
|
12
12
|
## Назначение
|
|
13
13
|
|
|
14
|
-
Связка «произвольный целевой элемент + выпадающий календарь». Компонент рендерит целевой элемент (render-prop `renderTarget`) и `Calendar` внутри `Dropdown`, хранит состояние открытости календаря и отдаёт его целевому элементу через `DatePickerExtendedContext`. На мобильной ширине экрана вместо десктопного дропдауна открывается
|
|
14
|
+
Связка «произвольный целевой элемент + выпадающий календарь». Компонент рендерит целевой элемент (render-prop `renderTarget`) и `Calendar` внутри `Dropdown`, хранит состояние открытости календаря и отдаёт его целевому элементу через `DatePickerExtendedContext`. На мобильной ширине экрана вместо десктопного дропдауна открывается нижняя шторка `Puller` с заголовком из `renderDropdownHeaderTarget`.
|
|
15
15
|
|
|
16
16
|
Используй когда: строишь собственный компонент выбора даты с нестандартным триггером (поле, чип, кнопка) и хочешь переиспользовать готовую механику дропдауна с календарём. Именно так устроены `DateField`, `MonthYearField` и `ChipDatePicker`.
|
|
17
17
|
|
|
@@ -28,7 +28,7 @@ version: "1.0"
|
|
|
28
28
|
| Prop | Тип | Описание |
|
|
29
29
|
|---|---|---|
|
|
30
30
|
| `renderTarget` | `() => ReactNode` | Рендер целевого элемента. Вызывается внутри `DatePickerExtendedContext.Provider`, поэтому возвращённое дерево может читать и менять открытость календаря через контекст. |
|
|
31
|
-
| `renderDropdownHeaderTarget` | `() => ReactNode` | Рендер целевого элемента в заголовке мобильного дропдауна (`
|
|
31
|
+
| `renderDropdownHeaderTarget` | `() => ReactNode` | Рендер целевого элемента в заголовке мобильного дропдауна (`PullerHeader`). Обычно — `PullerInput` с текущим значением. |
|
|
32
32
|
| `pickedDate` | `string \| Moment \| null` | Выбранная дата (prop `Calendar`). Компонент неуправляемый только в части открытости — выбранную дату хранит потребитель. |
|
|
33
33
|
| `onDateChange` | `(date: Moment) => void` | Вызывается при выборе даты в календаре. Компонент дополнительно закрывает дропдаун. |
|
|
34
34
|
|
|
@@ -94,6 +94,7 @@ version: "1.0"
|
|
|
94
94
|
|
|
95
95
|
- `Calendar` — календарная сетка, рендерится внутри дропдауна; её props входят в `IDatePickerExtendedProps`, кроме `adaptiveMode`.
|
|
96
96
|
- `Dropdown` — выпадающее меню; из его props наружу выведен только `alignment`, мобильная версия конфигурируется внутри.
|
|
97
|
+
- `Puller` — мобильная вёрстка дропдауна (`PullerHeader`, `PullerClose`, `PullerBody`); заголовок обычно содержит `PullerInput`.
|
|
97
98
|
- `DateField` — поле ввода даты; передаёт `dropdownTargetRef` и открывает календарь из `DateFieldTarget` через контекст.
|
|
98
99
|
- `MonthYearField` — выбор месяца и года; конфигурирует `pickType={ECalendarPickType.MONTH_YEAR}`.
|
|
99
100
|
- `ChipDatePicker` — чип-фильтр с календарём; открывает дропдаун из `ChipDatePickerTarget` через контекст.
|
|
@@ -102,7 +103,7 @@ version: "1.0"
|
|
|
102
103
|
|
|
103
104
|
Внутренние части (в barrel не экспортируются, отдельного AI.md не имеют):
|
|
104
105
|
|
|
105
|
-
- `DatePickerExtendedDropdown` — дропдаун с `FocusTrap` вокруг календаря и мобильной версткой (`
|
|
106
|
+
- `DatePickerExtendedDropdown` — дропдаун с `FocusTrap` вокруг календаря и мобильной версткой (`PullerHeader` + `PullerBody`).
|
|
106
107
|
|
|
107
108
|
---
|
|
108
109
|
|
|
@@ -131,6 +132,7 @@ version: "1.0"
|
|
|
131
132
|
|
|
132
133
|
| Дата | Изменение |
|
|
133
134
|
|---|---|
|
|
135
|
+
| 2026-10-21 | Переход на `Puller` (TRIPLEX-961): мобильная версия собрана из частей `Puller` (бывший `DropdownMobile*`). |
|
|
134
136
|
| 2026-09-07 | В потребители базы добавлен `ChipMonthYearPicker` (чип-фильтр с `pickType` `MONTH_YEAR`) |
|
|
135
137
|
| 2026-08-24 | `DatePickerExtendedContext` и `IDatePickerExtendedContext` экспортируются из barrel — целевой элемент можно написать снаружи библиотеки. `adaptiveMode` исключён из `IDatePickerExtendedProps` (ломающее изменение, затрагивает также `DateField`, `MonthYearField`, `ChipDatePicker`) |
|
|
136
138
|
| 2026-08-20 | Создан документ. AI-рефакторинг, unit-тесты (`__tests__/DatePickerExtended.test.tsx`), stories по modern pattern. Исправлено: `yesterdayButtonProps` / `tomorrowButtonProps` пробрасываются в `Calendar`, `adaptiveMode` больше не попадает в DOM |
|
|
@@ -150,9 +152,9 @@ import {
|
|
|
150
152
|
Button,
|
|
151
153
|
DatePickerExtended,
|
|
152
154
|
DatePickerExtendedContext,
|
|
153
|
-
DropdownMobileInput,
|
|
154
155
|
EButtonTheme,
|
|
155
156
|
EComponentSize,
|
|
157
|
+
PullerInput,
|
|
156
158
|
} from "@sberbusiness/triplex-next";
|
|
157
159
|
|
|
158
160
|
const DATE_FORMAT = "YYYYMMDD";
|
|
@@ -189,7 +191,7 @@ export const Default = () => {
|
|
|
189
191
|
format={DATE_FORMAT}
|
|
190
192
|
onDateChange={(date) => setValue(date.format(DATE_FORMAT))}
|
|
191
193
|
renderTarget={() => <Target>{displayedValue}</Target>}
|
|
192
|
-
renderDropdownHeaderTarget={() => <
|
|
194
|
+
renderDropdownHeaderTarget={() => <PullerInput value={displayedValue} readOnly />}
|
|
193
195
|
/>
|
|
194
196
|
);
|
|
195
197
|
};
|
|
@@ -206,10 +208,10 @@ import {
|
|
|
206
208
|
Button,
|
|
207
209
|
DatePickerExtended,
|
|
208
210
|
DatePickerExtendedContext,
|
|
209
|
-
DropdownMobileInput,
|
|
210
211
|
EButtonTheme,
|
|
211
212
|
EComponentSize,
|
|
212
213
|
EDropdownAlignment,
|
|
214
|
+
PullerInput,
|
|
213
215
|
} from "@sberbusiness/triplex-next";
|
|
214
216
|
|
|
215
217
|
const DATE_FORMAT = "YYYYMMDD";
|
|
@@ -256,7 +258,7 @@ const AlignmentItem = ({ alignment }: IAlignmentItemProps) => {
|
|
|
256
258
|
format={DATE_FORMAT}
|
|
257
259
|
onDateChange={(date) => setValue(date.format(DATE_FORMAT))}
|
|
258
260
|
renderTarget={() => <Target>{displayedValue}</Target>}
|
|
259
|
-
renderDropdownHeaderTarget={() => <
|
|
261
|
+
renderDropdownHeaderTarget={() => <PullerInput value={displayedValue} readOnly />}
|
|
260
262
|
/>
|
|
261
263
|
</div>
|
|
262
264
|
);
|
|
@@ -282,7 +284,7 @@ import {
|
|
|
282
284
|
Button,
|
|
283
285
|
DatePickerExtended,
|
|
284
286
|
DatePickerExtendedContext,
|
|
285
|
-
|
|
287
|
+
PullerInput,
|
|
286
288
|
ECalendarPickType,
|
|
287
289
|
EButtonTheme,
|
|
288
290
|
EComponentSize,
|
|
@@ -337,7 +339,7 @@ const PickTypeItem = ({ pickType }: IPickTypeItemProps) => {
|
|
|
337
339
|
format={DATE_FORMAT}
|
|
338
340
|
onDateChange={(date) => setValue(date.format(DATE_FORMAT))}
|
|
339
341
|
renderTarget={() => <Target>{displayedValue}</Target>}
|
|
340
|
-
renderDropdownHeaderTarget={() => <
|
|
342
|
+
renderDropdownHeaderTarget={() => <PullerInput value={displayedValue} readOnly />}
|
|
341
343
|
/>
|
|
342
344
|
</div>
|
|
343
345
|
);
|
|
@@ -363,7 +365,7 @@ import {
|
|
|
363
365
|
Button,
|
|
364
366
|
DatePickerExtended,
|
|
365
367
|
DatePickerExtendedContext,
|
|
366
|
-
|
|
368
|
+
PullerInput,
|
|
367
369
|
ECalendarDateMarkType,
|
|
368
370
|
EButtonTheme,
|
|
369
371
|
EComponentSize,
|
|
@@ -422,7 +424,7 @@ export const WithMarkedAndDisabledDays = () => {
|
|
|
422
424
|
disabledDays={DISABLED_DAYS}
|
|
423
425
|
onDateChange={(date) => setValue(date.format(DATE_FORMAT))}
|
|
424
426
|
renderTarget={() => <Target>{displayedValue}</Target>}
|
|
425
|
-
renderDropdownHeaderTarget={() => <
|
|
427
|
+
renderDropdownHeaderTarget={() => <PullerInput value={displayedValue} readOnly />}
|
|
426
428
|
/>
|
|
427
429
|
);
|
|
428
430
|
};
|
|
@@ -439,9 +441,9 @@ import {
|
|
|
439
441
|
Button,
|
|
440
442
|
DatePickerExtended,
|
|
441
443
|
DatePickerExtendedContext,
|
|
442
|
-
DropdownMobileInput,
|
|
443
444
|
EButtonTheme,
|
|
444
445
|
EComponentSize,
|
|
446
|
+
PullerInput,
|
|
445
447
|
} from "@sberbusiness/triplex-next";
|
|
446
448
|
|
|
447
449
|
const DATE_FORMAT = "YYYYMMDD";
|
|
@@ -483,7 +485,7 @@ export const WithFooterButtons = () => {
|
|
|
483
485
|
tomorrowButtonProps={{ children: "Завтра" }}
|
|
484
486
|
onDateChange={(date) => setValue(date.format(DATE_FORMAT))}
|
|
485
487
|
renderTarget={() => <Target>{displayedValue}</Target>}
|
|
486
|
-
renderDropdownHeaderTarget={() => <
|
|
488
|
+
renderDropdownHeaderTarget={() => <PullerInput value={displayedValue} readOnly />}
|
|
487
489
|
/>
|
|
488
490
|
);
|
|
489
491
|
};
|
|
@@ -38,24 +38,28 @@ version: "1.0"
|
|
|
38
38
|
|
|
39
39
|
| Prop | Тип | По умолчанию | Описание |
|
|
40
40
|
|---|---|---|---|
|
|
41
|
+
| `format` | `string` | `dateFormatYYYYMMDD` | Формат дат moment в `value` и в `onChange` пикеров. Передаётся пикерам в render-props вместе с `value` и `onChange`. |
|
|
41
42
|
| `shiftAmount` | `number` | `1` | На сколько единиц `shiftUnit` сдвигаются обе границы за одно нажатие. |
|
|
42
43
|
| `shiftUnit` | `EDateRangeShiftUnit` | `MONTH` | Единица сдвига: `DAY`, `WEEK`, `MONTH`, `QUARTER`, `YEAR`. Значения enum — строки, которые напрямую уходят в `moment`. |
|
|
43
44
|
| `hideNavigation` | `boolean` | `false` | Скрывает обе кнопки сдвига: render-props кнопок не вызываются вовсе. |
|
|
44
45
|
|
|
45
46
|
### Формат значения
|
|
46
47
|
|
|
47
|
-
`TDateRangeValue` — `[start, end]`, обе даты строками в формате `
|
|
48
|
+
`TDateRangeValue` — `[start, end]`, обе даты строками в формате `format` (по умолчанию `YYYYMMDD` — `dateFormatYYYYMMDD` из `src/consts/DateConst`). Пустая строка означает «дата не выбрана»; допустимо состояние, когда заполнена только одна граница.
|
|
49
|
+
|
|
50
|
+
Формат задаётся на DateRange и приходит в объект пикера: при spread в `DateField` поле получает тот же `format`. Не задавай `format` полю отдельно: если он стоит в JSX до spread (`<DateField format="DD.MM.YYYY" {...props} />`), его перезапишет формат DateRange; если после — формат поля и диапазона разойдутся.
|
|
48
51
|
|
|
49
52
|
### Что получают render-props
|
|
50
53
|
|
|
51
54
|
| Поле | Кому | Значение |
|
|
52
55
|
|---|---|---|
|
|
53
56
|
| `value` | пикеры | Соответствующая граница диапазона (`start` или `end`). |
|
|
54
|
-
| `
|
|
57
|
+
| `format` | пикеры | Значение `format` компонента. При spread в `DateField` задаёт полю тот же формат. |
|
|
58
|
+
| `onChange` | пикеры | Принимает строку в формате `format`; внутри применяется согласование границ (ниже). |
|
|
55
59
|
| `children` | кнопки | Иконка направления (`CaretleftStrokeSrvIcon20` / `CaretrightStrokeSrvIcon20`). |
|
|
56
60
|
| `className` | кнопки | Класс кнопки из LESS-модуля; при неполном диапазоне к нему добавляется литерал `disabled` (см. «Инварианты»). |
|
|
57
61
|
| `disabled` | кнопки | `true`, пока заполнены не обе границы. Применить его к DOM-элементу — задача потребителя. |
|
|
58
|
-
| `onClick` | кнопки | Сдвиг диапазона. На неполном диапазоне обработчик выходит, не вызывая `onChange`. |
|
|
62
|
+
| `onClick` | кнопки | Сдвиг диапазона. На неполном диапазоне или при невалидной в `format` границе обработчик выходит, не вызывая `onChange`. |
|
|
59
63
|
|
|
60
64
|
Объекты сформированы так, чтобы их можно было целиком спредить в `DateField` (пикеры) и `ButtonIcon` (кнопки) — именно так устроены все примеры в stories.
|
|
61
65
|
|
|
@@ -67,14 +71,16 @@ version: "1.0"
|
|
|
67
71
|
|
|
68
72
|
- Новая дата «от» больше текущей «до» → `onChange([date, ""])`: дата «до» сбрасывается.
|
|
69
73
|
- Новая дата «до» меньше текущей «от» → `onChange(["", date])`: дата «от» сбрасывается.
|
|
74
|
+
- Равные даты допустимы: диапазон из одного дня сохраняется без сброса.
|
|
70
75
|
- Если новая дата пустая или противоположная граница пустая, проверка порядка не выполняется — значение записывается как есть.
|
|
71
|
-
-
|
|
76
|
+
- Даты сравниваются хронологически через `isDateAfter` в `utils.ts`: обе строго парсятся `moment(value, format, true)`. Сравнение строк не используется — для форматов вроде `DD.MM.YYYY` лексикографический порядок не совпадает с хронологическим.
|
|
77
|
+
- Если хотя бы одна из сравниваемых дат невалидна в `format`, порядок считается корректным и значение записывается как есть.
|
|
72
78
|
|
|
73
79
|
### Сдвиг диапазона
|
|
74
80
|
|
|
75
|
-
- Обе границы сдвигаются на одну и ту же величину (`shiftDateRange` в `utils.ts`, под капотом `moment().add()`)
|
|
76
|
-
- Сдвиг доступен только при обеих заполненных границах: иначе кнопкам приходит `disabled: true
|
|
77
|
-
-
|
|
81
|
+
- Обе границы сдвигаются на одну и ту же величину (`shiftDateRange` в `utils.ts`, под капотом `moment().add()`); парсинг и результат — в формате `format`. При сдвиге на месяцы, кварталы и годы число месяца клампится по длине целевого месяца, поэтому длина диапазона в днях может измениться: `20240331` минус месяц → `20240229`.
|
|
82
|
+
- Сдвиг доступен только при обеих заполненных границах: иначе кнопкам приходит `disabled: true`.
|
|
83
|
+
- Перед сдвигом обе границы строго парсятся `moment(value, format, true)`. Если хотя бы одна пустая или невалидна (в том числе не совпадает с `format`), `shiftDateRange` возвращает `null` и `onChange` не вызывается. При непустой, но невалидной границе кнопки остаются активными: `disabled` зависит только от пустых границ — так же, как в `MonthYearRange`.
|
|
78
84
|
|
|
79
85
|
---
|
|
80
86
|
|
|
@@ -90,9 +96,9 @@ version: "1.0"
|
|
|
90
96
|
|
|
91
97
|
- **`forwardRef` обязателен.** `ref` уходит на корневой `<div>` — туда же, куда `className` и остальные html-атрибуты. Убирать `forwardRef` нельзя.
|
|
92
98
|
- **К `className` кнопок при неполном диапазоне подмешивается литерал `disabled`**, а не класс LESS-модуля (класса `.disabled` в модуле нет). Строка уходит наружу, в `className` компонента потребителя, и на неё опираются тесты, поэтому «починка» на `styles.disabled` — изменение наблюдаемого поведения. `MonthYearRange` в том же месте ничего не подмешивает.
|
|
93
|
-
- **Формат дат `
|
|
99
|
+
- **Формат дат задаётся одним `format` на весь компонент**: по нему парсятся границы при согласовании и сдвиге. `value` и аргумент `onChange` пикера должны быть в этом формате; по умолчанию — `YYYYMMDD`, и смена значения по умолчанию — breaking change.
|
|
94
100
|
- Значения `EDateRangeShiftUnit` — это строковые единицы `moment` (`day`, `week`, `month`, `quarter`, `year`); переименование значений ломает и публичный API, и сдвиг.
|
|
95
|
-
- Публичный API (`DateRange`, `IDateRangeProps`, `IDateRangeButtonProvideProps`, `IDateRangePickerProvideProps`, `TDateRangeValue`, `EDateRangeShiftUnit`) экспортируется через `src/components/DateRange/index.ts`; `TDateRangeValue`
|
|
101
|
+
- Публичный API (`DateRange`, `IDateRangeProps`, `IDateRangeButtonProvideProps`, `IDateRangePickerProvideProps`, `TDateRangeValue`, `EDateRangeShiftUnit`) экспортируется через `src/components/DateRange/index.ts`; интерфейсы и `TDateRangeValue` живут в `types.ts`, компонент — в `DateRange.tsx`, enum — в `enums.ts`. `utils.ts` внутренний и в barrel не экспортируется, но `isDateAfter` и `shiftDateRange` импортирует `MonthYearRange` — не переименовывать и не менять сигнатуры без проверки этого потребителя.
|
|
96
102
|
- Отступы завязаны на подстроки `sm` / `md` / `lg` в классе вложенного поля — переименование классов размеров в `FormField`-полях тихо сломает выравнивание.
|
|
97
103
|
- Компонент не хардкодит текст: доступные имена полей и кнопок задаёт потребитель — библиотека мультиязычная.
|
|
98
104
|
- React 17-совместимость (ветка `release-0`): не переводить на `useId` и другие React 18-only API.
|
|
@@ -112,7 +118,7 @@ version: "1.0"
|
|
|
112
118
|
## Связанные компоненты
|
|
113
119
|
|
|
114
120
|
- `MonthYearRange` — тот же сценарий для диапазона месяцев: одинаковый набор props и render-props, отличается единицами сдвига (`EMonthYearRangeShiftUnit`).
|
|
115
|
-
- `DateField` — поле, ради которого сделан контракт `IDateRangePickerProvideProps`: объект пикера спредится в `DateField` целиком (`value` + `onChange`).
|
|
121
|
+
- `DateField` — поле, ради которого сделан контракт `IDateRangePickerProvideProps`: объект пикера спредится в `DateField` целиком (`value` + `format` + `onChange`).
|
|
116
122
|
- `ButtonIcon` — кнопка, ради которой сделан контракт `IDateRangeButtonProvideProps`: объект кнопки спредится в `ButtonIcon` целиком (`children` + `className` + `disabled` + `onClick`).
|
|
117
123
|
|
|
118
124
|
`DatePickerExtended` и `Calendar` в DateRange не используются напрямую — они находятся внутри `DateField`.
|
|
@@ -140,6 +146,7 @@ version: "1.0"
|
|
|
140
146
|
|---|---|
|
|
141
147
|
| 2026-09-18 | Создан документ. Компонент переведён с `React.FC` на `forwardRef`: `ref` уходит на корневой `<div>`, тип стал `React.ForwardRefExoticComponent`. AI-рефакторинг: логика сдвига вынесена в `utils.ts` (`shiftDateRange`), добавлены JSDoc и unit-тесты на хелпер. Остальной публичный API и поведение не изменились |
|
|
142
148
|
| 2026-09-18 | По итогам ревью: обработчики сдвига сведены в один `shiftRange(amount)`; `TDateRangeValue` вынесен в `types.ts` и реэкспортируется из `index.ts` (набор публичных имён не изменился) |
|
|
149
|
+
| 2026-10-15 | Добавлен prop `format` (по умолчанию `YYYYMMDD`), передаётся пикерам в render-props (`IDateRangePickerProvideProps.format`). Границы сравниваются хронологически (`isDateAfter`) вместо сравнения строк. Сдвиг не вызывает `onChange` при невалидной в `format` границе. Интерфейсы props перенесены в `types.ts` |
|
|
143
150
|
|
|
144
151
|
---
|
|
145
152
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
component: Dropdown
|
|
3
3
|
category: Dropdown
|
|
4
|
-
related: [ButtonDropdown, ButtonDropdownExtended, Portal, MobileView, LoaderSmall]
|
|
4
|
+
related: [ButtonDropdown, ButtonDropdownExtended, Portal, MobileView, Puller, LoaderSmall]
|
|
5
5
|
tokens:
|
|
6
6
|
- Dropdown.Background
|
|
7
7
|
- Dropdown.Shadow
|
|
@@ -9,15 +9,6 @@ tokens:
|
|
|
9
9
|
- DropdownList.Background_Default
|
|
10
10
|
- DropdownList.Background_Active
|
|
11
11
|
- DropdownList.Background_Selected
|
|
12
|
-
- DropdownMobile.Backdrop
|
|
13
|
-
- DropdownMobile.Header_Background
|
|
14
|
-
- DropdownMobile.Content_Background
|
|
15
|
-
- DropdownMobile.Footer_Background
|
|
16
|
-
- DropdownMobile.Border_Color
|
|
17
|
-
- DropdownMobileList.Active_Background
|
|
18
|
-
- DropdownMobileList.Selected_Background
|
|
19
|
-
- FormField.Input_Color_Default
|
|
20
|
-
- FormField.Placeholder_Color
|
|
21
12
|
stories: stories/Dropdown/Dropdown.stories.tsx
|
|
22
13
|
version: "1.0"
|
|
23
14
|
---
|
|
@@ -26,7 +17,7 @@ version: "1.0"
|
|
|
26
17
|
|
|
27
18
|
## Назначение
|
|
28
19
|
|
|
29
|
-
Выпадающее меню: рендерится через `Portal` в `document.body` и фиксированно позиционируется относительно управляющего элемента (`targetRef`). Содержимое произвольное — список опций, форма фильтра, любой блок. Если задан `
|
|
20
|
+
Выпадающее меню: рендерится через `Portal` в `document.body` и фиксированно позиционируется относительно управляющего элемента (`targetRef`). Содержимое произвольное — список опций, форма фильтра, любой блок. Если задан `pullerProps`, на мобильной ширине экрана (<768px) вместо десктопного меню открывается нижняя шторка `Puller`.
|
|
30
21
|
|
|
31
22
|
Используй когда: нужно показать всплывающий блок, привязанный к элементу-триггеру, и при этом выйти за пределы контейнеров с `overflow: hidden`.
|
|
32
23
|
Не используй когда:
|
|
@@ -59,8 +50,8 @@ version: "1.0"
|
|
|
59
50
|
| `width` | `EDropdownWidth` | `CONTENT` | Расчёт ширины: по содержимому, равна ширине управляющего элемента (`TARGET`) или не уже неё (`MIN_TARGET`) |
|
|
60
51
|
| `onOpen` | `() => void` | — | Вызывается при переходе `opened` в `true`. На первом рендере не вызывается |
|
|
61
52
|
| `onClose` | `() => void` | — | Вызывается при переходе `opened` в `false`. На первом рендере не вызывается |
|
|
62
|
-
| `
|
|
63
|
-
| `className` | `string` | — | Дополнительный
|
|
53
|
+
| `pullerProps` | `Omit<IPullerProps, "opened" \| "setOpened">` | — | Включает адаптивный режим и целиком спредится в `Puller`. `pullerProps.children` заменяет `children` в мобильной версии; если не задан, используется `children` |
|
|
54
|
+
| `className` | `string` | — | Дополнительный класс корневого `<div>` десктопного меню. В мобильную версию не попадает: там класс задаётся через `pullerProps.className` |
|
|
64
55
|
| `style` | `React.CSSProperties` | — | Мержится **поверх** вычисленного положения, поэтому им можно перебить `top` / `left` / `width` |
|
|
65
56
|
|
|
66
57
|
### Список опций — `DropdownList` и `DropdownList.Item`
|
|
@@ -88,14 +79,13 @@ version: "1.0"
|
|
|
88
79
|
- `DropdownList` в качестве `children` принимает только `DropdownList.Item` — остальные узлы рендерятся как есть, но не участвуют в навигации и нумерации.
|
|
89
80
|
- `DropdownList` — не `forwardRef`-компонент: ссылка на контейнер списка передаётся через prop `listRef`.
|
|
90
81
|
- Закрытие по `Escape`, `Tab` и клику вне меню `Dropdown` **не реализует**. Это ответственность потребителя либо `ButtonDropdownExtended`.
|
|
91
|
-
- Мобильная версия строится на `OverlayBase` и имеет собственный набор субкомпонентов (`DropdownMobileHeader`, `DropdownMobileBody`, `DropdownMobileList` и т.д.), которые передаются через `mobileViewProps.children`.
|
|
92
82
|
|
|
93
83
|
---
|
|
94
84
|
|
|
95
85
|
## Дизайн-токены
|
|
96
86
|
|
|
97
87
|
Переопределяются через `ThemeProvider` (prop `tokens`) — см. `ThemeProvider-ai.md` →
|
|
98
|
-
«Как переопределять токены». Значения по умолчанию — файлы групп `Dropdown`, `DropdownList
|
|
88
|
+
«Как переопределять токены». Значения по умолчанию — файлы групп `Dropdown`, `DropdownList` в `src/components/DesignTokens/components/`. Токены мобильной версии (группы `Puller`, `PullerList`) описаны в `Puller-ai.md`.
|
|
99
89
|
|
|
100
90
|
```text
|
|
101
91
|
Dropdown.Background
|
|
@@ -104,44 +94,34 @@ DropdownList.Color
|
|
|
104
94
|
DropdownList.Background_Default
|
|
105
95
|
DropdownList.Background_Active
|
|
106
96
|
DropdownList.Background_Selected
|
|
107
|
-
DropdownMobile.Backdrop
|
|
108
|
-
DropdownMobile.Header_Background
|
|
109
|
-
DropdownMobile.Content_Background
|
|
110
|
-
DropdownMobile.Footer_Background
|
|
111
|
-
DropdownMobile.Border_Color
|
|
112
|
-
DropdownMobileList.Active_Background
|
|
113
|
-
DropdownMobileList.Selected_Background
|
|
114
97
|
```
|
|
115
98
|
|
|
116
|
-
`DropdownMobileInput` дополнительно использует токены поля ввода: `FormField.Input_Color_Default`, `FormField.Placeholder_Color`.
|
|
117
|
-
|
|
118
99
|
---
|
|
119
100
|
|
|
120
101
|
## Инварианты
|
|
121
102
|
|
|
122
|
-
- `forwardRef` на `Dropdown`, `DropdownDesktop`, `DropdownListItem`
|
|
103
|
+
- `forwardRef` на `Dropdown`, `DropdownDesktop`, `DropdownListItem` — не убирать. Ref `Dropdown` указывает на корневой `<div>` десктопного меню, а в мобильном режиме — на контентный `<div>` `Puller` (`.puller`).
|
|
123
104
|
- Меню в закрытом состоянии не рендерит ничего (`DropdownDesktop` возвращает `null`) — на это опираются потребители, монтирующие `Dropdown` безусловно.
|
|
124
105
|
- Компонент управляемый: не добавлять внутренний state открытости, иначе сломается контракт `opened` / `setOpened`.
|
|
125
|
-
- `Dropdown` рендерится в `document.body` через `Portal`. Смена контейнера портала — breaking change для потребителей, которые ищут меню в DOM.
|
|
126
|
-
- Публичные barrel-экспорты `src/components/Dropdown/index.ts`, `desktop/index.ts
|
|
106
|
+
- `Dropdown` рендерится в `document.body` через `Portal`. Смена контейнера портала — breaking change для потребителей, которые ищут меню в DOM. Мобильная версия рендерится туда же, если не задан `pullerProps.renderContainer`.
|
|
107
|
+
- Публичные barrel-экспорты `src/components/Dropdown/index.ts`, `desktop/index.ts` должны сохраняться: компонент используется внутри `ButtonDropdown`, `SelectField`, `SelectExtendedField`, `SuggestField`, `MultiselectField`, `TabsLine`, `DatePickerExtended`, `Chip`, `List`.
|
|
127
108
|
- `DropdownList` проставляет дочерним элементам `active`, `size`, `ref`, `onMouseOver` и `onMouseOut` через `cloneElement` — эти props у `DropdownList.Item` нельзя считать «пользовательскими».
|
|
128
|
-
- Пока десктопное меню открыто, на `document.body` висит класс блокировки скролла, а на документе — обработчики `wheel` и `keydown`. Снятие обработчиков в cleanup эффекта обязательно.
|
|
109
|
+
- Пока десктопное меню открыто, на `document.body` висит класс блокировки скролла, а на документе — обработчики `wheel` и `keydown`. Снятие обработчиков в cleanup эффекта обязательно. Класс вешается только через `lockBodyScroll` (`src/utils/scroll/bodyScrollLock.ts`): прямой `classList.remove` снял бы блокировку при другом открытом меню.
|
|
129
110
|
- Числовой формат `keyCodesForSelection` (`number[]`, а не `KeyboardEvent.code`) — часть публичного API.
|
|
130
111
|
- Хелперы из `desktop/utils.ts` внутренние: не экспортировать их через barrel.
|
|
131
|
-
- У мобильного списка (`DropdownMobileList`) нет собственного класса-зацепки — в отличие от десктопного `.dropdownDesktopList`. Это осознанно: в `styles/DropdownMobile.module.less` такого класса никогда не было, стилизуются только элементы списка и лоадер. Корневой `<div>` получает только `className` снаружи; не добавлять пустое правило в LESS ради зацепки без задачи.
|
|
132
112
|
- `listRef` у `DropdownList` и `setActiveDescendant` из `DropdownListContext` должны быть стабильными между рендерами (ref из `useRef`, сеттер из `useState`). Нестабильные значения переподписывают клавиатурный обработчик и сбрасывают `aria-activedescendant` на каждый рендер.
|
|
133
113
|
|
|
134
114
|
---
|
|
135
115
|
|
|
136
116
|
## Accessibility
|
|
137
117
|
|
|
138
|
-
- `DropdownList` рендерит контейнер с `role="listbox"`, `DropdownList.Item` — `role="option"` и `aria-selected`.
|
|
118
|
+
- `DropdownList` рендерит контейнер с `role="listbox"`, `DropdownList.Item` — `role="option"` и `aria-selected`. В мобильной версии `PullerList` / `PullerListItem` — те же роли.
|
|
139
119
|
- Клавиатурная навигация по списку: `ArrowDown` / `ArrowUp` перемещают активный элемент по кругу и прокручивают контейнер к нему, `Enter` / `Space` (или клавиши из `keyCodesForSelection`) вызывают `onSelect` активного элемента. Обработчики висят на `document`, пока `dropdownOpened === true`.
|
|
140
120
|
- При открытии активным становится элемент с `selected`, иначе первый; список прокручивается к нему.
|
|
141
121
|
- Пока меню открыто, `DropdownDesktop` гасит клавиши прокрутки страницы (`Space`, `PageUp`, `PageDown`, `Home`, `End`, стрелки), но только если фокус на `document.body`.
|
|
142
122
|
- `aria-activedescendant` компонент сам на триггер **не выставляет**: `DropdownList` публикует id активного элемента через `DropdownListContext.setActiveDescendant`, а связать его с триггером должен потребитель (см. `SelectField`, `ButtonDropdown`). Контекст сбрасывается при закрытии и размонтировании списка.
|
|
143
123
|
- Потребитель отвечает за атрибуты триггера: `aria-haspopup`, `aria-expanded`, `aria-controls`, `aria-activedescendant`.
|
|
144
|
-
- Компонент не хардкодит текстовые строки: `aria-label` для `
|
|
124
|
+
- Компонент не хардкодит текстовые строки: `aria-label` для `PullerClose` и заголовки мобильной версии передаёт потребитель (библиотека мультиязычная).
|
|
145
125
|
- **Известное ограничение:** при уходе курсора с элемента списка (`onMouseOut`) подсветка снимается, но `activeDescendant` в контексте не сбрасывается — `aria-activedescendant` на триггере продолжает указывать на последний наведённый пункт до следующей смены активного элемента или закрытия меню. Поведение унаследованное; его исправление меняет наблюдаемый контракт для `SelectField` / `ButtonDropdown` / `SuggestField` и требует отдельного решения.
|
|
146
126
|
- Фокус компонент не перехватывает и не возвращает. Если нужен focus trap, его добавляет потребитель (так делает `MultiselectField.Dropdown`).
|
|
147
127
|
|
|
@@ -153,7 +133,7 @@ DropdownMobileList.Selected_Background
|
|
|
153
133
|
Это область задачи TRI-35 («Scope: только Dropdown; соседние экспортируемые компоненты
|
|
154
134
|
из `src/components/Dropdown/` — отдельные задачи»), а не вывод по критериям
|
|
155
135
|
`docs/ai/CONTEXT.md` → «Когда создавать `{ComponentName}-ai.md`». По этим критериям
|
|
156
|
-
`DropdownList
|
|
136
|
+
`DropdownList` и `DropdownListItem`
|
|
157
137
|
заслуживают собственных AI.md и отдельных строк в `docs/ai/ROADMAP.md` — как это
|
|
158
138
|
сделано для семейства `List`. Решение о том, заводить ли их, за владельцем репозитория.
|
|
159
139
|
|
|
@@ -163,17 +143,9 @@ DropdownMobileList.Selected_Background
|
|
|
163
143
|
- `DropdownList` — контейнер списка с клавиатурной навигацией, `role="listbox"`. Статическое свойство `DropdownList.Item`.
|
|
164
144
|
- `DropdownListItem` — элемент списка, `role="option"`, выбор мышью и клавиатурой.
|
|
165
145
|
|
|
166
|
-
**Мобильная
|
|
146
|
+
**Мобильная версия:**
|
|
167
147
|
|
|
168
|
-
- `
|
|
169
|
-
- `DropdownMobileInner` — внутренняя разметка оверлея: подложка и контент с анимацией открытия/закрытия.
|
|
170
|
-
- `DropdownMobileHeader` — шапка со слотом `controlButtons` (тип `IDropdownMobileHeaderProps` объявлен в `src/components/Tooltip/types.ts`).
|
|
171
|
-
- `DropdownMobileBody` — скроллируемое тело.
|
|
172
|
-
- `DropdownMobileFooter` — футер для кнопок.
|
|
173
|
-
- `DropdownMobileList` / `DropdownMobileListItem` — список и элемент списка мобильной версии.
|
|
174
|
-
- `DropdownMobileClose` — кнопка закрытия (обёртка над `ButtonIcon`).
|
|
175
|
-
- `DropdownMobileLoader` — лоадер для шапки (обёртка над `LoaderSmall`).
|
|
176
|
-
- `DropdownMobileInput` / `DropdownMobileMaskedInput` — поля ввода для шапки (второе — обёртка над `FormFieldMaskedInput` с `presets`).
|
|
148
|
+
- `Puller` (`src/components/Puller/`) — нижняя шторка на базе `OverlayBase`, которую `Dropdown` рендерит в адаптивном режиме вместо десктопного меню. Части шторки описаны в `Puller-ai.md`.
|
|
177
149
|
|
|
178
150
|
**Общее:**
|
|
179
151
|
|
|
@@ -206,7 +178,7 @@ DropdownMobileList.Selected_Background
|
|
|
206
178
|
| `Widths` | `Widths.tsx` | `CONTENT` / `TARGET` / `MIN_TARGET` |
|
|
207
179
|
| `Loading` | `Loading.tsx` | Подгрузка опций: элемент с лоадером в конце списка |
|
|
208
180
|
| `WithSelectedOption` | `WithSelectedOption.tsx` | Выбранная опция подсвечена и активна при открытии |
|
|
209
|
-
| `MobileView` | `MobileView.tsx` | Адаптивный режим: `
|
|
181
|
+
| `MobileView` | `MobileView.tsx` | Адаптивный режим: `pullerProps` и части `Puller` |
|
|
210
182
|
| `VisualTests` | `VisualTests.tsx` | Длинный список со скроллом, значок уведомления, произвольный контент вместо списка |
|
|
211
183
|
|
|
212
184
|
---
|
|
@@ -220,6 +192,9 @@ DropdownMobileList.Selected_Background
|
|
|
220
192
|
| 2026-07-31 | Правки по ревью PR #491: эффект `onOpen` / `onClose` в `Dropdown` сравнивает предыдущее значение `opened` вместо флага «смонтирован» — колбэки больше не срабатывают ложно при двойном маунте в React 18 StrictMode (регрессионный тест `__tests__/Dropdown.strictMode.test.tsx`). Документационные stories стартуют закрытыми и раскрываются через `play`. Сброс активного элемента `DropdownList` сделан симметричным: при закрытии подсветка снимается вместе с `activeDescendant`. |
|
|
221
193
|
| 2026-09-28 | Токен `Dropdown.Shadow` ссылается на `Shadow.07` (светлая) и `DarkShadow.07` (тёмная) вместо литералов; в тёмной теме прозрачность 0.25 вместо 0.35. |
|
|
222
194
|
| 2026-09-29 | Аудит симметрии `related` (TRI-156): из `related` убраны имена без строки в `docs/ai/ROADMAP.md` (`OverlayBase`) и потребители `SelectField` / `SelectExtendedField` / `SuggestField` / `MultiselectField` / `TabsLine` — по `docs/ai/CONTEXT.md` ссылка стоит с их стороны; добавлен `LoaderSmall` — контракт по рендеру в верную сторону. Пояснения к убранным именам сохранены прозой в «Связанные компоненты»; публичный API и поведение не затронуты. |
|
|
195
|
+
| 2026-10-21 | **Breaking** (TRIPLEX-961): мобильная версия выделена в компонент `Puller`, `DropdownMobile*` удалены без алиасов. Prop `mobileViewProps` переименован в `pullerProps`, группы токенов `DropdownMobile` / `DropdownMobileList` — в `Puller` / `PullerList` (ключ `Content_Background` → `Body_Background`). |
|
|
196
|
+
| 2026-10-23 | Исправлено: закрытие одного из открытых `DropdownDesktop` снимало блокировку скролла страницы при другом открытом меню. **Breaking:** класс блокировки на `body` переименован `.dropdownDesktopOverflowHidden` → `.dropdownDesktopScrollLock`. |
|
|
197
|
+
| 2026-10-31 | Мобильная версия рендерится в `pullerProps.renderContainer`, если он задан, иначе в `document.body` (TRIPLEX-961). |
|
|
223
198
|
|
|
224
199
|
---
|
|
225
200
|
|
|
@@ -746,16 +721,16 @@ import {
|
|
|
746
721
|
Button,
|
|
747
722
|
Dropdown,
|
|
748
723
|
DropdownList,
|
|
749
|
-
DropdownMobileBody,
|
|
750
|
-
DropdownMobileClose,
|
|
751
|
-
DropdownMobileFooter,
|
|
752
|
-
DropdownMobileHeader,
|
|
753
|
-
DropdownMobileList,
|
|
754
|
-
DropdownMobileListItem,
|
|
755
724
|
EButtonTheme,
|
|
756
725
|
EComponentSize,
|
|
757
726
|
EDropdownWidth,
|
|
758
727
|
ETextSize,
|
|
728
|
+
PullerBody,
|
|
729
|
+
PullerClose,
|
|
730
|
+
PullerFooter,
|
|
731
|
+
PullerHeader,
|
|
732
|
+
PullerList,
|
|
733
|
+
PullerListItem,
|
|
759
734
|
Text,
|
|
760
735
|
} from "@sberbusiness/triplex-next";
|
|
761
736
|
|
|
@@ -793,31 +768,29 @@ export const MobileView = () => {
|
|
|
793
768
|
targetRef={targetRef}
|
|
794
769
|
size={EComponentSize.MD}
|
|
795
770
|
width={EDropdownWidth.MIN_TARGET}
|
|
796
|
-
|
|
771
|
+
pullerProps={{
|
|
797
772
|
children: (
|
|
798
773
|
<>
|
|
799
|
-
<
|
|
800
|
-
controlButtons={
|
|
801
|
-
<DropdownMobileClose aria-label="Закрыть" onClick={() => setOpened(false)} />
|
|
802
|
-
}
|
|
774
|
+
<PullerHeader
|
|
775
|
+
controlButtons={<PullerClose aria-label="Закрыть" onClick={() => setOpened(false)} />}
|
|
803
776
|
>
|
|
804
777
|
<Text size={ETextSize.B1}>Заголовок</Text>
|
|
805
|
-
</
|
|
806
|
-
<
|
|
807
|
-
<
|
|
778
|
+
</PullerHeader>
|
|
779
|
+
<PullerBody>
|
|
780
|
+
<PullerList>
|
|
808
781
|
{OPTIONS.map((option) => (
|
|
809
|
-
<
|
|
782
|
+
<PullerListItem
|
|
810
783
|
key={option.id}
|
|
811
784
|
id={`mobile-${option.id}`}
|
|
812
785
|
selected={option.id === selectedId}
|
|
813
786
|
onSelect={() => handleSelect(option.id)}
|
|
814
787
|
>
|
|
815
788
|
{option.label}
|
|
816
|
-
</
|
|
789
|
+
</PullerListItem>
|
|
817
790
|
))}
|
|
818
|
-
</
|
|
819
|
-
</
|
|
820
|
-
<
|
|
791
|
+
</PullerList>
|
|
792
|
+
</PullerBody>
|
|
793
|
+
<PullerFooter>
|
|
821
794
|
<Button
|
|
822
795
|
theme={EButtonTheme.SECONDARY}
|
|
823
796
|
size={EComponentSize.MD}
|
|
@@ -825,7 +798,7 @@ export const MobileView = () => {
|
|
|
825
798
|
>
|
|
826
799
|
Button text
|
|
827
800
|
</Button>
|
|
828
|
-
</
|
|
801
|
+
</PullerFooter>
|
|
829
802
|
</>
|
|
830
803
|
),
|
|
831
804
|
}}
|
|
@@ -26,22 +26,26 @@ version: "1.0"
|
|
|
26
26
|
|
|
27
27
|
| Prop | Тип | Описание |
|
|
28
28
|
|---|---|---|
|
|
29
|
-
| `size` | `EEmptyViewSize` |
|
|
29
|
+
| `size` | `EEmptyViewSize` | Обязательный размер: `SM` — текст `B3`, `MD` — текст `B2` |
|
|
30
30
|
|
|
31
31
|
### Опциональные props
|
|
32
32
|
|
|
33
33
|
| Prop | Тип | По умолчанию | Описание |
|
|
34
34
|
|---|---|---|---|
|
|
35
|
-
| `icon` | `ReactNode` | — |
|
|
36
|
-
| `title` | `ReactNode` | — | Заголовок |
|
|
35
|
+
| `icon` | `ReactNode` | — | Иконка; размер задаёт потребитель (обычно 96px для `SM`, 128px для `MD`) |
|
|
36
|
+
| `title` | `ReactNode` | — | Заголовок `H3` с начертанием `MEDIUM` для обоих размеров |
|
|
37
37
|
| `description` | `ReactNode` | — | Текст описания |
|
|
38
38
|
| `caption` | `ReactNode` | — | Подпись |
|
|
39
|
-
| `buttons` | `ReactNode` | — | Кнопки действий |
|
|
39
|
+
| `buttons` | `ReactNode` | — | Кнопки действий с отступом 16px для `SM` и 24px для `MD` |
|
|
40
|
+
|
|
41
|
+
`className` объединяется с классами корневого `<div>`. HTML-атрибуты и обработчики
|
|
42
|
+
передаются на тот же элемент; `ref` указывает на него.
|
|
40
43
|
|
|
41
44
|
### Ограничения по темам
|
|
42
45
|
|
|
43
46
|
- Если `title` задан — описание отображается вторичным цветом (Secondary).
|
|
44
47
|
- Если `title` не задан — описание становится основным текстом (Primary).
|
|
48
|
+
- `caption` всегда отображается вторичным цветом; подпись можно передать без описания.
|
|
45
49
|
|
|
46
50
|
---
|
|
47
51
|
|
|
@@ -103,6 +107,7 @@ Typography.Secondary_Color (description с title, caption)
|
|
|
103
107
|
| 2026-04-09 | Создан компонент и документ. |
|
|
104
108
|
| 2026-04-27 | Приведён в соответствие с `docs/ai/template-ai.md`: добавлена колонка `Example file`, переименована подсекция «Поведение описания» в «Ограничения по темам». |
|
|
105
109
|
| 2026-08-31 | Заголовок всегда рендерится как `Title` с `ETitleSize.H3` и `EFontWeightTitle.MEDIUM`: убран `TITLE_CONFIG`, типографика `title` больше не зависит от `size`. |
|
|
110
|
+
| 2026-10-08 | Проведён AI-рефакторинг: уточнены условия и JSDoc props, дополнены тесты условного рендеринга и точной передачи ref. Уточнены размерная типографика, размер иконки, отступы кнопок и корневые HTML-атрибуты. |
|
|
106
111
|
|
|
107
112
|
---
|
|
108
113
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
component: ExpandAnimation
|
|
3
3
|
category: ExpandAnimation
|
|
4
|
-
related: [
|
|
4
|
+
related: []
|
|
5
5
|
tokens: []
|
|
6
6
|
stories: stories/ExpandAnimation/ExpandAnimation.stories.tsx
|
|
7
7
|
version: "1.0"
|
|
@@ -24,9 +24,10 @@ version: "1.0"
|
|
|
24
24
|
раскрытия уже живёт в потребителе (аккордеон, «показать ещё», раскрываемая
|
|
25
25
|
строка таблицы).
|
|
26
26
|
|
|
27
|
-
Не используй когда: нужен готовый аккордеон с внутренним
|
|
28
|
-
|
|
29
|
-
|
|
27
|
+
Не используй когда: нужен готовый аккордеон с внутренним состоянием и
|
|
28
|
+
доступным управлением — используй публичный `IslandAccordion`.
|
|
29
|
+
Для иерархического дерева используй `CollapsibleTree`. Его внутренняя база
|
|
30
|
+
`AccordionBase` не экспортируется.
|
|
30
31
|
|
|
31
32
|
---
|
|
32
33
|
|
|
@@ -163,7 +164,8 @@ Tab и не читается скринридером. Во время аним
|
|
|
163
164
|
- `AccordionBase` (`src/components/AccordionBase`) — слой выше: хранит
|
|
164
165
|
состояние раскрытия (controlled / uncontrolled), генерирует id и
|
|
165
166
|
ARIA-атрибуты для связки header ↔ body, рендерит `ExpandAnimation` внутри.
|
|
166
|
-
|
|
167
|
+
Это внутренняя реализация; для потребительского кода выбирай публичный
|
|
168
|
+
компонент, построенный на ней.
|
|
167
169
|
- Потребители внутри библиотеки, чьё поведение зависит от этого компонента:
|
|
168
170
|
`IslandAccordion` (передаёт `transitionProps`, `role="region"`, `id`),
|
|
169
171
|
`IslandWidget` и `IslandWidgetExtraFooter`, `CollapsibleTreeExtended`
|
|
@@ -191,6 +193,8 @@ Tab и не читается скринридером. Во время аним
|
|
|
191
193
|
| Дата | Изменение |
|
|
192
194
|
|---|---|
|
|
193
195
|
| 2026-08-21 | Создан документ. Зафиксированы: карта стилей по фазам анимации, требование хранить её в ref, порядок применения `transitionProps`, контракт `visibility: hidden` в `exited`, отсутствие собственных ARIA-атрибутов и токенов |
|
|
196
|
+
| 2026-10-16 | Внутренний `AccordionBase` исключён из `related` при сверке ROADMAP; рекомендации потребителю направлены на публичные компоненты, описание внутренней зависимости сохранено. |
|
|
197
|
+
| 2026-10-19 | Уточнён выбор готового компонента: `IslandAccordion` для аккордеона, `CollapsibleTree` для иерархического дерева. |
|
|
194
198
|
|
|
195
199
|
---
|
|
196
200
|
|
|
@@ -63,7 +63,7 @@ version: "1.0"
|
|
|
63
63
|
- Класс `filled` выставляется, когда вложенный элемент ввода сообщил о наличии значения. Для `FormFieldInput` / `FormFieldTextarea` это включает браузерное автозаполнение (ловится через CSS-анимационные хуки `autofill-applied-hook` / `autofill-cancelled-hook`), для `FormFieldTarget` — наличие `children`.
|
|
64
64
|
- Кнопка `FormFieldClear` показывается стилями только при `filled` и hover/активном состоянии непустого и не заблокированного поля, а также при собственном фокусе (`:focus`) — чтобы кнопка оставалась видимой при клавиатурной навигации.
|
|
65
65
|
- Идентификаторы генерируются субкомпонентами (`uniqueId` из `lodash-es`, не `React.useId` — код должен оставаться совместимым с React 17) и попадают в контекст как `targetId` / `labelId`; на них опирается связка лейбла и поля. `targetId` публикуют только labelable-элементы (`FormFieldInput`, `FormFieldTextarea`, а также `FormFieldMaskedInput` — он рендерит `FormFieldInput` внутри себя через `render`); `FormFieldTarget` его не публикует — см. Accessibility.
|
|
66
|
-
- Вне `FormField` субкомпоненты используют `initialFormFieldContext`, где `filled: true` — это осознанный временный фикс для `
|
|
66
|
+
- Вне `FormField` субкомпоненты используют `initialFormFieldContext`, где `filled: true` — это осознанный временный фикс для `PullerHeader`, а сеттеры являются no-op.
|
|
67
67
|
|
|
68
68
|
---
|
|
69
69
|
|