@sberbusiness/triplex-next-mcp-server 0.24.0-beta1

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 (140) hide show
  1. package/README.md +124 -0
  2. package/bundle/.gitkeep +0 -0
  3. package/bundle/components/AbstractTree.md +198 -0
  4. package/bundle/components/AccordionBase.md +227 -0
  5. package/bundle/components/Alert.md +317 -0
  6. package/bundle/components/Amount.md +225 -0
  7. package/bundle/components/AmountField.md +289 -0
  8. package/bundle/components/Avatar.md +209 -0
  9. package/bundle/components/Badge.md +277 -0
  10. package/bundle/components/Body.md +167 -0
  11. package/bundle/components/Button.md +230 -0
  12. package/bundle/components/ButtonDropdown.md +159 -0
  13. package/bundle/components/ButtonIcon.md +122 -0
  14. package/bundle/components/Calendar.md +445 -0
  15. package/bundle/components/Card.md +374 -0
  16. package/bundle/components/CarouselExtended.md +250 -0
  17. package/bundle/components/Checkbox.md +126 -0
  18. package/bundle/components/CheckboxTree.md +280 -0
  19. package/bundle/components/CheckboxTreeExtended.md +321 -0
  20. package/bundle/components/CheckboxXGroup.md +68 -0
  21. package/bundle/components/CheckboxYGroup.md +67 -0
  22. package/bundle/components/Chip.md +404 -0
  23. package/bundle/components/ChipGroup.md +179 -0
  24. package/bundle/components/Col.md +221 -0
  25. package/bundle/components/CollapsableTree.md +255 -0
  26. package/bundle/components/Confirm.md +343 -0
  27. package/bundle/components/DateField.md +371 -0
  28. package/bundle/components/DatePickerExtended.md +342 -0
  29. package/bundle/components/DateRange.md +275 -0
  30. package/bundle/components/DesignTokens.md +385 -0
  31. package/bundle/components/Divider.md +196 -0
  32. package/bundle/components/DocumentNumberEdit.md +280 -0
  33. package/bundle/components/Dropdown.md +433 -0
  34. package/bundle/components/Ellipsis.md +208 -0
  35. package/bundle/components/EmptyView.md +99 -0
  36. package/bundle/components/ExpandAnimation.md +281 -0
  37. package/bundle/components/Footer.md +339 -0
  38. package/bundle/components/FormField.md +326 -0
  39. package/bundle/components/FormGroup.md +99 -0
  40. package/bundle/components/Gap.md +113 -0
  41. package/bundle/components/Header.md +280 -0
  42. package/bundle/components/HelpBox.md +154 -0
  43. package/bundle/components/IconWrapper.md +145 -0
  44. package/bundle/components/Island.md +197 -0
  45. package/bundle/components/IslandAccordion.md +230 -0
  46. package/bundle/components/IslandWidget.md +233 -0
  47. package/bundle/components/KeyDownListener.md +157 -0
  48. package/bundle/components/LightBox.md +322 -0
  49. package/bundle/components/Link.md +161 -0
  50. package/bundle/components/List.md +247 -0
  51. package/bundle/components/ListMaster.md +204 -0
  52. package/bundle/components/Loader.md +161 -0
  53. package/bundle/components/LoaderScreen.md +134 -0
  54. package/bundle/components/Marker.md +127 -0
  55. package/bundle/components/MarkerStatus.md +148 -0
  56. package/bundle/components/MediaWidth.md +169 -0
  57. package/bundle/components/MobileView.md +120 -0
  58. package/bundle/components/ModalWindow.md +308 -0
  59. package/bundle/components/MonthYearField.md +168 -0
  60. package/bundle/components/MultiselectField.md +213 -0
  61. package/bundle/components/Notification.md +233 -0
  62. package/bundle/components/NumberField.md +178 -0
  63. package/bundle/components/OrderedList.md +169 -0
  64. package/bundle/components/Overlay.md +237 -0
  65. package/bundle/components/Page.md +246 -0
  66. package/bundle/components/Pagination.md +298 -0
  67. package/bundle/components/Portal.md +119 -0
  68. package/bundle/components/Radio.md +174 -0
  69. package/bundle/components/Row.md +125 -0
  70. package/bundle/components/SMSField.md +160 -0
  71. package/bundle/components/SegmentedControl.md +235 -0
  72. package/bundle/components/SelectExtendedField.md +322 -0
  73. package/bundle/components/SelectField.md +213 -0
  74. package/bundle/components/Skeleton.md +129 -0
  75. package/bundle/components/Slider.md +157 -0
  76. package/bundle/components/SliderExtended.md +210 -0
  77. package/bundle/components/SliderRange.md +160 -0
  78. package/bundle/components/SmallInput.md +101 -0
  79. package/bundle/components/Spoiler.md +121 -0
  80. package/bundle/components/StatusTracker.md +151 -0
  81. package/bundle/components/Step.md +121 -0
  82. package/bundle/components/Stepper.md +163 -0
  83. package/bundle/components/Suggest.md +160 -0
  84. package/bundle/components/SuggestField.md +157 -0
  85. package/bundle/components/SwipeableArea.md +144 -0
  86. package/bundle/components/Table.md +199 -0
  87. package/bundle/components/Tabs.md +152 -0
  88. package/bundle/components/TabsExtended.md +273 -0
  89. package/bundle/components/TabsLine.md +189 -0
  90. package/bundle/components/Tag.md +135 -0
  91. package/bundle/components/TagColor.md +121 -0
  92. package/bundle/components/TagGroup.md +100 -0
  93. package/bundle/components/TextField.md +152 -0
  94. package/bundle/components/TextareaField.md +124 -0
  95. package/bundle/components/ThemeProvider.md +181 -0
  96. package/bundle/components/Tooltip.md +254 -0
  97. package/bundle/components/TopOverlay.md +145 -0
  98. package/bundle/components/TreeView.md +224 -0
  99. package/bundle/components/Triggers.md +121 -0
  100. package/bundle/guides/codestyle.md +205 -0
  101. package/bundle/guides/commits.md +166 -0
  102. package/bundle/guides/context.md +277 -0
  103. package/bundle/guides/stories.md +618 -0
  104. package/bundle/guides/template.md +154 -0
  105. package/bundle/guides/tests.md +301 -0
  106. package/bundle/icons.json +4156 -0
  107. package/bundle/manifest.json +796 -0
  108. package/bundle/release-notes/1.1.0.mdx +26 -0
  109. package/bundle/release-notes/1.1.1.mdx +14 -0
  110. package/bundle/release-notes/1.10.0.mdx +43 -0
  111. package/bundle/release-notes/1.11.0.mdx +19 -0
  112. package/bundle/release-notes/1.12.0.mdx +21 -0
  113. package/bundle/release-notes/1.13.0.mdx +43 -0
  114. package/bundle/release-notes/1.14.0.mdx +29 -0
  115. package/bundle/release-notes/1.15.0.mdx +56 -0
  116. package/bundle/release-notes/1.16.0.mdx +55 -0
  117. package/bundle/release-notes/1.17.0.mdx +15 -0
  118. package/bundle/release-notes/1.18.0.mdx +24 -0
  119. package/bundle/release-notes/1.19.0.mdx +48 -0
  120. package/bundle/release-notes/1.19.1.mdx +9 -0
  121. package/bundle/release-notes/1.2.0.mdx +80 -0
  122. package/bundle/release-notes/1.20.0.mdx +48 -0
  123. package/bundle/release-notes/1.21.0.mdx +66 -0
  124. package/bundle/release-notes/1.22.0.mdx +75 -0
  125. package/bundle/release-notes/1.23.0.mdx +50 -0
  126. package/bundle/release-notes/1.24.0.mdx +59 -0
  127. package/bundle/release-notes/1.25.0.mdx +52 -0
  128. package/bundle/release-notes/1.26.0.mdx +11 -0
  129. package/bundle/release-notes/1.3.0.mdx +21 -0
  130. package/bundle/release-notes/1.4.0.mdx +37 -0
  131. package/bundle/release-notes/1.5.0.mdx +24 -0
  132. package/bundle/release-notes/1.6.0.mdx +37 -0
  133. package/bundle/release-notes/1.6.1.mdx +11 -0
  134. package/bundle/release-notes/1.7.0.mdx +29 -0
  135. package/bundle/release-notes/1.8.0.mdx +38 -0
  136. package/bundle/release-notes/1.9.0.mdx +28 -0
  137. package/dist/index.d.ts +2 -0
  138. package/dist/index.js +324 -0
  139. package/dist/index.js.map +1 -0
  140. package/package.json +51 -0
