@wordrhyme/auto-crud 1.1.8 → 1.2.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/dist/index.cjs +130 -63
- package/dist/index.d.cts +9 -10
- package/dist/index.d.ts +25 -26
- package/dist/index.js +128 -62
- package/package.json +9 -5
package/dist/index.cjs
CHANGED
|
@@ -61,6 +61,8 @@ let __formily_react = require("@formily/react");
|
|
|
61
61
|
__formily_react = __toESM(__formily_react);
|
|
62
62
|
let sonner = require("sonner");
|
|
63
63
|
sonner = __toESM(sonner);
|
|
64
|
+
let __tanstack_react_query = require("@tanstack/react-query");
|
|
65
|
+
__tanstack_react_query = __toESM(__tanstack_react_query);
|
|
64
66
|
|
|
65
67
|
//#region src/lib/utils.ts
|
|
66
68
|
function cn(...inputs) {
|
|
@@ -5173,12 +5175,8 @@ function getComponentProps(type, parsed) {
|
|
|
5173
5175
|
function createFormSchema(schema, options) {
|
|
5174
5176
|
const shape = schema.shape;
|
|
5175
5177
|
const properties = {};
|
|
5176
|
-
const { overrides, exclude = [], layout = "vertical", gridColumns = 1
|
|
5178
|
+
const { overrides, exclude = [], layout = "vertical", gridColumns = 1 } = options ?? {};
|
|
5177
5179
|
const decoratorProps = { className: "space-y-2" };
|
|
5178
|
-
if (labelAlign) decoratorProps.labelAlign = labelAlign;
|
|
5179
|
-
if (labelWidth) decoratorProps.labelWidth = labelWidth;
|
|
5180
|
-
if (labelCol) decoratorProps.labelCol = labelCol;
|
|
5181
|
-
if (wrapperCol) decoratorProps.wrapperCol = wrapperCol;
|
|
5182
5180
|
for (const [key, fieldSchema] of Object.entries(shape)) {
|
|
5183
5181
|
if (exclude.includes(key)) continue;
|
|
5184
5182
|
const parsed = parseZodField(fieldSchema);
|
|
@@ -5235,65 +5233,59 @@ function createEditFormSchema(schema, options) {
|
|
|
5235
5233
|
//#endregion
|
|
5236
5234
|
//#region src/lib/registries.ts
|
|
5237
5235
|
function createRegistry(label) {
|
|
5238
|
-
const
|
|
5236
|
+
const entries = /* @__PURE__ */ new Map();
|
|
5239
5237
|
const listeners = /* @__PURE__ */ new Set();
|
|
5240
|
-
let notifyQueued = false;
|
|
5241
5238
|
const notify = () => {
|
|
5242
|
-
if (notifyQueued) return;
|
|
5243
|
-
notifyQueued = true;
|
|
5244
5239
|
queueMicrotask(() => {
|
|
5245
|
-
|
|
5246
|
-
listeners.forEach((listener) => listener());
|
|
5240
|
+
for (const listener of listeners) listener();
|
|
5247
5241
|
});
|
|
5248
5242
|
};
|
|
5249
5243
|
return {
|
|
5250
5244
|
register(name, value) {
|
|
5251
|
-
|
|
5252
|
-
if (current === value) return;
|
|
5253
|
-
if (current !== void 0) {
|
|
5245
|
+
if (entries.has(name)) {
|
|
5254
5246
|
console.warn(`[AutoCrud] ${label} "${name}" is already registered.`);
|
|
5255
5247
|
return;
|
|
5256
5248
|
}
|
|
5257
|
-
|
|
5249
|
+
entries.set(name, value);
|
|
5258
5250
|
notify();
|
|
5259
5251
|
},
|
|
5260
5252
|
unregister(name) {
|
|
5261
|
-
if (!
|
|
5253
|
+
if (!entries.delete(name)) return;
|
|
5262
5254
|
notify();
|
|
5263
5255
|
},
|
|
5264
5256
|
get(name) {
|
|
5265
|
-
return
|
|
5257
|
+
return entries.get(name);
|
|
5266
5258
|
},
|
|
5267
5259
|
all() {
|
|
5268
|
-
return Object.fromEntries(
|
|
5260
|
+
return Object.fromEntries(entries);
|
|
5269
5261
|
},
|
|
5270
5262
|
subscribe(listener) {
|
|
5271
5263
|
listeners.add(listener);
|
|
5272
|
-
return () =>
|
|
5273
|
-
listeners.delete(listener);
|
|
5274
|
-
};
|
|
5264
|
+
return () => listeners.delete(listener);
|
|
5275
5265
|
}
|
|
5276
5266
|
};
|
|
5277
5267
|
}
|
|
5278
5268
|
const formComponents = createRegistry("form component");
|
|
5279
5269
|
const dataSources = createRegistry("data source");
|
|
5280
5270
|
function normalizeDataSourceConfig(config) {
|
|
5281
|
-
if (
|
|
5282
|
-
if (typeof config === "string") return {
|
|
5271
|
+
if (typeof config === "string") return config.length > 0 ? {
|
|
5283
5272
|
key: config,
|
|
5284
5273
|
dependsOn: [],
|
|
5285
5274
|
resetOnChange: false
|
|
5286
|
-
};
|
|
5275
|
+
} : void 0;
|
|
5276
|
+
if (!config?.key) return void 0;
|
|
5287
5277
|
return {
|
|
5288
5278
|
key: config.key,
|
|
5289
|
-
dependsOn: config.dependsOn
|
|
5290
|
-
resetOnChange: config.resetOnChange
|
|
5279
|
+
dependsOn: Array.isArray(config.dependsOn) ? config.dependsOn : [],
|
|
5280
|
+
resetOnChange: config.resetOnChange === true
|
|
5291
5281
|
};
|
|
5292
5282
|
}
|
|
5293
5283
|
function normalizeOptions(result) {
|
|
5294
5284
|
const options = Array.isArray(result) ? result : result?.options;
|
|
5295
|
-
if (!options) return [];
|
|
5296
|
-
return options.
|
|
5285
|
+
if (!Array.isArray(options)) return [];
|
|
5286
|
+
return options.filter((option) => {
|
|
5287
|
+
return typeof option === "object" && option !== null && typeof option.value === "string" && typeof option.label === "string";
|
|
5288
|
+
}).map((option) => ({
|
|
5297
5289
|
...option,
|
|
5298
5290
|
value: String(option.value)
|
|
5299
5291
|
}));
|
|
@@ -5347,7 +5339,7 @@ const defaultFieldComponents = {
|
|
|
5347
5339
|
decorator: "FormItem"
|
|
5348
5340
|
}
|
|
5349
5341
|
};
|
|
5350
|
-
function useRegistryVersion(subscribe) {
|
|
5342
|
+
function useRegistryVersion$1(subscribe) {
|
|
5351
5343
|
const [version, setVersion] = (0, react.useState)(0);
|
|
5352
5344
|
(0, react.useEffect)(() => subscribe(() => setVersion((current) => current + 1)), [subscribe]);
|
|
5353
5345
|
return version;
|
|
@@ -5359,13 +5351,15 @@ function isDataSourceConfig(value) {
|
|
|
5359
5351
|
}
|
|
5360
5352
|
function collectDataSourceConfigs(schema, result = {}) {
|
|
5361
5353
|
const properties = schema.properties;
|
|
5362
|
-
if (!properties) return result;
|
|
5354
|
+
if (!properties || typeof properties !== "object") return result;
|
|
5363
5355
|
for (const [key, property] of Object.entries(properties)) {
|
|
5364
|
-
|
|
5365
|
-
const
|
|
5356
|
+
if (!property || typeof property !== "object" || Array.isArray(property)) continue;
|
|
5357
|
+
const record = property;
|
|
5358
|
+
const customDataSource = record["x-data-source"];
|
|
5359
|
+
const schemaDataSource = record.dataSource;
|
|
5366
5360
|
const dataSourceConfig = isDataSourceConfig(customDataSource) ? customDataSource : isDataSourceConfig(schemaDataSource) ? schemaDataSource : void 0;
|
|
5367
5361
|
if (dataSourceConfig) result[key] = dataSourceConfig;
|
|
5368
|
-
collectDataSourceConfigs(
|
|
5362
|
+
collectDataSourceConfigs(record, result);
|
|
5369
5363
|
}
|
|
5370
5364
|
return result;
|
|
5371
5365
|
}
|
|
@@ -5374,7 +5368,7 @@ function useFormDataSources(form, schema) {
|
|
|
5374
5368
|
const sourceEntries = Object.entries(collectDataSourceConfigs(schema)).map(([fieldName, config]) => {
|
|
5375
5369
|
const normalized = normalizeDataSourceConfig(config);
|
|
5376
5370
|
return normalized ? [fieldName, normalized] : void 0;
|
|
5377
|
-
}).filter(
|
|
5371
|
+
}).filter((entry) => entry !== void 0);
|
|
5378
5372
|
if (sourceEntries.length === 0) return;
|
|
5379
5373
|
let active = true;
|
|
5380
5374
|
const effectId = Symbol("autoCrudDataSources");
|
|
@@ -5410,14 +5404,12 @@ function useFormDataSources(form, schema) {
|
|
|
5410
5404
|
};
|
|
5411
5405
|
for (const [fieldName, source] of sourceEntries) loadFieldOptions(fieldName, source);
|
|
5412
5406
|
form.addEffects(effectId, () => {
|
|
5413
|
-
new Set(sourceEntries.flatMap(([, source]) => source.dependsOn)).
|
|
5414
|
-
(
|
|
5415
|
-
|
|
5416
|
-
|
|
5417
|
-
|
|
5418
|
-
|
|
5419
|
-
});
|
|
5420
|
-
});
|
|
5407
|
+
for (const dependency of new Set(sourceEntries.flatMap(([, source]) => source.dependsOn))) (0, __formily_core.onFieldValueChange)(dependency, () => {
|
|
5408
|
+
for (const [fieldName, source] of sourceEntries) {
|
|
5409
|
+
if (!source.dependsOn.includes(dependency)) continue;
|
|
5410
|
+
if (source.resetOnChange) form.setValuesIn(fieldName, void 0);
|
|
5411
|
+
loadFieldOptions(fieldName, source);
|
|
5412
|
+
}
|
|
5421
5413
|
});
|
|
5422
5414
|
});
|
|
5423
5415
|
return () => {
|
|
@@ -5426,9 +5418,9 @@ function useFormDataSources(form, schema) {
|
|
|
5426
5418
|
form.removeEffects(effectId);
|
|
5427
5419
|
};
|
|
5428
5420
|
}, [
|
|
5429
|
-
useRegistryVersion(dataSources.subscribe),
|
|
5430
5421
|
form,
|
|
5431
|
-
schema
|
|
5422
|
+
schema,
|
|
5423
|
+
useRegistryVersion$1(dataSources.subscribe)
|
|
5432
5424
|
]);
|
|
5433
5425
|
}
|
|
5434
5426
|
function AutoFormInner({ schema: zodSchema, initialValues, onSubmit, overrides, mode = "create", loading = false, gridColumns = 1, labelAlign = "top", labelWidth, showSubmitButton = true }, ref) {
|
|
@@ -5436,20 +5428,26 @@ function AutoFormInner({ schema: zodSchema, initialValues, onSubmit, overrides,
|
|
|
5436
5428
|
const formSchema = (0, react.useMemo)(() => createEditFormSchema(zodSchema, {
|
|
5437
5429
|
overrides,
|
|
5438
5430
|
layout: "grid",
|
|
5439
|
-
gridColumns
|
|
5440
|
-
labelAlign,
|
|
5441
|
-
labelWidth
|
|
5431
|
+
gridColumns
|
|
5442
5432
|
}), [
|
|
5443
5433
|
zodSchema,
|
|
5444
5434
|
overrides,
|
|
5445
|
-
gridColumns
|
|
5446
|
-
labelAlign,
|
|
5447
|
-
labelWidth
|
|
5435
|
+
gridColumns
|
|
5448
5436
|
]);
|
|
5437
|
+
const formLayout = (0, react.useMemo)(() => {
|
|
5438
|
+
const inlineLabel = labelAlign === "left" || labelAlign === "right";
|
|
5439
|
+
return {
|
|
5440
|
+
labelPlacement: inlineLabel ? "start" : "top",
|
|
5441
|
+
itemProps: inlineLabel ? { label: {
|
|
5442
|
+
className: labelAlign === "right" ? "text-right" : void 0,
|
|
5443
|
+
style: labelWidth != null ? { width: labelWidth } : void 0
|
|
5444
|
+
} } : void 0
|
|
5445
|
+
};
|
|
5446
|
+
}, [labelAlign, labelWidth]);
|
|
5449
5447
|
const fieldComponents = (0, react.useMemo)(() => ({ fields: {
|
|
5450
5448
|
...defaultFieldComponents,
|
|
5451
5449
|
...formComponents.all()
|
|
5452
|
-
} }), [useRegistryVersion(formComponents.subscribe)]);
|
|
5450
|
+
} }), [useRegistryVersion$1(formComponents.subscribe)]);
|
|
5453
5451
|
useFormDataSources(form, formSchema);
|
|
5454
5452
|
const handleSubmit = async () => {
|
|
5455
5453
|
await form.validate();
|
|
@@ -5458,6 +5456,7 @@ function AutoFormInner({ schema: zodSchema, initialValues, onSubmit, overrides,
|
|
|
5458
5456
|
(0, react.useImperativeHandle)(ref, () => ({ submit: handleSubmit }));
|
|
5459
5457
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(__pixpilot_formily_shadcn.Form, {
|
|
5460
5458
|
form,
|
|
5459
|
+
layout: formLayout,
|
|
5461
5460
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(__pixpilot_formily_shadcn.JsonSchemaField, {
|
|
5462
5461
|
schema: formSchema,
|
|
5463
5462
|
components: fieldComponents
|
|
@@ -6307,6 +6306,69 @@ function toBatchOptions(options) {
|
|
|
6307
6306
|
value
|
|
6308
6307
|
}));
|
|
6309
6308
|
}
|
|
6309
|
+
function getFilterDataSource(config) {
|
|
6310
|
+
if (config.filter && typeof config.filter === "object") return config.filter.dataSource ?? config.dataSource;
|
|
6311
|
+
return config.dataSource;
|
|
6312
|
+
}
|
|
6313
|
+
function shouldLoadDynamicFilterOptions(config) {
|
|
6314
|
+
if (config.filter === false) return false;
|
|
6315
|
+
if (normalizeFieldOptions(config.enum)) return false;
|
|
6316
|
+
if (config.filter && typeof config.filter === "object") {
|
|
6317
|
+
if (config.filter.enabled === false || config.filter.hidden === true) return false;
|
|
6318
|
+
if (normalizeFieldOptions(config.filter.options)) return false;
|
|
6319
|
+
}
|
|
6320
|
+
return normalizeDataSourceConfig(getFilterDataSource(config)) !== void 0;
|
|
6321
|
+
}
|
|
6322
|
+
function useRegistryVersion(subscribe) {
|
|
6323
|
+
const [version, setVersion] = react.useState(0);
|
|
6324
|
+
react.useEffect(() => subscribe(() => setVersion((current) => current + 1)), [subscribe]);
|
|
6325
|
+
return version;
|
|
6326
|
+
}
|
|
6327
|
+
function useDynamicFilterOptions(fields) {
|
|
6328
|
+
const registryVersion = useRegistryVersion(dataSources.subscribe);
|
|
6329
|
+
const [optionsByField, setOptionsByField] = react.useState({});
|
|
6330
|
+
const sourceEntries = react.useMemo(() => {
|
|
6331
|
+
if (!fields) return [];
|
|
6332
|
+
return Object.entries(fields).flatMap(([field, config]) => {
|
|
6333
|
+
if (!shouldLoadDynamicFilterOptions(config)) return [];
|
|
6334
|
+
const source = normalizeDataSourceConfig(getFilterDataSource(config));
|
|
6335
|
+
return source ? [{
|
|
6336
|
+
field,
|
|
6337
|
+
source
|
|
6338
|
+
}] : [];
|
|
6339
|
+
});
|
|
6340
|
+
}, [fields]);
|
|
6341
|
+
react.useEffect(() => {
|
|
6342
|
+
if (sourceEntries.length === 0) {
|
|
6343
|
+
setOptionsByField({});
|
|
6344
|
+
return;
|
|
6345
|
+
}
|
|
6346
|
+
let active = true;
|
|
6347
|
+
const controller = typeof AbortController === "undefined" ? void 0 : new AbortController();
|
|
6348
|
+
Promise.all(sourceEntries.map(async ({ field, source }) => {
|
|
6349
|
+
const loader = dataSources.get(source.key);
|
|
6350
|
+
if (!loader) return [field, []];
|
|
6351
|
+
try {
|
|
6352
|
+
return [field, normalizeOptions(await loader({
|
|
6353
|
+
field,
|
|
6354
|
+
values: {},
|
|
6355
|
+
signal: controller?.signal
|
|
6356
|
+
}))];
|
|
6357
|
+
} catch (error) {
|
|
6358
|
+
if (!controller?.signal.aborted) console.warn(`[AutoCrud] Failed to load filter data source "${source.key}".`, error);
|
|
6359
|
+
return [field, []];
|
|
6360
|
+
}
|
|
6361
|
+
})).then((entries) => {
|
|
6362
|
+
if (!active) return;
|
|
6363
|
+
setOptionsByField(Object.fromEntries(entries));
|
|
6364
|
+
});
|
|
6365
|
+
return () => {
|
|
6366
|
+
active = false;
|
|
6367
|
+
controller?.abort();
|
|
6368
|
+
};
|
|
6369
|
+
}, [sourceEntries, registryVersion]);
|
|
6370
|
+
return optionsByField;
|
|
6371
|
+
}
|
|
6310
6372
|
function getOptionLabel(value, options) {
|
|
6311
6373
|
const stringValue = String(value);
|
|
6312
6374
|
return options?.find((option) => option.value === stringValue)?.label ?? stringValue;
|
|
@@ -6315,15 +6377,21 @@ function getOptionLabel(value, options) {
|
|
|
6315
6377
|
* 从统一配置生成表格 overrides
|
|
6316
6378
|
* 当 filter 配置存在时,合并到 meta 中(不影响列隐藏)
|
|
6317
6379
|
*/
|
|
6318
|
-
function buildTableOverrides(fields, legacyOverrides) {
|
|
6380
|
+
function buildTableOverrides(fields, legacyOverrides, dynamicFilterOptions) {
|
|
6319
6381
|
const result = { ...legacyOverrides };
|
|
6320
6382
|
if (fields) for (const [key, config] of Object.entries(fields)) {
|
|
6321
|
-
const
|
|
6383
|
+
const fieldOptions = normalizeFieldOptions(config.enum);
|
|
6384
|
+
const tableOptions = toTableOptions(fieldOptions);
|
|
6385
|
+
const dynamicOptions = !fieldOptions ? toTableOptions(normalizeFieldOptions(dynamicFilterOptions?.[key])) : void 0;
|
|
6322
6386
|
const tableMeta = config.table !== false && typeof config.table === "object" ? config.table.meta : void 0;
|
|
6323
6387
|
const fieldEnumMeta = tableOptions ? {
|
|
6324
6388
|
options: tableOptions,
|
|
6325
6389
|
variant: "multiSelect"
|
|
6326
6390
|
} : void 0;
|
|
6391
|
+
const fieldDataSourceMeta = dynamicOptions ? {
|
|
6392
|
+
options: dynamicOptions,
|
|
6393
|
+
variant: "multiSelect"
|
|
6394
|
+
} : void 0;
|
|
6327
6395
|
let filterMeta;
|
|
6328
6396
|
if (config.filter && typeof config.filter === "object") {
|
|
6329
6397
|
if (config.filter.enabled !== false && config.filter.hidden !== true) {
|
|
@@ -6341,11 +6409,12 @@ function buildTableOverrides(fields, legacyOverrides) {
|
|
|
6341
6409
|
enableColumnFilter: false
|
|
6342
6410
|
};
|
|
6343
6411
|
}
|
|
6344
|
-
if (fieldEnumMeta || tableMeta || filterMeta) result[key] = {
|
|
6412
|
+
if (fieldEnumMeta || fieldDataSourceMeta || tableMeta || filterMeta) result[key] = {
|
|
6345
6413
|
...result[key],
|
|
6346
6414
|
meta: {
|
|
6347
6415
|
...result[key]?.meta ?? {},
|
|
6348
6416
|
...fieldEnumMeta ?? {},
|
|
6417
|
+
...fieldDataSourceMeta ?? {},
|
|
6349
6418
|
...tableMeta ?? {},
|
|
6350
6419
|
...filterMeta ?? {}
|
|
6351
6420
|
}
|
|
@@ -6658,6 +6727,7 @@ function AutoCrudTable({ title, description, schema, resource, fields, table: ta
|
|
|
6658
6727
|
const [selectedCount, setSelectedCount] = react.useState(0);
|
|
6659
6728
|
const [exporting, setExporting] = react.useState(false);
|
|
6660
6729
|
const getSelectedRowsRef = react.useRef(null);
|
|
6730
|
+
const dynamicFilterOptions = useDynamicFilterOptions(resolvedFields);
|
|
6661
6731
|
const importColumns = react.useMemo(() => {
|
|
6662
6732
|
const shape = resolvedSchema.shape;
|
|
6663
6733
|
const denySet = new Set(denyFields ?? []);
|
|
@@ -6694,7 +6764,11 @@ function AutoCrudTable({ title, description, schema, resource, fields, table: ta
|
|
|
6694
6764
|
setExporting(false);
|
|
6695
6765
|
}
|
|
6696
6766
|
}, [selectedCount, handleExport]);
|
|
6697
|
-
const tableOverrides = react.useMemo(() => buildTableOverrides(resolvedFields, tableConfig?.overrides), [
|
|
6767
|
+
const tableOverrides = react.useMemo(() => buildTableOverrides(resolvedFields, tableConfig?.overrides, dynamicFilterOptions), [
|
|
6768
|
+
resolvedFields,
|
|
6769
|
+
tableConfig?.overrides,
|
|
6770
|
+
dynamicFilterOptions
|
|
6771
|
+
]);
|
|
6698
6772
|
const formOverrides = react.useMemo(() => buildFormOverrides(resolvedFields, formConfig?.overrides, denyFields), [
|
|
6699
6773
|
resolvedFields,
|
|
6700
6774
|
formConfig?.overrides,
|
|
@@ -7491,13 +7565,6 @@ function DataTableToolbarFilter({ column }) {
|
|
|
7491
7565
|
}
|
|
7492
7566
|
}
|
|
7493
7567
|
|
|
7494
|
-
//#endregion
|
|
7495
|
-
//#region ../../node_modules/.pnpm/@tanstack+query-core@5.90.20/node_modules/@tanstack/query-core/build/modern/utils.js
|
|
7496
|
-
var isServer = typeof window === "undefined" || "Deno" in globalThis;
|
|
7497
|
-
function keepPreviousData(previousData) {
|
|
7498
|
-
return previousData;
|
|
7499
|
-
}
|
|
7500
|
-
|
|
7501
7568
|
//#endregion
|
|
7502
7569
|
//#region src/hooks/use-auto-crud-resource.ts
|
|
7503
7570
|
/** 默认 toast 适配器(使用 sonner) */
|
|
@@ -7647,7 +7714,7 @@ function useAutoCrudResource({ router, schema, query: queryTransform, options =
|
|
|
7647
7714
|
queryTransform
|
|
7648
7715
|
]);
|
|
7649
7716
|
const listQuery = router.list.useQuery(queryInput, {
|
|
7650
|
-
placeholderData: keepPreviousData,
|
|
7717
|
+
placeholderData: __tanstack_react_query.keepPreviousData,
|
|
7651
7718
|
staleTime: 0
|
|
7652
7719
|
});
|
|
7653
7720
|
const tableRows = listQuery.data?.data ?? [];
|
package/dist/index.d.cts
CHANGED
|
@@ -642,11 +642,10 @@ type AutoCrudDataSourceContext = {
|
|
|
642
642
|
values?: Record<string, unknown>;
|
|
643
643
|
signal?: AbortSignal;
|
|
644
644
|
};
|
|
645
|
-
type
|
|
646
|
-
options?: AutoCrudOption[];
|
|
647
|
-
}> | AutoCrudOption[] | {
|
|
645
|
+
type AutoCrudDataSourceResult = AutoCrudOption[] | {
|
|
648
646
|
options?: AutoCrudOption[];
|
|
649
647
|
};
|
|
648
|
+
type AutoCrudDataSourceLoader = (context: AutoCrudDataSourceContext) => AutoCrudDataSourceResult | Promise<AutoCrudDataSourceResult>;
|
|
650
649
|
type AutoCrudDataSourceConfig = string | {
|
|
651
650
|
key: string;
|
|
652
651
|
dependsOn?: string[];
|
|
@@ -672,9 +671,7 @@ declare function normalizeDataSourceConfig(config: AutoCrudDataSourceConfig | un
|
|
|
672
671
|
dependsOn: string[];
|
|
673
672
|
resetOnChange: boolean;
|
|
674
673
|
} | undefined;
|
|
675
|
-
declare function normalizeOptions(result: AutoCrudOption[]
|
|
676
|
-
options?: AutoCrudOption[];
|
|
677
|
-
} | undefined): AutoCrudOption[];
|
|
674
|
+
declare function normalizeOptions(result: AutoCrudDataSourceResult | undefined): AutoCrudOption[];
|
|
678
675
|
//#endregion
|
|
679
676
|
//#region src/components/auto-crud/auto-crud-table.d.ts
|
|
680
677
|
/**
|
|
@@ -688,6 +685,8 @@ interface FilterConfig {
|
|
|
688
685
|
variant?: 'text' | 'number' | 'range' | 'date' | 'dateRange' | 'boolean' | 'select' | 'multiSelect';
|
|
689
686
|
/** select/multiSelect 的选项列表 */
|
|
690
687
|
options?: FieldOption[];
|
|
688
|
+
/** select/multiSelect 的动态选项源 */
|
|
689
|
+
dataSource?: AutoCrudDataSourceConfig;
|
|
691
690
|
/** range 的最小/最大值 */
|
|
692
691
|
range?: [number, number];
|
|
693
692
|
/** number 的单位 */
|
|
@@ -1182,12 +1181,12 @@ declare const filterItemSchema: z.ZodObject<{
|
|
|
1182
1181
|
variant: z.ZodEnum<{
|
|
1183
1182
|
number: "number";
|
|
1184
1183
|
boolean: "boolean";
|
|
1185
|
-
date: "date";
|
|
1186
1184
|
text: "text";
|
|
1185
|
+
range: "range";
|
|
1186
|
+
date: "date";
|
|
1187
|
+
dateRange: "dateRange";
|
|
1187
1188
|
select: "select";
|
|
1188
1189
|
multiSelect: "multiSelect";
|
|
1189
|
-
dateRange: "dateRange";
|
|
1190
|
-
range: "range";
|
|
1191
1190
|
}>;
|
|
1192
1191
|
operator: z.ZodEnum<{
|
|
1193
1192
|
iLike: "iLike";
|
|
@@ -1524,7 +1523,7 @@ declare function createFormSchema<T extends z.ZodObject<z.ZodRawShape>>(schema:
|
|
|
1524
1523
|
/**
|
|
1525
1524
|
* 为编辑模式创建 Form Schema(排除 id, createdAt, updatedAt)
|
|
1526
1525
|
*/
|
|
1527
|
-
declare function createEditFormSchema<T extends z.ZodObject<z.ZodRawShape>>(schema: T, options?: Omit<CreateFormSchemaOptions,
|
|
1526
|
+
declare function createEditFormSchema<T extends z.ZodObject<z.ZodRawShape>>(schema: T, options?: Omit<CreateFormSchemaOptions, 'exclude'>): ISchema;
|
|
1528
1527
|
//#endregion
|
|
1529
1528
|
//#region src/lib/schema-bridge/schema-adapter.d.ts
|
|
1530
1529
|
/**
|
package/dist/index.d.ts
CHANGED
|
@@ -4,7 +4,7 @@ import * as _tanstack_react_table0 from "@tanstack/react-table";
|
|
|
4
4
|
import { Column, ColumnDef, ColumnSort, Row, RowData, Table, TableOptions, TableState } from "@tanstack/react-table";
|
|
5
5
|
import { Command, DropdownMenuTrigger, PopoverContent } from "@pixpilot/shadcn";
|
|
6
6
|
import { ClassValue } from "clsx";
|
|
7
|
-
import * as
|
|
7
|
+
import * as react_jsx_runtime3 from "react/jsx-runtime";
|
|
8
8
|
import { z } from "zod";
|
|
9
9
|
import { ISchema } from "@formily/json-schema";
|
|
10
10
|
|
|
@@ -362,7 +362,7 @@ declare function AutoTableActionBar<TData>({
|
|
|
362
362
|
enableDelete,
|
|
363
363
|
extraActions,
|
|
364
364
|
actions
|
|
365
|
-
}: AutoTableActionBarProps<TData>):
|
|
365
|
+
}: AutoTableActionBarProps<TData>): react_jsx_runtime3.JSX.Element;
|
|
366
366
|
//#endregion
|
|
367
367
|
//#region src/lib/schema-bridge/types.d.ts
|
|
368
368
|
/**
|
|
@@ -530,7 +530,7 @@ declare function AutoTable<T extends z.ZodObject<z.ZodRawShape>>({
|
|
|
530
530
|
showDefaultExport,
|
|
531
531
|
onSelectedCountChange,
|
|
532
532
|
getSelectedRows
|
|
533
|
-
}: AutoTableProps<T>):
|
|
533
|
+
}: AutoTableProps<T>): react_jsx_runtime3.JSX.Element;
|
|
534
534
|
//#endregion
|
|
535
535
|
//#region src/i18n/locale.d.ts
|
|
536
536
|
/**
|
|
@@ -642,11 +642,10 @@ type AutoCrudDataSourceContext = {
|
|
|
642
642
|
values?: Record<string, unknown>;
|
|
643
643
|
signal?: AbortSignal;
|
|
644
644
|
};
|
|
645
|
-
type
|
|
646
|
-
options?: AutoCrudOption[];
|
|
647
|
-
}> | AutoCrudOption[] | {
|
|
645
|
+
type AutoCrudDataSourceResult = AutoCrudOption[] | {
|
|
648
646
|
options?: AutoCrudOption[];
|
|
649
647
|
};
|
|
648
|
+
type AutoCrudDataSourceLoader = (context: AutoCrudDataSourceContext) => AutoCrudDataSourceResult | Promise<AutoCrudDataSourceResult>;
|
|
650
649
|
type AutoCrudDataSourceConfig = string | {
|
|
651
650
|
key: string;
|
|
652
651
|
dependsOn?: string[];
|
|
@@ -672,9 +671,7 @@ declare function normalizeDataSourceConfig(config: AutoCrudDataSourceConfig | un
|
|
|
672
671
|
dependsOn: string[];
|
|
673
672
|
resetOnChange: boolean;
|
|
674
673
|
} | undefined;
|
|
675
|
-
declare function normalizeOptions(result: AutoCrudOption[]
|
|
676
|
-
options?: AutoCrudOption[];
|
|
677
|
-
} | undefined): AutoCrudOption[];
|
|
674
|
+
declare function normalizeOptions(result: AutoCrudDataSourceResult | undefined): AutoCrudOption[];
|
|
678
675
|
//#endregion
|
|
679
676
|
//#region src/components/auto-crud/auto-crud-table.d.ts
|
|
680
677
|
/**
|
|
@@ -688,6 +685,8 @@ interface FilterConfig {
|
|
|
688
685
|
variant?: 'text' | 'number' | 'range' | 'date' | 'dateRange' | 'boolean' | 'select' | 'multiSelect';
|
|
689
686
|
/** select/multiSelect 的选项列表 */
|
|
690
687
|
options?: FieldOption[];
|
|
688
|
+
/** select/multiSelect 的动态选项源 */
|
|
689
|
+
dataSource?: AutoCrudDataSourceConfig;
|
|
691
690
|
/** range 的最小/最大值 */
|
|
692
691
|
range?: [number, number];
|
|
693
692
|
/** number 的单位 */
|
|
@@ -936,7 +935,7 @@ declare function AutoCrudTable<TSchema extends z.ZodObject<z.ZodRawShape>>({
|
|
|
936
935
|
toolbarActions,
|
|
937
936
|
locale: localeProp,
|
|
938
937
|
onCreate
|
|
939
|
-
}: AutoCrudTableProps<TSchema>):
|
|
938
|
+
}: AutoCrudTableProps<TSchema>): react_jsx_runtime3.JSX.Element;
|
|
940
939
|
//#endregion
|
|
941
940
|
//#region src/components/auto-crud/auto-form.d.ts
|
|
942
941
|
interface AutoFormProps<T extends z.ZodObject<z.ZodRawShape>> {
|
|
@@ -968,7 +967,7 @@ declare function AutoFormInner<T extends z.ZodObject<z.ZodRawShape>>({
|
|
|
968
967
|
labelAlign,
|
|
969
968
|
labelWidth,
|
|
970
969
|
showSubmitButton
|
|
971
|
-
}: AutoFormProps<T>, ref: React.Ref<AutoFormRef>):
|
|
970
|
+
}: AutoFormProps<T>, ref: React.Ref<AutoFormRef>): react_jsx_runtime3.JSX.Element;
|
|
972
971
|
declare const AutoForm: <T extends z.ZodObject<z.ZodRawShape>>(props: AutoFormProps<T> & {
|
|
973
972
|
ref?: React.Ref<AutoFormRef>;
|
|
974
973
|
}) => ReturnType<typeof AutoFormInner>;
|
|
@@ -1182,12 +1181,12 @@ declare const filterItemSchema: z.ZodObject<{
|
|
|
1182
1181
|
variant: z.ZodEnum<{
|
|
1183
1182
|
number: "number";
|
|
1184
1183
|
boolean: "boolean";
|
|
1185
|
-
date: "date";
|
|
1186
1184
|
text: "text";
|
|
1185
|
+
range: "range";
|
|
1186
|
+
date: "date";
|
|
1187
|
+
dateRange: "dateRange";
|
|
1187
1188
|
select: "select";
|
|
1188
1189
|
multiSelect: "multiSelect";
|
|
1189
|
-
dateRange: "dateRange";
|
|
1190
|
-
range: "range";
|
|
1191
1190
|
}>;
|
|
1192
1191
|
operator: z.ZodEnum<{
|
|
1193
1192
|
iLike: "iLike";
|
|
@@ -1270,7 +1269,7 @@ declare function AutoTableSimpleFilters<TData>({
|
|
|
1270
1269
|
filters: externalFilters,
|
|
1271
1270
|
onFiltersChange,
|
|
1272
1271
|
leading
|
|
1273
|
-
}: AutoTableSimpleFiltersProps<TData>):
|
|
1272
|
+
}: AutoTableSimpleFiltersProps<TData>): react_jsx_runtime3.JSX.Element | null;
|
|
1274
1273
|
//#endregion
|
|
1275
1274
|
//#region src/components/auto-crud/form-modal.d.ts
|
|
1276
1275
|
type ModalVariant = "dialog" | "sheet";
|
|
@@ -1312,7 +1311,7 @@ declare function CrudFormModal<T extends z.ZodObject<z.ZodRawShape>>({
|
|
|
1312
1311
|
labelAlign,
|
|
1313
1312
|
labelWidth,
|
|
1314
1313
|
className
|
|
1315
|
-
}: CrudFormModalProps<T>):
|
|
1314
|
+
}: CrudFormModalProps<T>): react_jsx_runtime3.JSX.Element;
|
|
1316
1315
|
//#endregion
|
|
1317
1316
|
//#region src/components/auto-crud/import-dialog.d.ts
|
|
1318
1317
|
interface ImportDialogProps {
|
|
@@ -1334,7 +1333,7 @@ declare function ImportDialog({
|
|
|
1334
1333
|
columns,
|
|
1335
1334
|
title,
|
|
1336
1335
|
locale
|
|
1337
|
-
}: ImportDialogProps):
|
|
1336
|
+
}: ImportDialogProps): react_jsx_runtime3.JSX.Element;
|
|
1338
1337
|
//#endregion
|
|
1339
1338
|
//#region src/components/auto-crud/export-dialog.d.ts
|
|
1340
1339
|
type ExportMode = "selected" | "filtered";
|
|
@@ -1354,7 +1353,7 @@ declare function ExportDialog({
|
|
|
1354
1353
|
selectedCount,
|
|
1355
1354
|
onExport,
|
|
1356
1355
|
canExportFiltered
|
|
1357
|
-
}: ExportDialogProps):
|
|
1356
|
+
}: ExportDialogProps): react_jsx_runtime3.JSX.Element;
|
|
1358
1357
|
//#endregion
|
|
1359
1358
|
//#region src/components/data-table/data-table.d.ts
|
|
1360
1359
|
interface DataTableProps<TData> extends React$1.ComponentProps<"div"> {
|
|
@@ -1367,7 +1366,7 @@ declare function DataTable<TData>({
|
|
|
1367
1366
|
children,
|
|
1368
1367
|
className,
|
|
1369
1368
|
...props
|
|
1370
|
-
}: DataTableProps<TData>):
|
|
1369
|
+
}: DataTableProps<TData>): react_jsx_runtime3.JSX.Element;
|
|
1371
1370
|
//#endregion
|
|
1372
1371
|
//#region src/components/data-table/data-table-advanced-toolbar.d.ts
|
|
1373
1372
|
interface DataTableAdvancedToolbarProps<TData> extends React$1.ComponentProps<"div"> {
|
|
@@ -1378,7 +1377,7 @@ declare function DataTableAdvancedToolbar<TData>({
|
|
|
1378
1377
|
children,
|
|
1379
1378
|
className,
|
|
1380
1379
|
...props
|
|
1381
|
-
}: DataTableAdvancedToolbarProps<TData>):
|
|
1380
|
+
}: DataTableAdvancedToolbarProps<TData>): react_jsx_runtime3.JSX.Element;
|
|
1382
1381
|
//#endregion
|
|
1383
1382
|
//#region src/components/data-table/data-table-column-header.d.ts
|
|
1384
1383
|
interface DataTableColumnHeaderProps<TData, TValue> extends React.ComponentProps<typeof DropdownMenuTrigger> {
|
|
@@ -1390,7 +1389,7 @@ declare function DataTableColumnHeader<TData, TValue>({
|
|
|
1390
1389
|
label,
|
|
1391
1390
|
className,
|
|
1392
1391
|
...props
|
|
1393
|
-
}: DataTableColumnHeaderProps<TData, TValue>):
|
|
1392
|
+
}: DataTableColumnHeaderProps<TData, TValue>): react_jsx_runtime3.JSX.Element;
|
|
1394
1393
|
//#endregion
|
|
1395
1394
|
//#region src/components/data-table/data-table-faceted-filter.d.ts
|
|
1396
1395
|
interface DataTableFacetedFilterProps<TData, TValue> {
|
|
@@ -1404,7 +1403,7 @@ declare function DataTableFacetedFilter<TData, TValue>({
|
|
|
1404
1403
|
title,
|
|
1405
1404
|
options,
|
|
1406
1405
|
multiple
|
|
1407
|
-
}: DataTableFacetedFilterProps<TData, TValue>):
|
|
1406
|
+
}: DataTableFacetedFilterProps<TData, TValue>): react_jsx_runtime3.JSX.Element;
|
|
1408
1407
|
//#endregion
|
|
1409
1408
|
//#region src/components/data-table/data-table-pagination.d.ts
|
|
1410
1409
|
interface DataTablePaginationProps<TData> extends React.ComponentProps<"div"> {
|
|
@@ -1416,7 +1415,7 @@ declare function DataTablePagination<TData>({
|
|
|
1416
1415
|
pageSizeOptions,
|
|
1417
1416
|
className,
|
|
1418
1417
|
...props
|
|
1419
|
-
}: DataTablePaginationProps<TData>):
|
|
1418
|
+
}: DataTablePaginationProps<TData>): react_jsx_runtime3.JSX.Element;
|
|
1420
1419
|
//#endregion
|
|
1421
1420
|
//#region src/components/data-table/data-table-toolbar.d.ts
|
|
1422
1421
|
interface DataTableToolbarProps<TData> extends React$1.ComponentProps<"div"> {
|
|
@@ -1427,7 +1426,7 @@ declare function DataTableToolbar<TData>({
|
|
|
1427
1426
|
children,
|
|
1428
1427
|
className,
|
|
1429
1428
|
...props
|
|
1430
|
-
}: DataTableToolbarProps<TData>):
|
|
1429
|
+
}: DataTableToolbarProps<TData>): react_jsx_runtime3.JSX.Element;
|
|
1431
1430
|
//#endregion
|
|
1432
1431
|
//#region src/components/data-table/data-table-view-options.d.ts
|
|
1433
1432
|
interface DataTableViewOptionsProps<TData> extends React$1.ComponentProps<typeof PopoverContent> {
|
|
@@ -1438,7 +1437,7 @@ declare function DataTableViewOptions<TData>({
|
|
|
1438
1437
|
table,
|
|
1439
1438
|
disabled,
|
|
1440
1439
|
...props
|
|
1441
|
-
}: DataTableViewOptionsProps<TData>):
|
|
1440
|
+
}: DataTableViewOptionsProps<TData>): react_jsx_runtime3.JSX.Element;
|
|
1442
1441
|
//#endregion
|
|
1443
1442
|
//#region src/components/ui/multi-combobox.d.ts
|
|
1444
1443
|
interface MultiComboboxOption {
|
|
@@ -1524,7 +1523,7 @@ declare function createFormSchema<T extends z.ZodObject<z.ZodRawShape>>(schema:
|
|
|
1524
1523
|
/**
|
|
1525
1524
|
* 为编辑模式创建 Form Schema(排除 id, createdAt, updatedAt)
|
|
1526
1525
|
*/
|
|
1527
|
-
declare function createEditFormSchema<T extends z.ZodObject<z.ZodRawShape>>(schema: T, options?: Omit<CreateFormSchemaOptions,
|
|
1526
|
+
declare function createEditFormSchema<T extends z.ZodObject<z.ZodRawShape>>(schema: T, options?: Omit<CreateFormSchemaOptions, 'exclude'>): ISchema;
|
|
1528
1527
|
//#endregion
|
|
1529
1528
|
//#region src/lib/schema-bridge/schema-adapter.d.ts
|
|
1530
1529
|
/**
|
package/dist/index.js
CHANGED
|
@@ -19,6 +19,7 @@ import { createForm, onFieldValueChange } from "@formily/core";
|
|
|
19
19
|
import { Form, JsonSchemaField } from "@pixpilot/formily-shadcn";
|
|
20
20
|
import { connect, mapProps } from "@formily/react";
|
|
21
21
|
import { toast } from "sonner";
|
|
22
|
+
import { keepPreviousData } from "@tanstack/react-query";
|
|
22
23
|
|
|
23
24
|
//#region src/lib/utils.ts
|
|
24
25
|
function cn(...inputs) {
|
|
@@ -5131,12 +5132,8 @@ function getComponentProps(type, parsed) {
|
|
|
5131
5132
|
function createFormSchema(schema, options) {
|
|
5132
5133
|
const shape = schema.shape;
|
|
5133
5134
|
const properties = {};
|
|
5134
|
-
const { overrides, exclude = [], layout = "vertical", gridColumns = 1
|
|
5135
|
+
const { overrides, exclude = [], layout = "vertical", gridColumns = 1 } = options ?? {};
|
|
5135
5136
|
const decoratorProps = { className: "space-y-2" };
|
|
5136
|
-
if (labelAlign) decoratorProps.labelAlign = labelAlign;
|
|
5137
|
-
if (labelWidth) decoratorProps.labelWidth = labelWidth;
|
|
5138
|
-
if (labelCol) decoratorProps.labelCol = labelCol;
|
|
5139
|
-
if (wrapperCol) decoratorProps.wrapperCol = wrapperCol;
|
|
5140
5137
|
for (const [key, fieldSchema] of Object.entries(shape)) {
|
|
5141
5138
|
if (exclude.includes(key)) continue;
|
|
5142
5139
|
const parsed = parseZodField(fieldSchema);
|
|
@@ -5193,65 +5190,59 @@ function createEditFormSchema(schema, options) {
|
|
|
5193
5190
|
//#endregion
|
|
5194
5191
|
//#region src/lib/registries.ts
|
|
5195
5192
|
function createRegistry(label) {
|
|
5196
|
-
const
|
|
5193
|
+
const entries = /* @__PURE__ */ new Map();
|
|
5197
5194
|
const listeners = /* @__PURE__ */ new Set();
|
|
5198
|
-
let notifyQueued = false;
|
|
5199
5195
|
const notify = () => {
|
|
5200
|
-
if (notifyQueued) return;
|
|
5201
|
-
notifyQueued = true;
|
|
5202
5196
|
queueMicrotask(() => {
|
|
5203
|
-
|
|
5204
|
-
listeners.forEach((listener) => listener());
|
|
5197
|
+
for (const listener of listeners) listener();
|
|
5205
5198
|
});
|
|
5206
5199
|
};
|
|
5207
5200
|
return {
|
|
5208
5201
|
register(name, value) {
|
|
5209
|
-
|
|
5210
|
-
if (current === value) return;
|
|
5211
|
-
if (current !== void 0) {
|
|
5202
|
+
if (entries.has(name)) {
|
|
5212
5203
|
console.warn(`[AutoCrud] ${label} "${name}" is already registered.`);
|
|
5213
5204
|
return;
|
|
5214
5205
|
}
|
|
5215
|
-
|
|
5206
|
+
entries.set(name, value);
|
|
5216
5207
|
notify();
|
|
5217
5208
|
},
|
|
5218
5209
|
unregister(name) {
|
|
5219
|
-
if (!
|
|
5210
|
+
if (!entries.delete(name)) return;
|
|
5220
5211
|
notify();
|
|
5221
5212
|
},
|
|
5222
5213
|
get(name) {
|
|
5223
|
-
return
|
|
5214
|
+
return entries.get(name);
|
|
5224
5215
|
},
|
|
5225
5216
|
all() {
|
|
5226
|
-
return Object.fromEntries(
|
|
5217
|
+
return Object.fromEntries(entries);
|
|
5227
5218
|
},
|
|
5228
5219
|
subscribe(listener) {
|
|
5229
5220
|
listeners.add(listener);
|
|
5230
|
-
return () =>
|
|
5231
|
-
listeners.delete(listener);
|
|
5232
|
-
};
|
|
5221
|
+
return () => listeners.delete(listener);
|
|
5233
5222
|
}
|
|
5234
5223
|
};
|
|
5235
5224
|
}
|
|
5236
5225
|
const formComponents = createRegistry("form component");
|
|
5237
5226
|
const dataSources = createRegistry("data source");
|
|
5238
5227
|
function normalizeDataSourceConfig(config) {
|
|
5239
|
-
if (
|
|
5240
|
-
if (typeof config === "string") return {
|
|
5228
|
+
if (typeof config === "string") return config.length > 0 ? {
|
|
5241
5229
|
key: config,
|
|
5242
5230
|
dependsOn: [],
|
|
5243
5231
|
resetOnChange: false
|
|
5244
|
-
};
|
|
5232
|
+
} : void 0;
|
|
5233
|
+
if (!config?.key) return void 0;
|
|
5245
5234
|
return {
|
|
5246
5235
|
key: config.key,
|
|
5247
|
-
dependsOn: config.dependsOn
|
|
5248
|
-
resetOnChange: config.resetOnChange
|
|
5236
|
+
dependsOn: Array.isArray(config.dependsOn) ? config.dependsOn : [],
|
|
5237
|
+
resetOnChange: config.resetOnChange === true
|
|
5249
5238
|
};
|
|
5250
5239
|
}
|
|
5251
5240
|
function normalizeOptions(result) {
|
|
5252
5241
|
const options = Array.isArray(result) ? result : result?.options;
|
|
5253
|
-
if (!options) return [];
|
|
5254
|
-
return options.
|
|
5242
|
+
if (!Array.isArray(options)) return [];
|
|
5243
|
+
return options.filter((option) => {
|
|
5244
|
+
return typeof option === "object" && option !== null && typeof option.value === "string" && typeof option.label === "string";
|
|
5245
|
+
}).map((option) => ({
|
|
5255
5246
|
...option,
|
|
5256
5247
|
value: String(option.value)
|
|
5257
5248
|
}));
|
|
@@ -5305,7 +5296,7 @@ const defaultFieldComponents = {
|
|
|
5305
5296
|
decorator: "FormItem"
|
|
5306
5297
|
}
|
|
5307
5298
|
};
|
|
5308
|
-
function useRegistryVersion(subscribe) {
|
|
5299
|
+
function useRegistryVersion$1(subscribe) {
|
|
5309
5300
|
const [version, setVersion] = useState(0);
|
|
5310
5301
|
useEffect(() => subscribe(() => setVersion((current) => current + 1)), [subscribe]);
|
|
5311
5302
|
return version;
|
|
@@ -5317,13 +5308,15 @@ function isDataSourceConfig(value) {
|
|
|
5317
5308
|
}
|
|
5318
5309
|
function collectDataSourceConfigs(schema, result = {}) {
|
|
5319
5310
|
const properties = schema.properties;
|
|
5320
|
-
if (!properties) return result;
|
|
5311
|
+
if (!properties || typeof properties !== "object") return result;
|
|
5321
5312
|
for (const [key, property] of Object.entries(properties)) {
|
|
5322
|
-
|
|
5323
|
-
const
|
|
5313
|
+
if (!property || typeof property !== "object" || Array.isArray(property)) continue;
|
|
5314
|
+
const record = property;
|
|
5315
|
+
const customDataSource = record["x-data-source"];
|
|
5316
|
+
const schemaDataSource = record.dataSource;
|
|
5324
5317
|
const dataSourceConfig = isDataSourceConfig(customDataSource) ? customDataSource : isDataSourceConfig(schemaDataSource) ? schemaDataSource : void 0;
|
|
5325
5318
|
if (dataSourceConfig) result[key] = dataSourceConfig;
|
|
5326
|
-
collectDataSourceConfigs(
|
|
5319
|
+
collectDataSourceConfigs(record, result);
|
|
5327
5320
|
}
|
|
5328
5321
|
return result;
|
|
5329
5322
|
}
|
|
@@ -5332,7 +5325,7 @@ function useFormDataSources(form, schema) {
|
|
|
5332
5325
|
const sourceEntries = Object.entries(collectDataSourceConfigs(schema)).map(([fieldName, config]) => {
|
|
5333
5326
|
const normalized = normalizeDataSourceConfig(config);
|
|
5334
5327
|
return normalized ? [fieldName, normalized] : void 0;
|
|
5335
|
-
}).filter(
|
|
5328
|
+
}).filter((entry) => entry !== void 0);
|
|
5336
5329
|
if (sourceEntries.length === 0) return;
|
|
5337
5330
|
let active = true;
|
|
5338
5331
|
const effectId = Symbol("autoCrudDataSources");
|
|
@@ -5368,14 +5361,12 @@ function useFormDataSources(form, schema) {
|
|
|
5368
5361
|
};
|
|
5369
5362
|
for (const [fieldName, source] of sourceEntries) loadFieldOptions(fieldName, source);
|
|
5370
5363
|
form.addEffects(effectId, () => {
|
|
5371
|
-
new Set(sourceEntries.flatMap(([, source]) => source.dependsOn))
|
|
5372
|
-
|
|
5373
|
-
|
|
5374
|
-
|
|
5375
|
-
|
|
5376
|
-
|
|
5377
|
-
});
|
|
5378
|
-
});
|
|
5364
|
+
for (const dependency of new Set(sourceEntries.flatMap(([, source]) => source.dependsOn))) onFieldValueChange(dependency, () => {
|
|
5365
|
+
for (const [fieldName, source] of sourceEntries) {
|
|
5366
|
+
if (!source.dependsOn.includes(dependency)) continue;
|
|
5367
|
+
if (source.resetOnChange) form.setValuesIn(fieldName, void 0);
|
|
5368
|
+
loadFieldOptions(fieldName, source);
|
|
5369
|
+
}
|
|
5379
5370
|
});
|
|
5380
5371
|
});
|
|
5381
5372
|
return () => {
|
|
@@ -5384,9 +5375,9 @@ function useFormDataSources(form, schema) {
|
|
|
5384
5375
|
form.removeEffects(effectId);
|
|
5385
5376
|
};
|
|
5386
5377
|
}, [
|
|
5387
|
-
useRegistryVersion(dataSources.subscribe),
|
|
5388
5378
|
form,
|
|
5389
|
-
schema
|
|
5379
|
+
schema,
|
|
5380
|
+
useRegistryVersion$1(dataSources.subscribe)
|
|
5390
5381
|
]);
|
|
5391
5382
|
}
|
|
5392
5383
|
function AutoFormInner({ schema: zodSchema, initialValues, onSubmit, overrides, mode = "create", loading = false, gridColumns = 1, labelAlign = "top", labelWidth, showSubmitButton = true }, ref) {
|
|
@@ -5394,20 +5385,26 @@ function AutoFormInner({ schema: zodSchema, initialValues, onSubmit, overrides,
|
|
|
5394
5385
|
const formSchema = useMemo(() => createEditFormSchema(zodSchema, {
|
|
5395
5386
|
overrides,
|
|
5396
5387
|
layout: "grid",
|
|
5397
|
-
gridColumns
|
|
5398
|
-
labelAlign,
|
|
5399
|
-
labelWidth
|
|
5388
|
+
gridColumns
|
|
5400
5389
|
}), [
|
|
5401
5390
|
zodSchema,
|
|
5402
5391
|
overrides,
|
|
5403
|
-
gridColumns
|
|
5404
|
-
labelAlign,
|
|
5405
|
-
labelWidth
|
|
5392
|
+
gridColumns
|
|
5406
5393
|
]);
|
|
5394
|
+
const formLayout = useMemo(() => {
|
|
5395
|
+
const inlineLabel = labelAlign === "left" || labelAlign === "right";
|
|
5396
|
+
return {
|
|
5397
|
+
labelPlacement: inlineLabel ? "start" : "top",
|
|
5398
|
+
itemProps: inlineLabel ? { label: {
|
|
5399
|
+
className: labelAlign === "right" ? "text-right" : void 0,
|
|
5400
|
+
style: labelWidth != null ? { width: labelWidth } : void 0
|
|
5401
|
+
} } : void 0
|
|
5402
|
+
};
|
|
5403
|
+
}, [labelAlign, labelWidth]);
|
|
5407
5404
|
const fieldComponents = useMemo(() => ({ fields: {
|
|
5408
5405
|
...defaultFieldComponents,
|
|
5409
5406
|
...formComponents.all()
|
|
5410
|
-
} }), [useRegistryVersion(formComponents.subscribe)]);
|
|
5407
|
+
} }), [useRegistryVersion$1(formComponents.subscribe)]);
|
|
5411
5408
|
useFormDataSources(form, formSchema);
|
|
5412
5409
|
const handleSubmit = async () => {
|
|
5413
5410
|
await form.validate();
|
|
@@ -5416,6 +5413,7 @@ function AutoFormInner({ schema: zodSchema, initialValues, onSubmit, overrides,
|
|
|
5416
5413
|
useImperativeHandle(ref, () => ({ submit: handleSubmit }));
|
|
5417
5414
|
return /* @__PURE__ */ jsxs(Form, {
|
|
5418
5415
|
form,
|
|
5416
|
+
layout: formLayout,
|
|
5419
5417
|
children: [/* @__PURE__ */ jsx(JsonSchemaField, {
|
|
5420
5418
|
schema: formSchema,
|
|
5421
5419
|
components: fieldComponents
|
|
@@ -6265,6 +6263,69 @@ function toBatchOptions(options) {
|
|
|
6265
6263
|
value
|
|
6266
6264
|
}));
|
|
6267
6265
|
}
|
|
6266
|
+
function getFilterDataSource(config) {
|
|
6267
|
+
if (config.filter && typeof config.filter === "object") return config.filter.dataSource ?? config.dataSource;
|
|
6268
|
+
return config.dataSource;
|
|
6269
|
+
}
|
|
6270
|
+
function shouldLoadDynamicFilterOptions(config) {
|
|
6271
|
+
if (config.filter === false) return false;
|
|
6272
|
+
if (normalizeFieldOptions(config.enum)) return false;
|
|
6273
|
+
if (config.filter && typeof config.filter === "object") {
|
|
6274
|
+
if (config.filter.enabled === false || config.filter.hidden === true) return false;
|
|
6275
|
+
if (normalizeFieldOptions(config.filter.options)) return false;
|
|
6276
|
+
}
|
|
6277
|
+
return normalizeDataSourceConfig(getFilterDataSource(config)) !== void 0;
|
|
6278
|
+
}
|
|
6279
|
+
function useRegistryVersion(subscribe) {
|
|
6280
|
+
const [version, setVersion] = React.useState(0);
|
|
6281
|
+
React.useEffect(() => subscribe(() => setVersion((current) => current + 1)), [subscribe]);
|
|
6282
|
+
return version;
|
|
6283
|
+
}
|
|
6284
|
+
function useDynamicFilterOptions(fields) {
|
|
6285
|
+
const registryVersion = useRegistryVersion(dataSources.subscribe);
|
|
6286
|
+
const [optionsByField, setOptionsByField] = React.useState({});
|
|
6287
|
+
const sourceEntries = React.useMemo(() => {
|
|
6288
|
+
if (!fields) return [];
|
|
6289
|
+
return Object.entries(fields).flatMap(([field, config]) => {
|
|
6290
|
+
if (!shouldLoadDynamicFilterOptions(config)) return [];
|
|
6291
|
+
const source = normalizeDataSourceConfig(getFilterDataSource(config));
|
|
6292
|
+
return source ? [{
|
|
6293
|
+
field,
|
|
6294
|
+
source
|
|
6295
|
+
}] : [];
|
|
6296
|
+
});
|
|
6297
|
+
}, [fields]);
|
|
6298
|
+
React.useEffect(() => {
|
|
6299
|
+
if (sourceEntries.length === 0) {
|
|
6300
|
+
setOptionsByField({});
|
|
6301
|
+
return;
|
|
6302
|
+
}
|
|
6303
|
+
let active = true;
|
|
6304
|
+
const controller = typeof AbortController === "undefined" ? void 0 : new AbortController();
|
|
6305
|
+
Promise.all(sourceEntries.map(async ({ field, source }) => {
|
|
6306
|
+
const loader = dataSources.get(source.key);
|
|
6307
|
+
if (!loader) return [field, []];
|
|
6308
|
+
try {
|
|
6309
|
+
return [field, normalizeOptions(await loader({
|
|
6310
|
+
field,
|
|
6311
|
+
values: {},
|
|
6312
|
+
signal: controller?.signal
|
|
6313
|
+
}))];
|
|
6314
|
+
} catch (error) {
|
|
6315
|
+
if (!controller?.signal.aborted) console.warn(`[AutoCrud] Failed to load filter data source "${source.key}".`, error);
|
|
6316
|
+
return [field, []];
|
|
6317
|
+
}
|
|
6318
|
+
})).then((entries) => {
|
|
6319
|
+
if (!active) return;
|
|
6320
|
+
setOptionsByField(Object.fromEntries(entries));
|
|
6321
|
+
});
|
|
6322
|
+
return () => {
|
|
6323
|
+
active = false;
|
|
6324
|
+
controller?.abort();
|
|
6325
|
+
};
|
|
6326
|
+
}, [sourceEntries, registryVersion]);
|
|
6327
|
+
return optionsByField;
|
|
6328
|
+
}
|
|
6268
6329
|
function getOptionLabel(value, options) {
|
|
6269
6330
|
const stringValue = String(value);
|
|
6270
6331
|
return options?.find((option) => option.value === stringValue)?.label ?? stringValue;
|
|
@@ -6273,15 +6334,21 @@ function getOptionLabel(value, options) {
|
|
|
6273
6334
|
* 从统一配置生成表格 overrides
|
|
6274
6335
|
* 当 filter 配置存在时,合并到 meta 中(不影响列隐藏)
|
|
6275
6336
|
*/
|
|
6276
|
-
function buildTableOverrides(fields, legacyOverrides) {
|
|
6337
|
+
function buildTableOverrides(fields, legacyOverrides, dynamicFilterOptions) {
|
|
6277
6338
|
const result = { ...legacyOverrides };
|
|
6278
6339
|
if (fields) for (const [key, config] of Object.entries(fields)) {
|
|
6279
|
-
const
|
|
6340
|
+
const fieldOptions = normalizeFieldOptions(config.enum);
|
|
6341
|
+
const tableOptions = toTableOptions(fieldOptions);
|
|
6342
|
+
const dynamicOptions = !fieldOptions ? toTableOptions(normalizeFieldOptions(dynamicFilterOptions?.[key])) : void 0;
|
|
6280
6343
|
const tableMeta = config.table !== false && typeof config.table === "object" ? config.table.meta : void 0;
|
|
6281
6344
|
const fieldEnumMeta = tableOptions ? {
|
|
6282
6345
|
options: tableOptions,
|
|
6283
6346
|
variant: "multiSelect"
|
|
6284
6347
|
} : void 0;
|
|
6348
|
+
const fieldDataSourceMeta = dynamicOptions ? {
|
|
6349
|
+
options: dynamicOptions,
|
|
6350
|
+
variant: "multiSelect"
|
|
6351
|
+
} : void 0;
|
|
6285
6352
|
let filterMeta;
|
|
6286
6353
|
if (config.filter && typeof config.filter === "object") {
|
|
6287
6354
|
if (config.filter.enabled !== false && config.filter.hidden !== true) {
|
|
@@ -6299,11 +6366,12 @@ function buildTableOverrides(fields, legacyOverrides) {
|
|
|
6299
6366
|
enableColumnFilter: false
|
|
6300
6367
|
};
|
|
6301
6368
|
}
|
|
6302
|
-
if (fieldEnumMeta || tableMeta || filterMeta) result[key] = {
|
|
6369
|
+
if (fieldEnumMeta || fieldDataSourceMeta || tableMeta || filterMeta) result[key] = {
|
|
6303
6370
|
...result[key],
|
|
6304
6371
|
meta: {
|
|
6305
6372
|
...result[key]?.meta ?? {},
|
|
6306
6373
|
...fieldEnumMeta ?? {},
|
|
6374
|
+
...fieldDataSourceMeta ?? {},
|
|
6307
6375
|
...tableMeta ?? {},
|
|
6308
6376
|
...filterMeta ?? {}
|
|
6309
6377
|
}
|
|
@@ -6616,6 +6684,7 @@ function AutoCrudTable({ title, description, schema, resource, fields, table: ta
|
|
|
6616
6684
|
const [selectedCount, setSelectedCount] = React.useState(0);
|
|
6617
6685
|
const [exporting, setExporting] = React.useState(false);
|
|
6618
6686
|
const getSelectedRowsRef = React.useRef(null);
|
|
6687
|
+
const dynamicFilterOptions = useDynamicFilterOptions(resolvedFields);
|
|
6619
6688
|
const importColumns = React.useMemo(() => {
|
|
6620
6689
|
const shape = resolvedSchema.shape;
|
|
6621
6690
|
const denySet = new Set(denyFields ?? []);
|
|
@@ -6652,7 +6721,11 @@ function AutoCrudTable({ title, description, schema, resource, fields, table: ta
|
|
|
6652
6721
|
setExporting(false);
|
|
6653
6722
|
}
|
|
6654
6723
|
}, [selectedCount, handleExport]);
|
|
6655
|
-
const tableOverrides = React.useMemo(() => buildTableOverrides(resolvedFields, tableConfig?.overrides), [
|
|
6724
|
+
const tableOverrides = React.useMemo(() => buildTableOverrides(resolvedFields, tableConfig?.overrides, dynamicFilterOptions), [
|
|
6725
|
+
resolvedFields,
|
|
6726
|
+
tableConfig?.overrides,
|
|
6727
|
+
dynamicFilterOptions
|
|
6728
|
+
]);
|
|
6656
6729
|
const formOverrides = React.useMemo(() => buildFormOverrides(resolvedFields, formConfig?.overrides, denyFields), [
|
|
6657
6730
|
resolvedFields,
|
|
6658
6731
|
formConfig?.overrides,
|
|
@@ -7449,13 +7522,6 @@ function DataTableToolbarFilter({ column }) {
|
|
|
7449
7522
|
}
|
|
7450
7523
|
}
|
|
7451
7524
|
|
|
7452
|
-
//#endregion
|
|
7453
|
-
//#region ../../node_modules/.pnpm/@tanstack+query-core@5.90.20/node_modules/@tanstack/query-core/build/modern/utils.js
|
|
7454
|
-
var isServer = typeof window === "undefined" || "Deno" in globalThis;
|
|
7455
|
-
function keepPreviousData(previousData) {
|
|
7456
|
-
return previousData;
|
|
7457
|
-
}
|
|
7458
|
-
|
|
7459
7525
|
//#endregion
|
|
7460
7526
|
//#region src/hooks/use-auto-crud-resource.ts
|
|
7461
7527
|
/** 默认 toast 适配器(使用 sonner) */
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@wordrhyme/auto-crud",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "1.1
|
|
4
|
+
"version": "1.2.1",
|
|
5
5
|
"description": "Schema-first CRUD components with auto-generated tables and forms",
|
|
6
6
|
"author": "wordrhyme",
|
|
7
7
|
"license": "MIT",
|
|
@@ -27,6 +27,7 @@
|
|
|
27
27
|
"access": "public"
|
|
28
28
|
},
|
|
29
29
|
"peerDependencies": {
|
|
30
|
+
"@tanstack/react-query": "^5.0.0",
|
|
30
31
|
"react": "^18.0.0 || ^19.0.0",
|
|
31
32
|
"react-dom": "^18.0.0 || ^19.0.0",
|
|
32
33
|
"sonner": "^2.0.0",
|
|
@@ -71,6 +72,7 @@
|
|
|
71
72
|
},
|
|
72
73
|
"devDependencies": {
|
|
73
74
|
"@tanstack/react-query": "^5.90.15",
|
|
75
|
+
"@testing-library/jest-dom": "^6.9.1",
|
|
74
76
|
"@testing-library/react": "^16.3.2",
|
|
75
77
|
"@trpc/server": "^11.0.0",
|
|
76
78
|
"@types/node": "^22.19.17",
|
|
@@ -78,15 +80,17 @@
|
|
|
78
80
|
"@types/react-dom": "^19.2.3",
|
|
79
81
|
"drizzle-orm": "1.0.0-beta.15-859cf75",
|
|
80
82
|
"eslint": "^9.39.4",
|
|
83
|
+
"jsdom": "^27.4.0",
|
|
81
84
|
"react": "19.2.4",
|
|
82
85
|
"react-dom": "19.2.4",
|
|
83
86
|
"tsdown": "^0.15.12",
|
|
84
87
|
"typescript": "^5.9.3",
|
|
85
|
-
"
|
|
86
|
-
"@internal/prettier-config": "0.0.1",
|
|
88
|
+
"vitest": "^3.2.4",
|
|
87
89
|
"@internal/tsconfig": "0.1.0",
|
|
88
|
-
"@internal/
|
|
89
|
-
"@internal/vitest-config": "0.1.0"
|
|
90
|
+
"@internal/prettier-config": "0.0.1",
|
|
91
|
+
"@internal/vitest-config": "0.1.0",
|
|
92
|
+
"@internal/eslint-config": "0.3.0",
|
|
93
|
+
"@internal/tsdown-config": "0.1.0"
|
|
90
94
|
},
|
|
91
95
|
"prettier": "@internal/prettier-config",
|
|
92
96
|
"scripts": {
|