@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,45 @@
1
+ import type { DateTime } from '@gravity-ui/date-utils';
2
+ import type { InputBase, ValueBase } from '../../types';
3
+ export type Granularity = 'day' | 'hour' | 'minute' | 'second';
4
+ export interface DatePickerState {
5
+ /** The currently selected date. */
6
+ value: DateTime | null;
7
+ /** Sets the selected date. */
8
+ setValue(value: DateTime | null): void;
9
+ /**
10
+ * The date portion of the value. This may be set prior to `value` if the user has
11
+ * selected a date but has not yet selected a time.
12
+ */
13
+ dateValue: DateTime | null;
14
+ /** Sets the date portion of the value. */
15
+ setDateValue(value: DateTime): void;
16
+ /**
17
+ * The time portion of the value. This may be set prior to `value` if the user has
18
+ * selected a time but has not yet selected a date.
19
+ */
20
+ timeValue: DateTime | null;
21
+ /** Sets the time portion of the value. */
22
+ setTimeValue(value: DateTime | null): void;
23
+ /** Whether the field is read only. */
24
+ readOnly?: boolean;
25
+ /** Whether the field is disabled. */
26
+ disabled?: boolean;
27
+ /** Format of the date when rendered in the input. */
28
+ format: string;
29
+ /** Whether the date picker supports selecting a date. */
30
+ hasDate: boolean;
31
+ /** Whether the date picker supports selecting a time. */
32
+ hasTime: boolean;
33
+ /** Format of the time when rendered in the input. */
34
+ timeFormat?: string;
35
+ /** Whether the calendar popover is currently open. */
36
+ isOpen: boolean;
37
+ /** Sets whether the calendar popover is open. */
38
+ setOpen(isOpen: boolean): void;
39
+ }
40
+ export interface DatePickerStateOptions extends ValueBase<DateTime>, InputBase {
41
+ placeholderValue?: DateTime;
42
+ timeZone?: string;
43
+ format?: string;
44
+ }
45
+ export declare function useDatePickerState(props: DatePickerStateOptions): DatePickerState;
@@ -0,0 +1,107 @@
1
+ import React from 'react';
2
+ import { createPlaceholderValue, splitFormatIntoSections } from '../../DateField/utils';
3
+ import { useControlledState } from '../../hooks/useControlledState';
4
+ export function useDatePickerState(props) {
5
+ const { disabled, readOnly } = props;
6
+ const [isOpen, setOpen] = React.useState(false);
7
+ const [value, setValue] = useControlledState(props.value, props.defaultValue, props.onUpdate);
8
+ const [selectedDateInner, setSelectedDate] = React.useState(null);
9
+ const [selectedTimeInner, setSelectedTime] = React.useState(null);
10
+ let selectedDate = selectedDateInner;
11
+ let selectedTime = selectedTimeInner;
12
+ const format = props.format || 'L';
13
+ const { hasDate, hasTime, timeFormat } = React.useMemo(() => {
14
+ const sections = splitFormatIntoSections(format);
15
+ let hasDate = false;
16
+ let hasHours = false;
17
+ let hasMinutes = false;
18
+ let hasSeconds = false;
19
+ for (const s of sections) {
20
+ hasHours || (hasHours = s.type === 'hour');
21
+ hasMinutes || (hasMinutes = s.type === 'minute');
22
+ hasSeconds || (hasSeconds = s.type === 'second');
23
+ hasDate || (hasDate = ['day', 'month', 'year'].includes(s.type));
24
+ }
25
+ return {
26
+ hasTime: hasHours || hasMinutes || hasSeconds,
27
+ timeFormat: hasSeconds ? 'LTS' : 'LT',
28
+ hasDate,
29
+ };
30
+ }, [format]);
31
+ if (value) {
32
+ selectedDate = value;
33
+ if (hasTime) {
34
+ selectedTime = value;
35
+ }
36
+ }
37
+ const commitValue = (date, time) => {
38
+ if (disabled || readOnly) {
39
+ return;
40
+ }
41
+ setValue(mergeDateTime(date, time));
42
+ setSelectedDate(null);
43
+ setSelectedTime(null);
44
+ };
45
+ // Intercept setValue to make sure the Time section is not changed by date selection in Calendar
46
+ const selectDate = (newValue) => {
47
+ if (disabled || readOnly) {
48
+ return;
49
+ }
50
+ const shouldClose = !hasTime;
51
+ if (hasTime) {
52
+ if (selectedTime || shouldClose) {
53
+ commitValue(newValue, selectedTime || getPlaceholderTime(props.placeholderValue, props.timeZone));
54
+ }
55
+ else {
56
+ setSelectedDate(newValue);
57
+ }
58
+ }
59
+ else {
60
+ setValue(newValue);
61
+ }
62
+ if (shouldClose) {
63
+ setOpen(false);
64
+ }
65
+ };
66
+ const selectTime = (newValue) => {
67
+ if (disabled || readOnly) {
68
+ return;
69
+ }
70
+ if (selectedDate) {
71
+ commitValue(selectedDate, newValue);
72
+ }
73
+ else {
74
+ setSelectedTime(newValue);
75
+ }
76
+ };
77
+ return {
78
+ value: value !== null && value !== void 0 ? value : null,
79
+ setValue,
80
+ dateValue: selectedDate,
81
+ timeValue: selectedTime,
82
+ setDateValue: selectDate,
83
+ setTimeValue: selectTime,
84
+ disabled,
85
+ readOnly,
86
+ format,
87
+ hasDate,
88
+ hasTime,
89
+ timeFormat,
90
+ isOpen,
91
+ setOpen(newIsOpen) {
92
+ if (!newIsOpen && !value && selectedDate && hasTime) {
93
+ commitValue(selectedDate, selectedTime || getPlaceholderTime(props.placeholderValue, props.timeZone));
94
+ }
95
+ setOpen(newIsOpen);
96
+ },
97
+ };
98
+ }
99
+ function mergeDateTime(date, time) {
100
+ return date
101
+ .set('hours', time.hour())
102
+ .set('minutes', time.minute())
103
+ .set('seconds', time.second());
104
+ }
105
+ function getPlaceholderTime(placeholderValue, timeZone) {
106
+ return createPlaceholderValue({ placeholderValue, timeZone });
107
+ }
@@ -0,0 +1,3 @@
1
+ {
2
+ "Calendar": "Calendar"
3
+ }
@@ -0,0 +1 @@
1
+ export declare const i18n: (key: "Calendar", params?: import("@gravity-ui/i18n").Params | undefined) => string;
@@ -0,0 +1,5 @@
1
+ import { addComponentKeysets } from '@gravity-ui/uikit/i18n';
2
+ import { NAMESPACE } from '../../../utils/cn';
3
+ import en from './en.json';
4
+ import ru from './ru.json';
5
+ export const i18n = addComponentKeysets({ en, ru }, `${NAMESPACE}date-picker`);
@@ -0,0 +1,3 @@
1
+ {
2
+ "Calendar": "Календарь"
3
+ }
@@ -0,0 +1,2 @@
1
+ export * from './DatePicker';
2
+ export * from './hooks/useDatePickerState';
@@ -0,0 +1,2 @@
1
+ export * from './DatePicker';
2
+ export * from './hooks/useDatePickerState';
@@ -0,0 +1 @@
1
+ export declare const b: import("@bem-react/classname").ClassNameFormatter;
@@ -0,0 +1,2 @@
1
+ import { block } from '../../utils/cn';
2
+ export const b = block('date-picker');
@@ -0,0 +1,16 @@
1
+ .g-date-relative-date-field {
2
+ position: relative;
3
+ display: inline-block;
4
+ }
5
+ .g-date-relative-date-field__field {
6
+ width: 100%;
7
+ }
8
+ .g-date-relative-date-field__popup-content {
9
+ outline: none;
10
+ }
11
+ .g-date-relative-date-field__time-field {
12
+ width: 100%;
13
+ }
14
+ .g-date-relative-date-field__time-field-wrapper {
15
+ padding: 10px;
16
+ }
@@ -0,0 +1,11 @@
1
+ import type { AccessibilityProps, DomProps, FocusableProps, KeyboardEvents, StyleProps, TextInputExtendProps, TextInputProps } from '../types';
2
+ import type { RelativeDateFieldOptions } from './hooks/useRelativeDateFieldState';
3
+ import './RelativeDateField.css';
4
+ export interface RelativeDateFieldProps extends RelativeDateFieldOptions, TextInputProps, TextInputExtendProps, DomProps, StyleProps, AccessibilityProps, FocusableProps, KeyboardEvents {
5
+ /**
6
+ * Show time field in popup
7
+ * @default false
8
+ */
9
+ hasTime?: boolean;
10
+ }
11
+ export declare function RelativeDateField(props: RelativeDateFieldProps): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,30 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import React from 'react';
3
+ import { Popup, TextInput, useFocusWithin, useMobile } from '@gravity-ui/uikit';
4
+ import { block } from '../../utils/cn';
5
+ import { Calendar } from '../Calendar';
6
+ import { DateField } from '../DateField';
7
+ import { useRelativeDateFieldProps } from './hooks/useRelativeDateFieldProps';
8
+ import { useRelativeDateFieldState } from './hooks/useRelativeDateFieldState';
9
+ import './RelativeDateField.css';
10
+ const b = block('relative-date-field');
11
+ export function RelativeDateField(props) {
12
+ const state = useRelativeDateFieldState(props);
13
+ const { inputProps, calendarProps, timeInputProps } = useRelativeDateFieldProps(state, props);
14
+ const [isMobile] = useMobile();
15
+ const anchorRef = React.useRef(null);
16
+ const [isOpen, setOpen] = React.useState(false);
17
+ const { focusWithinProps } = useFocusWithin({
18
+ onBlurWithin: () => {
19
+ setOpen(false);
20
+ },
21
+ isDisabled: isMobile,
22
+ });
23
+ return (_jsxs("div", Object.assign({ role: "group", className: b(null, props.className), style: props.style }, focusWithinProps, { children: [_jsx(TextInput, Object.assign({}, inputProps, { className: b('field'), ref: anchorRef, onFocus: (e) => {
24
+ var _a;
25
+ if (!isMobile) {
26
+ setOpen(true);
27
+ }
28
+ (_a = props.onFocus) === null || _a === void 0 ? void 0 : _a.call(props, e);
29
+ }, onBlur: props.onBlur })), !isMobile && (_jsx(Popup, { anchorRef: anchorRef, open: isOpen, children: _jsxs("div", { className: b('popup-content'), children: [_jsx(Calendar, Object.assign({}, calendarProps)), props.hasTime ? (_jsx("div", { className: b('time-field-wrapper'), children: _jsx(DateField, Object.assign({ className: b('time-field') }, timeInputProps)) })) : null] }) }))] })));
30
+ }
@@ -0,0 +1,12 @@
1
+ import type { TextInputProps } from '@gravity-ui/uikit';
2
+ import type { CalendarProps } from '../../Calendar';
3
+ import type { DateFieldProps } from '../../DateField';
4
+ import type { RelativeDateFieldProps } from '../RelativeDateField';
5
+ import type { RelativeDateFieldState } from './useRelativeDateFieldState';
6
+ interface RelativeDateProps {
7
+ inputProps: TextInputProps;
8
+ calendarProps: CalendarProps;
9
+ timeInputProps: DateFieldProps;
10
+ }
11
+ export declare function useRelativeDateFieldProps(state: RelativeDateFieldState, props: RelativeDateFieldProps): RelativeDateProps;
12
+ export {};
@@ -0,0 +1,59 @@
1
+ import React from 'react';
2
+ export function useRelativeDateFieldProps(state, props) {
3
+ const [prevCorrectDate, setPrevCorrectDate] = React.useState(state.lastCorrectDate);
4
+ const [focusedDate, setFocusedDate] = React.useState(state.lastCorrectDate);
5
+ if (prevCorrectDate !== state.lastCorrectDate) {
6
+ setPrevCorrectDate(state.lastCorrectDate);
7
+ setFocusedDate(state.lastCorrectDate);
8
+ }
9
+ let error;
10
+ if (props.validationState === 'invalid') {
11
+ error = props.errorMessage || true;
12
+ }
13
+ else {
14
+ error = state.validationState === 'invalid';
15
+ }
16
+ return {
17
+ inputProps: {
18
+ size: props.size,
19
+ autoFocus: props.autoFocus,
20
+ value: state.text,
21
+ onUpdate: state.setText,
22
+ disabled: state.disabled,
23
+ hasClear: props.hasClear,
24
+ //@ts-expect-error TODO: use new TextInput API
25
+ error,
26
+ label: props.label,
27
+ id: props.id,
28
+ leftContent: props.leftContent,
29
+ rightContent: props.rightContent,
30
+ pin: props.pin,
31
+ view: props.view,
32
+ placeholder: props.placeholder,
33
+ onKeyDown: props.onKeyDown,
34
+ onKeyUp: props.onKeyUp,
35
+ onBlur: props.onBlur,
36
+ onFocus: props.onFocus,
37
+ controlProps: {
38
+ 'aria-label': props['aria-label'] || undefined,
39
+ 'aria-labelledby': props['aria-labelledby'] || undefined,
40
+ 'aria-describedby': props['aria-describedby'] || undefined,
41
+ 'aria-details': props['aria-details'] || undefined,
42
+ 'aria-disabled': state.disabled || undefined,
43
+ },
44
+ },
45
+ calendarProps: {
46
+ size: props.size === 's' ? 'm' : props.size,
47
+ readOnly: true,
48
+ value: state.parsedDate,
49
+ focusedValue: focusedDate,
50
+ onFocusUpdate: setFocusedDate,
51
+ },
52
+ timeInputProps: {
53
+ size: props.size,
54
+ readOnly: true,
55
+ value: state.lastCorrectDate,
56
+ format: 'LTS',
57
+ },
58
+ };
59
+ }
@@ -0,0 +1,30 @@
1
+ import type { DateTime } from '@gravity-ui/date-utils';
2
+ import type { InputBase, Validation, ValueBase } from '../../types';
3
+ export interface RelativeDateFieldState {
4
+ /** The current field value. */
5
+ value: string | null;
6
+ /** Sets the field value */
7
+ setValue: (v: string | null) => void;
8
+ /** Current user input */
9
+ text: string;
10
+ /** Sets text */
11
+ setText: (t: string) => void;
12
+ /** */
13
+ parsedDate: DateTime | null;
14
+ /** */
15
+ lastCorrectDate: DateTime | null;
16
+ /** */
17
+ validationState?: 'invalid';
18
+ /**
19
+ * Whether the field is disabled.
20
+ */
21
+ disabled?: boolean;
22
+ /**
23
+ * Whether the value is immutable.
24
+ */
25
+ readOnly?: boolean;
26
+ }
27
+ export interface RelativeDateFieldOptions extends ValueBase<string, string | null>, InputBase, Validation {
28
+ timeZone?: string;
29
+ }
30
+ export declare function useRelativeDateFieldState(props: RelativeDateFieldOptions): RelativeDateFieldState;
@@ -0,0 +1,59 @@
1
+ import React from 'react';
2
+ import { dateTimeParse, isValid } from '@gravity-ui/date-utils';
3
+ import { useControlledState } from '../../hooks/useControlledState';
4
+ export function useRelativeDateFieldState(props) {
5
+ const [value, setValue] = useControlledState(props.value, props.defaultValue, props.onUpdate);
6
+ const [text, setText] = React.useState(value !== null && value !== void 0 ? value : '');
7
+ if (value && value !== text) {
8
+ setText(value);
9
+ }
10
+ const handleTextChange = (t) => {
11
+ if (props.disabled || props.readOnly) {
12
+ return;
13
+ }
14
+ setText(t);
15
+ if (isLikeRelativeDate(t)) {
16
+ const date = dateTimeParse(t);
17
+ if (date && isValid(date)) {
18
+ setValue(t);
19
+ }
20
+ else {
21
+ setValue(null);
22
+ }
23
+ }
24
+ else {
25
+ setValue(null);
26
+ }
27
+ };
28
+ const parsedDate = React.useMemo(() => {
29
+ var _a;
30
+ if (!value) {
31
+ return null;
32
+ }
33
+ return (_a = dateTimeParse(value, { timeZone: props.timeZone })) !== null && _a !== void 0 ? _a : null;
34
+ }, [value, props.timeZone]);
35
+ const [lastCorrectDate, setLastCorrectDate] = React.useState(parsedDate);
36
+ if (parsedDate && parsedDate !== lastCorrectDate) {
37
+ setLastCorrectDate(parsedDate);
38
+ }
39
+ const validationState = props.validationState || (text && !parsedDate) ? 'invalid' : undefined;
40
+ return {
41
+ value: value !== null && value !== void 0 ? value : null,
42
+ setValue(v) {
43
+ if (props.disabled || props.readOnly) {
44
+ return;
45
+ }
46
+ setValue(v);
47
+ },
48
+ text,
49
+ setText: handleTextChange,
50
+ parsedDate,
51
+ lastCorrectDate,
52
+ validationState,
53
+ disabled: props.disabled,
54
+ readOnly: props.readOnly,
55
+ };
56
+ }
57
+ function isLikeRelativeDate(text) {
58
+ return /^now/i.test(text);
59
+ }
@@ -0,0 +1,3 @@
1
+ export * from './RelativeDateField';
2
+ export * from './hooks/useRelativeDateFieldProps';
3
+ export * from './hooks/useRelativeDateFieldState';
@@ -0,0 +1,3 @@
1
+ export * from './RelativeDateField';
2
+ export * from './hooks/useRelativeDateFieldProps';
3
+ export * from './hooks/useRelativeDateFieldState';
@@ -0,0 +1,16 @@
1
+ .g-date-relative-date-picker {
2
+ position: relative;
3
+ display: inline-flex;
4
+ }
5
+ .g-date-relative-date-picker__field {
6
+ width: 100%;
7
+ }
8
+ .g-date-relative-date-picker__popup-content {
9
+ outline: none;
10
+ }
11
+ .g-date-relative-date-picker__time-field {
12
+ width: 100%;
13
+ }
14
+ .g-date-relative-date-picker__time-field-wrapper {
15
+ padding: 10px;
16
+ }
@@ -0,0 +1,6 @@
1
+ import type { AccessibilityProps, DomProps, FocusableProps, KeyboardEvents, StyleProps, TextInputProps } from '../types';
2
+ import type { RelativeDatePickerStateOptions } from './hooks/useRelativeDatePickerState';
3
+ import './RelativeDatePicker.css';
4
+ export interface RelativeDatePickerProps extends RelativeDatePickerStateOptions, TextInputProps, FocusableProps, KeyboardEvents, DomProps, StyleProps, AccessibilityProps {
5
+ }
6
+ export declare function RelativeDatePicker(props: RelativeDatePickerProps): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,25 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import React from 'react';
3
+ import { Calendar as CalendarIcon, Function as FunctionIcon } from '@gravity-ui/icons';
4
+ import { Button, Icon, Popup, TextInput, useMobile } from '@gravity-ui/uikit';
5
+ import { block } from '../../utils/cn';
6
+ import { Calendar } from '../Calendar';
7
+ import { DateField } from '../DateField';
8
+ import { MobileCalendar, MobileCalendarIcon } from '../DatePicker/MobileCalendar';
9
+ import { useRelativeDatePickerProps } from './hooks/useRelativeDatePickerProps';
10
+ import { useRelativeDatePickerState } from './hooks/useRelativeDatePickerState';
11
+ import './RelativeDatePicker.css';
12
+ const b = block('relative-date-picker');
13
+ export function RelativeDatePicker(props) {
14
+ const state = useRelativeDatePickerState(props);
15
+ const { groupProps, fieldProps, modeSwitcherProps, calendarButtonProps, popupProps, calendarProps, timeInputProps, } = useRelativeDatePickerProps(state, props);
16
+ const anchorRef = React.useRef(null);
17
+ const [isMobile] = useMobile();
18
+ return (_jsxs("div", Object.assign({ ref: anchorRef, className: b(null, props.className) }, groupProps, { children: [isMobile && state.mode === 'absolute' && (_jsx(MobileCalendar, { state: state.datePickerState, props: {
19
+ id: props.id,
20
+ disabled: props.disabled,
21
+ readOnly: props.readOnly,
22
+ placeholderValue: props.placeholderValue,
23
+ timeZone: props.timeZone,
24
+ } })), _jsx(TextInput, Object.assign({}, fieldProps, { leftContent: _jsx(Button, Object.assign({}, modeSwitcherProps, { children: _jsx(Icon, { data: FunctionIcon }) })), rightContent: _jsxs(React.Fragment, { children: [!isMobile && (_jsx(Button, Object.assign({}, calendarButtonProps, { children: _jsx(Icon, { data: CalendarIcon }) }))), isMobile && state.mode === 'absolute' && (_jsx(MobileCalendarIcon, { state: state.datePickerState, props: { size: props.size } }))] }) })), !isMobile && (_jsx(Popup, Object.assign({}, popupProps, { anchorRef: anchorRef, children: _jsxs("div", { className: b('popup-content'), children: [_jsx(Calendar, Object.assign({}, calendarProps)), state.datePickerState.hasTime && (_jsx("div", { className: b('time-field-wrapper'), children: _jsx(DateField, Object.assign({}, timeInputProps)) }))] }) })))] })));
25
+ }
@@ -0,0 +1,17 @@
1
+ import React from 'react';
2
+ import type { ButtonProps, PopupProps, TextInputProps } from '@gravity-ui/uikit';
3
+ import type { Calendar } from '../../Calendar';
4
+ import type { DateFieldProps } from '../../DateField';
5
+ import type { RelativeDatePickerProps } from '../RelativeDatePicker';
6
+ import type { RelativeDatePickerState } from './useRelativeDatePickerState';
7
+ interface InnerRelativeDatePickerProps {
8
+ groupProps: React.HTMLAttributes<unknown>;
9
+ fieldProps: TextInputProps;
10
+ modeSwitcherProps: ButtonProps;
11
+ calendarButtonProps: ButtonProps;
12
+ popupProps: PopupProps;
13
+ calendarProps: React.ComponentProps<typeof Calendar>;
14
+ timeInputProps: DateFieldProps;
15
+ }
16
+ export declare function useRelativeDatePickerProps(state: RelativeDatePickerState, { onFocus, onBlur, ...props }: RelativeDatePickerProps): InnerRelativeDatePickerProps;
17
+ export {};
@@ -0,0 +1,168 @@
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 React from 'react';
13
+ import { useFocusWithin, useForkRef } from '@gravity-ui/uikit';
14
+ import { useDateFieldProps } from '../../DateField';
15
+ import { getButtonSizeForInput } from '../../utils/getButtonSizeForInput';
16
+ import { mergeProps } from '../../utils/mergeProps';
17
+ import { i18n } from '../i18n';
18
+ export function useRelativeDatePickerProps(state, _a) {
19
+ var { onFocus, onBlur } = _a, props = __rest(_a, ["onFocus", "onBlur"]);
20
+ const { mode, setMode, datePickerState, dateFieldState, relativeDateState } = state;
21
+ const [focusedDate, setFocusedDate] = React.useState(relativeDateState.lastCorrectDate);
22
+ const [prevCorrectDate, setPrevCorrectDate] = React.useState(relativeDateState.lastCorrectDate);
23
+ if (prevCorrectDate !== relativeDateState.lastCorrectDate) {
24
+ setPrevCorrectDate(relativeDateState.lastCorrectDate);
25
+ setFocusedDate(relativeDateState.lastCorrectDate);
26
+ }
27
+ const [prevDateValue, setPrevDateValue] = React.useState(datePickerState.dateValue);
28
+ if (datePickerState.dateValue !== prevDateValue) {
29
+ setPrevDateValue(datePickerState.dateValue);
30
+ if (datePickerState.dateValue) {
31
+ setFocusedDate(datePickerState.dateValue);
32
+ }
33
+ }
34
+ const { focusWithinProps } = useFocusWithin({
35
+ onFocusWithin: onFocus,
36
+ onBlurWithin: onBlur,
37
+ onFocusWithinChange(isFocusWithin) {
38
+ if (!isFocusWithin) {
39
+ state.setActive(isFocusWithin);
40
+ }
41
+ },
42
+ });
43
+ const [isOpen, setOpen] = React.useState(false);
44
+ if (!state.isActive && isOpen) {
45
+ setOpen(false);
46
+ }
47
+ const [prevActive, setPrevActive] = React.useState(state.isActive);
48
+ if (prevActive !== state.isActive) {
49
+ setPrevActive(state.isActive);
50
+ if (state.isActive && !isOpen) {
51
+ setOpen(true);
52
+ }
53
+ }
54
+ const commonInputProps = {
55
+ onFocus: () => {
56
+ state.setActive(true);
57
+ },
58
+ };
59
+ const { inputProps } = useDateFieldProps(dateFieldState, Object.assign(Object.assign({}, props), { value: undefined, defaultValue: undefined, onUpdate: undefined }));
60
+ const relativeDateProps = {
61
+ disabled: relativeDateState.disabled,
62
+ value: relativeDateState.text,
63
+ onUpdate: relativeDateState.setText,
64
+ hasClear: props.hasClear && !relativeDateState.readOnly,
65
+ size: props.size,
66
+ };
67
+ let error;
68
+ let validationState = props.validationState;
69
+ if (validationState) {
70
+ error = validationState === 'invalid' ? props.errorMessage || true : undefined;
71
+ }
72
+ else {
73
+ validationState =
74
+ mode === 'relative'
75
+ ? relativeDateState.validationState
76
+ : dateFieldState.validationState;
77
+ error = validationState === 'invalid';
78
+ }
79
+ const wasActiveBeforeClickRef = React.useRef(state.isActive);
80
+ const inputRef = React.useRef(null);
81
+ const handleRef = useForkRef(inputRef, mode === 'relative' ? relativeDateProps.controlRef : inputProps.controlRef);
82
+ const calendarRef = React.useRef(null);
83
+ return {
84
+ groupProps: Object.assign({ role: 'group' }, focusWithinProps),
85
+ fieldProps: mergeProps(commonInputProps, mode === 'relative' ? relativeDateProps : inputProps, { controlRef: handleRef, error }),
86
+ modeSwitcherProps: {
87
+ size: getButtonSizeForInput(props.size),
88
+ disabled: state.readOnly || state.disabled,
89
+ view: 'flat-secondary',
90
+ style: { zIndex: 2 },
91
+ selected: mode === 'relative',
92
+ extraProps: {
93
+ 'aria-label': i18n('Formula input mode'),
94
+ },
95
+ onClick: () => {
96
+ setMode(mode === 'relative' ? 'absolute' : 'relative');
97
+ if (mode === 'relative') {
98
+ const valueDate = datePickerState.value;
99
+ if (valueDate) {
100
+ setFocusedDate(valueDate);
101
+ }
102
+ }
103
+ else if (relativeDateState.parsedDate) {
104
+ setFocusedDate(relativeDateState.parsedDate);
105
+ }
106
+ setTimeout(() => { var _a; return (_a = inputRef.current) === null || _a === void 0 ? void 0 : _a.focus(); });
107
+ },
108
+ },
109
+ calendarButtonProps: {
110
+ size: getButtonSizeForInput(props.size),
111
+ disabled: state.disabled,
112
+ extraProps: {
113
+ 'aria-label': i18n('Calendar'),
114
+ 'aria-haspopup': 'dialog',
115
+ 'aria-expanded': isOpen,
116
+ },
117
+ view: 'flat-secondary',
118
+ onFocus: () => {
119
+ wasActiveBeforeClickRef.current = state.isActive;
120
+ },
121
+ onClick: () => {
122
+ state.setActive(true);
123
+ if (wasActiveBeforeClickRef.current) {
124
+ setOpen(!isOpen);
125
+ if (!isOpen) {
126
+ setTimeout(() => {
127
+ var _a;
128
+ (_a = calendarRef.current) === null || _a === void 0 ? void 0 : _a.focus();
129
+ });
130
+ }
131
+ }
132
+ wasActiveBeforeClickRef.current = state.isActive;
133
+ },
134
+ },
135
+ popupProps: {
136
+ open: isOpen,
137
+ onEscapeKeyDown: () => {
138
+ setOpen(false);
139
+ },
140
+ restoreFocus: true,
141
+ },
142
+ calendarProps: {
143
+ ref: calendarRef,
144
+ size: props.size === 's' ? 'm' : props.size,
145
+ readOnly: props.readOnly,
146
+ value: state.selectedDate,
147
+ onUpdate: (v) => {
148
+ datePickerState.setDateValue(v);
149
+ if (!state.datePickerState.hasTime) {
150
+ setOpen(false);
151
+ setTimeout(() => { var _a; return (_a = inputRef.current) === null || _a === void 0 ? void 0 : _a.focus(); });
152
+ }
153
+ },
154
+ focusedValue: focusedDate,
155
+ onFocusUpdate: setFocusedDate,
156
+ },
157
+ timeInputProps: {
158
+ value: datePickerState.timeValue,
159
+ onUpdate: datePickerState.setTimeValue,
160
+ format: datePickerState.timeFormat,
161
+ readOnly: state.readOnly,
162
+ disabled: state.disabled,
163
+ timeZone: props.timeZone,
164
+ hasClear: props.hasClear,
165
+ size: props.size,
166
+ },
167
+ };
168
+ }