@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.
- package/README.md +203 -181
- package/dist/chunk-X4LIYOS5.js +3 -0
- package/dist/chunk-X4LIYOS5.js.map +1 -0
- package/dist/index.cjs +5 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +2740 -1473
- package/dist/index.d.ts +2740 -1473
- package/dist/index.js +5 -1
- package/dist/index.js.map +1 -1
- package/dist/query.cjs +3 -0
- package/dist/query.cjs.map +1 -0
- package/dist/query.d.cts +431 -0
- package/dist/query.d.ts +431 -0
- package/dist/query.js +3 -0
- package/dist/query.js.map +1 -0
- package/dist/useInfiniteScroll-D9AW6cQV.d.cts +598 -0
- package/dist/useInfiniteScroll-D9AW6cQV.d.ts +598 -0
- package/docs/AGENTS.md +144 -0
- package/docs/README.md +150 -0
- package/docs/SKILL.md +54 -0
- package/docs/components/alert.md +246 -0
- package/docs/components/badge.md +238 -0
- package/docs/components/button.md +262 -0
- package/docs/components/carousel.md +354 -0
- package/docs/components/checkbox.md +252 -0
- package/docs/components/collapse.md +318 -0
- package/docs/components/confirm.md +322 -0
- package/docs/components/datepicker.md +259 -0
- package/docs/components/daterangepicker.md +260 -0
- package/docs/components/datetimepicker.md +226 -0
- package/docs/components/datetimerangepicker.md +222 -0
- package/docs/components/dropdown.md +275 -0
- package/docs/components/empty.md +200 -0
- package/docs/components/file-preview.md +180 -0
- package/docs/components/input.md +559 -0
- package/docs/components/modal.md +293 -0
- package/docs/components/popover.md +255 -0
- package/docs/components/radio.md +245 -0
- package/docs/components/select.md +254 -0
- package/docs/components/skeleton.md +150 -0
- package/docs/components/slider.md +346 -0
- package/docs/components/table.md +316 -0
- package/docs/components/tabs.md +432 -0
- package/docs/components/textarea.md +193 -0
- package/docs/components/timepicker.md +242 -0
- package/docs/components/timerangepicker.md +210 -0
- package/docs/components/toast.md +282 -0
- package/docs/components/toggle.md +211 -0
- package/docs/components/tooltip.md +213 -0
- package/docs/components/upload-avatar.md +318 -0
- package/docs/components/upload-file.md +245 -0
- package/docs/components/upload-image.md +126 -0
- package/docs/hooks/useDebounce.md +92 -0
- package/docs/hooks/useInfiniteScroll.md +95 -0
- package/docs/hooks/useMutationApp.md +242 -0
- package/docs/hooks/useSelectInfiniteQuery.md +123 -0
- package/docs/hooks/useTableQuery.md +124 -0
- 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. |
|