@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 +266 -63
- package/dist/index.d.cts +51 -20
- package/dist/index.d.ts +51 -20
- package/dist/index.js +266 -64
- package/package.json +4 -4
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()),
|
|
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)
|
|
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
|
-
|
|
2774
|
-
|
|
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.
|
|
2778
|
-
el.
|
|
2779
|
-
const
|
|
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
|
|
2782
|
-
|
|
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
|
-
|
|
2786
|
-
|
|
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
|
|
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
|
|
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
|
-
|
|
3982
|
-
|
|
3983
|
-
|
|
3984
|
-
|
|
3985
|
-
|
|
3986
|
-
|
|
3987
|
-
|
|
3988
|
-
|
|
3989
|
-
|
|
3990
|
-
|
|
3991
|
-
|
|
3992
|
-
|
|
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
|
|
4859
|
+
className: "flex min-h-[40px] min-w-0 flex-1 items-start gap-2",
|
|
4775
4860
|
"data-filter-parent": true,
|
|
4776
|
-
children: [
|
|
4777
|
-
|
|
4778
|
-
|
|
4779
|
-
|
|
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: {
|
|
4997
|
-
|
|
4998
|
-
|
|
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 =
|
|
6240
|
+
const shape = resolvedSchema.shape;
|
|
6107
6241
|
const denySet = new Set(denyFields ?? []);
|
|
6108
6242
|
return Object.keys(shape).filter((key) => !denySet.has(key));
|
|
6109
|
-
}, [
|
|
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(
|
|
6142
|
-
const formOverrides = react.useMemo(() => buildFormOverrides(
|
|
6143
|
-
|
|
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(
|
|
6148
|
-
|
|
6281
|
+
const hiddenColumns = react.useMemo(() => buildHiddenColumns(resolvedFields, tableConfig?.hidden, denyFields), [
|
|
6282
|
+
resolvedFields,
|
|
6149
6283
|
tableConfig?.hidden,
|
|
6150
6284
|
denyFields
|
|
6151
6285
|
]);
|
|
6152
|
-
const
|
|
6153
|
-
|
|
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
|
-
|
|
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:
|
|
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
|
|
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:
|
|
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,
|