@wordrhyme/auto-crud 1.8.2 → 1.10.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/README.md +59 -0
- package/dist/index.cjs +148 -41
- package/dist/index.d.cts +52 -35
- package/dist/index.d.ts +52 -35
- package/dist/index.js +169 -62
- package/package.json +6 -6
package/README.md
CHANGED
|
@@ -264,6 +264,8 @@ interface AutoCrudTableProps<TSchema> {
|
|
|
264
264
|
table?: {
|
|
265
265
|
hidden?: string[]; // 隐藏的列
|
|
266
266
|
overrides?: Record<string, any>; // 列覆盖配置
|
|
267
|
+
getRowId?: (record: z.output<TSchema>) => string; // 稳定行标识;展开时必填
|
|
268
|
+
expandedRow?: (record: z.output<TSchema>) => React.ReactNode; // 展开行内容
|
|
267
269
|
filterModes?: FilterMode | FilterMode[]; // 过滤模式
|
|
268
270
|
batchFields?: (string | BatchUpdateField)[]; // 批量更新字段
|
|
269
271
|
/** @deprecated 新代码请使用 actions.batch */
|
|
@@ -294,6 +296,30 @@ interface AutoCrudTableProps<TSchema> {
|
|
|
294
296
|
}
|
|
295
297
|
```
|
|
296
298
|
|
|
299
|
+
#### 展开行详情
|
|
300
|
+
|
|
301
|
+
配置 `table.expandedRow` 后,表格自动添加展开箭头。默认全部收起,允许同时展开多行。
|
|
302
|
+
必须提供 TanStack Table 的 `getRowId`,使用稳定且唯一的业务主键,避免分页、排序后展开错位。
|
|
303
|
+
|
|
304
|
+
```tsx
|
|
305
|
+
<AutoCrudTable
|
|
306
|
+
schema={orderSchema}
|
|
307
|
+
resource={resource}
|
|
308
|
+
table={{
|
|
309
|
+
getRowId: (order) => order.id,
|
|
310
|
+
expandedRow: (order) => <OrderDetails orderId={order.id} />,
|
|
311
|
+
}}
|
|
312
|
+
/>
|
|
313
|
+
```
|
|
314
|
+
|
|
315
|
+
回调接收当前行的原始记录,并同步返回 React 内容。详情组件只在展开时挂载,收起时卸载;
|
|
316
|
+
可由详情组件按订单 ID 查询数据并处理加载、错误和重试,也可直接使用记录中已有的明细。
|
|
317
|
+
明细数据及其字段权限由业务组件和现有服务负责。
|
|
318
|
+
|
|
319
|
+
详情区域跨全部可见列,不计入分页条数,也不参与主表选择或导出。
|
|
320
|
+
数据更新时,展开状态遵循 TanStack Table 默认重置行为。
|
|
321
|
+
`AutoTable` 同样支持 `getRowId` 和 `expandedRow`;直接使用 `DataTable` 时,需自行配置 TanStack 展开能力及触发按钮。
|
|
322
|
+
|
|
297
323
|
### `useAutoCrudResource()`
|
|
298
324
|
|
|
299
325
|
连接数据源到组件的 Hook。
|
|
@@ -1651,6 +1677,14 @@ MIT © [wordrhyme](https://github.com/denvey/wordrhyme-components)
|
|
|
1651
1677
|
- [Notion](https://notion.so) - 高级筛选器设计
|
|
1652
1678
|
- [Linear](https://linear.app) - 命令面板设计
|
|
1653
1679
|
|
|
1680
|
+
### Status badge colors
|
|
1681
|
+
|
|
1682
|
+
For `table.display: "badge"`, a scalar field's `table.options` entries may set `badgeTone` to `neutral`, `success`, `warning`, or `info`. This renders a tinted, outlined rounded label with a decorative dot. Labels remain readable without color; omitted tones retain the default badge. `display: "text"` is unaffected. Domain-specific state-to-tone mappings belong in the consuming plugin metadata.
|
|
1683
|
+
|
|
1684
|
+
Registered custom actions may set `before: "delete"` (or another builtin action type) to appear immediately before that action. If the builtin is absent, the existing `position` fallback applies. This only controls ordering and does not add or grant the target action.
|
|
1685
|
+
|
|
1686
|
+
Custom row components receive `MenuItem` in their action context. Use this supplied primitive instead of importing a menu item from another UI bundle: the menu root and item must share the same Radix runtime context. Prevent the select event's default dismissal when the component owns an open dialog, so the menu does not unmount the workflow.
|
|
1687
|
+
|
|
1654
1688
|
### Detail presentation
|
|
1655
1689
|
|
|
1656
1690
|
Details automatically share field labels, `enum`/`dataSource` mappings and
|
|
@@ -1711,6 +1745,31 @@ is pinned to the already-published alpha.19 for reproducible ESM tests and type
|
|
|
1711
1745
|
checks until alpha.20 is published; it is not the supported CommonJS runtime.
|
|
1712
1746
|
The CommonJS export/identity regression belongs to the UI provider repository.
|
|
1713
1747
|
|
|
1748
|
+
### Filter-only fields
|
|
1749
|
+
|
|
1750
|
+
Use `table: false` with an explicit `filter` to keep a field in the standard
|
|
1751
|
+
filter bar without rendering a data column. Shared `hidden`, permission `deny`,
|
|
1752
|
+
and explicit table exclusions still hide the filter. Pass the same filter
|
|
1753
|
+
configuration to the resource so URL values use the same variants/operators:
|
|
1754
|
+
|
|
1755
|
+
```tsx
|
|
1756
|
+
const fields = {
|
|
1757
|
+
customerId: {
|
|
1758
|
+
label: 'Customer',
|
|
1759
|
+
table: false as const,
|
|
1760
|
+
form: false as const,
|
|
1761
|
+
filter: { variant: 'select' as const, dataSource: 'customers', index: 20 },
|
|
1762
|
+
},
|
|
1763
|
+
};
|
|
1764
|
+
const resource = useAutoCrudResource({ router, schema, fields });
|
|
1765
|
+
return <AutoCrudTable schema={schema} resource={resource} fields={fields} />;
|
|
1766
|
+
```
|
|
1767
|
+
|
|
1768
|
+
The field must exist in the schema or resource metadata, and the server must
|
|
1769
|
+
resolve its filter. A data source loads options; it does not translate query
|
|
1770
|
+
conditions. Standard filters share URL state, Reset, and pagination. Dynamically
|
|
1771
|
+
registered schema fields hydrate existing URL values without resetting the page.
|
|
1772
|
+
|
|
1714
1773
|
### 按 ID 隐藏或替换自定义操作
|
|
1715
1774
|
|
|
1716
1775
|
拥有方为 custom 操作声明稳定 `id` 后,扩展可以在相同 CRUD target 和操作区域
|
package/dist/index.cjs
CHANGED
|
@@ -566,7 +566,7 @@ function formatDate(date, opts = {}, preset) {
|
|
|
566
566
|
|
|
567
567
|
//#endregion
|
|
568
568
|
//#region src/components/data-table/data-table.tsx
|
|
569
|
-
function DataTable({ table, actionBar, pagination, children, className, classNames,...props }) {
|
|
569
|
+
function DataTable({ table, actionBar, pagination, expandedRow, children, className, classNames,...props }) {
|
|
570
570
|
useDateFormatterVersion();
|
|
571
571
|
const columns = [
|
|
572
572
|
...table.getLeftVisibleLeafColumns(),
|
|
@@ -606,7 +606,7 @@ function DataTable({ table, actionBar, pagination, children, className, classNam
|
|
|
606
606
|
}),
|
|
607
607
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(__wordrhyme_shadcn.TableBody, {
|
|
608
608
|
className: classNames?.tbody,
|
|
609
|
-
children: table.getRowModel().rows?.length ? table.getRowModel().rows.map((row) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__wordrhyme_shadcn.TableRow, {
|
|
609
|
+
children: table.getRowModel().rows?.length ? table.getRowModel().rows.map((row) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(__wordrhyme_shadcn.TableRow, {
|
|
610
610
|
className: classNames?.tr,
|
|
611
611
|
"data-state": row.getIsSelected() && "selected",
|
|
612
612
|
children: row.getVisibleCells().map((cell) => {
|
|
@@ -617,7 +617,14 @@ function DataTable({ table, actionBar, pagination, children, className, classNam
|
|
|
617
617
|
children: (0, __tanstack_react_table.flexRender)(cell.column.columnDef.cell, cell.getContext())
|
|
618
618
|
}, cell.id);
|
|
619
619
|
})
|
|
620
|
-
}, row.
|
|
620
|
+
}), expandedRow && row.getCanExpand() && row.getIsExpanded() && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__wordrhyme_shadcn.TableRow, {
|
|
621
|
+
className: classNames?.tr,
|
|
622
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__wordrhyme_shadcn.TableCell, {
|
|
623
|
+
colSpan: row.getVisibleCells().length,
|
|
624
|
+
className: classNames?.td,
|
|
625
|
+
children: expandedRow(row.original)
|
|
626
|
+
})
|
|
627
|
+
})] }, row.id)) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__wordrhyme_shadcn.TableRow, {
|
|
621
628
|
className: classNames?.tr,
|
|
622
629
|
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__wordrhyme_shadcn.TableCell, {
|
|
623
630
|
colSpan: table.getAllColumns().length,
|
|
@@ -1012,6 +1019,11 @@ function useReadableFilters(columns, options = {}) {
|
|
|
1012
1019
|
filtersRef.current = initial;
|
|
1013
1020
|
return initial;
|
|
1014
1021
|
});
|
|
1022
|
+
react.useEffect(() => {
|
|
1023
|
+
const next = parseReadableFilters(getUrlParams(), columnSnapshot);
|
|
1024
|
+
filtersRef.current = next;
|
|
1025
|
+
setFilters((previous) => JSON.stringify(previous) === JSON.stringify(next) ? previous : next);
|
|
1026
|
+
}, [columnSnapshot]);
|
|
1015
1027
|
react.useEffect(() => {
|
|
1016
1028
|
const handleUrlChange = () => {
|
|
1017
1029
|
if (lastWrittenUrlRef.current !== null && window.location.search === lastWrittenUrlRef.current) {
|
|
@@ -3901,9 +3913,12 @@ function coerceRowValues(rows, schema) {
|
|
|
3901
3913
|
|
|
3902
3914
|
//#endregion
|
|
3903
3915
|
//#region src/lib/export.ts
|
|
3916
|
+
function getExportColumnIds(table) {
|
|
3917
|
+
return table.getAllLeafColumns().filter((column) => column.accessorFn).map((column) => column.id);
|
|
3918
|
+
}
|
|
3904
3919
|
function exportTableToCSV(table, opts = {}) {
|
|
3905
3920
|
const { filename = "table", excludeColumns = [], onlySelected = false } = opts;
|
|
3906
|
-
const headers = table
|
|
3921
|
+
const headers = getExportColumnIds(table).filter((id) => !excludeColumns.includes(id));
|
|
3907
3922
|
downloadCSV([headers.join(","), ...(onlySelected ? table.getFilteredSelectedRowModel().rows : table.getRowModel().rows).map((row) => headers.map((header) => {
|
|
3908
3923
|
const cellValue = row.getValue(header);
|
|
3909
3924
|
return typeof cellValue === "string" ? `"${cellValue.replace(/"/g, "\"\"")}"` : cellValue;
|
|
@@ -4414,7 +4429,9 @@ const filterModeConfig = {
|
|
|
4414
4429
|
tooltip: "Linear-style command filters"
|
|
4415
4430
|
}
|
|
4416
4431
|
};
|
|
4417
|
-
function AutoTable({ schema, data, pageCount = 1, pagination, total, overrides, classNames, enableRowSelection = true, exclude, actions, pinnedColumns, filterMode, search = false, onDeleteSelected, onUpdateSelected, batchUpdateFields, actionBarExtra, actionBarActions, deleteConfirmation, initialSorting, enableExport = true, showDefaultExport = true, onSelectedCountChange, onSelectedRowsChange, getSelectedRows }) {
|
|
4432
|
+
function AutoTable({ schema, data, getRowId, expandedRow, pageCount = 1, pagination, total, overrides, classNames, enableRowSelection = true, exclude, filterOnly, actions, pinnedColumns, filterMode, search = false, onDeleteSelected, onUpdateSelected, batchUpdateFields, actionBarExtra, actionBarActions, deleteConfirmation, initialSorting, enableExport = true, showDefaultExport = true, onSelectedCountChange, onSelectedRowsChange, getSelectedRows }) {
|
|
4433
|
+
if (expandedRow && !getRowId) throw new Error("AutoTable requires getRowId when expandedRow is provided.");
|
|
4434
|
+
const enableExpansion = !!expandedRow;
|
|
4418
4435
|
const modes = filterMode ? Array.isArray(filterMode) ? filterMode : [filterMode] : DEFAULT_MODES;
|
|
4419
4436
|
const [currentMode, setCurrentMode] = useQueryState("filterMode", parseAsStringEnum(modes).withDefault(modes[0] ?? "simple"));
|
|
4420
4437
|
const showToggle = modes.length > 1;
|
|
@@ -4428,36 +4445,85 @@ function AutoTable({ schema, data, pageCount = 1, pagination, total, overrides,
|
|
|
4428
4445
|
});
|
|
4429
4446
|
const searchConfig = search && typeof search === "object" ? search : {};
|
|
4430
4447
|
const enableAdvancedFilter = true;
|
|
4431
|
-
const columns = (0, react.useMemo)(() => {
|
|
4448
|
+
const { columns, expandColumnId } = (0, react.useMemo)(() => {
|
|
4449
|
+
const columnOverrides = Object.assign({}, overrides);
|
|
4450
|
+
for (const key of filterOnly ?? []) {
|
|
4451
|
+
const field = key;
|
|
4452
|
+
Object.assign(columnOverrides, { [field]: {
|
|
4453
|
+
...columnOverrides[field],
|
|
4454
|
+
hidden: false
|
|
4455
|
+
} });
|
|
4456
|
+
}
|
|
4432
4457
|
const dataColumns = createTableSchema(schema, {
|
|
4433
|
-
overrides,
|
|
4458
|
+
overrides: columnOverrides,
|
|
4434
4459
|
exclude
|
|
4460
|
+
}).map((column) => {
|
|
4461
|
+
const key = column.id ?? ("accessorKey" in column ? String(column.accessorKey) : "");
|
|
4462
|
+
return filterOnly?.includes(key) ? {
|
|
4463
|
+
...column,
|
|
4464
|
+
enableHiding: false,
|
|
4465
|
+
enableSorting: false
|
|
4466
|
+
} : column;
|
|
4435
4467
|
});
|
|
4436
4468
|
const result = enableRowSelection ? [createSelectColumn(), ...dataColumns] : dataColumns;
|
|
4469
|
+
let expandColumnId$1 = "expand";
|
|
4470
|
+
if (enableExpansion) {
|
|
4471
|
+
while (expandColumnId$1 in schema.shape || result.some((column) => column.id === expandColumnId$1)) expandColumnId$1 = `_${expandColumnId$1}`;
|
|
4472
|
+
result.unshift({
|
|
4473
|
+
id: expandColumnId$1,
|
|
4474
|
+
header: () => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
4475
|
+
className: "sr-only",
|
|
4476
|
+
children: "Row details"
|
|
4477
|
+
}),
|
|
4478
|
+
cell: ({ row }) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__wordrhyme_shadcn.Button, {
|
|
4479
|
+
type: "button",
|
|
4480
|
+
variant: "ghost",
|
|
4481
|
+
size: "icon-sm",
|
|
4482
|
+
"aria-label": row.getIsExpanded() ? "Collapse row" : "Expand row",
|
|
4483
|
+
"aria-expanded": row.getIsExpanded(),
|
|
4484
|
+
onClick: row.getToggleExpandedHandler(),
|
|
4485
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(lucide_react.ChevronRightIcon, { className: row.getIsExpanded() ? "size-4 rotate-90" : "size-4" })
|
|
4486
|
+
}),
|
|
4487
|
+
enableHiding: false,
|
|
4488
|
+
enableSorting: false,
|
|
4489
|
+
size: 40
|
|
4490
|
+
});
|
|
4491
|
+
}
|
|
4437
4492
|
if (actions) result.push(createActionsColumn(actions));
|
|
4438
|
-
return
|
|
4493
|
+
return {
|
|
4494
|
+
columns: result,
|
|
4495
|
+
expandColumnId: expandColumnId$1
|
|
4496
|
+
};
|
|
4439
4497
|
}, [
|
|
4440
4498
|
schema,
|
|
4441
4499
|
overrides,
|
|
4442
4500
|
enableRowSelection,
|
|
4501
|
+
enableExpansion,
|
|
4443
4502
|
exclude,
|
|
4444
|
-
actions
|
|
4503
|
+
actions,
|
|
4504
|
+
filterOnly
|
|
4445
4505
|
]);
|
|
4446
4506
|
const stableInitialState = (0, react.useMemo)(() => ({
|
|
4447
4507
|
sorting: initialSorting,
|
|
4448
4508
|
columnPinning: pinnedColumns ?? {
|
|
4449
|
-
left: enableRowSelection ? ["select"] : [],
|
|
4509
|
+
left: [...enableExpansion ? [expandColumnId] : [], ...enableRowSelection ? ["select"] : []],
|
|
4450
4510
|
right: actions ? ["actions"] : []
|
|
4451
4511
|
}
|
|
4452
4512
|
}), [
|
|
4453
4513
|
initialSorting,
|
|
4454
4514
|
pinnedColumns,
|
|
4455
4515
|
enableRowSelection,
|
|
4516
|
+
enableExpansion,
|
|
4517
|
+
expandColumnId,
|
|
4456
4518
|
actions
|
|
4457
4519
|
]);
|
|
4520
|
+
const expandedRowModel = (0, react.useMemo)(() => (0, __tanstack_react_table.getExpandedRowModel)(), []);
|
|
4458
4521
|
const { table, shallow, debounceMs, throttleMs } = useDataTable({
|
|
4459
4522
|
data,
|
|
4460
4523
|
columns,
|
|
4524
|
+
getRowId,
|
|
4525
|
+
getRowCanExpand: () => enableExpansion,
|
|
4526
|
+
getExpandedRowModel: enableExpansion ? expandedRowModel : void 0,
|
|
4461
4527
|
pageCount,
|
|
4462
4528
|
...total !== void 0 ? { rowCount: total } : {},
|
|
4463
4529
|
enableAdvancedFilter,
|
|
@@ -4465,6 +4531,16 @@ function AutoTable({ schema, data, pageCount = 1, pagination, total, overrides,
|
|
|
4465
4531
|
shallow: false,
|
|
4466
4532
|
clearOnDefault: true
|
|
4467
4533
|
});
|
|
4534
|
+
(0, react.useEffect)(() => {
|
|
4535
|
+
if (!filterOnly?.length) return;
|
|
4536
|
+
table.setColumnVisibility((previous) => {
|
|
4537
|
+
if (filterOnly.every((key) => previous[key] === false)) return previous;
|
|
4538
|
+
return {
|
|
4539
|
+
...previous,
|
|
4540
|
+
...Object.fromEntries(filterOnly.map((key) => [key, false]))
|
|
4541
|
+
};
|
|
4542
|
+
});
|
|
4543
|
+
}, [table, filterOnly]);
|
|
4468
4544
|
const rowSelection = table.getState().rowSelection;
|
|
4469
4545
|
const columnFilters = table.getState().columnFilters;
|
|
4470
4546
|
const selectedRows = (0, react.useMemo)(() => table.getFilteredSelectedRowModel().rows.map((row) => row.original), [
|
|
@@ -4579,7 +4655,8 @@ function AutoTable({ schema, data, pageCount = 1, pagination, total, overrides,
|
|
|
4579
4655
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(DataTable, {
|
|
4580
4656
|
pagination,
|
|
4581
4657
|
table,
|
|
4582
|
-
classNames
|
|
4658
|
+
classNames,
|
|
4659
|
+
expandedRow
|
|
4583
4660
|
}),
|
|
4584
4661
|
enableRowSelection && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(AutoTableActionBar, {
|
|
4585
4662
|
table,
|
|
@@ -4905,6 +4982,30 @@ function normalizeHasMore(result) {
|
|
|
4905
4982
|
|
|
4906
4983
|
//#endregion
|
|
4907
4984
|
//#region src/lib/field-config.ts
|
|
4985
|
+
function mergeFieldPart(base, override) {
|
|
4986
|
+
if (override === void 0) return base;
|
|
4987
|
+
if (override === false || base === false || typeof base !== "object" || typeof override !== "object" || base === null || override === null || Array.isArray(base) || Array.isArray(override)) return override;
|
|
4988
|
+
return {
|
|
4989
|
+
...base,
|
|
4990
|
+
...override
|
|
4991
|
+
};
|
|
4992
|
+
}
|
|
4993
|
+
function mergeFieldConfig(base, override) {
|
|
4994
|
+
return {
|
|
4995
|
+
...base,
|
|
4996
|
+
...override,
|
|
4997
|
+
enum: override?.enum ?? base?.enum,
|
|
4998
|
+
dataSource: override?.dataSource ?? base?.dataSource,
|
|
4999
|
+
table: mergeFieldPart(base?.table, override?.table),
|
|
5000
|
+
filter: mergeFieldPart(base?.filter, override?.filter),
|
|
5001
|
+
form: mergeFieldPart(base?.form, override?.form)
|
|
5002
|
+
};
|
|
5003
|
+
}
|
|
5004
|
+
function mergeFields(base, override) {
|
|
5005
|
+
if (!base && !override) return void 0;
|
|
5006
|
+
const keys = new Set([...Object.keys(base ?? {}), ...Object.keys(override ?? {})]);
|
|
5007
|
+
return Object.fromEntries(Array.from(keys).map((key) => [key, mergeFieldConfig(base?.[key], override?.[key])]));
|
|
5008
|
+
}
|
|
4908
5009
|
function normalizeFieldOptions$1(options) {
|
|
4909
5010
|
if (!options || options.length === 0) return void 0;
|
|
4910
5011
|
return options.map((option) => ({
|
|
@@ -6292,7 +6393,6 @@ function resolveActions$1(targetId, zone, ownerActions) {
|
|
|
6292
6393
|
const ownerCustomIds = new Set(ownerActions.flatMap((action$1) => isCustomAction$1(action$1) && action$1.id ? [action$1.id] : []));
|
|
6293
6394
|
const baseActions = ownerActions.filter((action$1) => !action$1.hidden && !isMaskedCustom(action$1)).map((action$1) => ({ ...isCustomAction$1(action$1) && action$1.id ? customOverrides.get(action$1.id)?.action ?? action$1 : action$1 }));
|
|
6294
6395
|
const isReplacedCustom = ({ action: action$1, seq: seq$1 }) => isCustomAction$1(action$1) && Boolean(action$1.id && (ownerCustomIds.has(action$1.id) || customOverrides.get(action$1.id)?.seq !== seq$1));
|
|
6295
|
-
if (registered.length === 0) return require_position.positionActions(baseActions).map(withoutRegistryMeta);
|
|
6296
6396
|
const nextActions = [...baseActions];
|
|
6297
6397
|
const extraActions = [];
|
|
6298
6398
|
const groups = /* @__PURE__ */ new Map();
|
|
@@ -6319,7 +6419,13 @@ function resolveActions$1(targetId, zone, ownerActions) {
|
|
|
6319
6419
|
const items = group.sort((left, right) => left.seq - right.seq).flatMap((entry) => {
|
|
6320
6420
|
const { action: action$1 } = entry;
|
|
6321
6421
|
if (action$1.hidden || isReplacedCustom(entry)) return [];
|
|
6322
|
-
if (isCustomAction$1(action$1))
|
|
6422
|
+
if (isCustomAction$1(action$1)) {
|
|
6423
|
+
if (action$1.before) {
|
|
6424
|
+
extraActions.push({ ...action$1 });
|
|
6425
|
+
return [];
|
|
6426
|
+
}
|
|
6427
|
+
return [{ ...action$1 }];
|
|
6428
|
+
}
|
|
6323
6429
|
const builtin = nextActions.find((item) => item.type === action$1.type);
|
|
6324
6430
|
return builtin ? [builtin] : [];
|
|
6325
6431
|
});
|
|
@@ -6340,6 +6446,15 @@ function resolveActions$1(targetId, zone, ownerActions) {
|
|
|
6340
6446
|
}
|
|
6341
6447
|
} else extraActions.push(...items);
|
|
6342
6448
|
}
|
|
6449
|
+
for (const action$1 of [...nextActions, ...extraActions]) {
|
|
6450
|
+
if (!isCustomAction$1(action$1) || !action$1.before) continue;
|
|
6451
|
+
const anchor = nextActions.find((item) => !isCustomAction$1(item) && item.type === action$1.before);
|
|
6452
|
+
if (anchor) action$1.position = {
|
|
6453
|
+
anchor: anchor.id ?? anchor.type,
|
|
6454
|
+
side: "before"
|
|
6455
|
+
};
|
|
6456
|
+
else if (typeof action$1.position === "object") action$1.position = "end";
|
|
6457
|
+
}
|
|
6343
6458
|
return require_position.positionActions(nextActions, extraActions).map(withoutRegistryMeta);
|
|
6344
6459
|
}
|
|
6345
6460
|
const crudActions = {
|
|
@@ -6489,30 +6604,6 @@ function resolveToolbarActionsWithResolver(targetId, ownerActions, context) {
|
|
|
6489
6604
|
const resolver = autoCrudToolbarResolver;
|
|
6490
6605
|
return targetId !== void 0 && targetId.length > 0 && resolver !== null ? resolver(targetId, ownerActions, context) : ownerActions;
|
|
6491
6606
|
}
|
|
6492
|
-
function mergeFieldPart(base, override) {
|
|
6493
|
-
if (override === void 0) return base;
|
|
6494
|
-
if (override === false || base === false || typeof base !== "object" || typeof override !== "object" || base === null || override === null || Array.isArray(base) || Array.isArray(override)) return override;
|
|
6495
|
-
return {
|
|
6496
|
-
...base,
|
|
6497
|
-
...override
|
|
6498
|
-
};
|
|
6499
|
-
}
|
|
6500
|
-
function mergeFieldConfig(base, override) {
|
|
6501
|
-
return {
|
|
6502
|
-
...base,
|
|
6503
|
-
...override,
|
|
6504
|
-
enum: override?.enum ?? base?.enum,
|
|
6505
|
-
dataSource: override?.dataSource ?? base?.dataSource,
|
|
6506
|
-
table: mergeFieldPart(base?.table, override?.table),
|
|
6507
|
-
filter: mergeFieldPart(base?.filter, override?.filter),
|
|
6508
|
-
form: mergeFieldPart(base?.form, override?.form)
|
|
6509
|
-
};
|
|
6510
|
-
}
|
|
6511
|
-
function mergeFields(base, override) {
|
|
6512
|
-
if (!base && !override) return void 0;
|
|
6513
|
-
const keys = new Set([...Object.keys(base ?? {}), ...Object.keys(override ?? {})]);
|
|
6514
|
-
return Object.fromEntries(Array.from(keys).map((key) => [key, mergeFieldConfig(base?.[key], override?.[key])]));
|
|
6515
|
-
}
|
|
6516
6607
|
function applyResourceTablePresentation(fields, resourceFields) {
|
|
6517
6608
|
if (!fields || !resourceFields) return fields;
|
|
6518
6609
|
return Object.fromEntries(Object.entries(fields).map(([key, config]) => {
|
|
@@ -7414,6 +7505,12 @@ function AutoCrudTable({ id, title, description, schema, resource, fields, table
|
|
|
7414
7505
|
const [exporting, setExporting] = react.useState(false);
|
|
7415
7506
|
const getSelectedRowsRef = react.useRef(null);
|
|
7416
7507
|
const dynamicFilterOptions = useDynamicFilterOptions(resolvedFields);
|
|
7508
|
+
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), [
|
|
7509
|
+
resolvedFields,
|
|
7510
|
+
denyFields,
|
|
7511
|
+
tableConfig?.hidden,
|
|
7512
|
+
tableConfig?.overrides
|
|
7513
|
+
]);
|
|
7417
7514
|
const hiddenColumns = react.useMemo(() => buildHiddenColumns(resolvedFields, tableConfig?.hidden, denyFields, tableConfig?.overrides), [
|
|
7418
7515
|
resolvedFields,
|
|
7419
7516
|
tableConfig?.hidden,
|
|
@@ -7502,7 +7599,7 @@ function AutoCrudTable({ id, title, description, schema, resource, fields, table
|
|
|
7502
7599
|
return {
|
|
7503
7600
|
...action$1,
|
|
7504
7601
|
onClick: (_rows, { table }) => {
|
|
7505
|
-
const headers = table
|
|
7602
|
+
const headers = getExportColumnIds(table).filter((key) => key !== "select" && key !== "actions" && !denyFields?.includes(key));
|
|
7506
7603
|
exportAllToCSV(table.getFilteredSelectedRowModel().rows.map((row) => formatExportRow(row.original, resolvedFields, Object.fromEntries(headers.map((key) => [key, row.getValue(key)])))), {
|
|
7507
7604
|
filename: "table",
|
|
7508
7605
|
headers
|
|
@@ -7762,9 +7859,12 @@ function AutoCrudTable({ id, title, description, schema, resource, fields, table
|
|
|
7762
7859
|
...pagination
|
|
7763
7860
|
} : void 0,
|
|
7764
7861
|
...resource.tableData.total !== void 0 ? { total: resource.tableData.total } : {},
|
|
7862
|
+
getRowId: tableConfig?.getRowId,
|
|
7863
|
+
expandedRow: tableConfig?.expandedRow,
|
|
7765
7864
|
overrides: tableOverrides,
|
|
7766
7865
|
classNames: tableConfig?.classNames,
|
|
7767
|
-
exclude: hiddenColumns,
|
|
7866
|
+
exclude: hiddenColumns.filter((key) => !filterOnly.includes(key)),
|
|
7867
|
+
filterOnly,
|
|
7768
7868
|
filterMode: tableConfig?.filterModes,
|
|
7769
7869
|
search: searchConfig,
|
|
7770
7870
|
actions: tableRowActions,
|
|
@@ -8597,7 +8697,7 @@ function modalReducer(state, action$1) {
|
|
|
8597
8697
|
/**
|
|
8598
8698
|
* Hook 主函数
|
|
8599
8699
|
*/
|
|
8600
|
-
function useAutoCrudResource({ router, schema, query: queryTransform, options = {} }) {
|
|
8700
|
+
function useAutoCrudResource({ router, schema, query: queryTransform, fields, options = {} }) {
|
|
8601
8701
|
const { idKey = "id", defaultVariant = "dialog", hooks, toast: toastAdapter, exportFetcher, defaultSort: defaultSortOption } = options;
|
|
8602
8702
|
const toast = (0, react.useMemo)(() => toastAdapter === false ? noopToastAdapter : toastAdapter ?? defaultToastAdapter, [toastAdapter]);
|
|
8603
8703
|
const hooksRef = (0, react.useRef)(hooks);
|
|
@@ -8609,7 +8709,14 @@ function useAutoCrudResource({ router, schema, query: queryTransform, options =
|
|
|
8609
8709
|
const defaultSorting = (0, react.useMemo)(() => defaultSortOption === false ? [] : defaultSortOption ?? getDefaultSortingForSchema(resolvedSchema), [defaultSortOption, resolvedSchema]);
|
|
8610
8710
|
const metadataFields = (0, react.useMemo)(() => readMetadataFields(metadata?.fields), [metadata?.fields]);
|
|
8611
8711
|
const capabilities = metadata?.capabilities;
|
|
8612
|
-
const columns = (0, react.useMemo)(() => createTableSchema(resolvedSchema),
|
|
8712
|
+
const columns = (0, react.useMemo)(() => createTableSchema(resolvedSchema, { overrides: Object.fromEntries(Object.entries(mergeFields(metadataFields, fields) ?? {}).flatMap(([key, field]) => {
|
|
8713
|
+
const filter = field.filter;
|
|
8714
|
+
return filter && typeof filter === "object" && filter.variant ? [[key, { meta: { variant: filter.variant } }]] : [];
|
|
8715
|
+
})) }), [
|
|
8716
|
+
resolvedSchema,
|
|
8717
|
+
fields,
|
|
8718
|
+
metadataFields
|
|
8719
|
+
]);
|
|
8613
8720
|
const [urlParams] = useQueryStates({
|
|
8614
8721
|
page: parseAsInteger.withDefault(1),
|
|
8615
8722
|
perPage: parseAsInteger.withDefault(10),
|