@sberbusiness/triplex-next-mcp-server 1.40.0-beta → 1.41.0-beta
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bundle/components/Calendar.md +183 -368
- package/bundle/components/Chip.md +310 -314
- package/bundle/components/FormField.md +468 -232
- package/bundle/components/IconWrapper.md +142 -89
- package/bundle/components/Island.md +221 -129
- package/bundle/components/KeyDownListener.md +215 -102
- package/bundle/components/LoaderMiddle.md +265 -0
- package/bundle/components/LoaderScreen.md +178 -81
- package/bundle/components/ThemeProvider.md +305 -117
- package/bundle/components/Tooltip.md +421 -181
- package/bundle/guides/commits.md +14 -6
- package/bundle/manifest.json +22 -11
- package/bundle/release-notes/1.41.0.mdx +76 -0
- package/bundle/release-notes/1.42.0.mdx +7 -0
- package/package.json +1 -1
|
@@ -1,9 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
component: Calendar
|
|
3
|
-
category: Date
|
|
4
|
-
|
|
5
|
-
figma-file: ""
|
|
6
|
-
related: [DateField, DateRange, DatePickerExtended, MonthYearField]
|
|
3
|
+
category: Date components
|
|
4
|
+
related: [DatePickerExtended, DateField, DateRange, MonthYearField, MonthYearRange, ChipDatePicker]
|
|
7
5
|
tokens:
|
|
8
6
|
- --triplex-next-Calendar-Background
|
|
9
7
|
- --triplex-next-Calendar-View_Header_Color
|
|
@@ -17,6 +15,7 @@ tokens:
|
|
|
17
15
|
- --triplex-next-Calendar-View_Item_BorderColor_Focus
|
|
18
16
|
- --triplex-next-Calendar-View_Item_Color_Default
|
|
19
17
|
- --triplex-next-Calendar-View_Item_Color_Disabled
|
|
18
|
+
- --triplex-next-Calendar-View_Item_Color_Hover
|
|
20
19
|
- --triplex-next-Calendar-View_Item_Color_Muted
|
|
21
20
|
- --triplex-next-Calendar-View_Item_Color_Selected
|
|
22
21
|
- --triplex-next-Calendar-View_Item_Color_Selected_Muted_Default
|
|
@@ -41,348 +40,76 @@ version: "1.0"
|
|
|
41
40
|
|
|
42
41
|
## Назначение
|
|
43
42
|
|
|
44
|
-
|
|
45
|
-
Рендерит заголовок с навигацией (предыдущий/следующий период, смена вида),
|
|
46
|
-
сетку периода (дни/месяцы/годы) и опциональный футер с кнопками быстрого
|
|
47
|
-
перехода (Вчера/Сегодня/Завтра).
|
|
43
|
+
Календарная сетка выбора даты или месяца: заголовок с периодом и кнопками перелистывания, таблица дней / месяцев / годов и опциональный футер с кнопками быстрого перехода. Компонент полностью управляемый — выбранную дату хранит потребитель (`pickedDate` + `onDateChange`), внутри хранится только состояние навигации (текущая страница и вид отображения).
|
|
48
44
|
|
|
49
|
-
|
|
50
|
-
`monthYearPick` (только месяц+год, без выбора конкретного дня). Позволяет
|
|
51
|
-
отмечать дни цветными метками четырёх типов (BASIC/STANDARD/ATTENTION/CRITICAL),
|
|
52
|
-
ограничивать диапазон допустимых дат, запрещать отдельные дни.
|
|
45
|
+
Используй когда: нужна встроенная в макет календарная сетка (внутри поповера, дропдауна, карточки) — например, при построении собственного date picker'а.
|
|
53
46
|
|
|
54
|
-
|
|
55
|
-
в попапе DateField, в модальном окне бронирования).
|
|
56
|
-
Не используй когда: нужен input с календарным попапом — бери `DateField`
|
|
57
|
-
(обёртка с `DateFieldPicker`); нужен range picker — бери `DateRange`; нужен
|
|
58
|
-
компактный picker только месяц+год — для input'а используй `MonthYearField`.
|
|
59
|
-
|
|
60
|
-
---
|
|
61
|
-
|
|
62
|
-
## Файловая структура
|
|
63
|
-
|
|
64
|
-
```text
|
|
65
|
-
src/components/Calendar/
|
|
66
|
-
├── Calendar.tsx # React.PureComponent (class), root компонент
|
|
67
|
-
├── CalendarContext.tsx # Контекст с format/limitRange/pickType/viewDate/viewMode/periodId + обработчики
|
|
68
|
-
├── CalendarViewContext.tsx # Контекст с viewItemFocusedRef (mutable)
|
|
69
|
-
├── enums.ts # ECalendarPickType, ECalendarViewMode, ECalendarDateMarkType
|
|
70
|
-
├── types.ts # ICalendarProps, TPickedDate, TCalendarMarkedDays, и др.
|
|
71
|
-
├── utils.ts # parsePickedDate, getHeader, formatDate, isDateOutOfRange, isDayDisabled
|
|
72
|
-
├── index.ts # Barrel: Calendar + enums + types
|
|
73
|
-
├── styles/
|
|
74
|
-
│ ├── Calendar.module.less # Root (width 300px, adaptive)
|
|
75
|
-
│ ├── CalendarControls.module.less # Заголовок с кнопками prev/next/view
|
|
76
|
-
│ ├── CalendarView.module.less # Таблица сетки, все состояния и метки
|
|
77
|
-
│ └── CalendarFooter.module.less # Футер с кнопками дат
|
|
78
|
-
└── components/ # Внутренние (не экспортируются наружу)
|
|
79
|
-
├── CalendarControls.tsx # Заголовок + prev/next/view кнопки
|
|
80
|
-
├── CalendarView.tsx # Переключатель между Days/Months/Years
|
|
81
|
-
├── CalendarViewDays.tsx # Сетка дней с weekday-шапкой
|
|
82
|
-
├── CalendarViewMonths.tsx # Сетка 12 месяцев
|
|
83
|
-
├── CalendarViewYears.tsx # Сетка 12 лет (диапазон -5..+6 от viewDate)
|
|
84
|
-
├── CalendarViewItem.tsx # Ячейка: день/месяц/год + marks/states
|
|
85
|
-
├── CalendarFooter.tsx # Контейнер футера
|
|
86
|
-
└── CalendarFooterButton.tsx # Кнопка "Вчера"/"Сегодня"/"Завтра"
|
|
87
|
-
```
|
|
88
|
-
|
|
89
|
-
Особенности:
|
|
90
|
-
- **`Calendar` — class component (`React.PureComponent`)**, не функция и не
|
|
91
|
-
`forwardRef`. Это легаси и осознанное решение: state-машина viewMode + viewDate
|
|
92
|
-
+ header проще как класс (cоответствующая рефакторинговая задача не запланирована).
|
|
93
|
-
- **Нет `forwardRef`** — один из немногих компонентов без него. Не добавлять
|
|
94
|
-
без согласования, класс не поддерживает forwardRef без обёртки.
|
|
95
|
-
- **Два контекста**: `CalendarContext` (данные + обработчики) и
|
|
96
|
-
`CalendarViewContext` (мутабельный ref для управления фокусом между viewMode).
|
|
97
|
-
- **`moment.js`** — зависимость времени. Весь внешний API построен вокруг
|
|
98
|
-
`Moment` и строк с `format`. Пользователь должен подключить локаль через
|
|
99
|
-
`moment.locale("ru")` (делается в stories).
|
|
47
|
+
Не используй когда: нужен готовый инпут с датой (`DateField`), выпадающий календарь у поля (`DatePickerExtended`), выбор месяца и года через поле (`MonthYearField`) или выбор диапазона (`DateRange`, `MonthYearRange`) — все они уже используют `Calendar` внутри.
|
|
100
48
|
|
|
101
49
|
---
|
|
102
50
|
|
|
103
51
|
## Варианты и props
|
|
104
52
|
|
|
105
|
-
###
|
|
106
|
-
|
|
107
|
-
**Обязательные:**
|
|
53
|
+
### Обязательные props
|
|
108
54
|
|
|
109
55
|
| Prop | Тип | Описание |
|
|
110
56
|
|---|---|---|
|
|
111
|
-
| `pickedDate` | `
|
|
112
|
-
| `onDateChange` | `(date: Moment) => void` |
|
|
57
|
+
| `pickedDate` | `string \| Moment \| null` | Выбранная дата. Строка парсится по `format`. `null` — дата не выбрана. |
|
|
58
|
+
| `onDateChange` | `(date: Moment) => void` | Вызывается при выборе даты пользователем. Компонент не хранит выбор — потребитель обязан обновить `pickedDate`. |
|
|
113
59
|
|
|
114
|
-
|
|
60
|
+
### Опциональные props
|
|
115
61
|
|
|
116
|
-
| Prop | Тип |
|
|
62
|
+
| Prop | Тип | По умолчанию | Описание |
|
|
117
63
|
|---|---|---|---|
|
|
118
|
-
| `format` | `string` | `
|
|
119
|
-
| `pickType` | `ECalendarPickType` | `
|
|
120
|
-
| `
|
|
121
|
-
| `
|
|
122
|
-
| `
|
|
123
|
-
| `
|
|
124
|
-
| `
|
|
125
|
-
| `adaptiveMode` | `boolean` |
|
|
126
|
-
| `onPageChange` | `(viewDate, viewMode) => void` | — |
|
|
127
|
-
| `onViewChange` | `(viewDate, viewMode) => void` | — |
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
Все `*ButtonProps` принимают либо объект HTML-атрибутов кнопки, либо функцию,
|
|
132
|
-
возвращающую атрибуты. Для `todayButtonProps` функция получает
|
|
133
|
-
`{viewMode, currentPeriodSelected}`, остальные — только `viewMode`.
|
|
134
|
-
|
|
135
|
-
| Prop | Тип | Назначение |
|
|
136
|
-
|---|---|---|
|
|
137
|
-
| `prevButtonProps` | `TButtonHTMLAttributesWithData \| (viewMode) => ...` | Кнопка "Предыдущий". Обязательные `data-*` атрибуты. |
|
|
138
|
-
| `nextButtonProps` | `TButtonHTMLAttributesWithData \| (viewMode) => ...` | Кнопка "Следующий". |
|
|
139
|
-
| `viewButtonProps` | `TButtonHTMLAttributesWithData \| (viewMode) => ...` | Заголовок (кнопка смены viewMode). |
|
|
140
|
-
| `yesterdayButtonProps` | `TButtonHTMLAttributesWithData \| (viewMode) => ...` | Только в `datePick`+DAYS+текущий месяц. |
|
|
141
|
-
| `todayButtonProps` | `TButtonHTMLAttributesWithData \| ({viewMode, currentPeriodSelected}) => ...` | **Ключевой: если не передан — футер не рендерится вообще.** |
|
|
142
|
-
| `tomorrowButtonProps` | `TButtonHTMLAttributesWithData \| (viewMode) => ...` | Только в `datePick`+DAYS+текущий месяц. |
|
|
143
|
-
| `dayHtmlAttributes` | `TTdHTMLAttributesWithData \| (params: {marked}) => ...` | Атрибуты `<td>` ячейки дня. Функция получает `{marked: boolean}`. |
|
|
144
|
-
| `monthHtmlAttributes` | `TTdHTMLAttributesWithData` | Атрибуты `<td>` ячейки месяца. |
|
|
145
|
-
| `yearHtmlAttributes` | `TTdHTMLAttributesWithData` | Атрибуты `<td>` ячейки года. |
|
|
146
|
-
|
|
147
|
-
**`TButtonHTMLAttributesWithData` / `TTdHTMLAttributesWithData`** — требуют
|
|
148
|
-
минимум один `data-*` атрибут (intersection-тип). Это форсит потребителя
|
|
149
|
-
добавлять testId-подобные атрибуты.
|
|
150
|
-
|
|
151
|
-
---
|
|
152
|
-
|
|
153
|
-
### Enums
|
|
64
|
+
| `format` | `string` | `dateFormatYYYYMMDD` (`"YYYYMMDD"`) | Формат парсинга `pickedDate` / `defaultViewDate` и ключей `markedDays` / `disabledDays`. |
|
|
65
|
+
| `pickType` | `ECalendarPickType` | `DATE` | `DATE` — выбор дня (стартовый вид `DAYS`), `MONTH_YEAR` — выбор месяца (стартовый вид `MONTHS`, клик по месяцу вызывает `onDateChange`). |
|
|
66
|
+
| `defaultViewDate` | `string \| Moment` | — | Страница, открываемая при пустой `pickedDate`. Учитывается только при инициализации. |
|
|
67
|
+
| `limitRange` | `IDateLimitRange` | `globalLimitRange` (1900-01-01 — 2199-12-31) | Допустимый период. Даты вне периода отключены, кнопки перелистывания у границы — `disabled`. Обе границы (`dateFrom` / `dateTo`) опциональны — незаданная сторона берётся из `globalLimitRange`. |
|
|
68
|
+
| `markedDays` | `string[] \| Record<string, ECalendarDateMarkType>` | — | Дни с точкой-отметкой. Массив — отметка типа `BASIC`; объект — тип отметки на каждый день. Ключи в формате `format`. |
|
|
69
|
+
| `disabledDays` | `string[]` | — | Дни, недоступные для выбора (в формате `format`). При клавиатурной навигации пропускаются. |
|
|
70
|
+
| `reversedPick` | `boolean` | `false` | Обратный порядок выбора: старт с вида `YEARS` (год → месяц → день). Учитывается только при инициализации. |
|
|
71
|
+
| `adaptiveMode` | `boolean` | `false` | Адаптивная (мобильная) вёрстка: резиновая ширина и увеличенные ячейки, кнопки футера размера `MD` вместо `SM`. |
|
|
72
|
+
| `onPageChange` | `(viewDate: Moment, viewMode: ECalendarViewMode) => void` | — | Смена страницы внутри текущего вида (перелистывание, уход навигации за пределы страницы). |
|
|
73
|
+
| `onViewChange` | `(viewDate: Moment, viewMode: ECalendarViewMode) => void` | — | Смена вида отображения (дни ⇄ месяцы ⇄ годы). |
|
|
74
|
+
|
|
75
|
+
### Props вложенных элементов (`ICalendarNestedProps`)
|
|
154
76
|
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
| Значение | Числовой код | Описание |
|
|
77
|
+
| Prop | Тип | Описание |
|
|
158
78
|
|---|---|---|
|
|
159
|
-
| `
|
|
160
|
-
| `
|
|
79
|
+
| `dayHtmlAttributes` | `TTdHTMLAttributesWithData \| (({marked}) => TTdHTMLAttributesWithData)` | Атрибуты ячейки дня. Функциональный вариант получает признак `marked` — им удобно помечать дни с отметкой. Оба варианта допускают `data-*` атрибуты. |
|
|
80
|
+
| `monthHtmlAttributes` / `yearHtmlAttributes` | `TTdHTMLAttributesWithData` | Атрибуты ячеек месяца / года. |
|
|
81
|
+
| `prevButtonProps` / `nextButtonProps` | `TButtonHTMLAttributesWithData \| ((viewMode) => ...)` | Props кнопок перелистывания. `disabled` объединяется с внутренним расчётом по `limitRange`, `onClick` вызывается после смены страницы. |
|
|
82
|
+
| `viewButtonProps` | `TButtonHTMLAttributesWithData \| ((viewMode) => ...)` | Props кнопки заголовка (смена вида). В виде `YEARS` кнопка не рендерится — период выводится как `<span>`. |
|
|
83
|
+
| `todayButtonProps` | `TButtonHTMLAttributesWithData \| (({viewMode, currentPeriodSelected}) => ...)` | **Наличие этого prop включает футер.** Без него футер не рендерится вовсе. Текст задаёт потребитель (обычно «Сегодня» / «К текущей дате» по `currentPeriodSelected`). |
|
|
84
|
+
| `yesterdayButtonProps` / `tomorrowButtonProps` | `TButtonHTMLAttributesWithData \| ((viewMode) => ...)` | Кнопки «Вчера» / «Завтра». Рендерятся только вместе с футером и только когда открыта страница текущего месяца в виде `DAYS`. |
|
|
161
85
|
|
|
162
|
-
|
|
86
|
+
### Ограничения
|
|
163
87
|
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
| `"days"` (DAYS) | Сетка 6×7: weekday-шапка + дни месяца (включая края соседних). |
|
|
167
|
-
| `"months"` (MONTHS) | Сетка 3×4 из 12 месяцев года. |
|
|
168
|
-
| `"years"` (YEARS) | Сетка 3×4 из 12 лет (диапазон viewDate −5..+6). |
|
|
169
|
-
|
|
170
|
-
#### `ECalendarDateMarkType` (numeric enum)
|
|
171
|
-
|
|
172
|
-
| Значение | Код | Назначение |
|
|
173
|
-
|---|---|---|
|
|
174
|
-
| `BASIC` | 0 | Нейтральная метка (общие события). |
|
|
175
|
-
| `STANDARD` | 1 | Стандартная метка (выплаты, платежи). |
|
|
176
|
-
| `ATTENTION` | 2 | Внимание (срочно, но не критично). |
|
|
177
|
-
| `CRITICAL` | 3 | Критическая метка (просрочка, ошибка). |
|
|
178
|
-
|
|
179
|
-
Каждая метка — отдельный токен для `Default`/`Selected_Default`/`Selected_Hover`
|
|
180
|
-
(всего 12 токенов цветов меток).
|
|
181
|
-
|
|
182
|
-
---
|
|
183
|
-
|
|
184
|
-
## Ключевые особенности реализации
|
|
185
|
-
|
|
186
|
-
### Инициализация viewMode в конструкторе
|
|
187
|
-
|
|
188
|
-
```ts
|
|
189
|
-
if (reversedPick) {
|
|
190
|
-
viewMode = ECalendarViewMode.YEARS;
|
|
191
|
-
} else if (pickType == ECalendarPickType.monthYearPick) {
|
|
192
|
-
viewMode = ECalendarViewMode.MONTHS;
|
|
193
|
-
} else {
|
|
194
|
-
viewMode = ECalendarViewMode.DAYS;
|
|
195
|
-
}
|
|
196
|
-
```
|
|
197
|
-
|
|
198
|
-
`reversedPick` имеет приоритет над `pickType` — всегда начинает с YEARS.
|
|
199
|
-
В обычном `monthYearPick` стартует с MONTHS. По умолчанию — DAYS.
|
|
200
|
-
|
|
201
|
-
### Синхронизация `pickedDate` → `viewDate` в `componentDidUpdate`
|
|
202
|
-
|
|
203
|
-
Если внешний `pickedDate` изменился на валидную дату в **другом месяце**
|
|
204
|
-
относительно текущего `viewDate`, календарь автоматически переключается
|
|
205
|
-
в DAYS и навигирует на месяц новой даты. Это не типичный controlled-паттерн —
|
|
206
|
-
`viewDate` не контролируется потребителем, он производится от `pickedDate`.
|
|
207
|
-
|
|
208
|
-
### `uniqueId()` для `periodId`
|
|
209
|
-
|
|
210
|
-
```ts
|
|
211
|
-
private periodId = `calendar-period-${uniqueId()}`;
|
|
212
|
-
```
|
|
213
|
-
|
|
214
|
-
Генерируется в инстансе класса. Нужен для связывания weekday-шапки в
|
|
215
|
-
CalendarViewDays и сетки дней через `headers`/`id` ARIA-атрибуты (a11y для
|
|
216
|
-
screen reader'а: "Понедельник, 15 января").
|
|
217
|
-
|
|
218
|
-
### `CalendarContext.Provider` — единственный способ доступа subcomponent'ов
|
|
219
|
-
|
|
220
|
-
Внутренние компоненты (`CalendarView`, `CalendarControls`, `CalendarFooter`)
|
|
221
|
-
не принимают большинство props от Calendar напрямую. Все данные и обработчики
|
|
222
|
-
идут через контекст. Исключение — `prevButtonProps`/`nextButtonProps`/
|
|
223
|
-
`viewButtonProps` передаются в `CalendarControls` пропсами, так как имеют
|
|
224
|
-
различные типы и легче передать явно.
|
|
225
|
-
|
|
226
|
-
### Футер рендерится только при `todayButtonProps`
|
|
227
|
-
|
|
228
|
-
```tsx
|
|
229
|
-
{todayButtonProps && this.renderFooter()}
|
|
230
|
-
```
|
|
231
|
-
|
|
232
|
-
Это единственный гейт — без `todayButtonProps` футер не появляется. Кнопки
|
|
233
|
-
"Вчера" и "Завтра" показываются дополнительно только в `datePick`+DAYS,
|
|
234
|
-
когда `viewDate` находится в текущем месяце (`currentPeriodSelected`).
|
|
235
|
-
|
|
236
|
-
### `currentPeriodSelected` имеет разный смысл для pickType
|
|
237
|
-
|
|
238
|
-
```ts
|
|
239
|
-
if (pickType === ECalendarPickType.datePick) {
|
|
240
|
-
currentPeriodSelected = viewMode === DAYS && viewDate.isSame(todayDate, "month");
|
|
241
|
-
} else {
|
|
242
|
-
currentPeriodSelected = viewMode === MONTHS && viewDate.isSame(todayDate, "year");
|
|
243
|
-
}
|
|
244
|
-
```
|
|
245
|
-
|
|
246
|
-
Для `datePick` — "текущий месяц в поле зрения". Для `monthYearPick` —
|
|
247
|
-
"текущий год в поле зрения". Потребитель получает этот флаг в
|
|
248
|
-
`todayButtonProps`-функции, чтобы менять текст кнопки ("Сегодня" vs
|
|
249
|
-
"К текущей дате").
|
|
250
|
-
|
|
251
|
-
### `adaptiveMode` — не responsive, а переключатель размера
|
|
252
|
-
|
|
253
|
-
Не про media-queries. Прокидывает `.adaptive` класс на root, меняет ширину
|
|
254
|
-
на `auto` и паддинги на `20px`. Используется в контексте модальных окон /
|
|
255
|
-
DatePickerExtended, где родитель управляет шириной.
|
|
256
|
-
|
|
257
|
-
### `formatDate` для YEARS показывает диапазон
|
|
258
|
-
|
|
259
|
-
```ts
|
|
260
|
-
case YEARS:
|
|
261
|
-
const yearFrom = checkedDate.clone().add(-5, "y").format("YYYY");
|
|
262
|
-
const yearTo = checkedDate.clone().add(6, "y").format("YYYY");
|
|
263
|
-
return `${yearFrom} - ${yearTo}`;
|
|
264
|
-
```
|
|
265
|
-
|
|
266
|
-
12-летний диапазон, где viewDate находится на 6-м месте (−5 назад, +6 вперёд).
|
|
267
|
-
Это определяет и заголовок, и сами ячейки.
|
|
268
|
-
|
|
269
|
-
### `TCalendarMarkedDays` — два формата
|
|
270
|
-
|
|
271
|
-
```ts
|
|
272
|
-
type TCalendarMarkedDays = string[] | Record<string, ECalendarDateMarkType>;
|
|
273
|
-
```
|
|
274
|
-
|
|
275
|
-
Массив → все метки `BASIC`. Объект → тип метки по дате. Внутренне
|
|
276
|
-
`CalendarViewDays` нормализует оба варианта в `Record` и достаёт тип по
|
|
277
|
-
строковому ключу даты.
|
|
278
|
-
|
|
279
|
-
### `dayHtmlAttributes` как функция
|
|
280
|
-
|
|
281
|
-
```ts
|
|
282
|
-
type TDayHtmlAttributesFunction = (params: {marked: boolean}) => React.TdHTMLAttributes<...>;
|
|
283
|
-
```
|
|
284
|
-
|
|
285
|
-
Если передать функцию — получает `marked` для каждого дня, возвращает атрибуты.
|
|
286
|
-
Позволяет делать `data-marked="true"` атрибут без ручной проверки. Для месяцев
|
|
287
|
-
и годов функционального варианта нет (нет `marked`-логики).
|
|
288
|
-
|
|
289
|
-
---
|
|
290
|
-
|
|
291
|
-
## Инварианты
|
|
292
|
-
|
|
293
|
-
- **`React.PureComponent` class** — не превращать в функцию без масштабного
|
|
294
|
-
рефакторинга state-машины. Перезапись изменит поведение `componentDidUpdate`.
|
|
295
|
-
- **Нет `forwardRef`** — класс не форвардит ref. Не добавлять (осознанное
|
|
296
|
-
решение, есть в ROADMAP).
|
|
297
|
-
- **`defaultProps` на классе** — не удалять, не переносить в деструктуризацию.
|
|
298
|
-
Используется в `this.context`-типизации и вычислениях конструктора.
|
|
299
|
-
- **`periodId` на инстансе** (`this.periodId =`) — не static, иначе два
|
|
300
|
-
Calendar'а на странице получат одинаковый id и сломают a11y weekday-ссылок.
|
|
301
|
-
- **`moment.locale("ru")` — ответственность потребителя/stories.**
|
|
302
|
-
Calendar НЕ устанавливает локаль сам. Если забыть — заголовок и дни недели
|
|
303
|
-
будут на английском.
|
|
304
|
-
- **`pickedDate` — controlled**, `viewDate` / `viewMode` — internal state.
|
|
305
|
-
Не делать viewDate controlled без согласования: изменит API.
|
|
306
|
-
- **`onDateChange` — обязателен**. Нет default'а, без него выбор даты не
|
|
307
|
-
работает.
|
|
308
|
-
- **`todayButtonProps` — только гейт для футера**. Не рендерить футер без
|
|
309
|
-
него.
|
|
310
|
-
- **`data-tx={process.env.npm_package_version}`** на root — не удалять.
|
|
311
|
-
- **`CalendarContext.Provider` значение** передаётся через `pickType!` и
|
|
312
|
-
`limitRange!` (non-null assertion). Вся логика опирается на наличие
|
|
313
|
-
defaults — не убирать `defaultProps`.
|
|
314
|
-
- **`TButtonHTMLAttributesWithData` / `TTdHTMLAttributesWithData`** требуют
|
|
315
|
-
хотя бы один `data-*` атрибут (intersection). Не упрощать до
|
|
316
|
-
`ButtonHTMLAttributes` — это публичное обязательство контрактов для тестов.
|
|
317
|
-
- **Weekday-шапка, селектед-день и выключенный день — все проверяются через
|
|
318
|
-
`moment.isSame(..., unit)`**, не строковыми сравнениями. Не переписывать на
|
|
319
|
-
`===` по строкам: формат может отличаться.
|
|
320
|
-
|
|
321
|
-
---
|
|
322
|
-
|
|
323
|
-
## Accessibility
|
|
324
|
-
|
|
325
|
-
- **`periodId` + `headers`** на `<td>`-ячейках — озвучивает день недели для
|
|
326
|
-
screen reader'а ("Понедельник, 15 января"). Не убирать.
|
|
327
|
-
- **Фокус-менеджмент** — `CalendarViewContext.viewItemFocusedRef` хранит
|
|
328
|
-
последнюю сфокусированную ячейку, чтобы при смене viewMode (MONTHS → DAYS)
|
|
329
|
-
фокус возвращался на соответствующую ячейку. Не сломать при рефакторинге
|
|
330
|
-
CalendarViewItem.
|
|
331
|
-
- **Клавиатура** — arrow keys навигация реализована в `CalendarViewItem`
|
|
332
|
-
через внутренний handler (смотри компонент при изменениях).
|
|
333
|
-
- **`aria-label` на кнопках** — задаётся потребителем через
|
|
334
|
-
`prevButtonProps`/`nextButtonProps`/`viewButtonProps`. Нет default'ов,
|
|
335
|
-
потому что локализация — на стороне потребителя.
|
|
336
|
-
- **`data-*` атрибуты обязательны** через типы — обеспечивает testability и
|
|
337
|
-
часто используется как hook для analytics.
|
|
338
|
-
- **`disabledDays` и `limitRange`** — визуальное отключение + `disabled` на
|
|
339
|
-
`<button>` внутри ячейки. Screen reader озвучивает как "недоступно".
|
|
340
|
-
- **`role="status"` на весь Calendar?** — нет. Calendar — grid of interactive
|
|
341
|
-
cells, не статус. Внутри используется семантика `<table>` → `<td>` с
|
|
342
|
-
кнопками.
|
|
88
|
+
- `defaultViewDate` и `reversedPick` влияют на стартовое состояние и не пересчитываются при последующих ререндерах (кроме синхронизации страницы с изменившейся `pickedDate`).
|
|
89
|
+
- `pickType` задаёт стартовый вид только при инициализации, но продолжает влиять на поведение при каждом рендере: от него зависят выбор месяца (`MONTH_YEAR` возвращает дату, иначе — проваливается в дни) и состав футера.
|
|
343
90
|
|
|
344
91
|
---
|
|
345
92
|
|
|
346
93
|
## Дизайн-токены
|
|
347
94
|
|
|
348
|
-
**Root:**
|
|
349
|
-
```
|
|
350
|
-
--triplex-next-Calendar-Background # Фон календаря
|
|
351
95
|
```
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
```
|
|
355
|
-
--triplex-next-Calendar-View_Header_Color # Дни недели в шапке
|
|
356
|
-
--triplex-next-Calendar-View_Item_Color_Default
|
|
96
|
+
--triplex-next-Calendar-Background
|
|
97
|
+
--triplex-next-Calendar-View_Header_Color
|
|
357
98
|
--triplex-next-Calendar-View_Item_Background_Default
|
|
358
99
|
--triplex-next-Calendar-View_Item_Background_Hover
|
|
359
|
-
--triplex-next-Calendar-View_Item_BorderColor_Default
|
|
360
|
-
--triplex-next-Calendar-View_Item_BorderColor_Focus # Keyboard-фокус
|
|
361
|
-
```
|
|
362
|
-
|
|
363
|
-
**View — выбранная ячейка:**
|
|
364
|
-
```
|
|
365
|
-
--triplex-next-Calendar-View_Item_Color_Selected
|
|
366
100
|
--triplex-next-Calendar-View_Item_Background_Selected_Default
|
|
367
101
|
--triplex-next-Calendar-View_Item_Background_Selected_Hover
|
|
368
|
-
```
|
|
369
|
-
|
|
370
|
-
**View — "приглушённый" выбранный (muted, для days вне текущего месяца):**
|
|
371
|
-
```
|
|
372
|
-
--triplex-next-Calendar-View_Item_Color_Selected_Muted_Default
|
|
373
|
-
--triplex-next-Calendar-View_Item_Color_Selected_Muted_Hover
|
|
374
102
|
--triplex-next-Calendar-View_Item_Background_Selected_Muted_Default
|
|
375
103
|
--triplex-next-Calendar-View_Item_Background_Selected_Muted_Hover
|
|
376
|
-
--triplex-next-Calendar-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
--triplex-next-Calendar-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
```
|
|
104
|
+
--triplex-next-Calendar-View_Item_BorderColor_Default
|
|
105
|
+
--triplex-next-Calendar-View_Item_BorderColor_Focus
|
|
106
|
+
--triplex-next-Calendar-View_Item_Color_Default
|
|
107
|
+
--triplex-next-Calendar-View_Item_Color_Disabled
|
|
108
|
+
--triplex-next-Calendar-View_Item_Color_Hover
|
|
109
|
+
--triplex-next-Calendar-View_Item_Color_Muted
|
|
110
|
+
--triplex-next-Calendar-View_Item_Color_Selected
|
|
111
|
+
--triplex-next-Calendar-View_Item_Color_Selected_Muted_Default
|
|
112
|
+
--triplex-next-Calendar-View_Item_Color_Selected_Muted_Hover
|
|
386
113
|
--triplex-next-Calendar-View_Item_Mark_Basic_Background_Default
|
|
387
114
|
--triplex-next-Calendar-View_Item_Mark_Basic_Background_Selected_Default
|
|
388
115
|
--triplex-next-Calendar-View_Item_Mark_Basic_Background_Selected_Hover
|
|
@@ -397,44 +124,72 @@ type TDayHtmlAttributesFunction = (params: {marked: boolean}) => React.TdHTMLAtt
|
|
|
397
124
|
--triplex-next-Calendar-View_Item_Mark_Critical_Background_Selected_Hover
|
|
398
125
|
```
|
|
399
126
|
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
127
|
+
Кнопки заголовка и футера используют токены `Button` / `ButtonIcon`.
|
|
128
|
+
|
|
129
|
+
---
|
|
130
|
+
|
|
131
|
+
## Инварианты
|
|
132
|
+
|
|
133
|
+
- **`Calendar` — классовый компонент** (`React.PureComponent`), один из немногих оставшихся в библиотеке. `ref` на нём — экземпляр класса, а не DOM-элемент. Перевод на функциональный компонент или добавление `forwardRef` — breaking change, требует отдельного решения мейнтейнера.
|
|
134
|
+
- Компонент полностью управляемый: клик по дате не меняет `pickedDate`, а только вызывает `onDateChange`. Внутреннее состояние — `viewDate` (страница), `viewMode` (вид) и производная от `pickedDate` копия.
|
|
135
|
+
- Публичный API (`ICalendarProps`, `ICalendarNestedProps`, `ICalendarViewProps`, `TPickedDate`, `TPickedDateProp`, `TCalendarMarkedDays`, `TDayHtmlAttributes`, `TTdHTMLAttributesWithData`, `TButtonHTMLAttributesWithData`, `ECalendarPickType`, `ECalendarViewMode`, `ECalendarDateMarkType`) экспортируется через `src/components/Calendar/index.ts` — переименования и изменения значений enum'ов недопустимы.
|
|
136
|
+
- `ECalendarDateMarkType` — числовой enum. Проверки типа отметки делаются через `markType !== undefined`, а не через truthy — `BASIC` равен `0`.
|
|
137
|
+
- Компонент не хардкодит текст: подписи кнопок футера, `aria-label` кнопок перелистывания и любые заголовки передаёт потребитель через `*ButtonProps`. Библиотека мультиязычная.
|
|
138
|
+
- Внутренние модули (`utils.ts`, `CalendarContext`, `CalendarViewContext`, `components/*`) в barrel не экспортируются, но `isDayDisabled` и `isDateOutOfRange` из `utils.ts` импортируются соседними компонентами (`DateField`, `MonthYearField`) — не удалять и не переименовывать без проверки потребителей внутри `src`.
|
|
139
|
+
- `periodId` (`calendar-period-{uniqueId}`) связывает заголовок и таблицу через `aria-labelledby` — не удалять.
|
|
140
|
+
- В библиотеке действует React 17-совместимость (ветка `release-0`): не переводить компонент на `useId` и другие React 18-only API.
|
|
141
|
+
|
|
142
|
+
---
|
|
143
|
+
|
|
144
|
+
## Accessibility
|
|
145
|
+
|
|
146
|
+
- Таблица вида имеет `role="grid"` и `aria-labelledby={periodId}`, указывающий на заголовок с текущим периодом. Заголовок помечен `aria-live="polite"` — смена месяца/года озвучивается скринридером.
|
|
147
|
+
- Внутри сетки реализована roving tabindex-навигация: ровно одна ячейка имеет `tabIndex=0` (выбранная дата, либо первая доступная дата страницы), остальные — `tabIndex=-1`.
|
|
148
|
+
- Клавиатура в сетке:
|
|
149
|
+
- `ArrowLeft` / `ArrowRight` — соседняя ячейка строки (день / месяц / год);
|
|
150
|
+
- `ArrowUp` / `ArrowDown` — соседняя строка (неделя / 3 месяца / 3 года);
|
|
151
|
+
- `PageUp` / `PageDown` — предыдущая / следующая страница (месяц / год / 12 лет);
|
|
152
|
+
- `Enter` / `Space` — выбор даты (с `preventDefault`).
|
|
153
|
+
- Недоступные дни (`disabledDays`, выход за `limitRange`) при навигации стрелками пропускаются; попытка выйти за `limitRange` оставляет фокус на текущей дате.
|
|
154
|
+
- Уход навигации за пределы текущей страницы автоматически перелистывает страницу (`onPageChange`) и возвращает фокус на новую tabbable-ячейку. Фокус восстанавливается только если он уже находился внутри сетки — программная смена страницы (кнопки перелистывания, внешняя смена `pickedDate`) фокус не забирает.
|
|
155
|
+
- Выбранная ячейка помечается `aria-selected="true"`; отключённая — классом `disabled` и `pointer-events: none` (атрибут `disabled` у `<td>` невозможен).
|
|
156
|
+
- Кнопки перелистывания — `ButtonIcon` без текста: потребитель обязан передать `aria-label` через `prevButtonProps` / `nextButtonProps`.
|
|
404
157
|
|
|
405
158
|
---
|
|
406
159
|
|
|
407
160
|
## Связанные компоненты
|
|
408
161
|
|
|
409
|
-
- `
|
|
410
|
-
|
|
411
|
-
- `
|
|
412
|
-
|
|
413
|
-
- `
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
- `
|
|
418
|
-
|
|
419
|
-
- `
|
|
420
|
-
|
|
162
|
+
- `DatePickerExtended` — выпадающий календарь над произвольным таргетом; рендерит `Calendar` внутри дропдауна.
|
|
163
|
+
- `DateField` — поле ввода даты с календарём; использует `isDayDisabled` и `isDateOutOfRange` из `Calendar/utils` и тип `TPickedDate` из `Calendar/types`.
|
|
164
|
+
- `MonthYearField`, `MonthYearRange` — выбор месяца и года, конфигурируют `Calendar` через `pickType={ECalendarPickType.MONTH_YEAR}`; `MonthYearField` использует `isDateOutOfRange` из `Calendar/utils`.
|
|
165
|
+
- `DateRange` — выбор диапазона дат двумя полями.
|
|
166
|
+
- `ChipDatePicker` — чип-фильтр с календарём.
|
|
167
|
+
|
|
168
|
+
Внутренние субкомпоненты (в barrel не экспортируются, отдельного AI.md не имеют):
|
|
169
|
+
|
|
170
|
+
- `CalendarControls` — заголовок с кнопками перелистывания и смены вида.
|
|
171
|
+
- `CalendarView` — переключатель видов; хранит `viewItemFocusedRef` (признак фокуса внутри сетки) в `CalendarViewContext`, чтобы не сбрасывать выбранную для фокуса ячейку при перелистывании.
|
|
172
|
+
- `CalendarViewDays` / `CalendarViewMonths` / `CalendarViewYears` — сетки дней, месяцев и годов.
|
|
173
|
+
- `CalendarViewItem` — ячейка сетки: классы состояний, отметка, `aria-selected`, обработка `Enter` / `Space` и фокуса.
|
|
174
|
+
- `CalendarFooter` / `CalendarFooterButton` — футер и его кнопки быстрого перехода («Вчера» / «Сегодня» / «Завтра»).
|
|
175
|
+
- `utils.ts` — чистые функции: парсинг даты, формат заголовка, проверки периода и отключённых дней, сдвиг даты при клавиатурной навигации. Покрыты `__tests__/utils.test.tsx`.
|
|
421
176
|
|
|
422
177
|
---
|
|
423
178
|
|
|
424
179
|
## Stories
|
|
425
180
|
|
|
426
|
-
`stories/Calendar/Calendar.stories.tsx`
|
|
181
|
+
Основные истории: `stories/Calendar/Calendar.stories.tsx`
|
|
427
182
|
Файлы примеров: `stories/Calendar/examples/`
|
|
428
183
|
|
|
429
184
|
| Story | Example file | Что демонстрирует |
|
|
430
185
|
|---|---|---|
|
|
431
|
-
| `Playground` | `PlaygroundExample.tsx` | Интерактивный
|
|
432
|
-
| `Default` | `DefaultExample.tsx` |
|
|
433
|
-
| `
|
|
434
|
-
| `
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
186
|
+
| `Playground` | `PlaygroundExample.tsx` | Интерактивный контроль `defaultViewDate` / `adaptiveMode` и переключатели отмеченных дней, отключённых дней и кнопок футера |
|
|
187
|
+
| `Default` | `DefaultExample.tsx` | Минимальный управляемый календарь: `pickedDate` + `onDateChange` |
|
|
188
|
+
| `MarkedDays` | `MarkedDaysExample.tsx` | Отображается как «With marked days». Все четыре типа отметок (`BASIC` / `STANDARD` / `ATTENTION` / `CRITICAL`) |
|
|
189
|
+
| `DisabledDays` | `DisabledDaysExample.tsx` | Отображается как «With disabled days». Дни, недоступные для выбора |
|
|
190
|
+
| `Buttons` | `ButtonsExample.tsx` | Отображается как «With buttons». Футер с кнопками «Вчера» / «Сегодня» / «Завтра» и текстом по `currentPeriodSelected` |
|
|
191
|
+
| `ReversedPick` | `ReversedPickExample.tsx` | Обратный порядок выбора: год → месяц → день |
|
|
192
|
+
| `VisualTests` | `VisualTestsExample.tsx` | Скриншот-регрессия: календарь на фиксированной дате (январь 1970), чтобы снимок не зависел от текущей даты |
|
|
438
193
|
|
|
439
194
|
---
|
|
440
195
|
|
|
@@ -442,7 +197,8 @@ weekday на английском.
|
|
|
442
197
|
|
|
443
198
|
| Дата | Изменение |
|
|
444
199
|
|---|---|
|
|
445
|
-
| 2026-
|
|
200
|
+
| 2026-07-30 | Создан документ. AI-рефакторинг: общие хелперы клавиатурной навигации и сдвига даты в `utils.ts`, чистка дублирования в `CalendarControls` и сетках, JSDoc на публичных типах, `displayName` у субкомпонентов; добавлены unit-тесты (`Calendar`, `CalendarViewItem`, `utils`) |
|
|
201
|
+
| 2026-07-30 | Исправлены найденные при рефакторинге баги: выбор первой tabbable-ячейки перебирал кандидатов с накоплением шага (в виде годов — месяцами вместо лет; хелпер `getFirstEnabledDate`); заголовок при внешней смене `pickedDate` игнорировал формат вида (`MONTH_YEAR` показывал «March 1970» вместо «1970»); фокус после `PageUp`/`PageDown` не возвращался на tabbable-ячейку, если её позиция сетки не менялась; шаг страницы в `changeTabbableDate`/`changeFocusedDate` переведён на `NAVIGATION_STEPS.page`. Расширения API: `dateFrom`/`dateTo` в `IDateLimitRange` стали опциональными, функциональный `dayHtmlAttributes` допускает `data-*` (`TTdHTMLAttributesWithData`) |
|
|
446
202
|
|
|
447
203
|
---
|
|
448
204
|
|
|
@@ -453,8 +209,8 @@ weekday на английском.
|
|
|
453
209
|
`stories/Calendar/examples/DefaultExample.tsx`
|
|
454
210
|
|
|
455
211
|
```tsx
|
|
456
|
-
import moment from "moment";
|
|
457
212
|
import React, { useState } from "react";
|
|
213
|
+
import moment from "moment";
|
|
458
214
|
import { Calendar } from "@sberbusiness/triplex-next";
|
|
459
215
|
|
|
460
216
|
export const DefaultExample = () => {
|
|
@@ -464,40 +220,99 @@ export const DefaultExample = () => {
|
|
|
464
220
|
};
|
|
465
221
|
```
|
|
466
222
|
|
|
467
|
-
###
|
|
223
|
+
### MarkedDays
|
|
224
|
+
|
|
225
|
+
`stories/Calendar/examples/MarkedDaysExample.tsx`
|
|
226
|
+
|
|
227
|
+
```tsx
|
|
228
|
+
import React, { useState } from "react";
|
|
229
|
+
import moment from "moment";
|
|
230
|
+
import { Calendar, dateFormatYYYYMMDD, ECalendarDateMarkType } from "@sberbusiness/triplex-next";
|
|
231
|
+
|
|
232
|
+
export const MarkedDaysExample = () => {
|
|
233
|
+
const [pickedDate, setPickedDate] = useState(moment());
|
|
234
|
+
|
|
235
|
+
return (
|
|
236
|
+
<Calendar
|
|
237
|
+
pickedDate={pickedDate}
|
|
238
|
+
markedDays={{
|
|
239
|
+
[moment().subtract(2, "days").format(dateFormatYYYYMMDD)]: ECalendarDateMarkType.BASIC,
|
|
240
|
+
[moment().subtract(4, "days").format(dateFormatYYYYMMDD)]: ECalendarDateMarkType.STANDARD,
|
|
241
|
+
[moment().add(2, "days").format(dateFormatYYYYMMDD)]: ECalendarDateMarkType.ATTENTION,
|
|
242
|
+
[moment().add(4, "days").format(dateFormatYYYYMMDD)]: ECalendarDateMarkType.CRITICAL,
|
|
243
|
+
}}
|
|
244
|
+
onDateChange={setPickedDate}
|
|
245
|
+
/>
|
|
246
|
+
);
|
|
247
|
+
};
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
### DisabledDays
|
|
468
251
|
|
|
469
|
-
`stories/Calendar/examples/
|
|
252
|
+
`stories/Calendar/examples/DisabledDaysExample.tsx`
|
|
470
253
|
|
|
471
254
|
```tsx
|
|
255
|
+
import React, { useState } from "react";
|
|
472
256
|
import moment from "moment";
|
|
257
|
+
import { Calendar, dateFormatYYYYMMDD } from "@sberbusiness/triplex-next";
|
|
258
|
+
|
|
259
|
+
export const DisabledDaysExample = () => {
|
|
260
|
+
const [pickedDate, setPickedDate] = useState(moment());
|
|
261
|
+
|
|
262
|
+
return (
|
|
263
|
+
<Calendar
|
|
264
|
+
pickedDate={pickedDate}
|
|
265
|
+
disabledDays={[
|
|
266
|
+
moment().subtract(3, "days").format(dateFormatYYYYMMDD),
|
|
267
|
+
moment().subtract(5, "days").format(dateFormatYYYYMMDD),
|
|
268
|
+
moment().add(3, "days").format(dateFormatYYYYMMDD),
|
|
269
|
+
moment().add(5, "days").format(dateFormatYYYYMMDD),
|
|
270
|
+
]}
|
|
271
|
+
onDateChange={setPickedDate}
|
|
272
|
+
/>
|
|
273
|
+
);
|
|
274
|
+
};
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
### Buttons
|
|
278
|
+
|
|
279
|
+
`stories/Calendar/examples/ButtonsExample.tsx`
|
|
280
|
+
|
|
281
|
+
```tsx
|
|
473
282
|
import React, { useState } from "react";
|
|
474
|
-
import
|
|
283
|
+
import moment from "moment";
|
|
284
|
+
import { Calendar } from "@sberbusiness/triplex-next";
|
|
475
285
|
|
|
476
|
-
export const
|
|
477
|
-
const
|
|
478
|
-
const [pickedDateDatePick, setPickedDateDatePick] = useState(initialDate);
|
|
479
|
-
const [pickedDateMonthYearPick, setPickedDateMonthYearPick] = useState(initialDate);
|
|
286
|
+
export const ButtonsExample = () => {
|
|
287
|
+
const [pickedDate, setPickedDate] = useState(moment());
|
|
480
288
|
|
|
481
289
|
return (
|
|
482
|
-
<
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
<div>
|
|
492
|
-
<div style={{ marginBottom: "8px", fontSize: "16px", fontWeight: "700" }}>MONTHYEARPICK</div>
|
|
493
|
-
<Calendar
|
|
494
|
-
pickType={ECalendarPickType.monthYearPick}
|
|
495
|
-
pickedDate={pickedDateMonthYearPick}
|
|
496
|
-
onDateChange={setPickedDateMonthYearPick}
|
|
497
|
-
/>
|
|
498
|
-
</div>
|
|
499
|
-
</div>
|
|
290
|
+
<Calendar
|
|
291
|
+
pickedDate={pickedDate}
|
|
292
|
+
onDateChange={setPickedDate}
|
|
293
|
+
todayButtonProps={({ currentPeriodSelected }) => ({
|
|
294
|
+
children: currentPeriodSelected ? "Сегодня" : "К текущей дате",
|
|
295
|
+
})}
|
|
296
|
+
yesterdayButtonProps={{ children: "Вчера" }}
|
|
297
|
+
tomorrowButtonProps={{ children: "Завтра" }}
|
|
298
|
+
/>
|
|
500
299
|
);
|
|
501
300
|
};
|
|
502
301
|
```
|
|
503
302
|
|
|
303
|
+
### ReversedPick
|
|
304
|
+
|
|
305
|
+
`stories/Calendar/examples/ReversedPickExample.tsx`
|
|
306
|
+
|
|
307
|
+
```tsx
|
|
308
|
+
import React, { useState } from "react";
|
|
309
|
+
import moment from "moment";
|
|
310
|
+
import { Calendar } from "@sberbusiness/triplex-next";
|
|
311
|
+
|
|
312
|
+
export const ReversedPickExample = () => {
|
|
313
|
+
const [pickedDate, setPickedDate] = useState(moment());
|
|
314
|
+
|
|
315
|
+
return <Calendar pickedDate={pickedDate} onDateChange={setPickedDate} reversedPick />;
|
|
316
|
+
};
|
|
317
|
+
```
|
|
318
|
+
|