@gravity-ui/date-components 1.0.0

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 (217) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +15 -0
  3. package/dist/cjs/components/Calendar/Calendar.css +220 -0
  4. package/dist/cjs/components/Calendar/Calendar.d.ts +13 -0
  5. package/dist/cjs/components/Calendar/Calendar.js +15 -0
  6. package/dist/cjs/components/Calendar/CalendarBase.d.ts +18 -0
  7. package/dist/cjs/components/Calendar/CalendarBase.js +89 -0
  8. package/dist/cjs/components/Calendar/RangeCalendar.d.ts +13 -0
  9. package/dist/cjs/components/Calendar/RangeCalendar.js +22 -0
  10. package/dist/cjs/components/Calendar/hooks/types.d.ts +124 -0
  11. package/dist/cjs/components/Calendar/hooks/types.js +2 -0
  12. package/dist/cjs/components/Calendar/hooks/useCalendarCellProps.d.ts +19 -0
  13. package/dist/cjs/components/Calendar/hooks/useCalendarCellProps.js +98 -0
  14. package/dist/cjs/components/Calendar/hooks/useCalendarGridProps.d.ts +5 -0
  15. package/dist/cjs/components/Calendar/hooks/useCalendarGridProps.js +57 -0
  16. package/dist/cjs/components/Calendar/hooks/useCalendarProps.d.ts +10 -0
  17. package/dist/cjs/components/Calendar/hooks/useCalendarProps.js +110 -0
  18. package/dist/cjs/components/Calendar/hooks/useCalendarState.d.ts +7 -0
  19. package/dist/cjs/components/Calendar/hooks/useCalendarState.js +178 -0
  20. package/dist/cjs/components/Calendar/hooks/useRangeCalendarState.d.ts +7 -0
  21. package/dist/cjs/components/Calendar/hooks/useRangeCalendarState.js +82 -0
  22. package/dist/cjs/components/Calendar/i18n/en.json +4 -0
  23. package/dist/cjs/components/Calendar/i18n/index.d.ts +1 -0
  24. package/dist/cjs/components/Calendar/i18n/index.js +11 -0
  25. package/dist/cjs/components/Calendar/i18n/ru.json +4 -0
  26. package/dist/cjs/components/Calendar/index.d.ts +8 -0
  27. package/dist/cjs/components/Calendar/index.js +23 -0
  28. package/dist/cjs/components/Calendar/utils.d.ts +4 -0
  29. package/dist/cjs/components/Calendar/utils.js +40 -0
  30. package/dist/cjs/components/DateField/DateField.css +4 -0
  31. package/dist/cjs/components/DateField/DateField.d.ts +6 -0
  32. package/dist/cjs/components/DateField/DateField.js +38 -0
  33. package/dist/cjs/components/DateField/hooks/useDateFieldProps.d.ts +6 -0
  34. package/dist/cjs/components/DateField/hooks/useDateFieldProps.js +203 -0
  35. package/dist/cjs/components/DateField/hooks/useDateFieldState.d.ts +67 -0
  36. package/dist/cjs/components/DateField/hooks/useDateFieldState.js +628 -0
  37. package/dist/cjs/components/DateField/i18n/en.json +10 -0
  38. package/dist/cjs/components/DateField/i18n/index.d.ts +1 -0
  39. package/dist/cjs/components/DateField/i18n/index.js +11 -0
  40. package/dist/cjs/components/DateField/i18n/ru.json +10 -0
  41. package/dist/cjs/components/DateField/index.d.ts +3 -0
  42. package/dist/cjs/components/DateField/index.js +19 -0
  43. package/dist/cjs/components/DateField/types.d.ts +67 -0
  44. package/dist/cjs/components/DateField/types.js +2 -0
  45. package/dist/cjs/components/DateField/utils.d.ts +14 -0
  46. package/dist/cjs/components/DateField/utils.js +276 -0
  47. package/dist/cjs/components/DatePicker/DatePicker.css +19 -0
  48. package/dist/cjs/components/DatePicker/DatePicker.d.ts +5 -0
  49. package/dist/cjs/components/DatePicker/DatePicker.js +66 -0
  50. package/dist/cjs/components/DatePicker/DesktopCalendar.d.ts +15 -0
  51. package/dist/cjs/components/DatePicker/DesktopCalendar.js +56 -0
  52. package/dist/cjs/components/DatePicker/MobileCalendar.css +49 -0
  53. package/dist/cjs/components/DatePicker/MobileCalendar.d.ts +14 -0
  54. package/dist/cjs/components/DatePicker/MobileCalendar.js +78 -0
  55. package/dist/cjs/components/DatePicker/hooks/useDatePickerState.d.ts +45 -0
  56. package/dist/cjs/components/DatePicker/hooks/useDatePickerState.js +114 -0
  57. package/dist/cjs/components/DatePicker/i18n/en.json +3 -0
  58. package/dist/cjs/components/DatePicker/i18n/index.d.ts +1 -0
  59. package/dist/cjs/components/DatePicker/i18n/index.js +11 -0
  60. package/dist/cjs/components/DatePicker/i18n/ru.json +3 -0
  61. package/dist/cjs/components/DatePicker/index.d.ts +2 -0
  62. package/dist/cjs/components/DatePicker/index.js +18 -0
  63. package/dist/cjs/components/DatePicker/utils.d.ts +1 -0
  64. package/dist/cjs/components/DatePicker/utils.js +5 -0
  65. package/dist/cjs/components/RelativeDateField/RelativeDateField.css +16 -0
  66. package/dist/cjs/components/RelativeDateField/RelativeDateField.d.ts +11 -0
  67. package/dist/cjs/components/RelativeDateField/RelativeDateField.js +37 -0
  68. package/dist/cjs/components/RelativeDateField/hooks/useRelativeDateFieldProps.d.ts +12 -0
  69. package/dist/cjs/components/RelativeDateField/hooks/useRelativeDateFieldProps.js +66 -0
  70. package/dist/cjs/components/RelativeDateField/hooks/useRelativeDateFieldState.d.ts +30 -0
  71. package/dist/cjs/components/RelativeDateField/hooks/useRelativeDateFieldState.js +66 -0
  72. package/dist/cjs/components/RelativeDateField/index.d.ts +3 -0
  73. package/dist/cjs/components/RelativeDateField/index.js +19 -0
  74. package/dist/cjs/components/RelativeDatePicker/RelativeDatePicker.css +16 -0
  75. package/dist/cjs/components/RelativeDatePicker/RelativeDatePicker.d.ts +6 -0
  76. package/dist/cjs/components/RelativeDatePicker/RelativeDatePicker.js +32 -0
  77. package/dist/cjs/components/RelativeDatePicker/hooks/useRelativeDatePickerProps.d.ts +17 -0
  78. package/dist/cjs/components/RelativeDatePicker/hooks/useRelativeDatePickerProps.js +175 -0
  79. package/dist/cjs/components/RelativeDatePicker/hooks/useRelativeDatePickerState.d.ts +36 -0
  80. package/dist/cjs/components/RelativeDatePicker/hooks/useRelativeDatePickerState.js +109 -0
  81. package/dist/cjs/components/RelativeDatePicker/i18n/en.json +4 -0
  82. package/dist/cjs/components/RelativeDatePicker/i18n/index.d.ts +1 -0
  83. package/dist/cjs/components/RelativeDatePicker/i18n/index.js +11 -0
  84. package/dist/cjs/components/RelativeDatePicker/i18n/ru.json +4 -0
  85. package/dist/cjs/components/RelativeDatePicker/index.d.ts +3 -0
  86. package/dist/cjs/components/RelativeDatePicker/index.js +19 -0
  87. package/dist/cjs/components/hooks/useControlledState.d.ts +1 -0
  88. package/dist/cjs/components/hooks/useControlledState.js +30 -0
  89. package/dist/cjs/components/index.d.ts +5 -0
  90. package/dist/cjs/components/index.js +21 -0
  91. package/dist/cjs/components/types/datePicker.d.ts +16 -0
  92. package/dist/cjs/components/types/datePicker.js +2 -0
  93. package/dist/cjs/components/types/events.d.ts +17 -0
  94. package/dist/cjs/components/types/events.js +2 -0
  95. package/dist/cjs/components/types/index.d.ts +4 -0
  96. package/dist/cjs/components/types/index.js +2 -0
  97. package/dist/cjs/components/types/inputs.d.ts +79 -0
  98. package/dist/cjs/components/types/inputs.js +2 -0
  99. package/dist/cjs/components/types/style.d.ts +7 -0
  100. package/dist/cjs/components/types/style.js +2 -0
  101. package/dist/cjs/components/utils/getButtonSizeForInput.d.ts +2 -0
  102. package/dist/cjs/components/utils/getButtonSizeForInput.js +20 -0
  103. package/dist/cjs/components/utils/mergeProps.d.ts +13 -0
  104. package/dist/cjs/components/utils/mergeProps.js +47 -0
  105. package/dist/cjs/components/variables.css +0 -0
  106. package/dist/cjs/index.d.ts +1 -0
  107. package/dist/cjs/index.js +17 -0
  108. package/dist/cjs/utils/cn.d.ts +2 -0
  109. package/dist/cjs/utils/cn.js +6 -0
  110. package/dist/esm/components/Calendar/Calendar.css +220 -0
  111. package/dist/esm/components/Calendar/Calendar.d.ts +13 -0
  112. package/dist/esm/components/Calendar/Calendar.js +9 -0
  113. package/dist/esm/components/Calendar/CalendarBase.d.ts +18 -0
  114. package/dist/esm/components/Calendar/CalendarBase.js +83 -0
  115. package/dist/esm/components/Calendar/RangeCalendar.d.ts +13 -0
  116. package/dist/esm/components/Calendar/RangeCalendar.js +16 -0
  117. package/dist/esm/components/Calendar/hooks/types.d.ts +124 -0
  118. package/dist/esm/components/Calendar/hooks/types.js +1 -0
  119. package/dist/esm/components/Calendar/hooks/useCalendarCellProps.d.ts +19 -0
  120. package/dist/esm/components/Calendar/hooks/useCalendarCellProps.js +91 -0
  121. package/dist/esm/components/Calendar/hooks/useCalendarGridProps.d.ts +5 -0
  122. package/dist/esm/components/Calendar/hooks/useCalendarGridProps.js +54 -0
  123. package/dist/esm/components/Calendar/hooks/useCalendarProps.d.ts +10 -0
  124. package/dist/esm/components/Calendar/hooks/useCalendarProps.js +103 -0
  125. package/dist/esm/components/Calendar/hooks/useCalendarState.d.ts +7 -0
  126. package/dist/esm/components/Calendar/hooks/useCalendarState.js +171 -0
  127. package/dist/esm/components/Calendar/hooks/useRangeCalendarState.d.ts +7 -0
  128. package/dist/esm/components/Calendar/hooks/useRangeCalendarState.js +75 -0
  129. package/dist/esm/components/Calendar/i18n/en.json +4 -0
  130. package/dist/esm/components/Calendar/i18n/index.d.ts +1 -0
  131. package/dist/esm/components/Calendar/i18n/index.js +5 -0
  132. package/dist/esm/components/Calendar/i18n/ru.json +4 -0
  133. package/dist/esm/components/Calendar/index.d.ts +8 -0
  134. package/dist/esm/components/Calendar/index.js +7 -0
  135. package/dist/esm/components/Calendar/utils.d.ts +4 -0
  136. package/dist/esm/components/Calendar/utils.js +34 -0
  137. package/dist/esm/components/DateField/DateField.css +4 -0
  138. package/dist/esm/components/DateField/DateField.d.ts +6 -0
  139. package/dist/esm/components/DateField/DateField.js +31 -0
  140. package/dist/esm/components/DateField/hooks/useDateFieldProps.d.ts +6 -0
  141. package/dist/esm/components/DateField/hooks/useDateFieldProps.js +196 -0
  142. package/dist/esm/components/DateField/hooks/useDateFieldState.d.ts +67 -0
  143. package/dist/esm/components/DateField/hooks/useDateFieldState.js +621 -0
  144. package/dist/esm/components/DateField/i18n/en.json +10 -0
  145. package/dist/esm/components/DateField/i18n/index.d.ts +1 -0
  146. package/dist/esm/components/DateField/i18n/index.js +5 -0
  147. package/dist/esm/components/DateField/i18n/ru.json +10 -0
  148. package/dist/esm/components/DateField/index.d.ts +3 -0
  149. package/dist/esm/components/DateField/index.js +3 -0
  150. package/dist/esm/components/DateField/types.d.ts +67 -0
  151. package/dist/esm/components/DateField/types.js +1 -0
  152. package/dist/esm/components/DateField/utils.d.ts +14 -0
  153. package/dist/esm/components/DateField/utils.js +268 -0
  154. package/dist/esm/components/DatePicker/DatePicker.css +19 -0
  155. package/dist/esm/components/DatePicker/DatePicker.d.ts +5 -0
  156. package/dist/esm/components/DatePicker/DatePicker.js +59 -0
  157. package/dist/esm/components/DatePicker/DesktopCalendar.d.ts +15 -0
  158. package/dist/esm/components/DatePicker/DesktopCalendar.js +48 -0
  159. package/dist/esm/components/DatePicker/MobileCalendar.css +49 -0
  160. package/dist/esm/components/DatePicker/MobileCalendar.d.ts +14 -0
  161. package/dist/esm/components/DatePicker/MobileCalendar.js +73 -0
  162. package/dist/esm/components/DatePicker/hooks/useDatePickerState.d.ts +45 -0
  163. package/dist/esm/components/DatePicker/hooks/useDatePickerState.js +107 -0
  164. package/dist/esm/components/DatePicker/i18n/en.json +3 -0
  165. package/dist/esm/components/DatePicker/i18n/index.d.ts +1 -0
  166. package/dist/esm/components/DatePicker/i18n/index.js +5 -0
  167. package/dist/esm/components/DatePicker/i18n/ru.json +3 -0
  168. package/dist/esm/components/DatePicker/index.d.ts +2 -0
  169. package/dist/esm/components/DatePicker/index.js +2 -0
  170. package/dist/esm/components/DatePicker/utils.d.ts +1 -0
  171. package/dist/esm/components/DatePicker/utils.js +2 -0
  172. package/dist/esm/components/RelativeDateField/RelativeDateField.css +16 -0
  173. package/dist/esm/components/RelativeDateField/RelativeDateField.d.ts +11 -0
  174. package/dist/esm/components/RelativeDateField/RelativeDateField.js +30 -0
  175. package/dist/esm/components/RelativeDateField/hooks/useRelativeDateFieldProps.d.ts +12 -0
  176. package/dist/esm/components/RelativeDateField/hooks/useRelativeDateFieldProps.js +59 -0
  177. package/dist/esm/components/RelativeDateField/hooks/useRelativeDateFieldState.d.ts +30 -0
  178. package/dist/esm/components/RelativeDateField/hooks/useRelativeDateFieldState.js +59 -0
  179. package/dist/esm/components/RelativeDateField/index.d.ts +3 -0
  180. package/dist/esm/components/RelativeDateField/index.js +3 -0
  181. package/dist/esm/components/RelativeDatePicker/RelativeDatePicker.css +16 -0
  182. package/dist/esm/components/RelativeDatePicker/RelativeDatePicker.d.ts +6 -0
  183. package/dist/esm/components/RelativeDatePicker/RelativeDatePicker.js +25 -0
  184. package/dist/esm/components/RelativeDatePicker/hooks/useRelativeDatePickerProps.d.ts +17 -0
  185. package/dist/esm/components/RelativeDatePicker/hooks/useRelativeDatePickerProps.js +168 -0
  186. package/dist/esm/components/RelativeDatePicker/hooks/useRelativeDatePickerState.d.ts +36 -0
  187. package/dist/esm/components/RelativeDatePicker/hooks/useRelativeDatePickerState.js +102 -0
  188. package/dist/esm/components/RelativeDatePicker/i18n/en.json +4 -0
  189. package/dist/esm/components/RelativeDatePicker/i18n/index.d.ts +1 -0
  190. package/dist/esm/components/RelativeDatePicker/i18n/index.js +5 -0
  191. package/dist/esm/components/RelativeDatePicker/i18n/ru.json +4 -0
  192. package/dist/esm/components/RelativeDatePicker/index.d.ts +3 -0
  193. package/dist/esm/components/RelativeDatePicker/index.js +3 -0
  194. package/dist/esm/components/hooks/useControlledState.d.ts +1 -0
  195. package/dist/esm/components/hooks/useControlledState.js +23 -0
  196. package/dist/esm/components/index.d.ts +5 -0
  197. package/dist/esm/components/index.js +5 -0
  198. package/dist/esm/components/types/datePicker.d.ts +16 -0
  199. package/dist/esm/components/types/datePicker.js +1 -0
  200. package/dist/esm/components/types/events.d.ts +17 -0
  201. package/dist/esm/components/types/events.js +1 -0
  202. package/dist/esm/components/types/index.d.ts +4 -0
  203. package/dist/esm/components/types/index.js +1 -0
  204. package/dist/esm/components/types/inputs.d.ts +79 -0
  205. package/dist/esm/components/types/inputs.js +1 -0
  206. package/dist/esm/components/types/style.d.ts +7 -0
  207. package/dist/esm/components/types/style.js +1 -0
  208. package/dist/esm/components/utils/getButtonSizeForInput.d.ts +2 -0
  209. package/dist/esm/components/utils/getButtonSizeForInput.js +16 -0
  210. package/dist/esm/components/utils/mergeProps.d.ts +13 -0
  211. package/dist/esm/components/utils/mergeProps.js +43 -0
  212. package/dist/esm/components/variables.css +0 -0
  213. package/dist/esm/index.d.ts +1 -0
  214. package/dist/esm/index.js +1 -0
  215. package/dist/esm/utils/cn.d.ts +2 -0
  216. package/dist/esm/utils/cn.js +3 -0
  217. package/package.json +119 -0
