@wordrhyme/auto-crud 1.11.2 → 1.12.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +174 -30
- package/dist/index.d.cts +51 -22
- package/dist/index.d.ts +35 -6
- package/dist/index.js +155 -12
- package/package.json +5 -4
package/dist/index.cjs
CHANGED
|
@@ -4641,6 +4641,27 @@ function coerceRowValues(rows, schema) {
|
|
|
4641
4641
|
|
|
4642
4642
|
//#endregion
|
|
4643
4643
|
//#region src/lib/export.ts
|
|
4644
|
+
/** Snapshot rendered business columns without changing the legacy CSV column policy. */
|
|
4645
|
+
function getVisibleExportColumns(table, deny = []) {
|
|
4646
|
+
const excluded = new Set([
|
|
4647
|
+
"select",
|
|
4648
|
+
"actions",
|
|
4649
|
+
...deny
|
|
4650
|
+
]);
|
|
4651
|
+
return [
|
|
4652
|
+
...table.getLeftVisibleLeafColumns(),
|
|
4653
|
+
...table.getCenterVisibleLeafColumns(),
|
|
4654
|
+
...table.getRightVisibleLeafColumns()
|
|
4655
|
+
].filter((column) => {
|
|
4656
|
+
if (excluded.has(column.id)) return false;
|
|
4657
|
+
const meta = column.columnDef.meta;
|
|
4658
|
+
if (meta?.autoCrudExpandColumn) return false;
|
|
4659
|
+
return ![meta?.classNames?.th, meta?.classNames?.td].some((value) => typeof value === "string" && /(?:^|\s)!?hidden!?(?:\s|$)/.test(value));
|
|
4660
|
+
}).map((column) => ({
|
|
4661
|
+
id: column.id,
|
|
4662
|
+
label: column.columnDef.meta?.label ?? (typeof column.columnDef.header === "string" ? column.columnDef.header : column.id)
|
|
4663
|
+
}));
|
|
4664
|
+
}
|
|
4644
4665
|
function getExportColumnIds(table) {
|
|
4645
4666
|
return table.getAllLeafColumns().filter((column) => column.accessorFn).map((column) => column.id);
|
|
4646
4667
|
}
|
|
@@ -5157,7 +5178,7 @@ const filterModeConfig = {
|
|
|
5157
5178
|
tooltip: "Linear-style command filters"
|
|
5158
5179
|
}
|
|
5159
5180
|
};
|
|
5160
|
-
function AutoTable({ schema, data, getRowId, expandedRow, defaultExpanded = false, pageCount = 1, pagination, total, overrides, classNames, enableRowSelection = true, exclude, filterOnly, actions, pinnedColumns, filterMode, search = false, toolbarExtra, locale = enUSTableControls, onDeleteSelected, onUpdateSelected, batchUpdateFields, actionBarExtra, actionBarActions, deleteConfirmation, initialSorting, enableExport = true, showDefaultExport = true, onSelectedCountChange, onSelectedRowsChange, getSelectedRows }) {
|
|
5181
|
+
function AutoTable({ schema, data, getRowId, expandedRow, defaultExpanded = false, pageCount = 1, pagination, total, overrides, classNames, enableRowSelection = true, exclude, filterOnly, actions, pinnedColumns, filterMode, search = false, toolbarExtra, locale = enUSTableControls, onDeleteSelected, onUpdateSelected, batchUpdateFields, actionBarExtra, actionBarActions, deleteConfirmation, initialSorting, enableExport = true, showDefaultExport = true, onSelectedCountChange, onSelectedRowsChange, getSelectedRows, exportTableRef }) {
|
|
5161
5182
|
if (expandedRow && !getRowId) throw new Error("AutoTable requires getRowId when expandedRow is provided.");
|
|
5162
5183
|
const enableExpansion = !!expandedRow;
|
|
5163
5184
|
const modes = filterMode ? Array.isArray(filterMode) ? filterMode : [filterMode] : DEFAULT_MODES;
|
|
@@ -5199,6 +5220,7 @@ function AutoTable({ schema, data, getRowId, expandedRow, defaultExpanded = fals
|
|
|
5199
5220
|
while (expandColumnId$1 in schema.shape || result.some((column) => column.id === expandColumnId$1)) expandColumnId$1 = `_${expandColumnId$1}`;
|
|
5200
5221
|
result.unshift({
|
|
5201
5222
|
id: expandColumnId$1,
|
|
5223
|
+
meta: { autoCrudExpandColumn: true },
|
|
5202
5224
|
header: () => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
5203
5225
|
className: "sr-only",
|
|
5204
5226
|
children: "Row details"
|
|
@@ -5272,6 +5294,13 @@ function AutoTable({ schema, data, getRowId, expandedRow, defaultExpanded = fals
|
|
|
5272
5294
|
};
|
|
5273
5295
|
});
|
|
5274
5296
|
}, [table, filterOnly]);
|
|
5297
|
+
(0, react.useEffect)(() => {
|
|
5298
|
+
if (!exportTableRef) return;
|
|
5299
|
+
exportTableRef.current = table;
|
|
5300
|
+
return () => {
|
|
5301
|
+
exportTableRef.current = null;
|
|
5302
|
+
};
|
|
5303
|
+
}, [table, exportTableRef]);
|
|
5275
5304
|
const rowSelection = table.getState().rowSelection;
|
|
5276
5305
|
const columnFilters = table.getState().columnFilters;
|
|
5277
5306
|
const selectedRows = (0, react.useMemo)(() => table.getFilteredSelectedRowModel().rows.map((row) => row.original), [
|
|
@@ -6830,6 +6859,9 @@ function resolveToolbarActionsWithResolver(targetId, ownerActions, context) {
|
|
|
6830
6859
|
const resolver = autoCrudToolbarResolver;
|
|
6831
6860
|
return targetId !== void 0 && targetId.length > 0 && resolver !== null ? resolver(targetId, ownerActions, context) : ownerActions;
|
|
6832
6861
|
}
|
|
6862
|
+
function reportExportError(error) {
|
|
6863
|
+
sonner.toast.error(error instanceof Error ? error.message : String(error));
|
|
6864
|
+
}
|
|
6833
6865
|
function applyResourceTablePresentation(fields, resourceFields) {
|
|
6834
6866
|
if (!fields || !resourceFields) return fields;
|
|
6835
6867
|
return Object.fromEntries(Object.entries(fields).map(([key, config]) => {
|
|
@@ -7709,7 +7741,7 @@ function resolveActions(actions, defaults, rowActionsLocale, context) {
|
|
|
7709
7741
|
*
|
|
7710
7742
|
* 高级 CRUD 表格组件,封装了完整的增删改查流程
|
|
7711
7743
|
*/
|
|
7712
|
-
function AutoCrudTable({ id, title, description, schema, resource, fields, table: tableConfig, form: formConfig, view: viewConfig, permissions, actions: actionItems, toolbar, toolbarActions, locale: localeProp, onCreate }) {
|
|
7744
|
+
function AutoCrudTable({ id, title, description, schema, resource, fields, table: tableConfig, form: formConfig, view: viewConfig, permissions, actions: actionItems, toolbar, toolbarActions, locale: localeProp, onCreate, onExport }) {
|
|
7713
7745
|
const locale = resolveLocale(localeProp);
|
|
7714
7746
|
const getPagination = react.useCallback(() => id ? tablePagination.get(id) : void 0, [id]);
|
|
7715
7747
|
const registeredPagination = react.useSyncExternalStore(tablePagination.subscribe, getPagination, getPagination);
|
|
@@ -7729,6 +7761,8 @@ function AutoCrudTable({ id, title, description, schema, resource, fields, table
|
|
|
7729
7761
|
const [importOpen, setImportOpen] = react.useState(false);
|
|
7730
7762
|
const [selectedRows, setSelectedRows] = react.useState([]);
|
|
7731
7763
|
const [exporting, setExporting] = react.useState(false);
|
|
7764
|
+
const exportBusyRef = react.useRef(false);
|
|
7765
|
+
const exportTableRef = react.useRef(null);
|
|
7732
7766
|
const getSelectedRowsRef = react.useRef(null);
|
|
7733
7767
|
const dynamicFilterOptions = useDynamicFilterOptions(resolvedFields);
|
|
7734
7768
|
const filterOnly = react.useMemo(() => Object.entries(resolvedFields).filter(([key, config]) => config.table === false && config.filter && config.hidden !== true && !denyFields?.includes(key) && !tableConfig?.hidden?.includes(key) && tableConfig?.overrides?.[key]?.hidden !== true).map(([key]) => key), [
|
|
@@ -7787,6 +7821,26 @@ function AutoCrudTable({ id, title, description, schema, resource, fields, table
|
|
|
7787
7821
|
return Object.keys(shape).filter((key) => !denySet.has(key));
|
|
7788
7822
|
}, [resolvedSchema, denyFields]);
|
|
7789
7823
|
const handleExport = react.useCallback(async (mode) => {
|
|
7824
|
+
if (onExport) {
|
|
7825
|
+
if (exportBusyRef.current || !exportTableRef.current) return;
|
|
7826
|
+
exportBusyRef.current = true;
|
|
7827
|
+
setExporting(true);
|
|
7828
|
+
try {
|
|
7829
|
+
const denied = new Set(denyFields);
|
|
7830
|
+
const columns = getVisibleExportColumns(exportTableRef.current, denyFields);
|
|
7831
|
+
const rows = mode === "selected" ? getSelectedRowsRef.current?.() : await resource.handlers.export?.();
|
|
7832
|
+
if (!rows || mode === "selected" && rows.length === 0) return;
|
|
7833
|
+
await onExport({
|
|
7834
|
+
mode,
|
|
7835
|
+
rows: denied.size ? rows.map((row) => Object.fromEntries(Object.entries(row).filter(([key]) => !denied.has(key)))) : rows,
|
|
7836
|
+
columns
|
|
7837
|
+
});
|
|
7838
|
+
} finally {
|
|
7839
|
+
exportBusyRef.current = false;
|
|
7840
|
+
setExporting(false);
|
|
7841
|
+
}
|
|
7842
|
+
return;
|
|
7843
|
+
}
|
|
7790
7844
|
const filename = title ?? "export";
|
|
7791
7845
|
const excludeColumns = denyFields;
|
|
7792
7846
|
if (mode === "selected") {
|
|
@@ -7807,18 +7861,30 @@ function AutoCrudTable({ id, title, description, schema, resource, fields, table
|
|
|
7807
7861
|
resource.handlers.export,
|
|
7808
7862
|
title,
|
|
7809
7863
|
denyFields,
|
|
7810
|
-
resolvedFields
|
|
7864
|
+
resolvedFields,
|
|
7865
|
+
onExport
|
|
7811
7866
|
]);
|
|
7812
7867
|
const handleExportClick = react.useCallback(async () => {
|
|
7813
7868
|
const mode = selectedCount > 0 ? "selected" : "filtered";
|
|
7869
|
+
if (onExport) return handleExport(mode);
|
|
7814
7870
|
setExporting(true);
|
|
7815
7871
|
try {
|
|
7816
7872
|
await handleExport(mode);
|
|
7817
7873
|
} finally {
|
|
7818
7874
|
setExporting(false);
|
|
7819
7875
|
}
|
|
7820
|
-
}, [
|
|
7876
|
+
}, [
|
|
7877
|
+
selectedCount,
|
|
7878
|
+
handleExport,
|
|
7879
|
+
onExport
|
|
7880
|
+
]);
|
|
7821
7881
|
const exportBatchActions = react.useMemo(() => {
|
|
7882
|
+
if (onExport) return registryBatchActions.map((action$1) => action$1.type === "export" && action$1.onClick === void 0 && !action$1.component ? {
|
|
7883
|
+
...action$1,
|
|
7884
|
+
onClick: () => {
|
|
7885
|
+
handleExport("selected").catch(reportExportError);
|
|
7886
|
+
}
|
|
7887
|
+
} : action$1);
|
|
7822
7888
|
if (!Object.values(resolvedFields).some((field) => field.format)) return registryBatchActions;
|
|
7823
7889
|
return registryBatchActions.map((action$1) => {
|
|
7824
7890
|
if (action$1.type !== "export" || action$1.onClick !== void 0 || Boolean(action$1.component)) return action$1;
|
|
@@ -7836,7 +7902,9 @@ function AutoCrudTable({ id, title, description, schema, resource, fields, table
|
|
|
7836
7902
|
}, [
|
|
7837
7903
|
registryBatchActions,
|
|
7838
7904
|
resolvedFields,
|
|
7839
|
-
denyFields
|
|
7905
|
+
denyFields,
|
|
7906
|
+
onExport,
|
|
7907
|
+
handleExport
|
|
7840
7908
|
]);
|
|
7841
7909
|
const toolbarRefresh = react.useMemo(() => {
|
|
7842
7910
|
const refresh = ownerRefreshAction?.onClick ?? resource.handlers.refresh;
|
|
@@ -8013,7 +8081,9 @@ function AutoCrudTable({ id, title, description, schema, resource, fields, table
|
|
|
8013
8081
|
if (type === "export" && canExport) return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(__wordrhyme_shadcn.Button, {
|
|
8014
8082
|
variant: "outline",
|
|
8015
8083
|
size: "sm",
|
|
8016
|
-
onClick: overrides?.onClick ??
|
|
8084
|
+
onClick: overrides?.onClick ?? (!ownerExportAction?.onClick ? () => {
|
|
8085
|
+
toolbarExportData?.().catch(reportExportError);
|
|
8086
|
+
} : toolbarExportData),
|
|
8017
8087
|
disabled: exporting || selectedCount === 0 && !resource.handlers.export,
|
|
8018
8088
|
children: [exporting ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.Loader2, { className: "mr-2 h-4 w-4 animate-spin" }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.Upload, { className: "mr-2 h-4 w-4" }), overrides?.label ?? (selectedCount > 0 ? locale.toolbar.exportSelected(selectedCount) : locale.toolbar.export)]
|
|
8019
8089
|
}, "export");
|
|
@@ -8092,7 +8162,8 @@ function AutoCrudTable({ id, title, description, schema, resource, fields, table
|
|
|
8092
8162
|
showDefaultExport: false,
|
|
8093
8163
|
initialSorting: defaultSort,
|
|
8094
8164
|
onSelectedRowsChange: handleSelectedRowsChange,
|
|
8095
|
-
getSelectedRows: getSelectedRowsRef
|
|
8165
|
+
getSelectedRows: getSelectedRowsRef,
|
|
8166
|
+
...onExport ? { exportTableRef } : {}
|
|
8096
8167
|
}),
|
|
8097
8168
|
rowActionDialog,
|
|
8098
8169
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(CrudFormModal, {
|
|
@@ -8914,7 +8985,7 @@ function modalReducer(state, action$1) {
|
|
|
8914
8985
|
*/
|
|
8915
8986
|
function useAutoCrudResource({ router, schema, query: queryTransform, fields, options = {} }) {
|
|
8916
8987
|
const { idKey = "id", defaultVariant = "dialog", hooks, toast: toastAdapter, exportFetcher, defaultSort: defaultSortOption } = options;
|
|
8917
|
-
const toast = (0, react.useMemo)(() => toastAdapter === false ? noopToastAdapter : toastAdapter ?? defaultToastAdapter, [toastAdapter]);
|
|
8988
|
+
const toast$1 = (0, react.useMemo)(() => toastAdapter === false ? noopToastAdapter : toastAdapter ?? defaultToastAdapter, [toastAdapter]);
|
|
8918
8989
|
const hooksRef = (0, react.useRef)(hooks);
|
|
8919
8990
|
(0, react.useEffect)(() => {
|
|
8920
8991
|
hooksRef.current = hooks;
|
|
@@ -9006,7 +9077,7 @@ function useAutoCrudResource({ router, schema, query: queryTransform, fields, op
|
|
|
9006
9077
|
const result = await currentHooks.beforeCreate(values);
|
|
9007
9078
|
if (typeof result === "boolean") {
|
|
9008
9079
|
if (result) {
|
|
9009
|
-
toast.success("创建成功");
|
|
9080
|
+
toast$1.success("创建成功");
|
|
9010
9081
|
closeModals();
|
|
9011
9082
|
listQuery.refetch();
|
|
9012
9083
|
currentHooks?.onSuccess?.("create", values);
|
|
@@ -9017,20 +9088,20 @@ function useAutoCrudResource({ router, schema, query: queryTransform, fields, op
|
|
|
9017
9088
|
}
|
|
9018
9089
|
createMutation.mutate(values, {
|
|
9019
9090
|
onSuccess: (data) => {
|
|
9020
|
-
toast.success("创建成功");
|
|
9091
|
+
toast$1.success("创建成功");
|
|
9021
9092
|
closeModals();
|
|
9022
9093
|
listQuery.refetch();
|
|
9023
9094
|
currentHooks?.onSuccess?.("create", data);
|
|
9024
9095
|
},
|
|
9025
9096
|
onError: (error) => {
|
|
9026
9097
|
const err = error;
|
|
9027
|
-
toast.error(`创建失败: ${err.message}`);
|
|
9098
|
+
toast$1.error(`创建失败: ${err.message}`);
|
|
9028
9099
|
currentHooks?.onError?.("create", err);
|
|
9029
9100
|
}
|
|
9030
9101
|
});
|
|
9031
9102
|
} catch (error) {
|
|
9032
9103
|
const err = error;
|
|
9033
|
-
toast.error(`创建失败: ${err.message}`);
|
|
9104
|
+
toast$1.error(`创建失败: ${err.message}`);
|
|
9034
9105
|
currentHooks?.onError?.("create", err);
|
|
9035
9106
|
}
|
|
9036
9107
|
}, [
|
|
@@ -9046,7 +9117,7 @@ function useAutoCrudResource({ router, schema, query: queryTransform, fields, op
|
|
|
9046
9117
|
const result = await currentHooks.beforeUpdate(values, modal.selected);
|
|
9047
9118
|
if (typeof result === "boolean") {
|
|
9048
9119
|
if (result) {
|
|
9049
|
-
toast.success("更新成功");
|
|
9120
|
+
toast$1.success("更新成功");
|
|
9050
9121
|
closeModals();
|
|
9051
9122
|
listQuery.refetch();
|
|
9052
9123
|
currentHooks?.onSuccess?.("update", values);
|
|
@@ -9061,20 +9132,20 @@ function useAutoCrudResource({ router, schema, query: queryTransform, fields, op
|
|
|
9061
9132
|
data: values
|
|
9062
9133
|
}, {
|
|
9063
9134
|
onSuccess: (data) => {
|
|
9064
|
-
toast.success("更新成功");
|
|
9135
|
+
toast$1.success("更新成功");
|
|
9065
9136
|
closeModals();
|
|
9066
9137
|
listQuery.refetch();
|
|
9067
9138
|
currentHooks?.onSuccess?.("update", data);
|
|
9068
9139
|
},
|
|
9069
9140
|
onError: (error) => {
|
|
9070
9141
|
const err = error;
|
|
9071
|
-
toast.error(`更新失败: ${err.message}`);
|
|
9142
|
+
toast$1.error(`更新失败: ${err.message}`);
|
|
9072
9143
|
currentHooks?.onError?.("update", err);
|
|
9073
9144
|
}
|
|
9074
9145
|
});
|
|
9075
9146
|
} catch (error) {
|
|
9076
9147
|
const err = error;
|
|
9077
|
-
toast.error(`更新失败: ${err.message}`);
|
|
9148
|
+
toast$1.error(`更新失败: ${err.message}`);
|
|
9078
9149
|
currentHooks?.onError?.("update", err);
|
|
9079
9150
|
}
|
|
9080
9151
|
}, [
|
|
@@ -9092,7 +9163,7 @@ function useAutoCrudResource({ router, schema, query: queryTransform, fields, op
|
|
|
9092
9163
|
const result = await currentHooks.beforeDelete(modal.selected);
|
|
9093
9164
|
if (typeof result === "boolean") {
|
|
9094
9165
|
if (result) {
|
|
9095
|
-
toast.success("删除成功");
|
|
9166
|
+
toast$1.success("删除成功");
|
|
9096
9167
|
closeModals();
|
|
9097
9168
|
listQuery.refetch();
|
|
9098
9169
|
currentHooks?.onSuccess?.("delete", modal.selected);
|
|
@@ -9103,20 +9174,20 @@ function useAutoCrudResource({ router, schema, query: queryTransform, fields, op
|
|
|
9103
9174
|
const id = modal.selected[idKey];
|
|
9104
9175
|
deleteMutation.mutate(id, {
|
|
9105
9176
|
onSuccess: (data) => {
|
|
9106
|
-
toast.success("删除成功");
|
|
9177
|
+
toast$1.success("删除成功");
|
|
9107
9178
|
closeModals();
|
|
9108
9179
|
listQuery.refetch();
|
|
9109
9180
|
currentHooks?.onSuccess?.("delete", data);
|
|
9110
9181
|
},
|
|
9111
9182
|
onError: (error) => {
|
|
9112
9183
|
const err = error;
|
|
9113
|
-
toast.error(`删除失败: ${err.message}`);
|
|
9184
|
+
toast$1.error(`删除失败: ${err.message}`);
|
|
9114
9185
|
currentHooks?.onError?.("delete", err);
|
|
9115
9186
|
}
|
|
9116
9187
|
});
|
|
9117
9188
|
} catch (error) {
|
|
9118
9189
|
const err = error;
|
|
9119
|
-
toast.error(`删除失败: ${err.message}`);
|
|
9190
|
+
toast$1.error(`删除失败: ${err.message}`);
|
|
9120
9191
|
currentHooks?.onError?.("delete", err);
|
|
9121
9192
|
}
|
|
9122
9193
|
}, [
|
|
@@ -9129,19 +9200,19 @@ function useAutoCrudResource({ router, schema, query: queryTransform, fields, op
|
|
|
9129
9200
|
const deleteMany = (0, react.useCallback)((rows) => {
|
|
9130
9201
|
const currentHooks = hooksRef.current;
|
|
9131
9202
|
if (!deleteManyMutation) {
|
|
9132
|
-
toast.error("批量删除功能未启用");
|
|
9203
|
+
toast$1.error("批量删除功能未启用");
|
|
9133
9204
|
return;
|
|
9134
9205
|
}
|
|
9135
9206
|
const ids = rows.map((row) => row[idKey]);
|
|
9136
9207
|
deleteManyMutation.mutate(ids, {
|
|
9137
9208
|
onSuccess: (data) => {
|
|
9138
|
-
toast.success(`成功删除 ${ids.length} 条记录`);
|
|
9209
|
+
toast$1.success(`成功删除 ${ids.length} 条记录`);
|
|
9139
9210
|
listQuery.refetch();
|
|
9140
9211
|
currentHooks?.onSuccess?.("delete", data);
|
|
9141
9212
|
},
|
|
9142
9213
|
onError: (error) => {
|
|
9143
9214
|
const err = error;
|
|
9144
|
-
toast.error(`批量删除失败: ${err.message}`);
|
|
9215
|
+
toast$1.error(`批量删除失败: ${err.message}`);
|
|
9145
9216
|
currentHooks?.onError?.("delete", err);
|
|
9146
9217
|
}
|
|
9147
9218
|
});
|
|
@@ -9153,7 +9224,7 @@ function useAutoCrudResource({ router, schema, query: queryTransform, fields, op
|
|
|
9153
9224
|
const updateMany = (0, react.useCallback)((rows, data) => {
|
|
9154
9225
|
const currentHooks = hooksRef.current;
|
|
9155
9226
|
if (!updateManyMutation) {
|
|
9156
|
-
toast.error("批量更新功能未启用");
|
|
9227
|
+
toast$1.error("批量更新功能未启用");
|
|
9157
9228
|
return;
|
|
9158
9229
|
}
|
|
9159
9230
|
const ids = rows.map((row) => row[idKey]);
|
|
@@ -9162,13 +9233,13 @@ function useAutoCrudResource({ router, schema, query: queryTransform, fields, op
|
|
|
9162
9233
|
data
|
|
9163
9234
|
}, {
|
|
9164
9235
|
onSuccess: (result) => {
|
|
9165
|
-
toast.success(`成功更新 ${ids.length} 条记录`);
|
|
9236
|
+
toast$1.success(`成功更新 ${ids.length} 条记录`);
|
|
9166
9237
|
listQuery.refetch();
|
|
9167
9238
|
currentHooks?.onSuccess?.("update", result);
|
|
9168
9239
|
},
|
|
9169
9240
|
onError: (error) => {
|
|
9170
9241
|
const err = error;
|
|
9171
|
-
toast.error(`批量更新失败: ${err.message}`);
|
|
9242
|
+
toast$1.error(`批量更新失败: ${err.message}`);
|
|
9172
9243
|
currentHooks?.onError?.("update", err);
|
|
9173
9244
|
}
|
|
9174
9245
|
});
|
|
@@ -9198,12 +9269,18 @@ function useAutoCrudResource({ router, schema, query: queryTransform, fields, op
|
|
|
9198
9269
|
schema
|
|
9199
9270
|
]);
|
|
9200
9271
|
const handleExport = (0, react.useCallback)(async () => {
|
|
9272
|
+
let result;
|
|
9201
9273
|
if (exportFetcher) {
|
|
9202
9274
|
const { page: _p, perPage: _pp,...exportParams } = queryInput ?? {};
|
|
9203
|
-
|
|
9204
|
-
}
|
|
9205
|
-
|
|
9206
|
-
|
|
9275
|
+
result = await exportFetcher(exportParams);
|
|
9276
|
+
} else if (exportQuery) {
|
|
9277
|
+
const response = await exportQuery.refetch();
|
|
9278
|
+
if (response.error) throw response.error;
|
|
9279
|
+
result = response.data;
|
|
9280
|
+
} else throw new Error("Export not supported");
|
|
9281
|
+
if (!result || !Array.isArray(result.data)) throw new Error("Export returned no data. Please retry.");
|
|
9282
|
+
if (result.hasMore !== false || result.total > result.data.length) throw new Error("Export results are incomplete. Narrow your filters and retry.");
|
|
9283
|
+
return result.data;
|
|
9207
9284
|
}, [
|
|
9208
9285
|
exportFetcher,
|
|
9209
9286
|
exportQuery,
|
|
@@ -9557,6 +9634,72 @@ function createMemoryDataSource(initialData = []) {
|
|
|
9557
9634
|
};
|
|
9558
9635
|
}
|
|
9559
9636
|
|
|
9637
|
+
//#endregion
|
|
9638
|
+
//#region src/lib/workbook.ts
|
|
9639
|
+
const MAX_ROWS = 1048576;
|
|
9640
|
+
const MAX_COLUMNS = 16384;
|
|
9641
|
+
const MAX_CELL_LENGTH = 32767;
|
|
9642
|
+
const MAX_LINE_FEEDS = 253;
|
|
9643
|
+
function validateCell(value) {
|
|
9644
|
+
if (typeof value === "string") {
|
|
9645
|
+
if (value.length > MAX_CELL_LENGTH) throw new RangeError(`Excel cells cannot exceed ${MAX_CELL_LENGTH} characters.`);
|
|
9646
|
+
if ((value.match(/\n/g)?.length ?? 0) > MAX_LINE_FEEDS) throw new RangeError(`Excel cells cannot exceed ${MAX_LINE_FEEDS} line feeds.`);
|
|
9647
|
+
} else if (value != null && (typeof value !== "number" || !Number.isFinite(value))) throw new TypeError("Workbook cells must contain text, finite numbers, or null.");
|
|
9648
|
+
}
|
|
9649
|
+
function validateSheets(sheets) {
|
|
9650
|
+
if (sheets.length === 0) throw new Error("A workbook needs at least one sheet.");
|
|
9651
|
+
const names = /* @__PURE__ */ new Set();
|
|
9652
|
+
for (const sheet of sheets) {
|
|
9653
|
+
if (!sheet.name || sheet.name.length > 31 || /[*?:\\/\[\]]/.test(sheet.name) || sheet.name.startsWith("'") || sheet.name.endsWith("'")) throw new Error("Worksheet names must be valid Excel names of 1–31 characters.");
|
|
9654
|
+
const name = sheet.name.toLowerCase();
|
|
9655
|
+
if (names.has(name)) throw new Error("Worksheet names must be unique.");
|
|
9656
|
+
names.add(name);
|
|
9657
|
+
if (sheet.rows.length + 1 > MAX_ROWS) throw new RangeError(`Excel worksheets cannot exceed ${MAX_ROWS} rows including headers.`);
|
|
9658
|
+
if (sheet.headers.length > MAX_COLUMNS) throw new RangeError(`Excel worksheets cannot exceed ${MAX_COLUMNS} columns.`);
|
|
9659
|
+
sheet.headers.forEach(validateCell);
|
|
9660
|
+
for (const row of sheet.rows) {
|
|
9661
|
+
if (row.length > MAX_COLUMNS) throw new RangeError(`Excel worksheets cannot exceed ${MAX_COLUMNS} columns.`);
|
|
9662
|
+
row.forEach(validateCell);
|
|
9663
|
+
}
|
|
9664
|
+
}
|
|
9665
|
+
}
|
|
9666
|
+
/** Internal byte builder, exported from this module for real workbook roundtrip tests. */
|
|
9667
|
+
async function buildWorkbookBytes(sheets) {
|
|
9668
|
+
validateSheets(sheets);
|
|
9669
|
+
const { default: ExcelJS } = await import("exceljs");
|
|
9670
|
+
const workbook = new ExcelJS.Workbook();
|
|
9671
|
+
for (const sheet of sheets) {
|
|
9672
|
+
const worksheet = workbook.addWorksheet(sheet.name);
|
|
9673
|
+
const append = (values) => {
|
|
9674
|
+
worksheet.addRow(values.map((value) => value ?? null)).eachCell((cell, column) => {
|
|
9675
|
+
const numberFormat = sheet.numberFormats?.[column - 1];
|
|
9676
|
+
if (typeof cell.value === "number" && numberFormat !== void 0) cell.numFmt = numberFormat;
|
|
9677
|
+
if (typeof cell.value === "string" && /[\r\n]/.test(cell.value)) cell.alignment = { wrapText: true };
|
|
9678
|
+
});
|
|
9679
|
+
};
|
|
9680
|
+
append(sheet.headers);
|
|
9681
|
+
sheet.rows.forEach(append);
|
|
9682
|
+
}
|
|
9683
|
+
return new Uint8Array(await workbook.xlsx.writeBuffer()).buffer;
|
|
9684
|
+
}
|
|
9685
|
+
/** Download a real XLSX workbook. Errors reject; no CSV or raw-value fallback. */
|
|
9686
|
+
async function exportWorkbook(sheets, { filename }) {
|
|
9687
|
+
const bytes = await buildWorkbookBytes(sheets);
|
|
9688
|
+
const blob = new Blob([bytes], { type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" });
|
|
9689
|
+
const url = URL.createObjectURL(blob);
|
|
9690
|
+
const link = document.createElement("a");
|
|
9691
|
+
try {
|
|
9692
|
+
link.href = url;
|
|
9693
|
+
link.download = /\.xlsx$/i.test(filename) ? filename : `${filename}.xlsx`;
|
|
9694
|
+
link.hidden = true;
|
|
9695
|
+
document.body.appendChild(link);
|
|
9696
|
+
link.click();
|
|
9697
|
+
} finally {
|
|
9698
|
+
link.remove();
|
|
9699
|
+
setTimeout(() => URL.revokeObjectURL(url), 0);
|
|
9700
|
+
}
|
|
9701
|
+
}
|
|
9702
|
+
|
|
9560
9703
|
//#endregion
|
|
9561
9704
|
exports.AutoCrudTable = AutoCrudTable;
|
|
9562
9705
|
exports.AutoForm = AutoForm;
|
|
@@ -9607,6 +9750,7 @@ exports.enUS = enUS;
|
|
|
9607
9750
|
exports.esES = esES;
|
|
9608
9751
|
exports.exportAllToCSV = exportAllToCSV;
|
|
9609
9752
|
exports.exportTableToCSV = exportTableToCSV;
|
|
9753
|
+
exports.exportWorkbook = exportWorkbook;
|
|
9610
9754
|
exports.formComponents = formComponents;
|
|
9611
9755
|
exports.formatDate = formatDate;
|
|
9612
9756
|
exports.frFR = frFR;
|
package/dist/index.d.cts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { a as positionActions, i as matchesPosition, n as actionPositionSchema, r as compareOrder, t as ActionPosition } from "./position-DoBV7yB6.cjs";
|
|
2
|
-
import * as
|
|
2
|
+
import * as react_jsx_runtime8 from "react/jsx-runtime";
|
|
3
3
|
import * as React$1 from "react";
|
|
4
4
|
import { ComponentProps, ReactNode } from "react";
|
|
5
5
|
import * as _tanstack_react_table0 from "@tanstack/react-table";
|
|
@@ -276,6 +276,8 @@ declare module '@tanstack/react-table' {
|
|
|
276
276
|
interface ColumnMeta<TData extends RowData, TValue> {
|
|
277
277
|
label?: string;
|
|
278
278
|
index?: number;
|
|
279
|
+
/** @internal Identifies the generated row expansion control, regardless of its id. */
|
|
280
|
+
autoCrudExpandColumn?: boolean;
|
|
279
281
|
/** 按元素分组的列类名,优先于全局单元格类名 */
|
|
280
282
|
classNames?: Pick<DataTableClassNames, 'th' | 'td'>;
|
|
281
283
|
placeholder?: string;
|
|
@@ -670,7 +672,7 @@ declare function DataTable<TData>({
|
|
|
670
672
|
className,
|
|
671
673
|
classNames,
|
|
672
674
|
...props
|
|
673
|
-
}: DataTableProps<TData>):
|
|
675
|
+
}: DataTableProps<TData>): react_jsx_runtime8.JSX.Element;
|
|
674
676
|
//#endregion
|
|
675
677
|
//#region src/components/auto-crud/auto-table-action-bar.d.ts
|
|
676
678
|
/** 批量更新字段配置 */
|
|
@@ -759,7 +761,7 @@ declare function AutoTableActionBar<TData>({
|
|
|
759
761
|
extraActions,
|
|
760
762
|
actions,
|
|
761
763
|
deleteConfirmation
|
|
762
|
-
}: AutoTableActionBarProps<TData>):
|
|
764
|
+
}: AutoTableActionBarProps<TData>): react_jsx_runtime8.JSX.Element;
|
|
763
765
|
//#endregion
|
|
764
766
|
//#region src/lib/schema-bridge/types.d.ts
|
|
765
767
|
/**
|
|
@@ -1059,6 +1061,8 @@ interface AutoTableProps<T extends z.ZodObject<z.ZodRawShape>> extends Pick<Data
|
|
|
1059
1061
|
onSelectedCountChange?: (count: number) => void;
|
|
1060
1062
|
/** 选中行数据变化回调(用于外层组件获知选中行身份变化) */
|
|
1061
1063
|
onSelectedRowsChange?: (rows: z.infer<T>[]) => void;
|
|
1064
|
+
/** @internal Supplies the current table for opt-in export snapshots. */
|
|
1065
|
+
exportTableRef?: React.MutableRefObject<Table<z.infer<T>> | null>;
|
|
1062
1066
|
/** 获取选中行数据的回调(由外层组件调用) */
|
|
1063
1067
|
getSelectedRows?: React.MutableRefObject<(() => z.infer<T>[]) | null>;
|
|
1064
1068
|
}
|
|
@@ -1093,8 +1097,9 @@ declare function AutoTable<T extends z.ZodObject<z.ZodRawShape>>({
|
|
|
1093
1097
|
showDefaultExport,
|
|
1094
1098
|
onSelectedCountChange,
|
|
1095
1099
|
onSelectedRowsChange,
|
|
1096
|
-
getSelectedRows
|
|
1097
|
-
|
|
1100
|
+
getSelectedRows,
|
|
1101
|
+
exportTableRef
|
|
1102
|
+
}: AutoTableProps<T>): react_jsx_runtime8.JSX.Element;
|
|
1098
1103
|
//#endregion
|
|
1099
1104
|
//#region src/lib/row-action-dialog.d.ts
|
|
1100
1105
|
interface RowActionDialogProps {
|
|
@@ -1405,9 +1410,15 @@ interface AutoCrudToolbarContext {
|
|
|
1405
1410
|
}
|
|
1406
1411
|
type AutoCrudToolbarResolver = (targetId: string, ownerActions: readonly ToolbarActionItem[], context: AutoCrudToolbarContext) => ToolbarActionItem[];
|
|
1407
1412
|
declare function setToolbarResolver(resolver: AutoCrudToolbarResolver | null): void;
|
|
1408
|
-
/**
|
|
1409
|
-
|
|
1410
|
-
|
|
1413
|
+
/** Unformatted rows without denied fields, and visible localized business columns. */
|
|
1414
|
+
interface AutoCrudExportContext {
|
|
1415
|
+
mode: 'selected' | 'filtered';
|
|
1416
|
+
rows: Record<string, unknown>[];
|
|
1417
|
+
columns: Array<{
|
|
1418
|
+
id: string;
|
|
1419
|
+
label: string;
|
|
1420
|
+
}>;
|
|
1421
|
+
}
|
|
1411
1422
|
interface AutoCrudTableProps<TSchema extends z.ZodObject<z.ZodRawShape>> {
|
|
1412
1423
|
/** Public CRUD id. Used by hosts to document the extension target. */
|
|
1413
1424
|
id?: string;
|
|
@@ -1526,6 +1537,8 @@ interface AutoCrudTableProps<TSchema extends z.ZodObject<z.ZodRawShape>> {
|
|
|
1526
1537
|
* 如果提供,将覆盖默认的打开新建弹窗行为
|
|
1527
1538
|
*/
|
|
1528
1539
|
onCreate?: () => void;
|
|
1540
|
+
/** Owns export delivery when supplied. Rejections propagate without CSV fallback. */
|
|
1541
|
+
onExport?: (context: AutoCrudExportContext) => Promise<void>;
|
|
1529
1542
|
}
|
|
1530
1543
|
/**
|
|
1531
1544
|
* AutoCrudTable 组件
|
|
@@ -1547,8 +1560,9 @@ declare function AutoCrudTable<TSchema extends z.ZodObject<z.ZodRawShape>>({
|
|
|
1547
1560
|
toolbar,
|
|
1548
1561
|
toolbarActions,
|
|
1549
1562
|
locale: localeProp,
|
|
1550
|
-
onCreate
|
|
1551
|
-
|
|
1563
|
+
onCreate,
|
|
1564
|
+
onExport
|
|
1565
|
+
}: AutoCrudTableProps<TSchema>): react_jsx_runtime8.JSX.Element;
|
|
1552
1566
|
//#endregion
|
|
1553
1567
|
//#region src/components/auto-crud/auto-form.d.ts
|
|
1554
1568
|
interface AutoFormProps<T extends z.ZodObject<z.ZodRawShape>> {
|
|
@@ -1587,7 +1601,7 @@ declare function AutoFormInner<T extends z.ZodObject<z.ZodRawShape>>({
|
|
|
1587
1601
|
labelAlign,
|
|
1588
1602
|
labelWidth,
|
|
1589
1603
|
showSubmitButton
|
|
1590
|
-
}: AutoFormProps<T>, ref: React.Ref<AutoFormRef>):
|
|
1604
|
+
}: AutoFormProps<T>, ref: React.Ref<AutoFormRef>): react_jsx_runtime8.JSX.Element;
|
|
1591
1605
|
declare const AutoForm: <T extends z.ZodObject<z.ZodRawShape>>(props: AutoFormProps<T> & {
|
|
1592
1606
|
ref?: React.Ref<AutoFormRef>;
|
|
1593
1607
|
}) => ReturnType<typeof AutoFormInner>;
|
|
@@ -1606,7 +1620,7 @@ declare function AutoTableSimpleFilters<TData>({
|
|
|
1606
1620
|
filters: externalFilters,
|
|
1607
1621
|
onFiltersChange,
|
|
1608
1622
|
leading
|
|
1609
|
-
}: AutoTableSimpleFiltersProps<TData>):
|
|
1623
|
+
}: AutoTableSimpleFiltersProps<TData>): react_jsx_runtime8.JSX.Element | null;
|
|
1610
1624
|
//#endregion
|
|
1611
1625
|
//#region src/components/auto-crud/form-modal.d.ts
|
|
1612
1626
|
type ModalVariant = 'dialog' | 'sheet';
|
|
@@ -1650,7 +1664,7 @@ declare function CrudFormModal<T extends z.ZodObject<z.ZodRawShape>>({
|
|
|
1650
1664
|
labelAlign,
|
|
1651
1665
|
labelWidth,
|
|
1652
1666
|
className
|
|
1653
|
-
}: CrudFormModalProps<T>):
|
|
1667
|
+
}: CrudFormModalProps<T>): react_jsx_runtime8.JSX.Element;
|
|
1654
1668
|
//#endregion
|
|
1655
1669
|
//#region src/components/auto-crud/import-dialog.d.ts
|
|
1656
1670
|
interface ImportDialogProps {
|
|
@@ -1672,7 +1686,7 @@ declare function ImportDialog({
|
|
|
1672
1686
|
columns,
|
|
1673
1687
|
title,
|
|
1674
1688
|
locale
|
|
1675
|
-
}: ImportDialogProps):
|
|
1689
|
+
}: ImportDialogProps): react_jsx_runtime8.JSX.Element;
|
|
1676
1690
|
//#endregion
|
|
1677
1691
|
//#region src/components/auto-crud/export-dialog.d.ts
|
|
1678
1692
|
type ExportMode = 'selected' | 'filtered';
|
|
@@ -1692,7 +1706,7 @@ declare function ExportDialog({
|
|
|
1692
1706
|
selectedCount,
|
|
1693
1707
|
onExport,
|
|
1694
1708
|
canExportFiltered
|
|
1695
|
-
}: ExportDialogProps):
|
|
1709
|
+
}: ExportDialogProps): react_jsx_runtime8.JSX.Element;
|
|
1696
1710
|
//#endregion
|
|
1697
1711
|
//#region src/components/data-table/data-table-advanced-toolbar.d.ts
|
|
1698
1712
|
interface DataTableAdvancedToolbarProps<TData> extends React$1.ComponentProps<'div'> {
|
|
@@ -1703,7 +1717,7 @@ declare function DataTableAdvancedToolbar<TData>({
|
|
|
1703
1717
|
children,
|
|
1704
1718
|
className,
|
|
1705
1719
|
...props
|
|
1706
|
-
}: DataTableAdvancedToolbarProps<TData>):
|
|
1720
|
+
}: DataTableAdvancedToolbarProps<TData>): react_jsx_runtime8.JSX.Element;
|
|
1707
1721
|
//#endregion
|
|
1708
1722
|
//#region src/components/data-table/data-table-column-header.d.ts
|
|
1709
1723
|
interface DataTableColumnHeaderProps<TData, TValue> extends React.ComponentProps<typeof DropdownMenuTrigger> {
|
|
@@ -1715,7 +1729,7 @@ declare function DataTableColumnHeader<TData, TValue>({
|
|
|
1715
1729
|
label,
|
|
1716
1730
|
className,
|
|
1717
1731
|
...props
|
|
1718
|
-
}: DataTableColumnHeaderProps<TData, TValue>):
|
|
1732
|
+
}: DataTableColumnHeaderProps<TData, TValue>): react_jsx_runtime8.JSX.Element;
|
|
1719
1733
|
//#endregion
|
|
1720
1734
|
//#region src/components/data-table/data-table-faceted-filter.d.ts
|
|
1721
1735
|
interface DataTableFacetedFilterProps<TData, TValue> {
|
|
@@ -1729,7 +1743,7 @@ declare function DataTableFacetedFilter<TData, TValue>({
|
|
|
1729
1743
|
title,
|
|
1730
1744
|
options,
|
|
1731
1745
|
multiple
|
|
1732
|
-
}: DataTableFacetedFilterProps<TData, TValue>):
|
|
1746
|
+
}: DataTableFacetedFilterProps<TData, TValue>): react_jsx_runtime8.JSX.Element;
|
|
1733
1747
|
//#endregion
|
|
1734
1748
|
//#region src/components/data-table/data-table-pagination.d.ts
|
|
1735
1749
|
interface DataTablePaginationProps<TData> extends React.ComponentProps<'div'>, TablePaginationOptions {
|
|
@@ -1745,7 +1759,7 @@ declare function DataTablePagination<TData>({
|
|
|
1745
1759
|
pageJumpLabel,
|
|
1746
1760
|
className,
|
|
1747
1761
|
...props
|
|
1748
|
-
}: DataTablePaginationProps<TData>):
|
|
1762
|
+
}: DataTablePaginationProps<TData>): react_jsx_runtime8.JSX.Element;
|
|
1749
1763
|
//#endregion
|
|
1750
1764
|
//#region src/components/data-table/data-table-toolbar.d.ts
|
|
1751
1765
|
interface DataTableToolbarProps<TData> extends React$1.ComponentProps<'div'> {
|
|
@@ -1756,7 +1770,7 @@ declare function DataTableToolbar<TData>({
|
|
|
1756
1770
|
children,
|
|
1757
1771
|
className,
|
|
1758
1772
|
...props
|
|
1759
|
-
}: DataTableToolbarProps<TData>):
|
|
1773
|
+
}: DataTableToolbarProps<TData>): react_jsx_runtime8.JSX.Element;
|
|
1760
1774
|
//#endregion
|
|
1761
1775
|
//#region src/components/data-table/data-table-view-options.d.ts
|
|
1762
1776
|
interface DataTableViewOptionsProps<TData> extends PopoverContentProps {
|
|
@@ -1769,7 +1783,7 @@ declare function DataTableViewOptions<TData>({
|
|
|
1769
1783
|
disabled,
|
|
1770
1784
|
locale,
|
|
1771
1785
|
...props
|
|
1772
|
-
}: DataTableViewOptionsProps<TData>):
|
|
1786
|
+
}: DataTableViewOptionsProps<TData>): react_jsx_runtime8.JSX.Element;
|
|
1773
1787
|
//#endregion
|
|
1774
1788
|
//#region src/lib/crud-actions.d.ts
|
|
1775
1789
|
type CrudActionZone = 'toolbar' | 'row' | 'batch';
|
|
@@ -2163,4 +2177,19 @@ declare function exportAllToCSV<T extends Record<string, unknown>>(data: T[], op
|
|
|
2163
2177
|
*/
|
|
2164
2178
|
declare function downloadCSVTemplate(headers: string[], filename?: string): void;
|
|
2165
2179
|
//#endregion
|
|
2166
|
-
|
|
2180
|
+
//#region src/lib/workbook.d.ts
|
|
2181
|
+
interface WorkbookSheet {
|
|
2182
|
+
name: string;
|
|
2183
|
+
headers: string[];
|
|
2184
|
+
/** Excel formats by header index; applied only to numeric cells. */
|
|
2185
|
+
numberFormats?: (string | undefined)[];
|
|
2186
|
+
rows: (string | number | null | undefined)[][];
|
|
2187
|
+
}
|
|
2188
|
+
/** Download a real XLSX workbook. Errors reject; no CSV or raw-value fallback. */
|
|
2189
|
+
declare function exportWorkbook(sheets: WorkbookSheet[], {
|
|
2190
|
+
filename
|
|
2191
|
+
}: {
|
|
2192
|
+
filename: string;
|
|
2193
|
+
}): Promise<void>;
|
|
2194
|
+
//#endregion
|
|
2195
|
+
export { type ActionConfig, type ActionItem, type ActionPosition, type ActionsColumnConfig, type AutoCrudActionConfig, type AutoCrudActionsConfig, type AutoCrudDataSourceConfig, type AutoCrudDataSourceContext, type AutoCrudDataSourceEntry, type AutoCrudDataSourceLoader, type AutoCrudDataSourceRegistration, type AutoCrudExportContext, type AutoCrudFormComponentConfig, type AutoCrudLocale, type AutoCrudOption, type AutoCrudQueryCapabilities, type AutoCrudRowActionContext, type AutoCrudRowOpenOptions, AutoCrudTable, type AutoCrudTableProps, type AutoCrudToolbarContext, type AutoCrudToolbarResolver, AutoForm, type AutoFormProps, type AutoQueryParams, AutoTable, AutoTableActionBar, type AutoTableProps, AutoTableSimpleFilters, type BatchActionConfig, type BatchActionContext, type BatchActionItem, type BatchBuiltinActionItem, type BatchBuiltinActionType, type BatchCustomActionItem, type BatchUpdateField, type ColumnOverrides, type CreateFormSchemaOptions, type CreateTableSchemaOptions, type CrudActionBase, type CrudActionEntry, type CrudActionRegistration, type CrudActionZone, CrudFormModal, type CrudHooks, type CrudOperationPermissions, type CrudPermissions, type DataSource, DataTable, DataTableAdvancedToolbar, DataTableClassNames, DataTableColumnHeader, DataTableFacetedFilter, DataTablePagination, type DataTableProps, DataTableRowAction, DataTableToolbar, DataTableViewOptions, type DataTableViewOptionsProps, type DateFormatPreset, type DateFormatter, type DateLocaleOptions, type EnumOption, ExportDialog, type ExportDialogProps, type ExportMode, ExtendedColumnFilter, ExtendedColumnSort, type Field, type FieldOption, type FieldTableDisplay, type FieldType, type Fields, type FilterConfig, FilterOperator, type FilterVariant, type FormSchemaOverrides, ImportDialog, type ImportDialogProps, type ImportResult, type JSONSchema, type JSONSchemaProperty, JoinOperator, type ListParams, type ListResult, type LocaleProp, MultiCombobox, type MultiComboboxOption, type MultiComboboxProps, type MultiComboboxTriggerRenderProps, Option, type ParsedImportData, type ParsedZodField, type Parser, QueryKeys, type RowActionConfig, type RowActionDialogProps, type RowActionItem, type RowBuiltinActionItem, type RowBuiltinActionType, type RowCustomActionItem, SchemaAdapter, Select, type SelectMode, type SelectOption, type SelectProps, type SelectSearchableDynamicProps, type SelectSearchableMultipleProps, type SelectSearchableProps, type SelectSearchableSingleProps, type SelectSimpleProps, type SelectTriggerRenderProps, type SimpleFieldConfig, type SimpleFieldsConfig, type TableControlsLocale, type TablePaginationOptions, type ToastAdapter, type ToolbarActionConfig, type ToolbarActionItem, type ToolbarBuiltinActionItem, type ToolbarBuiltinActionType, type ToolbarCustomActionItem, type UnifiedField, type UnifiedSchema, type UrlStateOptions, type UseAutoCrudResourceOptions, type UseAutoCrudResourceParams, type UseAutoCrudResourceReturn, type WorkbookSheet, actionPositionSchema, cn, coerceRowValues, compareOrder, components, createActionsColumn, createEditFormSchema, createFormSchema, createMemoryDataSource, createSelectColumn, createTRPCDataSource, createTableSchema, crudActions, dataSources, dataToCSV, deDE, downloadCSVTemplate, enUS, esES, exportAllToCSV, exportTableToCSV, exportWorkbook, formComponents, formatDate, frFR, generateCSVTemplate, getUrlParams, humanize, jaJP, koKR, matchesPosition, noopToastAdapter, normalizeDataSourceConfig, normalizeOptions, parseAsArrayOf, parseAsInteger, parseAsString, parseAsStringEnum, parseCSV, parseImportFile, parseJSON, parseZodField, positionActions, resolveLocale, setDateFormatter, setSearchParams, setToolbarResolver, tablePagination, useAutoCrudResource, useDataTable, useQueryState, useQueryStates, useReadableFilters, useUrlState, useUrlStates, zhCN };
|
package/dist/index.d.ts
CHANGED
|
@@ -276,6 +276,8 @@ declare module '@tanstack/react-table' {
|
|
|
276
276
|
interface ColumnMeta<TData extends RowData, TValue> {
|
|
277
277
|
label?: string;
|
|
278
278
|
index?: number;
|
|
279
|
+
/** @internal Identifies the generated row expansion control, regardless of its id. */
|
|
280
|
+
autoCrudExpandColumn?: boolean;
|
|
279
281
|
/** 按元素分组的列类名,优先于全局单元格类名 */
|
|
280
282
|
classNames?: Pick<DataTableClassNames, 'th' | 'td'>;
|
|
281
283
|
placeholder?: string;
|
|
@@ -1059,6 +1061,8 @@ interface AutoTableProps<T extends z.ZodObject<z.ZodRawShape>> extends Pick<Data
|
|
|
1059
1061
|
onSelectedCountChange?: (count: number) => void;
|
|
1060
1062
|
/** 选中行数据变化回调(用于外层组件获知选中行身份变化) */
|
|
1061
1063
|
onSelectedRowsChange?: (rows: z.infer<T>[]) => void;
|
|
1064
|
+
/** @internal Supplies the current table for opt-in export snapshots. */
|
|
1065
|
+
exportTableRef?: React.MutableRefObject<Table<z.infer<T>> | null>;
|
|
1062
1066
|
/** 获取选中行数据的回调(由外层组件调用) */
|
|
1063
1067
|
getSelectedRows?: React.MutableRefObject<(() => z.infer<T>[]) | null>;
|
|
1064
1068
|
}
|
|
@@ -1093,7 +1097,8 @@ declare function AutoTable<T extends z.ZodObject<z.ZodRawShape>>({
|
|
|
1093
1097
|
showDefaultExport,
|
|
1094
1098
|
onSelectedCountChange,
|
|
1095
1099
|
onSelectedRowsChange,
|
|
1096
|
-
getSelectedRows
|
|
1100
|
+
getSelectedRows,
|
|
1101
|
+
exportTableRef
|
|
1097
1102
|
}: AutoTableProps<T>): react_jsx_runtime8.JSX.Element;
|
|
1098
1103
|
//#endregion
|
|
1099
1104
|
//#region src/lib/row-action-dialog.d.ts
|
|
@@ -1405,9 +1410,15 @@ interface AutoCrudToolbarContext {
|
|
|
1405
1410
|
}
|
|
1406
1411
|
type AutoCrudToolbarResolver = (targetId: string, ownerActions: readonly ToolbarActionItem[], context: AutoCrudToolbarContext) => ToolbarActionItem[];
|
|
1407
1412
|
declare function setToolbarResolver(resolver: AutoCrudToolbarResolver | null): void;
|
|
1408
|
-
/**
|
|
1409
|
-
|
|
1410
|
-
|
|
1413
|
+
/** Unformatted rows without denied fields, and visible localized business columns. */
|
|
1414
|
+
interface AutoCrudExportContext {
|
|
1415
|
+
mode: 'selected' | 'filtered';
|
|
1416
|
+
rows: Record<string, unknown>[];
|
|
1417
|
+
columns: Array<{
|
|
1418
|
+
id: string;
|
|
1419
|
+
label: string;
|
|
1420
|
+
}>;
|
|
1421
|
+
}
|
|
1411
1422
|
interface AutoCrudTableProps<TSchema extends z.ZodObject<z.ZodRawShape>> {
|
|
1412
1423
|
/** Public CRUD id. Used by hosts to document the extension target. */
|
|
1413
1424
|
id?: string;
|
|
@@ -1526,6 +1537,8 @@ interface AutoCrudTableProps<TSchema extends z.ZodObject<z.ZodRawShape>> {
|
|
|
1526
1537
|
* 如果提供,将覆盖默认的打开新建弹窗行为
|
|
1527
1538
|
*/
|
|
1528
1539
|
onCreate?: () => void;
|
|
1540
|
+
/** Owns export delivery when supplied. Rejections propagate without CSV fallback. */
|
|
1541
|
+
onExport?: (context: AutoCrudExportContext) => Promise<void>;
|
|
1529
1542
|
}
|
|
1530
1543
|
/**
|
|
1531
1544
|
* AutoCrudTable 组件
|
|
@@ -1547,7 +1560,8 @@ declare function AutoCrudTable<TSchema extends z.ZodObject<z.ZodRawShape>>({
|
|
|
1547
1560
|
toolbar,
|
|
1548
1561
|
toolbarActions,
|
|
1549
1562
|
locale: localeProp,
|
|
1550
|
-
onCreate
|
|
1563
|
+
onCreate,
|
|
1564
|
+
onExport
|
|
1551
1565
|
}: AutoCrudTableProps<TSchema>): react_jsx_runtime8.JSX.Element;
|
|
1552
1566
|
//#endregion
|
|
1553
1567
|
//#region src/components/auto-crud/auto-form.d.ts
|
|
@@ -2163,4 +2177,19 @@ declare function exportAllToCSV<T extends Record<string, unknown>>(data: T[], op
|
|
|
2163
2177
|
*/
|
|
2164
2178
|
declare function downloadCSVTemplate(headers: string[], filename?: string): void;
|
|
2165
2179
|
//#endregion
|
|
2166
|
-
|
|
2180
|
+
//#region src/lib/workbook.d.ts
|
|
2181
|
+
interface WorkbookSheet {
|
|
2182
|
+
name: string;
|
|
2183
|
+
headers: string[];
|
|
2184
|
+
/** Excel formats by header index; applied only to numeric cells. */
|
|
2185
|
+
numberFormats?: (string | undefined)[];
|
|
2186
|
+
rows: (string | number | null | undefined)[][];
|
|
2187
|
+
}
|
|
2188
|
+
/** Download a real XLSX workbook. Errors reject; no CSV or raw-value fallback. */
|
|
2189
|
+
declare function exportWorkbook(sheets: WorkbookSheet[], {
|
|
2190
|
+
filename
|
|
2191
|
+
}: {
|
|
2192
|
+
filename: string;
|
|
2193
|
+
}): Promise<void>;
|
|
2194
|
+
//#endregion
|
|
2195
|
+
export { type ActionConfig, type ActionItem, type ActionPosition, type ActionsColumnConfig, type AutoCrudActionConfig, type AutoCrudActionsConfig, type AutoCrudDataSourceConfig, type AutoCrudDataSourceContext, type AutoCrudDataSourceEntry, type AutoCrudDataSourceLoader, type AutoCrudDataSourceRegistration, type AutoCrudExportContext, type AutoCrudFormComponentConfig, type AutoCrudLocale, type AutoCrudOption, type AutoCrudQueryCapabilities, type AutoCrudRowActionContext, type AutoCrudRowOpenOptions, AutoCrudTable, type AutoCrudTableProps, type AutoCrudToolbarContext, type AutoCrudToolbarResolver, AutoForm, type AutoFormProps, type AutoQueryParams, AutoTable, AutoTableActionBar, type AutoTableProps, AutoTableSimpleFilters, type BatchActionConfig, type BatchActionContext, type BatchActionItem, type BatchBuiltinActionItem, type BatchBuiltinActionType, type BatchCustomActionItem, type BatchUpdateField, type ColumnOverrides, type CreateFormSchemaOptions, type CreateTableSchemaOptions, type CrudActionBase, type CrudActionEntry, type CrudActionRegistration, type CrudActionZone, CrudFormModal, type CrudHooks, type CrudOperationPermissions, type CrudPermissions, type DataSource, DataTable, DataTableAdvancedToolbar, DataTableClassNames, DataTableColumnHeader, DataTableFacetedFilter, DataTablePagination, type DataTableProps, DataTableRowAction, DataTableToolbar, DataTableViewOptions, type DataTableViewOptionsProps, type DateFormatPreset, type DateFormatter, type DateLocaleOptions, type EnumOption, ExportDialog, type ExportDialogProps, type ExportMode, ExtendedColumnFilter, ExtendedColumnSort, type Field, type FieldOption, type FieldTableDisplay, type FieldType, type Fields, type FilterConfig, FilterOperator, type FilterVariant, type FormSchemaOverrides, ImportDialog, type ImportDialogProps, type ImportResult, type JSONSchema, type JSONSchemaProperty, JoinOperator, type ListParams, type ListResult, type LocaleProp, MultiCombobox, type MultiComboboxOption, type MultiComboboxProps, type MultiComboboxTriggerRenderProps, Option, type ParsedImportData, type ParsedZodField, type Parser, QueryKeys, type RowActionConfig, type RowActionDialogProps, type RowActionItem, type RowBuiltinActionItem, type RowBuiltinActionType, type RowCustomActionItem, SchemaAdapter, Select, type SelectMode, type SelectOption, type SelectProps, type SelectSearchableDynamicProps, type SelectSearchableMultipleProps, type SelectSearchableProps, type SelectSearchableSingleProps, type SelectSimpleProps, type SelectTriggerRenderProps, type SimpleFieldConfig, type SimpleFieldsConfig, type TableControlsLocale, type TablePaginationOptions, type ToastAdapter, type ToolbarActionConfig, type ToolbarActionItem, type ToolbarBuiltinActionItem, type ToolbarBuiltinActionType, type ToolbarCustomActionItem, type UnifiedField, type UnifiedSchema, type UrlStateOptions, type UseAutoCrudResourceOptions, type UseAutoCrudResourceParams, type UseAutoCrudResourceReturn, type WorkbookSheet, actionPositionSchema, cn, coerceRowValues, compareOrder, components, createActionsColumn, createEditFormSchema, createFormSchema, createMemoryDataSource, createSelectColumn, createTRPCDataSource, createTableSchema, crudActions, dataSources, dataToCSV, deDE, downloadCSVTemplate, enUS, esES, exportAllToCSV, exportTableToCSV, exportWorkbook, formComponents, formatDate, frFR, generateCSVTemplate, getUrlParams, humanize, jaJP, koKR, matchesPosition, noopToastAdapter, normalizeDataSourceConfig, normalizeOptions, parseAsArrayOf, parseAsInteger, parseAsString, parseAsStringEnum, parseCSV, parseImportFile, parseJSON, parseZodField, positionActions, resolveLocale, setDateFormatter, setSearchParams, setToolbarResolver, tablePagination, useAutoCrudResource, useDataTable, useQueryState, useQueryStates, useReadableFilters, useUrlState, useUrlStates, zhCN };
|
package/dist/index.js
CHANGED
|
@@ -4626,6 +4626,27 @@ function coerceRowValues(rows, schema) {
|
|
|
4626
4626
|
|
|
4627
4627
|
//#endregion
|
|
4628
4628
|
//#region src/lib/export.ts
|
|
4629
|
+
/** Snapshot rendered business columns without changing the legacy CSV column policy. */
|
|
4630
|
+
function getVisibleExportColumns(table, deny = []) {
|
|
4631
|
+
const excluded = new Set([
|
|
4632
|
+
"select",
|
|
4633
|
+
"actions",
|
|
4634
|
+
...deny
|
|
4635
|
+
]);
|
|
4636
|
+
return [
|
|
4637
|
+
...table.getLeftVisibleLeafColumns(),
|
|
4638
|
+
...table.getCenterVisibleLeafColumns(),
|
|
4639
|
+
...table.getRightVisibleLeafColumns()
|
|
4640
|
+
].filter((column) => {
|
|
4641
|
+
if (excluded.has(column.id)) return false;
|
|
4642
|
+
const meta = column.columnDef.meta;
|
|
4643
|
+
if (meta?.autoCrudExpandColumn) return false;
|
|
4644
|
+
return ![meta?.classNames?.th, meta?.classNames?.td].some((value) => typeof value === "string" && /(?:^|\s)!?hidden!?(?:\s|$)/.test(value));
|
|
4645
|
+
}).map((column) => ({
|
|
4646
|
+
id: column.id,
|
|
4647
|
+
label: column.columnDef.meta?.label ?? (typeof column.columnDef.header === "string" ? column.columnDef.header : column.id)
|
|
4648
|
+
}));
|
|
4649
|
+
}
|
|
4629
4650
|
function getExportColumnIds(table) {
|
|
4630
4651
|
return table.getAllLeafColumns().filter((column) => column.accessorFn).map((column) => column.id);
|
|
4631
4652
|
}
|
|
@@ -5142,7 +5163,7 @@ const filterModeConfig = {
|
|
|
5142
5163
|
tooltip: "Linear-style command filters"
|
|
5143
5164
|
}
|
|
5144
5165
|
};
|
|
5145
|
-
function AutoTable({ schema, data, getRowId, expandedRow, defaultExpanded = false, pageCount = 1, pagination, total, overrides, classNames, enableRowSelection = true, exclude, filterOnly, actions, pinnedColumns, filterMode, search = false, toolbarExtra, locale = enUSTableControls, onDeleteSelected, onUpdateSelected, batchUpdateFields, actionBarExtra, actionBarActions, deleteConfirmation, initialSorting, enableExport = true, showDefaultExport = true, onSelectedCountChange, onSelectedRowsChange, getSelectedRows }) {
|
|
5166
|
+
function AutoTable({ schema, data, getRowId, expandedRow, defaultExpanded = false, pageCount = 1, pagination, total, overrides, classNames, enableRowSelection = true, exclude, filterOnly, actions, pinnedColumns, filterMode, search = false, toolbarExtra, locale = enUSTableControls, onDeleteSelected, onUpdateSelected, batchUpdateFields, actionBarExtra, actionBarActions, deleteConfirmation, initialSorting, enableExport = true, showDefaultExport = true, onSelectedCountChange, onSelectedRowsChange, getSelectedRows, exportTableRef }) {
|
|
5146
5167
|
if (expandedRow && !getRowId) throw new Error("AutoTable requires getRowId when expandedRow is provided.");
|
|
5147
5168
|
const enableExpansion = !!expandedRow;
|
|
5148
5169
|
const modes = filterMode ? Array.isArray(filterMode) ? filterMode : [filterMode] : DEFAULT_MODES;
|
|
@@ -5184,6 +5205,7 @@ function AutoTable({ schema, data, getRowId, expandedRow, defaultExpanded = fals
|
|
|
5184
5205
|
while (expandColumnId$1 in schema.shape || result.some((column) => column.id === expandColumnId$1)) expandColumnId$1 = `_${expandColumnId$1}`;
|
|
5185
5206
|
result.unshift({
|
|
5186
5207
|
id: expandColumnId$1,
|
|
5208
|
+
meta: { autoCrudExpandColumn: true },
|
|
5187
5209
|
header: () => /* @__PURE__ */ jsx("span", {
|
|
5188
5210
|
className: "sr-only",
|
|
5189
5211
|
children: "Row details"
|
|
@@ -5257,6 +5279,13 @@ function AutoTable({ schema, data, getRowId, expandedRow, defaultExpanded = fals
|
|
|
5257
5279
|
};
|
|
5258
5280
|
});
|
|
5259
5281
|
}, [table, filterOnly]);
|
|
5282
|
+
useEffect(() => {
|
|
5283
|
+
if (!exportTableRef) return;
|
|
5284
|
+
exportTableRef.current = table;
|
|
5285
|
+
return () => {
|
|
5286
|
+
exportTableRef.current = null;
|
|
5287
|
+
};
|
|
5288
|
+
}, [table, exportTableRef]);
|
|
5260
5289
|
const rowSelection = table.getState().rowSelection;
|
|
5261
5290
|
const columnFilters = table.getState().columnFilters;
|
|
5262
5291
|
const selectedRows = useMemo(() => table.getFilteredSelectedRowModel().rows.map((row) => row.original), [
|
|
@@ -6815,6 +6844,9 @@ function resolveToolbarActionsWithResolver(targetId, ownerActions, context) {
|
|
|
6815
6844
|
const resolver = autoCrudToolbarResolver;
|
|
6816
6845
|
return targetId !== void 0 && targetId.length > 0 && resolver !== null ? resolver(targetId, ownerActions, context) : ownerActions;
|
|
6817
6846
|
}
|
|
6847
|
+
function reportExportError(error) {
|
|
6848
|
+
toast.error(error instanceof Error ? error.message : String(error));
|
|
6849
|
+
}
|
|
6818
6850
|
function applyResourceTablePresentation(fields, resourceFields) {
|
|
6819
6851
|
if (!fields || !resourceFields) return fields;
|
|
6820
6852
|
return Object.fromEntries(Object.entries(fields).map(([key, config]) => {
|
|
@@ -7694,7 +7726,7 @@ function resolveActions(actions, defaults, rowActionsLocale, context) {
|
|
|
7694
7726
|
*
|
|
7695
7727
|
* 高级 CRUD 表格组件,封装了完整的增删改查流程
|
|
7696
7728
|
*/
|
|
7697
|
-
function AutoCrudTable({ id, title, description, schema, resource, fields, table: tableConfig, form: formConfig, view: viewConfig, permissions, actions: actionItems, toolbar, toolbarActions, locale: localeProp, onCreate }) {
|
|
7729
|
+
function AutoCrudTable({ id, title, description, schema, resource, fields, table: tableConfig, form: formConfig, view: viewConfig, permissions, actions: actionItems, toolbar, toolbarActions, locale: localeProp, onCreate, onExport }) {
|
|
7698
7730
|
const locale = resolveLocale(localeProp);
|
|
7699
7731
|
const getPagination = React.useCallback(() => id ? tablePagination.get(id) : void 0, [id]);
|
|
7700
7732
|
const registeredPagination = React.useSyncExternalStore(tablePagination.subscribe, getPagination, getPagination);
|
|
@@ -7714,6 +7746,8 @@ function AutoCrudTable({ id, title, description, schema, resource, fields, table
|
|
|
7714
7746
|
const [importOpen, setImportOpen] = React.useState(false);
|
|
7715
7747
|
const [selectedRows, setSelectedRows] = React.useState([]);
|
|
7716
7748
|
const [exporting, setExporting] = React.useState(false);
|
|
7749
|
+
const exportBusyRef = React.useRef(false);
|
|
7750
|
+
const exportTableRef = React.useRef(null);
|
|
7717
7751
|
const getSelectedRowsRef = React.useRef(null);
|
|
7718
7752
|
const dynamicFilterOptions = useDynamicFilterOptions(resolvedFields);
|
|
7719
7753
|
const filterOnly = React.useMemo(() => Object.entries(resolvedFields).filter(([key, config]) => config.table === false && config.filter && config.hidden !== true && !denyFields?.includes(key) && !tableConfig?.hidden?.includes(key) && tableConfig?.overrides?.[key]?.hidden !== true).map(([key]) => key), [
|
|
@@ -7772,6 +7806,26 @@ function AutoCrudTable({ id, title, description, schema, resource, fields, table
|
|
|
7772
7806
|
return Object.keys(shape).filter((key) => !denySet.has(key));
|
|
7773
7807
|
}, [resolvedSchema, denyFields]);
|
|
7774
7808
|
const handleExport = React.useCallback(async (mode) => {
|
|
7809
|
+
if (onExport) {
|
|
7810
|
+
if (exportBusyRef.current || !exportTableRef.current) return;
|
|
7811
|
+
exportBusyRef.current = true;
|
|
7812
|
+
setExporting(true);
|
|
7813
|
+
try {
|
|
7814
|
+
const denied = new Set(denyFields);
|
|
7815
|
+
const columns = getVisibleExportColumns(exportTableRef.current, denyFields);
|
|
7816
|
+
const rows = mode === "selected" ? getSelectedRowsRef.current?.() : await resource.handlers.export?.();
|
|
7817
|
+
if (!rows || mode === "selected" && rows.length === 0) return;
|
|
7818
|
+
await onExport({
|
|
7819
|
+
mode,
|
|
7820
|
+
rows: denied.size ? rows.map((row) => Object.fromEntries(Object.entries(row).filter(([key]) => !denied.has(key)))) : rows,
|
|
7821
|
+
columns
|
|
7822
|
+
});
|
|
7823
|
+
} finally {
|
|
7824
|
+
exportBusyRef.current = false;
|
|
7825
|
+
setExporting(false);
|
|
7826
|
+
}
|
|
7827
|
+
return;
|
|
7828
|
+
}
|
|
7775
7829
|
const filename = title ?? "export";
|
|
7776
7830
|
const excludeColumns = denyFields;
|
|
7777
7831
|
if (mode === "selected") {
|
|
@@ -7792,18 +7846,30 @@ function AutoCrudTable({ id, title, description, schema, resource, fields, table
|
|
|
7792
7846
|
resource.handlers.export,
|
|
7793
7847
|
title,
|
|
7794
7848
|
denyFields,
|
|
7795
|
-
resolvedFields
|
|
7849
|
+
resolvedFields,
|
|
7850
|
+
onExport
|
|
7796
7851
|
]);
|
|
7797
7852
|
const handleExportClick = React.useCallback(async () => {
|
|
7798
7853
|
const mode = selectedCount > 0 ? "selected" : "filtered";
|
|
7854
|
+
if (onExport) return handleExport(mode);
|
|
7799
7855
|
setExporting(true);
|
|
7800
7856
|
try {
|
|
7801
7857
|
await handleExport(mode);
|
|
7802
7858
|
} finally {
|
|
7803
7859
|
setExporting(false);
|
|
7804
7860
|
}
|
|
7805
|
-
}, [
|
|
7861
|
+
}, [
|
|
7862
|
+
selectedCount,
|
|
7863
|
+
handleExport,
|
|
7864
|
+
onExport
|
|
7865
|
+
]);
|
|
7806
7866
|
const exportBatchActions = React.useMemo(() => {
|
|
7867
|
+
if (onExport) return registryBatchActions.map((action$1) => action$1.type === "export" && action$1.onClick === void 0 && !action$1.component ? {
|
|
7868
|
+
...action$1,
|
|
7869
|
+
onClick: () => {
|
|
7870
|
+
handleExport("selected").catch(reportExportError);
|
|
7871
|
+
}
|
|
7872
|
+
} : action$1);
|
|
7807
7873
|
if (!Object.values(resolvedFields).some((field) => field.format)) return registryBatchActions;
|
|
7808
7874
|
return registryBatchActions.map((action$1) => {
|
|
7809
7875
|
if (action$1.type !== "export" || action$1.onClick !== void 0 || Boolean(action$1.component)) return action$1;
|
|
@@ -7821,7 +7887,9 @@ function AutoCrudTable({ id, title, description, schema, resource, fields, table
|
|
|
7821
7887
|
}, [
|
|
7822
7888
|
registryBatchActions,
|
|
7823
7889
|
resolvedFields,
|
|
7824
|
-
denyFields
|
|
7890
|
+
denyFields,
|
|
7891
|
+
onExport,
|
|
7892
|
+
handleExport
|
|
7825
7893
|
]);
|
|
7826
7894
|
const toolbarRefresh = React.useMemo(() => {
|
|
7827
7895
|
const refresh = ownerRefreshAction?.onClick ?? resource.handlers.refresh;
|
|
@@ -7998,7 +8066,9 @@ function AutoCrudTable({ id, title, description, schema, resource, fields, table
|
|
|
7998
8066
|
if (type === "export" && canExport) return /* @__PURE__ */ jsxs(Button, {
|
|
7999
8067
|
variant: "outline",
|
|
8000
8068
|
size: "sm",
|
|
8001
|
-
onClick: overrides?.onClick ??
|
|
8069
|
+
onClick: overrides?.onClick ?? (!ownerExportAction?.onClick ? () => {
|
|
8070
|
+
toolbarExportData?.().catch(reportExportError);
|
|
8071
|
+
} : toolbarExportData),
|
|
8002
8072
|
disabled: exporting || selectedCount === 0 && !resource.handlers.export,
|
|
8003
8073
|
children: [exporting ? /* @__PURE__ */ jsx(Loader2, { className: "mr-2 h-4 w-4 animate-spin" }) : /* @__PURE__ */ jsx(Upload, { className: "mr-2 h-4 w-4" }), overrides?.label ?? (selectedCount > 0 ? locale.toolbar.exportSelected(selectedCount) : locale.toolbar.export)]
|
|
8004
8074
|
}, "export");
|
|
@@ -8077,7 +8147,8 @@ function AutoCrudTable({ id, title, description, schema, resource, fields, table
|
|
|
8077
8147
|
showDefaultExport: false,
|
|
8078
8148
|
initialSorting: defaultSort,
|
|
8079
8149
|
onSelectedRowsChange: handleSelectedRowsChange,
|
|
8080
|
-
getSelectedRows: getSelectedRowsRef
|
|
8150
|
+
getSelectedRows: getSelectedRowsRef,
|
|
8151
|
+
...onExport ? { exportTableRef } : {}
|
|
8081
8152
|
}),
|
|
8082
8153
|
rowActionDialog,
|
|
8083
8154
|
/* @__PURE__ */ jsx(CrudFormModal, {
|
|
@@ -9183,12 +9254,18 @@ function useAutoCrudResource({ router, schema, query: queryTransform, fields, op
|
|
|
9183
9254
|
schema
|
|
9184
9255
|
]);
|
|
9185
9256
|
const handleExport = useCallback(async () => {
|
|
9257
|
+
let result;
|
|
9186
9258
|
if (exportFetcher) {
|
|
9187
9259
|
const { page: _p, perPage: _pp,...exportParams } = queryInput ?? {};
|
|
9188
|
-
|
|
9189
|
-
}
|
|
9190
|
-
|
|
9191
|
-
|
|
9260
|
+
result = await exportFetcher(exportParams);
|
|
9261
|
+
} else if (exportQuery) {
|
|
9262
|
+
const response = await exportQuery.refetch();
|
|
9263
|
+
if (response.error) throw response.error;
|
|
9264
|
+
result = response.data;
|
|
9265
|
+
} else throw new Error("Export not supported");
|
|
9266
|
+
if (!result || !Array.isArray(result.data)) throw new Error("Export returned no data. Please retry.");
|
|
9267
|
+
if (result.hasMore !== false || result.total > result.data.length) throw new Error("Export results are incomplete. Narrow your filters and retry.");
|
|
9268
|
+
return result.data;
|
|
9192
9269
|
}, [
|
|
9193
9270
|
exportFetcher,
|
|
9194
9271
|
exportQuery,
|
|
@@ -9543,4 +9620,70 @@ function createMemoryDataSource(initialData = []) {
|
|
|
9543
9620
|
}
|
|
9544
9621
|
|
|
9545
9622
|
//#endregion
|
|
9546
|
-
|
|
9623
|
+
//#region src/lib/workbook.ts
|
|
9624
|
+
const MAX_ROWS = 1048576;
|
|
9625
|
+
const MAX_COLUMNS = 16384;
|
|
9626
|
+
const MAX_CELL_LENGTH = 32767;
|
|
9627
|
+
const MAX_LINE_FEEDS = 253;
|
|
9628
|
+
function validateCell(value) {
|
|
9629
|
+
if (typeof value === "string") {
|
|
9630
|
+
if (value.length > MAX_CELL_LENGTH) throw new RangeError(`Excel cells cannot exceed ${MAX_CELL_LENGTH} characters.`);
|
|
9631
|
+
if ((value.match(/\n/g)?.length ?? 0) > MAX_LINE_FEEDS) throw new RangeError(`Excel cells cannot exceed ${MAX_LINE_FEEDS} line feeds.`);
|
|
9632
|
+
} else if (value != null && (typeof value !== "number" || !Number.isFinite(value))) throw new TypeError("Workbook cells must contain text, finite numbers, or null.");
|
|
9633
|
+
}
|
|
9634
|
+
function validateSheets(sheets) {
|
|
9635
|
+
if (sheets.length === 0) throw new Error("A workbook needs at least one sheet.");
|
|
9636
|
+
const names = /* @__PURE__ */ new Set();
|
|
9637
|
+
for (const sheet of sheets) {
|
|
9638
|
+
if (!sheet.name || sheet.name.length > 31 || /[*?:\\/\[\]]/.test(sheet.name) || sheet.name.startsWith("'") || sheet.name.endsWith("'")) throw new Error("Worksheet names must be valid Excel names of 1–31 characters.");
|
|
9639
|
+
const name = sheet.name.toLowerCase();
|
|
9640
|
+
if (names.has(name)) throw new Error("Worksheet names must be unique.");
|
|
9641
|
+
names.add(name);
|
|
9642
|
+
if (sheet.rows.length + 1 > MAX_ROWS) throw new RangeError(`Excel worksheets cannot exceed ${MAX_ROWS} rows including headers.`);
|
|
9643
|
+
if (sheet.headers.length > MAX_COLUMNS) throw new RangeError(`Excel worksheets cannot exceed ${MAX_COLUMNS} columns.`);
|
|
9644
|
+
sheet.headers.forEach(validateCell);
|
|
9645
|
+
for (const row of sheet.rows) {
|
|
9646
|
+
if (row.length > MAX_COLUMNS) throw new RangeError(`Excel worksheets cannot exceed ${MAX_COLUMNS} columns.`);
|
|
9647
|
+
row.forEach(validateCell);
|
|
9648
|
+
}
|
|
9649
|
+
}
|
|
9650
|
+
}
|
|
9651
|
+
/** Internal byte builder, exported from this module for real workbook roundtrip tests. */
|
|
9652
|
+
async function buildWorkbookBytes(sheets) {
|
|
9653
|
+
validateSheets(sheets);
|
|
9654
|
+
const { default: ExcelJS } = await import("exceljs");
|
|
9655
|
+
const workbook = new ExcelJS.Workbook();
|
|
9656
|
+
for (const sheet of sheets) {
|
|
9657
|
+
const worksheet = workbook.addWorksheet(sheet.name);
|
|
9658
|
+
const append = (values) => {
|
|
9659
|
+
worksheet.addRow(values.map((value) => value ?? null)).eachCell((cell, column) => {
|
|
9660
|
+
const numberFormat = sheet.numberFormats?.[column - 1];
|
|
9661
|
+
if (typeof cell.value === "number" && numberFormat !== void 0) cell.numFmt = numberFormat;
|
|
9662
|
+
if (typeof cell.value === "string" && /[\r\n]/.test(cell.value)) cell.alignment = { wrapText: true };
|
|
9663
|
+
});
|
|
9664
|
+
};
|
|
9665
|
+
append(sheet.headers);
|
|
9666
|
+
sheet.rows.forEach(append);
|
|
9667
|
+
}
|
|
9668
|
+
return new Uint8Array(await workbook.xlsx.writeBuffer()).buffer;
|
|
9669
|
+
}
|
|
9670
|
+
/** Download a real XLSX workbook. Errors reject; no CSV or raw-value fallback. */
|
|
9671
|
+
async function exportWorkbook(sheets, { filename }) {
|
|
9672
|
+
const bytes = await buildWorkbookBytes(sheets);
|
|
9673
|
+
const blob = new Blob([bytes], { type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" });
|
|
9674
|
+
const url = URL.createObjectURL(blob);
|
|
9675
|
+
const link = document.createElement("a");
|
|
9676
|
+
try {
|
|
9677
|
+
link.href = url;
|
|
9678
|
+
link.download = /\.xlsx$/i.test(filename) ? filename : `${filename}.xlsx`;
|
|
9679
|
+
link.hidden = true;
|
|
9680
|
+
document.body.appendChild(link);
|
|
9681
|
+
link.click();
|
|
9682
|
+
} finally {
|
|
9683
|
+
link.remove();
|
|
9684
|
+
setTimeout(() => URL.revokeObjectURL(url), 0);
|
|
9685
|
+
}
|
|
9686
|
+
}
|
|
9687
|
+
|
|
9688
|
+
//#endregion
|
|
9689
|
+
export { AutoCrudTable, AutoForm, AutoTable, AutoTableActionBar, AutoTableSimpleFilters, CrudFormModal, DataTable, DataTableAdvancedToolbar, DataTableColumnHeader, DataTableFacetedFilter, DataTablePagination, DataTableToolbar, DataTableViewOptions, ExportDialog, ImportDialog, MultiCombobox, SchemaAdapter, Select, actionPositionSchema, cn, coerceRowValues, compareOrder, components, createActionsColumn, createEditFormSchema, createFormSchema, createMemoryDataSource, createSelectColumn, createTRPCDataSource, createTableSchema, crudActions, dataSources, dataToCSV, deDE, downloadCSVTemplate, enUS, esES, exportAllToCSV, exportTableToCSV, exportWorkbook, formComponents, formatDate, frFR, generateCSVTemplate, getUrlParams, humanize, jaJP, koKR, matchesPosition, noopToastAdapter, normalizeDataSourceConfig, normalizeOptions, parseAsArrayOf, parseAsInteger, parseAsString, parseAsStringEnum, parseCSV, parseImportFile, parseJSON, parseZodField, positionActions, resolveLocale, setDateFormatter, setSearchParams, setToolbarResolver, tablePagination, useAutoCrudResource, useDataTable, useQueryState, useQueryStates, useReadableFilters, useUrlState, useUrlStates, zhCN };
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@wordrhyme/auto-crud",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "1.
|
|
4
|
+
"version": "1.12.0",
|
|
5
5
|
"description": "Schema-first CRUD components with auto-generated tables and forms",
|
|
6
6
|
"author": "wordrhyme",
|
|
7
7
|
"license": "MIT",
|
|
@@ -66,6 +66,7 @@
|
|
|
66
66
|
"class-variance-authority": "^0.7.1",
|
|
67
67
|
"clsx": "^2.1.1",
|
|
68
68
|
"cmdk": "^1.1.1",
|
|
69
|
+
"exceljs": "4.4.0",
|
|
69
70
|
"lucide-react": "0.553.0",
|
|
70
71
|
"nanoid": "^5.1.6",
|
|
71
72
|
"tailwind-merge": "^3.5.0",
|
|
@@ -93,11 +94,11 @@
|
|
|
93
94
|
"tsdown": "^0.15.12",
|
|
94
95
|
"typescript": "^5.9.3",
|
|
95
96
|
"vitest": "^3.2.4",
|
|
97
|
+
"@internal/eslint-config": "0.3.0",
|
|
98
|
+
"@internal/prettier-config": "0.0.1",
|
|
96
99
|
"@internal/tsdown-config": "0.1.0",
|
|
97
|
-
"@internal/vitest-config": "0.1.0",
|
|
98
100
|
"@internal/tsconfig": "0.1.0",
|
|
99
|
-
"@internal/
|
|
100
|
-
"@internal/eslint-config": "0.3.0"
|
|
101
|
+
"@internal/vitest-config": "0.1.0"
|
|
101
102
|
},
|
|
102
103
|
"prettier": "@internal/prettier-config",
|
|
103
104
|
"scripts": {
|