x-ui-design 1.0.31-gamma.1 → 1.0.33

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 (218) hide show
  1. package/.github/workflows/x-ui-design.yml +14 -0
  2. package/README.md +22 -1
  3. package/compile.sh +15 -0
  4. package/dist/{components → esm/types/components}/DatePicker/DatePicker.d.ts +2 -1
  5. package/dist/{components → esm/types/components}/DatePicker/RangePicker/RangePicker.d.ts +2 -1
  6. package/dist/{components → esm/types/components}/Dropdown/Dropdown.d.ts +2 -1
  7. package/dist/{components → esm/types/components}/Empty/Empty.d.ts +2 -1
  8. package/dist/{components → esm/types/components}/Form/Item/Item.d.ts +2 -1
  9. package/dist/esm/types/components/Icons/Icons.d.ts +19 -0
  10. package/dist/{components → esm/types/components}/Input/Input.d.ts +2 -1
  11. package/dist/{components → esm/types/components}/Input/Textarea/Textarea.d.ts +2 -1
  12. package/dist/{components → esm/types/components}/Popover/Popover.d.ts +2 -1
  13. package/dist/{components → esm/types/components}/Radio/Button/Button.d.ts +2 -1
  14. package/dist/{components → esm/types/components}/Radio/Group/Group.d.ts +2 -1
  15. package/dist/{components → esm/types/components}/Radio/Radio.d.ts +2 -1
  16. package/dist/{components → esm/types/components}/Result/Result.d.ts +2 -1
  17. package/dist/{components → esm/types/components}/Switch/Switch.d.ts +2 -1
  18. package/dist/{components → esm/types/components}/Upload/Upload.d.ts +2 -1
  19. package/dist/esm/types/index.d.ts +50 -0
  20. package/dist/{types → esm/types/types}/input.d.ts +2 -2
  21. package/dist/index.d.ts +144 -1
  22. package/dist/index.esm.js +4233 -3792
  23. package/dist/index.esm.js.map +1 -1
  24. package/dist/index.js +4338 -3897
  25. package/dist/index.js.map +1 -1
  26. package/eslint.config.mjs +16 -0
  27. package/lib/components/Button/Button.tsx +136 -0
  28. package/lib/components/Button/index.ts +1 -0
  29. package/lib/components/Button/style.css +197 -0
  30. package/lib/components/Checkbox/Checkbox.tsx +131 -0
  31. package/lib/components/Checkbox/index.ts +1 -0
  32. package/lib/components/Checkbox/style.css +95 -0
  33. package/lib/components/ConditionalWrapper/index.tsx +12 -0
  34. package/lib/components/DatePicker/DatePicker.tsx +526 -0
  35. package/lib/components/DatePicker/RangePicker/RangePicker.tsx +500 -0
  36. package/lib/components/DatePicker/RangePicker/index.ts +1 -0
  37. package/lib/components/DatePicker/RangePicker/style.css +434 -0
  38. package/lib/components/DatePicker/TimePicker/TimePicker.tsx +497 -0
  39. package/lib/components/DatePicker/TimePicker/index.ts +1 -0
  40. package/lib/components/DatePicker/TimePicker/style.css +197 -0
  41. package/lib/components/DatePicker/index.ts +1 -0
  42. package/lib/components/DatePicker/style.css +318 -0
  43. package/lib/components/Dropdown/Dropdown.tsx +234 -0
  44. package/lib/components/Dropdown/index.ts +1 -0
  45. package/lib/components/Dropdown/style.css +124 -0
  46. package/lib/components/Empty/Empty.tsx +45 -0
  47. package/lib/components/Empty/index.ts +1 -0
  48. package/lib/components/Empty/style.css +13 -0
  49. package/lib/components/Form/Form.tsx +130 -0
  50. package/lib/components/Form/Item/Item.tsx +294 -0
  51. package/lib/components/Form/Item/index.ts +1 -0
  52. package/lib/components/Form/Item/style.css +61 -0
  53. package/lib/components/Form/index.ts +1 -0
  54. package/lib/components/Icons/Icons.tsx +433 -0
  55. package/lib/components/Icons/index.ts +15 -0
  56. package/lib/components/Input/Input.tsx +218 -0
  57. package/lib/components/Input/Textarea/Textarea.tsx +110 -0
  58. package/lib/components/Input/Textarea/index.ts +1 -0
  59. package/lib/components/Input/Textarea/style.css +104 -0
  60. package/lib/components/Input/index.ts +1 -0
  61. package/lib/components/Input/style.css +137 -0
  62. package/lib/components/Menu/Item/Item.tsx +65 -0
  63. package/lib/components/Menu/Menu.tsx +261 -0
  64. package/lib/components/Menu/SubMenu/SubMenu.tsx +68 -0
  65. package/lib/components/Menu/index.css +145 -0
  66. package/lib/components/Menu/index.ts +1 -0
  67. package/lib/components/Popover/Popover.tsx +135 -0
  68. package/lib/components/Popover/index.ts +1 -0
  69. package/lib/components/Popover/style.css +82 -0
  70. package/lib/components/Radio/Button/Button.tsx +42 -0
  71. package/lib/components/Radio/Button/index.ts +1 -0
  72. package/lib/components/Radio/Button/style.css +43 -0
  73. package/lib/components/Radio/Group/Group.tsx +105 -0
  74. package/lib/components/Radio/Group/index.ts +1 -0
  75. package/lib/components/Radio/Group/style.css +53 -0
  76. package/lib/components/Radio/Radio.tsx +83 -0
  77. package/lib/components/Radio/index.ts +1 -0
  78. package/lib/components/Radio/style.css +73 -0
  79. package/lib/components/Result/Result.tsx +39 -0
  80. package/lib/components/Result/index.ts +1 -0
  81. package/lib/components/Result/style.css +173 -0
  82. package/lib/components/Select/Option/Option.tsx +49 -0
  83. package/lib/components/Select/Option/index.ts +1 -0
  84. package/lib/components/Select/Option/style.css +50 -0
  85. package/lib/components/Select/Select.tsx +935 -0
  86. package/lib/components/Select/Tag/Tag.tsx +43 -0
  87. package/lib/components/Select/Tag/index.ts +1 -0
  88. package/lib/components/Select/Tag/style.css +87 -0
  89. package/lib/components/Select/index.ts +1 -0
  90. package/lib/components/Select/style.css +186 -0
  91. package/lib/components/Skeleton/Avatar/Avatar.tsx +61 -0
  92. package/lib/components/Skeleton/Avatar/index.ts +1 -0
  93. package/lib/components/Skeleton/Avatar/style.css +27 -0
  94. package/lib/components/Skeleton/Button/Button.tsx +44 -0
  95. package/lib/components/Skeleton/Button/index.ts +1 -0
  96. package/lib/components/Skeleton/Button/style.css +50 -0
  97. package/lib/components/Skeleton/Image/Image.tsx +45 -0
  98. package/lib/components/Skeleton/Image/index.ts +1 -0
  99. package/lib/components/Skeleton/Image/style.css +23 -0
  100. package/lib/components/Skeleton/Input/Input.tsx +42 -0
  101. package/lib/components/Skeleton/Input/index.ts +1 -0
  102. package/lib/components/Skeleton/Input/style.css +56 -0
  103. package/lib/components/Skeleton/Skeleton.tsx +97 -0
  104. package/lib/components/Skeleton/index.ts +1 -0
  105. package/lib/components/Skeleton/style.css +84 -0
  106. package/lib/components/Switch/Switch.tsx +68 -0
  107. package/lib/components/Switch/index.css +50 -0
  108. package/lib/components/Switch/index.ts +1 -0
  109. package/lib/components/Upload/Upload.tsx +291 -0
  110. package/lib/components/Upload/index.ts +1 -0
  111. package/lib/components/Upload/style.css +151 -0
  112. package/lib/global.d.ts +1 -0
  113. package/lib/helpers/flatten.ts +26 -0
  114. package/lib/helpers/index.ts +52 -0
  115. package/lib/helpers/mask.ts +52 -0
  116. package/lib/hooks/useForm.ts +548 -0
  117. package/lib/hooks/usePosition.ts +206 -0
  118. package/lib/hooks/useWatch.ts +41 -0
  119. package/lib/hooks/useWatchError.ts +20 -0
  120. package/lib/index.ts +184 -0
  121. package/lib/styles/global.css +57 -0
  122. package/lib/types/button.ts +83 -0
  123. package/lib/types/checkbox.ts +32 -0
  124. package/lib/types/datepicker.ts +165 -0
  125. package/lib/types/dropdown.ts +41 -0
  126. package/lib/types/empty.ts +8 -0
  127. package/lib/types/form.ts +179 -0
  128. package/lib/types/index.ts +38 -0
  129. package/lib/types/input.ts +72 -0
  130. package/lib/types/menu.ts +55 -0
  131. package/lib/types/popover.ts +16 -0
  132. package/lib/types/radio.ts +69 -0
  133. package/lib/types/result.ts +22 -0
  134. package/lib/types/select.ts +126 -0
  135. package/lib/types/skeleton.ts +62 -0
  136. package/lib/types/switch.ts +22 -0
  137. package/lib/types/upload.ts +67 -0
  138. package/lib/utils/index.ts +37 -0
  139. package/lib/utils/lazy.ts +17 -0
  140. package/next.config.ts +7 -0
  141. package/package.json +18 -20
  142. package/rollup.config.js +71 -0
  143. package/src/app/favicon.ico +0 -0
  144. package/src/app/globals.css +48 -0
  145. package/src/app/layout.d.ts +5 -0
  146. package/src/app/layout.tsx +16 -0
  147. package/src/app/page.d.ts +1 -0
  148. package/src/app/page.tsx +22 -0
  149. package/tsconfig.json +46 -0
  150. package/dist/components/Icons/Icons.d.ts +0 -18
  151. /package/dist/{components → esm/types/components}/Button/Button.d.ts +0 -0
  152. /package/dist/{components → esm/types/components}/Button/index.d.ts +0 -0
  153. /package/dist/{components → esm/types/components}/Checkbox/Checkbox.d.ts +0 -0
  154. /package/dist/{components → esm/types/components}/Checkbox/index.d.ts +0 -0
  155. /package/dist/{components → esm/types/components}/ConditionalWrapper/index.d.ts +0 -0
  156. /package/dist/{components → esm/types/components}/DatePicker/RangePicker/index.d.ts +0 -0
  157. /package/dist/{components → esm/types/components}/DatePicker/TimePicker/TimePicker.d.ts +0 -0
  158. /package/dist/{components → esm/types/components}/DatePicker/TimePicker/index.d.ts +0 -0
  159. /package/dist/{components → esm/types/components}/DatePicker/index.d.ts +0 -0
  160. /package/dist/{components → esm/types/components}/Dropdown/index.d.ts +0 -0
  161. /package/dist/{components → esm/types/components}/Empty/index.d.ts +0 -0
  162. /package/dist/{components → esm/types/components}/Form/Form.d.ts +0 -0
  163. /package/dist/{components → esm/types/components}/Form/Item/index.d.ts +0 -0
  164. /package/dist/{components → esm/types/components}/Form/index.d.ts +0 -0
  165. /package/dist/{components → esm/types/components}/Icons/index.d.ts +0 -0
  166. /package/dist/{components → esm/types/components}/Input/Textarea/index.d.ts +0 -0
  167. /package/dist/{components → esm/types/components}/Input/index.d.ts +0 -0
  168. /package/dist/{components → esm/types/components}/Menu/Item/Item.d.ts +0 -0
  169. /package/dist/{components → esm/types/components}/Menu/Menu.d.ts +0 -0
  170. /package/dist/{components → esm/types/components}/Menu/SubMenu/SubMenu.d.ts +0 -0
  171. /package/dist/{components → esm/types/components}/Menu/index.d.ts +0 -0
  172. /package/dist/{components → esm/types/components}/Popover/index.d.ts +0 -0
  173. /package/dist/{components → esm/types/components}/Radio/Button/index.d.ts +0 -0
  174. /package/dist/{components → esm/types/components}/Radio/Group/index.d.ts +0 -0
  175. /package/dist/{components → esm/types/components}/Radio/index.d.ts +0 -0
  176. /package/dist/{components → esm/types/components}/Result/index.d.ts +0 -0
  177. /package/dist/{components → esm/types/components}/Select/Option/Option.d.ts +0 -0
  178. /package/dist/{components → esm/types/components}/Select/Option/index.d.ts +0 -0
  179. /package/dist/{components → esm/types/components}/Select/Select.d.ts +0 -0
  180. /package/dist/{components → esm/types/components}/Select/Tag/Tag.d.ts +0 -0
  181. /package/dist/{components → esm/types/components}/Select/Tag/index.d.ts +0 -0
  182. /package/dist/{components → esm/types/components}/Select/index.d.ts +0 -0
  183. /package/dist/{components → esm/types/components}/Skeleton/Avatar/Avatar.d.ts +0 -0
  184. /package/dist/{components → esm/types/components}/Skeleton/Avatar/index.d.ts +0 -0
  185. /package/dist/{components → esm/types/components}/Skeleton/Button/Button.d.ts +0 -0
  186. /package/dist/{components → esm/types/components}/Skeleton/Button/index.d.ts +0 -0
  187. /package/dist/{components → esm/types/components}/Skeleton/Image/Image.d.ts +0 -0
  188. /package/dist/{components → esm/types/components}/Skeleton/Image/index.d.ts +0 -0
  189. /package/dist/{components → esm/types/components}/Skeleton/Input/Input.d.ts +0 -0
  190. /package/dist/{components → esm/types/components}/Skeleton/Input/index.d.ts +0 -0
  191. /package/dist/{components → esm/types/components}/Skeleton/Skeleton.d.ts +0 -0
  192. /package/dist/{components → esm/types/components}/Skeleton/index.d.ts +0 -0
  193. /package/dist/{components → esm/types/components}/Switch/index.d.ts +0 -0
  194. /package/dist/{components → esm/types/components}/Upload/index.d.ts +0 -0
  195. /package/dist/{helpers → esm/types/helpers}/flatten.d.ts +0 -0
  196. /package/dist/{helpers → esm/types/helpers}/index.d.ts +0 -0
  197. /package/dist/{helpers → esm/types/helpers}/mask.d.ts +0 -0
  198. /package/dist/{hooks → esm/types/hooks}/useForm.d.ts +0 -0
  199. /package/dist/{hooks → esm/types/hooks}/usePosition.d.ts +0 -0
  200. /package/dist/{hooks → esm/types/hooks}/useWatch.d.ts +0 -0
  201. /package/dist/{hooks → esm/types/hooks}/useWatchError.d.ts +0 -0
  202. /package/dist/{types → esm/types/types}/button.d.ts +0 -0
  203. /package/dist/{types → esm/types/types}/checkbox.d.ts +0 -0
  204. /package/dist/{types → esm/types/types}/datepicker.d.ts +0 -0
  205. /package/dist/{types → esm/types/types}/dropdown.d.ts +0 -0
  206. /package/dist/{types → esm/types/types}/empty.d.ts +0 -0
  207. /package/dist/{types → esm/types/types}/form.d.ts +0 -0
  208. /package/dist/{types → esm/types/types}/index.d.ts +0 -0
  209. /package/dist/{types → esm/types/types}/menu.d.ts +0 -0
  210. /package/dist/{types → esm/types/types}/popover.d.ts +0 -0
  211. /package/dist/{types → esm/types/types}/radio.d.ts +0 -0
  212. /package/dist/{types → esm/types/types}/result.d.ts +0 -0
  213. /package/dist/{types → esm/types/types}/select.d.ts +0 -0
  214. /package/dist/{types → esm/types/types}/skeleton.d.ts +0 -0
  215. /package/dist/{types → esm/types/types}/switch.d.ts +0 -0
  216. /package/dist/{types → esm/types/types}/upload.d.ts +0 -0
  217. /package/dist/{utils → esm/types/utils}/index.d.ts +0 -0
  218. /package/dist/{utils → esm/types/utils}/lazy.d.ts +0 -0
