@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 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, labelAlign = "top", labelWidth, labelCol, wrapperCol } = options ?? {};
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 items = /* @__PURE__ */ new Map();
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
- notifyQueued = false;
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
- const current = items.get(name);
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
- items.set(name, value);
5249
+ entries.set(name, value);
5258
5250
  notify();
5259
5251
  },
5260
5252
  unregister(name) {
5261
- if (!items.delete(name)) return;
5253
+ if (!entries.delete(name)) return;
5262
5254
  notify();
5263
5255
  },
5264
5256
  get(name) {
5265
- return items.get(name);
5257
+ return entries.get(name);
5266
5258
  },
5267
5259
  all() {
5268
- return Object.fromEntries(items);
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 (!config) return void 0;
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 ?? false
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.map((option) => ({
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
- const customDataSource = property["x-data-source"];
5365
- const schemaDataSource = property.dataSource;
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(property, result);
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(Boolean);
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)).forEach((dependency) => {
5414
- (0, __formily_core.onFieldValueChange)(dependency, () => {
5415
- sourceEntries.forEach(([fieldName, source]) => {
5416
- if (!source.dependsOn.includes(dependency)) return;
5417
- if (source.resetOnChange) form.setValuesIn(fieldName, void 0);
5418
- loadFieldOptions(fieldName, source);
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 tableOptions = toTableOptions(normalizeFieldOptions(config.enum));
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), [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 AutoCrudDataSourceLoader = (context: AutoCrudDataSourceContext) => Promise<AutoCrudOption[] | {
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, "exclude">): ISchema;
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 react_jsx_runtime2 from "react/jsx-runtime";
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>): react_jsx_runtime2.JSX.Element;
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>): react_jsx_runtime2.JSX.Element;
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 AutoCrudDataSourceLoader = (context: AutoCrudDataSourceContext) => Promise<AutoCrudOption[] | {
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>): react_jsx_runtime2.JSX.Element;
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>): react_jsx_runtime2.JSX.Element;
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>): react_jsx_runtime2.JSX.Element | null;
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>): react_jsx_runtime2.JSX.Element;
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): react_jsx_runtime2.JSX.Element;
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): react_jsx_runtime2.JSX.Element;
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>): react_jsx_runtime2.JSX.Element;
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>): react_jsx_runtime2.JSX.Element;
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>): react_jsx_runtime2.JSX.Element;
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>): react_jsx_runtime2.JSX.Element;
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>): react_jsx_runtime2.JSX.Element;
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>): react_jsx_runtime2.JSX.Element;
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>): react_jsx_runtime2.JSX.Element;
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, "exclude">): ISchema;
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, labelAlign = "top", labelWidth, labelCol, wrapperCol } = options ?? {};
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 items = /* @__PURE__ */ new Map();
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
- notifyQueued = false;
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
- const current = items.get(name);
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
- items.set(name, value);
5206
+ entries.set(name, value);
5216
5207
  notify();
5217
5208
  },
5218
5209
  unregister(name) {
5219
- if (!items.delete(name)) return;
5210
+ if (!entries.delete(name)) return;
5220
5211
  notify();
5221
5212
  },
5222
5213
  get(name) {
5223
- return items.get(name);
5214
+ return entries.get(name);
5224
5215
  },
5225
5216
  all() {
5226
- return Object.fromEntries(items);
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 (!config) return void 0;
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 ?? false
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.map((option) => ({
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
- const customDataSource = property["x-data-source"];
5323
- const schemaDataSource = property.dataSource;
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(property, result);
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(Boolean);
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)).forEach((dependency) => {
5372
- onFieldValueChange(dependency, () => {
5373
- sourceEntries.forEach(([fieldName, source]) => {
5374
- if (!source.dependsOn.includes(dependency)) return;
5375
- if (source.resetOnChange) form.setValuesIn(fieldName, void 0);
5376
- loadFieldOptions(fieldName, source);
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 tableOptions = toTableOptions(normalizeFieldOptions(config.enum));
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), [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.8",
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
- "@internal/eslint-config": "0.3.0",
86
- "@internal/prettier-config": "0.0.1",
88
+ "vitest": "^3.2.4",
87
89
  "@internal/tsconfig": "0.1.0",
88
- "@internal/tsdown-config": "0.1.0",
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": {