@lobehub/ui 5.61.0 → 5.62.0
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/es/base-ui/Input/style.d.mts +1 -1
- package/es/base-ui/ProTable/ColumnSetting.mjs +156 -0
- package/es/base-ui/ProTable/ColumnSetting.mjs.map +1 -0
- package/es/base-ui/ProTable/FieldControl.mjs +108 -0
- package/es/base-ui/ProTable/FieldControl.mjs.map +1 -0
- package/es/base-ui/ProTable/LightFilter.mjs +71 -0
- package/es/base-ui/ProTable/LightFilter.mjs.map +1 -0
- package/es/base-ui/ProTable/OptionPopover.mjs +21 -0
- package/es/base-ui/ProTable/OptionPopover.mjs.map +1 -0
- package/es/base-ui/ProTable/ProTable.d.mts +13 -0
- package/es/base-ui/ProTable/ProTable.mjs +651 -0
- package/es/base-ui/ProTable/ProTable.mjs.map +1 -0
- package/es/base-ui/ProTable/SearchForm.mjs +138 -0
- package/es/base-ui/ProTable/SearchForm.mjs.map +1 -0
- package/es/base-ui/ProTable/index.d.mts +3 -0
- package/es/base-ui/ProTable/index.mjs +1 -0
- package/es/base-ui/ProTable/renderValue.mjs +118 -0
- package/es/base-ui/ProTable/renderValue.mjs.map +1 -0
- package/es/base-ui/ProTable/style.mjs +373 -0
- package/es/base-ui/ProTable/style.mjs.map +1 -0
- package/es/base-ui/ProTable/type.d.mts +343 -0
- package/es/base-ui/ProTable/useFetchData.mjs +98 -0
- package/es/base-ui/ProTable/useFetchData.mjs.map +1 -0
- package/es/base-ui/ProTable/useSearchForm.mjs +151 -0
- package/es/base-ui/ProTable/useSearchForm.mjs.map +1 -0
- package/es/base-ui/ProTable/utils.mjs +102 -0
- package/es/base-ui/ProTable/utils.mjs.map +1 -0
- package/es/base-ui/Select/Select.mjs +15 -8
- package/es/base-ui/Select/Select.mjs.map +1 -1
- package/es/base-ui/Select/helpers.mjs +8 -1
- package/es/base-ui/Select/helpers.mjs.map +1 -1
- package/es/base-ui/Table/Table.mjs +6 -2
- package/es/base-ui/Table/Table.mjs.map +1 -1
- package/es/base-ui/Text/style.d.mts +1 -1
- package/es/dashboard/ConsoleShell/style.mjs +3 -1
- package/es/dashboard/ConsoleShell/style.mjs.map +1 -1
- package/es/dashboard/MetricStrip/MetricCell.mjs +25 -14
- package/es/dashboard/MetricStrip/MetricCell.mjs.map +1 -1
- package/es/dashboard/MetricStrip/style.mjs +14 -0
- package/es/dashboard/MetricStrip/style.mjs.map +1 -1
- package/es/dashboard/MetricStrip/type.d.mts +2 -0
- package/es/dashboard/StatCard/StatCard.d.mts +1 -1
- package/es/dashboard/StatCard/StatCard.mjs +2 -1
- package/es/dashboard/StatCard/StatCard.mjs.map +1 -1
- package/es/dashboard/StatCard/type.d.mts +2 -0
- package/es/dashboard/Surface/style.d.mts +3 -2
- package/es/dashboard/Surface/style.mjs +7 -3
- package/es/dashboard/Surface/style.mjs.map +1 -1
- package/package.json +5 -1
|
@@ -0,0 +1,343 @@
|
|
|
1
|
+
import { FormInstance, FormValues, UseFormOptions } from "../FormKit/type.mjs";
|
|
2
|
+
import { SortOrder, TableColumnFilterItem, TableColumnType, TablePaginationConfig, TableProps, TableRowSelection, TableSize, TableVariant } from "../Table/type.mjs";
|
|
3
|
+
import "../FormKit/index.mjs";
|
|
4
|
+
import { CSSProperties, Key, MutableRefObject, ReactNode, Ref } from "react";
|
|
5
|
+
//#region src/base-ui/ProTable/type.d.ts
|
|
6
|
+
type ValidateOn = UseFormOptions<FormValues>['validateOn'];
|
|
7
|
+
/** Built-in value types; they pick both the cell renderer and the search-form control. */
|
|
8
|
+
type ProTableValueType = 'text' | 'textarea' | 'password' | 'digit' | 'money' | 'percent' | 'select' | 'radio' | 'checkbox' | 'switch' | 'date' | 'dateTime' | 'dateRange' | 'dateTimeRange' | 'time' | 'progress' | 'code' | 'index' | 'indexBorder' | 'option';
|
|
9
|
+
/** ProComponents `valueEnum` status names; `status` draws a colored dot before the text. */
|
|
10
|
+
type ProTableStatus = 'Default' | 'Error' | 'Processing' | 'Success' | 'Warning';
|
|
11
|
+
interface ProTableValueEnumItem {
|
|
12
|
+
color?: string;
|
|
13
|
+
disabled?: boolean;
|
|
14
|
+
status?: ProTableStatus;
|
|
15
|
+
text: ReactNode;
|
|
16
|
+
}
|
|
17
|
+
/** `{ value: text | { text, status, color, disabled } }`, or a `Map` of the same. */
|
|
18
|
+
type ProTableValueEnum = Record<string, ReactNode | ProTableValueEnumItem> | Map<string | number | boolean, ReactNode | ProTableValueEnumItem>;
|
|
19
|
+
/** `transform(value, name, allValues)` returns the params to merge (e.g. split a range). */
|
|
20
|
+
type ProTableSearchTransform = (value: any, name: string, allValues: Record<string, any>) => Record<string, any> | string | undefined;
|
|
21
|
+
/** antd-style validation rule, mapped onto base-ui Form `required` / `validate`. */
|
|
22
|
+
interface ProTableFormRule {
|
|
23
|
+
len?: number;
|
|
24
|
+
max?: number;
|
|
25
|
+
message?: ReactNode;
|
|
26
|
+
min?: number;
|
|
27
|
+
pattern?: RegExp;
|
|
28
|
+
required?: boolean;
|
|
29
|
+
/** Reject (throw) to fail; the error message (or `message`) is shown. */
|
|
30
|
+
validator?: (rule: ProTableFormRule, value: any, values: Record<string, any>) => Promise<void> | void;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* The search-form handle passed as `formRef`: the base-ui Form instance (`getValue`,
|
|
34
|
+
* `getValues`, `setValue`, `setValues`, `reset`, `resetField`, `validate`, `submit`,
|
|
35
|
+
* `setErrors`, `isDirty`, `isTouched`, `subscribe`, `setSchema`) plus antd-style aliases.
|
|
36
|
+
*/
|
|
37
|
+
interface ProTableFormInstance<Values extends Record<string, any> = Record<string, any>> extends FormInstance<Values> {
|
|
38
|
+
/** Values converted the way they are sent to `request` (`dateFormatter`, `transform`). */
|
|
39
|
+
getFieldsFormatValue: () => Record<string, any>;
|
|
40
|
+
/** Raw field values (dates stay `Date`). */
|
|
41
|
+
getFieldsValue: () => Values;
|
|
42
|
+
getFieldValue: (name: string) => any;
|
|
43
|
+
/** Back to the initial values (base-ui `reset()` goes back to the last submitted values). */
|
|
44
|
+
resetFields: () => void;
|
|
45
|
+
setFieldsValue: (values: Partial<Values>) => void;
|
|
46
|
+
setFieldValue: (name: string, value: any) => void;
|
|
47
|
+
/** Validate every registered field; resolves the values or rejects with `{ errors, valid }`. */
|
|
48
|
+
validateFields: () => Promise<Values>;
|
|
49
|
+
}
|
|
50
|
+
interface ProTablePageInfo {
|
|
51
|
+
current: number;
|
|
52
|
+
pageSize: number;
|
|
53
|
+
total: number;
|
|
54
|
+
}
|
|
55
|
+
/** `actionRef.current`: imperative actions of the table. */
|
|
56
|
+
interface ProTableActionType {
|
|
57
|
+
clearSelected: () => void;
|
|
58
|
+
fullScreen: () => void;
|
|
59
|
+
getSortFilter: () => {
|
|
60
|
+
filter: Record<string, (string | number | boolean)[] | null>;
|
|
61
|
+
sort: Record<string, SortOrder>;
|
|
62
|
+
};
|
|
63
|
+
nativeElement: HTMLDivElement | null;
|
|
64
|
+
pageInfo: ProTablePageInfo;
|
|
65
|
+
/** Fetch again; `resetPageIndex` jumps back to page 1 first. */
|
|
66
|
+
reload: (resetPageIndex?: boolean) => Promise<void>;
|
|
67
|
+
/** Clear the selection, go back to page 1 and fetch. */
|
|
68
|
+
reloadAndRest: () => Promise<void>;
|
|
69
|
+
/** Reset the search form to its initial values, go back to page 1 and fetch. */
|
|
70
|
+
reset: () => void;
|
|
71
|
+
setPageInfo: (pageInfo: Partial<ProTablePageInfo>) => void;
|
|
72
|
+
}
|
|
73
|
+
interface ProTableSchemaConfig<T> {
|
|
74
|
+
/** Current form handle; lets `renderFormItem` read sibling fields. */
|
|
75
|
+
form: ProTableFormInstance;
|
|
76
|
+
/** `true` in the search form, `false` in the light filter bar. */
|
|
77
|
+
isQuery: boolean;
|
|
78
|
+
onChange: (value: any) => void;
|
|
79
|
+
record?: T;
|
|
80
|
+
value: any;
|
|
81
|
+
}
|
|
82
|
+
interface ProColumnType<T = any> extends Omit<TableColumnType<T>, 'filters' | 'onFilter' | 'render' | 'sorter' | 'title'> {
|
|
83
|
+
children?: ProColumnType<T>[];
|
|
84
|
+
/** Grid cells the field takes in the search form. @default 1 */
|
|
85
|
+
colSize?: number;
|
|
86
|
+
/** Show a copy button after the cell text. */
|
|
87
|
+
copyable?: boolean;
|
|
88
|
+
/** Column-setting checkbox disabled (the column can't be hidden). */
|
|
89
|
+
disable?: boolean | {
|
|
90
|
+
checkbox: boolean;
|
|
91
|
+
};
|
|
92
|
+
/** Props of the generated form control (placeholder, options, allowClear, …). */
|
|
93
|
+
fieldProps?: Record<string, any>;
|
|
94
|
+
/** `true` builds header filters from `valueEnum`. */
|
|
95
|
+
filters?: boolean | TableColumnFilterItem[];
|
|
96
|
+
/** Extra props for the field wrapper; `label` overrides the title in the form. */
|
|
97
|
+
formItemProps?: {
|
|
98
|
+
/** Help text under the control. */
|
|
99
|
+
extra?: ReactNode;
|
|
100
|
+
label?: ReactNode;
|
|
101
|
+
required?: boolean;
|
|
102
|
+
/** Validation rules (base-ui Form `required` / `validate`); the query is blocked until they pass. */
|
|
103
|
+
rules?: ProTableFormRule[];
|
|
104
|
+
style?: CSSProperties;
|
|
105
|
+
};
|
|
106
|
+
hideInForm?: boolean;
|
|
107
|
+
hideInSearch?: boolean;
|
|
108
|
+
hideInSetting?: boolean;
|
|
109
|
+
hideInTable?: boolean;
|
|
110
|
+
/** Initial value of the search field. */
|
|
111
|
+
initialValue?: any;
|
|
112
|
+
/** `true` filters locally by exact `valueEnum` key match; `false` disables it. */
|
|
113
|
+
onFilter?: boolean | TableColumnType<T>['onFilter'];
|
|
114
|
+
/** Order in the search form (larger first). */
|
|
115
|
+
order?: number;
|
|
116
|
+
/**
|
|
117
|
+
* `render(dom, record, index, action, schema)`; `dom` is the value-type rendering. With
|
|
118
|
+
* `valueType: 'option'` return an array of action nodes.
|
|
119
|
+
*/
|
|
120
|
+
render?: (dom: ReactNode, record: T, index: number, action: ProTableActionType | undefined, schema: ProColumnType<T>) => ReactNode;
|
|
121
|
+
/** Custom search control: `(schema, config, form) => node`. */
|
|
122
|
+
renderFormItem?: (schema: ProColumnType<T>, config: ProTableSchemaConfig<T>, form: ProTableFormInstance) => ReactNode;
|
|
123
|
+
/** Transform the raw cell value before the value-type rendering. */
|
|
124
|
+
renderText?: (text: any, record: T, index: number, action: ProTableActionType | undefined) => any;
|
|
125
|
+
/** `false` hides the column in the search form; `{ transform }` reshapes the submitted value. */
|
|
126
|
+
search?: boolean | {
|
|
127
|
+
transform: ProTableSearchTransform;
|
|
128
|
+
};
|
|
129
|
+
/** `true` sorts on the server (sent to `request` as `sort`); a function sorts locally. */
|
|
130
|
+
sorter?: TableColumnType<T>['sorter'] | string;
|
|
131
|
+
title?: ReactNode;
|
|
132
|
+
/** Info icon next to the header title. */
|
|
133
|
+
tooltip?: ReactNode;
|
|
134
|
+
valueEnum?: ProTableValueEnum | ((record?: T) => ProTableValueEnum);
|
|
135
|
+
valueType?: ProTableValueType | ((record?: T) => ProTableValueType);
|
|
136
|
+
}
|
|
137
|
+
type ProColumns<T = any> = ProColumnType<T>;
|
|
138
|
+
interface ProTableColumnsStateItem {
|
|
139
|
+
/** Column-setting checkbox disabled. */
|
|
140
|
+
disable?: boolean;
|
|
141
|
+
fixed?: 'left' | 'right' | 'start' | 'end' | boolean;
|
|
142
|
+
order?: number;
|
|
143
|
+
show?: boolean;
|
|
144
|
+
}
|
|
145
|
+
type ProTableColumnsStateMap = Record<string, ProTableColumnsStateItem>;
|
|
146
|
+
interface ProTableColumnsState {
|
|
147
|
+
defaultValue?: ProTableColumnsStateMap;
|
|
148
|
+
onChange?: (map: ProTableColumnsStateMap) => void;
|
|
149
|
+
persistenceKey?: string;
|
|
150
|
+
persistenceType?: 'localStorage' | 'sessionStorage';
|
|
151
|
+
value?: ProTableColumnsStateMap;
|
|
152
|
+
}
|
|
153
|
+
interface ProTableRequestData<T> {
|
|
154
|
+
[key: string]: any;
|
|
155
|
+
data: T[] | undefined;
|
|
156
|
+
success?: boolean;
|
|
157
|
+
total?: number;
|
|
158
|
+
}
|
|
159
|
+
type ProTableRequest<T, Params> = (params: Params & {
|
|
160
|
+
current?: number;
|
|
161
|
+
keyword?: string;
|
|
162
|
+
pageSize?: number;
|
|
163
|
+
}, sort: Record<string, SortOrder>, filter: Record<string, (string | number | boolean)[] | null>) => Promise<Partial<ProTableRequestData<T>>>;
|
|
164
|
+
type ProTableDensity = 'large' | 'medium' | 'small';
|
|
165
|
+
interface ProTableOptionConfig {
|
|
166
|
+
density?: boolean;
|
|
167
|
+
fullScreen?: boolean | (() => void);
|
|
168
|
+
reload?: boolean | (() => void);
|
|
169
|
+
/** Keyword input in the toolbar; its value goes to `request` as `params[name ?? 'keyword']`. */
|
|
170
|
+
search?: boolean | {
|
|
171
|
+
name?: string;
|
|
172
|
+
onSearch?: (keyword: string) => boolean | void;
|
|
173
|
+
placeholder?: string;
|
|
174
|
+
};
|
|
175
|
+
setting?: boolean | {
|
|
176
|
+
checkable?: boolean;
|
|
177
|
+
/** Up / down buttons in the panel (drag sorting is not implemented). */
|
|
178
|
+
draggable?: boolean;
|
|
179
|
+
showListItemOption?: boolean;
|
|
180
|
+
};
|
|
181
|
+
}
|
|
182
|
+
interface ProTableSearchConfig {
|
|
183
|
+
/** Collapsed state (controlled). */
|
|
184
|
+
collapsed?: boolean;
|
|
185
|
+
/** Replace the collapse toggle; `false` hides it. */
|
|
186
|
+
collapseRender?: false | ((collapsed: boolean) => ReactNode);
|
|
187
|
+
/** @default true */
|
|
188
|
+
defaultCollapsed?: boolean;
|
|
189
|
+
/** Fields visible while collapsed. @default one row */
|
|
190
|
+
defaultColsNumber?: number;
|
|
191
|
+
/** `query` is the grid form; `light` is the compact filter bar. @default 'query' */
|
|
192
|
+
filterType?: 'query' | 'light';
|
|
193
|
+
/** Label column width in px; `'auto'` keeps the label width natural. @default 80 */
|
|
194
|
+
labelWidth?: number | 'auto';
|
|
195
|
+
/** Field layout of the query form. @default 'horizontal' */
|
|
196
|
+
layout?: 'horizontal' | 'vertical';
|
|
197
|
+
onCollapse?: (collapsed: boolean) => void;
|
|
198
|
+
/** Replace the action buttons; `false` hides them. */
|
|
199
|
+
optionRender?: false | ((config: {
|
|
200
|
+
form: ProTableFormInstance;
|
|
201
|
+
onReset: () => void;
|
|
202
|
+
onSubmit: () => void;
|
|
203
|
+
}, dom: ReactNode[]) => ReactNode[]);
|
|
204
|
+
resetText?: ReactNode;
|
|
205
|
+
searchText?: ReactNode;
|
|
206
|
+
/** Span of each field in a 24-column grid (`8` = 3 per row). Responsive to the width by default. */
|
|
207
|
+
span?: number;
|
|
208
|
+
submitText?: ReactNode;
|
|
209
|
+
}
|
|
210
|
+
interface ProTableAlertRenderProps<T> {
|
|
211
|
+
onCleanSelected: () => void;
|
|
212
|
+
selectedRowKeys: Key[];
|
|
213
|
+
selectedRows: T[];
|
|
214
|
+
}
|
|
215
|
+
type ProTableAlertRender<T> = false | ((props: ProTableAlertRenderProps<T>) => ReactNode);
|
|
216
|
+
interface ProTableToolbarConfig {
|
|
217
|
+
actions?: ReactNode[];
|
|
218
|
+
/** Replaces the default `options` icons. */
|
|
219
|
+
settings?: ReactNode[];
|
|
220
|
+
subTitle?: ReactNode;
|
|
221
|
+
title?: ReactNode;
|
|
222
|
+
tooltip?: ReactNode;
|
|
223
|
+
}
|
|
224
|
+
interface ProTableLocale {
|
|
225
|
+
alertClear?: ReactNode;
|
|
226
|
+
alertSelected?: (count: number) => ReactNode;
|
|
227
|
+
collapse?: ReactNode;
|
|
228
|
+
columnSetting?: string;
|
|
229
|
+
columnSettingReset?: ReactNode;
|
|
230
|
+
columnSettingTitle?: ReactNode;
|
|
231
|
+
copy?: string;
|
|
232
|
+
density?: string;
|
|
233
|
+
densityLarge?: ReactNode;
|
|
234
|
+
densityMedium?: ReactNode;
|
|
235
|
+
densitySmall?: ReactNode;
|
|
236
|
+
expand?: ReactNode;
|
|
237
|
+
fixedEnd?: string;
|
|
238
|
+
fixedEndTitle?: ReactNode;
|
|
239
|
+
fixedStart?: string;
|
|
240
|
+
fixedStartTitle?: ReactNode;
|
|
241
|
+
fullScreen?: string;
|
|
242
|
+
moveDown?: string;
|
|
243
|
+
moveUp?: string;
|
|
244
|
+
noFixed?: string;
|
|
245
|
+
reload?: string;
|
|
246
|
+
reset?: ReactNode;
|
|
247
|
+
search?: ReactNode;
|
|
248
|
+
searchPlaceholder?: string;
|
|
249
|
+
selectPlaceholder?: string;
|
|
250
|
+
unfixedTitle?: ReactNode;
|
|
251
|
+
}
|
|
252
|
+
interface ProTableProps<T = any, Params = Record<string, any>> extends Omit<TableProps<T>, 'columns' | 'dataSource' | 'pagination' | 'rowSelection' | 'size' | 'title' | 'loading' | 'ref'> {
|
|
253
|
+
actionRef?: MutableRefObject<ProTableActionType | undefined> | Ref<ProTableActionType>;
|
|
254
|
+
/** Hook to reshape the form values before they become `params`. */
|
|
255
|
+
beforeSearchSubmit?: (params: Record<string, any>) => Record<string, any>;
|
|
256
|
+
/** Frame the query form / table (border, radius, padding); they are flat by default. `{ search, table }` per part. */
|
|
257
|
+
cardBordered?: boolean | {
|
|
258
|
+
search?: boolean;
|
|
259
|
+
table?: boolean;
|
|
260
|
+
};
|
|
261
|
+
/** Render cells with empty values as this. @default '-' */
|
|
262
|
+
columnEmptyText?: ReactNode | false;
|
|
263
|
+
columns?: ProColumns<T>[];
|
|
264
|
+
columnsState?: ProTableColumnsState;
|
|
265
|
+
/** Controlled data, used when there is no `request`. */
|
|
266
|
+
dataSource?: T[];
|
|
267
|
+
/** How dates in the search form are sent: `'string'` (`YYYY-MM-DD`), `'number'` (ms), `false` (Date) or a function. @default 'string' */
|
|
268
|
+
dateFormatter?: 'string' | 'number' | false | ((value: Date, valueType: string) => any);
|
|
269
|
+
/** Debounce of `params` driven fetches in ms. @default 10 */
|
|
270
|
+
debounceTime?: number;
|
|
271
|
+
defaultData?: T[];
|
|
272
|
+
/** @default 'medium' */
|
|
273
|
+
defaultSize?: ProTableDensity;
|
|
274
|
+
/** Initial values of the search form. */
|
|
275
|
+
form?: {
|
|
276
|
+
initialValues?: Record<string, any>;
|
|
277
|
+
onValuesChange?: (changed: any, all: any) => void;
|
|
278
|
+
/** base-ui Form `validateOn`. @default 'auto' */
|
|
279
|
+
validateOn?: ValidateOn;
|
|
280
|
+
};
|
|
281
|
+
formRef?: MutableRefObject<ProTableFormInstance | undefined> | Ref<ProTableFormInstance>;
|
|
282
|
+
/** Kept for ProComponents compatibility; the parts have no card background to remove. */
|
|
283
|
+
ghost?: boolean;
|
|
284
|
+
headerTitle?: ReactNode;
|
|
285
|
+
loading?: boolean;
|
|
286
|
+
locale?: TableProps<T>['locale'] & ProTableLocale;
|
|
287
|
+
/** Don't fetch on mount; call `actionRef.current.reload()` (or submit the form). */
|
|
288
|
+
manualRequest?: boolean;
|
|
289
|
+
onDataSourceChange?: (dataSource: T[]) => void;
|
|
290
|
+
onLoad?: (dataSource: T[]) => void;
|
|
291
|
+
onLoadingChange?: (loading: boolean) => void;
|
|
292
|
+
onRequestError?: (error: Error) => void;
|
|
293
|
+
onReset?: () => void;
|
|
294
|
+
onSizeChange?: (size: ProTableDensity) => void;
|
|
295
|
+
onSubmit?: (params: Record<string, any>) => void;
|
|
296
|
+
/** Toolbar icons; `false` hides them all. */
|
|
297
|
+
options?: ProTableOptionConfig | false;
|
|
298
|
+
/** `pagination.total` is filled from `request`. */
|
|
299
|
+
pagination?: false | TablePaginationConfig;
|
|
300
|
+
/** Change of reference re-fetches from page 1. */
|
|
301
|
+
params?: Params;
|
|
302
|
+
/** Polling interval in ms (min 2000), or a function of the current data. */
|
|
303
|
+
polling?: number | ((dataSource: T[]) => number);
|
|
304
|
+
/** Post-process the `request` data before it's shown. */
|
|
305
|
+
postData?: (data: T[]) => T[];
|
|
306
|
+
request?: ProTableRequest<T, Params>;
|
|
307
|
+
/** `alwaysShowAlert` keeps the alert bar while nothing is selected. */
|
|
308
|
+
rowSelection?: (TableRowSelection<T> & {
|
|
309
|
+
alwaysShowAlert?: boolean;
|
|
310
|
+
}) | false;
|
|
311
|
+
/** Query form config; `false` hides it. */
|
|
312
|
+
search?: false | ProTableSearchConfig;
|
|
313
|
+
/** Wrap / replace the search form. */
|
|
314
|
+
searchFormRender?: (props: ProTableProps<T, Params>, defaultDom: ReactNode) => ReactNode;
|
|
315
|
+
/** Controlled density. */
|
|
316
|
+
size?: ProTableDensity;
|
|
317
|
+
tableAlertOptionRender?: ProTableAlertRender<T>;
|
|
318
|
+
tableAlertRender?: ProTableAlertRender<T>;
|
|
319
|
+
tableClassName?: string;
|
|
320
|
+
/** Content between the search form and the table card. */
|
|
321
|
+
tableExtraRender?: (props: ProTableProps<T, Params>, dataSource: T[]) => ReactNode;
|
|
322
|
+
/** Arrange the parts yourself. */
|
|
323
|
+
tableRender?: (props: ProTableProps<T, Params>, defaultDom: ReactNode, domList: {
|
|
324
|
+
alert: ReactNode;
|
|
325
|
+
table: ReactNode;
|
|
326
|
+
toolbar: ReactNode;
|
|
327
|
+
}) => ReactNode;
|
|
328
|
+
tableStyle?: CSSProperties;
|
|
329
|
+
/** Light / header title config (subset of Pro `ListToolBar`). */
|
|
330
|
+
toolbar?: ProTableToolbarConfig;
|
|
331
|
+
/** Buttons on the right of the toolbar; `false` hides the whole toolbar. */
|
|
332
|
+
toolBarRender?: false | ((action: ProTableActionType | undefined, rows: {
|
|
333
|
+
selectedRowKeys?: Key[];
|
|
334
|
+
selectedRows?: T[];
|
|
335
|
+
}) => ReactNode[]);
|
|
336
|
+
/** Info icon after `headerTitle`. */
|
|
337
|
+
tooltip?: ReactNode;
|
|
338
|
+
/** Table cell style; the card follows it. @default 'borderless' */
|
|
339
|
+
variant?: TableVariant;
|
|
340
|
+
}
|
|
341
|
+
//#endregion
|
|
342
|
+
export { ProColumnType, ProColumns, ProTableActionType, ProTableAlertRender, ProTableAlertRenderProps, ProTableColumnsState, ProTableColumnsStateItem, ProTableColumnsStateMap, ProTableDensity, ProTableFormInstance, ProTableFormRule, ProTableLocale, ProTableOptionConfig, ProTablePageInfo, ProTableProps, ProTableRequest, ProTableRequestData, ProTableSchemaConfig, ProTableSearchConfig, ProTableSearchTransform, ProTableStatus, ProTableToolbarConfig, ProTableValueEnum, ProTableValueEnumItem, ProTableValueType, type SortOrder };
|
|
343
|
+
//# sourceMappingURL=type.d.mts.map
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import { useCallback, useEffect, useRef, useState } from "react";
|
|
2
|
+
//#region src/base-ui/ProTable/useFetchData.ts
|
|
3
|
+
/** `request` driving: debounced fetch on deps / page change, stale-response guard, polling. */
|
|
4
|
+
const useFetchData = (options) => {
|
|
5
|
+
const [data, setData] = useState(options.defaultData ?? []);
|
|
6
|
+
const [loading, setLoading] = useState(false);
|
|
7
|
+
const latest = useRef(options);
|
|
8
|
+
latest.current = options;
|
|
9
|
+
const requestId = useRef(0);
|
|
10
|
+
const [enabled, setEnabled] = useState(!options.manual);
|
|
11
|
+
const [token, setToken] = useState(0);
|
|
12
|
+
const pending = useRef([]);
|
|
13
|
+
const updateLoading = (next) => {
|
|
14
|
+
setLoading(next);
|
|
15
|
+
latest.current.onLoadingChange?.(next);
|
|
16
|
+
};
|
|
17
|
+
const run = useCallback(async () => {
|
|
18
|
+
const opts = latest.current;
|
|
19
|
+
if (!opts.request) return;
|
|
20
|
+
const id = ++requestId.current;
|
|
21
|
+
updateLoading(true);
|
|
22
|
+
try {
|
|
23
|
+
const params = { ...opts.getParams() };
|
|
24
|
+
if (opts.paginated) {
|
|
25
|
+
params.current = opts.pageInfo.current;
|
|
26
|
+
params.pageSize = opts.pageInfo.pageSize;
|
|
27
|
+
}
|
|
28
|
+
const response = await opts.request(params, opts.sort, opts.filter);
|
|
29
|
+
if (id !== requestId.current) return;
|
|
30
|
+
if (response.success === false) return;
|
|
31
|
+
const raw = response.data ?? [];
|
|
32
|
+
const next = opts.postData ? opts.postData(raw) : raw;
|
|
33
|
+
setData(next);
|
|
34
|
+
opts.setTotal(response.total ?? next.length);
|
|
35
|
+
opts.onLoad?.(next);
|
|
36
|
+
opts.onDataSourceChange?.(next);
|
|
37
|
+
} catch (error) {
|
|
38
|
+
if (id !== requestId.current) return;
|
|
39
|
+
if (opts.onRequestError) opts.onRequestError(error);
|
|
40
|
+
else console.error(error);
|
|
41
|
+
} finally {
|
|
42
|
+
if (id === requestId.current) updateLoading(false);
|
|
43
|
+
const resolvers = pending.current;
|
|
44
|
+
pending.current = [];
|
|
45
|
+
resolvers.forEach((resolve) => resolve());
|
|
46
|
+
}
|
|
47
|
+
}, []);
|
|
48
|
+
const { current, pageSize } = options.pageInfo;
|
|
49
|
+
useEffect(() => {
|
|
50
|
+
if (!enabled || !latest.current.request) return;
|
|
51
|
+
const delay = latest.current.debounceTime;
|
|
52
|
+
if (delay <= 0) {
|
|
53
|
+
run();
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
const timer = setTimeout(() => void run(), delay);
|
|
57
|
+
return () => clearTimeout(timer);
|
|
58
|
+
}, [
|
|
59
|
+
enabled,
|
|
60
|
+
token,
|
|
61
|
+
options.depsKey,
|
|
62
|
+
current,
|
|
63
|
+
pageSize,
|
|
64
|
+
run
|
|
65
|
+
]);
|
|
66
|
+
useEffect(() => {
|
|
67
|
+
const { polling } = latest.current;
|
|
68
|
+
if (!polling || loading || !enabled) return;
|
|
69
|
+
const interval = typeof polling === "function" ? polling(data) : polling;
|
|
70
|
+
if (!interval) return;
|
|
71
|
+
const timer = setTimeout(() => void run(), Math.max(interval, 2e3));
|
|
72
|
+
return () => clearTimeout(timer);
|
|
73
|
+
}, [
|
|
74
|
+
data,
|
|
75
|
+
loading,
|
|
76
|
+
enabled,
|
|
77
|
+
options.polling,
|
|
78
|
+
run
|
|
79
|
+
]);
|
|
80
|
+
return {
|
|
81
|
+
data,
|
|
82
|
+
loading,
|
|
83
|
+
reload: useCallback(() => new Promise((resolve) => {
|
|
84
|
+
if (!latest.current.request) {
|
|
85
|
+
resolve();
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
pending.current.push(resolve);
|
|
89
|
+
setEnabled(true);
|
|
90
|
+
setToken((value) => value + 1);
|
|
91
|
+
}), []),
|
|
92
|
+
setData
|
|
93
|
+
};
|
|
94
|
+
};
|
|
95
|
+
//#endregion
|
|
96
|
+
export { useFetchData };
|
|
97
|
+
|
|
98
|
+
//# sourceMappingURL=useFetchData.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useFetchData.mjs","names":[],"sources":["../../../src/base-ui/ProTable/useFetchData.ts"],"sourcesContent":["import { useCallback, useEffect, useRef, useState } from 'react';\n\nimport type { SortOrder } from '@/base-ui/Table/type';\n\nimport type { ProTablePageInfo, ProTableRequest } from './type';\n\ntype Filter = Record<string, (string | number | boolean)[] | null>;\n\nexport interface UseFetchDataOptions<T> {\n debounceTime: number;\n defaultData?: T[];\n /** Everything except the page that a fetch depends on, as a stable string. */\n depsKey: string;\n filter: Filter;\n getParams: () => Record<string, any>;\n manual: boolean;\n onDataSourceChange?: (data: T[]) => void;\n onLoad?: (data: T[]) => void;\n onLoadingChange?: (loading: boolean) => void;\n onRequestError?: (error: Error) => void;\n pageInfo: ProTablePageInfo;\n paginated: boolean;\n polling?: number | ((data: T[]) => number);\n postData?: (data: T[]) => T[];\n request?: ProTableRequest<T, any>;\n setTotal: (total: number) => void;\n sort: Record<string, SortOrder>;\n}\n\n/** `request` driving: debounced fetch on deps / page change, stale-response guard, polling. */\nexport const useFetchData = <T>(options: UseFetchDataOptions<T>) => {\n const [data, setData] = useState<T[]>(options.defaultData ?? []);\n const [loading, setLoading] = useState(false);\n const latest = useRef(options);\n latest.current = options;\n const requestId = useRef(0);\n const [enabled, setEnabled] = useState(!options.manual);\n const [token, setToken] = useState(0);\n const pending = useRef<(() => void)[]>([]);\n\n const updateLoading = (next: boolean) => {\n setLoading(next);\n latest.current.onLoadingChange?.(next);\n };\n\n const run = useCallback(async () => {\n const opts = latest.current;\n if (!opts.request) return;\n const id = ++requestId.current;\n updateLoading(true);\n try {\n const params: Record<string, any> = { ...opts.getParams() };\n if (opts.paginated) {\n params.current = opts.pageInfo.current;\n params.pageSize = opts.pageInfo.pageSize;\n }\n const response = await opts.request(params as any, opts.sort, opts.filter);\n if (id !== requestId.current) return;\n if (response.success === false) {\n // Pro keeps the previous data when the request reports failure.\n return;\n }\n const raw = response.data ?? [];\n const next = opts.postData ? opts.postData(raw) : raw;\n setData(next);\n opts.setTotal(response.total ?? next.length);\n opts.onLoad?.(next);\n opts.onDataSourceChange?.(next);\n } catch (error) {\n if (id !== requestId.current) return;\n if (opts.onRequestError) opts.onRequestError(error as Error);\n else console.error(error);\n } finally {\n if (id === requestId.current) updateLoading(false);\n const resolvers = pending.current;\n pending.current = [];\n resolvers.forEach((resolve) => resolve());\n }\n }, []);\n\n const { current, pageSize } = options.pageInfo;\n useEffect(() => {\n if (!enabled || !latest.current.request) return;\n const delay = latest.current.debounceTime;\n if (delay <= 0) {\n void run();\n return;\n }\n const timer = setTimeout(() => void run(), delay);\n return () => clearTimeout(timer);\n }, [enabled, token, options.depsKey, current, pageSize, run]);\n\n // Polling: schedule the next round once the current one is done.\n useEffect(() => {\n const { polling } = latest.current;\n if (!polling || loading || !enabled) return;\n const interval = typeof polling === 'function' ? polling(data) : polling;\n if (!interval) return;\n const timer = setTimeout(() => void run(), Math.max(interval, 2000));\n return () => clearTimeout(timer);\n }, [data, loading, enabled, options.polling, run]);\n\n /** Force a fetch (also enables a `manualRequest` table); resolves when it settles. */\n const reload = useCallback(\n () =>\n new Promise<void>((resolve) => {\n if (!latest.current.request) {\n resolve();\n return;\n }\n pending.current.push(resolve);\n setEnabled(true);\n setToken((value) => value + 1);\n }),\n [],\n );\n\n return { data, loading, reload, setData };\n};\n"],"mappings":";;;AA8BA,MAAa,gBAAmB,YAAoC;CAClE,MAAM,CAAC,MAAM,WAAW,SAAc,QAAQ,eAAe,CAAC,CAAC;CAC/D,MAAM,CAAC,SAAS,cAAc,SAAS,KAAK;CAC5C,MAAM,SAAS,OAAO,OAAO;CAC7B,OAAO,UAAU;CACjB,MAAM,YAAY,OAAO,CAAC;CAC1B,MAAM,CAAC,SAAS,cAAc,SAAS,CAAC,QAAQ,MAAM;CACtD,MAAM,CAAC,OAAO,YAAY,SAAS,CAAC;CACpC,MAAM,UAAU,OAAuB,CAAC,CAAC;CAEzC,MAAM,iBAAiB,SAAkB;EACvC,WAAW,IAAI;EACf,OAAO,QAAQ,kBAAkB,IAAI;CACvC;CAEA,MAAM,MAAM,YAAY,YAAY;EAClC,MAAM,OAAO,OAAO;EACpB,IAAI,CAAC,KAAK,SAAS;EACnB,MAAM,KAAK,EAAE,UAAU;EACvB,cAAc,IAAI;EAClB,IAAI;GACF,MAAM,SAA8B,EAAE,GAAG,KAAK,UAAU,EAAE;GAC1D,IAAI,KAAK,WAAW;IAClB,OAAO,UAAU,KAAK,SAAS;IAC/B,OAAO,WAAW,KAAK,SAAS;GAClC;GACA,MAAM,WAAW,MAAM,KAAK,QAAQ,QAAe,KAAK,MAAM,KAAK,MAAM;GACzE,IAAI,OAAO,UAAU,SAAS;GAC9B,IAAI,SAAS,YAAY,OAEvB;GAEF,MAAM,MAAM,SAAS,QAAQ,CAAC;GAC9B,MAAM,OAAO,KAAK,WAAW,KAAK,SAAS,GAAG,IAAI;GAClD,QAAQ,IAAI;GACZ,KAAK,SAAS,SAAS,SAAS,KAAK,MAAM;GAC3C,KAAK,SAAS,IAAI;GAClB,KAAK,qBAAqB,IAAI;EAChC,SAAS,OAAO;GACd,IAAI,OAAO,UAAU,SAAS;GAC9B,IAAI,KAAK,gBAAgB,KAAK,eAAe,KAAc;QACtD,QAAQ,MAAM,KAAK;EAC1B,UAAU;GACR,IAAI,OAAO,UAAU,SAAS,cAAc,KAAK;GACjD,MAAM,YAAY,QAAQ;GAC1B,QAAQ,UAAU,CAAC;GACnB,UAAU,SAAS,YAAY,QAAQ,CAAC;EAC1C;CACF,GAAG,CAAC,CAAC;CAEL,MAAM,EAAE,SAAS,aAAa,QAAQ;CACtC,gBAAgB;EACd,IAAI,CAAC,WAAW,CAAC,OAAO,QAAQ,SAAS;EACzC,MAAM,QAAQ,OAAO,QAAQ;EAC7B,IAAI,SAAS,GAAG;GACd,IAAS;GACT;EACF;EACA,MAAM,QAAQ,iBAAiB,KAAK,IAAI,GAAG,KAAK;EAChD,aAAa,aAAa,KAAK;CACjC,GAAG;EAAC;EAAS;EAAO,QAAQ;EAAS;EAAS;EAAU;CAAG,CAAC;CAG5D,gBAAgB;EACd,MAAM,EAAE,YAAY,OAAO;EAC3B,IAAI,CAAC,WAAW,WAAW,CAAC,SAAS;EACrC,MAAM,WAAW,OAAO,YAAY,aAAa,QAAQ,IAAI,IAAI;EACjE,IAAI,CAAC,UAAU;EACf,MAAM,QAAQ,iBAAiB,KAAK,IAAI,GAAG,KAAK,IAAI,UAAU,GAAI,CAAC;EACnE,aAAa,aAAa,KAAK;CACjC,GAAG;EAAC;EAAM;EAAS;EAAS,QAAQ;EAAS;CAAG,CAAC;CAiBjD,OAAO;EAAE;EAAM;EAAS,QAdT,kBAEX,IAAI,SAAe,YAAY;GAC7B,IAAI,CAAC,OAAO,QAAQ,SAAS;IAC3B,QAAQ;IACR;GACF;GACA,QAAQ,QAAQ,KAAK,OAAO;GAC5B,WAAW,IAAI;GACf,UAAU,UAAU,QAAQ,CAAC;EAC/B,CAAC,GACH,CAAC,CAG0B;EAAG;CAAQ;AAC1C"}
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
import { useForm } from "../FormKit/useForm.mjs";
|
|
2
|
+
import { formatDateValue, getByPath, getFieldName, isDateType, isEmptyValue, isNil, resolveValueType, setByPath } from "./utils.mjs";
|
|
3
|
+
import { useMemo, useRef } from "react";
|
|
4
|
+
//#region src/base-ui/ProTable/useSearchForm.ts
|
|
5
|
+
const NON_SEARCH_TYPES = /* @__PURE__ */ new Set([
|
|
6
|
+
"index",
|
|
7
|
+
"indexBorder",
|
|
8
|
+
"option"
|
|
9
|
+
]);
|
|
10
|
+
/** Flatten (group children included) and pick the columns that show in the search form. */
|
|
11
|
+
const getSearchFields = (columns = []) => {
|
|
12
|
+
const list = [];
|
|
13
|
+
const walk = (items) => {
|
|
14
|
+
items.forEach((column) => {
|
|
15
|
+
if (column.children?.length) {
|
|
16
|
+
walk(column.children);
|
|
17
|
+
return;
|
|
18
|
+
}
|
|
19
|
+
const index = list.length;
|
|
20
|
+
const valueType = resolveValueType(column);
|
|
21
|
+
if (column.hideInSearch || column.search === false) return;
|
|
22
|
+
if (valueType && NON_SEARCH_TYPES.has(valueType)) return;
|
|
23
|
+
if (isNil(column.dataIndex) && isNil(column.key) && !column.renderFormItem) return;
|
|
24
|
+
list.push({
|
|
25
|
+
column,
|
|
26
|
+
index,
|
|
27
|
+
name: getFieldName(column, index)
|
|
28
|
+
});
|
|
29
|
+
});
|
|
30
|
+
};
|
|
31
|
+
walk(columns);
|
|
32
|
+
return list.map((field, position) => ({
|
|
33
|
+
field,
|
|
34
|
+
position
|
|
35
|
+
})).sort((a, b) => (b.field.column.order ?? 0) - (a.field.column.order ?? 0) || a.position - b.position).map(({ field }) => field);
|
|
36
|
+
};
|
|
37
|
+
const ruleLength = (value) => typeof value === "string" || Array.isArray(value) ? value.length : Number(value);
|
|
38
|
+
/**
|
|
39
|
+
* antd-style `rules` → the base-ui Form field props: the first `required` rule becomes
|
|
40
|
+
* `required` (its `message` is the text), the rest run in order through one `validate`.
|
|
41
|
+
*/
|
|
42
|
+
const rulesToFieldProps = (rules, required) => {
|
|
43
|
+
const list = rules ?? [];
|
|
44
|
+
const requiredRule = list.find((rule) => rule.required);
|
|
45
|
+
const rest = list.filter((rule) => !rule.required || Object.keys(rule).length > 2);
|
|
46
|
+
const result = {};
|
|
47
|
+
if (requiredRule) result.required = requiredRule.message ? String(requiredRule.message) : true;
|
|
48
|
+
else if (required) result.required = true;
|
|
49
|
+
if (rest.length === 0) return result;
|
|
50
|
+
result.validate = async (value, values) => {
|
|
51
|
+
for (const rule of rest) {
|
|
52
|
+
const message = rule.message ? String(rule.message) : void 0;
|
|
53
|
+
if (rule.validator) {
|
|
54
|
+
try {
|
|
55
|
+
await rule.validator(rule, value, values);
|
|
56
|
+
} catch (error) {
|
|
57
|
+
return (error instanceof Error ? error.message : String(error ?? "")) || message || "Invalid value";
|
|
58
|
+
}
|
|
59
|
+
continue;
|
|
60
|
+
}
|
|
61
|
+
if (isEmptyValue(value)) continue;
|
|
62
|
+
if (rule.pattern && !rule.pattern.test(String(value))) return message ?? "Invalid format";
|
|
63
|
+
if (rule.len !== void 0 && ruleLength(value) !== rule.len) return message ?? `Must be ${rule.len}`;
|
|
64
|
+
if (rule.min !== void 0 && ruleLength(value) < rule.min) return message ?? `At least ${rule.min}`;
|
|
65
|
+
if (rule.max !== void 0 && ruleLength(value) > rule.max) return message ?? `At most ${rule.max}`;
|
|
66
|
+
}
|
|
67
|
+
};
|
|
68
|
+
return result;
|
|
69
|
+
};
|
|
70
|
+
/**
|
|
71
|
+
* The search form instance: a base-ui `useForm` instance (validation, `reset`, `subscribe` …)
|
|
72
|
+
* with the antd-style aliases ProTable users expect (`getFieldsValue`, `setFieldsValue`,
|
|
73
|
+
* `resetFields`, `validateFields`, `getFieldsFormatValue`).
|
|
74
|
+
*/
|
|
75
|
+
const useSearchForm = ({ dateFormatter, fields, initialValues, onSubmit, onValuesChange, validateOn }) => {
|
|
76
|
+
const latest = useRef({
|
|
77
|
+
dateFormatter,
|
|
78
|
+
fields,
|
|
79
|
+
onSubmit
|
|
80
|
+
});
|
|
81
|
+
latest.current = {
|
|
82
|
+
dateFormatter,
|
|
83
|
+
fields,
|
|
84
|
+
onSubmit
|
|
85
|
+
};
|
|
86
|
+
const [initial] = useMemo(() => {
|
|
87
|
+
const values = {};
|
|
88
|
+
fields.forEach(({ column, name }) => {
|
|
89
|
+
if (column.initialValue !== void 0) setByPath(values, name, column.initialValue);
|
|
90
|
+
});
|
|
91
|
+
return [{
|
|
92
|
+
...values,
|
|
93
|
+
...initialValues
|
|
94
|
+
}];
|
|
95
|
+
}, []);
|
|
96
|
+
const format = (raw) => {
|
|
97
|
+
const { dateFormatter: formatter, fields: list } = latest.current;
|
|
98
|
+
const result = {};
|
|
99
|
+
const fieldRoots = new Set(list.map((field) => field.name.split(".")[0]));
|
|
100
|
+
Object.entries(raw).forEach(([key, value]) => {
|
|
101
|
+
if (!fieldRoots.has(key) && !isEmptyValue(value)) result[key] = value;
|
|
102
|
+
});
|
|
103
|
+
list.forEach(({ column, name }) => {
|
|
104
|
+
const value = getByPath(raw, name.split("."));
|
|
105
|
+
if (isEmptyValue(value)) return;
|
|
106
|
+
const valueType = resolveValueType(column);
|
|
107
|
+
let next = valueType && isDateType(valueType) ? formatDateValue(value, valueType, formatter) : value;
|
|
108
|
+
const { search } = column;
|
|
109
|
+
if (search && typeof search === "object" && search.transform) {
|
|
110
|
+
const transformed = search.transform(next, name, raw);
|
|
111
|
+
if (transformed && typeof transformed === "object") {
|
|
112
|
+
Object.assign(result, transformed);
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
115
|
+
if (typeof transformed === "string") {
|
|
116
|
+
setByPath(result, transformed, next);
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
next = transformed;
|
|
120
|
+
}
|
|
121
|
+
setByPath(result, name, next);
|
|
122
|
+
});
|
|
123
|
+
return result;
|
|
124
|
+
};
|
|
125
|
+
const base = useForm({
|
|
126
|
+
initialValues: initial,
|
|
127
|
+
onSubmit: (values) => latest.current.onSubmit(format(values)),
|
|
128
|
+
onValuesChange,
|
|
129
|
+
validateOn
|
|
130
|
+
});
|
|
131
|
+
return {
|
|
132
|
+
base,
|
|
133
|
+
form: useMemo(() => Object.assign(base, {
|
|
134
|
+
getFieldsFormatValue: () => format(base.getValues()),
|
|
135
|
+
getFieldsValue: () => base.getValues(),
|
|
136
|
+
getFieldValue: (name) => base.getValue(name),
|
|
137
|
+
resetFields: () => base.reset(structuredClone(initial)),
|
|
138
|
+
setFieldsValue: (values) => base.setValues(values),
|
|
139
|
+
setFieldValue: (name, value) => base.setValue(name, value),
|
|
140
|
+
validateFields: async () => {
|
|
141
|
+
const result = await base.validate();
|
|
142
|
+
if (!result.valid) throw result;
|
|
143
|
+
return base.getValues();
|
|
144
|
+
}
|
|
145
|
+
}), [base])
|
|
146
|
+
};
|
|
147
|
+
};
|
|
148
|
+
//#endregion
|
|
149
|
+
export { getSearchFields, rulesToFieldProps, useSearchForm };
|
|
150
|
+
|
|
151
|
+
//# sourceMappingURL=useSearchForm.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useSearchForm.mjs","names":[],"sources":["../../../src/base-ui/ProTable/useSearchForm.ts"],"sourcesContent":["import {\n type FieldValidate,\n type FormValues,\n useForm,\n type UseFormOptions,\n} from '@lobehub/ui/base-ui/form';\nimport { useMemo, useRef } from 'react';\n\nimport type { ProColumnType, ProTableFormInstance, ProTableFormRule } from './type';\nimport {\n type DateFormatter,\n formatDateValue,\n getByPath,\n getFieldName,\n isDateType,\n isEmptyValue,\n isNil,\n resolveValueType,\n setByPath,\n} from './utils';\n\ntype ValidateOn = UseFormOptions<FormValues>['validateOn'];\n\nexport interface SearchFieldSchema {\n column: ProColumnType<any>;\n index: number;\n name: string;\n}\n\nconst NON_SEARCH_TYPES = new Set(['index', 'indexBorder', 'option']);\n\n/** Flatten (group children included) and pick the columns that show in the search form. */\nexport const getSearchFields = (columns: ProColumnType<any>[] = []): SearchFieldSchema[] => {\n const list: SearchFieldSchema[] = [];\n const walk = (items: ProColumnType<any>[]) => {\n items.forEach((column) => {\n if (column.children?.length) {\n walk(column.children);\n return;\n }\n const index = list.length;\n const valueType = resolveValueType(column);\n if (column.hideInSearch || column.search === false) return;\n if (valueType && NON_SEARCH_TYPES.has(valueType)) return;\n if (isNil(column.dataIndex) && isNil(column.key) && !column.renderFormItem) return;\n list.push({ column, index, name: getFieldName(column, index) });\n });\n };\n walk(columns);\n // Pro: larger `order` first; stable for equal orders.\n return list\n .map((field, position) => ({ field, position }))\n .sort(\n (a, b) =>\n (b.field.column.order ?? 0) - (a.field.column.order ?? 0) || a.position - b.position,\n )\n .map(({ field }) => field);\n};\n\nconst ruleLength = (value: unknown) =>\n typeof value === 'string' || Array.isArray(value) ? value.length : Number(value);\n\n/**\n * antd-style `rules` → the base-ui Form field props: the first `required` rule becomes\n * `required` (its `message` is the text), the rest run in order through one `validate`.\n */\nexport const rulesToFieldProps = (\n rules: ProTableFormRule[] | undefined,\n required?: boolean,\n): { required?: boolean | string; validate?: FieldValidate } => {\n const list = rules ?? [];\n const requiredRule = list.find((rule) => rule.required);\n const rest = list.filter((rule) => !rule.required || Object.keys(rule).length > 2);\n const result: { required?: boolean | string; validate?: FieldValidate } = {};\n if (requiredRule) result.required = requiredRule.message ? String(requiredRule.message) : true;\n else if (required) result.required = true;\n if (rest.length === 0) return result;\n result.validate = async (value, values) => {\n for (const rule of rest) {\n const message = rule.message ? String(rule.message) : undefined;\n if (rule.validator) {\n try {\n await rule.validator(rule, value, values);\n } catch (error) {\n return (\n (error instanceof Error ? error.message : String(error ?? '')) ||\n message ||\n 'Invalid value'\n );\n }\n continue;\n }\n if (isEmptyValue(value)) continue;\n if (rule.pattern && !rule.pattern.test(String(value))) return message ?? 'Invalid format';\n if (rule.len !== undefined && ruleLength(value) !== rule.len)\n return message ?? `Must be ${rule.len}`;\n if (rule.min !== undefined && ruleLength(value) < rule.min)\n return message ?? `At least ${rule.min}`;\n if (rule.max !== undefined && ruleLength(value) > rule.max)\n return message ?? `At most ${rule.max}`;\n }\n return undefined;\n };\n return result;\n};\n\nexport interface UseSearchFormOptions {\n dateFormatter: DateFormatter;\n fields: SearchFieldSchema[];\n initialValues?: Record<string, any>;\n onSubmit: (formatted: Record<string, any>) => void;\n onValuesChange?: (changed: Record<string, any>, all: Record<string, any>) => void;\n validateOn?: ValidateOn;\n}\n\n/**\n * The search form instance: a base-ui `useForm` instance (validation, `reset`, `subscribe` …)\n * with the antd-style aliases ProTable users expect (`getFieldsValue`, `setFieldsValue`,\n * `resetFields`, `validateFields`, `getFieldsFormatValue`).\n */\nexport const useSearchForm = ({\n dateFormatter,\n fields,\n initialValues,\n onSubmit,\n onValuesChange,\n validateOn,\n}: UseSearchFormOptions) => {\n const latest = useRef({ dateFormatter, fields, onSubmit });\n latest.current = { dateFormatter, fields, onSubmit };\n\n const [initial] = useMemo(() => {\n const values: Record<string, any> = {};\n fields.forEach(({ column, name }) => {\n if (column.initialValue !== undefined) setByPath(values, name, column.initialValue);\n });\n return [{ ...values, ...initialValues }];\n // Initial values are read once, like antd / Pro.\n }, []);\n\n const format = (raw: Record<string, any>) => {\n const { dateFormatter: formatter, fields: list } = latest.current;\n const result: Record<string, any> = {};\n const fieldRoots = new Set(list.map((field) => field.name.split('.')[0]));\n // Values set through `formRef` without a matching field pass through untouched.\n Object.entries(raw).forEach(([key, value]) => {\n if (!fieldRoots.has(key) && !isEmptyValue(value)) result[key] = value;\n });\n list.forEach(({ column, name }) => {\n const value = getByPath(raw, name.split('.'));\n if (isEmptyValue(value)) return;\n const valueType = resolveValueType(column);\n let next =\n valueType && isDateType(valueType) ? formatDateValue(value, valueType, formatter) : value;\n const { search } = column;\n if (search && typeof search === 'object' && search.transform) {\n const transformed = search.transform(next, name, raw);\n if (transformed && typeof transformed === 'object') {\n Object.assign(result, transformed);\n return;\n }\n if (typeof transformed === 'string') {\n setByPath(result, transformed, next);\n return;\n }\n next = transformed;\n }\n setByPath(result, name, next);\n });\n return result;\n };\n\n const base = useForm<Record<string, any>>({\n initialValues: initial,\n onSubmit: (values) => latest.current.onSubmit(format(values)),\n onValuesChange,\n validateOn,\n });\n\n // Aliases go onto the instance itself: base-ui `Form` looks the instance up by identity.\n const form = useMemo<ProTableFormInstance>(\n () =>\n Object.assign(base, {\n getFieldsFormatValue: () => format(base.getValues()),\n getFieldsValue: () => base.getValues(),\n getFieldValue: (name: string) => base.getValue(name),\n // base-ui `reset()` returns to the last submitted values; Pro resets to the initial ones.\n resetFields: () => base.reset(structuredClone(initial)),\n setFieldsValue: (values: Record<string, any>) => base.setValues(values),\n setFieldValue: (name: string, value: any) => base.setValue(name, value),\n validateFields: async () => {\n const result = await base.validate();\n if (!result.valid) throw result;\n return base.getValues();\n },\n }) as ProTableFormInstance,\n // `format` only reads `latest`.\n [base],\n );\n\n return { base, form };\n};\n"],"mappings":"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"}
|