@cloud-ru/uikit-product-mobile-fields 0.11.24

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (65) hide show
  1. package/CHANGELOG.md +1300 -0
  2. package/LICENSE +201 -0
  3. package/README.md +8 -0
  4. package/package.json +66 -0
  5. package/src/components/AdaptiveField/AdaptiveField.tsx +88 -0
  6. package/src/components/AdaptiveField/index.ts +1 -0
  7. package/src/components/MobileFieldDate/MobileFieldDate.tsx +375 -0
  8. package/src/components/MobileFieldDate/constants.ts +33 -0
  9. package/src/components/MobileFieldDate/index.ts +2 -0
  10. package/src/components/MobileFieldDate/styles.module.scss +75 -0
  11. package/src/components/MobileFieldDate/types.ts +6 -0
  12. package/src/components/MobileFieldDate/utils.ts +49 -0
  13. package/src/components/MobileFieldSelect/MobileFieldSelect.tsx +18 -0
  14. package/src/components/MobileFieldSelect/MobileFieldSelectMultiple.tsx +331 -0
  15. package/src/components/MobileFieldSelect/MobileFieldSelectSingle.tsx +300 -0
  16. package/src/components/MobileFieldSelect/hooks.tsx +195 -0
  17. package/src/components/MobileFieldSelect/index.ts +13 -0
  18. package/src/components/MobileFieldSelect/legacy/components/Items/hooks.tsx +53 -0
  19. package/src/components/MobileFieldSelect/legacy/components/index.ts +1 -0
  20. package/src/components/MobileFieldSelect/legacy/hooks.ts +38 -0
  21. package/src/components/MobileFieldSelect/legacy/index.ts +3 -0
  22. package/src/components/MobileFieldSelect/legacy/utils.ts +176 -0
  23. package/src/components/MobileFieldSelect/styles.module.scss +176 -0
  24. package/src/components/MobileFieldSelect/types.ts +156 -0
  25. package/src/components/MobileFieldSelect/utils/extractFieldDecoratorProps.ts +35 -0
  26. package/src/components/MobileFieldSelect/utils/extractListProps.ts +30 -0
  27. package/src/components/MobileFieldSelect/utils/getArrowIcon.ts +15 -0
  28. package/src/components/MobileFieldSelect/utils/index.ts +6 -0
  29. package/src/components/MobileFieldSelect/utils/options.tsx +88 -0
  30. package/src/components/MobileFieldSelect/utils/typeGuards.ts +38 -0
  31. package/src/components/MobileFieldSelect/utils/updateItems.ts +121 -0
  32. package/src/components/index.ts +3 -0
  33. package/src/constants/allFields.ts +11 -0
  34. package/src/constants/dateFields.ts +127 -0
  35. package/src/constants/index.ts +2 -0
  36. package/src/helperComponents/ButtonCopyValue/ButtonCopyValue.tsx +79 -0
  37. package/src/helperComponents/ButtonCopyValue/helpers.tsx +19 -0
  38. package/src/helperComponents/ButtonCopyValue/index.ts +1 -0
  39. package/src/helperComponents/ButtonCopyValue/styles.module.scss +5 -0
  40. package/src/helperComponents/FieldContainerPrivate/FieldContainerPrivate.tsx +79 -0
  41. package/src/helperComponents/FieldContainerPrivate/index.ts +1 -0
  42. package/src/helperComponents/FieldContainerPrivate/styles.module.scss +131 -0
  43. package/src/helperComponents/ItemContent/ItemContent.tsx +37 -0
  44. package/src/helperComponents/ItemContent/index.ts +1 -0
  45. package/src/helperComponents/ItemContent/styles.module.scss +80 -0
  46. package/src/helperComponents/index.ts +3 -0
  47. package/src/hooks/dateHandlers/index.ts +3 -0
  48. package/src/hooks/dateHandlers/useDateField.ts +275 -0
  49. package/src/hooks/dateHandlers/useDateFieldHelpersForMode.ts +145 -0
  50. package/src/hooks/dateHandlers/useFocusHandlers.ts +46 -0
  51. package/src/hooks/dateHandlers/useHandlers.ts +15 -0
  52. package/src/hooks/index.ts +5 -0
  53. package/src/hooks/styles.module.scss +17 -0
  54. package/src/hooks/useCopyButton.tsx +47 -0
  55. package/src/hooks/usePostfix.tsx +21 -0
  56. package/src/hooks/usePrefix.tsx +21 -0
  57. package/src/hooks/useValueControl.ts +15 -0
  58. package/src/index.ts +3 -0
  59. package/src/styles.module.scss +55 -0
  60. package/src/types/allFields.ts +9 -0
  61. package/src/types/dateFields.ts +14 -0
  62. package/src/types/index.ts +2 -0
  63. package/src/utils/adaptiveField.tsx +19 -0
  64. package/src/utils/dateFields.ts +75 -0
  65. package/src/utils/getValidationState.ts +6 -0
