@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.
Files changed (74) hide show
  1. package/README.md +1 -0
  2. package/bundle/components/AlertProcess.md +6 -3
  3. package/bundle/components/AlertProcessSpoiler.md +216 -0
  4. package/bundle/components/Badge.md +4 -3
  5. package/bundle/components/ButtonDropdown.md +5 -3
  6. package/bundle/components/Carousel.md +671 -0
  7. package/bundle/components/CarouselExtended.md +7 -5
  8. package/bundle/components/CarouselIndicators.md +666 -0
  9. package/bundle/components/CarouselItem.md +579 -0
  10. package/bundle/components/CarouselNextButton.md +633 -0
  11. package/bundle/components/CarouselPrevButton.md +633 -0
  12. package/bundle/components/CarouselViewport.md +631 -0
  13. package/bundle/components/CheckboxTreeExtended.md +21 -7
  14. package/bundle/components/CheckboxTreeExtendedCheckbox.md +591 -0
  15. package/bundle/components/CheckboxTreeExtendedNode.md +577 -0
  16. package/bundle/components/Chip.md +1 -0
  17. package/bundle/components/ChipDatePicker.md +332 -0
  18. package/bundle/components/ChipDatePickerTarget.md +316 -0
  19. package/bundle/components/ChipMonthYearPicker.md +4 -3
  20. package/bundle/components/ChipMonthYearPickerTarget.md +318 -0
  21. package/bundle/components/ChipMultiselect.md +63 -73
  22. package/bundle/components/ChipSelect.md +385 -0
  23. package/bundle/components/ChipSelectTarget.md +388 -0
  24. package/bundle/components/ChipSuggest.md +10 -16
  25. package/bundle/components/ChipSuggestTarget.md +506 -0
  26. package/bundle/components/CollapsibleTree.md +2 -1
  27. package/bundle/components/CollapsibleTreeExtended.md +2 -1
  28. package/bundle/components/DateField.md +4 -2
  29. package/bundle/components/DatePickerExtended.md +16 -14
  30. package/bundle/components/DateRange.md +17 -10
  31. package/bundle/components/Dropdown.md +36 -63
  32. package/bundle/components/EmptyView.md +9 -4
  33. package/bundle/components/ExpandAnimation.md +9 -5
  34. package/bundle/components/FormField.md +1 -1
  35. package/bundle/components/ImageGallery.md +19 -52
  36. package/bundle/components/ImageGalleryExtended.md +123 -139
  37. package/bundle/components/ListItemControlsButtonDropdown.md +3 -2
  38. package/bundle/components/LoaderSmall.md +5 -4
  39. package/bundle/components/MonthYearRange.md +334 -0
  40. package/bundle/components/MultiselectField.md +81 -143
  41. package/bundle/components/Notification.md +2 -1
  42. package/bundle/components/NotificationGrouped.md +174 -0
  43. package/bundle/components/NumberField.md +259 -116
  44. package/bundle/components/OrderedList.md +149 -112
  45. package/bundle/components/Overlay.md +3 -2
  46. package/bundle/components/PageIndicators.md +306 -0
  47. package/bundle/components/Pagination.md +81 -3
  48. package/bundle/components/PaginationExtended.md +160 -0
  49. package/bundle/components/Portal.md +5 -2
  50. package/bundle/components/Puller.md +375 -0
  51. package/bundle/components/SMSField.md +277 -102
  52. package/bundle/components/SelectExtendedField.md +11 -7
  53. package/bundle/components/SelectField.md +7 -4
  54. package/bundle/components/SmartSpot.md +259 -0
  55. package/bundle/components/Step.md +2 -1
  56. package/bundle/components/Stepper.md +7 -7
  57. package/bundle/components/Suggest.md +7 -6
  58. package/bundle/components/SuggestField.md +8 -6
  59. package/bundle/components/TableBasic.md +12 -14
  60. package/bundle/components/Tooltip.md +9 -8
  61. package/bundle/guides/commits.md +4 -1
  62. package/bundle/guides/context.md +5 -5
  63. package/bundle/guides/stories.md +29 -21
  64. package/bundle/manifest.json +145 -13
  65. package/bundle/release-notes/1.50.0.mdx +4 -0
  66. package/bundle/release-notes/1.51.0.mdx +57 -0
  67. package/bundle/release-notes/1.52.0.mdx +55 -0
  68. package/bundle/release-notes/1.53.0.mdx +60 -0
  69. package/bundle/release-notes/1.54.0.mdx +45 -0
  70. package/bundle/release-notes/1.55.0.mdx +7 -0
  71. package/bundle/tokens.json +377 -222
  72. package/dist/index.js +39 -1
  73. package/dist/index.js.map +1 -1
  74. 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`. На мобильной ширине экрана вместо десктопного дропдауна открывается полноэкранный `DropdownMobile` с заголовком из `renderDropdownHeaderTarget`.
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` | Рендер целевого элемента в заголовке мобильного дропдауна (`DropdownMobileHeader`). Обычно — `DropdownMobileInput` с текущим значением. |
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` вокруг календаря и мобильной версткой (`DropdownMobileHeader` + `DropdownMobileBody`).
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={() => <DropdownMobileInput value={displayedValue} readOnly />}
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={() => <DropdownMobileInput value={displayedValue} readOnly />}
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
- DropdownMobileInput,
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={() => <DropdownMobileInput value={displayedValue} readOnly />}
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
- DropdownMobileInput,
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={() => <DropdownMobileInput value={displayedValue} readOnly />}
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={() => <DropdownMobileInput value={displayedValue} readOnly />}
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]`, обе даты строками в формате `YYYYMMDD` (`dateFormatYYYYMMDD` из `src/consts/DateConst`). Пустая строка означает «дата не выбрана»; допустимо состояние, когда заполнена только одна граница.
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
- | `onChange` | пикеры | Принимает строку `YYYYMMDD`; внутри применяется согласование границ (ниже). |
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
- - Сравнение строковое, без `moment`: формат `YYYYMMDD` лексикографически совпадает с хронологическим порядком. Поэтому формат дат менять нельзя (см. «Инварианты»).
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()`). При сдвиге на месяцы, кварталы и годы число месяца клампится по длине целевого месяца, поэтому длина диапазона в днях может измениться: `20240331` минус месяц → `20240229`.
76
- - Сдвиг доступен только при обеих заполненных границах: иначе кнопкам приходит `disabled: true`, а обработчик дополнительно выходит без `onChange`.
77
- - Валидность дат перед сдвигом не проверяется: строка не в формате `YYYYMMDD` даст `"Invalid date"` в результате. Близнец `MonthYearRange` в том же месте делает строгий `moment(..., true).isValid()` и выходит без `onChange` — эталон, если поведение решат выровнять.
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
- - **Формат дат `YYYYMMDD` зашит в контракт**: на нём держится строковое сравнение границ и парсинг в `moment`. Ни `value`, ни аргумент `onChange` пикера в другом формате работать не будут.
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` живёт в `types.ts`, остальное — в `DateRange.tsx` и `enums.ts`. `utils.ts` внутренний и в barrel не экспортируется.
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`). Содержимое произвольное — список опций, форма фильтра, любой блок. Если задан `mobileViewProps`, на мобильной ширине экрана (<768px) вместо десктопного меню открывается полноэкранная мобильная версия.
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
- | `mobileViewProps` | `Omit<IDropdownMobileProps, "opened" \| "setOpened">` | — | Включает адаптивный режим. `mobileViewProps.children` заменяет `children` в мобильной версии; если не задан, используется `children` |
63
- | `className` | `string` | — | Дополнительный класс. В десктопной версии — на корневом `<div>` меню, в мобильной — на wrapper'е оверлея |
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`, `DropdownMobile`, `DropdownMobileList` в `src/components/DesignTokens/components/`.
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` и всех мобильных субкомпонентах — не убирать. Ref `Dropdown` указывает на корневой `<div>` десктопного меню, а в мобильном режиме — на контентный `<div>` (`.dropdownMobile`).
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`, `mobile/index.ts` должны сохраняться: компонент используется внутри `ButtonDropdown`, `SelectField`, `SelectExtendedField`, `SuggestField`, `MultiselectField`, `TabsLine`, `DatePickerExtended`, `Chip`, `List`.
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`. Мобильные `DropdownMobileList` / `DropdownMobileListItem` — те же роли.
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` для `DropdownMobileClose` и заголовки мобильной версии передаёт потребитель (библиотека мультиязычная).
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`, `DropdownListItem`, `DropdownMobile` и `DropdownMobileListItem`
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
- **Мобильная версия (`src/components/Dropdown/mobile/`):**
146
+ **Мобильная версия:**
167
147
 
168
- - `DropdownMobile` — полноэкранный оверлей снизу на базе `OverlayBase`, блокирует скролл body.
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` | Адаптивный режим: `mobileViewProps` и мобильные субкомпоненты |
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
- mobileViewProps={{
771
+ pullerProps={{
797
772
  children: (
798
773
  <>
799
- <DropdownMobileHeader
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
- </DropdownMobileHeader>
806
- <DropdownMobileBody>
807
- <DropdownMobileList>
778
+ </PullerHeader>
779
+ <PullerBody>
780
+ <PullerList>
808
781
  {OPTIONS.map((option) => (
809
- <DropdownMobileListItem
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
- </DropdownMobileListItem>
789
+ </PullerListItem>
817
790
  ))}
818
- </DropdownMobileList>
819
- </DropdownMobileBody>
820
- <DropdownMobileFooter>
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
- </DropdownMobileFooter>
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` | Размер: `SM` / `MD` |
29
+ | `size` | `EEmptyViewSize` | Обязательный размер: `SM` — текст `B3`, `MD` — текст `B2` |
30
30
 
31
31
  ### Опциональные props
32
32
 
33
33
  | Prop | Тип | По умолчанию | Описание |
34
34
  |---|---|---|---|
35
- | `icon` | `ReactNode` | — | Иконка (96px для `SM`, 128px для `MD`) |
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: [AccordionBase]
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
- Не используй когда: нужен готовый аккордеон с внутренним состоянием, id-связкой
28
- заголовка и тела и ARIA-атрибутами — для этого есть `AccordionBase` и
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` — это осознанный временный фикс для `DropdownMobileHeader`, а сеттеры являются no-op.
66
+ - Вне `FormField` субкомпоненты используют `initialFormFieldContext`, где `filled: true` — это осознанный временный фикс для `PullerHeader`, а сеттеры являются no-op.
67
67
 
68
68
  ---
69
69