@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,226 @@
|
|
|
1
|
+
# 📅⏰ DateTimePicker Component Suite (`@owa/ui`)
|
|
2
|
+
|
|
3
|
+
Component **DateTimePicker** toàn diện, linh hoạt và trực quan, được thiết kế theo chuẩn **Design System**, hỗ trợ **Chọn đồng thời Ngày và Giờ trên một giao diện thống nhất**, **2 Bố cục hiển thị linh hoạt (`side-by-side` và `stacked`)**, **Tách bạch Định dạng Dữ liệu (`format`) & Hiển thị (`displayFormat`)**, **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`)**, **Giới hạn ngày giờ (`minDate`, `maxDate`, `minTime`, `maxTime`)**, 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 `'DD/MM/YYYY HH:mm:ss'`)*: Định dạng chuỗi ngày giờ chuẩn lưu trữ và phát ra qua `onChange` (ví dụ: `"25/12/2026 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ụ: `"25/12/2026 02:30:00 PM"`).
|
|
12
|
+
- **2 Bố cục popover linh hoạt (`layout`)**:
|
|
13
|
+
- `side-by-side` *(mặc định)*: Lịch ngày (`Calendar`) và Cột giờ (`TimeView`) nằm ngang cạnh nhau.
|
|
14
|
+
- `stacked`: Lịch ngày nằm phía trên, Cột giờ nằm phía dưới theo chiều dọc.
|
|
15
|
+
- **Chế độ 12 giờ / 24 giờ linh hoạt (`use12Hours`)**:
|
|
16
|
+
- Tự động chuyển đổi giữa 24h chuẩn (`00` - `23`) và 12h kèm cột chọn `AM` / `PM`.
|
|
17
|
+
- **Tùy chọn hiển thị Giây (`showSeconds`) & Bước nhảy (`Step`)**:
|
|
18
|
+
- Bật/tắt cột giây dễ dàng với `showSeconds={false}`.
|
|
19
|
+
- Tùy chỉnh bước nhảy số phút (`minuteStep={15}`), số giờ (`hourStep={2}`)...
|
|
20
|
+
- **Quản lý Tiêu điểm & Điều hướng Bàn phím Thông minh (WAI-ARIA Focus Management)**:
|
|
21
|
+
- Tự động chuyển tiêu điểm vào Lịch khi mở popover bằng bàn phím (`Enter`, `Space`, `ArrowDown`).
|
|
22
|
+
- Hỗ trợ đầy đủ phím mũi tên `ArrowUp`, `ArrowDown`, `ArrowLeft`, `ArrowRight`, `Home`, `End`, `Enter`, `Escape`.
|
|
23
|
+
- **5 Kích thước tiêu chuẩn (`size`)**: `xs` (24px), `sm` (32px), `md` (40px - *mặc định*), `lg` (48px), `xl` (56px).
|
|
24
|
+
- **3 Biến thể giao diện (`variant`)**: `outline` *(mặc định)*, `filled`, `ghost`.
|
|
25
|
+
- **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**.
|
|
26
|
+
- **6 Mức độ bo góc (`radius`)**: `none`, `sm`, `md`, `lg`, `xl`, `full`.
|
|
27
|
+
- **3 Vị trí đặt nhãn (`labelPlacement`)**: `top` *(mặc định)*, `left`, `floating`.
|
|
28
|
+
|
|
29
|
+
---
|
|
30
|
+
|
|
31
|
+
## 🚀 Cài đặt & Import
|
|
32
|
+
|
|
33
|
+
```tsx
|
|
34
|
+
import { DateTimePicker } from "@owa/ui";
|
|
35
|
+
import type {
|
|
36
|
+
DateTimePickerProps,
|
|
37
|
+
DateTimePickerConfig,
|
|
38
|
+
DateTimeValue,
|
|
39
|
+
DateTimePickerSize,
|
|
40
|
+
DateTimePickerVariant,
|
|
41
|
+
DateTimePickerColor,
|
|
42
|
+
DateTimePickerRadius,
|
|
43
|
+
DateTimePickerLayout,
|
|
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 { DateTimePicker } from "@owa/ui";
|
|
57
|
+
|
|
58
|
+
export function BasicDateTimePickerExample() {
|
|
59
|
+
const [datetime, setDatetime] = useState<string | null>("25/12/2026 14:30:00");
|
|
60
|
+
|
|
61
|
+
return (
|
|
62
|
+
<DateTimePicker
|
|
63
|
+
label="Thời gian diễn ra"
|
|
64
|
+
value={datetime}
|
|
65
|
+
onChange={(newVal) => setDatetime(newVal)}
|
|
66
|
+
placeholder="DD/MM/YYYY HH:mm:ss"
|
|
67
|
+
/>
|
|
68
|
+
);
|
|
69
|
+
}
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
---
|
|
73
|
+
|
|
74
|
+
### 2. Chế độ 12 giờ với AM / PM
|
|
75
|
+
|
|
76
|
+
```tsx
|
|
77
|
+
<DateTimePicker
|
|
78
|
+
label="Lịch họp"
|
|
79
|
+
use12Hours={true}
|
|
80
|
+
format="DD/MM/YYYY hh:mm:ss A"
|
|
81
|
+
defaultValue="25/12/2026 09:15:00 AM"
|
|
82
|
+
/>
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
---
|
|
86
|
+
|
|
87
|
+
### 3. Bố cục xếp chồng dọc (`layout="stacked"`)
|
|
88
|
+
|
|
89
|
+
```tsx
|
|
90
|
+
<DateTimePicker
|
|
91
|
+
label="Chọn ngày giờ"
|
|
92
|
+
layout="stacked"
|
|
93
|
+
showSeconds={false}
|
|
94
|
+
minuteStep={15}
|
|
95
|
+
/>
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
---
|
|
99
|
+
|
|
100
|
+
### 4. Giới hạn khoảng ngày và giờ (`minDate`, `maxDate`)
|
|
101
|
+
|
|
102
|
+
```tsx
|
|
103
|
+
<DateTimePicker
|
|
104
|
+
label="Hạn chót nộp bài"
|
|
105
|
+
minDate={new Date()}
|
|
106
|
+
maxDate="2026-12-31"
|
|
107
|
+
helperText="Chỉ cho phép chọn trong năm nay"
|
|
108
|
+
/>
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
---
|
|
112
|
+
|
|
113
|
+
### 5. Vị trí đặt nhãn (`labelPlacement`)
|
|
114
|
+
|
|
115
|
+
```tsx
|
|
116
|
+
// 1. Top (Phía trên - Mặc định)
|
|
117
|
+
<DateTimePicker label="Thời gian sự kiện" labelPlacement="top" />
|
|
118
|
+
|
|
119
|
+
// 2. Left (Ngang bên trái)
|
|
120
|
+
<DateTimePicker label="Thời gian sự kiện" labelPlacement="left" />
|
|
121
|
+
|
|
122
|
+
// 3. Floating (Lơ lửng trên viền)
|
|
123
|
+
<DateTimePicker label="Thời gian sự kiện" labelPlacement="floating" />
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
---
|
|
127
|
+
|
|
128
|
+
### 6. Trạng thái Form & Loading
|
|
129
|
+
|
|
130
|
+
```tsx
|
|
131
|
+
// Bắt buộc nhập (Required)
|
|
132
|
+
<DateTimePicker label="Thời gian hẹn" config={{ isRequired: true }} />
|
|
133
|
+
|
|
134
|
+
// Báo lỗi (Invalid)
|
|
135
|
+
<DateTimePicker
|
|
136
|
+
label="Thời gian hẹn"
|
|
137
|
+
errorMessage="Vui lòng chọn thời gian hợp lệ."
|
|
138
|
+
config={{ isInvalid: true }}
|
|
139
|
+
/>
|
|
140
|
+
|
|
141
|
+
// Đang tải dữ liệu (Loading)
|
|
142
|
+
<DateTimePicker
|
|
143
|
+
label="Đang đồng bộ"
|
|
144
|
+
config={{ isLoading: true, showSpinner: true }}
|
|
145
|
+
/>
|
|
146
|
+
|
|
147
|
+
// Vô hiệu hóa (Disabled) hoặc Chỉ đọc (ReadOnly)
|
|
148
|
+
<DateTimePicker label="Không khả dụng" disabled={true} />
|
|
149
|
+
<DateTimePicker label="Chỉ xem" readOnly={true} />
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
---
|
|
153
|
+
|
|
154
|
+
## ⌨️ Phím tắt điều hướng bàn phím (WAI-ARIA Keyboard Navigation)
|
|
155
|
+
|
|
156
|
+
| Vị trí | Phím bấm | Hành động |
|
|
157
|
+
| :--- | :--- | :--- |
|
|
158
|
+
| **Ô Input** | `ArrowDown` / `Enter` / `Space` | Mở popover và tự động chuyển tiêu điểm vào ô Ngày trong Lịch. |
|
|
159
|
+
| **Ô Input** | `Escape` | Đóng popover và giữ tiêu điểm tại ô Input. |
|
|
160
|
+
| **Bảng Lịch (Calendar)** | `ArrowRight` / `ArrowLeft` | Di chuyển sang ngày kế tiếp (+1) hoặc ngày liền trước (-1). |
|
|
161
|
+
| **Bảng Lịch (Calendar)** | `ArrowDown` / `ArrowUp` | Di chuyển xuống tuần sau (+7) hoặc lên tuần trước (-7). |
|
|
162
|
+
| **Bảng Lịch (Calendar)** | `Home` / `End` | Nhảy về ngày đầu tuần hoặc cuối tuần. |
|
|
163
|
+
| **Bảng Lịch (Calendar)** | `Enter` / `Space` | Chọn ngày đang focus (kết hợp với thời gian hiện tại). |
|
|
164
|
+
| **Cột Giờ (TimeView)** | `ArrowDown` / `ArrowUp` | Tăng / giảm mốc giờ, phút, giây. |
|
|
165
|
+
| **Cột Giờ (TimeView)** | `ArrowRight` / `ArrowLeft` | Chuyển tiêu điểm giữa các cột (Giờ $\rightarrow$ Phút $\rightarrow$ Giây $\rightarrow$ AM/PM). |
|
|
166
|
+
| **Cột Giờ (TimeView)** | `Home` / `End` | Nhảy về mốc đầu tiên hoặc cuối cùng của cột. |
|
|
167
|
+
| **Popover** | `Escape` | Đóng popover và trả lại tiêu điểm về ô Input. |
|
|
168
|
+
|
|
169
|
+
---
|
|
170
|
+
|
|
171
|
+
## 🛠 Bảng thông số Props
|
|
172
|
+
|
|
173
|
+
### `DateTimePickerProps`
|
|
174
|
+
|
|
175
|
+
| Tên Prop | Kiểu dữ liệu | Giá trị mặc định | Mô tả |
|
|
176
|
+
| :--- | :--- | :---: | :--- |
|
|
177
|
+
| `value` | `DateTimeValue` | — | Giá trị ngày giờ đang chọn (Controlled). |
|
|
178
|
+
| `defaultValue` | `DateTimeValue` | — | Giá trị ngày giờ mặc định ban đầu (Uncontrolled). |
|
|
179
|
+
| `onChange` | `(date: string \| null) => void` | — | Callback khi giá trị ngày giờ thay đổi (trả về chuỗi theo `format`, hoặc `null` khi xóa). |
|
|
180
|
+
| `format` | `string` | `'DD/MM/YYYY HH:mm:ss'` | Định dạng dữ liệu chính dùng cho cả input và đầu ra `onChange`. |
|
|
181
|
+
| `displayFormat` | `string` | Tự động | Định dạng chuỗi hiển thị trực quan trong ô input. |
|
|
182
|
+
| `layout` | `'side-by-side' \| 'stacked'` | `'side-by-side'` | Bố cục hiển thị bảng chọn trong popover. |
|
|
183
|
+
| `locale` | `'vi' \| 'en' \| LocaleConfig` | `'vi'` | Cấu hình ngôn ngữ quốc tế hóa. |
|
|
184
|
+
| `use12Hours` | `boolean` | `false` | Bật chế độ 12 giờ kèm cột chọn AM / PM. |
|
|
185
|
+
| `showSeconds` | `boolean` | `true` | Hiển thị cột chọn giây. |
|
|
186
|
+
| `hourStep` | `number` | `1` | Bước nhảy cho cột Giờ. |
|
|
187
|
+
| `minuteStep` | `number` | `1` | Bước nhảy cho cột Phút. |
|
|
188
|
+
| `secondStep` | `number` | `1` | Bước nhảy cho cột Giây. |
|
|
189
|
+
| `minDate` | `Date \| string` | — | Ngày nhỏ nhất cho phép chọn. |
|
|
190
|
+
| `maxDate` | `Date \| string` | — | Ngày lớn nhất cho phép chọn. |
|
|
191
|
+
| `isDateDisabled` | `(date: Date) => boolean` | — | Hàm kiểm tra vô hiệu hóa ngày tùy biến. |
|
|
192
|
+
| `minTime` | `TimeValue` | — | Thời gian nhỏ nhất cho phép chọn. |
|
|
193
|
+
| `maxTime` | `TimeValue` | — | Thời gian lớn nhất cho phép chọn. |
|
|
194
|
+
| `disabledHours` | `() => number[]` | — | Hàm trả về danh sách giờ bị vô hiệu hóa. |
|
|
195
|
+
| `disabledMinutes` | `(h: number) => number[]` | — | Hàm trả về danh sách phút bị vô hiệu hóa. |
|
|
196
|
+
| `disabledSeconds` | `(h: number, m: number) => number[]` | — | Hàm trả về danh sách giây bị vô hiệu hóa. |
|
|
197
|
+
| `size` | `'xs' \| 'sm' \| 'md' \| 'lg' \| 'xl'` | `'md'` | Kích cỡ ô nhập liệu và bảng chọn. |
|
|
198
|
+
| `variant` | `'outline' \| 'filled' \| 'ghost'` | `'outline'` | Biến thể viền/nền của ô nhập. |
|
|
199
|
+
| `color` | `'primary' \| 'secondary' \| 'error' \| 'success' \| 'warning' \| 'info' \| 'neutral'` | `'primary'` | Chủ đề màu sắc theo Design System. |
|
|
200
|
+
| `radius` | `'none' \| 'sm' \| 'md' \| 'lg' \| 'xl' \| 'full'` | — | Độ bo góc của ô nhập và popover. |
|
|
201
|
+
| `label` | `ReactNode` | — | Nhãn tiêu đề hiển thị cho ô nhập liệu. |
|
|
202
|
+
| `labelPlacement` | `'top' \| 'left' \| 'floating'` | `'top'` | Vị trí hiển thị của nhãn. |
|
|
203
|
+
| `placeholder` | `string` | Tự động | Văn bản giữ chỗ khi ô input rỗng. |
|
|
204
|
+
| `helperText` | `ReactNode` | — | Đoạn văn bản hướng dẫn/trợ giúp bên dưới ô. |
|
|
205
|
+
| `errorMessage` | `ReactNode` | — | Thông báo lỗi (tự động bật viền đỏ và animation). |
|
|
206
|
+
| `disabled` | `boolean` | `false` | Khóa toàn bộ tương tác của ô nhập. |
|
|
207
|
+
| `readOnly` | `boolean` | `false` | Chỉ cho phép xem, không mở popover chọn ngày giờ. |
|
|
208
|
+
| `config` | `DateTimePickerConfig` | — | Nhóm cấu hình tập trung các cờ tính năng (xem bảng dưới). |
|
|
209
|
+
| `placement` | `Placement` | `'bottom-start'` | Vị trí mở popover chọn ngày giờ (Floating UI). |
|
|
210
|
+
| `ref` | `Ref<HTMLInputElement>` | — | Ref chuyển tiếp tới thẻ `<input>` HTML bên dưới. |
|
|
211
|
+
|
|
212
|
+
---
|
|
213
|
+
|
|
214
|
+
### `DateTimePickerConfig`
|
|
215
|
+
|
|
216
|
+
| Cờ thuộc tính | Kiểu dữ liệu | Giá trị mặc định | Mô tả |
|
|
217
|
+
| :--- | :--- | :---: | :--- |
|
|
218
|
+
| `isRequired` | `boolean` | `false` | Hiển thị dấu sao đỏ `*` và đánh dấu `aria-required="true"`. |
|
|
219
|
+
| `isInvalid` | `boolean` | `false` | Bật trạng thái viền đỏ báo lỗi và `aria-invalid="true"`. |
|
|
220
|
+
| `isLoading` | `boolean` | `false` | Khóa tương tác, bật `aria-busy="true"` và `aria-disabled="true"`. |
|
|
221
|
+
| `showSpinner` | `boolean` | `false` | Hiển thị biểu tượng xoay spinner khi `isLoading={true}`. |
|
|
222
|
+
| `isClearable` | `boolean` | `true` | Hiển thị nút xóa nhanh ngày giờ đã chọn. |
|
|
223
|
+
| `isFullWidth` | `boolean` | `false` | Mở rộng chiếm toàn bộ 100% chiều ngang container cha. |
|
|
224
|
+
| `closeOnSelect` | `boolean` | `false` | Tự động đóng popover sau khi chọn (mặc định `false` để chọn cả ngày và giờ). |
|
|
225
|
+
| `showWeekNumbers` | `boolean` | `false` | Hiển thị cột số thứ tự tuần trong bảng lịch. |
|
|
226
|
+
| `showViewTabs` | `boolean` | `false` | Hiển thị thanh Tab Ngày / Tháng / Năm trên lịch. |
|
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
# 📅⏳ DateTimeRangePicker Component Suite (`@owa/ui`)
|
|
2
|
+
|
|
3
|
+
Component **DateTimeRangePicker** toàn diện, linh hoạt và tiện dụng, được thiết kế theo chuẩn **Design System**, hỗ trợ **Chọn khoảng Ngày & Giờ (Start - End) với cơ chế Chuyển bước (Stepped Selection)** giúp giao diện popover luôn nhỏ gọn, vừa vặn trên mọi màn hình, **Tách bạch Định dạng Dữ liệu (`format`) & Hiển thị (`displayFormat`)**, **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ự động ràng buộc `minDate` / `minTime` giữa 2 mốc**, và tuân thủ đầy đủ tiêu chuẩn **WAI-ARIA Accessibility**.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## 🌟 Điểm nổi bật
|
|
8
|
+
|
|
9
|
+
- **Cơ chế Chuyển bước thông minh (Stepped Navigation Buttons)**:
|
|
10
|
+
- Thay vì hiển thị 2 bảng lịch + 2 bảng giờ cồng kềnh (chiếm hơn 800px), DateTimeRangePicker hiển thị 1 bảng chọn tại một thời điểm.
|
|
11
|
+
- Phía trên có thanh tóm tắt `[ 1. Start: ... ]` và `[ 2. End: ... ]` cho phép click nhảy trực tiếp.
|
|
12
|
+
- Phía dưới có 2 nút chuyển đổi tiện lợi `[ ← Bắt đầu ]` và `[ Kết thúc → ]` kèm nút `Áp dụng`.
|
|
13
|
+
- **Tách bạch giữa Dữ liệu (`format`) và Hiển thị (`displayFormat`)**:
|
|
14
|
+
- `format` *(mặc định `'DD/MM/YYYY HH:mm:ss'`)*: Dữ liệu mảng phát ra qua `onChange` là `[string, string]` (ví dụ: `["25/08/2026 08:00:00", "28/08/2026 17:30:00"]`).
|
|
15
|
+
- `displayFormat` *(tùy chọn)*: Định dạng hiển thị trực quan trong ô input (ví dụ: `"25/08/2026 08:00 AM - 28/08/2026 05:30 PM"`).
|
|
16
|
+
- **2 Bố cục popover linh hoạt (`layout`)**:
|
|
17
|
+
- `side-by-side` *(mặc định)*: Lịch ngày (`Calendar`) và Cột giờ (`TimeView`) nằm ngang cạnh nhau.
|
|
18
|
+
- `stacked`: Lịch ngày nằm phía trên, Cột giờ nằm phía dưới theo chiều dọc.
|
|
19
|
+
- **Tự động ràng buộc tính hợp lệ giữa 2 mốc**:
|
|
20
|
+
- Khi chọn mốc kết thúc (`End`), các ngày và giờ trước mốc bắt đầu (`Start`) sẽ tự động bị vô hiệu hóa.
|
|
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
|
+
|
|
27
|
+
---
|
|
28
|
+
|
|
29
|
+
## 🚀 Cài đặt & Import
|
|
30
|
+
|
|
31
|
+
```tsx
|
|
32
|
+
import { DateTimeRangePicker } from "@owa/ui";
|
|
33
|
+
import type {
|
|
34
|
+
DateTimeRangePickerProps,
|
|
35
|
+
DateTimeRangePickerConfig,
|
|
36
|
+
DateTimeRangeValue,
|
|
37
|
+
DateTimeRange,
|
|
38
|
+
DateTimeRangePickerSize,
|
|
39
|
+
DateTimeRangePickerVariant,
|
|
40
|
+
DateTimeRangePickerColor,
|
|
41
|
+
DateTimeRangePickerRadius,
|
|
42
|
+
DateTimeRangePickerLayout,
|
|
43
|
+
LabelPlacement,
|
|
44
|
+
} from "@owa/ui";
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
---
|
|
48
|
+
|
|
49
|
+
## 📖 Hướng dẫn sử dụng
|
|
50
|
+
|
|
51
|
+
### 1. Cách sử dụng cơ bản
|
|
52
|
+
|
|
53
|
+
```tsx
|
|
54
|
+
import { useState } from "react";
|
|
55
|
+
import { DateTimeRangePicker } from "@owa/ui";
|
|
56
|
+
|
|
57
|
+
export function BasicDateTimeRangePickerExample() {
|
|
58
|
+
const [range, setRange] = useState<[string, string] | null>([
|
|
59
|
+
"25/08/2026 08:00:00",
|
|
60
|
+
"28/08/2026 17:30:00",
|
|
61
|
+
]);
|
|
62
|
+
|
|
63
|
+
return (
|
|
64
|
+
<DateTimeRangePicker
|
|
65
|
+
label="Khoảng thời gian công tác"
|
|
66
|
+
value={range}
|
|
67
|
+
onChange={(newRange) => setRange(newRange)}
|
|
68
|
+
placeholder="DD/MM/YYYY HH:mm:ss - DD/MM/YYYY HH:mm:ss"
|
|
69
|
+
/>
|
|
70
|
+
);
|
|
71
|
+
}
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
---
|
|
75
|
+
|
|
76
|
+
### 2. Chế độ 12 giờ với AM / PM & Tùy biến nhãn
|
|
77
|
+
|
|
78
|
+
```tsx
|
|
79
|
+
<DateTimeRangePicker
|
|
80
|
+
label="Thời gian thuê phòng"
|
|
81
|
+
use12Hours={true}
|
|
82
|
+
format="DD/MM/YYYY hh:mm A"
|
|
83
|
+
startLabel="Giờ nhận phòng"
|
|
84
|
+
endLabel="Giờ trả phòng"
|
|
85
|
+
separator=" đến "
|
|
86
|
+
defaultValue={["25/08/2026 02:00 PM", "27/08/2026 12:00 PM"]}
|
|
87
|
+
/>
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
---
|
|
91
|
+
|
|
92
|
+
### 3. Tắt giây & Bước nhảy 15 phút
|
|
93
|
+
|
|
94
|
+
```tsx
|
|
95
|
+
<DateTimeRangePicker
|
|
96
|
+
label="Ca trực"
|
|
97
|
+
showSeconds={false}
|
|
98
|
+
minuteStep={15}
|
|
99
|
+
format="DD/MM/YYYY HH:mm"
|
|
100
|
+
defaultValue={["25/08/2026 08:00", "25/08/2026 17:30"]}
|
|
101
|
+
/>
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
---
|
|
105
|
+
|
|
106
|
+
### 4. Vị trí đặt nhãn (`labelPlacement`)
|
|
107
|
+
|
|
108
|
+
```tsx
|
|
109
|
+
// 1. Top (Phía trên - Mặc định)
|
|
110
|
+
<DateTimeRangePicker label="Thời gian diễn ra" labelPlacement="top" />
|
|
111
|
+
|
|
112
|
+
// 2. Left (Ngang bên trái)
|
|
113
|
+
<DateTimeRangePicker label="Thời gian diễn ra" labelPlacement="left" />
|
|
114
|
+
|
|
115
|
+
// 3. Floating (Lơ lửng trên viền)
|
|
116
|
+
<DateTimeRangePicker label="Thời gian diễn ra" labelPlacement="floating" />
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
---
|
|
120
|
+
|
|
121
|
+
### 5. Trạng thái Form & Loading
|
|
122
|
+
|
|
123
|
+
```tsx
|
|
124
|
+
// Bắt buộc nhập (Required)
|
|
125
|
+
<DateTimeRangePicker label="Khoảng thời gian" config={{ isRequired: true }} />
|
|
126
|
+
|
|
127
|
+
// Báo lỗi (Invalid)
|
|
128
|
+
<DateTimeRangePicker
|
|
129
|
+
label="Khoảng thời gian"
|
|
130
|
+
errorMessage="Thời gian bắt đầu không được lớn hơn thời gian kết thúc."
|
|
131
|
+
config={{ isInvalid: true }}
|
|
132
|
+
/>
|
|
133
|
+
|
|
134
|
+
// Đang tải dữ liệu (Loading)
|
|
135
|
+
<DateTimeRangePicker
|
|
136
|
+
label="Đang đồng bộ"
|
|
137
|
+
config={{ isLoading: true, showSpinner: true }}
|
|
138
|
+
/>
|
|
139
|
+
|
|
140
|
+
// Vô hiệu hóa (Disabled) hoặc Chỉ đọc (ReadOnly)
|
|
141
|
+
<DateTimeRangePicker label="Không khả dụng" disabled={true} />
|
|
142
|
+
<DateTimeRangePicker label="Chỉ xem" readOnly={true} />
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
---
|
|
146
|
+
|
|
147
|
+
## ⌨️ Phím tắt điều hướng bàn phím (WAI-ARIA Keyboard Navigation)
|
|
148
|
+
|
|
149
|
+
| Vị trí | Phím bấm | Hành động |
|
|
150
|
+
| :--- | :--- | :--- |
|
|
151
|
+
| **Ô Input** | `ArrowDown` / `Enter` / `Space` | Mở popover và tự động chuyển tiêu điểm vào ô Ngày trong Lịch. |
|
|
152
|
+
| **Ô Input** | `Escape` | Đóng popover và giữ tiêu điểm tại ô Input. |
|
|
153
|
+
| **Bảng Lịch (Calendar)** | `ArrowRight` / `ArrowLeft` | Di chuyển sang ngày kế tiếp (+1) hoặc ngày liền trước (-1). |
|
|
154
|
+
| **Bảng Lịch (Calendar)** | `ArrowDown` / `ArrowUp` | Di chuyển xuống tuần sau (+7) hoặc lên tuần trước (-7). |
|
|
155
|
+
| **Bảng Lịch (Calendar)** | `Home` / `End` | Nhảy về ngày đầu tuần hoặc cuối tuần. |
|
|
156
|
+
| **Bảng Lịch (Calendar)** | `Enter` / `Space` | Chọn ngày đang focus (kết hợp với thời gian hiện tại). |
|
|
157
|
+
| **Cột Giờ (TimeView)** | `ArrowDown` / `ArrowUp` | Tăng / giảm mốc giờ, phút, giây. |
|
|
158
|
+
| **Cột Giờ (TimeView)** | `ArrowRight` / `ArrowLeft` | Chuyển tiêu điểm giữa các cột (Giờ $\rightarrow$ Phút $\rightarrow$ Giây $\rightarrow$ AM/PM). |
|
|
159
|
+
| **Popover** | `Escape` | Đóng popover và trả lại tiêu điểm về ô Input. |
|
|
160
|
+
|
|
161
|
+
---
|
|
162
|
+
|
|
163
|
+
## 🛠 Bảng thông số Props
|
|
164
|
+
|
|
165
|
+
### `DateTimeRangePickerProps`
|
|
166
|
+
|
|
167
|
+
| Tên Prop | Kiểu dữ liệu | Giá trị mặc định | Mô tả |
|
|
168
|
+
| :--- | :--- | :---: | :--- |
|
|
169
|
+
| `value` | `[DateTimeValue, DateTimeValue]` | — | Mảng khoảng ngày giờ đang chọn (Controlled). |
|
|
170
|
+
| `defaultValue` | `[DateTimeValue, DateTimeValue]` | — | Mảng khoảng ngày giờ mặc định ban đầu (Uncontrolled). |
|
|
171
|
+
| `onChange` | `(range: [string, string] \| null) => void` | — | Callback khi khoảng ngày giờ thay đổi (trả về `[start, end]` theo `format` hoặc `null` khi xóa). |
|
|
172
|
+
| `format` | `string` | `'DD/MM/YYYY HH:mm:ss'` | Định dạng dữ liệu chính dùng cho cả input và đầu ra `onChange`. |
|
|
173
|
+
| `displayFormat` | `string` | Tự động | Định dạng chuỗi hiển thị trực quan trong ô input. |
|
|
174
|
+
| `separator` | `string` | `' - '` | Chuỗi ký tự phân cách giữa Start DateTime và End DateTime. |
|
|
175
|
+
| `startLabel` | `string` | `'Start time'` | Nhãn tiêu đề mốc bắt đầu. |
|
|
176
|
+
| `endLabel` | `string` | `'End time'` | Nhãn tiêu đề mốc kết thúc. |
|
|
177
|
+
| `layout` | `'side-by-side' \| 'stacked'` | `'side-by-side'` | Bố cục hiển thị bảng chọn trong popover. |
|
|
178
|
+
| `locale` | `'vi' \| 'en' \| LocaleConfig` | `'vi'` | Cấu hình ngôn ngữ quốc tế hóa. |
|
|
179
|
+
| `use12Hours` | `boolean` | `false` | Bật chế độ 12 giờ kèm cột chọn AM / PM. |
|
|
180
|
+
| `showSeconds` | `boolean` | `true` | Hiển thị cột chọn giây. |
|
|
181
|
+
| `hourStep` | `number` | `1` | Bước nhảy cho cột Giờ. |
|
|
182
|
+
| `minuteStep` | `number` | `1` | Bước nhảy cho cột Phút. |
|
|
183
|
+
| `secondStep` | `number` | `1` | Bước nhảy cho cột Giây. |
|
|
184
|
+
| `minDate` | `Date \| string` | — | Ngày nhỏ nhất cho phép chọn. |
|
|
185
|
+
| `maxDate` | `Date \| string` | — | Ngày lớn nhất cho phép chọn. |
|
|
186
|
+
| `isDateDisabled` | `(date: Date) => boolean` | — | Hàm kiểm tra vô hiệu hóa ngày tùy biến. |
|
|
187
|
+
| `minTime` | `TimeValue` | — | Thời gian nhỏ nhất cho phép chọn. |
|
|
188
|
+
| `maxTime` | `TimeValue` | — | Thời gian lớn nhất cho phép chọn. |
|
|
189
|
+
| `disabledHours` | `() => number[]` | — | Hàm trả về danh sách giờ bị vô hiệu hóa. |
|
|
190
|
+
| `disabledMinutes` | `(h: number) => number[]` | — | Hàm trả về danh sách phút bị vô hiệu hóa. |
|
|
191
|
+
| `disabledSeconds` | `(h: number, m: number) => number[]` | — | Hàm trả về danh sách giây bị vô hiệu hóa. |
|
|
192
|
+
| `size` | `'xs' \| 'sm' \| 'md' \| 'lg' \| 'xl'` | `'md'` | Kích cỡ ô nhập liệu. |
|
|
193
|
+
| `variant` | `'outline' \| 'filled' \| 'ghost'` | `'outline'` | Biến thể viền/nền của ô nhập. |
|
|
194
|
+
| `color` | `'primary' \| 'secondary' \| 'error' \| 'success' \| 'warning' \| 'info' \| 'neutral'` | `'primary'` | Chủ đề màu sắc theo Design System. |
|
|
195
|
+
| `radius` | `'none' \| 'sm' \| 'md' \| 'lg' \| 'xl' \| 'full'` | — | Độ bo góc của ô nhập và popover. |
|
|
196
|
+
| `label` | `ReactNode` | — | Nhãn tiêu đề hiển thị cho ô nhập liệu. |
|
|
197
|
+
| `labelPlacement` | `'top' \| 'left' \| 'floating'` | `'top'` | Vị trí hiển thị của nhãn. |
|
|
198
|
+
| `placeholder` | `string` | Tự động | Văn bản giữ chỗ khi ô input rỗng. |
|
|
199
|
+
| `placeholders` | `[string, string]` | — | Văn bản giữ chỗ riêng cho Start và End. |
|
|
200
|
+
| `helperText` | `ReactNode` | — | Đoạn văn bản hướng dẫn/trợ giúp bên dưới ô. |
|
|
201
|
+
| `errorMessage` | `ReactNode` | — | Thông báo lỗi (tự động bật viền đỏ và animation). |
|
|
202
|
+
| `disabled` | `boolean` | `false` | Khóa toàn bộ tương tác của ô nhập. |
|
|
203
|
+
| `readOnly` | `boolean` | `false` | Chỉ cho phép xem, không mở popover chọn ngày giờ. |
|
|
204
|
+
| `config` | `DateTimeRangePickerConfig` | — | Nhóm cấu hình tập trung các cờ tính năng (xem bảng dưới). |
|
|
205
|
+
| `placement` | `Placement` | `'bottom-start'` | Vị trí mở popover chọn ngày giờ (Floating UI). |
|
|
206
|
+
| `ref` | `Ref<HTMLInputElement>` | — | Ref chuyển tiếp tới thẻ `<input>` HTML bên dưới. |
|
|
207
|
+
|
|
208
|
+
---
|
|
209
|
+
|
|
210
|
+
### `DateTimeRangePickerConfig`
|
|
211
|
+
|
|
212
|
+
| Cờ thuộc tính | Kiểu dữ liệu | Giá trị mặc định | Mô tả |
|
|
213
|
+
| :--- | :--- | :---: | :--- |
|
|
214
|
+
| `isRequired` | `boolean` | `false` | Hiển thị dấu sao đỏ `*` và đánh dấu `aria-required="true"`. |
|
|
215
|
+
| `isInvalid` | `boolean` | `false` | Bật trạng thái viền đỏ báo lỗi và `aria-invalid="true"`. |
|
|
216
|
+
| `isLoading` | `boolean` | `false` | Khóa tương tác, bật `aria-busy="true"` và `aria-disabled="true"`. |
|
|
217
|
+
| `showSpinner` | `boolean` | `false` | Hiển thị biểu tượng xoay spinner khi `isLoading={true}`. |
|
|
218
|
+
| `isClearable` | `boolean` | `true` | Hiển thị nút xóa nhanh khoảng ngày giờ đã chọn. |
|
|
219
|
+
| `isFullWidth` | `boolean` | `false` | Mở rộng chiếm toàn bộ 100% chiều ngang container cha. |
|
|
220
|
+
| `closeOnSelect` | `boolean` | `false` | Tự động đóng popover sau khi chọn xong cả 2 mốc. |
|
|
221
|
+
| `showWeekNumbers` | `boolean` | `false` | Hiển thị cột số thứ tự tuần trong bảng lịch. |
|
|
222
|
+
| `showViewTabs` | `boolean` | `false` | Hiển thị thanh Tab Ngày / Tháng / Năm trên lịch. |
|