@openway/ui 1.0.0 → 1.0.1
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-CJQNI3QX.js +3 -0
- package/dist/chunk-CJQNI3QX.js.map +1 -0
- package/dist/index.cjs +5 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +2642 -1103
- package/dist/index.d.ts +2642 -1103
- 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 +309 -0
- package/dist/query.d.ts +309 -0
- package/dist/query.js +3 -0
- package/dist/query.js.map +1 -0
- package/dist/useInfiniteScroll-DAlgjUN4.d.cts +325 -0
- package/dist/useInfiniteScroll-DAlgjUN4.d.ts +325 -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/useSelectInfiniteQuery.md +123 -0
- package/docs/hooks/useTableQuery.md +124 -0
- package/package.json +31 -12
|
@@ -0,0 +1,325 @@
|
|
|
1
|
+
import { HTMLAttributes, Ref, ReactNode, RefObject } from 'react';
|
|
2
|
+
import { Placement } from '@floating-ui/react';
|
|
3
|
+
|
|
4
|
+
type EmptySize = "sm" | "md" | "lg";
|
|
5
|
+
type EmptyPresetImage = "default" | "search" | "error" | "folder" | "simple";
|
|
6
|
+
type EmptyLayout = "vertical" | "horizontal";
|
|
7
|
+
interface EmptyProps extends Omit<HTMLAttributes<HTMLDivElement>, "title"> {
|
|
8
|
+
/**
|
|
9
|
+
* Ref chuyển tiếp đến phần tử HTML div
|
|
10
|
+
*/
|
|
11
|
+
ref?: Ref<HTMLDivElement>;
|
|
12
|
+
/**
|
|
13
|
+
* Kích cỡ của Empty component:
|
|
14
|
+
* - 'sm': Nhỏ gọn, thích hợp cho dropdown, popover, bảng nhỏ
|
|
15
|
+
* - 'md': Tiêu chuẩn, thích hợp cho card, section, modal
|
|
16
|
+
* - 'lg': Lớn, thích hợp cho toàn trang, dashboard
|
|
17
|
+
* @default 'md'
|
|
18
|
+
*/
|
|
19
|
+
size?: EmptySize;
|
|
20
|
+
/**
|
|
21
|
+
* Bố cục hiển thị:
|
|
22
|
+
* - 'vertical': Xếp dọc ảnh -> tiêu đề -> mô tả -> hành động
|
|
23
|
+
* - 'horizontal': Dàn ngang ảnh bên trái, nội dung & hành động bên phải
|
|
24
|
+
* @default 'vertical'
|
|
25
|
+
*/
|
|
26
|
+
layout?: EmptyLayout;
|
|
27
|
+
/**
|
|
28
|
+
* Ảnh hoặc minh họa:
|
|
29
|
+
* - Tên preset: 'default' | 'search' | 'error' | 'folder' | 'simple'
|
|
30
|
+
* - URL hình ảnh (string bắt đầu bằng http://, https://, /, hoặc ./)
|
|
31
|
+
* - ReactNode tùy biến (SVG icon, custom component, v.v.)
|
|
32
|
+
* @default 'default'
|
|
33
|
+
*/
|
|
34
|
+
image?: EmptyPresetImage | (string & {}) | ReactNode;
|
|
35
|
+
/**
|
|
36
|
+
* Kích thước tùy chỉnh cho ảnh / illustration (pixel hoặc chuỗi kích thước CSS)
|
|
37
|
+
*/
|
|
38
|
+
imageSize?: number | string;
|
|
39
|
+
/**
|
|
40
|
+
* Class tùy biến cho phần bao ngoài của ảnh
|
|
41
|
+
*/
|
|
42
|
+
imageClassName?: string;
|
|
43
|
+
/**
|
|
44
|
+
* Thuộc tính alt cho hình ảnh khi truyền URL
|
|
45
|
+
* @default 'Empty'
|
|
46
|
+
*/
|
|
47
|
+
imageAlt?: string;
|
|
48
|
+
/**
|
|
49
|
+
* Tiêu đề của trạng thái rỗng
|
|
50
|
+
*/
|
|
51
|
+
title?: ReactNode;
|
|
52
|
+
/**
|
|
53
|
+
* Class tùy biến cho tiêu đề
|
|
54
|
+
*/
|
|
55
|
+
titleClassName?: string;
|
|
56
|
+
/**
|
|
57
|
+
* Đoạn văn bản mô tả chi tiết hoặc hướng dẫn thao tác
|
|
58
|
+
*/
|
|
59
|
+
description?: ReactNode;
|
|
60
|
+
/**
|
|
61
|
+
* Class tùy biến cho phần mô tả
|
|
62
|
+
*/
|
|
63
|
+
descriptionClassName?: string;
|
|
64
|
+
/**
|
|
65
|
+
* Nút bấm hoặc nhóm hành động bên dưới (Call-to-Action)
|
|
66
|
+
*/
|
|
67
|
+
actions?: ReactNode;
|
|
68
|
+
/**
|
|
69
|
+
* Class tùy biến cho khu vực chứa actions
|
|
70
|
+
*/
|
|
71
|
+
actionsClassName?: string;
|
|
72
|
+
/**
|
|
73
|
+
* Nội dung bổ sung tùy biến bên dưới hoặc thay thế
|
|
74
|
+
*/
|
|
75
|
+
children?: ReactNode;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
type SelectSize = "xs" | "sm" | "md" | "lg" | "xl";
|
|
79
|
+
type SelectVariant = "outline" | "filled" | "ghost" | "other";
|
|
80
|
+
type SelectColor = "primary" | "secondary" | "error" | "success" | "warning" | "info" | "neutral";
|
|
81
|
+
type SelectRadius = "none" | "sm" | "md" | "lg" | "xl" | "full";
|
|
82
|
+
type SelectSearchMode = "client" | "server";
|
|
83
|
+
type SelectSearchPlacement = "trigger" | "menu" | "both";
|
|
84
|
+
type SelectFilterLayout = "vertical" | "grid";
|
|
85
|
+
type LabelPlacement = "top" | "left" | "floating";
|
|
86
|
+
interface SelectOptionItem<TData = unknown> {
|
|
87
|
+
value: string | number;
|
|
88
|
+
label: string;
|
|
89
|
+
disabled?: boolean;
|
|
90
|
+
description?: ReactNode;
|
|
91
|
+
icon?: ReactNode;
|
|
92
|
+
data?: TData;
|
|
93
|
+
}
|
|
94
|
+
type SelectFilterType = "string" | "text" | "number" | "date" | "date-range" | "checkbox-group" | "custom";
|
|
95
|
+
interface SelectFilterField<TValue = unknown> {
|
|
96
|
+
/** Tên định danh trường lọc (dùng làm key trong object filters) */
|
|
97
|
+
name: string;
|
|
98
|
+
/** Loại component lọc */
|
|
99
|
+
type: SelectFilterType;
|
|
100
|
+
/** Nhãn hiển thị của bộ lọc */
|
|
101
|
+
label?: ReactNode;
|
|
102
|
+
/** Placeholder cho input */
|
|
103
|
+
placeholder?: string;
|
|
104
|
+
/** Giá trị mặc định */
|
|
105
|
+
defaultValue?: TValue;
|
|
106
|
+
/** Giá trị điều khiển */
|
|
107
|
+
value?: TValue;
|
|
108
|
+
/** Số cột chiếm trong grid layout (1, 2, 3...) */
|
|
109
|
+
colSpan?: number;
|
|
110
|
+
/** Danh sách options (dùng cho checkbox-group hoặc select con) */
|
|
111
|
+
options?: {
|
|
112
|
+
label: ReactNode;
|
|
113
|
+
value: string | number;
|
|
114
|
+
disabled?: boolean;
|
|
115
|
+
}[];
|
|
116
|
+
/** Props truyền thêm vào component tương ứng (InputProps, DatePickerProps, ...) */
|
|
117
|
+
props?: Record<string, unknown>;
|
|
118
|
+
/** Custom render khi type="custom" */
|
|
119
|
+
render?: (fieldState: {
|
|
120
|
+
value: TValue | undefined;
|
|
121
|
+
onChange: (val: TValue) => void;
|
|
122
|
+
}) => ReactNode;
|
|
123
|
+
}
|
|
124
|
+
interface BaseSelectProps<TData = unknown, TFilters extends Record<string, unknown> = Record<string, unknown>> {
|
|
125
|
+
/** Ref chuyển tiếp tới root wrapper element */
|
|
126
|
+
ref?: Ref<HTMLDivElement>;
|
|
127
|
+
/** Kích thước: xs, sm, md, lg, xl. Mặc định 'md' */
|
|
128
|
+
size?: SelectSize;
|
|
129
|
+
/** Biến thể giao diện: outline, filled, ghost, other. Mặc định 'outline' */
|
|
130
|
+
variant?: SelectVariant;
|
|
131
|
+
/** Chủ đề màu sắc: primary, secondary, error, success, warning, info, neutral. Mặc định 'primary' */
|
|
132
|
+
color?: SelectColor;
|
|
133
|
+
/** Độ bo góc: none, sm, md, lg, xl, full */
|
|
134
|
+
radius?: SelectRadius;
|
|
135
|
+
/** Danh sách các tùy chọn */
|
|
136
|
+
options: SelectOptionItem<TData>[];
|
|
137
|
+
/** ID định danh cho Select */
|
|
138
|
+
id?: string;
|
|
139
|
+
/** Nhãn tiêu đề của Select */
|
|
140
|
+
label?: ReactNode;
|
|
141
|
+
/** Vị trí nhãn: top, left, floating. Mặc định 'top' */
|
|
142
|
+
labelPlacement?: LabelPlacement;
|
|
143
|
+
/** ClassName tùy biến cho nhãn */
|
|
144
|
+
labelClassName?: string;
|
|
145
|
+
/** Văn bản gợi ý khi chưa chọn */
|
|
146
|
+
placeholder?: string;
|
|
147
|
+
/** Văn bản hướng dẫn bên dưới */
|
|
148
|
+
helperText?: ReactNode;
|
|
149
|
+
/** Thông báo lỗi khi không hợp lệ */
|
|
150
|
+
errorMessage?: ReactNode;
|
|
151
|
+
/** Trạng thái không hợp lệ */
|
|
152
|
+
isInvalid?: boolean;
|
|
153
|
+
/** Bắt buộc chọn */
|
|
154
|
+
isRequired?: boolean;
|
|
155
|
+
/** Vô hiệu hóa Select */
|
|
156
|
+
isDisabled?: boolean;
|
|
157
|
+
/** Chỉ đọc, không cho phép thay đổi */
|
|
158
|
+
readOnly?: boolean;
|
|
159
|
+
/** Tên trường form HTML để submit */
|
|
160
|
+
name?: string;
|
|
161
|
+
/** Cho phép tìm kiếm. Mặc định false */
|
|
162
|
+
searchable?: boolean;
|
|
163
|
+
/** Chế độ tìm kiếm: 'client' (fuzzy search) hoặc 'server' (debounce + API). Mặc định 'client' */
|
|
164
|
+
searchMode?: SelectSearchMode;
|
|
165
|
+
/** Vị trí ô tìm kiếm: 'trigger' (trên trigger), 'menu' (trong dropdown), 'both'. Mặc định 'trigger' */
|
|
166
|
+
searchPlacement?: SelectSearchPlacement;
|
|
167
|
+
/** Placeholder cho ô tìm kiếm */
|
|
168
|
+
searchPlaceholder?: string;
|
|
169
|
+
/** Trường dùng để tìm kiếm (mặc định ['label', 'value']), hỗ trợ string, mảng hoặc hàm accessor tương tự CheckboxGroup */
|
|
170
|
+
searchField?: (keyof SelectOptionItem<TData> | string | ((item: SelectOptionItem<TData>) => string | undefined | null))[] | (keyof SelectOptionItem<TData> | string);
|
|
171
|
+
/** Giá trị từ khóa tìm kiếm (khi controlled) */
|
|
172
|
+
searchValue?: string;
|
|
173
|
+
/** Callback khi từ khóa thay đổi */
|
|
174
|
+
onSearchChange?: (value: string) => void;
|
|
175
|
+
/** Cấu hình danh sách các bộ lọc hiển thị bên trong dropdown menu */
|
|
176
|
+
menuFilters?: SelectFilterField<unknown>[];
|
|
177
|
+
/** Giá trị điều khiển cho các bộ lọc trong menu */
|
|
178
|
+
menuFilterValues?: Partial<TFilters>;
|
|
179
|
+
/** Bố cục danh sách bộ lọc: 'vertical' hoặc 'grid'. Mặc định 'vertical' */
|
|
180
|
+
menuFilterLayout?: SelectFilterLayout;
|
|
181
|
+
/** Số cột khi menuFilterLayout='grid'. Mặc định 2 */
|
|
182
|
+
menuFilterGridCols?: number;
|
|
183
|
+
/** Hiển thị nút Reset / Xóa bộ lọc. Mặc định true khi có menuFilters */
|
|
184
|
+
showResetFilters?: boolean;
|
|
185
|
+
/** Văn bản nút Reset bộ lọc. Mặc định 'Xoá bộ lọc' */
|
|
186
|
+
resetFiltersText?: ReactNode;
|
|
187
|
+
/** Callback khi bất kỳ filter nào trong menu thay đổi */
|
|
188
|
+
onMenuFilterChange?: (filters: TFilters) => void;
|
|
189
|
+
/** Nội dung tùy biến trên đầu menu (trước hoặc thay thế filter) */
|
|
190
|
+
menuHeader?: ReactNode;
|
|
191
|
+
/** Nội dung tùy biến dưới đáy menu */
|
|
192
|
+
menuFooter?: ReactNode;
|
|
193
|
+
/** Nội dung tùy biến ở đáy danh sách tùy chọn bên trong vùng cuộn (ví dụ: Sentinel/Skeleton/Spinner khi phân trang Infinite Scroll) */
|
|
194
|
+
listFooter?: ReactNode;
|
|
195
|
+
/** Callback được gọi sau khi debounce khi search query hoặc menu filters thay đổi (ở Server mode) */
|
|
196
|
+
onSearch?: (query: string, filters: TFilters) => void | Promise<void>;
|
|
197
|
+
/** Thời gian trì hoãn debounce tính theo ms. Mặc định 300 */
|
|
198
|
+
debounceMs?: number;
|
|
199
|
+
/** Hàm custom predicate lọc options ở chế độ client */
|
|
200
|
+
filterFn?: (option: SelectOptionItem<TData>, query: string, filters: TFilters) => boolean;
|
|
201
|
+
/** Trạng thái đang tải dữ liệu */
|
|
202
|
+
isLoading?: boolean;
|
|
203
|
+
/** Văn bản hiển thị khi không có dữ liệu */
|
|
204
|
+
emptyText?: ReactNode;
|
|
205
|
+
/** Props tùy biến chuyển tiếp đến component Empty */
|
|
206
|
+
emptyProps?: Partial<EmptyProps>;
|
|
207
|
+
/** Hiển thị nút xoá nhanh toàn bộ giá trị đã chọn */
|
|
208
|
+
clearable?: boolean;
|
|
209
|
+
/** Render menu qua Portal để tránh bị che bởi overflow hidden. Mặc định true */
|
|
210
|
+
portal?: boolean;
|
|
211
|
+
/** Vị trí hiển thị của popup dropdown. Mặc định 'bottom-start' */
|
|
212
|
+
placement?: Placement;
|
|
213
|
+
/** Chiều cao tối đa của vùng cuộn menu. Mặc định 280px */
|
|
214
|
+
maxMenuHeight?: number | string;
|
|
215
|
+
/** Bật/tắt animation khi dropdown menu xuất hiện/biến mất. Mặc định true */
|
|
216
|
+
animated?: boolean;
|
|
217
|
+
/** Thời lượng hiệu ứng tính bằng mili-giây (ms). Mặc định 150 */
|
|
218
|
+
animationDuration?: number;
|
|
219
|
+
/** Icon hoặc nội dung ở đầu trigger */
|
|
220
|
+
startContent?: ReactNode;
|
|
221
|
+
/** Icon hoặc nội dung ở cuối trigger */
|
|
222
|
+
endContent?: ReactNode;
|
|
223
|
+
/** Tùy biến render từng option trong menu */
|
|
224
|
+
renderOption?: (option: SelectOptionItem<TData>, state: {
|
|
225
|
+
selected: boolean;
|
|
226
|
+
active: boolean;
|
|
227
|
+
}) => ReactNode;
|
|
228
|
+
/** ClassName tùy biến cho wrapper ngoài cùng */
|
|
229
|
+
className?: string;
|
|
230
|
+
/** ClassName cho phần trigger box */
|
|
231
|
+
triggerClassName?: string;
|
|
232
|
+
/** ClassName cho popup dropdown menu */
|
|
233
|
+
menuClassName?: string;
|
|
234
|
+
}
|
|
235
|
+
/** Props cho component Select (Single Select) */
|
|
236
|
+
interface SelectProps<TData = unknown, TFilters extends Record<string, unknown> = Record<string, unknown>> extends BaseSelectProps<TData, TFilters> {
|
|
237
|
+
/** Giá trị đã chọn (controlled) */
|
|
238
|
+
value?: string | number | null;
|
|
239
|
+
/** Giá trị mặc định ban đầu (uncontrolled) */
|
|
240
|
+
defaultValue?: string | number | null;
|
|
241
|
+
/** Callback khi giá trị thay đổi */
|
|
242
|
+
onChange?: (value: string | number | null, option?: SelectOptionItem<TData> | null) => void;
|
|
243
|
+
/** Tùy biến render giá trị hiển thị trên trigger */
|
|
244
|
+
renderValue?: (selected: SelectOptionItem<TData>) => ReactNode;
|
|
245
|
+
}
|
|
246
|
+
/** Alias cho SelectProps */
|
|
247
|
+
type SingleSelectProps<TData = unknown, TFilters extends Record<string, unknown> = Record<string, unknown>> = SelectProps<TData, TFilters>;
|
|
248
|
+
/** Props cho component MultiSelect */
|
|
249
|
+
interface MultiSelectProps<TData = unknown, TFilters extends Record<string, unknown> = Record<string, unknown>> extends BaseSelectProps<TData, TFilters> {
|
|
250
|
+
/** Danh sách giá trị đã chọn (controlled) */
|
|
251
|
+
value?: (string | number)[];
|
|
252
|
+
/** Danh sách giá trị mặc định ban đầu (uncontrolled) */
|
|
253
|
+
defaultValue?: (string | number)[];
|
|
254
|
+
/** Callback khi giá trị thay đổi */
|
|
255
|
+
onChange?: (values: (string | number)[], options?: SelectOptionItem<TData>[]) => void;
|
|
256
|
+
/** Giới hạn số lượng tag Badge hiển thị trước khi gom thành +N */
|
|
257
|
+
maxTagCount?: number;
|
|
258
|
+
/** Tự động ghim bảo lưu các mục đã chọn khi tìm kiếm (tương tự CheckboxGroup). Mặc định true */
|
|
259
|
+
preserveSelected?: boolean;
|
|
260
|
+
/** Tùy biến render giá trị hiển thị trên trigger */
|
|
261
|
+
renderValue?: (selected: SelectOptionItem<TData>[]) => ReactNode;
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
/**
|
|
265
|
+
* Các tùy chọn cấu hình cho hook `useInfiniteScroll`.
|
|
266
|
+
*/
|
|
267
|
+
interface UseInfiniteScrollOptions {
|
|
268
|
+
/**
|
|
269
|
+
* Callback được gọi khi người dùng cuộn đến cuối hoặc phần tử sentinel xuất hiện trong tầm nhìn.
|
|
270
|
+
*/
|
|
271
|
+
onLoadMore: () => void | Promise<void>;
|
|
272
|
+
/**
|
|
273
|
+
* Cho biết còn dữ liệu để tải tiếp hay không.
|
|
274
|
+
* Nếu là `false`, `onLoadMore` sẽ không được kích hoạt.
|
|
275
|
+
* @default true
|
|
276
|
+
*/
|
|
277
|
+
hasMore?: boolean;
|
|
278
|
+
/**
|
|
279
|
+
* Trạng thái đang tải dữ liệu của trang hiện tại / tiếp theo.
|
|
280
|
+
* Nếu là `true`, `onLoadMore` sẽ tạm hoãn cho đến khi lượt tải trước hoàn tất.
|
|
281
|
+
* @default false
|
|
282
|
+
*/
|
|
283
|
+
isLoading?: boolean;
|
|
284
|
+
/**
|
|
285
|
+
* Vô hiệu hóa toàn bộ cơ chế theo dõi cuộn vô hạn.
|
|
286
|
+
* @default false
|
|
287
|
+
*/
|
|
288
|
+
disabled?: boolean;
|
|
289
|
+
/**
|
|
290
|
+
* Khoảng cách biên rootMargin của IntersectionObserver (vd: "100px", "0px 0px 100px 0px").
|
|
291
|
+
* Giúp kích hoạt tải trước khi người dùng chạm tới đáy danh sách để trải nghiệm mượt mà hơn.
|
|
292
|
+
* @default "100px"
|
|
293
|
+
*/
|
|
294
|
+
rootMargin?: string;
|
|
295
|
+
/**
|
|
296
|
+
* Ngưỡng hiển thị threshold của IntersectionObserver (từ 0.0 đến 1.0).
|
|
297
|
+
* @default 0
|
|
298
|
+
*/
|
|
299
|
+
threshold?: number | number[];
|
|
300
|
+
/**
|
|
301
|
+
* Phần tử gốc hoặc RefObject làm khung cuộn cho IntersectionObserver.
|
|
302
|
+
* Nếu là `null` hoặc `undefined`, sẽ dùng viewport của trình duyệt hoặc khung cuộn cha gần nhất.
|
|
303
|
+
* @default null
|
|
304
|
+
*/
|
|
305
|
+
root?: Element | null | RefObject<Element | null>;
|
|
306
|
+
}
|
|
307
|
+
/**
|
|
308
|
+
* Kết quả trả về từ hook `useInfiniteScroll`.
|
|
309
|
+
*/
|
|
310
|
+
interface UseInfiniteScrollReturn {
|
|
311
|
+
/**
|
|
312
|
+
* Ref callback gắn vào phần tử sentinel ở cuối danh sách để theo dõi bằng IntersectionObserver.
|
|
313
|
+
*/
|
|
314
|
+
sentinelRef: (node: HTMLElement | null) => void;
|
|
315
|
+
/**
|
|
316
|
+
* Trạng thái phần tử sentinel có đang nằm trong tầm quan sát hay không.
|
|
317
|
+
*/
|
|
318
|
+
isIntersecting: boolean;
|
|
319
|
+
}
|
|
320
|
+
/**
|
|
321
|
+
* Hook `useInfiniteScroll` độc lập hỗ trợ tải dữ liệu vô tận qua IntersectionObserver.
|
|
322
|
+
*/
|
|
323
|
+
declare function useInfiniteScroll({ onLoadMore, hasMore, isLoading, disabled, rootMargin, threshold, root, }: UseInfiniteScrollOptions): UseInfiniteScrollReturn;
|
|
324
|
+
|
|
325
|
+
export { type BaseSelectProps as B, type EmptyProps as E, type MultiSelectProps as M, type SelectProps as S, type UseInfiniteScrollOptions as U, type EmptyPresetImage as a, type SelectOptionItem as b, type SelectSize as c, type SelectVariant as d, type SelectColor as e, type SelectRadius as f, type SelectSearchPlacement as g, type SelectFilterField as h, type SelectFilterLayout as i, type EmptyLayout as j, type EmptySize as k, type SelectFilterType as l, type SelectSearchMode as m, type SingleSelectProps as n, type UseInfiniteScrollReturn as o, useInfiniteScroll as u };
|
|
@@ -0,0 +1,325 @@
|
|
|
1
|
+
import { HTMLAttributes, Ref, ReactNode, RefObject } from 'react';
|
|
2
|
+
import { Placement } from '@floating-ui/react';
|
|
3
|
+
|
|
4
|
+
type EmptySize = "sm" | "md" | "lg";
|
|
5
|
+
type EmptyPresetImage = "default" | "search" | "error" | "folder" | "simple";
|
|
6
|
+
type EmptyLayout = "vertical" | "horizontal";
|
|
7
|
+
interface EmptyProps extends Omit<HTMLAttributes<HTMLDivElement>, "title"> {
|
|
8
|
+
/**
|
|
9
|
+
* Ref chuyển tiếp đến phần tử HTML div
|
|
10
|
+
*/
|
|
11
|
+
ref?: Ref<HTMLDivElement>;
|
|
12
|
+
/**
|
|
13
|
+
* Kích cỡ của Empty component:
|
|
14
|
+
* - 'sm': Nhỏ gọn, thích hợp cho dropdown, popover, bảng nhỏ
|
|
15
|
+
* - 'md': Tiêu chuẩn, thích hợp cho card, section, modal
|
|
16
|
+
* - 'lg': Lớn, thích hợp cho toàn trang, dashboard
|
|
17
|
+
* @default 'md'
|
|
18
|
+
*/
|
|
19
|
+
size?: EmptySize;
|
|
20
|
+
/**
|
|
21
|
+
* Bố cục hiển thị:
|
|
22
|
+
* - 'vertical': Xếp dọc ảnh -> tiêu đề -> mô tả -> hành động
|
|
23
|
+
* - 'horizontal': Dàn ngang ảnh bên trái, nội dung & hành động bên phải
|
|
24
|
+
* @default 'vertical'
|
|
25
|
+
*/
|
|
26
|
+
layout?: EmptyLayout;
|
|
27
|
+
/**
|
|
28
|
+
* Ảnh hoặc minh họa:
|
|
29
|
+
* - Tên preset: 'default' | 'search' | 'error' | 'folder' | 'simple'
|
|
30
|
+
* - URL hình ảnh (string bắt đầu bằng http://, https://, /, hoặc ./)
|
|
31
|
+
* - ReactNode tùy biến (SVG icon, custom component, v.v.)
|
|
32
|
+
* @default 'default'
|
|
33
|
+
*/
|
|
34
|
+
image?: EmptyPresetImage | (string & {}) | ReactNode;
|
|
35
|
+
/**
|
|
36
|
+
* Kích thước tùy chỉnh cho ảnh / illustration (pixel hoặc chuỗi kích thước CSS)
|
|
37
|
+
*/
|
|
38
|
+
imageSize?: number | string;
|
|
39
|
+
/**
|
|
40
|
+
* Class tùy biến cho phần bao ngoài của ảnh
|
|
41
|
+
*/
|
|
42
|
+
imageClassName?: string;
|
|
43
|
+
/**
|
|
44
|
+
* Thuộc tính alt cho hình ảnh khi truyền URL
|
|
45
|
+
* @default 'Empty'
|
|
46
|
+
*/
|
|
47
|
+
imageAlt?: string;
|
|
48
|
+
/**
|
|
49
|
+
* Tiêu đề của trạng thái rỗng
|
|
50
|
+
*/
|
|
51
|
+
title?: ReactNode;
|
|
52
|
+
/**
|
|
53
|
+
* Class tùy biến cho tiêu đề
|
|
54
|
+
*/
|
|
55
|
+
titleClassName?: string;
|
|
56
|
+
/**
|
|
57
|
+
* Đoạn văn bản mô tả chi tiết hoặc hướng dẫn thao tác
|
|
58
|
+
*/
|
|
59
|
+
description?: ReactNode;
|
|
60
|
+
/**
|
|
61
|
+
* Class tùy biến cho phần mô tả
|
|
62
|
+
*/
|
|
63
|
+
descriptionClassName?: string;
|
|
64
|
+
/**
|
|
65
|
+
* Nút bấm hoặc nhóm hành động bên dưới (Call-to-Action)
|
|
66
|
+
*/
|
|
67
|
+
actions?: ReactNode;
|
|
68
|
+
/**
|
|
69
|
+
* Class tùy biến cho khu vực chứa actions
|
|
70
|
+
*/
|
|
71
|
+
actionsClassName?: string;
|
|
72
|
+
/**
|
|
73
|
+
* Nội dung bổ sung tùy biến bên dưới hoặc thay thế
|
|
74
|
+
*/
|
|
75
|
+
children?: ReactNode;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
type SelectSize = "xs" | "sm" | "md" | "lg" | "xl";
|
|
79
|
+
type SelectVariant = "outline" | "filled" | "ghost" | "other";
|
|
80
|
+
type SelectColor = "primary" | "secondary" | "error" | "success" | "warning" | "info" | "neutral";
|
|
81
|
+
type SelectRadius = "none" | "sm" | "md" | "lg" | "xl" | "full";
|
|
82
|
+
type SelectSearchMode = "client" | "server";
|
|
83
|
+
type SelectSearchPlacement = "trigger" | "menu" | "both";
|
|
84
|
+
type SelectFilterLayout = "vertical" | "grid";
|
|
85
|
+
type LabelPlacement = "top" | "left" | "floating";
|
|
86
|
+
interface SelectOptionItem<TData = unknown> {
|
|
87
|
+
value: string | number;
|
|
88
|
+
label: string;
|
|
89
|
+
disabled?: boolean;
|
|
90
|
+
description?: ReactNode;
|
|
91
|
+
icon?: ReactNode;
|
|
92
|
+
data?: TData;
|
|
93
|
+
}
|
|
94
|
+
type SelectFilterType = "string" | "text" | "number" | "date" | "date-range" | "checkbox-group" | "custom";
|
|
95
|
+
interface SelectFilterField<TValue = unknown> {
|
|
96
|
+
/** Tên định danh trường lọc (dùng làm key trong object filters) */
|
|
97
|
+
name: string;
|
|
98
|
+
/** Loại component lọc */
|
|
99
|
+
type: SelectFilterType;
|
|
100
|
+
/** Nhãn hiển thị của bộ lọc */
|
|
101
|
+
label?: ReactNode;
|
|
102
|
+
/** Placeholder cho input */
|
|
103
|
+
placeholder?: string;
|
|
104
|
+
/** Giá trị mặc định */
|
|
105
|
+
defaultValue?: TValue;
|
|
106
|
+
/** Giá trị điều khiển */
|
|
107
|
+
value?: TValue;
|
|
108
|
+
/** Số cột chiếm trong grid layout (1, 2, 3...) */
|
|
109
|
+
colSpan?: number;
|
|
110
|
+
/** Danh sách options (dùng cho checkbox-group hoặc select con) */
|
|
111
|
+
options?: {
|
|
112
|
+
label: ReactNode;
|
|
113
|
+
value: string | number;
|
|
114
|
+
disabled?: boolean;
|
|
115
|
+
}[];
|
|
116
|
+
/** Props truyền thêm vào component tương ứng (InputProps, DatePickerProps, ...) */
|
|
117
|
+
props?: Record<string, unknown>;
|
|
118
|
+
/** Custom render khi type="custom" */
|
|
119
|
+
render?: (fieldState: {
|
|
120
|
+
value: TValue | undefined;
|
|
121
|
+
onChange: (val: TValue) => void;
|
|
122
|
+
}) => ReactNode;
|
|
123
|
+
}
|
|
124
|
+
interface BaseSelectProps<TData = unknown, TFilters extends Record<string, unknown> = Record<string, unknown>> {
|
|
125
|
+
/** Ref chuyển tiếp tới root wrapper element */
|
|
126
|
+
ref?: Ref<HTMLDivElement>;
|
|
127
|
+
/** Kích thước: xs, sm, md, lg, xl. Mặc định 'md' */
|
|
128
|
+
size?: SelectSize;
|
|
129
|
+
/** Biến thể giao diện: outline, filled, ghost, other. Mặc định 'outline' */
|
|
130
|
+
variant?: SelectVariant;
|
|
131
|
+
/** Chủ đề màu sắc: primary, secondary, error, success, warning, info, neutral. Mặc định 'primary' */
|
|
132
|
+
color?: SelectColor;
|
|
133
|
+
/** Độ bo góc: none, sm, md, lg, xl, full */
|
|
134
|
+
radius?: SelectRadius;
|
|
135
|
+
/** Danh sách các tùy chọn */
|
|
136
|
+
options: SelectOptionItem<TData>[];
|
|
137
|
+
/** ID định danh cho Select */
|
|
138
|
+
id?: string;
|
|
139
|
+
/** Nhãn tiêu đề của Select */
|
|
140
|
+
label?: ReactNode;
|
|
141
|
+
/** Vị trí nhãn: top, left, floating. Mặc định 'top' */
|
|
142
|
+
labelPlacement?: LabelPlacement;
|
|
143
|
+
/** ClassName tùy biến cho nhãn */
|
|
144
|
+
labelClassName?: string;
|
|
145
|
+
/** Văn bản gợi ý khi chưa chọn */
|
|
146
|
+
placeholder?: string;
|
|
147
|
+
/** Văn bản hướng dẫn bên dưới */
|
|
148
|
+
helperText?: ReactNode;
|
|
149
|
+
/** Thông báo lỗi khi không hợp lệ */
|
|
150
|
+
errorMessage?: ReactNode;
|
|
151
|
+
/** Trạng thái không hợp lệ */
|
|
152
|
+
isInvalid?: boolean;
|
|
153
|
+
/** Bắt buộc chọn */
|
|
154
|
+
isRequired?: boolean;
|
|
155
|
+
/** Vô hiệu hóa Select */
|
|
156
|
+
isDisabled?: boolean;
|
|
157
|
+
/** Chỉ đọc, không cho phép thay đổi */
|
|
158
|
+
readOnly?: boolean;
|
|
159
|
+
/** Tên trường form HTML để submit */
|
|
160
|
+
name?: string;
|
|
161
|
+
/** Cho phép tìm kiếm. Mặc định false */
|
|
162
|
+
searchable?: boolean;
|
|
163
|
+
/** Chế độ tìm kiếm: 'client' (fuzzy search) hoặc 'server' (debounce + API). Mặc định 'client' */
|
|
164
|
+
searchMode?: SelectSearchMode;
|
|
165
|
+
/** Vị trí ô tìm kiếm: 'trigger' (trên trigger), 'menu' (trong dropdown), 'both'. Mặc định 'trigger' */
|
|
166
|
+
searchPlacement?: SelectSearchPlacement;
|
|
167
|
+
/** Placeholder cho ô tìm kiếm */
|
|
168
|
+
searchPlaceholder?: string;
|
|
169
|
+
/** Trường dùng để tìm kiếm (mặc định ['label', 'value']), hỗ trợ string, mảng hoặc hàm accessor tương tự CheckboxGroup */
|
|
170
|
+
searchField?: (keyof SelectOptionItem<TData> | string | ((item: SelectOptionItem<TData>) => string | undefined | null))[] | (keyof SelectOptionItem<TData> | string);
|
|
171
|
+
/** Giá trị từ khóa tìm kiếm (khi controlled) */
|
|
172
|
+
searchValue?: string;
|
|
173
|
+
/** Callback khi từ khóa thay đổi */
|
|
174
|
+
onSearchChange?: (value: string) => void;
|
|
175
|
+
/** Cấu hình danh sách các bộ lọc hiển thị bên trong dropdown menu */
|
|
176
|
+
menuFilters?: SelectFilterField<unknown>[];
|
|
177
|
+
/** Giá trị điều khiển cho các bộ lọc trong menu */
|
|
178
|
+
menuFilterValues?: Partial<TFilters>;
|
|
179
|
+
/** Bố cục danh sách bộ lọc: 'vertical' hoặc 'grid'. Mặc định 'vertical' */
|
|
180
|
+
menuFilterLayout?: SelectFilterLayout;
|
|
181
|
+
/** Số cột khi menuFilterLayout='grid'. Mặc định 2 */
|
|
182
|
+
menuFilterGridCols?: number;
|
|
183
|
+
/** Hiển thị nút Reset / Xóa bộ lọc. Mặc định true khi có menuFilters */
|
|
184
|
+
showResetFilters?: boolean;
|
|
185
|
+
/** Văn bản nút Reset bộ lọc. Mặc định 'Xoá bộ lọc' */
|
|
186
|
+
resetFiltersText?: ReactNode;
|
|
187
|
+
/** Callback khi bất kỳ filter nào trong menu thay đổi */
|
|
188
|
+
onMenuFilterChange?: (filters: TFilters) => void;
|
|
189
|
+
/** Nội dung tùy biến trên đầu menu (trước hoặc thay thế filter) */
|
|
190
|
+
menuHeader?: ReactNode;
|
|
191
|
+
/** Nội dung tùy biến dưới đáy menu */
|
|
192
|
+
menuFooter?: ReactNode;
|
|
193
|
+
/** Nội dung tùy biến ở đáy danh sách tùy chọn bên trong vùng cuộn (ví dụ: Sentinel/Skeleton/Spinner khi phân trang Infinite Scroll) */
|
|
194
|
+
listFooter?: ReactNode;
|
|
195
|
+
/** Callback được gọi sau khi debounce khi search query hoặc menu filters thay đổi (ở Server mode) */
|
|
196
|
+
onSearch?: (query: string, filters: TFilters) => void | Promise<void>;
|
|
197
|
+
/** Thời gian trì hoãn debounce tính theo ms. Mặc định 300 */
|
|
198
|
+
debounceMs?: number;
|
|
199
|
+
/** Hàm custom predicate lọc options ở chế độ client */
|
|
200
|
+
filterFn?: (option: SelectOptionItem<TData>, query: string, filters: TFilters) => boolean;
|
|
201
|
+
/** Trạng thái đang tải dữ liệu */
|
|
202
|
+
isLoading?: boolean;
|
|
203
|
+
/** Văn bản hiển thị khi không có dữ liệu */
|
|
204
|
+
emptyText?: ReactNode;
|
|
205
|
+
/** Props tùy biến chuyển tiếp đến component Empty */
|
|
206
|
+
emptyProps?: Partial<EmptyProps>;
|
|
207
|
+
/** Hiển thị nút xoá nhanh toàn bộ giá trị đã chọn */
|
|
208
|
+
clearable?: boolean;
|
|
209
|
+
/** Render menu qua Portal để tránh bị che bởi overflow hidden. Mặc định true */
|
|
210
|
+
portal?: boolean;
|
|
211
|
+
/** Vị trí hiển thị của popup dropdown. Mặc định 'bottom-start' */
|
|
212
|
+
placement?: Placement;
|
|
213
|
+
/** Chiều cao tối đa của vùng cuộn menu. Mặc định 280px */
|
|
214
|
+
maxMenuHeight?: number | string;
|
|
215
|
+
/** Bật/tắt animation khi dropdown menu xuất hiện/biến mất. Mặc định true */
|
|
216
|
+
animated?: boolean;
|
|
217
|
+
/** Thời lượng hiệu ứng tính bằng mili-giây (ms). Mặc định 150 */
|
|
218
|
+
animationDuration?: number;
|
|
219
|
+
/** Icon hoặc nội dung ở đầu trigger */
|
|
220
|
+
startContent?: ReactNode;
|
|
221
|
+
/** Icon hoặc nội dung ở cuối trigger */
|
|
222
|
+
endContent?: ReactNode;
|
|
223
|
+
/** Tùy biến render từng option trong menu */
|
|
224
|
+
renderOption?: (option: SelectOptionItem<TData>, state: {
|
|
225
|
+
selected: boolean;
|
|
226
|
+
active: boolean;
|
|
227
|
+
}) => ReactNode;
|
|
228
|
+
/** ClassName tùy biến cho wrapper ngoài cùng */
|
|
229
|
+
className?: string;
|
|
230
|
+
/** ClassName cho phần trigger box */
|
|
231
|
+
triggerClassName?: string;
|
|
232
|
+
/** ClassName cho popup dropdown menu */
|
|
233
|
+
menuClassName?: string;
|
|
234
|
+
}
|
|
235
|
+
/** Props cho component Select (Single Select) */
|
|
236
|
+
interface SelectProps<TData = unknown, TFilters extends Record<string, unknown> = Record<string, unknown>> extends BaseSelectProps<TData, TFilters> {
|
|
237
|
+
/** Giá trị đã chọn (controlled) */
|
|
238
|
+
value?: string | number | null;
|
|
239
|
+
/** Giá trị mặc định ban đầu (uncontrolled) */
|
|
240
|
+
defaultValue?: string | number | null;
|
|
241
|
+
/** Callback khi giá trị thay đổi */
|
|
242
|
+
onChange?: (value: string | number | null, option?: SelectOptionItem<TData> | null) => void;
|
|
243
|
+
/** Tùy biến render giá trị hiển thị trên trigger */
|
|
244
|
+
renderValue?: (selected: SelectOptionItem<TData>) => ReactNode;
|
|
245
|
+
}
|
|
246
|
+
/** Alias cho SelectProps */
|
|
247
|
+
type SingleSelectProps<TData = unknown, TFilters extends Record<string, unknown> = Record<string, unknown>> = SelectProps<TData, TFilters>;
|
|
248
|
+
/** Props cho component MultiSelect */
|
|
249
|
+
interface MultiSelectProps<TData = unknown, TFilters extends Record<string, unknown> = Record<string, unknown>> extends BaseSelectProps<TData, TFilters> {
|
|
250
|
+
/** Danh sách giá trị đã chọn (controlled) */
|
|
251
|
+
value?: (string | number)[];
|
|
252
|
+
/** Danh sách giá trị mặc định ban đầu (uncontrolled) */
|
|
253
|
+
defaultValue?: (string | number)[];
|
|
254
|
+
/** Callback khi giá trị thay đổi */
|
|
255
|
+
onChange?: (values: (string | number)[], options?: SelectOptionItem<TData>[]) => void;
|
|
256
|
+
/** Giới hạn số lượng tag Badge hiển thị trước khi gom thành +N */
|
|
257
|
+
maxTagCount?: number;
|
|
258
|
+
/** Tự động ghim bảo lưu các mục đã chọn khi tìm kiếm (tương tự CheckboxGroup). Mặc định true */
|
|
259
|
+
preserveSelected?: boolean;
|
|
260
|
+
/** Tùy biến render giá trị hiển thị trên trigger */
|
|
261
|
+
renderValue?: (selected: SelectOptionItem<TData>[]) => ReactNode;
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
/**
|
|
265
|
+
* Các tùy chọn cấu hình cho hook `useInfiniteScroll`.
|
|
266
|
+
*/
|
|
267
|
+
interface UseInfiniteScrollOptions {
|
|
268
|
+
/**
|
|
269
|
+
* Callback được gọi khi người dùng cuộn đến cuối hoặc phần tử sentinel xuất hiện trong tầm nhìn.
|
|
270
|
+
*/
|
|
271
|
+
onLoadMore: () => void | Promise<void>;
|
|
272
|
+
/**
|
|
273
|
+
* Cho biết còn dữ liệu để tải tiếp hay không.
|
|
274
|
+
* Nếu là `false`, `onLoadMore` sẽ không được kích hoạt.
|
|
275
|
+
* @default true
|
|
276
|
+
*/
|
|
277
|
+
hasMore?: boolean;
|
|
278
|
+
/**
|
|
279
|
+
* Trạng thái đang tải dữ liệu của trang hiện tại / tiếp theo.
|
|
280
|
+
* Nếu là `true`, `onLoadMore` sẽ tạm hoãn cho đến khi lượt tải trước hoàn tất.
|
|
281
|
+
* @default false
|
|
282
|
+
*/
|
|
283
|
+
isLoading?: boolean;
|
|
284
|
+
/**
|
|
285
|
+
* Vô hiệu hóa toàn bộ cơ chế theo dõi cuộn vô hạn.
|
|
286
|
+
* @default false
|
|
287
|
+
*/
|
|
288
|
+
disabled?: boolean;
|
|
289
|
+
/**
|
|
290
|
+
* Khoảng cách biên rootMargin của IntersectionObserver (vd: "100px", "0px 0px 100px 0px").
|
|
291
|
+
* Giúp kích hoạt tải trước khi người dùng chạm tới đáy danh sách để trải nghiệm mượt mà hơn.
|
|
292
|
+
* @default "100px"
|
|
293
|
+
*/
|
|
294
|
+
rootMargin?: string;
|
|
295
|
+
/**
|
|
296
|
+
* Ngưỡng hiển thị threshold của IntersectionObserver (từ 0.0 đến 1.0).
|
|
297
|
+
* @default 0
|
|
298
|
+
*/
|
|
299
|
+
threshold?: number | number[];
|
|
300
|
+
/**
|
|
301
|
+
* Phần tử gốc hoặc RefObject làm khung cuộn cho IntersectionObserver.
|
|
302
|
+
* Nếu là `null` hoặc `undefined`, sẽ dùng viewport của trình duyệt hoặc khung cuộn cha gần nhất.
|
|
303
|
+
* @default null
|
|
304
|
+
*/
|
|
305
|
+
root?: Element | null | RefObject<Element | null>;
|
|
306
|
+
}
|
|
307
|
+
/**
|
|
308
|
+
* Kết quả trả về từ hook `useInfiniteScroll`.
|
|
309
|
+
*/
|
|
310
|
+
interface UseInfiniteScrollReturn {
|
|
311
|
+
/**
|
|
312
|
+
* Ref callback gắn vào phần tử sentinel ở cuối danh sách để theo dõi bằng IntersectionObserver.
|
|
313
|
+
*/
|
|
314
|
+
sentinelRef: (node: HTMLElement | null) => void;
|
|
315
|
+
/**
|
|
316
|
+
* Trạng thái phần tử sentinel có đang nằm trong tầm quan sát hay không.
|
|
317
|
+
*/
|
|
318
|
+
isIntersecting: boolean;
|
|
319
|
+
}
|
|
320
|
+
/**
|
|
321
|
+
* Hook `useInfiniteScroll` độc lập hỗ trợ tải dữ liệu vô tận qua IntersectionObserver.
|
|
322
|
+
*/
|
|
323
|
+
declare function useInfiniteScroll({ onLoadMore, hasMore, isLoading, disabled, rootMargin, threshold, root, }: UseInfiniteScrollOptions): UseInfiniteScrollReturn;
|
|
324
|
+
|
|
325
|
+
export { type BaseSelectProps as B, type EmptyProps as E, type MultiSelectProps as M, type SelectProps as S, type UseInfiniteScrollOptions as U, type EmptyPresetImage as a, type SelectOptionItem as b, type SelectSize as c, type SelectVariant as d, type SelectColor as e, type SelectRadius as f, type SelectSearchPlacement as g, type SelectFilterField as h, type SelectFilterLayout as i, type EmptyLayout as j, type EmptySize as k, type SelectFilterType as l, type SelectSearchMode as m, type SingleSelectProps as n, type UseInfiniteScrollReturn as o, useInfiniteScroll as u };
|