@@ -0,0 +1,156 @@
1
+ import { ReactElement, ReactNode } from 'react';
2
+
3
+ import { FieldDecoratorProps } from '@snack-uikit/fields';
4
+ import { InputPrivateProps } from '@snack-uikit/input-private';
5
+ import {
6
+ AccordionItemProps,
7
+ BaseItemProps,
8
+ DroplistProps,
9
+ GroupItemProps,
10
+ ItemContentProps,
11
+ NextListItemProps,
12
+ SelectionMultipleState,
13
+ SelectionSingleState,
14
+ } from '@snack-uikit/list';
15
+ import { TagProps } from '@snack-uikit/tag';
16
+ import { WithSupportProps } from '@snack-uikit/utils';
17
+
18
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
19
+ export type AnyType = any;
20
+
21
+ export type OptionProps =
22
+ // eslint-disable-next-line no-use-before-define
23
+ | BaseOptionProps
24
+ // eslint-disable-next-line no-use-before-define
25
+ | AccordionOptionProps
26
+ // eslint-disable-next-line no-use-before-define
27
+ | GroupOptionProps
28
+ // eslint-disable-next-line no-use-before-define
29
+ | NestListOptionProps;
30
+
31
+ // eslint-disable-next-line no-use-before-define
32
+ export type OptionWithoutGroup = BaseOptionProps | AccordionOptionProps | NestListOptionProps;
33
+
34
+ export type BaseOptionProps = Pick<BaseItemProps, 'beforeContent' | 'afterContent' | 'disabled'> &
35
+ Pick<ItemContentProps, 'option' | 'caption' | 'description'> & { value: string | number } & Pick<
36
+ TagProps,
37
+ 'appearance'
38
+ >;
39
+
40
+ export type AccordionOptionProps = Pick<AccordionItemProps, 'type'> & BaseOptionProps & { options: OptionProps[] };
41
+
42
+ export type GroupOptionProps = Omit<GroupItemProps, 'items' | 'id'> & {
43
+ options: OptionProps[];
44
+ };
45
+ export type NestListOptionProps = Pick<NextListItemProps, 'type' | 'onSublistOpenChanged' | 'id'> &
46
+ BaseOptionProps & { options: OptionProps[] };
47
+
48
+ export type InputProps = Pick<
49
+ InputPrivateProps,
50
+ 'id' | 'name' | 'placeholder' | 'disabled' | 'readonly' | 'onFocus' | 'onBlur' | 'onKeyDown' | 'autoFocus'
51
+ >;
52
+
53
+ export type WrapperProps = Pick<
54
+ FieldDecoratorProps,
55
+ | 'className'
56
+ | 'label'
57
+ | 'labelTooltip'
58
+ | 'required'
59
+ | 'caption'
60
+ | 'hint'
61
+ | 'showHintIcon'
62
+ | 'size'
63
+ | 'validationState'
64
+ | 'labelTooltipPlacement'
65
+ | 'error'
66
+ >;
67
+
68
+ export type ItemWithId = (BaseItemProps | AccordionItemProps | NextListItemProps) & {
69
+ placeholder?: boolean;
70
+ appearance?: TagProps['appearance'];
71
+ };
72
+
73
+ export type SelectedOptionFormatter = (item?: ItemWithId) => string;
74
+
75
+ export type SearchState = {
76
+ value?: string;
77
+ defaultValue?: string;
78
+ onChange?(value: string): void;
79
+ };
80
+
81
+ export type FieldSelectPrivateProps = InputProps &
82
+ WrapperProps & {
83
+ options: OptionProps[];
84
+ loading?: boolean;
85
+
86
+ /** Произвольный префикс для поля */
87
+ prefix?: ReactNode;
88
+ /** Произвольный постфикс для поля */
89
+ postfix?: ReactNode;
90
+ };
91
+
92
+ type FiledSelectCommonProps = WithSupportProps<{
93
+ options: OptionProps[];
94
+
95
+ pinTop?: OptionProps[];
96
+ pinBottom?: OptionProps[];
97
+
98
+ searchable?: boolean;
99
+ /** Отображение кнопки Копировать для поля (актуально только для `readonly = true`) */
100
+ showCopyButton?: boolean;
101
+ /**
102
+ * Отображение кнопки очистки поля
103
+ * @default true
104
+ */
105
+ showClearButton?: boolean;
106
+ /**
107
+ * Является ли поле доступным только для чтения
108
+ * @default false
109
+ */
110
+ readonly?: boolean;
111
+
112
+ /** Иконка-префикс для поля */
113
+ prefixIcon?: ReactElement;
114
+
115
+ footer?: DroplistProps['footer'];
116
+
117
+ /**
118
+ * Включить виртуализацию на компоненты списка. Рекомендуется если у вас от 1к элементов списка
119
+ */
120
+ virtualized?: boolean;
121
+
122
+ widthStrategy?: DroplistProps['widthStrategy'];
123
+
124
+ search?: SearchState;
125
+
126
+ /**
127
+ * Включить нечеткий поиск
128
+ * @default true
129
+ */
130
+ enableFuzzySearch?: boolean;
131
+
132
+ autocomplete?: boolean;
133
+
134
+ addOptionByEnter?: boolean;
135
+
136
+ open?: boolean;
137
+
138
+ onOpenChange?(open: boolean): void;
139
+
140
+ selectedOptionFormatter?: SelectedOptionFormatter;
141
+ }> &
142
+ Pick<DroplistProps, 'dataError' | 'noDataState' | 'noResultsState' | 'errorDataState' | 'dataFiltered'>;
143
+
144
+ export type MobileFieldSelectSingleProps = FieldSelectPrivateProps &
145
+ Omit<SelectionSingleState, 'mode'> &
146
+ WrapperProps &
147
+ FiledSelectCommonProps;
148
+
149
+ export type MobileFieldSelectMultipleProps = FieldSelectPrivateProps & {
150
+ removeByBackspace?: boolean;
151
+ } & Omit<SelectionMultipleState, 'mode'> &
152
+ Omit<FiledSelectCommonProps, 'showCopyButton'>;
153
+
154
+ export type MobileFieldSelectProps =
155
+ | (MobileFieldSelectSingleProps & { selection?: 'single' })
156
+ | (MobileFieldSelectMultipleProps & { selection: 'multiple' });
@@ -0,0 +1,35 @@
1
+ import { FieldDecoratorProps } from '@snack-uikit/fields';
2
+
3
+ export function extractFieldDecoratorProps({
4
+ error,
5
+ required,
6
+ readonly,
7
+ label,
8
+ labelTooltip,
9
+ labelTooltipPlacement,
10
+ labelFor,
11
+ caption,
12
+ hint,
13
+ disabled,
14
+ showHintIcon,
15
+ size,
16
+ validationState,
17
+ className,
18
+ }: Partial<FieldDecoratorProps>): Partial<FieldDecoratorProps> {
19
+ return {
20
+ error,
21
+ required,
22
+ readonly,
23
+ label,
24
+ labelTooltip,
25
+ labelTooltipPlacement,
26
+ labelFor,
27
+ caption,
28
+ hint,
29
+ disabled,
30
+ showHintIcon,
31
+ size,
32
+ validationState,
33
+ className,
34
+ };
35
+ }
@@ -0,0 +1,30 @@
1
+ import { DroplistProps } from '@snack-uikit/list';
2
+
3
+ import { MobileFieldSelectProps } from '../types';
4
+
5
+ export function extractListProps({
6
+ dataError,
7
+ noDataState,
8
+ noResultsState,
9
+ errorDataState,
10
+ dataFiltered,
11
+ loading,
12
+ footer,
13
+ widthStrategy,
14
+ }: Partial<MobileFieldSelectProps>): Partial<DroplistProps> {
15
+ return {
16
+ dataError,
17
+ noDataState,
18
+ noResultsState,
19
+ errorDataState,
20
+ dataFiltered,
21
+ loading,
22
+ footer,
23
+ widthStrategy,
24
+ trigger: 'clickAndFocusVisible',
25
+ placement: 'bottom',
26
+ 'data-test-id': 'field-select__list',
27
+ scroll: true,
28
+ marker: true,
29
+ };
30
+ }
@@ -0,0 +1,15 @@
1
+ import { JSXElementConstructor } from 'react';
2
+
3
+ import { ChevronDownSVG, ChevronUpSVG } from '@cloud-ru/uikit-product-icons';
4
+ import { ICON_SIZE, SIZE, Size } from '@snack-uikit/input-private';
5
+ import { ValueOf } from '@snack-uikit/utils';
6
+
7
+ export function getArrowIcon({ size, open }: { size: Size; open: boolean }): {
8
+ ArrowIcon: JSXElementConstructor<{ className?: string; size: ValueOf<typeof ICON_SIZE> }>;
9
+ arrowIconSize: ValueOf<typeof ICON_SIZE>;
10
+ } {
11
+ return {
12
+ ArrowIcon: open ? ChevronUpSVG : ChevronDownSVG,
13
+ arrowIconSize: size === SIZE.S ? ICON_SIZE.Xs : ICON_SIZE.S,
14
+ };
15
+ }
@@ -0,0 +1,6 @@
1
+ export * from './typeGuards';
2
+ export * from './extractFieldDecoratorProps';
3
+ export * from './extractListProps';
4
+ export * from './options';
5
+ export * from './updateItems';
6
+ export * from './getArrowIcon';
@@ -0,0 +1,88 @@
1
+ import { ItemProps, SelectionSingleValueType } from '@snack-uikit/list';
2
+ import { TagProps } from '@snack-uikit/tag';
3
+
4
+ import { flattenItems } from '../legacy';
5
+ import { BaseOptionProps, ItemWithId, OptionProps } from '../types';
6
+ import { isAccordionOptionProps, isGroupOptionProps, isNextListOptionProps } from './typeGuards';
7
+
8
+ export function transformOptionsToItems(
9
+ options: OptionProps[],
10
+ ): (ItemProps & { appearance?: TagProps['appearance'] })[] {
11
+ return options.map(option => {
12
+ if (isAccordionOptionProps(option) || isNextListOptionProps(option)) {
13
+ const { description, option: contentOption, caption, options, value, ...rest } = option;
14
+
15
+ return {
16
+ 'data-test-id': 'field-select__list-option-' + option.value,
17
+ ...rest,
18
+ id: value,
19
+ content: { option: contentOption, caption, description },
20
+ items: transformOptionsToItems(options),
21
+ };
22
+ }
23
+
24
+ if (isGroupOptionProps(option)) {
25
+ const { options, ...rest } = option;
26
+
27
+ return {
28
+ ...rest,
29
+ items: transformOptionsToItems(options),
30
+ };
31
+ }
32
+
33
+ const { description, option: contentOption, caption, value, appearance, ...rest } = option as BaseOptionProps;
34
+
35
+ return {
36
+ 'data-test-id': 'field-select__list-option-' + option.value,
37
+ ...rest,
38
+ id: value,
39
+ appearance: appearance,
40
+ content: { description, option: contentOption, caption: caption },
41
+ };
42
+ });
43
+ }
44
+
45
+ export function findSelectedOption(
46
+ items: ItemProps[],
47
+ value: SelectionSingleValueType,
48
+ ): [ItemWithId | undefined, ItemWithId | undefined] {
49
+ const flatten: ItemWithId[] = flattenItems(items);
50
+
51
+ if (!value) {
52
+ return [undefined, undefined];
53
+ }
54
+
55
+ const foundItem = flatten.find(item => String(item.id) === String(value));
56
+ const placeholderItem = { id: value, content: { option: String(value) } };
57
+
58
+ return [foundItem, !foundItem ? placeholderItem : undefined];
59
+ }
60
+
61
+ export function findSelectedOptions(
62
+ items: ItemProps[],
63
+ value: SelectionSingleValueType[] | undefined,
64
+ ): [ItemWithId[] | undefined, ItemWithId[] | undefined] {
65
+ const flatten: ItemWithId[] | undefined = flattenItems(items);
66
+
67
+ if (!value || !value?.length) {
68
+ return [undefined, undefined];
69
+ }
70
+
71
+ let foundItems: ItemWithId[] | undefined;
72
+ let placeholderItems: ItemWithId[] | undefined;
73
+
74
+ value.forEach(value => {
75
+ if (flatten) {
76
+ const [found, placeholder] = findSelectedOption(flatten, value);
77
+ if (found || foundItems) {
78
+ foundItems = (foundItems ?? []).concat(found ?? []);
79
+ }
80
+
81
+ if (placeholder || placeholderItems) {
82
+ placeholderItems = (placeholderItems ?? []).concat(placeholder ?? []);
83
+ }
84
+ }
85
+ });
86
+
87
+ return [foundItems, placeholderItems];
88
+ }
@@ -0,0 +1,38 @@
1
+ import {
2
+ AccordionOptionProps,
3
+ BaseOptionProps,
4
+ GroupOptionProps,
5
+ MobileFieldSelectMultipleProps,
6
+ MobileFieldSelectSingleProps,
7
+ NestListOptionProps,
8
+ } from '../types';
9
+
10
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
11
+ export function isBaseOptionProps(option: any): option is BaseOptionProps {
12
+ return !('options' in option);
13
+ }
14
+
15
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
16
+ export function isAccordionOptionProps(option: any): option is AccordionOptionProps {
17
+ return 'options' in option && option['type'] === 'collapse';
18
+ }
19
+
20
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
21
+ export function isNextListOptionProps(option: any): option is NestListOptionProps {
22
+ return 'options' in option && option['type'] === 'next-list';
23
+ }
24
+
25
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
26
+ export function isGroupOptionProps(option: any): option is GroupOptionProps {
27
+ return 'options' in option && option['type'] === 'group';
28
+ }
29
+
30
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
31
+ export function isFieldSelectMultipleProps(props: any): props is MobileFieldSelectMultipleProps {
32
+ return 'selection' in props && props['selection'] === 'multiple';
33
+ }
34
+
35
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
36
+ export function isFieldSelectSingleProps(props: any): props is MobileFieldSelectSingleProps {
37
+ return ('selection' in props && props['selection'] === 'single') || props['selection'] === undefined;
38
+ }
@@ -0,0 +1,121 @@
1
+ import { ItemId, ItemProps, SelectionSingleValueType } from '@snack-uikit/list';
2
+
3
+ import { flattenItems } from '../legacy';
4
+ import { ItemWithId, OptionProps } from '../types';
5
+ import { transformOptionsToItems } from './options';
6
+
7
+ export function createPlaceholderItem(value: SelectionSingleValueType) {
8
+ return { id: value, content: { option: String(value) }, placeholder: true };
9
+ }
10
+
11
+ export function updateItems({
12
+ options,
13
+ value,
14
+ selectedItem,
15
+ }: {
16
+ options: OptionProps[];
17
+ value?: ItemId;
18
+ selectedItem?: ItemWithId;
19
+ currentItems?: ItemProps[];
20
+ }): {
21
+ selectedItem?: ItemWithId;
22
+ items: ItemProps[];
23
+ } {
24
+ const originalItems = transformOptionsToItems(options);
25
+
26
+ if (value === undefined) {
27
+ return {
28
+ selectedItem: undefined,
29
+ items: originalItems,
30
+ };
31
+ }
32
+
33
+ let newItems: ItemProps[] = originalItems;
34
+ let newSelectedItem = selectedItem;
35
+
36
+ const foundItem = flattenItems(originalItems).find(item => item.id === value);
37
+
38
+ if (!foundItem) {
39
+ if (selectedItem && selectedItem?.id === value) {
40
+ newItems = [selectedItem, ...newItems];
41
+ } else {
42
+ newSelectedItem = createPlaceholderItem(value);
43
+ newItems = [newSelectedItem, ...newItems];
44
+ }
45
+ } else {
46
+ newSelectedItem = foundItem;
47
+ }
48
+
49
+ return {
50
+ selectedItem: newSelectedItem,
51
+ items: newItems,
52
+ };
53
+ }
54
+
55
+ export function updateMultipleItems({
56
+ options,
57
+ value,
58
+ selectedItems,
59
+ }: {
60
+ options: OptionProps[];
61
+ value?: SelectionSingleValueType[];
62
+ selectedItems?: ItemWithId[];
63
+ currentItems?: ItemProps[];
64
+ }): {
65
+ selectedItems?: ItemWithId[];
66
+ items: ItemProps[];
67
+ } {
68
+ const originalItems = transformOptionsToItems(options);
69
+
70
+ if (!value || !value.length) {
71
+ return {
72
+ selectedItems: undefined,
73
+ items: originalItems,
74
+ };
75
+ }
76
+
77
+ const foundedValue: (number | string)[] = [];
78
+
79
+ let newItems: ItemProps[] = originalItems;
80
+ let newSelectedItems = selectedItems;
81
+
82
+ const flattenOriginalItems = flattenItems(originalItems);
83
+
84
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
85
+ const foundItems: ItemWithId[] = flattenOriginalItems.filter((item: any) => {
86
+ if (value.includes(item.id) && !foundedValue.includes(item.id)) {
87
+ foundedValue.push(item.id);
88
+ return true;
89
+ }
90
+ });
91
+ const nonFoundValues: SelectionSingleValueType[] = value.filter(
92
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
93
+ value => !flattenOriginalItems.find((item: any) => item.id === value),
94
+ );
95
+
96
+ if (nonFoundValues.length) {
97
+ const nonFoundItems = nonFoundValues.map(
98
+ value => selectedItems?.find(selectedItem => selectedItem.id === value) || createPlaceholderItem(value),
99
+ );
100
+
101
+ newSelectedItems = [...foundItems, ...nonFoundItems];
102
+ newItems = [...nonFoundItems, ...newItems];
103
+ } else {
104
+ newSelectedItems = foundItems;
105
+ }
106
+
107
+ return {
108
+ selectedItems: newSelectedItems.sort((a, b) => {
109
+ if (b.disabled && !a.disabled) {
110
+ return 1;
111
+ }
112
+
113
+ if (a.disabled && !b.disabled) {
114
+ return -1;
115
+ }
116
+
117
+ return 0;
118
+ }),
119
+ items: newItems,
120
+ };
121
+ }
@@ -0,0 +1,3 @@
1
+ export * from './AdaptiveField';
2
+ export * from './MobileFieldSelect';
3
+ export * from './MobileFieldDate';
@@ -0,0 +1,11 @@
1
+ export const VALIDATION_STATE = {
2
+ Default: 'default',
3
+ Error: 'error',
4
+ Warning: 'warning',
5
+ Success: 'success',
6
+ } as const;
7
+
8
+ export const CONTAINER_VARIANT = {
9
+ SingleLine: 'single-line-container',
10
+ MultiLine: 'multi-line-container',
11
+ } as const;
@@ -0,0 +1,127 @@
1
+ import { Mode, NoSecondsMode, Slot, TimeMode } from '../types';
2
+
3
+ export enum SlotKey {
4
+ Day = 'D',
5
+ Month = 'M',
6
+ Year = 'Y',
7
+ Hours = 'h',
8
+ Minutes = 'm',
9
+ Seconds = 's',
10
+ }
11
+
12
+ export const MODES = {
13
+ Date: 'date',
14
+ DateTime: 'date-time',
15
+ } as const;
16
+
17
+ export const TIME_MODES = {
18
+ FullTime: 'full-time',
19
+ NoSeconds: 'no-seconds',
20
+ } as const;
21
+
22
+ export const NO_SECONDS_MODE = 'date-time-no-sec';
23
+
24
+ export const MASK: Record<Mode | TimeMode | NoSecondsMode, Record<string, string>> = {
25
+ [MODES.Date]: {
26
+ 'ru-RU': 'ДД.ММ.ГГГГ',
27
+ 'en-US': 'DD.MM.YYYY',
28
+ },
29
+ [MODES.DateTime]: {
30
+ 'ru-RU': 'ДД.ММ.ГГГГ, чч:мм:сс',
31
+ 'en-US': 'DD.MM.YYYY, hh:mm:ss',
32
+ },
33
+ [NO_SECONDS_MODE]: {
34
+ 'ru-RU': 'ДД.ММ.ГГГГ, чч:мм',
35
+ 'en-US': 'DD.MM.YYYY, hh:mm',
36
+ },
37
+ [TIME_MODES.FullTime]: {
38
+ 'ru-RU': 'чч:мм:сс',
39
+ 'en-US': 'hh:mm:ss',
40
+ },
41
+ [TIME_MODES.NoSeconds]: {
42
+ 'ru-RU': 'чч:мм',
43
+ 'en-US': 'hh:mm',
44
+ },
45
+ };
46
+
47
+ export const DEFAULT_LOCALE = new Intl.Locale('ru-RU');
48
+
49
+ const DATE_SLOTS = {
50
+ [SlotKey.Day]: { start: 0, end: 2, max: 31, min: 1 },
51
+ [SlotKey.Month]: { start: 3, end: 5, max: 12, min: 1 },
52
+ [SlotKey.Year]: { start: 6, end: 10, max: 2100, min: 1900 },
53
+ };
54
+
55
+ const TIME_SLOTS = (shift: number, showSeconds: boolean) => ({
56
+ [SlotKey.Hours]: { start: shift, end: shift + 2, max: 23, min: 0 },
57
+ [SlotKey.Minutes]: { start: shift + 3, end: shift + 5, max: 59, min: 0 },
58
+ ...(showSeconds ? { [SlotKey.Seconds]: { start: shift + 6, end: shift + 8, max: 59, min: 0 } } : {}),
59
+ });
60
+
61
+ export const SLOTS: Record<Mode | TimeMode | NoSecondsMode, Record<SlotKey | string, Slot>> = {
62
+ [MODES.Date]: DATE_SLOTS,
63
+ [MODES.DateTime]: { ...DATE_SLOTS, ...TIME_SLOTS(12, true) },
64
+ [NO_SECONDS_MODE]: { ...DATE_SLOTS, ...TIME_SLOTS(12, false) },
65
+ [TIME_MODES.FullTime]: TIME_SLOTS(0, true),
66
+ [TIME_MODES.NoSeconds]: TIME_SLOTS(0, false),
67
+ };
68
+
69
+ export type FocusSlot = SlotKey | 'auto';
70
+
71
+ export const SLOT_ORDER: Record<Mode | TimeMode | NoSecondsMode, SlotKey[]> = {
72
+ [MODES.Date]: [SlotKey.Day, SlotKey.Month, SlotKey.Year],
73
+ [MODES.DateTime]: [SlotKey.Day, SlotKey.Month, SlotKey.Year, SlotKey.Hours, SlotKey.Minutes, SlotKey.Seconds],
74
+ [NO_SECONDS_MODE]: [SlotKey.Day, SlotKey.Month, SlotKey.Year, SlotKey.Hours, SlotKey.Minutes],
75
+ [TIME_MODES.FullTime]: [SlotKey.Hours, SlotKey.Minutes, SlotKey.Seconds],
76
+ [TIME_MODES.NoSeconds]: [SlotKey.Hours, SlotKey.Minutes],
77
+ };
78
+
79
+ const RU_DATE_SLOTS_PLACEHOLDER = {
80
+ [SlotKey.Day]: 'ДД',
81
+ [SlotKey.Month]: 'ММ',
82
+ [SlotKey.Year]: 'ГГГГ',
83
+ };
84
+
85
+ const RU_TIME_SLOTS_PLACEHOLDER = {
86
+ [SlotKey.Hours]: 'чч',
87
+ [SlotKey.Minutes]: 'мм',
88
+ [SlotKey.Seconds]: 'сс',
89
+ };
90
+
91
+ const EN_DATE_SLOTS_PLACEHOLDER = {
92
+ [SlotKey.Day]: 'DD',
93
+ [SlotKey.Month]: 'MM',
94
+ [SlotKey.Year]: 'YYYY',
95
+ };
96
+
97
+ const EN_TIME_SLOTS_PLACEHOLDER = {
98
+ [SlotKey.Hours]: 'hh',
99
+ [SlotKey.Minutes]: 'mm',
100
+ [SlotKey.Seconds]: 'ss',
101
+ };
102
+
103
+ export const SLOTS_PLACEHOLDER: Record<
104
+ Mode | TimeMode | NoSecondsMode,
105
+ Record<string, Partial<Record<SlotKey, string>>>
106
+ > = {
107
+ [MODES.Date]: {
108
+ 'ru-RU': RU_DATE_SLOTS_PLACEHOLDER,
109
+ 'en-US': EN_DATE_SLOTS_PLACEHOLDER,
110
+ },
111
+ [MODES.DateTime]: {
112
+ 'ru-RU': { ...RU_DATE_SLOTS_PLACEHOLDER, ...RU_TIME_SLOTS_PLACEHOLDER },
113
+ 'en-US': { ...EN_DATE_SLOTS_PLACEHOLDER, ...EN_TIME_SLOTS_PLACEHOLDER },
114
+ },
115
+ [NO_SECONDS_MODE]: {
116
+ 'ru-RU': { ...RU_DATE_SLOTS_PLACEHOLDER, ...RU_TIME_SLOTS_PLACEHOLDER, [SlotKey.Seconds]: undefined },
117
+ 'en-US': { ...EN_DATE_SLOTS_PLACEHOLDER, ...EN_TIME_SLOTS_PLACEHOLDER, [SlotKey.Seconds]: undefined },
118
+ },
119
+ [TIME_MODES.FullTime]: {
120
+ 'ru-RU': RU_TIME_SLOTS_PLACEHOLDER,
121
+ 'en-US': EN_TIME_SLOTS_PLACEHOLDER,
122
+ },
123
+ [TIME_MODES.NoSeconds]: {
124
+ 'ru-RU': { ...RU_TIME_SLOTS_PLACEHOLDER, [SlotKey.Seconds]: undefined },
125
+ 'en-US': { ...EN_TIME_SLOTS_PLACEHOLDER, [SlotKey.Seconds]: undefined },
126
+ },
127
+ };
@@ -0,0 +1,2 @@
1
+ export * from './allFields';
2
+ export * from './dateFields';