@os-design/core 1.0.280 → 1.0.282

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 (198) hide show
  1. package/dist/Avatar/utils/nameToInitials.d.ts.map +1 -1
  2. package/dist/Avatar/utils/strToHue.d.ts.map +1 -1
  3. package/dist/Button/index.d.ts.map +1 -1
  4. package/dist/Button/index.js +1 -1
  5. package/dist/Button/utils/useButtonColors.d.ts.map +1 -1
  6. package/dist/ButtonLink/index.d.ts.map +1 -1
  7. package/dist/ButtonLink/index.js +0 -5
  8. package/dist/Checkbox/index.d.ts.map +1 -1
  9. package/dist/Checkbox/index.js +1 -6
  10. package/dist/DateCalendar/Calendar.d.ts +25 -0
  11. package/dist/DateCalendar/Calendar.d.ts.map +1 -0
  12. package/dist/DateCalendar/Calendar.js +271 -0
  13. package/dist/DateCalendar/MonthPicker.d.ts +12 -0
  14. package/dist/DateCalendar/MonthPicker.d.ts.map +1 -0
  15. package/dist/DateCalendar/MonthPicker.js +159 -0
  16. package/dist/DateCalendar/index.d.ts +41 -0
  17. package/dist/DateCalendar/index.d.ts.map +1 -0
  18. package/dist/DateCalendar/index.js +77 -0
  19. package/dist/DateCalendar/locale.d.ts +6 -0
  20. package/dist/DateCalendar/locale.d.ts.map +1 -0
  21. package/dist/DateCalendar/locale.js +4 -0
  22. package/dist/DateCalendar/utils/calendarDays.d.ts +10 -0
  23. package/dist/DateCalendar/utils/calendarDays.d.ts.map +1 -0
  24. package/dist/DateCalendar/utils/calendarDays.js +46 -0
  25. package/dist/DateCalendar/utils/dayOfWeek.d.ts +8 -0
  26. package/dist/DateCalendar/utils/dayOfWeek.d.ts.map +1 -0
  27. package/dist/DateCalendar/utils/dayOfWeek.js +6 -0
  28. package/dist/DateCalendar/utils/daysInMonth.d.ts +7 -0
  29. package/dist/DateCalendar/utils/daysInMonth.d.ts.map +1 -0
  30. package/dist/DateCalendar/utils/daysInMonth.js +14 -0
  31. package/dist/DateCalendar/utils/month.d.ts +14 -0
  32. package/dist/DateCalendar/utils/month.d.ts.map +1 -0
  33. package/dist/DateCalendar/utils/month.js +24 -0
  34. package/dist/DateCalendar/utils/shift.d.ts +3 -0
  35. package/dist/DateCalendar/utils/shift.d.ts.map +1 -0
  36. package/dist/DateCalendar/utils/shift.js +12 -0
  37. package/dist/DatePicker/index.d.ts +68 -62
  38. package/dist/DatePicker/index.d.ts.map +1 -1
  39. package/dist/DatePicker/index.js +359 -265
  40. package/dist/DatePicker/utils/createTimes.d.ts +7 -0
  41. package/dist/DatePicker/utils/createTimes.d.ts.map +1 -0
  42. package/dist/DatePicker/utils/createTimes.js +15 -0
  43. package/dist/GlobalStyles/resetStyles.d.ts.map +1 -1
  44. package/dist/GlobalStyles/typographyStyles.d.ts.map +1 -1
  45. package/dist/Input/index.d.ts +15 -0
  46. package/dist/Input/index.d.ts.map +1 -1
  47. package/dist/Input/index.js +5 -5
  48. package/dist/Input/utils/getFocusableElements.d.ts.map +1 -1
  49. package/dist/InputDateUnstyled/index.d.ts +94 -0
  50. package/dist/InputDateUnstyled/index.d.ts.map +1 -0
  51. package/dist/InputDateUnstyled/index.js +406 -0
  52. package/dist/InputDateUnstyled/utils/convertHours.d.ts +4 -0
  53. package/dist/InputDateUnstyled/utils/convertHours.d.ts.map +1 -0
  54. package/dist/InputDateUnstyled/utils/convertHours.js +12 -0
  55. package/dist/InputDateUnstyled/utils/convertToFullYear.d.ts +3 -0
  56. package/dist/InputDateUnstyled/utils/convertToFullYear.d.ts.map +1 -0
  57. package/dist/InputDateUnstyled/utils/convertToFullYear.js +10 -0
  58. package/dist/InputDateUnstyled/utils/dateToString.d.ts +3 -0
  59. package/dist/InputDateUnstyled/utils/dateToString.d.ts.map +1 -0
  60. package/dist/InputDateUnstyled/utils/dateToString.js +10 -0
  61. package/dist/InputDateUnstyled/utils/daysInMonth.d.ts +7 -0
  62. package/dist/InputDateUnstyled/utils/daysInMonth.d.ts.map +1 -0
  63. package/dist/InputDateUnstyled/utils/daysInMonth.js +14 -0
  64. package/dist/InputDateUnstyled/utils/ensureCaretVisible.d.ts +3 -0
  65. package/dist/InputDateUnstyled/utils/ensureCaretVisible.d.ts.map +1 -0
  66. package/dist/InputDateUnstyled/utils/ensureCaretVisible.js +32 -0
  67. package/dist/InputDateUnstyled/utils/eraseSelectedTokens.d.ts +10 -0
  68. package/dist/InputDateUnstyled/utils/eraseSelectedTokens.d.ts.map +1 -0
  69. package/dist/InputDateUnstyled/utils/eraseSelectedTokens.js +29 -0
  70. package/dist/InputDateUnstyled/utils/replaceSubstring.d.ts +3 -0
  71. package/dist/InputDateUnstyled/utils/replaceSubstring.d.ts.map +1 -0
  72. package/dist/InputDateUnstyled/utils/replaceSubstring.js +9 -0
  73. package/dist/InputDateUnstyled/utils/same.d.ts +6 -0
  74. package/dist/InputDateUnstyled/utils/same.d.ts.map +1 -0
  75. package/dist/InputDateUnstyled/utils/same.js +3 -0
  76. package/dist/InputDateUnstyled/utils/stringToDay.d.ts +12 -0
  77. package/dist/InputDateUnstyled/utils/stringToDay.d.ts.map +1 -0
  78. package/dist/InputDateUnstyled/utils/stringToDay.js +55 -0
  79. package/dist/InputDateUnstyled/utils/stringToTime.d.ts +7 -0
  80. package/dist/InputDateUnstyled/utils/stringToTime.d.ts.map +1 -0
  81. package/dist/InputDateUnstyled/utils/stringToTime.js +40 -0
  82. package/dist/InputDateUnstyled/utils/token.d.ts +9 -0
  83. package/dist/InputDateUnstyled/utils/token.d.ts.map +1 -0
  84. package/dist/InputDateUnstyled/utils/token.js +66 -0
  85. package/dist/Link/index.d.ts.map +1 -1
  86. package/dist/Link/index.js +3 -8
  87. package/dist/LinkButton/index.d.ts.map +1 -1
  88. package/dist/LinkButton/index.js +0 -5
  89. package/dist/List/utils/bodyPointerEvents.d.ts.map +1 -1
  90. package/dist/List/utils/frameTimeout.d.ts.map +1 -1
  91. package/dist/List/utils/useRWLoadNext.d.ts.map +1 -1
  92. package/dist/LogoLink/index.d.ts.map +1 -1
  93. package/dist/LogoLink/index.js +1 -6
  94. package/dist/Menu/utils/useFocusWithArrows.d.ts.map +1 -1
  95. package/dist/Modal/index.d.ts +5 -0
  96. package/dist/Modal/index.d.ts.map +1 -1
  97. package/dist/Modal/index.js +53 -48
  98. package/dist/Navigation/utils/useScrollFlags.d.ts.map +1 -1
  99. package/dist/NavigationItem/index.d.ts.map +1 -1
  100. package/dist/NavigationItem/index.js +1 -6
  101. package/dist/Popover/utils/usePopoverPosition.d.ts.map +1 -1
  102. package/dist/ScrollButton/utils/useContainerPosition.d.ts.map +1 -1
  103. package/dist/ScrollButton/utils/useVisibility.d.ts.map +1 -1
  104. package/dist/Select/index.d.ts.map +1 -1
  105. package/dist/Select/index.js +2 -3
  106. package/dist/Switch/index.d.ts.map +1 -1
  107. package/dist/Switch/index.js +1 -7
  108. package/dist/TagLink/index.d.ts.map +1 -1
  109. package/dist/TagLink/index.js +1 -6
  110. package/dist/TimeGrid/index.d.ts +63 -0
  111. package/dist/TimeGrid/index.d.ts.map +1 -0
  112. package/dist/TimeGrid/index.js +111 -0
  113. package/dist/TimeGrid/utils/convertHours.d.ts +4 -0
  114. package/dist/TimeGrid/utils/convertHours.d.ts.map +1 -0
  115. package/dist/TimeGrid/utils/convertHours.js +12 -0
  116. package/dist/TimeGrid/utils/createTimes.d.ts +7 -0
  117. package/dist/TimeGrid/utils/createTimes.d.ts.map +1 -0
  118. package/dist/TimeGrid/utils/createTimes.js +15 -0
  119. package/dist/TimeGrid/utils/timeToString.d.ts +4 -0
  120. package/dist/TimeGrid/utils/timeToString.d.ts.map +1 -0
  121. package/dist/TimeGrid/utils/timeToString.js +12 -0
  122. package/dist/TimeGridSkeleton/index.d.ts +18 -0
  123. package/dist/TimeGridSkeleton/index.d.ts.map +1 -0
  124. package/dist/TimeGridSkeleton/index.js +33 -0
  125. package/dist/TimeList/index.d.ts +45 -0
  126. package/dist/TimeList/index.d.ts.map +1 -0
  127. package/dist/TimeList/index.js +80 -0
  128. package/dist/TimeList/utils/convertHours.d.ts +4 -0
  129. package/dist/TimeList/utils/convertHours.d.ts.map +1 -0
  130. package/dist/TimeList/utils/convertHours.js +12 -0
  131. package/dist/TimeList/utils/createTimes.d.ts +7 -0
  132. package/dist/TimeList/utils/createTimes.d.ts.map +1 -0
  133. package/dist/TimeList/utils/createTimes.js +15 -0
  134. package/dist/TimeList/utils/timeToString.d.ts +4 -0
  135. package/dist/TimeList/utils/timeToString.d.ts.map +1 -0
  136. package/dist/TimeList/utils/timeToString.js +12 -0
  137. package/dist/TimeListSkeleton/index.d.ts +13 -0
  138. package/dist/TimeListSkeleton/index.d.ts.map +1 -0
  139. package/dist/TimeListSkeleton/index.js +30 -0
  140. package/dist/index.d.ts +12 -0
  141. package/dist/index.d.ts.map +1 -1
  142. package/dist/index.js +12 -0
  143. package/dist/message/styles.d.ts.map +1 -1
  144. package/package.json +8 -8
  145. package/src/Button/index.tsx +1 -1
  146. package/src/ButtonLink/index.tsx +0 -5
  147. package/src/Checkbox/index.tsx +1 -6
  148. package/src/DateCalendar/Calendar.tsx +400 -0
  149. package/src/DateCalendar/MonthPicker.tsx +212 -0
  150. package/src/DateCalendar/index.tsx +135 -0
  151. package/src/DateCalendar/locale.ts +22 -0
  152. package/src/DateCalendar/utils/calendarDays.ts +61 -0
  153. package/src/DateCalendar/utils/dayOfWeek.ts +14 -0
  154. package/src/DateCalendar/utils/daysInMonth.ts +22 -0
  155. package/src/DateCalendar/utils/month.ts +30 -0
  156. package/src/DateCalendar/utils/shift.ts +14 -0
  157. package/src/DatePicker/index.tsx +506 -417
  158. package/src/DatePicker/utils/createTimes.ts +20 -0
  159. package/src/Input/index.tsx +11 -8
  160. package/src/InputDateUnstyled/index.tsx +533 -0
  161. package/src/InputDateUnstyled/utils/convertHours.ts +15 -0
  162. package/src/InputDateUnstyled/utils/convertToFullYear.ts +11 -0
  163. package/src/InputDateUnstyled/utils/dateToString.ts +21 -0
  164. package/src/InputDateUnstyled/utils/daysInMonth.ts +22 -0
  165. package/src/InputDateUnstyled/utils/ensureCaretVisible.ts +37 -0
  166. package/src/InputDateUnstyled/utils/eraseSelectedTokens.ts +38 -0
  167. package/src/InputDateUnstyled/utils/replaceSubstring.ts +10 -0
  168. package/src/InputDateUnstyled/utils/same.ts +15 -0
  169. package/src/InputDateUnstyled/utils/stringToDay.ts +69 -0
  170. package/src/InputDateUnstyled/utils/stringToTime.ts +48 -0
  171. package/src/InputDateUnstyled/utils/token.ts +102 -0
  172. package/src/Link/index.tsx +5 -25
  173. package/src/LinkButton/index.tsx +2 -15
  174. package/src/LogoLink/index.tsx +2 -6
  175. package/src/Modal/index.tsx +71 -60
  176. package/src/NavigationItem/index.tsx +2 -16
  177. package/src/Select/index.tsx +2 -3
  178. package/src/Switch/index.tsx +1 -11
  179. package/src/TagLink/index.tsx +3 -11
  180. package/src/TimeGrid/index.tsx +189 -0
  181. package/src/TimeGrid/utils/convertHours.ts +15 -0
  182. package/src/TimeGrid/utils/createTimes.ts +20 -0
  183. package/src/TimeGrid/utils/timeToString.ts +17 -0
  184. package/src/TimeGridSkeleton/index.tsx +50 -0
  185. package/src/TimeList/index.tsx +135 -0
  186. package/src/TimeList/utils/convertHours.ts +15 -0
  187. package/src/TimeList/utils/createTimes.ts +20 -0
  188. package/src/TimeList/utils/timeToString.ts +17 -0
  189. package/src/TimeListSkeleton/index.tsx +44 -0
  190. package/src/index.ts +12 -0
  191. package/dist/DatePicker/DatePickerCalendar.d.ts +0 -11
  192. package/dist/DatePicker/DatePickerCalendar.d.ts.map +0 -1
  193. package/dist/DatePicker/DatePickerCalendar.js +0 -178
  194. package/dist/TimePicker/index.d.ts +0 -29
  195. package/dist/TimePicker/index.d.ts.map +0 -1
  196. package/dist/TimePicker/index.js +0 -100
  197. package/src/DatePicker/DatePickerCalendar.tsx +0 -230
  198. package/src/TimePicker/index.tsx +0 -144
