@openway/ui 1.0.0 → 1.0.2

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 (58) hide show
  1. package/README.md +203 -181
  2. package/dist/chunk-X4LIYOS5.js +3 -0
  3. package/dist/chunk-X4LIYOS5.js.map +1 -0
  4. package/dist/index.cjs +5 -1
  5. package/dist/index.cjs.map +1 -1
  6. package/dist/index.d.cts +2740 -1473
  7. package/dist/index.d.ts +2740 -1473
  8. package/dist/index.js +5 -1
  9. package/dist/index.js.map +1 -1
  10. package/dist/query.cjs +3 -0
  11. package/dist/query.cjs.map +1 -0
  12. package/dist/query.d.cts +431 -0
  13. package/dist/query.d.ts +431 -0
  14. package/dist/query.js +3 -0
  15. package/dist/query.js.map +1 -0
  16. package/dist/useInfiniteScroll-D9AW6cQV.d.cts +598 -0
  17. package/dist/useInfiniteScroll-D9AW6cQV.d.ts +598 -0
  18. package/docs/AGENTS.md +144 -0
  19. package/docs/README.md +150 -0
  20. package/docs/SKILL.md +54 -0
  21. package/docs/components/alert.md +246 -0
  22. package/docs/components/badge.md +238 -0
  23. package/docs/components/button.md +262 -0
  24. package/docs/components/carousel.md +354 -0
  25. package/docs/components/checkbox.md +252 -0
  26. package/docs/components/collapse.md +318 -0
  27. package/docs/components/confirm.md +322 -0
  28. package/docs/components/datepicker.md +259 -0
  29. package/docs/components/daterangepicker.md +260 -0
  30. package/docs/components/datetimepicker.md +226 -0
  31. package/docs/components/datetimerangepicker.md +222 -0
  32. package/docs/components/dropdown.md +275 -0
  33. package/docs/components/empty.md +200 -0
  34. package/docs/components/file-preview.md +180 -0
  35. package/docs/components/input.md +559 -0
  36. package/docs/components/modal.md +293 -0
  37. package/docs/components/popover.md +255 -0
  38. package/docs/components/radio.md +245 -0
  39. package/docs/components/select.md +254 -0
  40. package/docs/components/skeleton.md +150 -0
  41. package/docs/components/slider.md +346 -0
  42. package/docs/components/table.md +316 -0
  43. package/docs/components/tabs.md +432 -0
  44. package/docs/components/textarea.md +193 -0
  45. package/docs/components/timepicker.md +242 -0
  46. package/docs/components/timerangepicker.md +210 -0
  47. package/docs/components/toast.md +282 -0
  48. package/docs/components/toggle.md +211 -0
  49. package/docs/components/tooltip.md +213 -0
  50. package/docs/components/upload-avatar.md +318 -0
  51. package/docs/components/upload-file.md +245 -0
  52. package/docs/components/upload-image.md +126 -0
  53. package/docs/hooks/useDebounce.md +92 -0
  54. package/docs/hooks/useInfiniteScroll.md +95 -0
  55. package/docs/hooks/useMutationApp.md +242 -0
  56. package/docs/hooks/useSelectInfiniteQuery.md +123 -0
  57. package/docs/hooks/useTableQuery.md +124 -0
  58. package/package.json +31 -12
