@snack-uikit/calendar 0.13.6 → 0.13.7
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/CHANGELOG.md +8 -0
- package/README.md +1 -0
- package/dist/cjs/components/Calendar/Calendar.d.ts +3 -1
- package/dist/cjs/components/Calendar/Calendar.js +2 -6
- package/dist/cjs/helperComponents/CalendarBase/CalendarBase.d.ts +3 -2
- package/dist/cjs/helperComponents/CalendarBase/CalendarBase.js +31 -4
- package/dist/cjs/helperComponents/CalendarBase/styles.module.css +5 -0
- package/dist/cjs/helperComponents/PeriodPresetsList/PeriodPresetsList.d.ts +16 -0
- package/dist/cjs/helperComponents/PeriodPresetsList/PeriodPresetsList.js +75 -0
- package/dist/cjs/helperComponents/PeriodPresetsList/index.d.ts +1 -0
- package/dist/cjs/helperComponents/PeriodPresetsList/index.js +25 -0
- package/dist/cjs/helperComponents/PeriodPresetsList/styles.module.css +56 -0
- package/dist/cjs/helperComponents/PeriodPresetsList/utils.d.ts +5 -0
- package/dist/cjs/helperComponents/PeriodPresetsList/utils.js +44 -0
- package/dist/cjs/types.d.ts +22 -0
- package/dist/esm/components/Calendar/Calendar.d.ts +3 -1
- package/dist/esm/components/Calendar/Calendar.js +2 -2
- package/dist/esm/helperComponents/CalendarBase/CalendarBase.d.ts +3 -2
- package/dist/esm/helperComponents/CalendarBase/CalendarBase.js +18 -4
- package/dist/esm/helperComponents/CalendarBase/styles.module.css +5 -0
- package/dist/esm/helperComponents/PeriodPresetsList/PeriodPresetsList.d.ts +16 -0
- package/dist/esm/helperComponents/PeriodPresetsList/PeriodPresetsList.js +35 -0
- package/dist/esm/helperComponents/PeriodPresetsList/index.d.ts +1 -0
- package/dist/esm/helperComponents/PeriodPresetsList/index.js +1 -0
- package/dist/esm/helperComponents/PeriodPresetsList/styles.module.css +56 -0
- package/dist/esm/helperComponents/PeriodPresetsList/utils.d.ts +5 -0
- package/dist/esm/helperComponents/PeriodPresetsList/utils.js +46 -0
- package/dist/esm/types.d.ts +22 -0
- package/package.json +2 -2
- package/src/components/Calendar/Calendar.tsx +4 -4
- package/src/helperComponents/CalendarBase/CalendarBase.tsx +45 -2
- package/src/helperComponents/CalendarBase/styles.module.scss +5 -0
- package/src/helperComponents/PeriodPresetsList/PeriodPresetsList.tsx +64 -0
- package/src/helperComponents/PeriodPresetsList/index.ts +1 -0
- package/src/helperComponents/PeriodPresetsList/styles.module.scss +39 -0
- package/src/helperComponents/PeriodPresetsList/utils.ts +54 -0
- package/src/types.ts +24 -0
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,14 @@
|
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
|
4
4
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
|
5
5
|
|
|
6
|
+
## <small>0.13.7 (2025-11-06)</small>
|
|
7
|
+
|
|
8
|
+
* feat(PDS-1292): presets for calendar range mode ([45929bd](https://github.com/cloud-ru-tech/snack-uikit/commit/45929bd))
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
|
|
6
14
|
## <small>0.13.6 (2025-10-28)</small>
|
|
7
15
|
|
|
8
16
|
### Only dependencies have been changed
|
package/README.md
CHANGED
|
@@ -43,6 +43,7 @@ import { Calendar } from '@snack-uikit/calendar';
|
|
|
43
43
|
| locale | `Intl.Locale` | Проставляется в соответствие с языком в настройках браузера | Локаль, в соответствие с которой выставляется язык названий и первый день недели |
|
|
44
44
|
| onFocusLeave | `(direction: FocusDirection) => void` | - | Колбек потери фокуса. Вызывается со значением `next`, когда фокус покидает компонент, передвигаясь вперед, по клавише `tab`. Со значением `prev` - по клавише стрелки вверх или `shift + tab`. |
|
|
45
45
|
| navigationStartRef | `RefObject<{ focus(): void; }>` | - | Ссылка на управление первым элементом навигации |
|
|
46
|
+
| presets | `PresetsOptions` | - | Настройки секции с пресетами быстрого выбора периода. Доступны только при mode === 'range' и отсутствии buildCellProps (временно PDS-3139) |
|
|
46
47
|
| value | `Date \| Range` | - | Выбранное значение.<br> - в режиме date тип `Date` <br> - в режиме range тип `Range` (`[Date, Date]`) <br> - в режиме month тип `Date` <br> - в режиме date-time тип `Date` <br> - в режиме year тип `Date` |
|
|
47
48
|
| defaultValue | `Date \| Range` | - | Значение по-умолчанию для uncontrolled.<br> - в режиме date тип `Date` <br> - в режиме range тип `Range` (`[Date, Date]`) <br> - в режиме month тип `Date` <br> - в режиме date-time тип `Date` <br> - в режиме year тип `Date` |
|
|
48
49
|
| onChangeValue | `((value: Date) => void) \| ((value: Range) => void) \| ((value: Date) => void) \| ((value: Date) => void) \| ((value: Date) => void)` | - | Колбек выбора значения.<br> - в режиме date принимает тип `Date` <br> - в режиме range принимает тип `Range` <br> - в режиме month принимает тип `Date` <br> - в режиме date-time принимает тип `Date` <br> - в режиме year принимает тип `Date` |
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { CSSProperties, RefObject } from 'react';
|
|
2
2
|
import { WithSupportProps } from '@snack-uikit/utils';
|
|
3
3
|
import { CALENDAR_MODE } from '../../constants';
|
|
4
|
-
import { BuildCellPropsFunction, FocusDirection, Range, Size } from '../../types';
|
|
4
|
+
import { BuildCellPropsFunction, FocusDirection, PresetsOptions, Range, Size } from '../../types';
|
|
5
5
|
type CommonCalendarProps = {
|
|
6
6
|
/**
|
|
7
7
|
* Размер
|
|
@@ -46,6 +46,8 @@ type CommonCalendarProps = {
|
|
|
46
46
|
navigationStartRef?: RefObject<{
|
|
47
47
|
focus(): void;
|
|
48
48
|
}>;
|
|
49
|
+
/** Настройки секции с пресетами быстрого выбора периода. Доступны только при mode === 'range' и отсутствии buildCellProps (временно PDS-3139) */
|
|
50
|
+
presets?: PresetsOptions;
|
|
49
51
|
};
|
|
50
52
|
type DateCalendarProps = CommonCalendarProps & {
|
|
51
53
|
/** Режим работы календаря: <br> - `date` - режим выбора даты */
|
|
@@ -19,12 +19,10 @@ const CalendarBase_1 = require("../../helperComponents/CalendarBase");
|
|
|
19
19
|
const utils_1 = require("./utils");
|
|
20
20
|
function Calendar(props) {
|
|
21
21
|
const {
|
|
22
|
-
className,
|
|
23
22
|
onChangeValue,
|
|
24
|
-
buildCellProps,
|
|
25
23
|
mode
|
|
26
24
|
} = props,
|
|
27
|
-
rest = __rest(props, ["
|
|
25
|
+
rest = __rest(props, ["onChangeValue", "mode"]);
|
|
28
26
|
const changeValueHandler = (0, react_1.useCallback)(value => {
|
|
29
27
|
if (mode === constants_1.CALENDAR_MODE.Date || mode === constants_1.CALENDAR_MODE.Month || mode === constants_1.CALENDAR_MODE.Year || mode === constants_1.CALENDAR_MODE.DateTime) {
|
|
30
28
|
const [date] = value;
|
|
@@ -35,10 +33,8 @@ function Calendar(props) {
|
|
|
35
33
|
}, [onChangeValue, mode]);
|
|
36
34
|
return (0, jsx_runtime_1.jsx)(CalendarBase_1.CalendarBase, Object.assign({}, rest, {
|
|
37
35
|
mode: mode,
|
|
38
|
-
className: className,
|
|
39
36
|
value: (0, utils_1.getNormalizedValue)(props.value),
|
|
40
37
|
defaultValue: (0, utils_1.getNormalizedValue)(props.defaultValue),
|
|
41
|
-
onChangeValue: changeValueHandler
|
|
42
|
-
buildCellProps: buildCellProps
|
|
38
|
+
onChangeValue: changeValueHandler
|
|
43
39
|
}));
|
|
44
40
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { CSSProperties, RefObject } from 'react';
|
|
2
2
|
import { WithSupportProps } from '@snack-uikit/utils';
|
|
3
|
-
import { BuildCellPropsFunction, CalendarMode, FocusDirection, Range, Size } from '../../types';
|
|
3
|
+
import { BuildCellPropsFunction, CalendarMode, FocusDirection, PresetsOptions, Range, Size } from '../../types';
|
|
4
4
|
export type CalendarBaseProps = WithSupportProps<{
|
|
5
5
|
mode: CalendarMode;
|
|
6
6
|
onChangeValue(value: Range): void;
|
|
@@ -20,5 +20,6 @@ export type CalendarBaseProps = WithSupportProps<{
|
|
|
20
20
|
navigationStartRef?: RefObject<{
|
|
21
21
|
focus(): void;
|
|
22
22
|
}>;
|
|
23
|
+
presets?: PresetsOptions;
|
|
23
24
|
}>;
|
|
24
|
-
export declare function CalendarBase({ className, mode, size, autofocus, fitToContainer, value: valueProp, defaultValue, onChangeValue, today: todayProp, showHolidays, showSeconds, style, locale: localeProp, onFocusLeave, buildCellProps, 'data-test-id': testId, navigationStartRef, ...rest }: CalendarBaseProps): import("react/jsx-runtime").JSX.Element;
|
|
25
|
+
export declare function CalendarBase({ className, mode, size, autofocus, fitToContainer, value: valueProp, defaultValue, onChangeValue, today: todayProp, showHolidays, showSeconds, style, locale: localeProp, onFocusLeave, buildCellProps, 'data-test-id': testId, navigationStartRef, presets, ...rest }: CalendarBaseProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -21,6 +21,7 @@ const jsx_runtime_1 = require("react/jsx-runtime");
|
|
|
21
21
|
const classnames_1 = __importDefault(require("classnames"));
|
|
22
22
|
const react_1 = require("react");
|
|
23
23
|
const uncontrollable_1 = require("uncontrollable");
|
|
24
|
+
const divider_1 = require("@snack-uikit/divider");
|
|
24
25
|
const locale_1 = require("@snack-uikit/locale");
|
|
25
26
|
const utils_1 = require("@snack-uikit/utils");
|
|
26
27
|
const constants_1 = require("../../constants");
|
|
@@ -31,6 +32,8 @@ const CalendarContext_1 = require("../CalendarContext");
|
|
|
31
32
|
const CalendarNavigation_1 = require("../CalendarNavigation");
|
|
32
33
|
const ColumnLabels_1 = require("../ColumnLabels");
|
|
33
34
|
const Footer_1 = require("../Footer");
|
|
35
|
+
const PeriodPresetsList_1 = require("../PeriodPresetsList");
|
|
36
|
+
const utils_3 = require("../PeriodPresetsList/utils");
|
|
34
37
|
const TimePickerBase_1 = require("../TimePickerBase");
|
|
35
38
|
const hooks_2 = require("./hooks");
|
|
36
39
|
const styles_module_scss_1 = __importDefault(require('./styles.module.css'));
|
|
@@ -69,13 +72,17 @@ function CalendarBase(_a) {
|
|
|
69
72
|
onFocusLeave,
|
|
70
73
|
buildCellProps,
|
|
71
74
|
'data-test-id': testId,
|
|
72
|
-
navigationStartRef
|
|
75
|
+
navigationStartRef,
|
|
76
|
+
presets
|
|
73
77
|
} = _a,
|
|
74
|
-
rest = __rest(_a, ["className", "mode", "size", "autofocus", "fitToContainer", "value", "defaultValue", "onChangeValue", "today", "showHolidays", "showSeconds", "style", "locale", "onFocusLeave", "buildCellProps", 'data-test-id', "navigationStartRef"]);
|
|
78
|
+
rest = __rest(_a, ["className", "mode", "size", "autofocus", "fitToContainer", "value", "defaultValue", "onChangeValue", "today", "showHolidays", "showSeconds", "style", "locale", "onFocusLeave", "buildCellProps", 'data-test-id', "navigationStartRef", "presets"]);
|
|
79
|
+
const {
|
|
80
|
+
t
|
|
81
|
+
} = (0, locale_1.useLocale)('Calendar');
|
|
75
82
|
const [viewMode, setViewMode] = (0, react_1.useState)(CALENDAR_DEFAULT_MODE_MAP[mode]);
|
|
76
83
|
const [viewShift, setViewShift] = (0, react_1.useState)(0);
|
|
77
84
|
const [value, setValueState] = (0, uncontrollable_1.useUncontrolledProp)(valueProp, defaultValue, onChangeValue);
|
|
78
|
-
const today = typeof todayProp === 'number' ? new Date(todayProp) : todayProp;
|
|
85
|
+
const today = (0, react_1.useMemo)(() => typeof todayProp === 'number' ? new Date(todayProp) : todayProp, [todayProp]);
|
|
79
86
|
const [referenceDate] = (0, react_1.useState)((value === null || value === void 0 ? void 0 : value[0]) || today || new Date());
|
|
80
87
|
const viewDate = (0, hooks_2.useViewDate)(referenceDate, viewMode, viewShift);
|
|
81
88
|
const [focus, setFocus] = (0, react_1.useState)(autofocus ? constants_1.AUTOFOCUS : undefined);
|
|
@@ -124,6 +131,16 @@ function CalendarBase(_a) {
|
|
|
124
131
|
ctxLang
|
|
125
132
|
}), [ctxLang, localeProp]);
|
|
126
133
|
const firstNotDisableCell = (0, react_1.useRef)([0, 0]);
|
|
134
|
+
const presetsItems = (0, react_1.useMemo)(() => {
|
|
135
|
+
if ((presets === null || presets === void 0 ? void 0 : presets.items) && presets.items.length > 0) {
|
|
136
|
+
return presets.items;
|
|
137
|
+
}
|
|
138
|
+
return (0, utils_3.getDefaultPresets)(t, today);
|
|
139
|
+
}, [presets === null || presets === void 0 ? void 0 : presets.items, t, today]);
|
|
140
|
+
const arePeriodPresetsDisplayed = mode === 'range' && (presets === null || presets === void 0 ? void 0 : presets.enabled) && !buildCellProps; // TODO PDS-3139
|
|
141
|
+
const onPresetClick = (0, react_1.useCallback)(selectedPeriod => {
|
|
142
|
+
setValue(selectedPeriod);
|
|
143
|
+
}, [setValue]);
|
|
127
144
|
return (0, jsx_runtime_1.jsx)("div", {
|
|
128
145
|
className: (0, classnames_1.default)(styles_module_scss_1.default.calendarWrapper, className),
|
|
129
146
|
"data-fit-to-container": fitToContainer || undefined,
|
|
@@ -174,7 +191,17 @@ function CalendarBase(_a) {
|
|
|
174
191
|
className: (0, classnames_1.default)(styles_module_scss_1.default.dateWrapper, DATE_WRAPPER_SIZE_MAP[size]),
|
|
175
192
|
"data-size": size,
|
|
176
193
|
"data-show-footer": mode === constants_1.CALENDAR_MODE.DateTime && viewMode === 'month' || undefined,
|
|
177
|
-
children: [(0, jsx_runtime_1.jsxs)(
|
|
194
|
+
children: [arePeriodPresetsDisplayed && (0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, {
|
|
195
|
+
children: [(0, jsx_runtime_1.jsx)(PeriodPresetsList_1.PeriodPresetsList, {
|
|
196
|
+
items: presetsItems,
|
|
197
|
+
onChange: onPresetClick,
|
|
198
|
+
showTitle: presets === null || presets === void 0 ? void 0 : presets.title,
|
|
199
|
+
"data-test-id": getTestId('presets')
|
|
200
|
+
}), (0, jsx_runtime_1.jsx)(divider_1.Divider, {
|
|
201
|
+
className: styles_module_scss_1.default.divider,
|
|
202
|
+
orientation: 'vertical'
|
|
203
|
+
})]
|
|
204
|
+
}), (0, jsx_runtime_1.jsxs)("div", Object.assign({}, (0, utils_1.extractSupportProps)(rest), {
|
|
178
205
|
className: (0, classnames_1.default)(styles_module_scss_1.default.calendar, CALENDAR_SIZE_MAP[size]),
|
|
179
206
|
style: style,
|
|
180
207
|
"data-size": size,
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { WithSupportProps } from '@snack-uikit/utils';
|
|
2
|
+
import { PresetItem, Range } from '../../types';
|
|
3
|
+
export type PresetsListProps = WithSupportProps<{
|
|
4
|
+
/** Действие при выборе пресета */
|
|
5
|
+
onChange(range: Range): void;
|
|
6
|
+
/** Список пресетов */
|
|
7
|
+
items: PresetItem[];
|
|
8
|
+
/**
|
|
9
|
+
* Скрытие заголовка списка
|
|
10
|
+
* @default true
|
|
11
|
+
*/
|
|
12
|
+
showTitle?: boolean;
|
|
13
|
+
/** CSS-класс */
|
|
14
|
+
className?: string;
|
|
15
|
+
}>;
|
|
16
|
+
export declare function PeriodPresetsList({ items, onChange, showTitle, className, ...rest }: PresetsListProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
var __rest = void 0 && (void 0).__rest || function (s, e) {
|
|
4
|
+
var t = {};
|
|
5
|
+
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0) t[p] = s[p];
|
|
6
|
+
if (s != null && typeof Object.getOwnPropertySymbols === "function") for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
|
|
7
|
+
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i])) t[p[i]] = s[p[i]];
|
|
8
|
+
}
|
|
9
|
+
return t;
|
|
10
|
+
};
|
|
11
|
+
var __importDefault = void 0 && (void 0).__importDefault || function (mod) {
|
|
12
|
+
return mod && mod.__esModule ? mod : {
|
|
13
|
+
"default": mod
|
|
14
|
+
};
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", {
|
|
17
|
+
value: true
|
|
18
|
+
});
|
|
19
|
+
exports.PeriodPresetsList = PeriodPresetsList;
|
|
20
|
+
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
21
|
+
const classnames_1 = __importDefault(require("classnames"));
|
|
22
|
+
const react_1 = require("react");
|
|
23
|
+
const list_1 = require("@snack-uikit/list");
|
|
24
|
+
const locale_1 = require("@snack-uikit/locale");
|
|
25
|
+
const utils_1 = require("@snack-uikit/utils");
|
|
26
|
+
const CalendarContext_1 = require("../CalendarContext");
|
|
27
|
+
const styles_module_scss_1 = __importDefault(require('./styles.module.css'));
|
|
28
|
+
function PeriodPresetsList(_a) {
|
|
29
|
+
var {
|
|
30
|
+
items,
|
|
31
|
+
onChange,
|
|
32
|
+
showTitle = true,
|
|
33
|
+
className
|
|
34
|
+
} = _a,
|
|
35
|
+
rest = __rest(_a, ["items", "onChange", "showTitle", "className"]);
|
|
36
|
+
const {
|
|
37
|
+
t
|
|
38
|
+
} = (0, locale_1.useLocale)('Calendar');
|
|
39
|
+
const {
|
|
40
|
+
size,
|
|
41
|
+
getTestId
|
|
42
|
+
} = (0, react_1.useContext)(CalendarContext_1.CalendarContext);
|
|
43
|
+
const listItems = (0, react_1.useMemo)(() => items.map(item => ({
|
|
44
|
+
id: item.id,
|
|
45
|
+
content: {
|
|
46
|
+
option: item.label
|
|
47
|
+
},
|
|
48
|
+
onClick() {
|
|
49
|
+
onChange(item.range);
|
|
50
|
+
},
|
|
51
|
+
checked: false
|
|
52
|
+
})), [items, onChange]);
|
|
53
|
+
return (0, jsx_runtime_1.jsxs)("div", Object.assign({
|
|
54
|
+
className: (0, classnames_1.default)(styles_module_scss_1.default.wrapper, className)
|
|
55
|
+
}, (0, utils_1.extractSupportProps)(rest), {
|
|
56
|
+
children: [showTitle && (0, jsx_runtime_1.jsx)("div", {
|
|
57
|
+
className: styles_module_scss_1.default.header,
|
|
58
|
+
"data-size": size,
|
|
59
|
+
children: (0, jsx_runtime_1.jsx)("span", {
|
|
60
|
+
className: styles_module_scss_1.default.title,
|
|
61
|
+
"data-test-id": getTestId('presets-header'),
|
|
62
|
+
children: t('presets')
|
|
63
|
+
})
|
|
64
|
+
}), (0, jsx_runtime_1.jsx)(list_1.List, {
|
|
65
|
+
size: size,
|
|
66
|
+
items: listItems,
|
|
67
|
+
scroll: true,
|
|
68
|
+
selection: {
|
|
69
|
+
mode: 'single',
|
|
70
|
+
value: undefined
|
|
71
|
+
},
|
|
72
|
+
hasListInFocusChain: false
|
|
73
|
+
})]
|
|
74
|
+
}));
|
|
75
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './PeriodPresetsList';
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
var __createBinding = void 0 && (void 0).__createBinding || (Object.create ? function (o, m, k, k2) {
|
|
4
|
+
if (k2 === undefined) k2 = k;
|
|
5
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
6
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
7
|
+
desc = {
|
|
8
|
+
enumerable: true,
|
|
9
|
+
get: function () {
|
|
10
|
+
return m[k];
|
|
11
|
+
}
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
Object.defineProperty(o, k2, desc);
|
|
15
|
+
} : function (o, m, k, k2) {
|
|
16
|
+
if (k2 === undefined) k2 = k;
|
|
17
|
+
o[k2] = m[k];
|
|
18
|
+
});
|
|
19
|
+
var __exportStar = void 0 && (void 0).__exportStar || function (m, exports) {
|
|
20
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
21
|
+
};
|
|
22
|
+
Object.defineProperty(exports, "__esModule", {
|
|
23
|
+
value: true
|
|
24
|
+
});
|
|
25
|
+
__exportStar(require("./PeriodPresetsList"), exports);
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
.wrapper{
|
|
2
|
+
display:flex;
|
|
3
|
+
flex-direction:column;
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
.title{
|
|
7
|
+
display:flex;
|
|
8
|
+
align-items:center;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
.header{
|
|
12
|
+
display:flex;
|
|
13
|
+
flex-grow:0;
|
|
14
|
+
flex-shrink:0;
|
|
15
|
+
align-items:center;
|
|
16
|
+
color:var(--sys-neutral-text-main, #41424e);
|
|
17
|
+
}
|
|
18
|
+
.header[data-size=s]{
|
|
19
|
+
padding-left:var(--space-calendar-container-s, 8px);
|
|
20
|
+
padding-right:var(--space-calendar-container-s, 8px);
|
|
21
|
+
font-family:var(--sans-label-m-font-family, SB Sans Interface);
|
|
22
|
+
font-weight:var(--sans-label-m-font-weight, Semibold);
|
|
23
|
+
line-height:var(--sans-label-m-line-height, 16px);
|
|
24
|
+
font-size:var(--sans-label-m-font-size, 12px);
|
|
25
|
+
letter-spacing:var(--sans-label-m-letter-spacing, 0px);
|
|
26
|
+
paragraph-spacing:var(--sans-label-m-paragraph-spacing, 6.6px);
|
|
27
|
+
}
|
|
28
|
+
.header[data-size=s] .title{
|
|
29
|
+
height:var(--size-calendar-container-header-lines-height-s, 32px);
|
|
30
|
+
}
|
|
31
|
+
.header[data-size=m]{
|
|
32
|
+
padding-left:var(--space-calendar-container-m, 8px);
|
|
33
|
+
padding-right:var(--space-calendar-container-m, 8px);
|
|
34
|
+
font-family:var(--sans-label-l-font-family, SB Sans Interface);
|
|
35
|
+
font-weight:var(--sans-label-l-font-weight, Semibold);
|
|
36
|
+
line-height:var(--sans-label-l-line-height, 20px);
|
|
37
|
+
font-size:var(--sans-label-l-font-size, 14px);
|
|
38
|
+
letter-spacing:var(--sans-label-l-letter-spacing, 0px);
|
|
39
|
+
paragraph-spacing:var(--sans-label-l-paragraph-spacing, 7.7px);
|
|
40
|
+
}
|
|
41
|
+
.header[data-size=m] .title{
|
|
42
|
+
height:var(--size-calendar-container-header-lines-height-m, 40px);
|
|
43
|
+
}
|
|
44
|
+
.header[data-size=l]{
|
|
45
|
+
padding-left:var(--space-calendar-container-l, 8px);
|
|
46
|
+
padding-right:var(--space-calendar-container-l, 8px);
|
|
47
|
+
font-family:var(--sans-label-l-font-family, SB Sans Interface);
|
|
48
|
+
font-weight:var(--sans-label-l-font-weight, Semibold);
|
|
49
|
+
line-height:var(--sans-label-l-line-height, 20px);
|
|
50
|
+
font-size:var(--sans-label-l-font-size, 14px);
|
|
51
|
+
letter-spacing:var(--sans-label-l-letter-spacing, 0px);
|
|
52
|
+
paragraph-spacing:var(--sans-label-l-paragraph-spacing, 7.7px);
|
|
53
|
+
}
|
|
54
|
+
.header[data-size=l] .title{
|
|
55
|
+
height:var(--size-calendar-container-header-lines-height-l, 48px);
|
|
56
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.getDefaultPresets = getDefaultPresets;
|
|
7
|
+
const dayInMs = 24 * 60 * 60 * 1000;
|
|
8
|
+
function getDefaultPresets(t, today) {
|
|
9
|
+
const now = today || new Date();
|
|
10
|
+
const nowInMs = now.getTime();
|
|
11
|
+
const calculatePeriodUpToNow = presetLimitInMs => {
|
|
12
|
+
const limit = new Date(now.getTime() + presetLimitInMs);
|
|
13
|
+
return nowInMs > limit.getTime() ? [limit, now] : [now, limit];
|
|
14
|
+
};
|
|
15
|
+
return [{
|
|
16
|
+
label: t('defaultPresets.lastWeek'),
|
|
17
|
+
id: 'week',
|
|
18
|
+
range: calculatePeriodUpToNow(dayInMs * -7)
|
|
19
|
+
}, {
|
|
20
|
+
label: t('defaultPresets.lastTwoWeeks'),
|
|
21
|
+
id: 'twoWeeks',
|
|
22
|
+
range: calculatePeriodUpToNow(dayInMs * -14)
|
|
23
|
+
}, {
|
|
24
|
+
label: t('defaultPresets.lastMonth'),
|
|
25
|
+
id: 'month',
|
|
26
|
+
range: calculatePeriodUpToNow(dayInMs * -30)
|
|
27
|
+
}, {
|
|
28
|
+
label: t('defaultPresets.lastQuarter'),
|
|
29
|
+
id: 'quarter',
|
|
30
|
+
range: calculatePeriodUpToNow(dayInMs * -90)
|
|
31
|
+
}, {
|
|
32
|
+
label: t('defaultPresets.lastThird'),
|
|
33
|
+
id: 'fourMonths',
|
|
34
|
+
range: calculatePeriodUpToNow(dayInMs * -120)
|
|
35
|
+
}, {
|
|
36
|
+
label: t('defaultPresets.lastYear'),
|
|
37
|
+
id: 'year',
|
|
38
|
+
range: calculatePeriodUpToNow(dayInMs * -365)
|
|
39
|
+
}, {
|
|
40
|
+
label: t('defaultPresets.lastTwoYears'),
|
|
41
|
+
id: 'twoYears',
|
|
42
|
+
range: calculatePeriodUpToNow(dayInMs * -365 * 2)
|
|
43
|
+
}];
|
|
44
|
+
}
|
package/dist/cjs/types.d.ts
CHANGED
|
@@ -43,3 +43,25 @@ export type DateAndTime = TimeValue & {
|
|
|
43
43
|
month?: number;
|
|
44
44
|
day?: number;
|
|
45
45
|
};
|
|
46
|
+
export type PresetItem = {
|
|
47
|
+
/** Лейбл пресета */
|
|
48
|
+
label: string;
|
|
49
|
+
/** ID периода */
|
|
50
|
+
id: string;
|
|
51
|
+
/** Период */
|
|
52
|
+
range: Range;
|
|
53
|
+
};
|
|
54
|
+
export type PresetsOptions = {
|
|
55
|
+
/**
|
|
56
|
+
* Включение отображения секции с пресетами
|
|
57
|
+
* @default false
|
|
58
|
+
*/
|
|
59
|
+
enabled?: boolean;
|
|
60
|
+
/**
|
|
61
|
+
* Отображение заголовка у секции с пресетами
|
|
62
|
+
* @default true
|
|
63
|
+
*/
|
|
64
|
+
title?: boolean;
|
|
65
|
+
/** Кастомные пресеты быстрого выбора периода относительно текущего момента */
|
|
66
|
+
items?: PresetItem[];
|
|
67
|
+
};
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { CSSProperties, RefObject } from 'react';
|
|
2
2
|
import { WithSupportProps } from '@snack-uikit/utils';
|
|
3
3
|
import { CALENDAR_MODE } from '../../constants';
|
|
4
|
-
import { BuildCellPropsFunction, FocusDirection, Range, Size } from '../../types';
|
|
4
|
+
import { BuildCellPropsFunction, FocusDirection, PresetsOptions, Range, Size } from '../../types';
|
|
5
5
|
type CommonCalendarProps = {
|
|
6
6
|
/**
|
|
7
7
|
* Размер
|
|
@@ -46,6 +46,8 @@ type CommonCalendarProps = {
|
|
|
46
46
|
navigationStartRef?: RefObject<{
|
|
47
47
|
focus(): void;
|
|
48
48
|
}>;
|
|
49
|
+
/** Настройки секции с пресетами быстрого выбора периода. Доступны только при mode === 'range' и отсутствии buildCellProps (временно PDS-3139) */
|
|
50
|
+
presets?: PresetsOptions;
|
|
49
51
|
};
|
|
50
52
|
type DateCalendarProps = CommonCalendarProps & {
|
|
51
53
|
/** Режим работы календаря: <br> - `date` - режим выбора даты */
|
|
@@ -15,7 +15,7 @@ import { CALENDAR_MODE } from '../../constants';
|
|
|
15
15
|
import { CalendarBase } from '../../helperComponents/CalendarBase';
|
|
16
16
|
import { getNormalizedValue } from './utils';
|
|
17
17
|
export function Calendar(props) {
|
|
18
|
-
const {
|
|
18
|
+
const { onChangeValue, mode } = props, rest = __rest(props, ["onChangeValue", "mode"]);
|
|
19
19
|
const changeValueHandler = useCallback((value) => {
|
|
20
20
|
if (mode === CALENDAR_MODE.Date ||
|
|
21
21
|
mode === CALENDAR_MODE.Month ||
|
|
@@ -27,5 +27,5 @@ export function Calendar(props) {
|
|
|
27
27
|
}
|
|
28
28
|
onChangeValue === null || onChangeValue === void 0 ? void 0 : onChangeValue(value);
|
|
29
29
|
}, [onChangeValue, mode]);
|
|
30
|
-
return (_jsx(CalendarBase, Object.assign({}, rest, { mode: mode,
|
|
30
|
+
return (_jsx(CalendarBase, Object.assign({}, rest, { mode: mode, value: getNormalizedValue(props.value), defaultValue: getNormalizedValue(props.defaultValue), onChangeValue: changeValueHandler })));
|
|
31
31
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { CSSProperties, RefObject } from 'react';
|
|
2
2
|
import { WithSupportProps } from '@snack-uikit/utils';
|
|
3
|
-
import { BuildCellPropsFunction, CalendarMode, FocusDirection, Range, Size } from '../../types';
|
|
3
|
+
import { BuildCellPropsFunction, CalendarMode, FocusDirection, PresetsOptions, Range, Size } from '../../types';
|
|
4
4
|
export type CalendarBaseProps = WithSupportProps<{
|
|
5
5
|
mode: CalendarMode;
|
|
6
6
|
onChangeValue(value: Range): void;
|
|
@@ -20,5 +20,6 @@ export type CalendarBaseProps = WithSupportProps<{
|
|
|
20
20
|
navigationStartRef?: RefObject<{
|
|
21
21
|
focus(): void;
|
|
22
22
|
}>;
|
|
23
|
+
presets?: PresetsOptions;
|
|
23
24
|
}>;
|
|
24
|
-
export declare function CalendarBase({ className, mode, size, autofocus, fitToContainer, value: valueProp, defaultValue, onChangeValue, today: todayProp, showHolidays, showSeconds, style, locale: localeProp, onFocusLeave, buildCellProps, 'data-test-id': testId, navigationStartRef, ...rest }: CalendarBaseProps): import("react/jsx-runtime").JSX.Element;
|
|
25
|
+
export declare function CalendarBase({ className, mode, size, autofocus, fitToContainer, value: valueProp, defaultValue, onChangeValue, today: todayProp, showHolidays, showSeconds, style, locale: localeProp, onFocusLeave, buildCellProps, 'data-test-id': testId, navigationStartRef, presets, ...rest }: CalendarBaseProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -9,10 +9,11 @@ var __rest = (this && this.__rest) || function (s, e) {
|
|
|
9
9
|
}
|
|
10
10
|
return t;
|
|
11
11
|
};
|
|
12
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
12
|
+
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
13
13
|
import cn from 'classnames';
|
|
14
14
|
import { useCallback, useMemo, useRef, useState } from 'react';
|
|
15
15
|
import { useUncontrolledProp } from 'uncontrollable';
|
|
16
|
+
import { Divider } from '@snack-uikit/divider';
|
|
16
17
|
import { useLocale } from '@snack-uikit/locale';
|
|
17
18
|
import { extractSupportProps } from '@snack-uikit/utils';
|
|
18
19
|
import { AUTOFOCUS, CALENDAR_MODE, SIZE, VIEW_MODE } from '../../constants';
|
|
@@ -23,6 +24,8 @@ import { CalendarContext } from '../CalendarContext';
|
|
|
23
24
|
import { CalendarNavigation } from '../CalendarNavigation';
|
|
24
25
|
import { ColumnLabels } from '../ColumnLabels';
|
|
25
26
|
import { Footer } from '../Footer';
|
|
27
|
+
import { PeriodPresetsList } from '../PeriodPresetsList';
|
|
28
|
+
import { getDefaultPresets } from '../PeriodPresetsList/utils';
|
|
26
29
|
import { TimePickerBase } from '../TimePickerBase';
|
|
27
30
|
import { useRange, useViewDate } from './hooks';
|
|
28
31
|
import styles from './styles.module.css';
|
|
@@ -44,11 +47,12 @@ const CALENDAR_DEFAULT_MODE_MAP = {
|
|
|
44
47
|
[CALENDAR_MODE.Year]: VIEW_MODE.Decade,
|
|
45
48
|
};
|
|
46
49
|
export function CalendarBase(_a) {
|
|
47
|
-
var { className, mode, size = SIZE.M, autofocus, fitToContainer = true, value: valueProp, defaultValue, onChangeValue, today: todayProp, showHolidays = false, showSeconds = true, style, locale: localeProp, onFocusLeave, buildCellProps, 'data-test-id': testId, navigationStartRef } = _a, rest = __rest(_a, ["className", "mode", "size", "autofocus", "fitToContainer", "value", "defaultValue", "onChangeValue", "today", "showHolidays", "showSeconds", "style", "locale", "onFocusLeave", "buildCellProps", 'data-test-id', "navigationStartRef"]);
|
|
50
|
+
var { className, mode, size = SIZE.M, autofocus, fitToContainer = true, value: valueProp, defaultValue, onChangeValue, today: todayProp, showHolidays = false, showSeconds = true, style, locale: localeProp, onFocusLeave, buildCellProps, 'data-test-id': testId, navigationStartRef, presets } = _a, rest = __rest(_a, ["className", "mode", "size", "autofocus", "fitToContainer", "value", "defaultValue", "onChangeValue", "today", "showHolidays", "showSeconds", "style", "locale", "onFocusLeave", "buildCellProps", 'data-test-id', "navigationStartRef", "presets"]);
|
|
51
|
+
const { t } = useLocale('Calendar');
|
|
48
52
|
const [viewMode, setViewMode] = useState(CALENDAR_DEFAULT_MODE_MAP[mode]);
|
|
49
53
|
const [viewShift, setViewShift] = useState(0);
|
|
50
54
|
const [value, setValueState] = useUncontrolledProp(valueProp, defaultValue, onChangeValue);
|
|
51
|
-
const today = typeof todayProp === 'number' ? new Date(todayProp) : todayProp;
|
|
55
|
+
const today = useMemo(() => (typeof todayProp === 'number' ? new Date(todayProp) : todayProp), [todayProp]);
|
|
52
56
|
const [referenceDate] = useState((value === null || value === void 0 ? void 0 : value[0]) || today || new Date());
|
|
53
57
|
const viewDate = useViewDate(referenceDate, viewMode, viewShift);
|
|
54
58
|
const [focus, setFocus] = useState(autofocus ? AUTOFOCUS : undefined);
|
|
@@ -69,6 +73,16 @@ export function CalendarBase(_a) {
|
|
|
69
73
|
const { lang: ctxLang } = useLocale();
|
|
70
74
|
const locale = useMemo(() => getLocale({ localeProp, ctxLang }), [ctxLang, localeProp]);
|
|
71
75
|
const firstNotDisableCell = useRef([0, 0]);
|
|
76
|
+
const presetsItems = useMemo(() => {
|
|
77
|
+
if ((presets === null || presets === void 0 ? void 0 : presets.items) && presets.items.length > 0) {
|
|
78
|
+
return presets.items;
|
|
79
|
+
}
|
|
80
|
+
return getDefaultPresets(t, today);
|
|
81
|
+
}, [presets === null || presets === void 0 ? void 0 : presets.items, t, today]);
|
|
82
|
+
const arePeriodPresetsDisplayed = mode === 'range' && (presets === null || presets === void 0 ? void 0 : presets.enabled) && !buildCellProps; // TODO PDS-3139
|
|
83
|
+
const onPresetClick = useCallback((selectedPeriod) => {
|
|
84
|
+
setValue(selectedPeriod);
|
|
85
|
+
}, [setValue]);
|
|
72
86
|
return (_jsx("div", { className: cn(styles.calendarWrapper, className), "data-fit-to-container": fitToContainer || undefined, "data-test-id": testId, children: _jsxs(CalendarContext.Provider, { value: {
|
|
73
87
|
locale,
|
|
74
88
|
size,
|
|
@@ -109,5 +123,5 @@ export function CalendarBase(_a) {
|
|
|
109
123
|
hoursKeyboardNavigationRef,
|
|
110
124
|
minutesKeyboardNavigationRef,
|
|
111
125
|
secondsKeyboardNavigationRef,
|
|
112
|
-
}, children: [_jsxs("div", { className: cn(styles.dateWrapper, DATE_WRAPPER_SIZE_MAP[size]), "data-size": size, "data-show-footer": (mode === CALENDAR_MODE.DateTime && viewMode === 'month') || undefined, children: [_jsxs("div", Object.assign({}, extractSupportProps(rest), { className: cn(styles.calendar, CALENDAR_SIZE_MAP[size]), style: style, "data-size": size, "data-fit-to-container": fitToContainer || undefined, children: [_jsxs("div", { className: styles.header, "data-size": size, children: [_jsx(CalendarNavigation, {}), _jsx(ColumnLabels, {})] }), _jsx("div", { className: styles.body, children: _jsx("div", { className: styles.rows, "data-size": size, children: _jsx(CalendarBody, {}) }) })] })), mode === CALENDAR_MODE.DateTime && viewMode === 'month' && _jsx(TimePickerBase, {})] }), _jsx(Footer, {})] }) }));
|
|
126
|
+
}, children: [_jsxs("div", { className: cn(styles.dateWrapper, DATE_WRAPPER_SIZE_MAP[size]), "data-size": size, "data-show-footer": (mode === CALENDAR_MODE.DateTime && viewMode === 'month') || undefined, children: [arePeriodPresetsDisplayed && (_jsxs(_Fragment, { children: [_jsx(PeriodPresetsList, { items: presetsItems, onChange: onPresetClick, showTitle: presets === null || presets === void 0 ? void 0 : presets.title, "data-test-id": getTestId('presets') }), _jsx(Divider, { className: styles.divider, orientation: 'vertical' })] })), _jsxs("div", Object.assign({}, extractSupportProps(rest), { className: cn(styles.calendar, CALENDAR_SIZE_MAP[size]), style: style, "data-size": size, "data-fit-to-container": fitToContainer || undefined, children: [_jsxs("div", { className: styles.header, "data-size": size, children: [_jsx(CalendarNavigation, {}), _jsx(ColumnLabels, {})] }), _jsx("div", { className: styles.body, children: _jsx("div", { className: styles.rows, "data-size": size, children: _jsx(CalendarBody, {}) }) })] })), mode === CALENDAR_MODE.DateTime && viewMode === 'month' && _jsx(TimePickerBase, {})] }), _jsx(Footer, {})] }) }));
|
|
113
127
|
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { WithSupportProps } from '@snack-uikit/utils';
|
|
2
|
+
import { PresetItem, Range } from '../../types';
|
|
3
|
+
export type PresetsListProps = WithSupportProps<{
|
|
4
|
+
/** Действие при выборе пресета */
|
|
5
|
+
onChange(range: Range): void;
|
|
6
|
+
/** Список пресетов */
|
|
7
|
+
items: PresetItem[];
|
|
8
|
+
/**
|
|
9
|
+
* Скрытие заголовка списка
|
|
10
|
+
* @default true
|
|
11
|
+
*/
|
|
12
|
+
showTitle?: boolean;
|
|
13
|
+
/** CSS-класс */
|
|
14
|
+
className?: string;
|
|
15
|
+
}>;
|
|
16
|
+
export declare function PeriodPresetsList({ items, onChange, showTitle, className, ...rest }: PresetsListProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
var __rest = (this && this.__rest) || function (s, e) {
|
|
2
|
+
var t = {};
|
|
3
|
+
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
|
|
4
|
+
t[p] = s[p];
|
|
5
|
+
if (s != null && typeof Object.getOwnPropertySymbols === "function")
|
|
6
|
+
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
|
|
7
|
+
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
|
|
8
|
+
t[p[i]] = s[p[i]];
|
|
9
|
+
}
|
|
10
|
+
return t;
|
|
11
|
+
};
|
|
12
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
13
|
+
import cn from 'classnames';
|
|
14
|
+
import { useContext, useMemo } from 'react';
|
|
15
|
+
import { List } from '@snack-uikit/list';
|
|
16
|
+
import { useLocale } from '@snack-uikit/locale';
|
|
17
|
+
import { extractSupportProps } from '@snack-uikit/utils';
|
|
18
|
+
import { CalendarContext } from '../CalendarContext';
|
|
19
|
+
import styles from './styles.module.css';
|
|
20
|
+
export function PeriodPresetsList(_a) {
|
|
21
|
+
var { items, onChange, showTitle = true, className } = _a, rest = __rest(_a, ["items", "onChange", "showTitle", "className"]);
|
|
22
|
+
const { t } = useLocale('Calendar');
|
|
23
|
+
const { size, getTestId } = useContext(CalendarContext);
|
|
24
|
+
const listItems = useMemo(() => items.map(item => ({
|
|
25
|
+
id: item.id,
|
|
26
|
+
content: {
|
|
27
|
+
option: item.label,
|
|
28
|
+
},
|
|
29
|
+
onClick() {
|
|
30
|
+
onChange(item.range);
|
|
31
|
+
},
|
|
32
|
+
checked: false,
|
|
33
|
+
})), [items, onChange]);
|
|
34
|
+
return (_jsxs("div", Object.assign({ className: cn(styles.wrapper, className) }, extractSupportProps(rest), { children: [showTitle && (_jsx("div", { className: styles.header, "data-size": size, children: _jsx("span", { className: styles.title, "data-test-id": getTestId('presets-header'), children: t('presets') }) })), _jsx(List, { size: size, items: listItems, scroll: true, selection: { mode: 'single', value: undefined }, hasListInFocusChain: false })] })));
|
|
35
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './PeriodPresetsList';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './PeriodPresetsList';
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
.wrapper{
|
|
2
|
+
display:flex;
|
|
3
|
+
flex-direction:column;
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
.title{
|
|
7
|
+
display:flex;
|
|
8
|
+
align-items:center;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
.header{
|
|
12
|
+
display:flex;
|
|
13
|
+
flex-grow:0;
|
|
14
|
+
flex-shrink:0;
|
|
15
|
+
align-items:center;
|
|
16
|
+
color:var(--sys-neutral-text-main, #41424e);
|
|
17
|
+
}
|
|
18
|
+
.header[data-size=s]{
|
|
19
|
+
padding-left:var(--space-calendar-container-s, 8px);
|
|
20
|
+
padding-right:var(--space-calendar-container-s, 8px);
|
|
21
|
+
font-family:var(--sans-label-m-font-family, SB Sans Interface);
|
|
22
|
+
font-weight:var(--sans-label-m-font-weight, Semibold);
|
|
23
|
+
line-height:var(--sans-label-m-line-height, 16px);
|
|
24
|
+
font-size:var(--sans-label-m-font-size, 12px);
|
|
25
|
+
letter-spacing:var(--sans-label-m-letter-spacing, 0px);
|
|
26
|
+
paragraph-spacing:var(--sans-label-m-paragraph-spacing, 6.6px);
|
|
27
|
+
}
|
|
28
|
+
.header[data-size=s] .title{
|
|
29
|
+
height:var(--size-calendar-container-header-lines-height-s, 32px);
|
|
30
|
+
}
|
|
31
|
+
.header[data-size=m]{
|
|
32
|
+
padding-left:var(--space-calendar-container-m, 8px);
|
|
33
|
+
padding-right:var(--space-calendar-container-m, 8px);
|
|
34
|
+
font-family:var(--sans-label-l-font-family, SB Sans Interface);
|
|
35
|
+
font-weight:var(--sans-label-l-font-weight, Semibold);
|
|
36
|
+
line-height:var(--sans-label-l-line-height, 20px);
|
|
37
|
+
font-size:var(--sans-label-l-font-size, 14px);
|
|
38
|
+
letter-spacing:var(--sans-label-l-letter-spacing, 0px);
|
|
39
|
+
paragraph-spacing:var(--sans-label-l-paragraph-spacing, 7.7px);
|
|
40
|
+
}
|
|
41
|
+
.header[data-size=m] .title{
|
|
42
|
+
height:var(--size-calendar-container-header-lines-height-m, 40px);
|
|
43
|
+
}
|
|
44
|
+
.header[data-size=l]{
|
|
45
|
+
padding-left:var(--space-calendar-container-l, 8px);
|
|
46
|
+
padding-right:var(--space-calendar-container-l, 8px);
|
|
47
|
+
font-family:var(--sans-label-l-font-family, SB Sans Interface);
|
|
48
|
+
font-weight:var(--sans-label-l-font-weight, Semibold);
|
|
49
|
+
line-height:var(--sans-label-l-line-height, 20px);
|
|
50
|
+
font-size:var(--sans-label-l-font-size, 14px);
|
|
51
|
+
letter-spacing:var(--sans-label-l-letter-spacing, 0px);
|
|
52
|
+
paragraph-spacing:var(--sans-label-l-paragraph-spacing, 7.7px);
|
|
53
|
+
}
|
|
54
|
+
.header[data-size=l] .title{
|
|
55
|
+
height:var(--size-calendar-container-header-lines-height-l, 48px);
|
|
56
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
const dayInMs = 24 * 60 * 60 * 1000;
|
|
2
|
+
export function getDefaultPresets(t, today) {
|
|
3
|
+
const now = today || new Date();
|
|
4
|
+
const nowInMs = now.getTime();
|
|
5
|
+
const calculatePeriodUpToNow = (presetLimitInMs) => {
|
|
6
|
+
const limit = new Date(now.getTime() + presetLimitInMs);
|
|
7
|
+
return nowInMs > limit.getTime() ? [limit, now] : [now, limit];
|
|
8
|
+
};
|
|
9
|
+
return [
|
|
10
|
+
{
|
|
11
|
+
label: t('defaultPresets.lastWeek'),
|
|
12
|
+
id: 'week',
|
|
13
|
+
range: calculatePeriodUpToNow(dayInMs * -7),
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
label: t('defaultPresets.lastTwoWeeks'),
|
|
17
|
+
id: 'twoWeeks',
|
|
18
|
+
range: calculatePeriodUpToNow(dayInMs * -14),
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
label: t('defaultPresets.lastMonth'),
|
|
22
|
+
id: 'month',
|
|
23
|
+
range: calculatePeriodUpToNow(dayInMs * -30),
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
label: t('defaultPresets.lastQuarter'),
|
|
27
|
+
id: 'quarter',
|
|
28
|
+
range: calculatePeriodUpToNow(dayInMs * -90),
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
label: t('defaultPresets.lastThird'),
|
|
32
|
+
id: 'fourMonths',
|
|
33
|
+
range: calculatePeriodUpToNow(dayInMs * -120),
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
label: t('defaultPresets.lastYear'),
|
|
37
|
+
id: 'year',
|
|
38
|
+
range: calculatePeriodUpToNow(dayInMs * -365),
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
label: t('defaultPresets.lastTwoYears'),
|
|
42
|
+
id: 'twoYears',
|
|
43
|
+
range: calculatePeriodUpToNow(dayInMs * -365 * 2),
|
|
44
|
+
},
|
|
45
|
+
];
|
|
46
|
+
}
|
package/dist/esm/types.d.ts
CHANGED
|
@@ -43,3 +43,25 @@ export type DateAndTime = TimeValue & {
|
|
|
43
43
|
month?: number;
|
|
44
44
|
day?: number;
|
|
45
45
|
};
|
|
46
|
+
export type PresetItem = {
|
|
47
|
+
/** Лейбл пресета */
|
|
48
|
+
label: string;
|
|
49
|
+
/** ID периода */
|
|
50
|
+
id: string;
|
|
51
|
+
/** Период */
|
|
52
|
+
range: Range;
|
|
53
|
+
};
|
|
54
|
+
export type PresetsOptions = {
|
|
55
|
+
/**
|
|
56
|
+
* Включение отображения секции с пресетами
|
|
57
|
+
* @default false
|
|
58
|
+
*/
|
|
59
|
+
enabled?: boolean;
|
|
60
|
+
/**
|
|
61
|
+
* Отображение заголовка у секции с пресетами
|
|
62
|
+
* @default true
|
|
63
|
+
*/
|
|
64
|
+
title?: boolean;
|
|
65
|
+
/** Кастомные пресеты быстрого выбора периода относительно текущего момента */
|
|
66
|
+
items?: PresetItem[];
|
|
67
|
+
};
|
package/package.json
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
"access": "public"
|
|
5
5
|
},
|
|
6
6
|
"title": "Calendar",
|
|
7
|
-
"version": "0.13.
|
|
7
|
+
"version": "0.13.7",
|
|
8
8
|
"sideEffects": [
|
|
9
9
|
"*.css",
|
|
10
10
|
"*.woff",
|
|
@@ -48,5 +48,5 @@
|
|
|
48
48
|
"peerDependencies": {
|
|
49
49
|
"@snack-uikit/locale": "*"
|
|
50
50
|
},
|
|
51
|
-
"gitHead": "
|
|
51
|
+
"gitHead": "6b989b5fe57383ddf2bd6c1186bf4e1652633da7"
|
|
52
52
|
}
|
|
@@ -4,7 +4,7 @@ import { WithSupportProps } from '@snack-uikit/utils';
|
|
|
4
4
|
|
|
5
5
|
import { CALENDAR_MODE } from '../../constants';
|
|
6
6
|
import { CalendarBase } from '../../helperComponents/CalendarBase';
|
|
7
|
-
import { BuildCellPropsFunction, FocusDirection, Range, Size } from '../../types';
|
|
7
|
+
import { BuildCellPropsFunction, FocusDirection, PresetsOptions, Range, Size } from '../../types';
|
|
8
8
|
import { getNormalizedValue } from './utils';
|
|
9
9
|
|
|
10
10
|
type CommonCalendarProps = {
|
|
@@ -49,6 +49,8 @@ type CommonCalendarProps = {
|
|
|
49
49
|
onFocusLeave?(direction: FocusDirection): void;
|
|
50
50
|
/** Ссылка на управление первым элементом навигации */
|
|
51
51
|
navigationStartRef?: RefObject<{ focus(): void }>;
|
|
52
|
+
/** Настройки секции с пресетами быстрого выбора периода. Доступны только при mode === 'range' и отсутствии buildCellProps (временно PDS-3139) */
|
|
53
|
+
presets?: PresetsOptions;
|
|
52
54
|
};
|
|
53
55
|
|
|
54
56
|
type DateCalendarProps = CommonCalendarProps & {
|
|
@@ -113,7 +115,7 @@ export type CalendarProps = WithSupportProps<
|
|
|
113
115
|
>;
|
|
114
116
|
|
|
115
117
|
export function Calendar(props: CalendarProps) {
|
|
116
|
-
const {
|
|
118
|
+
const { onChangeValue, mode, ...rest } = props;
|
|
117
119
|
|
|
118
120
|
const changeValueHandler = useCallback(
|
|
119
121
|
(value: Range) => {
|
|
@@ -136,11 +138,9 @@ export function Calendar(props: CalendarProps) {
|
|
|
136
138
|
<CalendarBase
|
|
137
139
|
{...rest}
|
|
138
140
|
mode={mode}
|
|
139
|
-
className={className}
|
|
140
141
|
value={getNormalizedValue(props.value)}
|
|
141
142
|
defaultValue={getNormalizedValue(props.defaultValue)}
|
|
142
143
|
onChangeValue={changeValueHandler}
|
|
143
|
-
buildCellProps={buildCellProps}
|
|
144
144
|
/>
|
|
145
145
|
);
|
|
146
146
|
}
|
|
@@ -2,19 +2,30 @@ import cn from 'classnames';
|
|
|
2
2
|
import { CSSProperties, RefObject, useCallback, useMemo, useRef, useState } from 'react';
|
|
3
3
|
import { useUncontrolledProp } from 'uncontrollable';
|
|
4
4
|
|
|
5
|
+
import { Divider } from '@snack-uikit/divider';
|
|
5
6
|
import { ListProps } from '@snack-uikit/list';
|
|
6
7
|
import { useLocale } from '@snack-uikit/locale';
|
|
7
8
|
import { extractSupportProps, WithSupportProps } from '@snack-uikit/utils';
|
|
8
9
|
|
|
9
10
|
import { AUTOFOCUS, CALENDAR_MODE, SIZE, VIEW_MODE } from '../../constants';
|
|
10
11
|
import { useDateAndTime } from '../../hooks';
|
|
11
|
-
import {
|
|
12
|
+
import {
|
|
13
|
+
BuildCellPropsFunction,
|
|
14
|
+
CalendarMode,
|
|
15
|
+
FocusDirection,
|
|
16
|
+
PresetsOptions,
|
|
17
|
+
Range,
|
|
18
|
+
Size,
|
|
19
|
+
ViewMode,
|
|
20
|
+
} from '../../types';
|
|
12
21
|
import { getEndOfTheDay, getLocale, getTestIdBuilder, sortDates } from '../../utils';
|
|
13
22
|
import { CalendarBody } from '../CalendarBody';
|
|
14
23
|
import { CalendarContext } from '../CalendarContext';
|
|
15
24
|
import { CalendarNavigation } from '../CalendarNavigation';
|
|
16
25
|
import { ColumnLabels } from '../ColumnLabels';
|
|
17
26
|
import { Footer } from '../Footer';
|
|
27
|
+
import { PeriodPresetsList } from '../PeriodPresetsList';
|
|
28
|
+
import { getDefaultPresets } from '../PeriodPresetsList/utils';
|
|
18
29
|
import { TimePickerBase } from '../TimePickerBase';
|
|
19
30
|
import { useRange, useViewDate } from './hooks';
|
|
20
31
|
import styles from './styles.module.scss';
|
|
@@ -36,6 +47,7 @@ export type CalendarBaseProps = WithSupportProps<{
|
|
|
36
47
|
autofocus?: boolean;
|
|
37
48
|
locale?: Intl.Locale;
|
|
38
49
|
navigationStartRef?: RefObject<{ focus(): void }>;
|
|
50
|
+
presets?: PresetsOptions;
|
|
39
51
|
}>;
|
|
40
52
|
|
|
41
53
|
const DATE_WRAPPER_SIZE_MAP: Record<Size, string> = {
|
|
@@ -76,12 +88,15 @@ export function CalendarBase({
|
|
|
76
88
|
buildCellProps,
|
|
77
89
|
'data-test-id': testId,
|
|
78
90
|
navigationStartRef,
|
|
91
|
+
presets,
|
|
79
92
|
...rest
|
|
80
93
|
}: CalendarBaseProps) {
|
|
94
|
+
const { t } = useLocale('Calendar');
|
|
95
|
+
|
|
81
96
|
const [viewMode, setViewMode] = useState<ViewMode>(CALENDAR_DEFAULT_MODE_MAP[mode]);
|
|
82
97
|
const [viewShift, setViewShift] = useState<number>(0);
|
|
83
98
|
const [value, setValueState] = useUncontrolledProp<Range | undefined>(valueProp, defaultValue, onChangeValue);
|
|
84
|
-
const today = typeof todayProp === 'number' ? new Date(todayProp) : todayProp;
|
|
99
|
+
const today = useMemo(() => (typeof todayProp === 'number' ? new Date(todayProp) : todayProp), [todayProp]);
|
|
85
100
|
const [referenceDate] = useState(value?.[0] || today || new Date());
|
|
86
101
|
const viewDate = useViewDate(referenceDate, viewMode, viewShift);
|
|
87
102
|
const [focus, setFocus] = useState<string | undefined>(autofocus ? AUTOFOCUS : undefined);
|
|
@@ -122,6 +137,23 @@ export function CalendarBase({
|
|
|
122
137
|
|
|
123
138
|
const firstNotDisableCell = useRef<[number, number]>([0, 0]);
|
|
124
139
|
|
|
140
|
+
const presetsItems = useMemo(() => {
|
|
141
|
+
if (presets?.items && presets.items.length > 0) {
|
|
142
|
+
return presets.items;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
return getDefaultPresets(t, today);
|
|
146
|
+
}, [presets?.items, t, today]);
|
|
147
|
+
|
|
148
|
+
const arePeriodPresetsDisplayed = mode === 'range' && presets?.enabled && !buildCellProps; // TODO PDS-3139
|
|
149
|
+
|
|
150
|
+
const onPresetClick = useCallback(
|
|
151
|
+
(selectedPeriod: Range) => {
|
|
152
|
+
setValue(selectedPeriod);
|
|
153
|
+
},
|
|
154
|
+
[setValue],
|
|
155
|
+
);
|
|
156
|
+
|
|
125
157
|
return (
|
|
126
158
|
<div
|
|
127
159
|
className={cn(styles.calendarWrapper, className)}
|
|
@@ -176,6 +208,17 @@ export function CalendarBase({
|
|
|
176
208
|
data-size={size}
|
|
177
209
|
data-show-footer={(mode === CALENDAR_MODE.DateTime && viewMode === 'month') || undefined}
|
|
178
210
|
>
|
|
211
|
+
{arePeriodPresetsDisplayed && (
|
|
212
|
+
<>
|
|
213
|
+
<PeriodPresetsList
|
|
214
|
+
items={presetsItems}
|
|
215
|
+
onChange={onPresetClick}
|
|
216
|
+
showTitle={presets?.title}
|
|
217
|
+
data-test-id={getTestId('presets')}
|
|
218
|
+
/>
|
|
219
|
+
<Divider className={styles.divider} orientation='vertical' />
|
|
220
|
+
</>
|
|
221
|
+
)}
|
|
179
222
|
<div
|
|
180
223
|
{...extractSupportProps(rest)}
|
|
181
224
|
className={cn(styles.calendar, CALENDAR_SIZE_MAP[size])}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import cn from 'classnames';
|
|
2
|
+
import React, { useContext, useMemo } from 'react';
|
|
3
|
+
|
|
4
|
+
import { List, ListProps } from '@snack-uikit/list';
|
|
5
|
+
import { useLocale } from '@snack-uikit/locale';
|
|
6
|
+
import { extractSupportProps, WithSupportProps } from '@snack-uikit/utils';
|
|
7
|
+
|
|
8
|
+
import { PresetItem, Range } from '../../types';
|
|
9
|
+
import { CalendarContext } from '../CalendarContext';
|
|
10
|
+
import styles from './styles.module.scss';
|
|
11
|
+
|
|
12
|
+
export type PresetsListProps = WithSupportProps<{
|
|
13
|
+
/** Действие при выборе пресета */
|
|
14
|
+
onChange(range: Range): void;
|
|
15
|
+
/** Список пресетов */
|
|
16
|
+
items: PresetItem[];
|
|
17
|
+
/**
|
|
18
|
+
* Скрытие заголовка списка
|
|
19
|
+
* @default true
|
|
20
|
+
*/
|
|
21
|
+
showTitle?: boolean;
|
|
22
|
+
/** CSS-класс */
|
|
23
|
+
className?: string;
|
|
24
|
+
}>;
|
|
25
|
+
|
|
26
|
+
export function PeriodPresetsList({ items, onChange, showTitle = true, className, ...rest }: PresetsListProps) {
|
|
27
|
+
const { t } = useLocale('Calendar');
|
|
28
|
+
|
|
29
|
+
const { size, getTestId } = useContext(CalendarContext);
|
|
30
|
+
|
|
31
|
+
const listItems: ListProps['items'] = useMemo(
|
|
32
|
+
() =>
|
|
33
|
+
items.map(item => ({
|
|
34
|
+
id: item.id,
|
|
35
|
+
content: {
|
|
36
|
+
option: item.label,
|
|
37
|
+
},
|
|
38
|
+
onClick() {
|
|
39
|
+
onChange(item.range);
|
|
40
|
+
},
|
|
41
|
+
checked: false,
|
|
42
|
+
})),
|
|
43
|
+
[items, onChange],
|
|
44
|
+
);
|
|
45
|
+
|
|
46
|
+
return (
|
|
47
|
+
<div className={cn(styles.wrapper, className)} {...extractSupportProps(rest)}>
|
|
48
|
+
{showTitle && (
|
|
49
|
+
<div className={styles.header} data-size={size}>
|
|
50
|
+
<span className={styles.title} data-test-id={getTestId('presets-header')}>
|
|
51
|
+
{t('presets')}
|
|
52
|
+
</span>
|
|
53
|
+
</div>
|
|
54
|
+
)}
|
|
55
|
+
<List
|
|
56
|
+
size={size}
|
|
57
|
+
items={listItems}
|
|
58
|
+
scroll
|
|
59
|
+
selection={{ mode: 'single', value: undefined }}
|
|
60
|
+
hasListInFocusChain={false}
|
|
61
|
+
/>
|
|
62
|
+
</div>
|
|
63
|
+
);
|
|
64
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './PeriodPresetsList';
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
@use '@snack-uikit/figma-tokens/build/scss/components/styles-tokens-calendar';
|
|
2
|
+
|
|
3
|
+
$sizes: 's', 'm', 'l';
|
|
4
|
+
|
|
5
|
+
$presets-header-typography: (
|
|
6
|
+
's': styles-tokens-calendar.$sans-label-m,
|
|
7
|
+
'm': styles-tokens-calendar.$sans-label-l,
|
|
8
|
+
'l': styles-tokens-calendar.$sans-label-l
|
|
9
|
+
) ;
|
|
10
|
+
|
|
11
|
+
.wrapper {
|
|
12
|
+
display: flex;
|
|
13
|
+
flex-direction: column;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
.title {
|
|
17
|
+
display: flex;
|
|
18
|
+
align-items: center;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
.header {
|
|
22
|
+
display: flex;
|
|
23
|
+
flex-grow: 0;
|
|
24
|
+
flex-shrink: 0;
|
|
25
|
+
align-items: center;
|
|
26
|
+
|
|
27
|
+
color: styles-tokens-calendar.$sys-neutral-text-main;
|
|
28
|
+
|
|
29
|
+
@each $size in $sizes {
|
|
30
|
+
&[data-size='#{$size}'] {
|
|
31
|
+
@include styles-tokens-calendar.composite-var(styles-tokens-calendar.$calendar, 'time', 'header', $size);
|
|
32
|
+
@include styles-tokens-calendar.composite-var($presets-header-typography, $size);
|
|
33
|
+
|
|
34
|
+
.title {
|
|
35
|
+
@include styles-tokens-calendar.composite-var(styles-tokens-calendar.$calendar, 'time', 'header', 'lines-height', $size);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { useLocale } from '@snack-uikit/locale';
|
|
2
|
+
|
|
3
|
+
import { PresetItem, Range } from '../../types';
|
|
4
|
+
type TFunction = ReturnType<typeof useLocale<'Calendar'>>['t'];
|
|
5
|
+
|
|
6
|
+
const dayInMs = 24 * 60 * 60 * 1000;
|
|
7
|
+
|
|
8
|
+
export function getDefaultPresets(t: TFunction, today?: Date): PresetItem[] {
|
|
9
|
+
const now = today || new Date();
|
|
10
|
+
const nowInMs = now.getTime();
|
|
11
|
+
|
|
12
|
+
const calculatePeriodUpToNow = (presetLimitInMs: number): Range => {
|
|
13
|
+
const limit = new Date(now.getTime() + presetLimitInMs);
|
|
14
|
+
return nowInMs > limit.getTime() ? [limit, now] : [now, limit];
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
return [
|
|
18
|
+
{
|
|
19
|
+
label: t('defaultPresets.lastWeek'),
|
|
20
|
+
id: 'week',
|
|
21
|
+
range: calculatePeriodUpToNow(dayInMs * -7),
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
label: t('defaultPresets.lastTwoWeeks'),
|
|
25
|
+
id: 'twoWeeks',
|
|
26
|
+
range: calculatePeriodUpToNow(dayInMs * -14),
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
label: t('defaultPresets.lastMonth'),
|
|
30
|
+
id: 'month',
|
|
31
|
+
range: calculatePeriodUpToNow(dayInMs * -30),
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
label: t('defaultPresets.lastQuarter'),
|
|
35
|
+
id: 'quarter',
|
|
36
|
+
range: calculatePeriodUpToNow(dayInMs * -90),
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
label: t('defaultPresets.lastThird'),
|
|
40
|
+
id: 'fourMonths',
|
|
41
|
+
range: calculatePeriodUpToNow(dayInMs * -120),
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
label: t('defaultPresets.lastYear'),
|
|
45
|
+
id: 'year',
|
|
46
|
+
range: calculatePeriodUpToNow(dayInMs * -365),
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
label: t('defaultPresets.lastTwoYears'),
|
|
50
|
+
id: 'twoYears',
|
|
51
|
+
range: calculatePeriodUpToNow(dayInMs * -365 * 2),
|
|
52
|
+
},
|
|
53
|
+
];
|
|
54
|
+
}
|
package/src/types.ts
CHANGED
|
@@ -51,3 +51,27 @@ export type DateAndTime = TimeValue & {
|
|
|
51
51
|
month?: number;
|
|
52
52
|
day?: number;
|
|
53
53
|
};
|
|
54
|
+
|
|
55
|
+
export type PresetItem = {
|
|
56
|
+
/** Лейбл пресета */
|
|
57
|
+
label: string;
|
|
58
|
+
/** ID периода */
|
|
59
|
+
id: string;
|
|
60
|
+
/** Период */
|
|
61
|
+
range: Range;
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
export type PresetsOptions = {
|
|
65
|
+
/**
|
|
66
|
+
* Включение отображения секции с пресетами
|
|
67
|
+
* @default false
|
|
68
|
+
*/
|
|
69
|
+
enabled?: boolean;
|
|
70
|
+
/**
|
|
71
|
+
* Отображение заголовка у секции с пресетами
|
|
72
|
+
* @default true
|
|
73
|
+
*/
|
|
74
|
+
title?: boolean;
|
|
75
|
+
/** Кастомные пресеты быстрого выбора периода относительно текущего момента */
|
|
76
|
+
items?: PresetItem[];
|
|
77
|
+
};
|