@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 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
- }, [selectedCount, handleExport]);
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 ?? toolbarExportData,
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
- return (await exportFetcher(exportParams))?.data ?? [];
9204
- }
9205
- if (exportQuery) return (await exportQuery.refetch())?.data?.data ?? [];
9206
- throw new Error("Export not supported");
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 react_jsx_runtime9 from "react/jsx-runtime";
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>): react_jsx_runtime9.JSX.Element;
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>): react_jsx_runtime9.JSX.Element;
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
- }: AutoTableProps<T>): react_jsx_runtime9.JSX.Element;
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
- * AutoCrudTable Props 接口
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
- }: AutoCrudTableProps<TSchema>): react_jsx_runtime9.JSX.Element;
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>): react_jsx_runtime9.JSX.Element;
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>): react_jsx_runtime9.JSX.Element | null;
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>): react_jsx_runtime9.JSX.Element;
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): react_jsx_runtime9.JSX.Element;
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): react_jsx_runtime9.JSX.Element;
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>): react_jsx_runtime9.JSX.Element;
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>): react_jsx_runtime9.JSX.Element;
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>): react_jsx_runtime9.JSX.Element;
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>): react_jsx_runtime9.JSX.Element;
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>): react_jsx_runtime9.JSX.Element;
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>): react_jsx_runtime9.JSX.Element;
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
- export { type ActionConfig, type ActionItem, type ActionPosition, type ActionsColumnConfig, type AutoCrudActionConfig, type AutoCrudActionsConfig, type AutoCrudDataSourceConfig, type AutoCrudDataSourceContext, type AutoCrudDataSourceEntry, type AutoCrudDataSourceLoader, type AutoCrudDataSourceRegistration, 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, actionPositionSchema, cn, coerceRowValues, compareOrder, components, createActionsColumn, createEditFormSchema, createFormSchema, createMemoryDataSource, createSelectColumn, createTRPCDataSource, createTableSchema, crudActions, dataSources, dataToCSV, deDE, downloadCSVTemplate, enUS, esES, exportAllToCSV, exportTableToCSV, 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 };
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
- * AutoCrudTable Props 接口
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
- export { type ActionConfig, type ActionItem, type ActionPosition, type ActionsColumnConfig, type AutoCrudActionConfig, type AutoCrudActionsConfig, type AutoCrudDataSourceConfig, type AutoCrudDataSourceContext, type AutoCrudDataSourceEntry, type AutoCrudDataSourceLoader, type AutoCrudDataSourceRegistration, 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, actionPositionSchema, cn, coerceRowValues, compareOrder, components, createActionsColumn, createEditFormSchema, createFormSchema, createMemoryDataSource, createSelectColumn, createTRPCDataSource, createTableSchema, crudActions, dataSources, dataToCSV, deDE, downloadCSVTemplate, enUS, esES, exportAllToCSV, exportTableToCSV, 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 };
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
- }, [selectedCount, handleExport]);
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 ?? toolbarExportData,
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
- return (await exportFetcher(exportParams))?.data ?? [];
9189
- }
9190
- if (exportQuery) return (await exportQuery.refetch())?.data?.data ?? [];
9191
- throw new Error("Export not supported");
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
- 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, 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 };
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.11.2",
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/prettier-config": "0.0.1",
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": {