@@ -0,0 +1,242 @@
1
+ # ⏰ TimePicker Component Suite (`@owa/ui`)
2
+
3
+ Bộ component **TimePicker**, **TimeView**, và **TimeColumn** toàn diện, linh hoạt và trực quan, được thiết kế theo chuẩn **Design System**, hỗ trợ **Chế độ 12h (AM/PM) & 24h**, **Tùy chọn hiển thị giây (`showSeconds`)**, **Bước nhảy tùy biến (`hourStep`, `minuteStep`, `secondStep`)**, **Tách bạch Định dạng Dữ liệu (`format`) & Hiển thị (`displayFormat`)**, **Giới hạn thời gian (`minTime`, `maxTime`)**, **Vô hiệu hóa giờ/phút/giây linh hoạt**, và tuân thủ đầy đủ tiêu chuẩn **WAI-ARIA Accessibility** với hỗ trợ bàn phím thông minh.
4
+
5
+ ---
6
+
7
+ ## 🌟 Điểm nổi bật
8
+
9
+ - **Tách bạch giữa Dữ liệu (`format`) và Hiển thị (`displayFormat`)**:
10
+ - `format` *(mặc định `'HH:mm:ss'`)*: Định dạng chuỗi thời gian chuẩn lưu trữ và phát ra qua `onChange` (ví dụ: `"14:30:00"`).
11
+ - `displayFormat` *(tùy chọn)*: Định dạng trực quan hiển thị trên ô nhập liệu (ví dụ: `"02:30:00 PM"`).
12
+ - **Chế độ 12 giờ / 24 giờ linh hoạt (`use12Hours`)**:
13
+ - Tự động chuyển đổi giữa 24h chuẩn (`00` - `23`) và 12h kèm cột chọn `AM` / `PM`.
14
+ - **Tùy chọn hiển thị Giây (`showSeconds`) & Bước nhảy (`Step`)**:
15
+ - Bật/tắt cột giây dễ dàng với `showSeconds={false}`.
16
+ - Tùy chỉnh bước nhảy số phút (`minuteStep={15}`), số giờ (`hourStep={2}`)...
17
+ - **Cột cuộn thông minh (Auto-Scroll & WAI-ARIA Focus Management)**:
18
+ - Tự động cuộn mượt mà (`smooth`) đến vị trí giá trị đang chọn khi mở popover.
19
+ - Chuyển tiêu điểm (focus) tự động vào cột thời gian khi mở popup bằng bàn phím.
20
+ - Hỗ trợ đầy đủ phím mũi tên `ArrowUp`, `ArrowDown`, `ArrowLeft`, `ArrowRight`, `Home`, `End`, `Enter`, `Escape`.
21
+ - **5 Kích thước tiêu chuẩn (`size`)**: `xs` (24px), `sm` (32px), `md` (40px - *mặc định*), `lg` (48px), `xl` (56px).
22
+ - **3 Biến thể giao diện (`variant`)**: `outline` *(mặc định)*, `filled`, `ghost`.
23
+ - **7 Chủ đề màu sắc (`color`)**: `primary`, `secondary`, `neutral`, `error`, `success`, `warning`, `info`. Màu `warning` sử dụng chữ `text-neutral-950` tối ưu tương phản chuẩn **WCAG AA**.
24
+ - **6 Mức độ bo góc (`radius`)**: `none`, `sm`, `md`, `lg`, `xl`, `full`.
25
+ - **3 Vị trí đặt nhãn (`labelPlacement`)**: `top` *(mặc định)*, `left`, `floating`.
26
+ - **Standalone `<TimeView>`**: Có thể nhúng trực tiếp bảng chọn giờ tĩnh trên giao diện.
27
+
28
+ ---
29
+
30
+ ## 🚀 Cài đặt & Import
31
+
32
+ ```tsx
33
+ import { TimePicker, TimeView, TimeColumn } from "@owa/ui";
34
+ import type {
35
+ TimePickerProps,
36
+ TimePickerConfig,
37
+ TimeViewProps,
38
+ TimeColumnProps,
39
+ TimeValue,
40
+ TimePickerSize,
41
+ TimePickerVariant,
42
+ TimePickerColor,
43
+ TimePickerRadius,
44
+ LabelPlacement,
45
+ } from "@owa/ui";
46
+ ```
47
+
48
+ ---
49
+
50
+ ## 📖 Hướng dẫn sử dụng
51
+
52
+ ### 1. Cách sử dụng cơ bản
53
+
54
+ ```tsx
55
+ import { useState } from "react";
56
+ import { TimePicker } from "@owa/ui";
57
+
58
+ export function BasicTimePickerExample() {
59
+ const [time, setTime] = useState<string | null>("14:30:00");
60
+
61
+ return (
62
+ <TimePicker
63
+ label="Giờ hẹn"
64
+ value={time}
65
+ onChange={(formattedTime) => setTime(formattedTime)}
66
+ placeholder="HH:mm:ss"
67
+ />
68
+ );
69
+ }
70
+ ```
71
+
72
+ ---
73
+
74
+ ### 2. Chế độ 12 giờ với AM / PM
75
+
76
+ ```tsx
77
+ <TimePicker
78
+ label="Giờ bắt đầu cuộc họp"
79
+ use12Hours={true}
80
+ format="hh:mm:ss A"
81
+ defaultValue="09:15:00 AM"
82
+ />
83
+ ```
84
+
85
+ ---
86
+
87
+ ### 3. Tắt hiển thị giây (`showSeconds={false}`) & Tùy chỉnh bước nhảy (`minuteStep`)
88
+
89
+ ```tsx
90
+ <TimePicker
91
+ label="Khung giờ đặt bàn"
92
+ showSeconds={false}
93
+ minuteStep={15} // Chỉ hiển thị các mốc phút: 00, 15, 30, 45
94
+ format="HH:mm"
95
+ placeholder="HH:mm"
96
+ />
97
+ ```
98
+
99
+ ---
100
+
101
+ ### 4. Giới hạn khoảng thời gian (`minTime` & `maxTime`)
102
+
103
+ ```tsx
104
+ <TimePicker
105
+ label="Giờ làm việc"
106
+ minTime="08:00:00"
107
+ maxTime="17:30:00"
108
+ helperText="Chỉ cho phép chọn từ 08:00 đến 17:30"
109
+ />
110
+ ```
111
+
112
+ ---
113
+
114
+ ### 5. Standalone `<TimeView>` (Bảng chọn giờ độc lập)
115
+
116
+ ```tsx
117
+ import { useState } from "react";
118
+ import { TimeView } from "@owa/ui";
119
+
120
+ export function StandaloneTimeViewExample() {
121
+ const [time, setTime] = useState<Date>(new Date());
122
+
123
+ return (
124
+ <TimeView
125
+ value={time}
126
+ onChange={(newDate) => setTime(newDate)}
127
+ showSeconds={true}
128
+ color="primary"
129
+ />
130
+ );
131
+ }
132
+ ```
133
+
134
+ ---
135
+
136
+ ### 6. Vị trí đặt nhãn (`labelPlacement`)
137
+
138
+ ```tsx
139
+ // 1. Top (Phía trên - Mặc định)
140
+ <TimePicker label="Thời gian diễn ra" labelPlacement="top" />
141
+
142
+ // 2. Left (Ngang bên trái)
143
+ <TimePicker label="Thời gian diễn ra" labelPlacement="left" />
144
+
145
+ // 3. Floating (Lơ lửng trên viền)
146
+ <TimePicker label="Thời gian diễn ra" labelPlacement="floating" />
147
+ ```
148
+
149
+ ---
150
+
151
+ ### 7. Trạng thái Form & Loading
152
+
153
+ ```tsx
154
+ // Bắt buộc nhập (Required)
155
+ <TimePicker label="Giờ xuất phát" config={{ isRequired: true }} />
156
+
157
+ // Báo lỗi (Invalid)
158
+ <TimePicker
159
+ label="Giờ hẹn"
160
+ errorMessage="Vui lòng chọn thời gian hợp lệ."
161
+ config={{ isInvalid: true }}
162
+ />
163
+
164
+ // Đang tải dữ liệu (Loading)
165
+ <TimePicker
166
+ label="Đang tải dữ liệu"
167
+ config={{ isLoading: true, showSpinner: true }}
168
+ />
169
+
170
+ // Vô hiệu hóa (Disabled) hoặc Chỉ đọc (ReadOnly)
171
+ <TimePicker label="Không khả dụng" disabled={true} />
172
+ <TimePicker label="Chỉ xem" readOnly={true} />
173
+ ```
174
+
175
+ ---
176
+
177
+ ## ⌨️ Phím tắt điều hướng bàn phím (WAI-ARIA Keyboard Navigation)
178
+
179
+ | Vị trí | Phím bấm | Hành động |
180
+ | :--- | :--- | :--- |
181
+ | **Ô Input** | `ArrowDown` / `Enter` / `Space` | Mở popover và tự động chuyển tiêu điểm vào cột Giờ. |
182
+ | **Ô Input** | `Escape` | Đóng popover và giữ tiêu điểm tại ô Input. |
183
+ | **Bảng chọn (Popup)** | `ArrowDown` | Di chuyển xuống và chọn mốc thời gian tiếp theo. |
184
+ | **Bảng chọn (Popup)** | `ArrowUp` | Di chuyển lên và chọn mốc thời gian phía trước. |
185
+ | **Bảng chọn (Popup)** | `ArrowRight` | Chuyển tiêu điểm sang cột kế tiếp (Giờ $\rightarrow$ Phút $\rightarrow$ Giây $\rightarrow$ AM/PM). |
186
+ | **Bảng chọn (Popup)** | `ArrowLeft` | Chuyển tiêu điểm sang cột liền trước. |
187
+ | **Bảng chọn (Popup)** | `Home` | Nhảy nhanh về mốc đầu tiên (ví dụ: `00`). |
188
+ | **Bảng chọn (Popup)** | `End` | Nhảy nhanh về mốc cuối cùng (ví dụ: `23` hoặc `59`). |
189
+ | **Bảng chọn (Popup)** | `Enter` / `Space` | Xác nhận chọn mốc thời gian đang focus. |
190
+ | **Bảng chọn (Popup)** | `Escape` | Đóng popover và trả lại tiêu điểm về ô Input. |
191
+
192
+ ---
193
+
194
+ ## 🛠 Bảng thông số Props
195
+
196
+ ### `TimePickerProps`
197
+
198
+ | Tên Prop | Kiểu dữ liệu | Giá trị mặc định | Mô tả |
199
+ | :--- | :--- | :---: | :--- |
200
+ | `value` | `TimeValue` | — | Giá trị thời gian đang được chọn (Controlled). |
201
+ | `defaultValue` | `TimeValue` | — | Giá trị thời gian mặc định ban đầu (Uncontrolled). |
202
+ | `onChange` | `(time: string \| null) => void` | — | Callback khi giá trị thời gian thay đổi (trả về chuỗi theo `format`, hoặc `null` khi xóa). |
203
+ | `format` | `string` | `'HH:mm:ss'` | Định dạng dữ liệu chính dùng cho cả input và đầu ra `onChange`. |
204
+ | `displayFormat` | `string` | Tự động | Định dạng chuỗi hiển thị trực quan trong ô input. |
205
+ | `use12Hours` | `boolean` | `false` | Bật chế độ 12 giờ kèm cột chọn AM / PM. |
206
+ | `showSeconds` | `boolean` | `true` | Hiển thị cột chọn giây. |
207
+ | `hourStep` | `number` | `1` | Bước nhảy cho cột Giờ. |
208
+ | `minuteStep` | `number` | `1` | Bước nhảy cho cột Phút. |
209
+ | `secondStep` | `number` | `1` | Bước nhảy cho cột Giây. |
210
+ | `minTime` | `TimeValue` | — | Thời gian nhỏ nhất cho phép chọn. |
211
+ | `maxTime` | `TimeValue` | — | Thời gian lớn nhất cho phép chọn. |
212
+ | `disabledHours` | `() => number[]` | — | Hàm trả về danh sách giờ bị vô hiệu hóa. |
213
+ | `disabledMinutes` | `(hour: number) => number[]` | — | Hàm trả về danh sách phút bị vô hiệu hóa. |
214
+ | `disabledSeconds` | `(h: number, m: number) => number[]` | — | Hàm trả về danh sách giây bị vô hiệu hóa. |
215
+ | `size` | `'xs' \| 'sm' \| 'md' \| 'lg' \| 'xl'` | `'md'` | Kích cỡ ô nhập liệu và bảng chọn. |
216
+ | `variant` | `'outline' \| 'filled' \| 'ghost'` | `'outline'` | Biến thể viền/nền của ô nhập. |
217
+ | `color` | `'primary' \| 'secondary' \| 'error' \| 'success' \| 'warning' \| 'info' \| 'neutral'` | `'primary'` | Chủ đề màu sắc theo Design System. |
218
+ | `radius` | `'none' \| 'sm' \| 'md' \| 'lg' \| 'xl' \| 'full'` | — | Độ bo góc của ô nhập và popover. |
219
+ | `label` | `ReactNode` | — | Nhãn tiêu đề hiển thị cho ô nhập liệu. |
220
+ | `labelPlacement` | `'top' \| 'left' \| 'floating'` | `'top'` | Vị trí hiển thị của nhãn. |
221
+ | `placeholder` | `string` | Tự động | Văn bản giữ chỗ khi ô input rỗng. |
222
+ | `helperText` | `ReactNode` | — | Đoạn văn bản hướng dẫn/trợ giúp bên dưới ô. |
223
+ | `errorMessage` | `ReactNode` | — | Thông báo lỗi (tự động bật viền đỏ và animation). |
224
+ | `disabled` | `boolean` | `false` | Khóa toàn bộ tương tác của ô nhập. |
225
+ | `readOnly` | `boolean` | `false` | Chỉ cho phép xem, không mở popover chọn giờ. |
226
+ | `config` | `TimePickerConfig` | — | Nhóm cấu hình tập trung các cờ tính năng (xem bảng dưới). |
227
+ | `placement` | `Placement` | `'bottom-start'` | Vị trí mở popover chọn giờ (Floating UI). |
228
+ | `ref` | `Ref<HTMLInputElement>` | — | Ref chuyển tiếp tới thẻ `<input>` HTML bên dưới. |
229
+
230
+ ---
231
+
232
+ ### `TimePickerConfig`
233
+
234
+ | Cờ thuộc tính | Kiểu dữ liệu | Giá trị mặc định | Mô tả |
235
+ | :--- | :--- | :---: | :--- |
236
+ | `isRequired` | `boolean` | `false` | Hiển thị dấu sao đỏ `*` và đánh dấu `aria-required="true"`. |
237
+ | `isInvalid` | `boolean` | `false` | Bật trạng thái viền đỏ báo lỗi và `aria-invalid="true"`. |
238
+ | `isLoading` | `boolean` | `false` | Khóa tương tác, bật `aria-busy="true"` và `aria-disabled="true"`. |
239
+ | `showSpinner` | `boolean` | `false` | Hiển thị biểu tượng xoay spinner khi `isLoading={true}`. |
240
+ | `isClearable` | `boolean` | `true` | Hiển thị nút xóa nhanh thời gian đã chọn. |
241
+ | `isFullWidth` | `boolean` | `false` | Mở rộng chiếm toàn bộ 100% chiều ngang container cha. |
242
+ | `closeOnSelect` | `boolean` | `false` | Tự động đóng popover sau khi người dùng chọn xong (mặc định `false` cho TimePicker để chọn đủ các cột). |
@@ -0,0 +1,210 @@
1
+ # ⏳ TimeRangePicker Component Suite (`@owa/ui`)
2
+
3
+ Component **TimeRangePicker** toàn diện, trực quan và linh hoạt, được thiết kế theo chuẩn **Design System**, hỗ trợ **Chọn khoảng thời gian (Start Time - End Time) trên 2 bảng chọn song song**, **Tách bạch Định dạng Dữ liệu (`format`) & Hiển thị (`displayFormat`)**, **Tùy biến ký tự phân cách (`separator`)**, **Chế độ 12h (AM/PM) & 24h**, **Tùy chọn hiển thị giây (`showSeconds`)**, **Bước nhảy tùy biến (`hourStep`, `minuteStep`, `secondStep`)**, và tuân thủ đầy đủ tiêu chuẩn **WAI-ARIA Accessibility** với hỗ trợ bàn phím thông minh.
4
+
5
+ ---
6
+
7
+ ## 🌟 Điểm nổi bật
8
+
9
+ - **Giao diện 2 Bảng chọn thời gian song song (Start Time & End Time)**:
10
+ - Bố cục 2 cột nằm ngang (`flex-row divide-x`) giúp người dùng chọn thời gian bắt đầu và kết thúc một cách tự nhiên và liền mạch.
11
+ - Tự động ràng buộc `minTime` của End Time theo Start Time đã chọn và ngược lại.
12
+ - **Tùy biến nhãn tiêu đề từng cột (`startLabel` & `endLabel`)**:
13
+ - Mặc định là `"Start time"` và `"End time"`, có thể tùy biến thành `"Giờ bắt đầu"`, `"Giờ kết thúc"`...
14
+ - **Tùy biến ký tự phân cách (`separator`)**:
15
+ - Mặc định là `' - '`, có thể tùy biến thành `' to '`, `' ~ '`...
16
+ - **Tách bạch giữa Dữ liệu (`format`) và Hiển thị (`displayFormat`)**:
17
+ - `format` *(mặc định `'HH:mm:ss'`)*: Dữ liệu mảng phát ra qua `onChange` là `[string, string]` (ví dụ: `["08:00:00", "17:30:00"]`).
18
+ - `displayFormat` *(tùy chọn)*: Định dạng hiển thị trong ô input (ví dụ: `08:00 AM - 05:30 PM`).
19
+ - **5 Kích thước tiêu chuẩn (`size`)**: `xs` (24px), `sm` (32px), `md` (40px - *mặc định*), `lg` (48px), `xl` (56px).
20
+ - **3 Biến thể giao diện (`variant`)**: `outline` *(mặc định)*, `filled`, `ghost`.
21
+ - **7 Chủ đề màu sắc (`color`)**: `primary`, `secondary`, `neutral`, `error`, `success`, `warning`, `info`. Màu `warning` sử dụng chữ `text-neutral-950` tối ưu tương phản chuẩn **WCAG AA**.
22
+ - **6 Mức độ bo góc (`radius`)**: `none`, `sm`, `md`, `lg`, `xl`, `full`.
23
+ - **3 Vị trí đặt nhãn (`labelPlacement`)**: `top` *(mặc định)*, `left`, `floating`.
24
+
25
+ ---
26
+
27
+ ## 🚀 Cài đặt & Import
28
+
29
+ ```tsx
30
+ import { TimeRangePicker } from "@owa/ui";
31
+ import type {
32
+ TimeRangePickerProps,
33
+ TimeRangePickerConfig,
34
+ TimeRangeValue,
35
+ TimeRange,
36
+ TimeRangePickerSize,
37
+ TimeRangePickerVariant,
38
+ TimeRangePickerColor,
39
+ TimeRangePickerRadius,
40
+ LabelPlacement,
41
+ } from "@owa/ui";
42
+ ```
43
+
44
+ ---
45
+
46
+ ## 📖 Hướng dẫn sử dụng
47
+
48
+ ### 1. Cách sử dụng cơ bản
49
+
50
+ ```tsx
51
+ import { useState } from "react";
52
+ import { TimeRangePicker } from "@owa/ui";
53
+
54
+ export function BasicTimeRangePickerExample() {
55
+ const [range, setRange] = useState<[string, string] | null>(["08:00:00", "17:00:00"]);
56
+
57
+ return (
58
+ <TimeRangePicker
59
+ label="Khung giờ làm việc"
60
+ value={range}
61
+ onChange={(formattedRange) => setRange(formattedRange)}
62
+ placeholder="HH:mm:ss - HH:mm:ss"
63
+ />
64
+ );
65
+ }
66
+ ```
67
+
68
+ ---
69
+
70
+ ### 2. Định dạng 12 giờ kèm AM / PM & Tùy biến nhãn
71
+
72
+ ```tsx
73
+ <TimeRangePicker
74
+ label="Khung giờ hoạt động"
75
+ use12Hours={true}
76
+ format="hh:mm A"
77
+ startLabel="Giờ mở cửa"
78
+ endLabel="Giờ đóng cửa"
79
+ separator=" đến "
80
+ defaultValue={["08:00 AM", "10:00 PM"]}
81
+ />
82
+ ```
83
+
84
+ ---
85
+
86
+ ### 3. Tắt giây & Bước nhảy 15 phút
87
+
88
+ ```tsx
89
+ <TimeRangePicker
90
+ label="Ca làm việc"
91
+ showSeconds={false}
92
+ minuteStep={15}
93
+ format="HH:mm"
94
+ defaultValue={["08:00", "17:30"]}
95
+ />
96
+ ```
97
+
98
+ ---
99
+
100
+ ### 4. Vị trí đặt nhãn (`labelPlacement`)
101
+
102
+ ```tsx
103
+ // 1. Top (Phía trên - Mặc định)
104
+ <TimeRangePicker label="Khoảng thời gian" labelPlacement="top" />
105
+
106
+ // 2. Left (Ngang bên trái)
107
+ <TimeRangePicker label="Khoảng thời gian" labelPlacement="left" />
108
+
109
+ // 3. Floating (Lơ lửng trên viền)
110
+ <TimeRangePicker label="Khoảng thời gian" labelPlacement="floating" />
111
+ ```
112
+
113
+ ---
114
+
115
+ ### 5. Trạng thái Form & Loading
116
+
117
+ ```tsx
118
+ // Bắt buộc nhập (Required)
119
+ <TimeRangePicker label="Thời gian diễn ra" config={{ isRequired: true }} />
120
+
121
+ // Báo lỗi (Invalid)
122
+ <TimeRangePicker
123
+ label="Khoảng thời gian"
124
+ errorMessage="Thời gian bắt đầu không được lớn hơn thời gian kết thúc."
125
+ config={{ isInvalid: true }}
126
+ />
127
+
128
+ // Đang tải dữ liệu (Loading)
129
+ <TimeRangePicker
130
+ label="Đang tải dữ liệu"
131
+ config={{ isLoading: true, showSpinner: true }}
132
+ />
133
+
134
+ // Vô hiệu hóa (Disabled) hoặc Chỉ đọc (ReadOnly)
135
+ <TimeRangePicker label="Không khả dụng" disabled={true} />
136
+ <TimeRangePicker label="Chỉ xem" readOnly={true} />
137
+ ```
138
+
139
+ ---
140
+
141
+ ## ⌨️ Phím tắt điều hướng bàn phím (WAI-ARIA Keyboard Navigation)
142
+
143
+ | Vị trí | Phím bấm | Hành động |
144
+ | :--- | :--- | :--- |
145
+ | **Ô Input** | `ArrowDown` / `Enter` / `Space` | Mở popover và tự động chuyển tiêu điểm vào cột Start Time. |
146
+ | **Ô Input** | `Escape` | Đóng popover và giữ tiêu điểm tại ô Input. |
147
+ | **Bảng chọn (Popup)** | `ArrowDown` | Di chuyển xuống và chọn mốc thời gian tiếp theo. |
148
+ | **Bảng chọn (Popup)** | `ArrowUp` | Di chuyển lên và chọn mốc thời gian phía trước. |
149
+ | **Bảng chọn (Popup)** | `ArrowRight` | Chuyển tiêu điểm sang cột kế tiếp (Giờ $\rightarrow$ Phút $\rightarrow$ Giây $\rightarrow$ Bảng End Time). |
150
+ | **Bảng chọn (Popup)** | `ArrowLeft` | Chuyển tiêu điểm sang cột liền trước. |
151
+ | **Bảng chọn (Popup)** | `Home` | Nhảy nhanh về mốc đầu tiên. |
152
+ | **Bảng chọn (Popup)** | `End` | Nhảy nhanh về mốc cuối cùng. |
153
+ | **Bảng chọn (Popup)** | `Enter` / `Space` | Xác nhận chọn mốc thời gian đang focus. |
154
+ | **Bảng chọn (Popup)** | `Escape` | Đóng popover và trả lại tiêu điểm về ô Input. |
155
+
156
+ ---
157
+
158
+ ## 🛠 Bảng thông số Props
159
+
160
+ ### `TimeRangePickerProps`
161
+
162
+ | Tên Prop | Kiểu dữ liệu | Giá trị mặc định | Mô tả |
163
+ | :--- | :--- | :---: | :--- |
164
+ | `value` | `[TimeValue, TimeValue]` | — | Mảng khoảng thời gian đang chọn (Controlled). |
165
+ | `defaultValue` | `[TimeValue, TimeValue]` | — | Mảng khoảng thời gian mặc định ban đầu (Uncontrolled). |
166
+ | `onChange` | `(range: [string, string] \| null) => void` | — | Callback khi thay đổi khoảng thời gian (trả về `[start, end]` hoặc `null` khi xóa). |
167
+ | `format` | `string` | `'HH:mm:ss'` | Định dạng dữ liệu chính dùng cho input và đầu ra `onChange`. |
168
+ | `displayFormat` | `string` | Tự động | Định dạng chuỗi hiển thị trực quan trong ô input. |
169
+ | `separator` | `string` | `' - '` | Chuỗi ký tự phân cách giữa Start Time và End Time. |
170
+ | `startLabel` | `string` | `'Start time'` | Nhãn tiêu đề cột giờ bắt đầu. |
171
+ | `endLabel` | `string` | `'End time'` | Nhãn tiêu đề cột giờ kết thúc. |
172
+ | `use12Hours` | `boolean` | `false` | Bật chế độ 12 giờ kèm cột chọn AM / PM. |
173
+ | `showSeconds` | `boolean` | `true` | Hiển thị cột chọn giây. |
174
+ | `hourStep` | `number` | `1` | Bước nhảy cho cột Giờ. |
175
+ | `minuteStep` | `number` | `1` | Bước nhảy cho cột Phút. |
176
+ | `secondStep` | `number` | `1` | Bước nhảy cho cột Giây. |
177
+ | `minTime` | `TimeValue` | — | Thời gian nhỏ nhất cho phép chọn. |
178
+ | `maxTime` | `TimeValue` | — | Thời gian lớn nhất cho phép chọn. |
179
+ | `disabledHours` | `() => number[]` | — | Hàm trả về danh sách giờ bị vô hiệu hóa. |
180
+ | `disabledMinutes` | `(hour: number) => number[]` | — | Hàm trả về danh sách phút bị vô hiệu hóa. |
181
+ | `disabledSeconds` | `(h: number, m: number) => number[]` | — | Hàm trả về danh sách giây bị vô hiệu hóa. |
182
+ | `size` | `'xs' \| 'sm' \| 'md' \| 'lg' \| 'xl'` | `'md'` | Kích cỡ ô nhập liệu. |
183
+ | `variant` | `'outline' \| 'filled' \| 'ghost'` | `'outline'` | Biến thể viền/nền của ô nhập. |
184
+ | `color` | `'primary' \| 'secondary' \| 'error' \| 'success' \| 'warning' \| 'info' \| 'neutral'` | `'primary'` | Chủ đề màu sắc theo Design System. |
185
+ | `radius` | `'none' \| 'sm' \| 'md' \| 'lg' \| 'xl' \| 'full'` | — | Độ bo góc của ô nhập và popover. |
186
+ | `label` | `ReactNode` | — | Nhãn tiêu đề hiển thị cho ô nhập liệu. |
187
+ | `labelPlacement` | `'top' \| 'left' \| 'floating'` | `'top'` | Vị trí hiển thị của nhãn. |
188
+ | `placeholder` | `string` | Tự động | Văn bản giữ chỗ khi ô input rỗng. |
189
+ | `placeholders` | `[string, string]` | — | Văn bản giữ chỗ riêng cho Start và End. |
190
+ | `helperText` | `ReactNode` | — | Đoạn văn bản hướng dẫn/trợ giúp bên dưới ô. |
191
+ | `errorMessage` | `ReactNode` | — | Thông báo lỗi (tự động bật viền đỏ và animation). |
192
+ | `disabled` | `boolean` | `false` | Khóa toàn bộ tương tác của ô nhập. |
193
+ | `readOnly` | `boolean` | `false` | Chỉ cho phép xem, không mở popover chọn giờ. |
194
+ | `config` | `TimeRangePickerConfig` | — | Nhóm cấu hình tập trung các cờ tính năng (xem bảng dưới). |
195
+ | `placement` | `Placement` | `'bottom-start'` | Vị trí mở popover chọn giờ (Floating UI). |
196
+ | `ref` | `Ref<HTMLInputElement>` | — | Ref chuyển tiếp tới thẻ `<input>` HTML bên dưới. |
197
+
198
+ ---
199
+
200
+ ### `TimeRangePickerConfig`
201
+
202
+ | Cờ thuộc tính | Kiểu dữ liệu | Giá trị mặc định | Mô tả |
203
+ | :--- | :--- | :---: | :--- |
204
+ | `isRequired` | `boolean` | `false` | Hiển thị dấu sao đỏ `*` và đánh dấu `aria-required="true"`. |
205
+ | `isInvalid` | `boolean` | `false` | Bật trạng thái viền đỏ báo lỗi và `aria-invalid="true"`. |
206
+ | `isLoading` | `boolean` | `false` | Khóa tương tác, bật `aria-busy="true"` và `aria-disabled="true"`. |
207
+ | `showSpinner` | `boolean` | `false` | Hiển thị biểu tượng xoay spinner khi `isLoading={true}`. |
208
+ | `isClearable` | `boolean` | `true` | Hiển thị nút xóa nhanh khoảng thời gian đã chọn. |
209
+ | `isFullWidth` | `boolean` | `false` | Mở rộng chiếm toàn bộ 100% chiều ngang container cha. |
210
+ | `closeOnSelect` | `boolean` | `false` | Tự động đóng popover sau khi chọn xong cả 2 mốc. |