@wordrhyme/auto-crud 1.1.2 → 1.1.4

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
@@ -59,6 +59,8 @@ let __pixpilot_formily_shadcn = require("@pixpilot/formily-shadcn");
59
59
  __pixpilot_formily_shadcn = __toESM(__pixpilot_formily_shadcn);
60
60
  let __formily_react = require("@formily/react");
61
61
  __formily_react = __toESM(__formily_react);
62
+ let __pixpilot_shadcn_ui = require("@pixpilot/shadcn-ui");
63
+ __pixpilot_shadcn_ui = __toESM(__pixpilot_shadcn_ui);
62
64
  let sonner = require("sonner");
63
65
  sonner = __toESM(sonner);
64
66
 
@@ -2705,8 +2707,17 @@ function DataTableViewOptions({ table, disabled,...props }) {
2705
2707
 
2706
2708
  //#endregion
2707
2709
  //#region src/components/auto-crud/auto-table-simple-filters.tsx
2708
- function AutoTableSimpleFilters({ table, shallow = false, filters: externalFilters, onFiltersChange }) {
2709
- const columns = react.useMemo(() => table.getAllColumns().filter((col) => col.getCanFilter()), [table]);
2710
+ function AutoTableSimpleFilters({ table, shallow = false, filters: externalFilters, onFiltersChange, leading }) {
2711
+ const columns = react.useMemo(() => table.getAllColumns().filter((col) => col.getCanFilter()).sort((a, b) => {
2712
+ const readOrder = (column) => {
2713
+ const metaIndex = column.columnDef.meta?.index;
2714
+ const columnIndex = column.columnDef.index;
2715
+ return typeof metaIndex === "number" ? metaIndex : typeof columnIndex === "number" ? columnIndex : 1e4;
2716
+ };
2717
+ const orderDiff = readOrder(a) - readOrder(b);
2718
+ if (orderDiff !== 0) return orderDiff;
2719
+ return a.getIndex() - b.getIndex();
2720
+ }), [table]);
2710
2721
  const queryStateOptions = table.options.meta?.queryStateOptions;
2711
2722
  const [queryFilters, setQueryFilters] = useReadableFilters(columns, {
2712
2723
  ...queryStateOptions,
@@ -2763,32 +2774,65 @@ function AutoTableSimpleFilters({ table, shallow = false, filters: externalFilte
2763
2774
  const [expanded, setExpanded] = react.useState(false);
2764
2775
  const [visibleCount, setVisibleCount] = react.useState(null);
2765
2776
  const containerRef = react.useRef(null);
2777
+ const hasLeading = leading != null;
2778
+ const filterMeasureKey = react.useMemo(() => filters.map((filter) => [
2779
+ filter.id,
2780
+ filter.operator,
2781
+ Array.isArray(filter.value) ? filter.value.join(",") : String(filter.value ?? "")
2782
+ ].join(":")).join("|"), [filters]);
2766
2783
  const calcVisibleCount = react.useCallback(() => {
2767
2784
  const el = containerRef.current;
2768
2785
  if (!el) return;
2769
- const items = el.querySelectorAll("[data-filter-item]");
2770
- if (items.length === 0) return;
2786
+ const items = Array.from(el.querySelectorAll("[data-filter-item]"));
2787
+ if (items.length === 0) {
2788
+ setVisibleCount(null);
2789
+ return;
2790
+ }
2771
2791
  const restoreList = [];
2772
- el.querySelectorAll(".hidden").forEach((node) => {
2773
- const htmlNode = node;
2774
- htmlNode.classList.remove("hidden");
2775
- restoreList.push(htmlNode);
2792
+ el.querySelectorAll("[data-filter-item].hidden, [data-filter-toggle].hidden").forEach((node) => {
2793
+ node.classList.remove("hidden");
2794
+ restoreList.push(node);
2776
2795
  });
2777
- el.style.flex = "1 1 0%";
2778
- el.offsetHeight;
2779
- const firstTop = items[0].offsetTop;
2796
+ const availableWidth = el.getBoundingClientRect().width;
2797
+ const previousFlex = el.style.flex;
2798
+ const previousWidth = el.style.width;
2799
+ el.style.flex = `0 0 ${availableWidth}px`;
2800
+ el.style.width = `${availableWidth}px`;
2801
+ el.getBoundingClientRect();
2802
+ const computedStyle = window.getComputedStyle(el);
2803
+ const gap = Number.parseFloat(computedStyle.columnGap) || Number.parseFloat(computedStyle.gap) || 0;
2804
+ const leadingEl = el.querySelector("[data-filter-leading]");
2805
+ const toggle = el.querySelector("[data-filter-toggle]");
2806
+ const reset = el.querySelector("[data-filter-reset]");
2807
+ const addWidth = (current, next) => {
2808
+ if (next <= 0) return current;
2809
+ if (current === 0) return next;
2810
+ return current + gap + next;
2811
+ };
2812
+ const leadingWidth = leadingEl?.offsetWidth ?? 0;
2813
+ const itemWidths = items.map((item) => item.offsetWidth);
2814
+ const toggleWidth = toggle?.offsetWidth ?? 0;
2815
+ const resetWidth = reset?.offsetWidth ?? 0;
2816
+ if (itemWidths.reduce((width, itemWidth) => addWidth(width, itemWidth), addWidth(leadingWidth, resetWidth)) <= availableWidth) {
2817
+ el.style.flex = previousFlex;
2818
+ el.style.width = previousWidth;
2819
+ restoreList.forEach((node) => node.classList.add("hidden"));
2820
+ setVisibleCount(null);
2821
+ return;
2822
+ }
2823
+ const trailingWidth = addWidth(addWidth(0, toggleWidth), resetWidth);
2824
+ let usedWidth = leadingWidth;
2780
2825
  let count = 0;
2781
- for (const item of Array.from(items)) {
2782
- if (item.offsetTop > firstTop) break;
2826
+ for (const itemWidth of itemWidths) {
2827
+ const nextWidth = addWidth(usedWidth, itemWidth);
2828
+ if (addWidth(nextWidth, trailingWidth) > availableWidth) break;
2829
+ usedWidth = nextWidth;
2783
2830
  count++;
2784
2831
  }
2785
- const btn = el.querySelector("[data-filter-toggle]");
2786
- if (btn && count < items.length && count > 0) {
2787
- if (btn.offsetTop > firstTop) count--;
2788
- }
2789
- el.style.flex = "";
2832
+ el.style.flex = previousFlex;
2833
+ el.style.width = previousWidth;
2790
2834
  restoreList.forEach((node) => node.classList.add("hidden"));
2791
- setVisibleCount(count >= items.length ? null : count);
2835
+ setVisibleCount(count);
2792
2836
  }, []);
2793
2837
  react.useLayoutEffect(() => {
2794
2838
  if (!mounted) return;
@@ -2796,6 +2840,9 @@ function AutoTableSimpleFilters({ table, shallow = false, filters: externalFilte
2796
2840
  }, [
2797
2841
  mounted,
2798
2842
  filterColumns.length,
2843
+ hasLeading,
2844
+ hasFilters,
2845
+ filterMeasureKey,
2799
2846
  calcVisibleCount
2800
2847
  ]);
2801
2848
  react.useEffect(() => {
@@ -2818,13 +2865,19 @@ function AutoTableSimpleFilters({ table, shallow = false, filters: externalFilte
2818
2865
  }, [
2819
2866
  mounted,
2820
2867
  filterColumns.length,
2868
+ hasLeading,
2821
2869
  calcVisibleCount
2822
2870
  ]);
2823
2871
  if (!mounted) return null;
2824
2872
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
2825
2873
  ref: containerRef,
2826
- className: "flex flex-wrap items-center gap-2 min-w-0",
2874
+ className: "flex flex-1 flex-wrap items-center gap-2 min-w-0",
2827
2875
  children: [
2876
+ leading != null ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
2877
+ className: "shrink-0",
2878
+ "data-filter-leading": true,
2879
+ children: leading
2880
+ }) : null,
2828
2881
  filterColumns.map((column, index) => {
2829
2882
  const meta = column.columnDef.meta;
2830
2883
  const value = getFilterValue(column.id);
@@ -2888,6 +2941,7 @@ function AutoTableSimpleFilters({ table, shallow = false, filters: externalFilte
2888
2941
  children: expanded ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.ChevronUp, { className: "size-4" }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.ChevronDown, { className: "size-4" })
2889
2942
  }),
2890
2943
  hasFilters && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(__pixpilot_shadcn.Button, {
2944
+ "data-filter-reset": true,
2891
2945
  variant: "outline",
2892
2946
  size: "sm",
2893
2947
  className: "h-8 border-dashed shrink-0",
@@ -3957,7 +4011,7 @@ function createTableSchema(schema, options) {
3957
4011
  if (cached) return [...cached];
3958
4012
  }
3959
4013
  const shape = schema.shape;
3960
- const columns = [];
4014
+ const columnEntries = [];
3961
4015
  for (const [key, fieldSchema] of Object.entries(shape)) {
3962
4016
  if (exclude.includes(key)) continue;
3963
4017
  const override = overrides?.[key];
@@ -3978,20 +4032,31 @@ function createTableSchema(schema, options) {
3978
4032
  options: override?.meta?.options ?? autoOptions
3979
4033
  };
3980
4034
  const { meta: _ignoredMeta,...restOverride } = override ?? {};
3981
- columns.push({
3982
- id: key,
3983
- accessorKey: key,
3984
- header: ({ column }) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(DataTableColumnHeader, {
3985
- column,
3986
- label
3987
- }),
3988
- cell: ({ row }) => renderCell(row.getValue(key), parsed.type),
3989
- enableColumnFilter: true,
3990
- enableSorting: true,
3991
- meta,
3992
- ...restOverride
4035
+ const sourceIndex = columnEntries.length;
4036
+ const order = typeof restOverride.index === "number" ? restOverride.index : typeof meta.index === "number" ? meta.index : 1e4 + sourceIndex;
4037
+ columnEntries.push({
4038
+ order,
4039
+ sourceIndex,
4040
+ column: {
4041
+ id: key,
4042
+ accessorKey: key,
4043
+ header: ({ column }) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(DataTableColumnHeader, {
4044
+ column,
4045
+ label
4046
+ }),
4047
+ cell: ({ row }) => renderCell(row.getValue(key), parsed.type),
4048
+ enableColumnFilter: true,
4049
+ enableSorting: true,
4050
+ meta,
4051
+ ...restOverride
4052
+ }
3993
4053
  });
3994
4054
  }
4055
+ const columns = columnEntries.sort((a, b) => {
4056
+ const orderDiff = a.order - b.order;
4057
+ if (orderDiff !== 0) return orderDiff;
4058
+ return a.sourceIndex - b.sourceIndex;
4059
+ }).map((entry) => entry.column);
3995
4060
  if (canCache) baseTableSchemaCache.set(schema, columns);
3996
4061
  return columns;
3997
4062
  }
@@ -4663,10 +4728,19 @@ const filterModeConfig = {
4663
4728
  tooltip: "Linear-style command filters"
4664
4729
  }
4665
4730
  };
4666
- function AutoTable({ schema, data, pageCount = 1, overrides, enableRowSelection = true, exclude, actions, pinnedColumns, filterMode = "simple", onDeleteSelected, onUpdateSelected, batchUpdateFields, actionBarExtra, actionBarActions, initialSorting, enableExport = true, showDefaultExport = true, onSelectedCountChange, getSelectedRows }) {
4731
+ function AutoTable({ schema, data, pageCount = 1, overrides, enableRowSelection = true, exclude, actions, pinnedColumns, filterMode = "simple", search = false, onDeleteSelected, onUpdateSelected, batchUpdateFields, actionBarExtra, actionBarActions, initialSorting, enableExport = true, showDefaultExport = true, onSelectedCountChange, getSelectedRows }) {
4667
4732
  const modes = filterMode ? Array.isArray(filterMode) ? filterMode : [filterMode] : DEFAULT_MODES;
4668
4733
  const [currentMode, setCurrentMode] = useQueryState("filterMode", parseAsStringEnum(modes).withDefault(modes[0] ?? "simple"));
4669
4734
  const showToggle = modes.length > 1;
4735
+ const [searchValue, setSearchValue] = useUrlState("search", parseAsString.withDefault(""), {
4736
+ shallow: false,
4737
+ clearOnDefault: true
4738
+ });
4739
+ const [, setSearchPage] = useUrlState("page", parseAsInteger.withDefault(1), {
4740
+ shallow: false,
4741
+ clearOnDefault: true
4742
+ });
4743
+ const searchConfig = search && typeof search === "object" ? search : {};
4670
4744
  const { table, shallow, debounceMs, throttleMs } = useDataTable({
4671
4745
  data,
4672
4746
  columns: (0, react.useMemo)(() => {
@@ -4738,11 +4812,21 @@ function AutoTable({ schema, data, pageCount = 1, overrides, enableRowSelection
4738
4812
  })
4739
4813
  })
4740
4814
  })] }) : null;
4815
+ const searchInput = search ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__pixpilot_shadcn.Input, {
4816
+ value: searchValue,
4817
+ onChange: (event) => {
4818
+ setSearchValue(event.target.value);
4819
+ setSearchPage(1);
4820
+ },
4821
+ placeholder: searchConfig.placeholder ?? "搜索",
4822
+ className: "h-8 w-48 shrink-0"
4823
+ }) : null;
4741
4824
  const filtersContent = (0, react.useMemo)(() => {
4742
4825
  switch (currentMode) {
4743
4826
  case "simple": return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(AutoTableSimpleFilters, {
4744
4827
  table,
4745
- shallow
4828
+ shallow,
4829
+ leading: searchInput
4746
4830
  });
4747
4831
  case "command": return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(DataTableFilterMenu, {
4748
4832
  table,
@@ -4762,6 +4846,7 @@ function AutoTable({ schema, data, pageCount = 1, overrides, enableRowSelection
4762
4846
  currentMode,
4763
4847
  table,
4764
4848
  shallow,
4849
+ searchInput,
4765
4850
  debounceMs,
4766
4851
  throttleMs
4767
4852
  ]);
@@ -4771,12 +4856,16 @@ function AutoTable({ schema, data, pageCount = 1, overrides, enableRowSelection
4771
4856
  /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
4772
4857
  className: "flex w-full items-start justify-between gap-2 p-1",
4773
4858
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
4774
- className: "flex flex-1 items-start gap-2 min-h-[40px]",
4859
+ className: "flex min-h-[40px] min-w-0 flex-1 items-start gap-2",
4775
4860
  "data-filter-parent": true,
4776
- children: [currentMode !== "simple" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(DataTableSortList, {
4777
- table,
4778
- align: "start"
4779
- }), filtersContent]
4861
+ children: [
4862
+ currentMode !== "simple" && searchInput,
4863
+ currentMode !== "simple" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(DataTableSortList, {
4864
+ table,
4865
+ align: "start"
4866
+ }),
4867
+ filtersContent
4868
+ ]
4780
4869
  }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
4781
4870
  className: "flex items-center gap-2",
4782
4871
  children: [
@@ -4965,10 +5054,44 @@ function createEditFormSchema(schema, options) {
4965
5054
 
4966
5055
  //#endregion
4967
5056
  //#region src/components/auto-crud/auto-form.tsx
5057
+ const COMBOBOX_LIST_CLASS = "[&_[cmdk-list]]:max-h-[360px]";
4968
5058
  const FormilySwitch = (0, __formily_react.connect)(__pixpilot_shadcn.Switch, (0, __formily_react.mapProps)({
4969
5059
  value: "checked",
4970
5060
  onInput: "onCheckedChange"
4971
5061
  }));
5062
+ const FormilyCombobox = (0, __formily_react.connect)(AutoCrudCombobox, (0, __formily_react.mapProps)({ dataSource: "options" }));
5063
+ function AutoCrudCombobox({ options = [], filter, className,...props }) {
5064
+ const resolvedFilter = (0, react.useMemo)(() => {
5065
+ if (typeof filter === "function") return filter;
5066
+ return createComboboxFilter(options);
5067
+ }, [filter, options]);
5068
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__pixpilot_shadcn_ui.Combobox, {
5069
+ ...props,
5070
+ options,
5071
+ filter: resolvedFilter,
5072
+ className: (0, __pixpilot_shadcn.cn)(COMBOBOX_LIST_CLASS, className)
5073
+ });
5074
+ }
5075
+ function createComboboxFilter(options) {
5076
+ const searchTextByValue = new Map(options.map((option) => [option.value, collectSearchText(option.value, option.label, option.searchText, option.keywords).toLocaleLowerCase()]));
5077
+ return (value, search, keywords) => {
5078
+ const query = search.trim().toLocaleLowerCase();
5079
+ if (!query) return 1;
5080
+ const searchText = collectSearchText(searchTextByValue.get(value), keywords).toLocaleLowerCase();
5081
+ if (searchText === query) return 2;
5082
+ return searchText.includes(query) ? 1 : 0;
5083
+ };
5084
+ }
5085
+ function collectSearchText(...parts) {
5086
+ const texts = [];
5087
+ for (const part of parts) {
5088
+ if (!part) continue;
5089
+ if (Array.isArray(part)) texts.push(...part.map(String));
5090
+ else if (typeof part === "object") texts.push(...Object.values(part).filter(Boolean).map(String));
5091
+ else texts.push(String(part));
5092
+ }
5093
+ return texts.join(" ");
5094
+ }
4972
5095
  function AutoFormInner({ schema: zodSchema, initialValues, onSubmit, overrides, mode = "create", loading = false, gridColumns = 1, labelAlign = "top", labelWidth, showSubmitButton = true }, ref) {
4973
5096
  const form = (0, react.useMemo)(() => (0, __formily_core.createForm)({ initialValues }), [JSON.stringify(initialValues)]);
4974
5097
  const formSchema = (0, react.useMemo)(() => createEditFormSchema(zodSchema, {
@@ -4993,10 +5116,16 @@ function AutoFormInner({ schema: zodSchema, initialValues, onSubmit, overrides,
4993
5116
  form,
4994
5117
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(__pixpilot_formily_shadcn.JsonSchemaField, {
4995
5118
  schema: formSchema,
4996
- components: { fields: { Switch: {
4997
- component: FormilySwitch,
4998
- decorator: "FormItem"
4999
- } } }
5119
+ components: { fields: {
5120
+ Combobox: {
5121
+ component: FormilyCombobox,
5122
+ decorator: "FormItem"
5123
+ },
5124
+ Switch: {
5125
+ component: FormilySwitch,
5126
+ decorator: "FormItem"
5127
+ }
5128
+ } }
5000
5129
  }), showSubmitButton && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
5001
5130
  className: "flex justify-end gap-2 mt-6",
5002
5131
  children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__pixpilot_shadcn.Button, {
@@ -6088,6 +6217,11 @@ function resolveActions(actionsOrFn, defaults, rowActionsLocale) {
6088
6217
  */
6089
6218
  function AutoCrudTable({ title, description, schema, resource, fields, table: tableConfig, form: formConfig, permissions, actions: actionItems, toolbarActions, locale: localeProp, onCreate }) {
6090
6219
  const locale = resolveLocale(localeProp);
6220
+ const resolvedSchema = resource.schema ?? schema;
6221
+ const resolvedFields = react.useMemo(() => ({
6222
+ ...fields,
6223
+ ...resource.fields
6224
+ }), [fields, resource.fields]);
6091
6225
  const can = {
6092
6226
  create: permissions?.can?.create ?? true,
6093
6227
  update: permissions?.can?.update ?? true,
@@ -6103,10 +6237,10 @@ function AutoCrudTable({ title, description, schema, resource, fields, table: ta
6103
6237
  const [exporting, setExporting] = react.useState(false);
6104
6238
  const getSelectedRowsRef = react.useRef(null);
6105
6239
  const importColumns = react.useMemo(() => {
6106
- const shape = schema.shape;
6240
+ const shape = resolvedSchema.shape;
6107
6241
  const denySet = new Set(denyFields ?? []);
6108
6242
  return Object.keys(shape).filter((key) => !denySet.has(key));
6109
- }, [schema, denyFields]);
6243
+ }, [resolvedSchema, denyFields]);
6110
6244
  const handleExport = react.useCallback(async (mode) => {
6111
6245
  const filename = title ?? "export";
6112
6246
  const excludeColumns = denyFields;
@@ -6138,21 +6272,34 @@ function AutoCrudTable({ title, description, schema, resource, fields, table: ta
6138
6272
  setExporting(false);
6139
6273
  }
6140
6274
  }, [selectedCount, handleExport]);
6141
- const tableOverrides = react.useMemo(() => buildTableOverrides(fields, tableConfig?.overrides), [fields, tableConfig?.overrides]);
6142
- const formOverrides = react.useMemo(() => buildFormOverrides(fields, formConfig?.overrides, denyFields), [
6143
- fields,
6275
+ const tableOverrides = react.useMemo(() => buildTableOverrides(resolvedFields, tableConfig?.overrides), [resolvedFields, tableConfig?.overrides]);
6276
+ const formOverrides = react.useMemo(() => buildFormOverrides(resolvedFields, formConfig?.overrides, denyFields), [
6277
+ resolvedFields,
6144
6278
  formConfig?.overrides,
6145
6279
  denyFields
6146
6280
  ]);
6147
- const hiddenColumns = react.useMemo(() => buildHiddenColumns(fields, tableConfig?.hidden, denyFields), [
6148
- fields,
6281
+ const hiddenColumns = react.useMemo(() => buildHiddenColumns(resolvedFields, tableConfig?.hidden, denyFields), [
6282
+ resolvedFields,
6149
6283
  tableConfig?.hidden,
6150
6284
  denyFields
6151
6285
  ]);
6152
- const batchFields = react.useMemo(() => buildBatchUpdateFields(schema, tableConfig?.batchFields, fields), [
6153
- schema,
6286
+ const searchConfig = react.useMemo(() => {
6287
+ const tableSearch = tableConfig?.search;
6288
+ if (tableSearch === false) return false;
6289
+ if (tableSearch && typeof tableSearch === "object") return tableSearch;
6290
+ return Object.values(resolvedFields).some((field) => field?.search === true) ? true : false;
6291
+ }, [resolvedFields, tableConfig?.search]);
6292
+ const batchFields = react.useMemo(() => buildBatchUpdateFields(resolvedSchema, tableConfig?.batchFields, resolvedFields), [
6293
+ resolvedSchema,
6154
6294
  tableConfig?.batchFields,
6155
- fields
6295
+ resolvedFields
6296
+ ]);
6297
+ const formInitialValues = react.useMemo(() => {
6298
+ return resource.modal.createOpen ? resource.modal.copySource ?? void 0 : resource.modal.selected ?? void 0;
6299
+ }, [
6300
+ resource.modal.createOpen,
6301
+ resource.modal.copySource,
6302
+ resource.modal.selected
6156
6303
  ]);
6157
6304
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
6158
6305
  className: "space-y-4",
@@ -6233,11 +6380,12 @@ function AutoCrudTable({ title, description, schema, resource, fields, table: ta
6233
6380
  }),
6234
6381
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)(AutoTable, {
6235
6382
  data: resource.tableData.data,
6236
- schema,
6383
+ schema: resolvedSchema,
6237
6384
  pageCount: resource.tableData.pageCount,
6238
6385
  overrides: tableOverrides,
6239
6386
  exclude: hiddenColumns,
6240
6387
  filterMode: tableConfig?.filterModes,
6388
+ search: searchConfig,
6241
6389
  actions: resolveActions(actionItems, {
6242
6390
  openView: resource.handlers.openView,
6243
6391
  openEdit: can.update ? resource.handlers.openEdit : void 0,
@@ -6258,8 +6406,8 @@ function AutoCrudTable({ title, description, schema, resource, fields, table: ta
6258
6406
  open: resource.modal.createOpen || resource.modal.editOpen,
6259
6407
  onOpenChange: (open) => !open && resource.handlers.closeModals(),
6260
6408
  mode: resource.modal.createOpen ? "create" : "edit",
6261
- schema,
6262
- initialValues: resource.modal.createOpen ? resource.modal.copySource ?? void 0 : resource.modal.selected ?? void 0,
6409
+ schema: resolvedSchema,
6410
+ initialValues: formInitialValues,
6263
6411
  onSubmit: (values) => {
6264
6412
  if (resource.modal.createOpen) resource.handlers.submitCreate(values);
6265
6413
  else resource.handlers.submitUpdate(values);
@@ -6276,8 +6424,8 @@ function AutoCrudTable({ title, description, schema, resource, fields, table: ta
6276
6424
  onOpenChange: (open) => !open && resource.handlers.closeModals(),
6277
6425
  variant: resource.modal.variant,
6278
6426
  data: resource.modal.selected,
6279
- schema,
6280
- fields,
6427
+ schema: resolvedSchema,
6428
+ fields: resolvedFields,
6281
6429
  denyFields,
6282
6430
  locale
6283
6431
  }),
@@ -6996,6 +7144,53 @@ const noopToastAdapter = {
6996
7144
  info: () => {},
6997
7145
  warning: () => {}
6998
7146
  };
7147
+ function isRecord(value) {
7148
+ return typeof value === "object" && value !== null && !Array.isArray(value);
7149
+ }
7150
+ function isJsonSchemaObject(value) {
7151
+ return isRecord(value) && isRecord(value.properties);
7152
+ }
7153
+ function readJsonSchemaType(property) {
7154
+ return Array.isArray(property.type) ? property.type.find((item) => item !== "null") : property.type;
7155
+ }
7156
+ function jsonPropertyToZod(property, required) {
7157
+ const enumValues = property.enum?.filter((value) => typeof value === "string");
7158
+ let schema;
7159
+ if (enumValues && enumValues.length > 0) schema = zod.z.enum(enumValues);
7160
+ else switch (readJsonSchemaType(property)) {
7161
+ case "number":
7162
+ case "integer":
7163
+ schema = zod.z.number();
7164
+ break;
7165
+ case "boolean":
7166
+ schema = zod.z.boolean();
7167
+ break;
7168
+ case "array":
7169
+ schema = zod.z.array(zod.z.unknown());
7170
+ break;
7171
+ case "object":
7172
+ schema = zod.z.record(zod.z.string(), zod.z.unknown());
7173
+ break;
7174
+ case "string":
7175
+ schema = property.format === "date" || property.format === "date-time" ? zod.z.coerce.date() : zod.z.string();
7176
+ break;
7177
+ default: schema = zod.z.unknown();
7178
+ }
7179
+ return required ? schema : schema.optional();
7180
+ }
7181
+ function mergeMetadataSchema(baseSchema, metadataSchema) {
7182
+ if (!isJsonSchemaObject(metadataSchema)) return baseSchema;
7183
+ const required = new Set(Array.isArray(metadataSchema.required) ? metadataSchema.required.filter((field) => typeof field === "string") : []);
7184
+ const extensionShape = {};
7185
+ for (const [field, property] of Object.entries(metadataSchema.properties ?? {})) {
7186
+ if (field in baseSchema.shape) continue;
7187
+ extensionShape[field] = jsonPropertyToZod(property, required.has(field));
7188
+ }
7189
+ return Object.keys(extensionShape).length > 0 ? baseSchema.extend(extensionShape) : baseSchema;
7190
+ }
7191
+ function readMetadataFields(fields) {
7192
+ return isRecord(fields) ? fields : void 0;
7193
+ }
6999
7194
  /**
7000
7195
  * Modal 状态 Reducer
7001
7196
  */
@@ -7054,12 +7249,16 @@ function useAutoCrudResource({ router, schema, query: queryTransform, options =
7054
7249
  (0, react.useEffect)(() => {
7055
7250
  hooksRef.current = hooks;
7056
7251
  }, [hooks]);
7057
- const columns = (0, react.useMemo)(() => createTableSchema(schema), [schema]);
7252
+ const metadata = (router.meta?.useQuery(void 0, { staleTime: 3e4 }))?.data;
7253
+ const resolvedSchema = (0, react.useMemo)(() => mergeMetadataSchema(schema, metadata?.schema), [schema, metadata?.schema]);
7254
+ const metadataFields = (0, react.useMemo)(() => readMetadataFields(metadata?.fields), [metadata?.fields]);
7255
+ const columns = (0, react.useMemo)(() => createTableSchema(resolvedSchema), [resolvedSchema]);
7058
7256
  const [urlParams] = useQueryStates({
7059
7257
  page: parseAsInteger.withDefault(1),
7060
7258
  perPage: parseAsInteger.withDefault(10),
7061
7259
  sort: getSortingStateParser().withDefault([]),
7062
- joinOperator: parseAsStringEnum(["and", "or"]).withDefault("and")
7260
+ joinOperator: parseAsStringEnum(["and", "or"]).withDefault("and"),
7261
+ search: parseAsString.withDefault("")
7063
7262
  }, { shallow: false });
7064
7263
  const [filters] = useReadableFilters(columns, { shallow: false });
7065
7264
  const queryInput = (0, react.useMemo)(() => {
@@ -7068,7 +7267,8 @@ function useAutoCrudResource({ router, schema, query: queryTransform, options =
7068
7267
  perPage: urlParams.perPage,
7069
7268
  sort: urlParams.sort,
7070
7269
  filters,
7071
- joinOperator: urlParams.joinOperator
7270
+ joinOperator: urlParams.joinOperator,
7271
+ ...urlParams.search.trim() ? { search: urlParams.search.trim() } : {}
7072
7272
  };
7073
7273
  return queryTransform ? queryTransform(autoParams) : autoParams;
7074
7274
  }, [
@@ -7080,6 +7280,7 @@ function useAutoCrudResource({ router, schema, query: queryTransform, options =
7080
7280
  placeholderData: keepPreviousData,
7081
7281
  staleTime: 0
7082
7282
  });
7283
+ const tableRows = listQuery.data?.data ?? [];
7083
7284
  const createMutation = router.create.useMutation();
7084
7285
  const updateMutation = router.update.useMutation();
7085
7286
  const deleteMutation = router.delete.useMutation();
@@ -7332,11 +7533,13 @@ function useAutoCrudResource({ router, schema, query: queryTransform, options =
7332
7533
  const canExport = !!(exportFetcher || exportQuery);
7333
7534
  return {
7334
7535
  tableData: {
7335
- data: listQuery.data?.data ?? [],
7536
+ data: tableRows,
7336
7537
  pageCount: listQuery.data?.pageCount ?? 0,
7337
7538
  isLoading: listQuery.isLoading,
7338
7539
  isFetching: listQuery.isFetching
7339
7540
  },
7541
+ schema: resolvedSchema,
7542
+ fields: metadataFields,
7340
7543
  modal,
7341
7544
  mutations: {
7342
7545
  isCreating: createMutation.isPending,