@@ -0,0 +1,120 @@
1
+ ---
2
+ component: MobileView
3
+ category: Utilities
4
+ figma-node: ""
5
+ figma-file: ""
6
+ related: [MediaWidth]
7
+ tokens: []
8
+ stories: ""
9
+ version: "1.0"
10
+ ---
11
+
12
+ # MobileView
13
+
14
+ ## Назначение
15
+
16
+ Удобная обёртка над `MediaWidth maxWidth={SM_MAX}` — рендерит `children`
17
+ на мобиле (< 768px), иначе `fallback`. Фактически **именованный пресет**
18
+ для частого use-case'а mobile-vs-desktop.
19
+
20
+ Используй когда: нужно рендерить разные React-деревья для mobile/desktop.
21
+ Для стилей — CSS `@media` напрямую. Для более гибких границ —
22
+ `MediaWidth`/`MediaMinWidth`/`MediaMaxWidth`.
23
+
24
+ ---
25
+
26
+ ## Файловая структура
27
+
28
+ ```text
29
+ src/components/MobileView/
30
+ ├── MobileView.tsx # FC, 3 строки — обёртка над MediaWidth
31
+ └── index.ts # Barrel
32
+ ```
33
+
34
+ Нет стилей, тестов, stories.
35
+
36
+ ---
37
+
38
+ ## Варианты и props
39
+
40
+ ### `IMobileViewProps`
41
+
42
+ | Prop | Тип | Обязателен | Описание |
43
+ |---|---|---|---|
44
+ | `children` | `ReactElement \| null` | Да | Рендерится при ширине ≤ SM_MAX (767px). |
45
+ | `fallback` | `ReactElement \| null` | Да | Рендерится в остальных случаях (desktop). |
46
+
47
+ Обе props — обязательные. Следует контракту `MediaWidth`.
48
+
49
+ ---
50
+
51
+ ## Ключевые особенности реализации
52
+
53
+ ### Брейкпоинт — `EScreenWidth.SM_MAX`
54
+
55
+ ```tsx
56
+ <MediaWidth maxWidth={EScreenWidth.SM_MAX} fallback={fallback}>
57
+ {children}
58
+ </MediaWidth>
59
+ ```
60
+
61
+ - `SM_MAX` обычно равен `767px` (проверь в `helpers/breakpoints`).
62
+ - **Фиксированный брейкпоинт** — если нужен другой (например, планшеты), используй `MediaWidth` с явным `maxWidth`, а не оборачивай MobileView.
63
+
64
+ ### `children` на mobile, `fallback` на desktop
65
+
66
+ Контракт отличается от "обычных" компонентов: **`children` — мобильное поведение**, а не базовое. При чтении кода это бывает контринтуитивно. Пример:
67
+
68
+ ```tsx
69
+ <MobileView
70
+ fallback={<DesktopLayout />}
71
+ >
72
+ <MobileLayout />
73
+ </MobileView>
74
+ ```
75
+
76
+ ### Не SSR-safe через `useMatchMedia` (см. MediaWidth-AI)
77
+
78
+ Поведение наследуется от `MediaWidth`: `window.innerWidth` читается при рендере.
79
+
80
+ ---
81
+
82
+ ## Инварианты
83
+
84
+ - **Брейкпоинт `SM_MAX`** фиксирован — публичное поведение.
85
+ - **`children` — mobile, `fallback` — desktop** — не менять семантику без миграции потребителей.
86
+ - **Обе prop'ы обязательны** (`ReactElement | null`, не `ReactNode`, не optional).
87
+ - **FC, не forwardRef** — ref не пробрасывается.
88
+
89
+ ---
90
+
91
+ ## Accessibility
92
+
93
+ Тот же нюанс, что и `MediaWidth`: разные React-деревья → смена DOM на брейкпоинте. Для стилистических различий предпочитай CSS.
94
+
95
+ ---
96
+
97
+ ## Дизайн-токены
98
+
99
+ Нет.
100
+
101
+ ---
102
+
103
+ ## Связанные компоненты
104
+
105
+ - `MediaWidth` — общая абстракция (MobileView — её конкретизация).
106
+ - `EScreenWidth.SM_MAX` — из `@sberbusiness/triplex-next/helpers/breakpoints`.
107
+
108
+ ---
109
+
110
+ ## Stories
111
+
112
+ Нет.
113
+
114
+ ---
115
+
116
+ ## История изменений
117
+
118
+ | Дата | Изменение |
119
+ |---|---|
120
+ | 2026-04-18 | Создан документ |
@@ -0,0 +1,308 @@
1
+ ---
2
+ component: ModalWindow
3
+ category: Overlays
4
+ figma-node: ""
5
+ figma-file: ""
6
+ related: [LightBox, Portal, Island, Page, TriggerClickOnKeyDownEvent, Button]
7
+ tokens:
8
+ - --modalWindow-screen-left
9
+ - --modalWindow-screen-width
10
+ - --modalWindow-screen-top
11
+ - --modal-window-top
12
+ stories: stories/ModalWindow/ModalWindow.stories.tsx
13
+ version: "1.0"
14
+ ---
15
+
16
+ # ModalWindow
17
+
18
+ ## Назначение
19
+
20
+ Модальный диалог средних размеров — контент по центру экрана, backdrop-маска,
21
+ focus-trap, Escape-close, анимированное появление/исчезновение (CSSTransition).
22
+ Подкомпоненты: `Header` (на базе `HeaderPage`), `Body` (на базе `Island`),
23
+ `Footer` (на базе `FooterPage`), `Content` (scroll-container с Page внутри),
24
+ `Close` (Button с Escape-handler), и `ViewManager` (вычисляет CSS-переменные
25
+ позиционирования).
26
+
27
+ Используй когда: нужен стандартный диалог (подтверждение, мастер, форма).
28
+ Для полноэкранного — `LightBox`. Для поповера — `Overlay`.
29
+
30
+ ---
31
+
32
+ ## Файловая структура
33
+
34
+ ```text
35
+ src/components/ModalWindow/
36
+ ├── ModalWindow.tsx # forwardRef, Portal + CSSTransition + FocusTrap
37
+ ├── index.ts # Barrel всех подкомпонентов
38
+ ├── components/
39
+ │ ├── ModalWindowHeader.tsx # Обёртка над HeaderPage type=FIRST, + .Title
40
+ │ ├── ModalWindowBody.tsx # Обёртка над Island TYPE_1, MD
41
+ │ ├── ModalWindowContent.tsx # <div> + <Page> + optional LoaderScreen
42
+ │ ├── ModalWindowFooter.tsx # Обёртка над FooterPage type=FIRST, + .Description
43
+ │ ├── ModalWindowClose.tsx # Button + TriggerClickOnKeyDownEvent(ESCAPE)
44
+ │ └── ModalWindowViewManager.tsx # CSS-переменные --modalWindow-screen-*
45
+ └── styles/*.module.less
46
+ ```
47
+
48
+ ---
49
+
50
+ ## Compound-структура
51
+
52
+ ```tsx
53
+ <ModalWindow
54
+ isOpen={isOpen}
55
+ closeButton={<ModalWindow.Close onClick={onClose} />}
56
+ size={EComponentSize.MD}
57
+ onExited={onAnimationClose}
58
+ >
59
+ <ModalWindow.Content isLoading={loading}>
60
+ <ModalWindow.Header>
61
+ <ModalWindow.Header.Title>Заголовок</ModalWindow.Header.Title>
62
+ </ModalWindow.Header>
63
+
64
+ <ModalWindow.Body>
65
+ Основной контент...
66
+ </ModalWindow.Body>
67
+
68
+ <ModalWindow.Footer>
69
+ <ModalWindow.Footer.Description>Подпись</ModalWindow.Footer.Description>
70
+ <Button>ОК</Button>
71
+ </ModalWindow.Footer>
72
+ </ModalWindow.Content>
73
+ </ModalWindow>
74
+ ```
75
+
76
+ **Важно:** сами подкомпоненты (`ModalWindowHeader`, `ModalWindowBody`,
77
+ `ModalWindowContent`, `ModalWindowFooter`, `ModalWindowClose`) экспортируются
78
+ из barrel, но **не навешаны** на `ModalWindow` через `Object.assign`. Нужно
79
+ импортировать их **именованно**: `import { ModalWindow, ModalWindowHeader } from ...`.
80
+
81
+ ---
82
+
83
+ ## Варианты и props
84
+
85
+ ### ModalWindow — `IModalWindowProps extends HTMLAttributes<HTMLDivElement>`
86
+
87
+ | Prop | Тип | Default | Описание |
88
+ |---|---|---|---|
89
+ | `isOpen` | `boolean` | — (обязателен) | Управляемое состояние. |
90
+ | `children` | `ReactElement` | — | Один корневой элемент (обычно `ModalWindowContent`). |
91
+ | `closeButton` | `ReactNode` | — (обязателен) | Кнопка закрыть, вставляется рядом с `children`. Обычно `<ModalWindowClose onClick={onClose} />`. |
92
+ | `focusTrapProps` | `FocusTrapProps` | — | Переопределение параметров `focus-trap-react`. |
93
+ | `onExited` | `() => void` | — | Callback после завершения анимации закрытия. |
94
+ | `size` | `EComponentSize` | `MD` | Размер модалки (ширина). |
95
+ | `topPosition` | `number` | `100` | Отступ сверху в px. Пишется в CSS-переменную `--modal-window-top`. |
96
+ | `containerClassName` | `string` | — | className внешнего контейнера (с backdrop'ом). |
97
+ | `className`, `...HTMLDivAttributes` | — | — | Стандартные, идут на `div[role="dialog"]`. |
98
+
99
+ ### ModalWindowClose — `IModalWindowCloseProps extends Omit<IButtonSecondaryProps, "size" | "theme" | "icon">`
100
+
101
+ - Кнопка с иконкой `CrossStrokeSrvIcon20` в правом-верхнем углу модалки.
102
+ - **Автоматически триггерит клик по Escape** через `TriggerClickOnKeyDownEvent` (eventKeyCode=ESCAPE).
103
+ - `size=MD` и `theme=SECONDARY` захардкожены — не переопределить.
104
+ - **Title "Закрыть"** — хардкод русского, i18n-debt.
105
+
106
+ ### ModalWindowHeader — `IModalWindowHeaderProps extends Omit<IHeaderPageTypeFirstProps, "children" | "type">`
107
+
108
+ - Обёртка над `<HeaderPage type={FIRST}>`.
109
+ - Статический `.Title = HeaderPage.Title`.
110
+
111
+ ### ModalWindowBody — `IModalWindowBodyProps extends IIslandProps`
112
+
113
+ - Обёртка над `<Island type={TYPE_1} size={MD}>` с дополнительным классом `modalWindowBody`.
114
+ - **Хардкод TYPE_1/MD** — если нужен другой тип/размер Island, текущий API не позволяет.
115
+
116
+ ### ModalWindowContent — `IModalWindowContentProps extends HTMLAttributes<HTMLDivElement>`
117
+
118
+ | Prop | Тип | Описание |
119
+ |---|---|---|
120
+ | `isLoading` | `boolean` | Показывает `LoaderScreen type="middle"` над содержимым. |
121
+ | `loadingTitle` | `ReactNode` | Подпись под спиннером. |
122
+
123
+ Внутри — обёртывает `children` в `<Page>`. Scroll-контейнер.
124
+
125
+ ### ModalWindowFooter — `IModalWindowFooterProps extends Omit<IFooterPageTypeFirstProps, "children" | "type">`
126
+
127
+ - Обёртка над `<FooterPage type={FIRST}>`.
128
+ - Статический `.Description = FooterPage.Description`.
129
+
130
+ ### ModalWindowViewManager
131
+
132
+ Рендерится автоматически внутри `ModalWindow`. Отдельно использовать не нужно (но экспортирован для nuance-сценариев).
133
+
134
+ ---
135
+
136
+ ## Ключевые особенности реализации
137
+
138
+ ### Два DOM-контейнера: `#ufs-modal-window-wrapper` + свой `portal-node`
139
+
140
+ ```ts
141
+ let wrapperNode = document.querySelector<HTMLDivElement>(`#${modalNodeName}-wrapper`);
142
+ if (!wrapperNode) { /* create & append to body */ }
143
+
144
+ mountNode.current = document.createElement("div");
145
+ mountNode.current.className = `${modalNodeName}-portal-node`;
146
+ wrapperNode.appendChild(mountNode.current);
147
+ ```
148
+
149
+ - **Каждая открытая ModalWindow создаёт свой `.portal-node`** внутри общего `#ufs-modal-window-wrapper`. Это позволяет держать несколько модалок параллельно (хотя обычно одна).
150
+ - При закрытии portal-node удаляется, wrapper — остаётся (для повторного использования).
151
+ - **ID wrapper'а `ufs-modal-window-wrapper`** — известная "точка привязки" для интеграции с Layout'ом (сббол).
152
+
153
+ ### Body-классы `modal-open` + `no-hash-overflow-hidden`
154
+
155
+ ```ts
156
+ const bodyClassNameModalOpen = ["modal-open", "no-hash-overflow-hidden"];
157
+ ```
158
+
159
+ - При открытии — **добавляются на `body`** (не на `documentElement`, в отличие от LightBox).
160
+ - Класс `modal-open` — от внешнего Layout (сббол), блюрит фоновый контент.
161
+ - Снимаются при закрытии/unmount.
162
+
163
+ ### Animation — `CSSTransition` + `appear` + `unmountOnExit`
164
+
165
+ ```tsx
166
+ <CSSTransition
167
+ in={isOpen}
168
+ timeout={animationExitTime} // 300
169
+ classNames="global-modalWindowTransition"
170
+ appear enter exit
171
+ onEnter={handleOpenModal} // добавить body-классы
172
+ onExited={handleCloseModal} // удалить body-классы + unmount portal + onExited()
173
+ mountOnEnter
174
+ unmountOnExit
175
+ >
176
+ ```
177
+
178
+ - **classNames `global-modalWindowTransition`** — строка, не CSS-Modules. Соответствующие CSS-правила живут в глобальных стилях/index.less.
179
+ - `appear` — чтобы `onEnter` сработал на самой первой анимации.
180
+ - `unmountOnExit` — DOM-элемент удаляется после анимации, `renderPortal` сбрасывается в false.
181
+
182
+ ### `renderPortal` state и двойное монтирование
183
+
184
+ ```ts
185
+ const [renderPortal, setRenderPortal] = useState(false);
186
+ useEffect(() => { if (isOpen) { mountPortalNode(); setRenderPortal(true); } }, [isOpen]);
187
+ ```
188
+
189
+ - До открытия `renderPortal=false` → ранний return `null` → Portal не рендерится.
190
+ - При `isOpen=true` — создаётся mountNode (imperatively), ставится `renderPortal=true` — затем react рендерит Portal.
191
+ - **На повторные открытия** — state остаётся true после закрытия (handleCloseModal сбрасывает в false), и при следующем `isOpen=true` — снова создаётся mountNode.
192
+
193
+ ### `topPosition` через CSS-переменную
194
+
195
+ ```tsx
196
+ const topPositionStyle = { "--modal-window-top": `${topPosition}px` };
197
+ ```
198
+
199
+ - Default 100px. Команды могут переопределять, передавая `topPosition`.
200
+ - CSS-переменная именно `--modal-window-top` (kebab с дефисами), **не в naming-convention `--triplex-next-*`**. Это исторический паттерн, не переименовывать.
201
+
202
+ ### ModalWindowClose с Escape-handler
203
+
204
+ ```tsx
205
+ <TriggerClickOnKeyDownEvent eventKeyCode={EVENT_KEY_CODES.ESCAPE} targetRef={ref}>
206
+ <Button ref={ref} icon={<CrossIcon />} ... />
207
+ </TriggerClickOnKeyDownEvent>
208
+ ```
209
+
210
+ - Escape нажатие → имитирует click на кнопку → `onClick` → закрытие.
211
+ - Кнопка **обязательно должна быть в дереве** (не опциональна). Если `closeButton` не передан — TS ошибка (prop обязателен).
212
+
213
+ ### `ModalWindowViewManager` — CSS-переменные позиционирования
214
+
215
+ Аналогично `LightBoxViewManager`: пишет в `:root` (именно в `:root`, а не на `lightBoxMountNode`):
216
+
217
+ ```css
218
+ :root {
219
+ --modalWindow-screen-left: ...;
220
+ --modalWindow-screen-width: ...;
221
+ --modalWindow-screen-top: ...;
222
+ }
223
+ ```
224
+
225
+ - Default id `modalWindowViewManagerNodeId` создаётся в body. Используется для позиционирования относительно окна (если есть глобальный sidebar, Layout может переопределить эту node).
226
+ - **Отличие от LightBox**: переменные в `:root` — глобальные; любая ModalWindow подстраивается.
227
+
228
+ ### `focus-trap-react` v11 (используется `FocusTrap` как default, не `.FocusTrap`)
229
+
230
+ ```ts
231
+ import { FocusTrap, FocusTrapProps } from "focus-trap-react";
232
+ ```
233
+
234
+ **Внимание**: старые версии использовали default-import. Не ломать.
235
+
236
+ ### ModalWindowBody хардкодит Island type=TYPE_1, size=MD
237
+
238
+ ```tsx
239
+ <Island type={EIslandType.TYPE_1} size={EComponentSize.MD} {...islandProps}>
240
+ ```
241
+
242
+ `...islandProps` идут **после** `type`/`size` — потребитель теоретически может переопределить через props. Но это не публичная часть API (not документированная). Не полагайся.
243
+
244
+ ---
245
+
246
+ ## Инварианты
247
+
248
+ - **`isOpen` управляемый, `children` — один `ReactElement`, `closeButton` — обязателен** — публичный контракт.
249
+ - **ID DOM-контейнеров: `ufs-modal-window-wrapper`, `modalWindowViewManagerNodeId`** — **интеграционные** точки с внешним Layout'ом. Не переименовывать.
250
+ - **Body-классы `modal-open`, `no-hash-overflow-hidden`** — контракт с внешним Layout; изменение — регрессия фонового блюра.
251
+ - **Escape → closeButton click** через `TriggerClickOnKeyDownEvent` — поведенческий контракт `ModalWindowClose`.
252
+ - **CSS-переменная `--modal-window-top`** (kebab-case, не `--triplex-next-ModalWindow-Top`) — публичная, исторический naming.
253
+ - **`CSSTransition classNames="global-modalWindowTransition"`** — строковый класс, нет в CSS-Modules. Стили в глобальном файле.
254
+ - **`ModalWindowHeader/Footer` — обёртки над HeaderPage/FooterPage type=FIRST** — не поддерживают TYPE_2/TYPE_3.
255
+ - **`ModalWindowBody` хардкодит Island TYPE_1/MD** — кастомизация через props не публична.
256
+ - **`ModalWindow.Close` title="Закрыть" + `TriggerClickOnKeyDownEvent` eventKeyCode=ESCAPE** — i18n-debt (rus-hardcode).
257
+ - **`forwardRef<HTMLDivElement>`** — корневая node (dialog), но **подкомпоненты не являются слотами через Object.assign**.
258
+ - **ViewManager пишет в `:root`**, не в scoped-элемент — глобально влияет на любой ModalWindow.
259
+
260
+ ---
261
+
262
+ ## Accessibility
263
+
264
+ - `role="dialog"` + `aria-modal="true"` — есть на корне.
265
+ - `FocusTrap` активен пока `isOpen` — фокус удерживается внутри.
266
+ - `allowOutsideClick: true` — outside click не ломает trap; закрытие делается **только через `closeButton`** или Escape.
267
+ - **Нет `aria-labelledby`/`aria-describedby` по умолчанию** — потребитель должен добавить через `...rest` или передать `aria-label`.
268
+ - `ModalWindowClose` — Button с `title="Закрыть"` (рус), tabIndex нормальный.
269
+ - Escape обрабатывается через TriggerClickOnKeyDownEvent — глобально на window (см. `KeyDownListener-AI`).
270
+
271
+ ---
272
+
273
+ ## Дизайн-токены
274
+
275
+ Runtime CSS-переменные:
276
+ ```
277
+ --modalWindow-screen-left, --modalWindow-screen-width, --modalWindow-screen-top — от ViewManager
278
+ --modal-window-top — от topPosition prop
279
+ ```
280
+
281
+ Визуал — от `Island` (внутри Body) и ThemeProvider scope (scopeClassName на контейнере).
282
+
283
+ ---
284
+
285
+ ## Связанные компоненты
286
+
287
+ - `LightBox` — полноэкранный аналог.
288
+ - `Portal` — используется для render'а.
289
+ - `Island` / `Page` / `HeaderPage` / `FooterPage` — базовые примитивы для Body/Content/Header/Footer.
290
+ - `Button` — в ModalWindowClose.
291
+ - `TriggerClickOnKeyDownEvent` — для Escape handling.
292
+ - `LoaderScreen` — loader в Content.
293
+ - `focus-trap-react`, `react-transition-group` — внешние зависимости.
294
+
295
+ ---
296
+
297
+ ## Stories
298
+
299
+ `stories/ModalWindow/ModalWindow.stories.tsx`
300
+ Примеры: `stories/ModalWindow/examples/`
301
+
302
+ ---
303
+
304
+ ## История изменений
305
+
306
+ | Дата | Изменение |
307
+ |---|---|
308
+ | 2026-04-18 | Создан документ |
@@ -0,0 +1,168 @@
1
+ ---
2
+ component: MonthYearField
3
+ category: Form Controls
4
+ figma-node: ""
5
+ figma-file: ""
6
+ related: [DatePickerExtended, Calendar, TextField, DropdownMobileInput, FormField]
7
+ tokens: []
8
+ stories: stories/MonthYearField/MonthYearField.stories.tsx
9
+ version: "1.0"
10
+ ---
11
+
12
+ # MonthYearField
13
+
14
+ ## Назначение
15
+
16
+ Поле для выбора **месяца и года** (без числа дня). Built on top of
17
+ `DatePickerExtended` c `pickType=monthYearPick`. Превращает строковое
18
+ значение ISO-даты (YYYY-MM-DD) в отображение "Mar 2026" в `TextField`,
19
+ показывает календарь по клику / Enter / Space, реализует clear-кнопку.
20
+
21
+ Используй когда: нужен month-picker (отчётные периоды, выбор срока).
22
+ Для полноценной даты — `DateField`. Для диапазона — `DateRange`/аналог.
23
+
24
+ ---
25
+
26
+ ## Файловая структура
27
+
28
+ ```text
29
+ src/components/MonthYearField/
30
+ ├── MonthYearField.tsx # forwardRef, обёртка над DatePickerExtended
31
+ ├── MonthYearFieldTarget.tsx # TextField с клик/keyboard → открытие dropdown
32
+ ├── MonthYearFieldContext.tsx # { onChange } — для clear в Target
33
+ ├── constants.ts # inputMonthYearFormat = "MMM YYYY"
34
+ ├── types.ts # IMonthYearFieldProps
35
+ ├── utils.ts # MonthYearPickerUtils: parse/validate даты
36
+ ├── index.ts # Barrel
37
+ └── __tests__/
38
+ ```
39
+
40
+ ---
41
+
42
+ ## Варианты и props
43
+
44
+ ### `IMonthYearFieldProps extends Omit<IDatePickerExtendedProps, "pickedDate" | "onDateChange" | "renderTarget" | "renderDropdownHeaderTarget">, Pick<ITextFieldProps, "size" | "status" | "label">`
45
+
46
+ | Prop | Тип | Default | Описание |
47
+ |---|---|---|---|
48
+ | `value` | `string` | — (обязателен) | ISO-дата (формата `format`), например `"2026-04-01"`. |
49
+ | `onChange` | `(value: string) => void` | — (обязателен) | Вызывается при выборе нового месяца. Передаётся дата в `format`. |
50
+ | `format` | `string` | `dateFormatYYYYMMDD` (`"YYYY-MM-DD"`) | moment-формат для `value`. |
51
+ | `limitRange` | `IDateLimitRange` | `globalLimitRange` | Границы доступных дат. |
52
+ | `placeholder` | `string` | — | Плейсхолдер input'а. |
53
+ | `size` | `EComponentSize` | — | Пробрасывается в TextField. |
54
+ | `status` | `EFormFieldStatus` | — | Пробрасывается в TextField (`DISABLED` → readOnly). |
55
+ | `label` | `ReactNode` | — | Label TextField'а. |
56
+ | `targetProps` | `ITextFieldProps` | — | Дополнительные props TextField. `inputProps` мержатся с внутренними. |
57
+ | `...DatePickerExtended-props` | — | — | Все остальные, кроме исключённых. |
58
+
59
+ **Исключены из `IDatePickerExtendedProps`**: `pickedDate`, `onDateChange`, `renderTarget`, `renderDropdownHeaderTarget` — они управляются внутренне.
60
+
61
+ ### Внутренние формирующие константы
62
+
63
+ - `inputMonthYearFormat = "MMM YYYY"` — как показывается в TextField (moment-format).
64
+ - `ECalendarPickType.monthYearPick` — `pickType` DatePickerExtended.
65
+
66
+ ---
67
+
68
+ ## Ключевые особенности реализации
69
+
70
+ ### Внутренний state `pickerValues`
71
+
72
+ ```ts
73
+ const [pickerValues, setPickerValues] = useState(
74
+ MonthYearPickerUtils.getPickerValues(value, format, limitRange)
75
+ );
76
+ ```
77
+
78
+ - `pickerValues = { inputString: "Mar 2026", calendarDate: moment }` — два представления одной даты.
79
+ - Синхронизируется с `value` через `useEffect(() => { if (newInputString !== current) setPickerValues(...) })` — обновление только при реальном изменении строки.
80
+
81
+ ### `MonthYearPickerUtils.getPickerValues`
82
+
83
+ ```ts
84
+ if (value.length === 0) return { calendarDate: null, inputString: "" };
85
+ const date = moment(value, format, true); // strict
86
+ if (!valid || outsideRange) return { calendarDate: null, inputString: "" };
87
+ return { calendarDate: date, inputString: date.format(inputMonthYearFormat) };
88
+ ```
89
+
90
+ - **Strict-парсинг** (третий аргумент `true`) — формат должен совпадать один-в-один.
91
+ - Выход за `limitRange` → `null` calendar date + пустая строка в input'е. **Невалидное значение воспринимается как отсутствующее** — потребитель не получит обратный callback.
92
+
93
+ ### `MonthYearFieldContext` — только для `onChange`
94
+
95
+ Нужен, чтобы `FormFieldClear` (внутри Target'а) мог вызвать `onChange("")` без prop-drilling через `renderTarget`.
96
+
97
+ ### `MonthYearFieldTarget` — Enter/Space → открыть dropdown
98
+
99
+ ```ts
100
+ handleInputKeyDown: isKey(event.code, "ENTER") → toggleDropdown;
101
+ isKey(event.code, "SPACE") → preventDefault + toggleDropdown;
102
+ ```
103
+
104
+ - Клик по input'у — тоже toggleDropdown.
105
+ - Input всегда `readOnly` (выбор только через календарь).
106
+ - В postfix'е — `FormFieldClear` + `ButtonIcon` с иконкой календаря. Иконка меняется по `size`: `CalendarStrokeSrvIcon16` (SM) / `CalendarStrokeSrvIcon20` (MD/LG).
107
+
108
+ ### Fallback на `dateFormatYYYYMMDD` и `globalLimitRange`
109
+
110
+ Если потребитель не передал `format`/`limitRange`, используются глобальные из `consts/DateConst`. Ожидается, что сам проект задаёт эти константы.
111
+
112
+ ### `DropdownMobileInput` как header-target для мобильного dropdown'а
113
+
114
+ На mobile (в `DatePickerExtended`) вместо TextField'а рендерится упрощённый input.
115
+
116
+ ---
117
+
118
+ ## Инварианты
119
+
120
+ - **`value: string` в `format`** — контракт. Не переключать на `Date`/`moment.Moment` без миграции.
121
+ - **Strict moment-parsing** — пустая/невалидная строка → `""` в UI. Потребитель должен синхронизировать невалидные значения сам.
122
+ - **`limitRange` — hard-clamp**: дата вне диапазона → пустое значение.
123
+ - **`MonthYearFieldContext` shape** (`{ onChange }`) — публичная точка расширения, не менять.
124
+ - **`inputMonthYearFormat = "MMM YYYY"`** — display-формат, не i18n-аизрован (moment уже локализован через global moment.locale, но формат-строка фиксирована).
125
+ - **`pickType=monthYearPick`** — контракт с DatePickerExtended. Изменение — breaking.
126
+ - **`targetProps.inputProps` мержатся** с внутренним `{ value, placeholder, readOnly }` — потребитель может переопределить.
127
+ - **ForwardRef<HTMLDivElement>** на корневую node DatePickerExtended.
128
+
129
+ ---
130
+
131
+ ## Accessibility
132
+
133
+ - Input `readOnly` — фокусируется, но не редактируется. Открытие: Enter / Space / click.
134
+ - **`<button>` с иконкой** (ButtonIcon) — `role="presentation"` и `tabIndex={-1}` — **не в tab-последовательности** (основной target — input). Дизайн-решение, чтобы не дублировать Tab-остановки.
135
+ - `FormFieldClear` — стандартная кнопка очистки (см. FormField-AI).
136
+ - Нет `aria-label` на иконке календаря — потребитель должен добавить через `targetProps` при необходимости.
137
+
138
+ ---
139
+
140
+ ## Дизайн-токены
141
+
142
+ Нет собственных — наследуются от `TextField` и `Calendar`.
143
+
144
+ ---
145
+
146
+ ## Связанные компоненты
147
+
148
+ - `DatePickerExtended` — основа логики (dropdown, popup, mobile-variant).
149
+ - `Calendar` — сам календарь (`pickType=monthYearPick` — режим месяца).
150
+ - `TextField` — визуальный target.
151
+ - `FormField.Clear` — кнопка очистки.
152
+ - `ButtonIcon` — кнопка календаря в postfix.
153
+ - `moment.js` — парсинг/форматирование (peerDependency в библиотеке).
154
+
155
+ ---
156
+
157
+ ## Stories
158
+
159
+ `stories/MonthYearField/MonthYearField.stories.tsx`
160
+ Примеры: `stories/MonthYearField/examples/`
161
+
162
+ ---
163
+
164
+ ## История изменений
165
+
166
+ | Дата | Изменение |
167
+ |---|---|
168
+ | 2026-04-18 | Создан документ |