@@ -0,0 +1,67 @@
1
+ export type DateFieldSectionType = Exclude<Intl.DateTimeFormatPartTypes, 'era'>;
2
+ export type DateFormatTokenMap = {
3
+ [formatToken: string]: DateFieldSectionType | {
4
+ sectionType: DateFieldSectionType;
5
+ contentType: 'digit' | 'letter';
6
+ };
7
+ };
8
+ export interface DateFieldSection {
9
+ value: number | undefined;
10
+ /**
11
+ * Value of the section, as rendered inside the input.
12
+ * For example, in the date `May 25, 1995`, the value of the month section is "May".
13
+ */
14
+ textValue: string;
15
+ /**
16
+ * Format token used to parse the value of this section from the date object.
17
+ * For example, in the format `MMMM D, YYYY`, the format of the month section is "MMMM".
18
+ */
19
+ format: string;
20
+ /**
21
+ * Placeholder rendered when the value of this section is empty.
22
+ */
23
+ placeholder: string;
24
+ /**
25
+ * Type of the section.
26
+ */
27
+ type: DateFieldSectionType;
28
+ /**
29
+ * Type of content of the section.
30
+ * Will determine if we should apply a digit-based editing or a letter-based editing.
31
+ */
32
+ contentType: 'digit' | 'letter';
33
+ /**
34
+ * Available values
35
+ */
36
+ options?: string[];
37
+ /** The minimum numeric value for the segment, if applicable. */
38
+ minValue?: number;
39
+ /** The maximum numeric value for the segment, if applicable. */
40
+ maxValue?: number;
41
+ /**
42
+ * If `true`, the value of this section is supposed to have leading zeroes.
43
+ * For example, the value `1` should be rendered as "01" instead of "1".
44
+ */
45
+ hasLeadingZeros: boolean;
46
+ /**
47
+ * If `true`, the section value has been modified since the last time the sections were generated from a valid date.
48
+ * When we can generate a valid date from the section, we don't directly pass it to `onChange`,
49
+ * Otherwise, we would lose all the information contained in the original date, things like:
50
+ * - time if the format does not contain it
51
+ * - timezone / UTC
52
+ *
53
+ * To avoid losing that information, we transfer the values of the modified sections from the newly generated date to the original date.
54
+ */
55
+ modified: boolean;
56
+ /**
57
+ * Start index of the section in the format
58
+ */
59
+ start: number;
60
+ /**
61
+ * End index of the section in the format
62
+ */
63
+ end: number;
64
+ previousEditableSection: number;
65
+ nextEditableSection: number;
66
+ }
67
+ export type DateFieldSectionWithoutPosition<TSection extends DateFieldSection = DateFieldSection> = Omit<TSection, 'start' | 'end' | 'value' | 'textValue' | 'modified' | 'previousEditableSection' | 'nextEditableSection'>;
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,14 @@
1
+ import type { DateTime } from '@gravity-ui/date-utils';
2
+ import type { DateFieldSectionWithoutPosition } from './types';
3
+ export declare const isMac: boolean;
4
+ export declare const CtrlCmd: string;
5
+ export declare function expandFormat(format: string): string;
6
+ export declare function splitFormatIntoSections(format: string): DateFieldSectionWithoutPosition[];
7
+ interface PlaceholderValueOptions {
8
+ placeholderValue?: DateTime;
9
+ timeZone?: string;
10
+ }
11
+ export declare function createPlaceholderValue({ placeholderValue, timeZone }: PlaceholderValueOptions): DateTime;
12
+ export declare function isInvalid(value: DateTime | null | undefined, minValue: DateTime | undefined, maxValue: DateTime | undefined): boolean;
13
+ export declare function mergeDateTime(date: DateTime, time: DateTime): DateTime;
14
+ export {};
@@ -0,0 +1,268 @@
1
+ import { dateTime, settings } from '@gravity-ui/date-utils';
2
+ import { i18n } from './i18n';
3
+ export const isMac = window.navigator.userAgent.indexOf('Macintosh') >= 0;
4
+ export const CtrlCmd = isMac ? 'metaKey' : 'ctrlKey';
5
+ export function expandFormat(format) {
6
+ const localeFormats = settings.getLocaleData().formats;
7
+ const getShortLocalizedFormatFromLongLocalizedFormat = (formatBis) => formatBis.replace(/(\[[^\]]+])|(MMMM|MM|DD|dddd)/g, (_, escapeSequence, localizedFormat) => escapeSequence || localizedFormat.slice(1));
8
+ return format.replace(/(\[[^\]]+])|(LTS?|l{1,4}|L{1,4})/g, (_, escapeSequence, localizedFormat) => {
9
+ const LongLocalizedFormat = localizedFormat && localizedFormat.toUpperCase();
10
+ return (escapeSequence ||
11
+ localeFormats[localizedFormat] ||
12
+ getShortLocalizedFormatFromLongLocalizedFormat(localeFormats[LongLocalizedFormat]));
13
+ });
14
+ }
15
+ const escapedCharacters = { start: '[', end: ']' };
16
+ const formatTokenMap = {
17
+ // Year
18
+ YY: 'year',
19
+ YYYY: 'year',
20
+ // Month
21
+ M: 'month',
22
+ MM: 'month',
23
+ MMM: { sectionType: 'month', contentType: 'letter' },
24
+ MMMM: { sectionType: 'month', contentType: 'letter' },
25
+ // Day of the month
26
+ D: 'day',
27
+ DD: 'day',
28
+ Do: 'day',
29
+ // Day of the week
30
+ d: 'weekday',
31
+ dd: { sectionType: 'weekday', contentType: 'letter' },
32
+ ddd: { sectionType: 'weekday', contentType: 'letter' },
33
+ dddd: { sectionType: 'weekday', contentType: 'letter' },
34
+ // Day period AM, PM
35
+ A: { sectionType: 'dayPeriod', contentType: 'letter' },
36
+ a: { sectionType: 'dayPeriod', contentType: 'letter' },
37
+ // Hours
38
+ H: 'hour',
39
+ HH: 'hour',
40
+ h: 'hour',
41
+ hh: 'hour',
42
+ // Minutes
43
+ m: 'minute',
44
+ mm: 'minute',
45
+ // Seconds
46
+ s: 'second',
47
+ ss: 'second',
48
+ // Timezone
49
+ z: { sectionType: 'timeZoneName', contentType: 'letter' },
50
+ zzz: { sectionType: 'timeZoneName', contentType: 'letter' },
51
+ Z: { sectionType: 'timeZoneName', contentType: 'letter' },
52
+ ZZ: { sectionType: 'timeZoneName', contentType: 'letter' },
53
+ };
54
+ function getDateSectionConfigFromFormatToken(formatToken) {
55
+ const config = formatTokenMap[formatToken];
56
+ if (!config) {
57
+ console.error([
58
+ `The token "${formatToken}" is not supported by the Date field.`,
59
+ 'Please try using another token.',
60
+ ].join('\n'));
61
+ return {
62
+ type: 'literal',
63
+ contentType: 'letter',
64
+ };
65
+ }
66
+ if (typeof config === 'string') {
67
+ return {
68
+ type: config,
69
+ contentType: 'digit',
70
+ };
71
+ }
72
+ return {
73
+ type: config.sectionType,
74
+ contentType: config.contentType,
75
+ };
76
+ }
77
+ function isFourDigitYearFormat(format) {
78
+ return dateTime().format(format).length === 4;
79
+ }
80
+ function doesSectionHaveLeadingZeros(contentType, sectionType, format) {
81
+ if (contentType !== 'digit') {
82
+ return false;
83
+ }
84
+ switch (sectionType) {
85
+ case 'year': {
86
+ if (isFourDigitYearFormat(format)) {
87
+ const formatted0001 = dateTime().set('year', 1).format(format);
88
+ return formatted0001 === '0001';
89
+ }
90
+ const formatted2001 = dateTime().set('year', 2001).format(format);
91
+ return formatted2001 === '01';
92
+ }
93
+ case 'month': {
94
+ return dateTime().startOf('year').format(format).length > 1;
95
+ }
96
+ case 'day': {
97
+ return dateTime().startOf('month').format(format).length > 1;
98
+ }
99
+ case 'weekday': {
100
+ return dateTime().startOf('week').format(format).length > 1;
101
+ }
102
+ case 'hour': {
103
+ return dateTime().set('hour', 1).format(format).length > 1;
104
+ }
105
+ case 'minute': {
106
+ return dateTime().set('minute', 1).format(format).length > 1;
107
+ }
108
+ case 'second': {
109
+ return dateTime().set('second', 1).format(format).length > 1;
110
+ }
111
+ default: {
112
+ throw new Error('Invalid section type');
113
+ }
114
+ }
115
+ }
116
+ function getSectionPlaceholder(sectionConfig, currentTokenValue) {
117
+ switch (sectionConfig.type) {
118
+ case 'year': {
119
+ return i18n('year_placeholder').repeat(dateTime().format(currentTokenValue).length);
120
+ }
121
+ case 'month': {
122
+ return i18n('month_placeholder').repeat(sectionConfig.contentType === 'letter' ? 4 : 2);
123
+ }
124
+ case 'day': {
125
+ return i18n('day_placeholder').repeat(2);
126
+ }
127
+ case 'weekday': {
128
+ return i18n('weekday_placeholder').repeat(sectionConfig.contentType === 'letter' ? 4 : 2);
129
+ }
130
+ case 'hour': {
131
+ return i18n('hour_placeholder').repeat(2);
132
+ }
133
+ case 'minute': {
134
+ return i18n('minute_placeholder').repeat(2);
135
+ }
136
+ case 'second': {
137
+ return i18n('second_placeholder').repeat(2);
138
+ }
139
+ case 'dayPeriod': {
140
+ return i18n('dayPeriod_placeholder');
141
+ }
142
+ default: {
143
+ return currentTokenValue;
144
+ }
145
+ }
146
+ }
147
+ export function splitFormatIntoSections(format) {
148
+ const sections = [];
149
+ const expandedFormat = expandFormat(format);
150
+ let currentTokenValue = '';
151
+ let isSeparator = false;
152
+ let isInEscapeBoundary = false;
153
+ for (let i = 0; i < expandedFormat.length; i++) {
154
+ const char = expandedFormat[i];
155
+ if (isInEscapeBoundary) {
156
+ if (char === escapedCharacters.end) {
157
+ isInEscapeBoundary = false;
158
+ continue;
159
+ }
160
+ currentTokenValue += char;
161
+ }
162
+ else if (char.match(/[a-zA-Z]/)) {
163
+ if (isSeparator) {
164
+ addLiteralSection(sections, currentTokenValue);
165
+ currentTokenValue = '';
166
+ }
167
+ isSeparator = false;
168
+ currentTokenValue += char;
169
+ }
170
+ else {
171
+ if (!isSeparator) {
172
+ addFormatSection(sections, currentTokenValue);
173
+ currentTokenValue = '';
174
+ }
175
+ isSeparator = true;
176
+ if (char === escapedCharacters.start) {
177
+ isInEscapeBoundary = true;
178
+ }
179
+ else {
180
+ currentTokenValue += char;
181
+ }
182
+ }
183
+ }
184
+ if (currentTokenValue) {
185
+ if (isSeparator) {
186
+ addLiteralSection(sections, currentTokenValue);
187
+ }
188
+ else {
189
+ addFormatSection(sections, currentTokenValue);
190
+ }
191
+ }
192
+ return sections;
193
+ }
194
+ function addFormatSection(sections, token) {
195
+ if (!token) {
196
+ return;
197
+ }
198
+ const sectionConfig = getDateSectionConfigFromFormatToken(token);
199
+ const hasLeadingZeros = doesSectionHaveLeadingZeros(sectionConfig.contentType, sectionConfig.type, token);
200
+ sections.push(Object.assign(Object.assign({}, sectionConfig), { format: token, placeholder: getSectionPlaceholder(sectionConfig, token), options: getSectionOptions(sectionConfig, token), hasLeadingZeros }));
201
+ }
202
+ function addLiteralSection(sections, token) {
203
+ if (!token) {
204
+ return;
205
+ }
206
+ sections.push({
207
+ type: 'literal',
208
+ contentType: 'letter',
209
+ format: token,
210
+ placeholder: token,
211
+ hasLeadingZeros: false,
212
+ });
213
+ }
214
+ function getSectionOptions(section, token) {
215
+ switch (section.type) {
216
+ case 'month': {
217
+ const format = section.contentType === 'letter' ? token : 'MMMM';
218
+ let date = dateTime().startOf('year');
219
+ const options = [];
220
+ for (let i = 0; i < 12; i++) {
221
+ options.push(date.format(format).toLocaleUpperCase());
222
+ date = date.add(1, 'months');
223
+ }
224
+ return options;
225
+ }
226
+ case 'dayPeriod': {
227
+ const amDayPeriod = dateTime().hour(0);
228
+ const pmDayPeriod = amDayPeriod.hour(12);
229
+ const options = [
230
+ amDayPeriod.format(token).toLocaleUpperCase(),
231
+ pmDayPeriod.format(token).toLocaleUpperCase(),
232
+ ];
233
+ return options;
234
+ }
235
+ case 'weekday': {
236
+ const format = section.contentType === 'letter' ? token : 'dddd';
237
+ let date = dateTime().day(0);
238
+ const options = [];
239
+ for (let i = 0; i < 7; i++) {
240
+ options.push(date.format(format).toLocaleUpperCase());
241
+ date = date.add(1, 'day');
242
+ }
243
+ return options;
244
+ }
245
+ }
246
+ return undefined;
247
+ }
248
+ export function createPlaceholderValue({ placeholderValue, timeZone }) {
249
+ return (placeholderValue !== null && placeholderValue !== void 0 ? placeholderValue : dateTime({ timeZone }).set('hour', 0).set('minute', 0).set('second', 0));
250
+ }
251
+ export function isInvalid(value, minValue, maxValue) {
252
+ if (!value) {
253
+ return false;
254
+ }
255
+ if (minValue && value.isBefore(minValue)) {
256
+ return true;
257
+ }
258
+ if (maxValue && maxValue.isBefore(value)) {
259
+ return true;
260
+ }
261
+ return false;
262
+ }
263
+ export function mergeDateTime(date, time) {
264
+ return date
265
+ .set('hours', time.hour())
266
+ .set('minutes', time.minute())
267
+ .set('seconds', time.second());
268
+ }
@@ -0,0 +1,19 @@
1
+ .g-date-date-picker {
2
+ position: relative;
3
+ display: inline-block;
4
+ }
5
+ .g-date-date-picker__field {
6
+ width: 100%;
7
+ }
8
+ .g-date-date-picker__field_mobile {
9
+ pointer-events: none;
10
+ }
11
+ .g-date-date-picker__popup-content {
12
+ outline: none;
13
+ }
14
+ .g-date-date-picker__time-field {
15
+ width: 100%;
16
+ }
17
+ .g-date-date-picker__time-field-wrapper {
18
+ padding: 10px;
19
+ }
@@ -0,0 +1,5 @@
1
+ import type { AccessibilityProps, DateFieldBase, DomProps, FocusableProps, KeyboardEvents, StyleProps, TextInputProps } from '../types';
2
+ import './DatePicker.css';
3
+ export interface DatePickerProps extends DateFieldBase, TextInputProps, FocusableProps, KeyboardEvents, DomProps, StyleProps, AccessibilityProps {
4
+ }
5
+ export declare function DatePicker({ value, defaultValue, onUpdate, className, onFocus, onBlur, ...props }: DatePickerProps): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,59 @@
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 React from 'react';
14
+ import { TextInput, useFocusWithin, useMobile } from '@gravity-ui/uikit';
15
+ import { useDateFieldProps, useDateFieldState } from '../DateField';
16
+ import { DesktopCalendar, DesktopCalendarButton } from './DesktopCalendar';
17
+ import { MobileCalendar, MobileCalendarIcon } from './MobileCalendar';
18
+ import { useDatePickerState } from './hooks/useDatePickerState';
19
+ import { b } from './utils';
20
+ import './DatePicker.css';
21
+ export function DatePicker(_a) {
22
+ var { value, defaultValue, onUpdate, className, onFocus, onBlur } = _a, props = __rest(_a, ["value", "defaultValue", "onUpdate", "className", "onFocus", "onBlur"]);
23
+ const anchorRef = React.useRef(null);
24
+ const state = useDatePickerState(Object.assign(Object.assign({}, props), { value,
25
+ defaultValue,
26
+ onUpdate }));
27
+ const [isMobile] = useMobile();
28
+ const [isActive, setActive] = React.useState(false);
29
+ const { focusWithinProps } = useFocusWithin({
30
+ onFocusWithin: onFocus,
31
+ onBlurWithin: onBlur,
32
+ onFocusWithinChange(isFocusWithin) {
33
+ setActive(isFocusWithin);
34
+ },
35
+ });
36
+ const fieldState = useDateFieldState({
37
+ value: state.value,
38
+ onUpdate: state.setValue,
39
+ disabled: state.disabled,
40
+ readOnly: state.readOnly,
41
+ validationState: props.validationState,
42
+ minValue: props.minValue,
43
+ maxValue: props.maxValue,
44
+ isDateUnavailable: props.isDateUnavailable,
45
+ format: state.format,
46
+ placeholderValue: props.placeholderValue,
47
+ timeZone: props.timeZone,
48
+ });
49
+ const { inputProps } = useDateFieldProps(fieldState, props);
50
+ return (
51
+ // eslint-disable-next-line jsx-a11y/no-noninteractive-element-interactions
52
+ _jsxs("div", Object.assign({ ref: anchorRef, className: b(null, className), style: props.style }, focusWithinProps, { role: "group", "aria-disabled": state.disabled || undefined, onKeyDown: (e) => {
53
+ if (e.altKey && (e.key === 'ArrowDown' || e.key === 'ArrowUp')) {
54
+ e.preventDefault();
55
+ e.stopPropagation();
56
+ state.setOpen(true);
57
+ }
58
+ }, children: [isMobile ? (_jsx(MobileCalendar, { props: props, state: state })) : (_jsx(DesktopCalendar, { anchorRef: anchorRef, props: props, state: state })), _jsx(TextInput, Object.assign({}, inputProps, { value: fieldState.isEmpty && !isActive && props.placeholder ? '' : inputProps.value, className: b('field', { mobile: isMobile }), hasClear: !isMobile && inputProps.hasClear, rightContent: isMobile ? (_jsx(MobileCalendarIcon, { props: props, state: state })) : (_jsx(DesktopCalendarButton, { props: props, state: state })) }))] })));
59
+ }
@@ -0,0 +1,15 @@
1
+ import React from 'react';
2
+ import type { DatePickerProps } from './DatePicker';
3
+ import type { DatePickerState } from './hooks/useDatePickerState';
4
+ interface DesktopCalendarProps {
5
+ anchorRef: React.RefObject<HTMLElement>;
6
+ props: DatePickerProps;
7
+ state: DatePickerState;
8
+ }
9
+ export declare function DesktopCalendar({ anchorRef, props, state }: DesktopCalendarProps): import("react/jsx-runtime").JSX.Element | null;
10
+ interface DesktopCalendarButtonProps {
11
+ props: DatePickerProps;
12
+ state: DatePickerState;
13
+ }
14
+ export declare function DesktopCalendarButton({ props, state }: DesktopCalendarButtonProps): import("react/jsx-runtime").JSX.Element | null;
15
+ export {};
@@ -0,0 +1,48 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import React from 'react';
3
+ import { Calendar as CalendarIcon } from '@gravity-ui/icons';
4
+ import { Button, Icon, Popup, useFocusWithin } from '@gravity-ui/uikit';
5
+ import { Calendar } from '../Calendar';
6
+ import { DateField } from '../DateField';
7
+ import { getButtonSizeForInput } from '../utils/getButtonSizeForInput';
8
+ import { i18n } from './i18n';
9
+ import { b } from './utils';
10
+ export function DesktopCalendar({ anchorRef, props, state }) {
11
+ var _a;
12
+ const { focusWithinProps } = useFocusWithin({
13
+ isDisabled: !state.isOpen,
14
+ onBlurWithin: () => {
15
+ state.setOpen(false);
16
+ },
17
+ });
18
+ if (!state.hasDate) {
19
+ return null;
20
+ }
21
+ return (_jsx(Popup, { open: state.isOpen, anchorRef: anchorRef, onClose: () => {
22
+ state.setOpen(false);
23
+ }, restoreFocus: true, children: _jsxs("div", Object.assign({}, focusWithinProps, { className: b('popup-content'), children: [_jsx(Calendar
24
+ // eslint-disable-next-line jsx-a11y/no-autofocus
25
+ , {
26
+ // eslint-disable-next-line jsx-a11y/no-autofocus
27
+ autoFocus: true, size: props.size === 's' ? 'm' : props.size, disabled: props.disabled, readOnly: props.readOnly, onUpdate: (d) => {
28
+ state.setDateValue(d);
29
+ }, defaultFocusedValue: (_a = state.dateValue) !== null && _a !== void 0 ? _a : undefined, value: state.dateValue, minValue: props.minValue, maxValue: props.maxValue, isDateUnavailable: props.isDateUnavailable, timeZone: props.timeZone }), state.hasTime && (_jsx("div", { className: b('time-field-wrapper'), children: _jsx(DateField, { className: b('time-field'), size: props.size, readOnly: props.readOnly, value: state.timeValue, onUpdate: state.setTimeValue, placeholderValue: props.placeholderValue, minValue: props.minValue, maxValue: props.maxValue, timeZone: props.timeZone, format: state.timeFormat }) }))] })) }));
30
+ }
31
+ export function DesktopCalendarButton({ props, state }) {
32
+ const wasOpenBeforeClickRef = React.useRef(false);
33
+ if (!state.hasDate) {
34
+ return null;
35
+ }
36
+ return (_jsx(Button, { view: "flat", size: getButtonSizeForInput(props.size), disabled: props.disabled, extraProps: {
37
+ 'aria-label': i18n('Calendar'),
38
+ 'aria-haspopup': 'dialog',
39
+ 'aria-expanded': state.isOpen,
40
+ }, onFocus: () => {
41
+ wasOpenBeforeClickRef.current = state.isOpen;
42
+ }, onClick: () => {
43
+ if (!wasOpenBeforeClickRef.current) {
44
+ state.setOpen(true);
45
+ }
46
+ wasOpenBeforeClickRef.current = false;
47
+ }, children: _jsx(Icon, { data: CalendarIcon }) }));
48
+ }
@@ -0,0 +1,49 @@
1
+ .g-date-mobile-calendar {
2
+ position: relative;
3
+ display: inline-block;
4
+ width: 24px;
5
+ height: 24px;
6
+ }
7
+ .g-date-mobile-calendar_size_xs {
8
+ width: 20px;
9
+ height: 20px;
10
+ }
11
+ .g-date-mobile-calendar_size_m {
12
+ width: 28px;
13
+ height: 28px;
14
+ }
15
+ .g-date-mobile-calendar_size_l {
16
+ width: 36px;
17
+ height: 36px;
18
+ }
19
+ .g-date-mobile-calendar__button {
20
+ position: absolute;
21
+ inset: 0;
22
+ display: flex;
23
+ justify-content: center;
24
+ align-items: center;
25
+ }
26
+ .g-date-mobile-calendar__input {
27
+ position: absolute;
28
+ z-index: 1;
29
+ top: 0;
30
+ left: 0;
31
+ box-sizing: border-box;
32
+ width: 100%;
33
+ height: 100%;
34
+ margin: 0;
35
+ padding: 0;
36
+ cursor: pointer;
37
+ opacity: 0;
38
+ border: none;
39
+ }
40
+ .g-date-mobile-calendar__input::-webkit-calendar-picker-indicator {
41
+ position: absolute;
42
+ top: 0;
43
+ left: 0;
44
+ width: 100%;
45
+ height: 100%;
46
+ margin: 0;
47
+ padding: 0;
48
+ cursor: pointer;
49
+ }
@@ -0,0 +1,14 @@
1
+ import type { DatePickerProps } from './DatePicker';
2
+ import type { DatePickerState } from './hooks/useDatePickerState';
3
+ import './MobileCalendar.css';
4
+ interface MobileCalendarProps {
5
+ props: DatePickerProps;
6
+ state: DatePickerState;
7
+ }
8
+ export declare function MobileCalendar({ props, state }: MobileCalendarProps): import("react/jsx-runtime").JSX.Element;
9
+ interface MobileCalendarIconProps {
10
+ props: DatePickerProps;
11
+ state: DatePickerState;
12
+ }
13
+ export declare function MobileCalendarIcon({ props }: MobileCalendarIconProps): import("react/jsx-runtime").JSX.Element;
14
+ export {};
@@ -0,0 +1,73 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { dateTime } from '@gravity-ui/date-utils';
3
+ import { Calendar as CalendarIcon } from '@gravity-ui/icons';
4
+ import { Icon } from '@gravity-ui/uikit';
5
+ import { block } from '../../utils/cn';
6
+ import { createPlaceholderValue, mergeDateTime } from '../DateField/utils';
7
+ import { getButtonSizeForInput } from '../utils/getButtonSizeForInput';
8
+ import './MobileCalendar.css';
9
+ const b = block('mobile-calendar');
10
+ export function MobileCalendar({ props, state }) {
11
+ let type = 'date';
12
+ if (state.hasTime && state.hasDate) {
13
+ type = 'datetime-local';
14
+ }
15
+ else if (state.hasTime) {
16
+ type = 'time';
17
+ }
18
+ return (_jsx("input", { className: b('input'), disabled: props.disabled, type: type, value: formatNative(state.value || props.placeholderValue, type), id: props.id, min: formatNative(props.minValue, type), max: formatNative(props.maxValue, type), tabIndex: -1, onChange: (e) => {
19
+ if (props.readOnly) {
20
+ return;
21
+ }
22
+ const newValue = e.target.value;
23
+ if (newValue) {
24
+ const localDate = dateTime({ input: newValue, format: getDateFormat(type) });
25
+ let newDate = state.hasDate
26
+ ? dateTime({
27
+ input: newValue,
28
+ format: getDateFormat(type),
29
+ timeZone: props.timeZone,
30
+ })
31
+ : createPlaceholderValue({
32
+ placeholderValue: props.placeholderValue,
33
+ timeZone: props.timeZone,
34
+ });
35
+ if (state.hasTime) {
36
+ newDate = mergeDateTime(newDate, localDate);
37
+ }
38
+ else if (state.dateValue) {
39
+ newDate = mergeDateTime(newDate, state.dateValue);
40
+ }
41
+ else {
42
+ newDate = newDate.set('hours', 0).set('minutes', 0).set('seconds', 0);
43
+ }
44
+ state.setValue(newDate);
45
+ }
46
+ else {
47
+ state.setValue(null);
48
+ }
49
+ } }));
50
+ }
51
+ export function MobileCalendarIcon({ props }) {
52
+ return (_jsx("span", { className: b({ size: getButtonSizeForInput(props.size) }), children: _jsx("span", { className: b('button'), children: _jsx(Icon, { data: CalendarIcon }) }) }));
53
+ }
54
+ function getDateFormat(type) {
55
+ switch (type) {
56
+ case 'time': {
57
+ return 'HH:mm';
58
+ }
59
+ case 'datetime-local': {
60
+ return 'YYYY-MM-DDTHH:mm';
61
+ }
62
+ default: {
63
+ return 'YYYY-MM-DD';
64
+ }
65
+ }
66
+ }
67
+ function formatNative(date, type) {
68
+ if (!date) {
69
+ return '';
70
+ }
71
+ const format = getDateFormat(type);
72
+ return date.format(format);
73
+ }