@@ -0,0 +1,497 @@
1
+ 'use client';
2
+
3
+ import React, {
4
+ ChangeEvent,
5
+ FC,
6
+ FocusEvent,
7
+ MouseEvent as ReactMouseEvent,
8
+ ReactNode,
9
+ RefObject,
10
+ useEffect,
11
+ useRef,
12
+ useState
13
+ } from 'react';
14
+ import { clsx } from '../../../helpers';
15
+ import { RuleType } from '../../../types';
16
+ import { TimePickerProps } from '../../../types/datepicker';
17
+ import { prefixClsTimePicker } from '../../../utils';
18
+ import { ClearIcon, TimeIcon } from '../../Icons/Icons';
19
+ import { ConditionalWrapper } from '../../../components/ConditionalWrapper';
20
+ import { createPortal } from 'react-dom';
21
+ import { usePosition } from '../../../hooks/usePosition';
22
+ import './style.css';
23
+
24
+ const HOURS = 24;
25
+ const INPUT_SIZE = 13;
26
+ const TIME_OPTION_PADDING = 4;
27
+ const MINUTES_AND_SECONDS = 60;
28
+ const ADD_EMPTY_SECTION_COUNT = 5;
29
+
30
+ const pad = (num: number): string => String(num).padStart(2, '0');
31
+
32
+ const TimePicker: FC<TimePickerProps> = ({
33
+ prefixCls = prefixClsTimePicker,
34
+ style = {},
35
+ className = '',
36
+ disabledTime,
37
+ inputReadOnly = false,
38
+ format = 'HH:mm:ss',
39
+ defaultValue = null,
40
+ value: propValue = null,
41
+ onChange,
42
+ onBlur,
43
+ onSelect,
44
+ showNow = true,
45
+ clearIcon = <ClearIcon />,
46
+ suffixIcon = <TimeIcon />,
47
+ placeholder = 'Select time',
48
+ getPopupContainer,
49
+ placement
50
+ }) => {
51
+ const [open, setOpen] = useState<boolean>(false);
52
+ const [innerValue, setInnerValue] = useState<Date | null>(
53
+ propValue || defaultValue ? new Date(propValue || defaultValue) : null
54
+ );
55
+
56
+ const [[showHour, showMinutes, showSeconds]] = useState(`${format}`.split(':'))
57
+
58
+ const [tempValue, setTempValue] = useState<Date | null>(null);
59
+
60
+ const inputRef = useRef<HTMLInputElement>(null);
61
+ const popupRef = useRef<HTMLDivElement>(null);
62
+ const hourRef = useRef<HTMLDivElement>(null);
63
+ const minuteRef = useRef<HTMLDivElement>(null);
64
+ const secondRef = useRef<HTMLDivElement>(null);
65
+
66
+ const { dropdownPosition } = usePosition({
67
+ popupRef,
68
+ placement,
69
+ isOpen: open,
70
+ triggerRef: inputRef,
71
+ getPopupContainer: getPopupContainer?.(inputRef.current as HTMLElement)
72
+ });
73
+
74
+
75
+ useEffect(() => {
76
+ setInnerValue(propValue || defaultValue ? new Date(propValue || defaultValue) : null);
77
+ }, [propValue])
78
+
79
+ useEffect(() => {
80
+ const handleClickOutside = (e: MouseEvent) => {
81
+ if (
82
+ popupRef.current &&
83
+ !popupRef.current.contains(e.target as Node) &&
84
+ inputRef.current &&
85
+ !inputRef.current.contains(e.target as Node)
86
+ ) {
87
+ setOpen(false);
88
+ setTempValue(null);
89
+
90
+ if (!innerValue) {
91
+ onChange?.(null as RuleType, '');
92
+ }
93
+ }
94
+ };
95
+
96
+ document.addEventListener('mousedown', handleClickOutside);
97
+
98
+ return () => {
99
+ document.removeEventListener('mousedown', handleClickOutside);
100
+ };
101
+ }, [innerValue, onChange]);
102
+
103
+ useEffect(() => {
104
+ if (open) {
105
+ setTempValue(innerValue ? new Date(innerValue) : null);
106
+
107
+ const { hour = 0, minute = 0, second = 0 } = getTimeParts(innerValue);
108
+ scrollToTop(hourRef as { current: HTMLDivElement }, hour || 0);
109
+ scrollToTop(minuteRef as { current: HTMLDivElement }, minute || 0);
110
+ scrollToTop(secondRef as { current: HTMLDivElement }, second || 0);
111
+ }
112
+ }, [open, innerValue]);
113
+
114
+ useEffect(() => {
115
+ onSelect?.(tempValue);
116
+ }, [tempValue, onSelect]);
117
+
118
+ useEffect(() => {
119
+ if (open) {
120
+ setTempValue(innerValue ? new Date(innerValue) : null);
121
+ }
122
+ }, [open, innerValue]);
123
+
124
+ const formatDate = (date: Date | null): string => {
125
+ if (!date) {
126
+ return '';
127
+ }
128
+
129
+ return `${format}`
130
+ .replace(/HH|hh/, pad(date.getHours()))
131
+ .replace(/MM|mm/, pad(date.getMinutes()))
132
+ .replace(/SS|ss/, pad(date.getSeconds()));
133
+ };
134
+
135
+ const getTimeParts = (date: Date | null) => ({
136
+ hour: date?.getHours() ?? null,
137
+ minute: date?.getMinutes() ?? null,
138
+ second: date?.getSeconds() ?? null
139
+ });
140
+
141
+ const getDisabled = (
142
+ type: 'hour' | 'minute' | 'second',
143
+ hour = 0,
144
+ minute = 0
145
+ ): number[] => {
146
+ const disabled = disabledTime?.(tempValue || new Date());
147
+
148
+ if (!disabled) {
149
+ return [];
150
+ }
151
+
152
+ if (type === 'hour') {
153
+ return disabled.disabledHours?.() || [];
154
+ }
155
+
156
+ if (type === 'minute') {
157
+ return disabled.disabledMinutes?.(hour) || [];
158
+ }
159
+
160
+ if (type === 'second') {
161
+ return disabled.disabledSeconds?.(hour, minute) || [];
162
+ }
163
+
164
+ return [];
165
+ };
166
+
167
+ const scrollToTop = (
168
+ ref: RefObject<HTMLDivElement>,
169
+ selectedIndex: number
170
+ ): void => {
171
+ const el = ref.current?.querySelectorAll('div')[selectedIndex];
172
+
173
+ if (el && ref.current) {
174
+ ref.current.scrollTo({
175
+ top: el.offsetTop - TIME_OPTION_PADDING * 2,
176
+ behavior: 'smooth'
177
+ });
178
+ }
179
+ };
180
+
181
+ const updateTempValue = (hour: number, minute: number, second: number) => {
182
+ const newDate = new Date();
183
+ newDate.setHours(hour, minute, second, 0);
184
+ setTempValue(newDate);
185
+ };
186
+
187
+ const onSelectHour = (h: number, auto?: boolean): void => {
188
+ const { minute = 0, second = 0 } = getTimeParts(tempValue);
189
+
190
+ if (!auto) {
191
+ if (!minute) {
192
+ onSelectMinute(0, true);
193
+ }
194
+
195
+ if (!second) {
196
+ onSelectSecond(0, true);
197
+ }
198
+ }
199
+
200
+ updateTempValue(h, minute || 0, second || 0);
201
+ scrollToTop(hourRef as { current: HTMLDivElement }, h);
202
+ };
203
+
204
+ const onSelectMinute = (m: number, auto?: boolean): void => {
205
+ const { hour = 0, second = 0 } = getTimeParts(tempValue);
206
+
207
+ if (!auto) {
208
+ if (!hour) {
209
+ onSelectHour(0, true);
210
+ }
211
+
212
+ if (!second) {
213
+ onSelectSecond(0, true);
214
+ }
215
+ }
216
+
217
+ updateTempValue(hour || 0, m, second || 0);
218
+ scrollToTop(minuteRef as { current: HTMLDivElement }, m);
219
+ };
220
+
221
+ const onSelectSecond = (s: number, auto?: boolean): void => {
222
+ const { hour = 0, minute = 0 } = getTimeParts(tempValue);
223
+
224
+ if (!auto) {
225
+ if (!hour) {
226
+ onSelectHour(0, true);
227
+ }
228
+
229
+ if (!minute) {
230
+ onSelectMinute(0, true);
231
+ }
232
+ }
233
+
234
+ updateTempValue(hour || 0, minute || 0, s);
235
+ scrollToTop(secondRef as { current: HTMLDivElement }, s);
236
+ };
237
+
238
+ const handleClear = (e?: ReactMouseEvent<HTMLSpanElement>): void => {
239
+ e?.stopPropagation();
240
+ setInnerValue(null);
241
+ setTempValue(null);
242
+ onChange?.(null as RuleType, '');
243
+ };
244
+
245
+ const handleShowNow = (): void => {
246
+ const now = new Date();
247
+ setTempValue(now);
248
+ setInnerValue(now);
249
+ onChange?.(now, formatDate(now));
250
+ onSelect?.(now);
251
+ setOpen(false);
252
+ };
253
+
254
+ const handleOkButton = (): void => {
255
+ if (tempValue) {
256
+ setInnerValue(tempValue);
257
+ onChange?.(tempValue, formatDate(tempValue));
258
+ onSelect?.(tempValue);
259
+ }
260
+
261
+ setOpen(false);
262
+ };
263
+
264
+ const handleOnChange = (e: ChangeEvent<HTMLInputElement>) => {
265
+ const inputVal = e.target.value.trim();
266
+ const timeRegex = /^(\d{2}):(\d{2}):(\d{2})$/;
267
+ const match = inputVal.match(timeRegex);
268
+
269
+ if (match) {
270
+ const [, hStr, mStr, sStr] = match;
271
+ const h = parseInt(hStr, 10);
272
+ const m = parseInt(mStr, 10);
273
+ const s = parseInt(sStr, 10);
274
+
275
+ if (
276
+ h >= 0 &&
277
+ h < HOURS &&
278
+ m >= 0 &&
279
+ m < MINUTES_AND_SECONDS &&
280
+ s >= 0 &&
281
+ s < MINUTES_AND_SECONDS &&
282
+ !getDisabled('hour').includes(h) &&
283
+ !getDisabled('minute', h).includes(m) &&
284
+ !getDisabled('second', h, m).includes(s)
285
+ ) {
286
+ onSelectHour(h);
287
+ onSelectMinute(m);
288
+ onSelectSecond(s);
289
+ }
290
+ }
291
+ };
292
+
293
+ const renderOptions = (): ReactNode => {
294
+ const hours = Array.from(
295
+ { length: HOURS + ADD_EMPTY_SECTION_COUNT },
296
+ (_, i) => (i < HOURS ? i : false)
297
+ );
298
+
299
+ const minutesSeconds = Array.from(
300
+ { length: MINUTES_AND_SECONDS + ADD_EMPTY_SECTION_COUNT },
301
+ (_, i) => (i < MINUTES_AND_SECONDS ? i : false)
302
+ );
303
+
304
+ const {
305
+ hour: selectedHour,
306
+ minute: selectedMinute,
307
+ second: selectedSecond
308
+ } = getTimeParts(tempValue);
309
+
310
+ const currentHour = selectedHour ?? 0;
311
+ const currentMinute = selectedMinute ?? 0;
312
+
313
+ return (
314
+ <div>
315
+ <div className={`${prefixCls}-panel`}>
316
+ {showHour ? <div ref={hourRef} className={`${prefixCls}-column`}>
317
+ {hours.map((h, index) =>
318
+ h === false ? (
319
+ <div
320
+ key={`${h}_${index}`}
321
+ className={`${prefixCls}-cell`}
322
+ style={{ opacity: 0, userSelect: 'none', cursor: 'inherit' }}
323
+ >
324
+ 0
325
+ </div>
326
+ ) : (
327
+ <div
328
+ key={h}
329
+ className={clsx([
330
+ `${prefixCls}-cell`,
331
+ {
332
+ [`${prefixCls}-cell-disabled`]:
333
+ getDisabled('hour').includes(h),
334
+ [`${prefixCls}-cell-selected`]: selectedHour === h
335
+ }
336
+ ])}
337
+ onClick={() =>
338
+ !getDisabled('hour').includes(h) && onSelectHour(h)
339
+ }
340
+ >
341
+ {pad(h)}
342
+ </div>
343
+ )
344
+ )}
345
+ </div> : null}
346
+
347
+ {showMinutes ? <div ref={minuteRef} className={`${prefixCls}-column`}>
348
+ {minutesSeconds.map((m, index) =>
349
+ m === false ? (
350
+ <div
351
+ key={`${m}_${index}`}
352
+ className={`${prefixCls}-cell`}
353
+ style={{ opacity: 0, userSelect: 'none', cursor: 'inherit' }}
354
+ >
355
+ 0
356
+ </div>
357
+ ) : (
358
+ <div
359
+ key={m}
360
+ className={clsx([
361
+ `${prefixCls}-cell`,
362
+ {
363
+ [`${prefixCls}-cell-disabled`]: getDisabled(
364
+ 'minute',
365
+ currentHour
366
+ ).includes(m),
367
+ [`${prefixCls}-cell-selected`]: selectedMinute === m
368
+ }
369
+ ])}
370
+ onClick={() =>
371
+ !getDisabled('minute', currentHour).includes(m) &&
372
+ onSelectMinute(m)
373
+ }
374
+ >
375
+ {pad(m)}
376
+ </div>
377
+ )
378
+ )}
379
+ </div> : null}
380
+
381
+ {showSeconds ? <div ref={secondRef} className={`${prefixCls}-column`}>
382
+ {minutesSeconds.map((s, index) =>
383
+ s === false ? (
384
+ <div
385
+ key={`${s}_${index}`}
386
+ className={`${prefixCls}-cell`}
387
+ style={{ opacity: 0, userSelect: 'none', cursor: 'inherit' }}
388
+ >
389
+ 0
390
+ </div>
391
+ ) : (
392
+ <div
393
+ key={s}
394
+ className={clsx([
395
+ `${prefixCls}-cell`,
396
+ {
397
+ [`${prefixCls}-cell-disabled`]: getDisabled(
398
+ 'second',
399
+ currentHour,
400
+ currentMinute
401
+ ).includes(s),
402
+ [`${prefixCls}-cell-selected`]: selectedSecond === s
403
+ }
404
+ ])}
405
+ onClick={() =>
406
+ !getDisabled('second', currentHour, currentMinute).includes(
407
+ s
408
+ ) && onSelectSecond(s)
409
+ }
410
+ >
411
+ {pad(s)}
412
+ </div>
413
+ )
414
+ )}
415
+ </div> : null}
416
+ </div>
417
+ <div className={`${prefixCls}-actions`}>
418
+ {showNow ? (
419
+ <div className={`${prefixCls}-now-btn`} onClick={handleShowNow}>
420
+ Now
421
+ </div>
422
+ ) : (
423
+ <span />
424
+ )}
425
+ <button
426
+ className={`${prefixCls}-ok-btn`}
427
+ disabled={
428
+ selectedHour === null ||
429
+ selectedMinute === null ||
430
+ selectedSecond === null
431
+ }
432
+ onClick={handleOkButton}
433
+ >
434
+ OK
435
+ </button>
436
+ </div>
437
+ </div>
438
+ );
439
+ };
440
+
441
+ return (
442
+ <div className={clsx([`${prefixCls}-wrapper`, className])} style={style}>
443
+ <div
444
+ className={`${prefixCls}-input-wrapper`}
445
+ onClick={() => setOpen(true)}
446
+ >
447
+ <input
448
+ ref={inputRef}
449
+ size={INPUT_SIZE}
450
+ placeholder={placeholder}
451
+ className={`${prefixCls}-input`}
452
+ readOnly={inputReadOnly}
453
+ onChange={handleOnChange}
454
+ // {...(open ? {} : { value: formatDate(innerValue) })}
455
+ value={open ? formatDate(tempValue) : formatDate(innerValue) || ''}
456
+ onBlur={(e: FocusEvent<HTMLInputElement>) => {
457
+ onBlur?.(e, { source: 'input' });
458
+ }}
459
+ />
460
+ <div className={`${prefixCls}-icons`}>
461
+ {clearIcon && innerValue ? (
462
+ <span className={`${prefixCls}-clear`} onClick={handleClear}>
463
+ {clearIcon}
464
+ </span>
465
+ ) : (
466
+ suffixIcon && (
467
+ <span
468
+ className={`${prefixCls}-suffix`}
469
+ onClick={e => {
470
+ e.stopPropagation();
471
+ setOpen(true);
472
+ }}
473
+ >
474
+ {suffixIcon}
475
+ </span>
476
+ )
477
+ )}
478
+ </div>
479
+ </div>
480
+
481
+ {open && (
482
+ <ConditionalWrapper
483
+ condition={getPopupContainer !== undefined}
484
+ wrapper={(element) => getPopupContainer ? createPortal(element, getPopupContainer(popupRef.current as HTMLElement)) : <>{element}</>}>
485
+ <div
486
+ ref={popupRef}
487
+ style={dropdownPosition}
488
+ className={`${prefixCls}-popup`}>
489
+ {renderOptions()}
490
+ </div>
491
+ </ConditionalWrapper>
492
+ )}
493
+ </div>
494
+ );
495
+ };
496
+
497
+ export default TimePicker;
@@ -0,0 +1 @@
1
+ export { default as TimePicker } from '../TimePicker/TimePicker'
@@ -0,0 +1,197 @@
1
+ .xUi-timepicker-wrapper {
2
+ position: relative;
3
+ display: inline-block;
4
+ font-size: 14px;
5
+ }
6
+
7
+ .xUi-timepicker-input-wrapper {
8
+ position: relative;
9
+ width: 100%;
10
+ }
11
+
12
+ .xUi-timepicker-input {
13
+ width: 100%;
14
+ height: 32px;
15
+ line-height: 32px;
16
+ padding: 4px 11px;
17
+ border: 1px solid var(--xui-border-color);
18
+ border-radius: 6px;
19
+ transition: all 0.3s;
20
+ box-sizing: border-box;
21
+ font-size: 14px;
22
+ }
23
+
24
+ .xUi-timepicker-input:hover {
25
+ border-color: var(--xui-primary-color-light);
26
+ }
27
+
28
+ .xUi-timepicker-input:focus {
29
+ border-color: var(--xui-primary-color-light);
30
+ outline: none;
31
+ }
32
+
33
+ .xUi-timepicker-input::placeholder {
34
+ opacity: 0.6;
35
+ }
36
+
37
+ .xUi-timepicker-clear {
38
+ position: absolute;
39
+ right: 8px;
40
+ top: 50%;
41
+ transform: translateY(-50%);
42
+ color: rgba(0, 0, 0, 0.45);
43
+ font-size: 12px;
44
+ cursor: pointer;
45
+ z-index: 2;
46
+ }
47
+
48
+ .xUi-timepicker-clear:hover {
49
+ color: rgba(0, 0, 0, 0.75);
50
+ }
51
+
52
+ .xUi-timepicker-popup {
53
+ display: flex;
54
+ min-width: max-content;
55
+ background: #fff;
56
+ border: 1px solid var(--xui-border-color);
57
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
58
+ border-radius: 8px;
59
+ margin-top: 4px;
60
+ padding: 8px 0;
61
+ z-index: 1;
62
+ position: absolute;
63
+ top: 100%;
64
+ left: 0;
65
+ }
66
+
67
+ .xUi-timepicker-panel {
68
+ display: flex;
69
+ width: 100%;
70
+ }
71
+
72
+ .xUi-timepicker-column {
73
+ flex: 1;
74
+ max-height: 169px;
75
+ overflow-y: auto;
76
+ overflow-x: hidden;
77
+ width: 52px;
78
+ display: flex;
79
+ flex-direction: column;
80
+ align-items: center;
81
+ padding-left: 4px;
82
+ margin-bottom: 5px;
83
+ }
84
+
85
+ .xUi-timepicker-column::-webkit-scrollbar,
86
+ .xUi-timepicker-column::-webkit-scrollbar-thumb,
87
+ .xUi-timepicker-column::-webkit-scrollbar-thumb {
88
+ width: 4px;
89
+ }
90
+
91
+ .xUi-timepicker-column:nth-child(2) {
92
+ border-left: 1px solid var(--xui-border-color);
93
+ border-right: 1px solid var(--xui-border-color);
94
+ }
95
+
96
+ .xUi-timepicker-cell {
97
+ padding: 6px 0;
98
+ text-align: center;
99
+ cursor: pointer;
100
+ transition: background 0.3s;
101
+ border-radius: 4px;
102
+ font-size: 14px;
103
+ width: 44px;
104
+ display: flex;
105
+ align-items: center;
106
+ justify-content: center;
107
+ margin-bottom: 2px;
108
+ }
109
+
110
+ .xUi-timepicker-cell:hover {
111
+ background-color: #e6f4ff;
112
+ }
113
+
114
+ .xUi-timepicker-cell-selected {
115
+ background-color: #e6f4ff;
116
+ font-weight: 500;
117
+ }
118
+
119
+ .xUi-timepicker-cell-disabled {
120
+ color: rgba(0, 0, 0, 0.25);
121
+ pointer-events: none;
122
+ user-select: none;
123
+ }
124
+
125
+ .xUi-timepicker-now-btn {
126
+ margin-top: 10px;
127
+ text-align: center;
128
+ padding: 0 0 4px;
129
+ cursor: pointer;
130
+ color: #4096ff;
131
+ font-weight: 500;
132
+ transition: background 0.3s;
133
+ }
134
+
135
+ .xUi-timepicker-icons {
136
+ display: flex;
137
+ align-items: center;
138
+ gap: 4px;
139
+ position: absolute;
140
+ right: 8px;
141
+ top: 50%;
142
+ transform: translateY(-50%);
143
+ }
144
+
145
+ .xUi-timepicker-suffix {
146
+ cursor: pointer;
147
+ display: flex;
148
+ align-items: center;
149
+ justify-content: center;
150
+ }
151
+
152
+ .xUi-timepicker-suffix svg {
153
+ width: 14px;
154
+ height: 14px;
155
+ color: #999;
156
+ }
157
+
158
+ .xUi-timepicker-clear {
159
+ right: 0;
160
+ top: 1px;
161
+ }
162
+
163
+ .xUi-timepicker-actions {
164
+ display: flex;
165
+ padding: 0 8px;
166
+ align-items: center;
167
+ justify-content: space-between;
168
+ border-top: 1px solid var(--xui-border-color);
169
+ }
170
+
171
+ .xUi-timepicker-ok-btn {
172
+ border: none;
173
+ outline: none;
174
+ margin-top: 7px;
175
+ border-radius: 4px;
176
+ color: #fff;
177
+ cursor: pointer;
178
+ padding: 4px 8px;
179
+ transition: 0.3s ease;
180
+ background-color: var(--xui-primary-color);
181
+ }
182
+
183
+ .xUi-timepicker-ok-btn:disabled {
184
+ color: grey;
185
+ font-size: 13px;
186
+ background-color: var(--xui-color-disabled);
187
+ }
188
+
189
+ .xUi-timepicker-ok-btn:not(:disabled):hover {
190
+ background-color: var(--xui-primary-color-light);
191
+ }
192
+
193
+ .xUi-timepicker-popup {
194
+ position: absolute;
195
+ top: 100%;
196
+ margin-top: 4px;
197
+ }
@@ -0,0 +1 @@
1
+ export { default as DatePicker } from '../DatePicker/DatePicker'