@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,259 @@
|
|
|
1
|
+
# 📅 DatePicker Component Suite (`@owa/ui`)
|
|
2
|
+
|
|
3
|
+
Bộ component **DatePicker** & **Calendar** toàn diện, linh hoạt và tương tác cao, thiết kế chuẩn **Design System**, hỗ trợ **3 Chế độ View (Ngày / Tháng / Năm)**, **Định dạng dữ liệu chuẩn hóa (`format`)**, **Hiển thị trực quan thích ứng (`displayFormat`)**, **Số thứ tự tuần (`showWeekNumbers`)**, và tuân thủ đầy đủ tiêu chuẩn **WAI-ARIA Accessibility**.
|
|
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'`)*: Đảm bảo dữ liệu lưu trữ trong State/Form và phát ra qua `onChange` luôn đồng nhất 100% theo chuẩn quy định.
|
|
11
|
+
- `displayFormat` *(tùy chọn)*: Cho phép hiển thị giao diện cho người xem khác với dữ liệu lưu trữ (ví dụ: `format="YYYY-MM-DD"` lưu vào database, nhưng `displayFormat="DD/MM/YYYY"` cho người Việt xem).
|
|
12
|
+
- **3 Chế độ Xem Linh Hoạt (`view` & `viewTabs`)**:
|
|
13
|
+
- `days`: Chọn ngày cụ thể trong tháng.
|
|
14
|
+
- `months`: Chọn tháng trong năm (giao diện tự động thích ứng thành `MM-YYYY` / `MM/YYYY` / `YYYY-MM`).
|
|
15
|
+
- `years`: Chọn năm trong thập kỷ (giao diện tự động thích ứng thành `YYYY`).
|
|
16
|
+
- **Drill-down thông minh**: Bấm vào tiêu đề tháng/năm trên lịch để duyệt nhanh mà không làm thay đổi tab đã chọn.
|
|
17
|
+
- **Standalone `<Calendar>` Component**: Có thể sử dụng độc lập dưới dạng lịch gắn tĩnh trên giao diện.
|
|
18
|
+
- **5 Kích thước tiêu chuẩn (`size`)**: `xs` (24px), `sm` (32px), `md` (40px - *mặc định*), `lg` (48px), `xl` (56px).
|
|
19
|
+
- **3 Biến thể giao diện (`variant`)**: `outline` *(mặc định)*, `filled`, `ghost`.
|
|
20
|
+
- **7 Chủ đề màu sắc (`color`)**: `primary`, `secondary`, `error`, `success`, `warning`, `info`, `neutral`.
|
|
21
|
+
- **6 Mức độ bo góc (`radius`)**: `none`, `sm`, `md`, `lg`, `xl`, `full`.
|
|
22
|
+
- **3 Vị trí đặt nhãn (`labelPlacement`)**: `top` *(mặc định)*, `left`, `floating`.
|
|
23
|
+
- **Hỗ trợ Đa Ngôn Ngữ (`locale`)**: Tích hợp sẵn tiếng Việt (`'vi'`), tiếng Anh (`'en'`), và cho phép tùy biến `LocaleConfig`.
|
|
24
|
+
- **Số thứ tự tuần (`showWeekNumbers`)**: Tự động tính số tuần chuẩn ISO 8601 kèm cột header `#` và nhãn `W1` - `W53`.
|
|
25
|
+
- **Safe Config Fallback**: Tích hợp hàm `getSafeConfig` đảm bảo component hoạt động an toàn, không bị crash kể cả khi truyền prop không hợp lệ.
|
|
26
|
+
|
|
27
|
+
---
|
|
28
|
+
|
|
29
|
+
## 🚀 Cài đặt & Import
|
|
30
|
+
|
|
31
|
+
```tsx
|
|
32
|
+
import { DatePicker, Calendar } from "@owa/ui";
|
|
33
|
+
import type {
|
|
34
|
+
DatePickerProps,
|
|
35
|
+
DatePickerConfig,
|
|
36
|
+
CalendarProps,
|
|
37
|
+
CalendarView,
|
|
38
|
+
DatePickerSize,
|
|
39
|
+
DatePickerVariant,
|
|
40
|
+
DatePickerColor,
|
|
41
|
+
DatePickerRadius,
|
|
42
|
+
LabelPlacement,
|
|
43
|
+
LocaleConfig,
|
|
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 { DatePicker } from "@owa/ui";
|
|
56
|
+
|
|
57
|
+
export function BasicDatePickerExample() {
|
|
58
|
+
const [date, setDate] = useState<string | null>("25/08/2026");
|
|
59
|
+
|
|
60
|
+
return (
|
|
61
|
+
<DatePicker
|
|
62
|
+
label="Ngày sinh"
|
|
63
|
+
value={date}
|
|
64
|
+
onChange={setDate}
|
|
65
|
+
placeholder="DD/MM/YYYY"
|
|
66
|
+
/>
|
|
67
|
+
);
|
|
68
|
+
}
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
---
|
|
72
|
+
|
|
73
|
+
### 2. Định dạng Dữ liệu (`format`) và Hiển thị (`displayFormat`)
|
|
74
|
+
|
|
75
|
+
```tsx
|
|
76
|
+
// 1. Dùng chung 1 format ISO cho cả dữ liệu và hiển thị
|
|
77
|
+
<DatePicker
|
|
78
|
+
label="Ngày hiệu lực (ISO)"
|
|
79
|
+
format="YYYY-MM-DD"
|
|
80
|
+
defaultValue="2026-08-25"
|
|
81
|
+
/>
|
|
82
|
+
|
|
83
|
+
// 2. Dữ liệu chuẩn ISO (gửi API) nhưng hiển thị tiếng Việt (DD/MM/YYYY)
|
|
84
|
+
<DatePicker
|
|
85
|
+
label="Ngày ký hợp đồng"
|
|
86
|
+
format="YYYY-MM-DD" // onChange trả về: "2026-08-25"
|
|
87
|
+
displayFormat="DD/MM/YYYY" // Ô input hiển thị: "25/08/2026"
|
|
88
|
+
defaultValue="2026-08-25"
|
|
89
|
+
/>
|
|
90
|
+
|
|
91
|
+
// 3. Định dạng Mỹ (MM/DD/YYYY)
|
|
92
|
+
<DatePicker
|
|
93
|
+
label="US Format"
|
|
94
|
+
format="MM/DD/YYYY"
|
|
95
|
+
defaultValue="08/25/2026"
|
|
96
|
+
/>
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
---
|
|
100
|
+
|
|
101
|
+
### 3. Tích hợp View Tabs (Ngày / Tháng / Năm)
|
|
102
|
+
|
|
103
|
+
Bật `config={{ showViewTabs: true }}` để cho phép người dùng chuyển nhanh chế độ chọn:
|
|
104
|
+
|
|
105
|
+
```tsx
|
|
106
|
+
<DatePicker
|
|
107
|
+
label="Thời gian báo cáo"
|
|
108
|
+
format="YYYY-MM-DD"
|
|
109
|
+
displayFormat="DD-MM-YYYY"
|
|
110
|
+
config={{ showViewTabs: true, isClearable: true }}
|
|
111
|
+
// Khi ở Tab Ngày: Hiển thị "25-08-2026", onChange phát "2026-08-25"
|
|
112
|
+
// Khi ở Tab Tháng: Hiển thị "08-2026", onChange phát "2026-08-01"
|
|
113
|
+
// Khi ở Tab Năm: Hiển thị "2026", onChange phát "2026-01-01"
|
|
114
|
+
/>
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
---
|
|
118
|
+
|
|
119
|
+
### 4. Standalone `<Calendar>` (Lịch tĩnh)
|
|
120
|
+
|
|
121
|
+
```tsx
|
|
122
|
+
import { useState } from "react";
|
|
123
|
+
import { Calendar } from "@owa/ui";
|
|
124
|
+
|
|
125
|
+
export function StandaloneCalendarExample() {
|
|
126
|
+
const [selectedDate, setSelectedDate] = useState<Date>(new Date());
|
|
127
|
+
const [view, setView] = useState<"days" | "months" | "years">("days");
|
|
128
|
+
|
|
129
|
+
return (
|
|
130
|
+
<Calendar
|
|
131
|
+
value={selectedDate}
|
|
132
|
+
onChange={setSelectedDate}
|
|
133
|
+
view={view}
|
|
134
|
+
onViewChange={setView}
|
|
135
|
+
showViewTabs={true}
|
|
136
|
+
showWeekNumbers={true}
|
|
137
|
+
color="primary"
|
|
138
|
+
/>
|
|
139
|
+
);
|
|
140
|
+
}
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
---
|
|
144
|
+
|
|
145
|
+
### 5. Vị trí đặt nhãn (`labelPlacement`)
|
|
146
|
+
|
|
147
|
+
```tsx
|
|
148
|
+
// 1. Top (Phía trên - Mặc định)
|
|
149
|
+
<DatePicker label="Ngày bắt đầu" labelPlacement="top" />
|
|
150
|
+
|
|
151
|
+
// 2. Left (Ngang bên trái)
|
|
152
|
+
<DatePicker label="Ngày bắt đầu" labelPlacement="left" />
|
|
153
|
+
|
|
154
|
+
// 3. Floating (Lơ lửng trên viền)
|
|
155
|
+
<DatePicker label="Ngày bắt đầu" labelPlacement="floating" />
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
---
|
|
159
|
+
|
|
160
|
+
### 6. Trạng thái Form & Loading
|
|
161
|
+
|
|
162
|
+
```tsx
|
|
163
|
+
// Bắt buộc nhập (Required)
|
|
164
|
+
<DatePicker label="Ngày hẹn" config={{ isRequired: true }} />
|
|
165
|
+
|
|
166
|
+
// Báo lỗi (Invalid)
|
|
167
|
+
<DatePicker
|
|
168
|
+
label="Ngày kết thúc"
|
|
169
|
+
defaultValue="10/08/2026"
|
|
170
|
+
errorMessage="Ngày kết thúc phải lớn hơn ngày bắt đầu."
|
|
171
|
+
config={{ isInvalid: true }}
|
|
172
|
+
/>
|
|
173
|
+
|
|
174
|
+
// Đang tải (Loading & Spinner)
|
|
175
|
+
<DatePicker
|
|
176
|
+
label="Đang đồng bộ"
|
|
177
|
+
defaultValue="25/08/2026"
|
|
178
|
+
config={{ isLoading: true, showSpinner: true }}
|
|
179
|
+
/>
|
|
180
|
+
|
|
181
|
+
// Vô hiệu hóa (Disabled) hoặc Chỉ đọc (ReadOnly)
|
|
182
|
+
<DatePicker label="Không khả dụng" disabled={true} defaultValue="25/08/2026" />
|
|
183
|
+
<DatePicker label="Chỉ xem" readOnly={true} defaultValue="25/08/2026" />
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
---
|
|
187
|
+
|
|
188
|
+
## 🛠 Bảng thông số Props
|
|
189
|
+
|
|
190
|
+
### `DatePickerProps`
|
|
191
|
+
|
|
192
|
+
| Tên Prop | Kiểu dữ liệu | Giá trị mặc định | Mô tả |
|
|
193
|
+
| :--- | :--- | :---: | :--- |
|
|
194
|
+
| `value` | `Date \| string \| number` | — | Giá trị ngày đang được chọn (Controlled). |
|
|
195
|
+
| `defaultValue` | `Date \| string \| number` | — | Giá trị ngày mặc định ban đầu (Uncontrolled). |
|
|
196
|
+
| `onChange` | `(date: string \| null) => void` | — | Callback kích hoạt khi thay đổi ngày (trả về chuỗi định dạng theo `format`, hoặc `null` khi xóa). |
|
|
197
|
+
| `format` | `string` | `'DD/MM/YYYY'` | Định dạng dữ liệu chính dùng chung cho cả đầu vào (`value`/`defaultValue`) và đầu ra (`onChange`). |
|
|
198
|
+
| `displayFormat` | `string` | Tự động | Định dạng chuỗi hiển thị trực quan trong ô input cho người xem. |
|
|
199
|
+
| `defaultView` | `'days' \| 'months' \| 'years'` | `'days'` | Chế độ xem mặc định ban đầu. |
|
|
200
|
+
| `view` | `'days' \| 'months' \| 'years'` | — | Chế độ xem đang kích hoạt (Controlled). |
|
|
201
|
+
| `onViewChange` | `(view: CalendarView) => void` | — | Callback khi người dùng chuyển đổi chế độ xem. |
|
|
202
|
+
| `viewTabs` | `CalendarView[]` | `['days', 'months', 'years']` | Danh sách các tab hiển thị trên thanh tab. |
|
|
203
|
+
| `minDate` | `Date \| string` | — | Giới hạn ngày nhỏ nhất cho phép chọn. |
|
|
204
|
+
| `maxDate` | `Date \| string` | — | Giới hạn ngày lớn nhất cho phép chọn. |
|
|
205
|
+
| `isDateDisabled` | `(date: Date) => boolean` | — | Hàm callback kiểm tra ngày cụ thể có bị vô hiệu hóa hay không. |
|
|
206
|
+
| `locale` | `'vi' \| 'en' \| LocaleConfig` | `'en'` | Cấu hình ngôn ngữ cho lịch. |
|
|
207
|
+
| `firstDayOfWeek` | `0 \| 1` | `1` | Ngày bắt đầu tuần: `0` (Chủ Nhật) hoặc `1` (Thứ Hai). |
|
|
208
|
+
| `size` | `'xs' \| 'sm' \| 'md' \| 'lg' \| 'xl'` | `'md'` | Kích cỡ ô nhập liệu và các nút bấm. |
|
|
209
|
+
| `variant` | `'outline' \| 'filled' \| 'ghost'` | `'outline'` | Biến thể giao diện của ô nhập liệu. |
|
|
210
|
+
| `color` | `'primary' \| 'secondary' \| 'error' \| 'success' \| 'warning' \| 'info' \| 'neutral'` | `'primary'` | Chủ đề màu sắc theo Design System. |
|
|
211
|
+
| `radius` | `'none' \| 'sm' \| 'md' \| 'lg' \| 'xl' \| 'full'` | — | Độ bo góc của ô nhập và popover. |
|
|
212
|
+
| `label` | `ReactNode` | — | Nhãn tiêu đề hiển thị cho ô nhập liệu. |
|
|
213
|
+
| `labelPlacement` | `'top' \| 'left' \| 'floating'` | `'top'` | Vị trí hiển thị của nhãn. |
|
|
214
|
+
| `placeholder` | `string` | Tự động | Văn bản giữ chỗ khi ô input rỗng. |
|
|
215
|
+
| `helperText` | `ReactNode` | — | Đoạn văn bản hướng dẫn/trợ giúp bên dưới ô. |
|
|
216
|
+
| `errorMessage` | `ReactNode` | — | Thông báo lỗi (tự động bật trạng thái viền đỏ và hiệu ứng xuất hiện). |
|
|
217
|
+
| `disabled` | `boolean` | `false` | Khóa toàn bộ tương tác của ô nhập liệu. |
|
|
218
|
+
| `readOnly` | `boolean` | `false` | Chỉ cho phép xem, không mở popover lịch. |
|
|
219
|
+
| `placement` | `Placement` | `'bottom-start'` | Vị trí mở popover lịch so với ô input (Floating UI). |
|
|
220
|
+
| `config` | `DatePickerConfig` | — | Nhóm cấu hình tập trung các cờ tính năng (xem bảng dưới). |
|
|
221
|
+
| `ref` | `Ref<HTMLInputElement>` | — | Ref chuyển tiếp đến thẻ `<input>` HTML bên dưới. |
|
|
222
|
+
|
|
223
|
+
---
|
|
224
|
+
|
|
225
|
+
### `DatePickerConfig`
|
|
226
|
+
|
|
227
|
+
| Cờ thuộc tính | Kiểu dữ liệu | Giá trị mặc định | Mô tả |
|
|
228
|
+
| :--- | :--- | :---: | :--- |
|
|
229
|
+
| `isRequired` | `boolean` | `false` | Hiển thị dấu sao đỏ `*` và đánh dấu `aria-required="true"`. |
|
|
230
|
+
| `isInvalid` | `boolean` | `false` | Bật trạng thái viền đỏ báo lỗi và `aria-invalid="true"`. |
|
|
231
|
+
| `isLoading` | `boolean` | `false` | Khóa tương tác, bật `aria-busy="true"` và `aria-disabled="true"`. |
|
|
232
|
+
| `showSpinner` | `boolean` | `false` | Hiển thị biểu tượng xoay spinner khi `isLoading={true}`. |
|
|
233
|
+
| `isClearable` | `boolean` | `true` | Hiển thị nút xóa nhanh nội dung khi có ngày được chọn. |
|
|
234
|
+
| `isFullWidth` | `boolean` | `false` | Mở rộng chiếm toàn bộ 100% chiều ngang container cha. |
|
|
235
|
+
| `showWeekNumbers` | `boolean` | `false` | Hiển thị cột số thứ tự tuần trong lịch. |
|
|
236
|
+
| `showViewTabs` | `boolean` | `false` | Hiển thị thanh chuyển Tab Ngày / Tháng / Năm. |
|
|
237
|
+
| `closeOnSelect` | `boolean` | `true` | Tự động đóng popover lịch ngay sau khi chọn ngày. |
|
|
238
|
+
|
|
239
|
+
---
|
|
240
|
+
|
|
241
|
+
### `CalendarProps` (Standalone)
|
|
242
|
+
|
|
243
|
+
| Tên Prop | Kiểu dữ liệu | Giá trị mặc định | Mô tả |
|
|
244
|
+
| :--- | :--- | :---: | :--- |
|
|
245
|
+
| `value` | `Date \| null` | — | Đối tượng ngày đang được chọn. |
|
|
246
|
+
| `onChange` | `(date: Date) => void` | — | Callback khi chọn ngày trên lịch. |
|
|
247
|
+
| `view` | `'days' \| 'months' \| 'years'` | `'days'` | Chế độ xem hiện tại của lịch. |
|
|
248
|
+
| `onViewChange` | `(view: CalendarView) => void` | — | Callback khi người dùng chuyển đổi chế độ xem. |
|
|
249
|
+
| `minDate` | `Date \| string` | — | Giới hạn ngày nhỏ nhất cho phép chọn. |
|
|
250
|
+
| `maxDate` | `Date \| string` | — | Giới hạn ngày lớn nhất cho phép chọn. |
|
|
251
|
+
| `isDateDisabled` | `(date: Date) => boolean` | — | Hàm kiểm tra ngày bị disable. |
|
|
252
|
+
| `locale` | `'vi' \| 'en' \| LocaleConfig` | `'vi'` | Cấu hình ngôn ngữ cho lịch. |
|
|
253
|
+
| `firstDayOfWeek` | `0 \| 1` | `1` | Ngày bắt đầu tuần: `0` (Chủ Nhật) hoặc `1` (Thứ Hai). |
|
|
254
|
+
| `showWeekNumbers` | `boolean` | `false` | Hiển thị cột số thứ tự tuần. |
|
|
255
|
+
| `showViewTabs` | `boolean` | `false` | Hiển thị thanh Tab Ngày / Tháng / Năm phía trên lịch. |
|
|
256
|
+
| `viewTabs` | `CalendarView[]` | `['days', 'months', 'years']` | Danh sách các tab hiển thị. |
|
|
257
|
+
| `size` | `DatePickerSize` | `'md'` | Kích cỡ các ô ngày và nút bấm trong lịch. |
|
|
258
|
+
| `color` | `DatePickerColor` | `'primary'` | Chủ đề màu sắc theo Design System. |
|
|
259
|
+
| `radius` | `DatePickerRadius` | `'lg'` | Bo góc khung viền lịch. |
|
|
@@ -0,0 +1,260 @@
|
|
|
1
|
+
# 📆 DateRangePicker Component Suite (`@owa/ui`)
|
|
2
|
+
|
|
3
|
+
Bộ component **DateRangePicker** & **DateRangeCalendar** toàn diện, linh hoạt và tương tác cao, thiết kế chuẩn **Design System**, hỗ trợ **Chọn khoảng ngày trên 2 tháng liên tiếp song song**, **3 Chế độ View (Khoảng Ngày / Tháng / Năm)**, **Định dạng dữ liệu chuẩn hóa (`format`)**, **Hiển thị trực quan thích ứng (`displayFormat`)**, **Số thứ tự tuần (`showWeekNumbers`)**, và tuân thủ đầy đủ tiêu chuẩn **WAI-ARIA Accessibility**.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## 🌟 Điểm nổi bật
|
|
8
|
+
|
|
9
|
+
- **Chọn khoảng ngày trực quan (2 Tháng song song)**:
|
|
10
|
+
- Hiển thị cùng lúc 2 tháng liên tiếp cạnh nhau giúp người dùng dễ dàng chọn khoảng thời gian kéo dài qua nhiều tháng.
|
|
11
|
+
- Hỗ trợ hiệu ứng rê chuột xem trước khoảng ngày (Hover Range Preview).
|
|
12
|
+
- **Tách bạch giữa Dữ liệu (`format`) và Hiển thị (`displayFormat`)**:
|
|
13
|
+
- `format` *(mặc định `'DD/MM/YYYY'`)*: Dữ liệu phát ra qua `onChange` là mảng 2 phần tử `[string, string]` luôn tuân thủ 100% theo chuẩn quy định (ví dụ: `["2026-08-01", "2026-08-15"]`).
|
|
14
|
+
- `displayFormat` *(tùy chọn)*: Tùy biến hiển thị trên ô input (ví dụ: `01/08/2026 - 15/08/2026`).
|
|
15
|
+
- **3 Chế độ Xem Linh Hoạt (`view` & `viewTabs`)**:
|
|
16
|
+
- `days`: Chọn khoảng ngày cụ thể.
|
|
17
|
+
- `months`: Chọn khoảng tháng trong năm (hiển thị `MM-YYYY - MM-YYYY` hoặc `MM/YYYY - MM/YYYY`).
|
|
18
|
+
- `years`: Chọn khoảng năm trong thập kỷ (hiển thị `YYYY - YYYY`).
|
|
19
|
+
- **Tùy biến ký tự phân cách (`separator`)**: Mặc định là `' - '`, có thể tùy biến thành `' to '`, `' ~ '`...
|
|
20
|
+
- **Standalone `<DateRangeCalendar>` Component**: Có thể sử dụng độc lập dưới dạng lịch chọn range gắn tĩnh trên giao diện.
|
|
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`, `error`, `success`, `warning`, `info`, `neutral`.
|
|
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
|
+
- **Hỗ trợ Đa Ngôn Ngữ (`locale`)**: Tích hợp sẵn tiếng Việt (`'vi'`), tiếng Anh (`'en'`), và cho phép tùy biến `LocaleConfig`.
|
|
27
|
+
- **Số thứ tự tuần (`showWeekNumbers`)**: Tự động tính số tuần chuẩn ISO 8601 kèm cột header `#` và nhãn `W1` - `W53`.
|
|
28
|
+
|
|
29
|
+
---
|
|
30
|
+
|
|
31
|
+
## 🚀 Cài đặt & Import
|
|
32
|
+
|
|
33
|
+
```tsx
|
|
34
|
+
import { DateRangePicker, DateRangeCalendar } from "@owa/ui";
|
|
35
|
+
import type {
|
|
36
|
+
DateRangePickerProps,
|
|
37
|
+
DateRangePickerConfig,
|
|
38
|
+
DateRangeCalendarProps,
|
|
39
|
+
DateRange,
|
|
40
|
+
DateRangeValue,
|
|
41
|
+
DateRangePickerSize,
|
|
42
|
+
DateRangePickerVariant,
|
|
43
|
+
DateRangePickerColor,
|
|
44
|
+
DateRangePickerRadius,
|
|
45
|
+
LabelPlacement,
|
|
46
|
+
LocaleConfig,
|
|
47
|
+
} from "@owa/ui";
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
---
|
|
51
|
+
|
|
52
|
+
## 📖 Hướng dẫn sử dụng
|
|
53
|
+
|
|
54
|
+
### 1. Cách sử dụng cơ bản
|
|
55
|
+
|
|
56
|
+
```tsx
|
|
57
|
+
import { useState } from "react";
|
|
58
|
+
import { DateRangePicker } from "@owa/ui";
|
|
59
|
+
|
|
60
|
+
export function BasicDateRangePickerExample() {
|
|
61
|
+
const [range, setRange] = useState<[string, string] | null>(["01/08/2026", "15/08/2026"]);
|
|
62
|
+
|
|
63
|
+
return (
|
|
64
|
+
<DateRangePicker
|
|
65
|
+
label="Khoảng thời gian nghỉ phép"
|
|
66
|
+
value={range}
|
|
67
|
+
onChange={setRange}
|
|
68
|
+
placeholder="DD/MM/YYYY - DD/MM/YYYY"
|
|
69
|
+
/>
|
|
70
|
+
);
|
|
71
|
+
}
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
---
|
|
75
|
+
|
|
76
|
+
### 2. Định dạng Dữ liệu (`format`) và Hiển thị (`displayFormat`)
|
|
77
|
+
|
|
78
|
+
```tsx
|
|
79
|
+
// 1. Dùng chung 1 format ISO cho cả dữ liệu và hiển thị
|
|
80
|
+
<DateRangePicker
|
|
81
|
+
label="Khoảng ngày ISO"
|
|
82
|
+
format="YYYY-MM-DD"
|
|
83
|
+
defaultValue={["2026-08-01", "2026-08-15"]}
|
|
84
|
+
/>
|
|
85
|
+
|
|
86
|
+
// 2. Dữ liệu chuẩn ISO (gửi API) nhưng hiển thị tiếng Việt
|
|
87
|
+
<DateRangePicker
|
|
88
|
+
label="Khoảng ngày dự án"
|
|
89
|
+
format="YYYY-MM-DD" // onChange trả về: ["2026-08-01", "2026-08-15"]
|
|
90
|
+
displayFormat="DD/MM/YYYY" // Ô input hiển thị: "01/08/2026 - 15/08/2026"
|
|
91
|
+
defaultValue={["2026-08-01", "2026-08-15"]}
|
|
92
|
+
/>
|
|
93
|
+
|
|
94
|
+
// 3. Tùy biến ký tự phân cách (separator)
|
|
95
|
+
<DateRangePicker
|
|
96
|
+
label="Khoảng thời gian"
|
|
97
|
+
separator=" to "
|
|
98
|
+
defaultValue={["01/08/2026", "15/08/2026"]}
|
|
99
|
+
/>
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
---
|
|
103
|
+
|
|
104
|
+
### 3. Tích hợp View Tabs (Khoảng Ngày / Tháng / Năm)
|
|
105
|
+
|
|
106
|
+
Bật `config={{ showViewTabs: true }}` để cho phép người dùng chọn khoảng tháng hoặc năm:
|
|
107
|
+
|
|
108
|
+
```tsx
|
|
109
|
+
<DateRangePicker
|
|
110
|
+
label="Kỳ kế toán / Báo cáo tài chính"
|
|
111
|
+
format="YYYY-MM-DD"
|
|
112
|
+
displayFormat="DD-MM-YYYY"
|
|
113
|
+
config={{ showViewTabs: true, isClearable: true }}
|
|
114
|
+
// Khi ở Tab Ngày: Hiển thị "01-08-2026 - 15-08-2026", onChange phát ["2026-08-01", "2026-08-15"]
|
|
115
|
+
// Khi ở Tab Tháng: Hiển thị "03-2026 - 08-2026", onChange phát ["2026-03-01", "2026-08-01"]
|
|
116
|
+
// Khi ở Tab Năm: Hiển thị "2026 - 2030", onChange phát ["2026-01-01", "2030-01-01"]
|
|
117
|
+
/>
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
---
|
|
121
|
+
|
|
122
|
+
### 4. Standalone `<DateRangeCalendar>` (Lịch tĩnh 2 tháng)
|
|
123
|
+
|
|
124
|
+
```tsx
|
|
125
|
+
import { useState } from "react";
|
|
126
|
+
import { DateRangeCalendar } from "@owa/ui";
|
|
127
|
+
|
|
128
|
+
export function StandaloneDateRangeCalendarExample() {
|
|
129
|
+
const [range, setRange] = useState<[Date | null, Date | null]>([
|
|
130
|
+
new Date(2026, 7, 5),
|
|
131
|
+
new Date(2026, 7, 20),
|
|
132
|
+
]);
|
|
133
|
+
|
|
134
|
+
return (
|
|
135
|
+
<DateRangeCalendar
|
|
136
|
+
value={range}
|
|
137
|
+
onChange={setRange}
|
|
138
|
+
showWeekNumbers={true}
|
|
139
|
+
color="primary"
|
|
140
|
+
/>
|
|
141
|
+
);
|
|
142
|
+
}
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
---
|
|
146
|
+
|
|
147
|
+
### 5. Vị trí đặt nhãn (`labelPlacement`)
|
|
148
|
+
|
|
149
|
+
```tsx
|
|
150
|
+
// 1. Top (Phía trên - Mặc định)
|
|
151
|
+
<DateRangePicker label="Thời gian thực hiện" labelPlacement="top" />
|
|
152
|
+
|
|
153
|
+
// 2. Left (Ngang bên trái)
|
|
154
|
+
<DateRangePicker label="Thời gian thực hiện" labelPlacement="left" />
|
|
155
|
+
|
|
156
|
+
// 3. Floating (Lơ lửng trên viền)
|
|
157
|
+
<DateRangePicker label="Thời gian thực hiện" labelPlacement="floating" />
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
---
|
|
161
|
+
|
|
162
|
+
### 6. Trạng thái Form & Loading
|
|
163
|
+
|
|
164
|
+
```tsx
|
|
165
|
+
// Bắt buộc nhập (Required)
|
|
166
|
+
<DateRangePicker label="Thời gian hiệu lực" config={{ isRequired: true }} />
|
|
167
|
+
|
|
168
|
+
// Báo lỗi (Invalid)
|
|
169
|
+
<DateRangePicker
|
|
170
|
+
label="Khoảng ngày"
|
|
171
|
+
errorMessage="Khoảng ngày đã chọn không hợp lệ."
|
|
172
|
+
config={{ isInvalid: true }}
|
|
173
|
+
/>
|
|
174
|
+
|
|
175
|
+
// Đang tải (Loading & Spinner)
|
|
176
|
+
<DateRangePicker
|
|
177
|
+
label="Đang tải dữ liệu"
|
|
178
|
+
config={{ isLoading: true, showSpinner: true }}
|
|
179
|
+
/>
|
|
180
|
+
|
|
181
|
+
// Vô hiệu hóa (Disabled) hoặc Chỉ đọc (ReadOnly)
|
|
182
|
+
<DateRangePicker label="Không khả dụng" disabled={true} />
|
|
183
|
+
<DateRangePicker label="Chỉ xem" readOnly={true} />
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
---
|
|
187
|
+
|
|
188
|
+
## 🛠 Bảng thông số Props
|
|
189
|
+
|
|
190
|
+
### `DateRangePickerProps`
|
|
191
|
+
|
|
192
|
+
| Tên Prop | Kiểu dữ liệu | Giá trị mặc định | Mô tả |
|
|
193
|
+
| :--- | :--- | :---: | :--- |
|
|
194
|
+
| `value` | `[DateValue, DateValue]` | — | Mảng khoảng ngày đang được chọn (Controlled). |
|
|
195
|
+
| `defaultValue` | `[DateValue, DateValue]` | — | Mảng khoảng ngày mặc định ban đầu (Uncontrolled). |
|
|
196
|
+
| `onChange` | `(range: [string, string] \| null) => void` | — | Callback kích hoạt khi thay đổi khoảng ngày (trả về mảng 2 chuỗi định dạng theo `format`, hoặc `null` khi xóa). |
|
|
197
|
+
| `separator` | `string` | `' - '` | Chuỗi ký tự phân cách giữa 2 ngày trong ô input. |
|
|
198
|
+
| `format` | `string` | `'DD/MM/YYYY'` | Định dạng dữ liệu chính dùng chung cho cả đầu vào (`value`/`defaultValue`) và đầu ra (`onChange`). |
|
|
199
|
+
| `displayFormat` | `string` | Tự động | Định dạng chuỗi hiển thị trực quan trong ô input cho người xem. |
|
|
200
|
+
| `defaultView` | `'days' \| 'months' \| 'years'` | `'days'` | Chế độ xem mặc định ban đầu. |
|
|
201
|
+
| `view` | `'days' \| 'months' \| 'years'` | — | Chế độ xem đang kích hoạt (Controlled). |
|
|
202
|
+
| `onViewChange` | `(view: CalendarView) => void` | — | Callback khi người dùng chuyển đổi chế độ xem. |
|
|
203
|
+
| `viewTabs` | `CalendarView[]` | `['days', 'months', 'years']` | Danh sách các tab hiển thị trên thanh tab. |
|
|
204
|
+
| `minDate` | `Date \| string` | — | Giới hạn ngày nhỏ nhất cho phép chọn. |
|
|
205
|
+
| `maxDate` | `Date \| string` | — | Giới hạn ngày lớn nhất cho phép chọn. |
|
|
206
|
+
| `isDateDisabled` | `(date: Date) => boolean` | — | Hàm callback kiểm tra ngày cụ thể có bị vô hiệu hóa hay không. |
|
|
207
|
+
| `locale` | `'vi' \| 'en' \| LocaleConfig` | `'en'` | Cấu hình ngôn ngữ cho lịch. |
|
|
208
|
+
| `firstDayOfWeek` | `0 \| 1` | `1` | Ngày bắt đầu tuần: `0` (Chủ Nhật) hoặc `1` (Thứ Hai). |
|
|
209
|
+
| `size` | `'xs' \| 'sm' \| 'md' \| 'lg' \| 'xl'` | `'md'` | Kích cỡ ô nhập liệu và các nút bấm. |
|
|
210
|
+
| `variant` | `'outline' \| 'filled' \| 'ghost'` | `'outline'` | Biến thể giao diện của ô nhập liệu. |
|
|
211
|
+
| `color` | `'primary' \| 'secondary' \| 'error' \| 'success' \| 'warning' \| 'info' \| 'neutral'` | `'primary'` | Chủ đề màu sắc theo Design System. |
|
|
212
|
+
| `radius` | `'none' \| 'sm' \| 'md' \| 'lg' \| 'xl' \| 'full'` | — | Độ bo góc của ô nhập và popover. |
|
|
213
|
+
| `label` | `ReactNode` | — | Nhãn tiêu đề hiển thị cho ô nhập liệu. |
|
|
214
|
+
| `labelPlacement` | `'top' \| 'left' \| 'floating'` | `'top'` | Vị trí hiển thị của nhãn. |
|
|
215
|
+
| `placeholder` | `string` | Tự động | Văn bản giữ chỗ khi ô input rỗng. |
|
|
216
|
+
| `helperText` | `ReactNode` | — | Đoạn văn bản hướng dẫn/trợ giúp bên dưới ô. |
|
|
217
|
+
| `errorMessage` | `ReactNode` | — | Thông báo lỗi (tự động bật trạng thái viền đỏ và hiệu ứng xuất hiện). |
|
|
218
|
+
| `disabled` | `boolean` | `false` | Khóa toàn bộ tương tác của ô nhập liệu. |
|
|
219
|
+
| `readOnly` | `boolean` | `false` | Chỉ cho phép xem, không mở popover lịch. |
|
|
220
|
+
| `placement` | `Placement` | `'bottom-start'` | Vị trí mở popover lịch so với ô input (Floating UI). |
|
|
221
|
+
| `config` | `DateRangePickerConfig` | — | Nhóm cấu hình tập trung các cờ tính năng (xem bảng dưới). |
|
|
222
|
+
| `ref` | `Ref<HTMLInputElement>` | — | Ref chuyển tiếp đến thẻ `<input>` HTML bên dưới. |
|
|
223
|
+
|
|
224
|
+
---
|
|
225
|
+
|
|
226
|
+
### `DateRangePickerConfig`
|
|
227
|
+
|
|
228
|
+
| Cờ thuộc tính | Kiểu dữ liệu | Giá trị mặc định | Mô tả |
|
|
229
|
+
| :--- | :--- | :---: | :--- |
|
|
230
|
+
| `isRequired` | `boolean` | `false` | Hiển thị dấu sao đỏ `*` và đánh dấu `aria-required="true"`. |
|
|
231
|
+
| `isInvalid` | `boolean` | `false` | Bật trạng thái viền đỏ báo lỗi và `aria-invalid="true"`. |
|
|
232
|
+
| `isLoading` | `boolean` | `false` | Khóa tương tác, bật `aria-busy="true"` và `aria-disabled="true"`. |
|
|
233
|
+
| `showSpinner` | `boolean` | `false` | Hiển thị biểu tượng xoay spinner khi `isLoading={true}`. |
|
|
234
|
+
| `isClearable` | `boolean` | `true` | Hiển thị nút xóa nhanh khoảng ngày đã chọn. |
|
|
235
|
+
| `isFullWidth` | `boolean` | `false` | Mở rộng chiếm toàn bộ 100% chiều ngang container cha. |
|
|
236
|
+
| `showWeekNumbers` | `boolean` | `false` | Hiển thị cột số thứ tự tuần trong cả 2 bảng lịch. |
|
|
237
|
+
| `showViewTabs` | `boolean` | `false` | Hiển thị thanh chuyển Tab Ngày / Tháng / Năm. |
|
|
238
|
+
| `closeOnSelect` | `boolean` | `true` | Tự động đóng popover lịch ngay sau khi chọn xong ngày kết thúc. |
|
|
239
|
+
|
|
240
|
+
---
|
|
241
|
+
|
|
242
|
+
### `DateRangeCalendarProps` (Standalone)
|
|
243
|
+
|
|
244
|
+
| Tên Prop | Kiểu dữ liệu | Giá trị mặc định | Mô tả |
|
|
245
|
+
| :--- | :--- | :---: | :--- |
|
|
246
|
+
| `value` | `[Date \| null, Date \| null]` | — | Mảng 2 đối tượng `[start, end]` đang được chọn. |
|
|
247
|
+
| `onChange` | `(range: [Date \| null, Date \| null]) => void` | — | Callback khi chọn khoảng ngày trên lịch. |
|
|
248
|
+
| `view` | `'days' \| 'months' \| 'years'` | `'days'` | Chế độ xem hiện tại của lịch. |
|
|
249
|
+
| `onViewChange` | `(view: CalendarView) => void` | — | Callback khi người dùng chuyển đổi chế độ xem. |
|
|
250
|
+
| `minDate` | `Date \| string` | — | Giới hạn ngày nhỏ nhất cho phép chọn. |
|
|
251
|
+
| `maxDate` | `Date \| string` | — | Giới hạn ngày lớn nhất cho phép chọn. |
|
|
252
|
+
| `isDateDisabled` | `(date: Date) => boolean` | — | Hàm kiểm tra ngày bị disable. |
|
|
253
|
+
| `locale` | `'vi' \| 'en' \| LocaleConfig` | `'vi'` | Cấu hình ngôn ngữ cho lịch. |
|
|
254
|
+
| `firstDayOfWeek` | `0 \| 1` | `1` | Ngày bắt đầu tuần: `0` (Chủ Nhật) hoặc `1` (Thứ Hai). |
|
|
255
|
+
| `showWeekNumbers` | `boolean` | `false` | Hiển thị cột số thứ tự tuần. |
|
|
256
|
+
| `showViewTabs` | `boolean` | `false` | Hiển thị thanh Tab Ngày / Tháng / Năm phía trên lịch. |
|
|
257
|
+
| `viewTabs` | `CalendarView[]` | `['days', 'months', 'years']` | Danh sách các tab hiển thị. |
|
|
258
|
+
| `size` | `DateRangePickerSize` | `'md'` | Kích cỡ các ô ngày và nút bấm trong lịch. |
|
|
259
|
+
| `color` | `DateRangePickerColor` | `'primary'` | Chủ đề màu sắc theo Design System. |
|
|
260
|
+
| `radius` | `DateRangePickerRadius` | `'lg'` | Bo góc khung viền lịch. |
|