@@ -1,569 +1,658 @@
1
- import styled from '@emotion/styled';
2
- import {
3
- type DatePickerLocale,
4
- defaultLocale,
5
- getAccessibilityDateLabel,
6
- useFormattedValue,
7
- isSameDay,
8
- } from '@os-design/date-picker-utils';
9
- import { Down, Up } from '@os-design/icons';
10
1
  import { useIsMinWidth } from '@os-design/media';
11
- import type { WithSize } from '@os-design/styles';
12
- import { ThemeOverrider } from '@os-design/theming';
13
2
  import {
3
+ enableScrollingStyles,
4
+ resetFocusStyles,
5
+ type WithSize,
6
+ } from '@os-design/styles';
7
+ import {
8
+ omitEmotionProps,
14
9
  useForwardedRef,
15
10
  useForwardedState,
16
- useKeyPress,
17
11
  } from '@os-design/utils';
18
- import React, {
12
+ import {
19
13
  forwardRef,
20
14
  useCallback,
21
15
  useEffect,
22
16
  useMemo,
23
17
  useRef,
24
18
  useState,
19
+ type FocusEvent,
20
+ type ForwardedRef,
21
+ type MouseEvent,
25
22
  } from 'react';
26
- import Button from '../Button/index.js';
27
23
  import Modal from '../Modal/index.js';
24
+ import { type DateCalendarLocale } from '../DateCalendar/locale.js';
25
+ import type { Day, Month } from '../DateCalendar/utils/calendarDays.js';
26
+ import InputDateUnstyled, {
27
+ convertTo12hours,
28
+ nextToken,
29
+ replaceSubstring,
30
+ tokens,
31
+ type OnDayError,
32
+ type Time,
33
+ type Token,
34
+ } from '../InputDateUnstyled/index.js';
35
+ import DateCalendar from '../DateCalendar/index.js';
36
+ import TimeGrid from '../TimeGrid/index.js';
37
+ import styled from '@emotion/styled';
28
38
  import Popover from '../Popover/index.js';
29
- import {
30
- ClearIcon,
31
- Placeholder,
32
- SelectContainer,
33
- Title,
34
- ToggleContent,
35
- ToggleIconContainer,
36
- ToggleLeftAddon,
37
- ToggleRightAddon,
38
- } from '../Select/index.js';
39
- import TimePicker from '../TimePicker/index.js';
40
- import DatePickerCalendar from './DatePickerCalendar.js';
39
+ import Button from '../Button/index.js';
40
+ import { Calendar } from '@os-design/icons';
41
+ import { InputContainer, TimeGridSkeleton } from '../index.js';
42
+ import { clr, ThemeOverrider } from '@os-design/theming';
43
+ import { css } from '@emotion/react';
44
+ import createTimes from './utils/createTimes.js';
45
+
46
+ /* eslint-disable @typescript-eslint/no-explicit-any */
41
47
 
42
48
  type JsxDivProps = Omit<
43
49
  JSX.IntrinsicElements['div'],
44
50
  'value' | 'defaultValue' | 'onChange' | 'ref'
45
51
  >;
46
- interface BaseDatePickerProps<T> extends JsxDivProps, WithSize {
52
+ export interface DatePickerProps extends JsxDivProps, WithSize {
47
53
  /**
48
- * The component located on the left side.
49
- * @default undefined
50
- */
51
- left?: React.ReactNode;
52
- /**
53
- * Adds padding to the left component.
54
- * It can be useful when passing an icon or text in the left component.
55
- * @default false
54
+ * The string format. Supported tokens:
55
+ * `DD` - The day of the month (01-31).
56
+ * `MM` - The month (01-12).
57
+ * `YYYY` - Four-digit year (2025).
58
+ * `YY` - Two-digit year (25).
59
+ * `hh` - The hour, 12-hour clock (01-12).
60
+ * `HH` - The hour, 24-hour clock (00-23).
61
+ * `mm` - The minute (00-59).
62
+ * `aa` - The meridiem (am, pm).
63
+ * @default `DD.MM.YYYY`
56
64
  */
57
- leftHasPadding?: boolean;
65
+ format?: string;
58
66
  /**
59
- * The component located on the right side.
60
- * @default undefined
67
+ * First day of week (0 - Sunday).
68
+ * @default 0
61
69
  */
62
- right?: React.ReactNode;
70
+ firstDayOfWeek?: number;
63
71
  /**
64
- * Adds padding to the right component.
65
- * It can be useful when passing an icon or text in the right component.
66
- * @default false
72
+ * Times shown in the list in minutes (600 is 10:00).
73
+ * @default []
67
74
  */
68
- rightHasPadding?: boolean;
75
+ times?: Time[];
69
76
  /**
70
- * The placeholder of the date picker.
71
- * @default undefined
77
+ * Number of columns where the times are shown.
78
+ * @default 3
72
79
  */
73
- placeholder?: string;
80
+ timeColumns?: number;
74
81
  /**
75
82
  * Whether the date picker is disabled.
76
83
  * @default false
77
84
  */
78
85
  disabled?: boolean;
79
86
  /**
80
- * Whether the component have a focus.
81
- * @default false
87
+ * What days are not allowed to choose in the selected month.
88
+ * @default undefined
82
89
  */
83
- autoFocus?: boolean;
90
+ disabledDays?: (month: Month) => number[] | Promise<number[]>;
84
91
  /**
85
- * Whether the component opens the popup calendar.
86
- * @default false
92
+ * What times are not allowed to choose in the selected day.
93
+ * @default undefined
87
94
  */
88
- autoOpen?: boolean;
95
+ disabledTimes?: (day: Day) => Time[] | Promise<Time[]>;
89
96
  /**
90
- * The format of the date.
97
+ * The locale.
91
98
  * @default undefined
92
99
  */
93
- format?: (value: T) => string;
100
+ locale?: DateCalendarLocale;
94
101
  /**
95
- * The first day of the week.
96
- * @default sunday
102
+ * Whether to show the calendar and time picker.
103
+ * When autoOpen is false, there is a button on the right side. When
104
+ * the user clicks on it, the calendar/time picker is shown. This button
105
+ * is hidden when autoOpen is true on the wide screens.
106
+ * @default false
97
107
  */
98
- firstDayOfWeek?: 'monday' | 'saturday' | 'sunday';
108
+ showPicker?: boolean;
99
109
  /**
100
- * The locale of the date picker.
101
- * @default undefined
110
+ * Whether the calendar and time picker is opened automatically.
111
+ * @default false
102
112
  */
103
- locale?: DatePickerLocale;
113
+ autoOpen?: boolean;
104
114
  /**
105
- * Whether the time is shown.
106
- * @default false
115
+ * The ref of the input container.
116
+ * @default undefined
107
117
  */
108
- showTime?: boolean;
118
+ containerRef?: ForwardedRef<HTMLDivElement>;
109
119
  /**
110
- * The time notation.
111
- * @default 12-hour
120
+ * The props of the input container.
121
+ * @default undefined
112
122
  */
113
- timeNotation?: '12-hour' | '24-hour';
123
+ containerProps?: JSX.IntrinsicElements['div'];
114
124
  /**
115
- * Selected date.
125
+ * The selected date.
116
126
  * @default undefined
117
127
  */
118
- value?: T | null;
128
+ value?: Date | null;
119
129
  /**
120
130
  * The default value.
121
131
  * @default undefined
122
132
  */
123
- defaultValue?: T | null;
133
+ defaultValue?: Date | null;
124
134
  /**
125
135
  * The change event handler.
126
136
  * @default undefined
127
137
  */
128
- onChange?: (value: T | null) => void;
138
+ onChange?: (value: Date | null) => void;
129
139
  /**
130
- * The event handler that is called whenever a popup closes.
140
+ * The callback called when the day is changed.
131
141
  * @default undefined
132
142
  */
133
- onClose?: () => void;
134
- }
135
- export interface DateNotRangePickerProps extends BaseDatePickerProps<Date> {
143
+ onDayChange?: (value: Day) => void;
136
144
  /**
137
- * Whether the value is a range.
138
- * @default false
145
+ * The callback called when the time is changed.
146
+ * @default undefined
139
147
  */
140
- range?: false;
141
- }
142
- export interface DateRangePickerProps
143
- extends BaseDatePickerProps<[Date, Date]> {
148
+ onTimeChange?: (value: Time) => void;
144
149
  /**
145
- * Whether the value is a range.
146
- * @default false
150
+ * The error handler called when the day has been entered incorrectly.
151
+ * @default undefined
147
152
  */
148
- range: true;
153
+ onDayError?: OnDayError;
154
+ /**
155
+ * The callback called when the caret has been moved to the end.
156
+ * @default undefined
157
+ */
158
+ onEnd?: () => void;
149
159
  }
150
- export type DatePickerProps = DateNotRangePickerProps | DateRangePickerProps;
151
160
 
152
- const StyledPopover = styled(Popover)`
153
- padding: ${(p) => p.theme.datePickerPadding}em;
161
+ type PickerType = 'date' | 'time';
162
+
163
+ const dateTokens = ['DD', 'MM', 'YYYY', 'YY'] as const;
164
+ const timeTokens = ['hh', 'HH', 'mm', 'aa'] as const;
165
+
166
+ type DateToken = (typeof dateTokens)[number];
167
+ type TimeToken = (typeof timeTokens)[number];
168
+
169
+ type TokenGetterMap = {
170
+ [P in Token]: (
171
+ value: P extends DateToken ? Day : P extends TimeToken ? Time : unknown
172
+ ) => string;
173
+ };
174
+
175
+ const tokenGetterMap: TokenGetterMap = {
176
+ DD: ({ day }) => day.toString().padStart(2, '0'),
177
+ MM: ({ month }) => (month + 1).toString().padStart(2, '0'),
178
+ YYYY: ({ year }) => year.toString(),
179
+ YY: ({ year }) => year.toString().slice(2, 4),
180
+ hh: ({ hour }) => {
181
+ const [h] = convertTo12hours(hour);
182
+ return h.toString().padStart(2, '0');
183
+ },
184
+ HH: ({ hour }) => hour.toString().padStart(2, '0'),
185
+ mm: ({ minute }) => minute.toString().padStart(2, '0'),
186
+ aa: ({ hour }) => {
187
+ const [, meridiem] = convertTo12hours(hour);
188
+ return meridiem;
189
+ },
190
+ };
191
+
192
+ const getPickerType = (token: Token) => {
193
+ if (dateTokens.includes(token as any)) {
194
+ return 'date';
195
+ }
196
+ if (timeTokens.includes(token as any)) {
197
+ return 'time';
198
+ }
199
+ return null;
200
+ };
201
+
202
+ const getNextTokenDiffType = (startPos: number, format: string) => {
203
+ const current = nextToken(startPos, format, tokens);
204
+ if (!current) return null;
205
+
206
+ const currentType = getPickerType(current.token);
207
+ let pos = current.end;
208
+
209
+ while (pos < format.length) {
210
+ const next = nextToken(pos, format, tokens);
211
+ if (!next) return null;
212
+ const nextType = getPickerType(next.token);
213
+ if (nextType !== currentType) return next;
214
+ pos = next.end;
215
+ }
216
+
217
+ return null;
218
+ };
219
+
220
+ const preventDefault = (e: MouseEvent) => e.preventDefault();
221
+
222
+ const notHasRightStyles = (p) =>
223
+ !p.hasRight &&
224
+ css`
225
+ padding-right: ${p.theme.inputPaddingHorizontal}em;
226
+ `;
227
+
228
+ const hideSpinButton = css`
229
+ /* Chrome, Safari, Edge, Opera */
230
+ &::-webkit-outer-spin-button,
231
+ &::-webkit-inner-spin-button {
232
+ -webkit-appearance: none;
233
+ margin: 0;
234
+ }
235
+ /* Firefox */
236
+ appearance: textfield;
154
237
  `;
155
238
 
156
- const TimeContainer = styled.div`
239
+ interface StyledInputDateUnstyledProps {
240
+ hasRight?: boolean;
241
+ }
242
+ const StyledInputDateUnstyled = styled(
243
+ InputDateUnstyled,
244
+ omitEmotionProps('hasRight')
245
+ )<StyledInputDateUnstyledProps>`
246
+ ${resetFocusStyles};
247
+ border: none;
248
+ font-size: 1em;
249
+ flex: 1;
250
+ width: 100%;
251
+ overflow: hidden;
252
+
253
+ color: ${(p) => clr(p.theme.inputColorText)};
254
+ background-color: transparent;
255
+ padding-left: ${(p) => p.theme.inputPaddingHorizontal}em;
256
+
257
+ &::placeholder {
258
+ color: ${(p) => clr(p.theme.inputColorPlaceholder)};
259
+ }
260
+
261
+ ${hideSpinButton};
262
+ ${notHasRightStyles};
263
+ `;
264
+
265
+ const PickerButtonContainer = styled.div`
157
266
  display: flex;
158
- justify-content: center;
159
- margin-top: 0.5em;
267
+ align-items: center;
160
268
  `;
161
269
 
162
- const Dash = styled.div`
163
- margin: 0 0.5em;
164
- line-height: ${(p) => p.theme.baseHeight}em;
270
+ const PADDING_EM = 0.8;
271
+
272
+ const StyledPopover = styled(Popover)`
273
+ padding: ${PADDING_EM}em;
274
+ width: ${(p) => p.theme.dateCalendarCellSize * 7 + PADDING_EM * 2}em;
275
+ max-height: ${(p) =>
276
+ p.theme.baseHeight * p.theme.sizes.small +
277
+ 0.5 +
278
+ p.theme.dateCalendarCellSize * 7}em;
279
+ ${enableScrollingStyles('y')};
165
280
  `;
166
281
 
167
- const StyledTimePicker = styled(TimePicker)`
168
- width: 2.5em;
169
- text-align: center;
282
+ const StyledModal = styled(Modal)`
283
+ padding: ${PADDING_EM}em;
170
284
  `;
171
285
 
172
- const timePickerStyle = { style: { width: 'auto' } };
173
-
174
- const createDate = (date: Date, time?: Date | null) => {
175
- const d = new Date(0);
176
- d.setFullYear(date.getFullYear());
177
- d.setMonth(date.getMonth());
178
- d.setDate(date.getDate());
179
- d.setHours(0);
180
- d.setMinutes(0);
181
- if (time) {
182
- d.setHours(time.getHours());
183
- d.setMinutes(time.getMinutes());
184
- }
185
- return d;
186
- };
286
+ const defaultTimes = createTimes(
287
+ { hour: 9, minute: 0 },
288
+ { hour: 18, minute: 0 },
289
+ 30
290
+ );
291
+
292
+ interface Selection {
293
+ start: number;
294
+ end: number;
295
+ }
187
296
 
188
297
  /**
189
- * The component to choose a date.
298
+ * The component to enter or choose a date/time.
190
299
  */
191
- const DatePicker = forwardRef<HTMLDivElement, DatePickerProps>(
300
+ const DatePicker = forwardRef<HTMLInputElement, DatePickerProps>(
192
301
  (
193
302
  {
194
- left,
195
- leftHasPadding = false,
196
- right,
197
- rightHasPadding = false,
198
- placeholder,
303
+ format = 'DD.MM.YYYY',
304
+ firstDayOfWeek = 0,
305
+ times = defaultTimes,
306
+ timeColumns = 3,
199
307
  disabled = false,
200
- autoFocus = false,
308
+ disabledDays,
309
+ disabledTimes: disabledTimesFn,
310
+ locale,
311
+ showPicker = false,
201
312
  autoOpen = false,
202
- format,
203
- firstDayOfWeek = 'sunday',
204
- locale = defaultLocale,
205
- showTime = false,
206
- timeNotation = '12-hour',
207
- range = false,
313
+ containerRef,
314
+ containerProps = {},
208
315
  value,
209
316
  defaultValue,
210
317
  onChange,
211
- onClose = () => {},
318
+ onDayChange = () => {},
319
+ onTimeChange = () => {},
320
+ onDayError,
321
+ onFocus = () => {},
212
322
  onBlur = () => {},
323
+ onEnd = () => {},
213
324
  size,
214
325
  ...rest
215
326
  },
216
327
  ref
217
328
  ) => {
218
- const [containerRef, mergedContainerRef] = useForwardedRef(ref);
219
- const [opened, setOpened] = useState(autoOpen);
220
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
221
- const [forwardedValue, setForwardedValue] = useForwardedState<any>({
329
+ const [innerContainerRef, mergedContainerRef] =
330
+ useForwardedRef(containerRef);
331
+ const [innerInputRef, mergedInputRef] = useForwardedRef(ref);
332
+ const [inputValue, setInputValue] = useState<string | undefined>(undefined);
333
+ const [inputSelection, setInputSelection] = useState<Selection>({
334
+ start: 0,
335
+ end: 0,
336
+ });
337
+ const [inputFocused, setInputFocused] = useState(false);
338
+ const [forwardedValue, setForwardedValue] = useForwardedState({
222
339
  value,
223
340
  defaultValue,
224
341
  onChange,
225
342
  });
226
343
 
344
+ const [pickerType, setPickerType] = useState<PickerType>('date');
345
+ const [pickerVisibility, setPickerVisibility] = useState(false);
346
+
347
+ const [day, setDay] = useState<Day | null>(null);
348
+ const [time, setTime] = useState<Time | null>(null);
349
+
350
+ const [loadingTimes, setLoadingTimes] = useState(false);
351
+ const [disabledTimes, setDisabledTimes] = useState<Time[]>([]);
352
+
353
+ const inputValueRef = useRef(inputValue);
354
+ useEffect(() => {
355
+ inputValueRef.current = inputValue;
356
+ }, [inputValue]);
357
+
358
+ const inputSelectionRef = useRef(inputSelection);
359
+ useEffect(() => {
360
+ inputSelectionRef.current = inputSelection;
361
+ }, [inputSelection]);
362
+
363
+ const forwardedValueRef = useRef(forwardedValue);
364
+ useEffect(() => {
365
+ forwardedValueRef.current = forwardedValue;
366
+ }, [forwardedValue]);
367
+
368
+ const formatRef = useRef(format);
369
+ useEffect(() => {
370
+ formatRef.current = format;
371
+ }, [format]);
372
+
227
373
  useEffect(() => {
228
- if (autoFocus) containerRef.current?.focus();
229
- }, [autoFocus, containerRef]);
374
+ if (day) onDayChange(day);
375
+ }, [day, onDayChange]);
230
376
 
231
- const onCloseRef = useRef(onClose);
232
377
  useEffect(() => {
233
- onCloseRef.current = onClose;
234
- }, [onClose]);
378
+ if (time) onTimeChange(time);
379
+ }, [time, onTimeChange]);
235
380
 
236
381
  useEffect(() => {
237
- if (!opened) {
238
- onCloseRef.current();
239
- containerRef.current?.focus();
382
+ const { start, end } = inputSelection;
383
+ if (end === start && end === format.length) {
384
+ onEnd();
240
385
  }
241
- }, [containerRef, opened]);
386
+ }, [format.length, inputSelection, onEnd]);
242
387
 
243
- const formattedValue = useFormattedValue({
244
- forwardedValue,
245
- format,
246
- showTime,
247
- timeNotation,
248
- });
388
+ const isMinXs = useIsMinWidth('xs');
389
+
390
+ // Set the input focus
391
+ useEffect(() => {
392
+ if (!innerInputRef.current) return;
393
+ if (inputFocused) {
394
+ innerInputRef.current.focus();
395
+ } else {
396
+ innerInputRef.current.blur();
397
+ }
398
+ }, [innerInputRef, inputFocused]);
399
+
400
+ // Open the picker automatically
401
+ useEffect(() => {
402
+ let pickerType: PickerType | null = null;
403
+
404
+ // Find the nearest token from the current caret position
405
+ const next = nextToken(inputSelection.start, formatRef.current, tokens);
406
+
407
+ if (next) {
408
+ if (dateTokens.includes(next.token as any)) {
409
+ pickerType = 'date';
410
+ }
411
+ if (timeTokens.includes(next.token as any)) {
412
+ pickerType = 'time';
413
+ }
414
+ }
249
415
 
250
- const closeHandler = useCallback(() => {
251
- setOpened(false);
416
+ if (pickerType && inputFocused) {
417
+ setPickerType(pickerType);
418
+ if (showPicker && autoOpen && isMinXs) {
419
+ setPickerVisibility(true);
420
+ }
421
+ } else {
422
+ setPickerVisibility(false);
423
+ }
424
+ }, [autoOpen, inputFocused, inputSelection.start, isMinXs, showPicker]);
425
+
426
+ const moveCaretToNextTokenDiffType = useCallback((pos: number) => {
427
+ const nextTokenDiffType = getNextTokenDiffType(pos, formatRef.current);
428
+ const nextPos = nextTokenDiffType
429
+ ? nextTokenDiffType.pos
430
+ : formatRef.current.length;
431
+
432
+ setInputSelection({ start: nextPos, end: nextPos });
252
433
  }, []);
253
434
 
254
- useKeyPress(
255
- (typeof window !== 'undefined' ? window : undefined) as EventTarget,
256
- 'Escape',
257
- closeHandler
258
- );
259
- const isMinXs = useIsMinWidth('xs');
435
+ const changeHandler = useCallback(
436
+ (tokens: typeof dateTokens | typeof timeTokens, value: any) => {
437
+ if (!inputValueRef.current || value === null) return;
438
+
439
+ const start = inputSelectionRef.current.start;
440
+
441
+ let nextValue = inputValueRef.current;
442
+ tokens.forEach((token) => {
443
+ const tokenIndex = formatRef.current.indexOf(token);
444
+ if (tokenIndex >= 0) {
445
+ nextValue = replaceSubstring(
446
+ nextValue,
447
+ tokenIndex,
448
+ '_'.repeat(token.length)
449
+ );
450
+ }
451
+ });
452
+ tokens.forEach((token) => {
453
+ const tokenIndex = formatRef.current.indexOf(token);
454
+ if (
455
+ tokenIndex >= 0 &&
456
+ tokenGetterMap[token] &&
457
+ nextValue[tokenIndex] === '_'
458
+ ) {
459
+ nextValue = replaceSubstring(
460
+ nextValue,
461
+ tokenIndex,
462
+ tokenGetterMap[token](value)
463
+ );
464
+ }
465
+ });
260
466
 
261
- const blurHandler = useCallback(
262
- (e) => {
263
- if (!opened) onBlur(e);
467
+ setInputValue(nextValue);
468
+ moveCaretToNextTokenDiffType(start);
264
469
  },
265
- [onBlur, opened]
470
+ [moveCaretToNextTokenDiffType]
266
471
  );
267
472
 
268
- const valueIsSpecified = useMemo(
269
- () => formattedValue !== undefined && formattedValue !== null,
270
- [formattedValue]
473
+ const dateChangeHandler = useCallback(
474
+ (day: Day | null) => {
475
+ changeHandler(dateTokens, day);
476
+ setDay(day);
477
+ },
478
+ [changeHandler]
479
+ );
480
+ const timeChangeHandler = useCallback(
481
+ (time: Time | null) => changeHandler(timeTokens, time),
482
+ [changeHandler]
271
483
  );
272
484
 
273
- const rightHasPaddingValue = useMemo(() => {
274
- if (valueIsSpecified) return false;
275
- return right ? rightHasPadding : true;
276
- }, [right, rightHasPadding, valueIsSpecified]);
485
+ const hour12 = useMemo(() => format.includes('hh'), [format]);
277
486
 
278
- const rightValue = useMemo(() => {
279
- if (valueIsSpecified) {
487
+ useEffect(() => {
488
+ if (!disabledTimesFn || !day) return;
489
+ let mounted = true;
490
+ const fn = async () => {
491
+ setLoadingTimes(true);
492
+ const disabledTimes = await disabledTimesFn(day);
493
+ if (mounted) {
494
+ setDisabledTimes(disabledTimes);
495
+ setLoadingTimes(false);
496
+ }
497
+ };
498
+ fn();
499
+ return () => {
500
+ mounted = false;
501
+ };
502
+ }, [disabledTimesFn, day]);
503
+
504
+ const pickerComponent = useMemo(() => {
505
+ if (pickerType === 'date') {
506
+ return (
507
+ <DateCalendar
508
+ firstDayOfWeek={firstDayOfWeek}
509
+ disabledDays={disabledDays}
510
+ locale={locale}
511
+ value={day}
512
+ onChange={dateChangeHandler}
513
+ />
514
+ );
515
+ }
516
+ if (pickerType === 'time') {
517
+ if (loadingTimes) {
518
+ return (
519
+ <TimeGridSkeleton count={times.length} columns={timeColumns} />
520
+ );
521
+ }
280
522
  return (
281
- <div aria-hidden>
282
- <Button
283
- type='ghost'
284
- wide='never'
285
- size='small'
286
- disabled={disabled}
287
- onClick={(e) => {
288
- setForwardedValue(null);
289
- e.stopPropagation();
290
- }}
291
- onKeyDown={(e) => {
292
- if (disabled) return;
293
- if (['Enter', ' '].includes(e.key)) {
294
- setForwardedValue(null);
295
- if (!containerRef.current) return;
296
- containerRef.current.focus();
297
- e.preventDefault();
298
- e.stopPropagation();
299
- }
300
- }}
301
- aria-label={locale.clearLabel}
302
- >
303
- <ClearIcon />
304
- </Button>
305
- </div>
523
+ <TimeGrid
524
+ times={times}
525
+ hour12={hour12}
526
+ columns={timeColumns}
527
+ disabledTimes={disabledTimes}
528
+ value={time}
529
+ onChange={timeChangeHandler}
530
+ />
306
531
  );
307
532
  }
308
- return (
309
- right || (
310
- <ToggleIconContainer>
311
- {opened ? <Up /> : <Down />}
312
- </ToggleIconContainer>
313
- )
314
- );
533
+ return null;
315
534
  }, [
316
- containerRef,
317
- disabled,
318
- locale.clearLabel,
319
- opened,
320
- right,
321
- setForwardedValue,
322
- valueIsSpecified,
535
+ dateChangeHandler,
536
+ day,
537
+ disabledDays,
538
+ disabledTimes,
539
+ firstDayOfWeek,
540
+ hour12,
541
+ loadingTimes,
542
+ locale,
543
+ pickerType,
544
+ time,
545
+ timeChangeHandler,
546
+ timeColumns,
547
+ times,
323
548
  ]);
324
549
 
325
- const firstTimePickerRef = useRef<HTMLInputElement>(null);
326
- const secondTimePickerRef = useRef<HTMLInputElement>(null);
327
- const rangeStartedRef = useRef(false);
328
-
329
- useEffect(() => {
330
- rangeStartedRef.current = false;
331
- }, [opened]);
332
-
333
- const changeHandler = useCallback(
334
- (date: Date) => {
335
- setForwardedValue((prev) => {
336
- if (!range) return createDate(date, prev);
337
-
338
- const d = createDate(date);
339
- const nextValue =
340
- prev && Array.isArray(prev) && rangeStartedRef.current
341
- ? [new Date(prev[0].getTime()), createDate(date, prev[1])]
342
- : [d, d];
343
-
344
- rangeStartedRef.current = !rangeStartedRef.current;
345
- return nextValue.sort(
346
- (a: Date, b: Date) => a.getTime() - b.getTime()
347
- );
348
- });
349
- },
350
- [range, setForwardedValue]
351
- );
352
-
353
- const calendarComponent = useMemo(
354
- () => (
355
- <DatePickerCalendar
356
- firstDayOfWeek={firstDayOfWeek}
357
- locale={locale}
358
- value={forwardedValue}
359
- onSelect={(v) => {
360
- changeHandler(v);
361
- firstTimePickerRef.current?.focus();
362
- if (!showTime && (!range || !rangeStartedRef.current)) {
363
- closeHandler();
364
- }
365
- }}
366
- />
367
- ),
368
- [
369
- changeHandler,
370
- closeHandler,
371
- firstDayOfWeek,
372
- forwardedValue,
373
- locale,
374
- range,
375
- showTime,
376
- ]
377
- );
550
+ const openPicker = useCallback(() => {
551
+ if (!innerInputRef.current) return;
552
+ const next = nextToken(0, formatRef.current, tokens);
553
+
554
+ if (next) {
555
+ const type = getPickerType(next.token);
556
+ if (type) {
557
+ setInputFocused(true);
558
+ setInputSelection({ start: next.pos, end: next.pos });
559
+ setPickerType(type);
560
+ setPickerVisibility(true);
561
+ }
562
+ }
563
+ }, [innerInputRef]);
378
564
 
379
- const tabOnInputComplete = useCallback(
380
- (e) => {
381
- if (e.target.selectionStart < 5) return;
382
- secondTimePickerRef.current?.focus();
383
- setForwardedValue(([from, to]) => {
384
- const nextTo = new Date(to.getTime());
385
- if (isSameDay(from, to)) {
386
- nextTo.setHours(from.getHours() + 1);
387
- nextTo.setMinutes(from.getMinutes());
388
- } else {
389
- nextTo.setHours(from.getHours());
390
- nextTo.setMinutes(from.getMinutes());
391
- }
392
- return [from, nextTo];
393
- });
565
+ const focusHandler = useCallback(
566
+ (e: FocusEvent<HTMLInputElement>) => {
567
+ setInputFocused(true);
568
+ onFocus(e);
394
569
  },
395
- [setForwardedValue]
570
+ [onFocus]
396
571
  );
397
572
 
398
- const closeOnInputComplete = useCallback(
399
- (e) => {
400
- if (e.target.selectionStart < 5) return;
401
- closeHandler();
573
+ const blurHandler = useCallback(
574
+ (e: FocusEvent<HTMLInputElement>) => {
575
+ if (isMinXs) {
576
+ setPickerVisibility(false);
577
+ }
578
+ setInputFocused(false);
579
+ onBlur(e);
402
580
  },
403
- [closeHandler]
581
+ [isMinXs, onBlur]
404
582
  );
405
583
 
406
- const timeComponent = useMemo(
407
- () =>
408
- showTime && forwardedValue ? (
409
- <TimeContainer>
410
- {Array.isArray(forwardedValue) ? (
411
- <>
412
- <StyledTimePicker
413
- ref={firstTimePickerRef}
414
- autoFocus
415
- disabled={disabled}
416
- notation={timeNotation}
417
- containerProps={timePickerStyle}
418
- value={forwardedValue[0]}
419
- onChange={(v) => setForwardedValue([v, forwardedValue[1]])}
420
- onKeyDown={(e) => {
421
- if (disabled) return;
422
- if (e.key === 'Enter') {
423
- setOpened(!opened);
424
- }
425
- }}
426
- onKeyUp={tabOnInputComplete}
427
- />
428
- <Dash>–</Dash>
429
- <StyledTimePicker
430
- ref={secondTimePickerRef}
431
- disabled={disabled}
432
- notation={timeNotation}
433
- containerProps={timePickerStyle}
434
- value={forwardedValue[1]}
435
- onChange={(v) => setForwardedValue([forwardedValue[0], v])}
436
- onKeyDown={(e) => {
437
- if (disabled) return;
438
- if (e.key === 'Enter') {
439
- setOpened(!opened);
440
- }
441
- }}
442
- onKeyUp={closeOnInputComplete}
443
- />
444
- </>
445
- ) : (
446
- <StyledTimePicker
447
- ref={firstTimePickerRef}
448
- autoFocus
449
- disabled={disabled}
450
- notation={timeNotation}
451
- containerProps={timePickerStyle}
452
- value={forwardedValue}
453
- onChange={setForwardedValue}
454
- onKeyDown={(e) => {
455
- if (disabled) return;
456
- if (e.key === 'Enter') {
457
- setOpened(!opened);
458
- }
459
- }}
460
- onKeyUp={closeOnInputComplete}
461
- />
462
- )}
463
- </TimeContainer>
464
- ) : null,
465
- [
466
- closeOnInputComplete,
467
- disabled,
468
- forwardedValue,
469
- opened,
470
- setForwardedValue,
471
- showTime,
472
- tabOnInputComplete,
473
- timeNotation,
474
- ]
584
+ const showPickerButton = useMemo(
585
+ () => showPicker && (!autoOpen || !isMinXs),
586
+ [autoOpen, isMinXs, showPicker]
475
587
  );
476
588
 
477
589
  return (
478
590
  <>
479
- <SelectContainer
480
- opened={opened}
591
+ <InputContainer
592
+ {...containerProps}
481
593
  disabled={disabled}
482
594
  size={size}
483
- tabIndex={!disabled ? 0 : -1}
484
- onClick={() => {
485
- if (disabled) return;
486
- setOpened(!opened);
487
- }}
488
- onKeyDown={(e) => {
489
- if (disabled) return;
490
- if (['Enter', ' '].includes(e.key)) {
491
- setOpened(!opened);
492
- e.preventDefault();
493
- }
494
- }}
495
- onMouseDown={(e) => e.preventDefault()}
496
- onBlur={blurHandler}
497
- role='combobox'
498
- aria-label={
499
- forwardedValue
500
- ? getAccessibilityDateLabel(forwardedValue, locale)
501
- : undefined
502
- }
503
- aria-disabled={disabled}
504
- {...rest}
505
595
  ref={mergedContainerRef}
506
596
  >
507
- {left && (
508
- <ThemeOverrider
509
- overrides={(t) => ({
510
- buttonPaddingHorizontal: 0.8,
511
- baseHeight: t.selectToggleListItemHeight / t.sizes.small,
512
- })}
513
- >
514
- <ToggleLeftAddon hasPadding={leftHasPadding}>
515
- {left}
516
- </ToggleLeftAddon>
517
- </ThemeOverrider>
518
- )}
519
-
520
- <ToggleContent hasLeft={!!left} hasRight={!!right}>
521
- {valueIsSpecified ? (
522
- <Title disabled={disabled} aria-hidden>
523
- {formattedValue}
524
- </Title>
525
- ) : (
526
- <Placeholder>{placeholder}</Placeholder>
527
- )}
528
- </ToggleContent>
529
-
530
- {rightValue && (
531
- <ThemeOverrider
532
- overrides={(t) => ({
533
- buttonPaddingHorizontal: 0.8,
534
- baseHeight: t.selectToggleListItemHeight / t.sizes.small,
535
- })}
536
- >
537
- <ToggleRightAddon hasPadding={rightHasPaddingValue}>
538
- {rightValue}
539
- </ToggleRightAddon>
597
+ <StyledInputDateUnstyled
598
+ {...rest}
599
+ disabled={disabled}
600
+ hasRight={showPickerButton}
601
+ format={format}
602
+ value={forwardedValue}
603
+ inputValue={inputValue}
604
+ selection={inputSelection}
605
+ onChange={setForwardedValue}
606
+ onInputChange={setInputValue}
607
+ onSelectionChange={setInputSelection}
608
+ onDayChange={setDay}
609
+ onTimeChange={setTime}
610
+ onDayError={onDayError}
611
+ onFocus={focusHandler}
612
+ onBlur={blurHandler}
613
+ ref={mergedInputRef}
614
+ />
615
+
616
+ {showPickerButton && (
617
+ <ThemeOverrider overrides={{ buttonPaddingHorizontal: 0.8 }}>
618
+ <PickerButtonContainer>
619
+ <Button
620
+ type='ghost'
621
+ size='small'
622
+ wide='never'
623
+ disabled={disabled}
624
+ onClick={openPicker}
625
+ onMouseDown={preventDefault}
626
+ >
627
+ <Calendar />
628
+ </Button>
629
+ </PickerButtonContainer>
540
630
  </ThemeOverrider>
541
631
  )}
542
- </SelectContainer>
632
+ </InputContainer>
543
633
 
544
634
  {isMinXs ? (
545
635
  <StyledPopover
546
- trigger={containerRef}
636
+ trigger={innerContainerRef}
547
637
  placement='bottom-start'
548
- visible={opened}
549
- onClose={closeHandler}
638
+ visible={pickerVisibility}
639
+ onMouseDown={preventDefault} // To prevent loosing the input focus
550
640
  size={size}
551
641
  >
552
- {calendarComponent}
553
- {timeComponent}
642
+ {pickerComponent}
554
643
  </StyledPopover>
555
644
  ) : (
556
- <Modal
557
- title={placeholder}
645
+ <StyledModal
558
646
  header={null}
559
647
  footer={null}
560
- visible={opened}
561
- onClose={closeHandler}
648
+ visible={pickerVisibility}
649
+ onClose={() => setPickerVisibility(false)}
650
+ lockedFocus={false} // To prevent loosing the input focus
651
+ onMouseDown={preventDefault} // To prevent loosing the input focus
562
652
  size={size}
563
653
  >
564
- {calendarComponent}
565
- {timeComponent}
566
- </Modal>
654
+ {pickerComponent}
655
+ </StyledModal>
567
656
  )}
568
657
  </>
569
658
  );