@wordrhyme/auto-crud 1.13.0 → 1.14.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 CHANGED
@@ -172,20 +172,20 @@ Zod Schema → 自动推断字段类型 → 生成表格列 + 表单字段
172
172
  ```typescript
173
173
  import { createMemoryDataSource } from '@wordrhyme/auto-crud';
174
174
 
175
- const dataSource = createMemoryDataSource({
176
- data: initialData,
177
- onCreate: async (data) => {
178
- /* ... */
179
- },
180
- onUpdate: async (id, data) => {
181
- /* ... */
182
- },
183
- onDelete: async (id) => {
184
- /* ... */
185
- },
175
+ const dataSource = createMemoryDataSource(initialData);
176
+ const result = await dataSource.list({
177
+ page: 1,
178
+ perPage: 10,
179
+ filters: [{ id: 'status', operator: 'inArray', value: ['draft', 'published'] }],
180
+ sort: [
181
+ { id: 'createdAt', desc: true },
182
+ { id: 'id', desc: false },
183
+ ],
186
184
  });
187
185
  ```
188
186
 
187
+ 筛选支持省略 operator / `eq`(严格相等)和 `inArray`(字段值属于给定数组);其他 operator 会报错。排序按配置顺序比较,前一字段相同时使用下一字段,再进行分页。内存数据源不执行全文搜索。
188
+
189
189
  #### 2. tRPC 数据源(tRPC Data Source)
190
190
 
191
191
  适合:全栈 TypeScript 项目
@@ -328,6 +328,37 @@ interface AutoCrudTableProps<TSchema> {
328
328
 
329
329
  `expandedRow` 的第二个参数是当前 TanStack `Row`,可通过 `row.getAllCells()` 读取已解析的列和单元格上下文,复用字段渲染器及数据源标签。只接收订单数据的单参数回调仍可使用。
330
330
 
331
+ #### 表格状态与 URL 参数
332
+
333
+ 同页显示多个表格时,在资源 Hook 中配置独立的分页、排序和条件连接符参数名,`AutoCrudTable` 会自动传给表格:
334
+
335
+ ```tsx
336
+ const resource = useAutoCrudResource({
337
+ router: trpc.orders,
338
+ schema: orderSchema,
339
+ queryKeys: {
340
+ page: 'ordersPage',
341
+ perPage: 'ordersSize',
342
+ sort: 'ordersSort',
343
+ joinOperator: 'ordersJoin',
344
+ },
345
+ });
346
+
347
+ <AutoCrudTable
348
+ id="orders"
349
+ schema={orderSchema}
350
+ resource={resource}
351
+ table={{
352
+ enableRowSelection: false,
353
+ initialColumnVisibility: { createdAt: false },
354
+ }}
355
+ />;
356
+ ```
357
+
358
+ 未配置的参数名保留默认值。搜索仍使用共享的 `search` 参数,字段筛选仍使用字段名;`queryKeys` 不隔离这些值。筛选或搜索变化时,仅重置该表格对应的页码。直接使用 `AutoTable` 时传入相同的 `queryKeys`;`table.queryKeys` 可覆盖资源提供的整组配置。
359
+
360
+ `enableRowSelection` 默认 `true`,设为 `false` 后关闭行选择并隐藏选择列和批量操作栏。`initialColumnVisibility` 只设置首次显示的列,用户仍可通过列设置切换;它不同于 `table.hidden` 对列的排除。
361
+
331
362
  ### `useAutoCrudResource()`
332
363
 
333
364
  连接数据源到组件的 Hook。
package/dist/index.cjs CHANGED
@@ -5122,7 +5122,7 @@ function useDataTable(props) {
5122
5122
  ...tableProps.defaultColumn,
5123
5123
  enableColumnFilter: false
5124
5124
  },
5125
- enableRowSelection: true,
5125
+ enableRowSelection: tableProps.enableRowSelection ?? true,
5126
5126
  onRowSelectionChange: setRowSelection,
5127
5127
  onPaginationChange,
5128
5128
  onSortingChange,
@@ -5188,7 +5188,7 @@ const filterModeConfig = {
5188
5188
  tooltip: "Linear-style command filters"
5189
5189
  }
5190
5190
  };
5191
- 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 }) {
5191
+ function AutoTable({ schema, data, getRowId, expandedRow, defaultExpanded = false, pageCount = 1, pagination, total, overrides, classNames, enableRowSelection = true, queryKeys, initialColumnVisibility, 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 }) {
5192
5192
  if (expandedRow && !getRowId) throw new Error("AutoTable requires getRowId when expandedRow is provided.");
5193
5193
  const enableExpansion = !!expandedRow;
5194
5194
  const modes = filterMode ? Array.isArray(filterMode) ? filterMode : [filterMode] : DEFAULT_MODES;
@@ -5198,7 +5198,7 @@ function AutoTable({ schema, data, getRowId, expandedRow, defaultExpanded = fals
5198
5198
  shallow: false,
5199
5199
  clearOnDefault: true
5200
5200
  });
5201
- const [, setSearchPage] = useUrlState("page", parseAsInteger.withDefault(1), {
5201
+ const [, setSearchPage] = useUrlState(queryKeys?.page ?? "page", parseAsInteger.withDefault(1), {
5202
5202
  shallow: false,
5203
5203
  clearOnDefault: true
5204
5204
  });
@@ -5265,6 +5265,7 @@ function AutoTable({ schema, data, getRowId, expandedRow, defaultExpanded = fals
5265
5265
  ]);
5266
5266
  const stableInitialState = (0, react.useMemo)(() => ({
5267
5267
  sorting: initialSorting,
5268
+ columnVisibility: initialColumnVisibility,
5268
5269
  expanded: enableExpansion && defaultExpanded ? true : {},
5269
5270
  columnPinning: pinnedColumns ?? {
5270
5271
  left: [...enableExpansion ? [expandColumnId] : [], ...enableRowSelection ? ["select"] : []],
@@ -5272,6 +5273,7 @@ function AutoTable({ schema, data, getRowId, expandedRow, defaultExpanded = fals
5272
5273
  }
5273
5274
  }), [
5274
5275
  initialSorting,
5276
+ initialColumnVisibility,
5275
5277
  pinnedColumns,
5276
5278
  enableRowSelection,
5277
5279
  enableExpansion,
@@ -5283,6 +5285,8 @@ function AutoTable({ schema, data, getRowId, expandedRow, defaultExpanded = fals
5283
5285
  const { table, shallow, debounceMs, throttleMs } = useDataTable({
5284
5286
  data,
5285
5287
  columns,
5288
+ queryKeys,
5289
+ enableRowSelection,
5286
5290
  getRowId,
5287
5291
  getRowCanExpand: () => enableExpansion,
5288
5292
  getExpandedRowModel: enableExpansion ? expandedRowModel : void 0,
@@ -8309,6 +8313,9 @@ function AutoCrudTable({ id, title, description, schema, resource, fields, table
8309
8313
  pageCount: resource.tableData.pageCount,
8310
8314
  pagination: paginationOptions,
8311
8315
  ...resource.tableData.total !== void 0 ? { total: resource.tableData.total } : {},
8316
+ queryKeys: tableConfig?.queryKeys ?? resource.queryKeys,
8317
+ enableRowSelection: tableConfig?.enableRowSelection,
8318
+ initialColumnVisibility: tableConfig?.initialColumnVisibility,
8312
8319
  getRowId: tableConfig?.getRowId,
8313
8320
  expandedRow: tableConfig?.expandedRow,
8314
8321
  defaultExpanded: tableConfig?.defaultExpanded,
@@ -9154,7 +9161,7 @@ function modalReducer(state, action$1) {
9154
9161
  /**
9155
9162
  * Hook 主函数
9156
9163
  */
9157
- function useAutoCrudResource({ router, schema, query: queryTransform, fields, options = {} }) {
9164
+ function useAutoCrudResource({ router, queryKeys, schema, query: queryTransform, fields, options = {} }) {
9158
9165
  const { idKey = "id", defaultVariant = "dialog", hooks, toast: toastAdapter, exportFetcher, defaultSort: defaultSortOption } = options;
9159
9166
  const toast$1 = (0, react.useMemo)(() => toastAdapter === false ? noopToastAdapter : toastAdapter ?? defaultToastAdapter, [toastAdapter]);
9160
9167
  const hooksRef = (0, react.useRef)(hooks);
@@ -9175,31 +9182,36 @@ function useAutoCrudResource({ router, schema, query: queryTransform, fields, op
9175
9182
  fields,
9176
9183
  metadataFields
9177
9184
  ]);
9178
- const [urlParams] = useQueryStates({
9179
- page: parseAsInteger.withDefault(1),
9180
- perPage: parseAsInteger.withDefault(10),
9181
- sort: getSortingStateParser().withDefault(defaultSorting),
9182
- joinOperator: parseAsStringEnum(["and", "or"]).withDefault("and"),
9183
- search: parseAsString.withDefault("")
9184
- }, { shallow: false });
9185
- const [filters] = useReadableFilters(columns, { shallow: false });
9185
+ const [page] = useQueryState(queryKeys?.page ?? "page", parseAsInteger.withDefault(1));
9186
+ const [perPage] = useQueryState(queryKeys?.perPage ?? "perPage", parseAsInteger.withDefault(10));
9187
+ const [sort] = useQueryState(queryKeys?.sort ?? "sort", getSortingStateParser().withDefault(defaultSorting));
9188
+ const [joinOperator] = useQueryState(queryKeys?.joinOperator ?? "joinOperator", parseAsStringEnum(["and", "or"]).withDefault("and"));
9189
+ const [search] = useQueryState("search", parseAsString.withDefault(""), { shallow: false });
9190
+ const [filters] = useReadableFilters(columns, {
9191
+ shallow: false,
9192
+ resetPageKey: queryKeys?.page ?? "page"
9193
+ });
9186
9194
  const metadataPending = Boolean(metadataQuery?.isPending);
9187
9195
  const [filtersReady, setFiltersReady] = (0, react.useState)(!metadataPending);
9188
9196
  (0, react.useEffect)(() => setFiltersReady(!metadataPending), [metadataPending]);
9189
9197
  const listReady = !metadataPending && filtersReady;
9190
9198
  const queryInput = (0, react.useMemo)(() => {
9191
9199
  const autoParams = {
9192
- page: urlParams.page,
9193
- perPage: urlParams.perPage,
9194
- sort: urlParams.sort,
9200
+ page,
9201
+ perPage,
9202
+ sort,
9195
9203
  filters,
9196
- joinOperator: urlParams.joinOperator,
9197
- ...urlParams.search.trim() ? { search: urlParams.search.trim() } : {}
9204
+ joinOperator,
9205
+ ...search.trim() ? { search: search.trim() } : {}
9198
9206
  };
9199
9207
  return queryTransform ? queryTransform(autoParams) : autoParams;
9200
9208
  }, [
9201
- urlParams,
9209
+ page,
9210
+ perPage,
9211
+ sort,
9202
9212
  filters,
9213
+ joinOperator,
9214
+ search,
9203
9215
  queryTransform
9204
9216
  ]);
9205
9217
  const hasReadyListData = (0, react.useRef)(false);
@@ -9475,6 +9487,7 @@ function useAutoCrudResource({ router, schema, query: queryTransform, fields, op
9475
9487
  }, [listQuery, listReady]);
9476
9488
  return {
9477
9489
  idKey,
9490
+ queryKeys,
9478
9491
  tableData: {
9479
9492
  data: tableRows,
9480
9493
  pageCount: listData?.pageCount ?? 0,
@@ -9762,18 +9775,21 @@ function createMemoryDataSource(initialData = []) {
9762
9775
  let filtered = [...data];
9763
9776
  if (params.filters) filtered = filtered.filter((item) => {
9764
9777
  return params.filters.every((filter) => {
9765
- return item[filter.id] === filter.value;
9778
+ const value = item[filter.id];
9779
+ if (filter.operator === "inArray") return Array.isArray(filter.value) && filter.value.includes(value);
9780
+ if (!filter.operator || filter.operator === "eq") return value === filter.value;
9781
+ throw new Error(`Unsupported memory filter operator: ${filter.operator}`);
9766
9782
  });
9767
9783
  });
9768
- if (params.sort && params.sort.length > 0) {
9769
- const sortItem = params.sort[0];
9770
- filtered.sort((a, b) => {
9784
+ if (params.sort && params.sort.length > 0) filtered.sort((a, b) => {
9785
+ for (const sortItem of params.sort) {
9771
9786
  const aVal = a[sortItem.id];
9772
9787
  const bVal = b[sortItem.id];
9773
9788
  const result = aVal > bVal ? 1 : aVal < bVal ? -1 : 0;
9774
- return sortItem.desc ? -result : result;
9775
- });
9776
- }
9789
+ if (result) return sortItem.desc ? -result : result;
9790
+ }
9791
+ return 0;
9792
+ });
9777
9793
  const start = (params.page - 1) * params.perPage;
9778
9794
  const end = start + params.perPage;
9779
9795
  return {
package/dist/index.d.cts CHANGED
@@ -1,9 +1,9 @@
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_runtime8 from "react/jsx-runtime";
2
+ import * as react_jsx_runtime11 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";
6
- import { Column, ColumnDef, ColumnMeta, ColumnSort, Row, RowData, Table, TableOptions, TableState } from "@tanstack/react-table";
6
+ import { Column, ColumnDef, ColumnMeta, ColumnSort, Row, RowData, Table, TableOptions, TableState, VisibilityState } from "@tanstack/react-table";
7
7
  import { z } from "zod";
8
8
  import { JsonSchemaFormScope } from "@wordrhyme/formily-shadcn";
9
9
  import { ISchema } from "@formily/json-schema";
@@ -471,6 +471,7 @@ interface ImportResult {
471
471
  * Hook 返回值类型
472
472
  */
473
473
  interface UseAutoCrudResourceReturn<TSchema extends z.ZodObject<z.ZodRawShape>, TListItem> {
474
+ queryKeys?: Partial<QueryKeys>;
474
475
  /** 主键字段名(默认: "id") */
475
476
  idKey?: keyof TListItem & string;
476
477
  tableData: {
@@ -574,6 +575,8 @@ interface AutoQueryParams {
574
575
  interface UseAutoCrudResourceParams<TSchema extends z.ZodObject<z.ZodRawShape>, TListItem> {
575
576
  /** tRPC router (如 trpc.tasks) */
576
577
  router: CrudRouter;
578
+ /** URL keys shared with the table, for multiple resources on one page. */
579
+ queryKeys?: Partial<QueryKeys>;
577
580
  /** Zod schema */
578
581
  schema: TSchema;
579
582
  /** Field filter configuration shared with AutoCrudTable. */
@@ -602,6 +605,7 @@ interface UseAutoCrudResourceParams<TSchema extends z.ZodObject<z.ZodRawShape>,
602
605
  */
603
606
  declare function useAutoCrudResource<TSchema extends z.ZodObject<z.ZodRawShape>, TListItem = z.output<TSchema>>({
604
607
  router,
608
+ queryKeys,
605
609
  schema,
606
610
  query: queryTransform,
607
611
  fields,
@@ -672,7 +676,7 @@ declare function DataTable<TData>({
672
676
  className,
673
677
  classNames,
674
678
  ...props
675
- }: DataTableProps<TData>): react_jsx_runtime8.JSX.Element;
679
+ }: DataTableProps<TData>): react_jsx_runtime11.JSX.Element;
676
680
  //#endregion
677
681
  //#region src/components/auto-crud/auto-table-action-bar.d.ts
678
682
  /** 批量更新字段配置 */
@@ -761,7 +765,7 @@ declare function AutoTableActionBar<TData>({
761
765
  extraActions,
762
766
  actions,
763
767
  deleteConfirmation
764
- }: AutoTableActionBarProps<TData>): react_jsx_runtime8.JSX.Element;
768
+ }: AutoTableActionBarProps<TData>): react_jsx_runtime11.JSX.Element;
765
769
  //#endregion
766
770
  //#region src/lib/schema-bridge/types.d.ts
767
771
  /**
@@ -1016,6 +1020,10 @@ interface AutoTableProps<T extends z.ZodObject<z.ZodRawShape>> extends Pick<Data
1016
1020
  /** 按元素分组的全局表格类名 */
1017
1021
  classNames?: DataTableClassNames;
1018
1022
  enableRowSelection?: boolean;
1023
+ /** URL keys shared with useAutoCrudResource. */
1024
+ queryKeys?: Partial<QueryKeys>;
1025
+ /** Initial visibility; users can still toggle columns in the view options. */
1026
+ initialColumnVisibility?: VisibilityState;
1019
1027
  exclude?: (keyof z.infer<T>)[];
1020
1028
  filterOnly?: string[];
1021
1029
  actions?: ActionsColumnConfig<z.infer<T>>;
@@ -1078,6 +1086,8 @@ declare function AutoTable<T extends z.ZodObject<z.ZodRawShape>>({
1078
1086
  overrides,
1079
1087
  classNames,
1080
1088
  enableRowSelection,
1089
+ queryKeys,
1090
+ initialColumnVisibility,
1081
1091
  exclude,
1082
1092
  filterOnly,
1083
1093
  actions,
@@ -1099,7 +1109,7 @@ declare function AutoTable<T extends z.ZodObject<z.ZodRawShape>>({
1099
1109
  onSelectedRowsChange,
1100
1110
  getSelectedRows,
1101
1111
  exportTableRef
1102
- }: AutoTableProps<T>): react_jsx_runtime8.JSX.Element;
1112
+ }: AutoTableProps<T>): react_jsx_runtime11.JSX.Element;
1103
1113
  //#endregion
1104
1114
  //#region src/components/auto-crud/auto-form.d.ts
1105
1115
  type AutoFormProps<T extends z.ZodObject<z.ZodRawShape>> = {
@@ -1142,7 +1152,7 @@ declare function AutoFormInner<T extends z.ZodObject<z.ZodRawShape>>({
1142
1152
  labelAlign,
1143
1153
  labelWidth,
1144
1154
  showSubmitButton
1145
- }: AutoFormProps<T>, ref: React.Ref<AutoFormRef>): react_jsx_runtime8.JSX.Element;
1155
+ }: AutoFormProps<T>, ref: React.Ref<AutoFormRef>): react_jsx_runtime11.JSX.Element;
1146
1156
  declare const AutoForm: <T extends z.ZodObject<z.ZodRawShape>>(props: AutoFormProps<T> & {
1147
1157
  ref?: React.Ref<AutoFormRef>;
1148
1158
  }) => ReturnType<typeof AutoFormInner>;
@@ -1523,7 +1533,7 @@ interface AutoCrudTableProps<TSchema extends z.ZodObject<z.ZodRawShape>> {
1523
1533
  batchActions?: BatchActionConfig<z.output<TSchema>>;
1524
1534
  /** 默认排序 */
1525
1535
  defaultSort?: any[];
1526
- } & Pick<AutoTableProps<TSchema>, 'getRowId' | 'expandedRow' | 'defaultExpanded'>;
1536
+ } & Pick<AutoTableProps<TSchema>, 'getRowId' | 'expandedRow' | 'defaultExpanded' | 'enableRowSelection' | 'initialColumnVisibility' | 'queryKeys'>;
1527
1537
  /** 表单配置 */
1528
1538
  form?: {
1529
1539
  /** 表单覆盖配置(兼容旧 API,优先级低于 fieldOverrides) */
@@ -1612,7 +1622,7 @@ declare function AutoCrudTable<TSchema extends z.ZodObject<z.ZodRawShape>>({
1612
1622
  locale: localeProp,
1613
1623
  onCreate,
1614
1624
  onExport
1615
- }: AutoCrudTableProps<TSchema>): react_jsx_runtime8.JSX.Element;
1625
+ }: AutoCrudTableProps<TSchema>): react_jsx_runtime11.JSX.Element;
1616
1626
  //#endregion
1617
1627
  //#region src/components/auto-crud/auto-table-simple-filters.d.ts
1618
1628
  interface AutoTableSimpleFiltersProps<TData> {
@@ -1628,7 +1638,7 @@ declare function AutoTableSimpleFilters<TData>({
1628
1638
  filters: externalFilters,
1629
1639
  onFiltersChange,
1630
1640
  leading
1631
- }: AutoTableSimpleFiltersProps<TData>): react_jsx_runtime8.JSX.Element | null;
1641
+ }: AutoTableSimpleFiltersProps<TData>): react_jsx_runtime11.JSX.Element | null;
1632
1642
  //#endregion
1633
1643
  //#region src/components/auto-crud/form-modal.d.ts
1634
1644
  type ModalVariant = 'dialog' | 'sheet';
@@ -1677,7 +1687,7 @@ declare function CrudFormModal<T extends z.ZodObject<z.ZodRawShape>>({
1677
1687
  labelAlign,
1678
1688
  labelWidth,
1679
1689
  className
1680
- }: CrudFormModalProps<T> & CrudFormModalMode<T>): react_jsx_runtime8.JSX.Element;
1690
+ }: CrudFormModalProps<T> & CrudFormModalMode<T>): react_jsx_runtime11.JSX.Element;
1681
1691
  //#endregion
1682
1692
  //#region src/components/auto-crud/import-dialog.d.ts
1683
1693
  interface ImportDialogProps {
@@ -1699,7 +1709,7 @@ declare function ImportDialog({
1699
1709
  columns,
1700
1710
  title,
1701
1711
  locale
1702
- }: ImportDialogProps): react_jsx_runtime8.JSX.Element;
1712
+ }: ImportDialogProps): react_jsx_runtime11.JSX.Element;
1703
1713
  //#endregion
1704
1714
  //#region src/components/auto-crud/export-dialog.d.ts
1705
1715
  type ExportMode = 'selected' | 'filtered';
@@ -1719,7 +1729,7 @@ declare function ExportDialog({
1719
1729
  selectedCount,
1720
1730
  onExport,
1721
1731
  canExportFiltered
1722
- }: ExportDialogProps): react_jsx_runtime8.JSX.Element;
1732
+ }: ExportDialogProps): react_jsx_runtime11.JSX.Element;
1723
1733
  //#endregion
1724
1734
  //#region src/components/data-table/data-table-advanced-toolbar.d.ts
1725
1735
  interface DataTableAdvancedToolbarProps<TData> extends React$1.ComponentProps<'div'> {
@@ -1730,7 +1740,7 @@ declare function DataTableAdvancedToolbar<TData>({
1730
1740
  children,
1731
1741
  className,
1732
1742
  ...props
1733
- }: DataTableAdvancedToolbarProps<TData>): react_jsx_runtime8.JSX.Element;
1743
+ }: DataTableAdvancedToolbarProps<TData>): react_jsx_runtime11.JSX.Element;
1734
1744
  //#endregion
1735
1745
  //#region src/components/data-table/data-table-column-header.d.ts
1736
1746
  interface DataTableColumnHeaderProps<TData, TValue> extends React.ComponentProps<typeof DropdownMenuTrigger> {
@@ -1742,7 +1752,7 @@ declare function DataTableColumnHeader<TData, TValue>({
1742
1752
  label,
1743
1753
  className,
1744
1754
  ...props
1745
- }: DataTableColumnHeaderProps<TData, TValue>): react_jsx_runtime8.JSX.Element;
1755
+ }: DataTableColumnHeaderProps<TData, TValue>): react_jsx_runtime11.JSX.Element;
1746
1756
  //#endregion
1747
1757
  //#region src/components/data-table/data-table-faceted-filter.d.ts
1748
1758
  interface DataTableFacetedFilterProps<TData, TValue> {
@@ -1756,7 +1766,7 @@ declare function DataTableFacetedFilter<TData, TValue>({
1756
1766
  title,
1757
1767
  options,
1758
1768
  multiple
1759
- }: DataTableFacetedFilterProps<TData, TValue>): react_jsx_runtime8.JSX.Element;
1769
+ }: DataTableFacetedFilterProps<TData, TValue>): react_jsx_runtime11.JSX.Element;
1760
1770
  //#endregion
1761
1771
  //#region src/components/data-table/data-table-pagination.d.ts
1762
1772
  interface DataTablePaginationProps<TData> extends React.ComponentProps<'div'>, TablePaginationOptions {
@@ -1772,7 +1782,7 @@ declare function DataTablePagination<TData>({
1772
1782
  pageJumpLabel,
1773
1783
  className,
1774
1784
  ...props
1775
- }: DataTablePaginationProps<TData>): react_jsx_runtime8.JSX.Element;
1785
+ }: DataTablePaginationProps<TData>): react_jsx_runtime11.JSX.Element;
1776
1786
  //#endregion
1777
1787
  //#region src/components/data-table/data-table-toolbar.d.ts
1778
1788
  interface DataTableToolbarProps<TData> extends React$1.ComponentProps<'div'> {
@@ -1783,7 +1793,7 @@ declare function DataTableToolbar<TData>({
1783
1793
  children,
1784
1794
  className,
1785
1795
  ...props
1786
- }: DataTableToolbarProps<TData>): react_jsx_runtime8.JSX.Element;
1796
+ }: DataTableToolbarProps<TData>): react_jsx_runtime11.JSX.Element;
1787
1797
  //#endregion
1788
1798
  //#region src/components/data-table/data-table-view-options.d.ts
1789
1799
  interface DataTableViewOptionsProps<TData> extends PopoverContentProps {
@@ -1796,7 +1806,7 @@ declare function DataTableViewOptions<TData>({
1796
1806
  disabled,
1797
1807
  locale,
1798
1808
  ...props
1799
- }: DataTableViewOptionsProps<TData>): react_jsx_runtime8.JSX.Element;
1809
+ }: DataTableViewOptionsProps<TData>): react_jsx_runtime11.JSX.Element;
1800
1810
  //#endregion
1801
1811
  //#region src/lib/crud-actions.d.ts
1802
1812
  type CrudActionZone = 'toolbar' | 'row' | 'batch';
package/dist/index.d.ts CHANGED
@@ -2,7 +2,7 @@ import { a as positionActions, i as matchesPosition, n as actionPositionSchema,
2
2
  import * as React$1 from "react";
3
3
  import { ComponentProps, ReactNode } from "react";
4
4
  import * as _tanstack_react_table0 from "@tanstack/react-table";
5
- import { Column, ColumnDef, ColumnMeta, ColumnSort, Row, RowData, Table, TableOptions, TableState } from "@tanstack/react-table";
5
+ import { Column, ColumnDef, ColumnMeta, ColumnSort, Row, RowData, Table, TableOptions, TableState, VisibilityState } from "@tanstack/react-table";
6
6
  import { DropdownMenuTrigger, PopoverContentProps } from "@wordrhyme/shadcn";
7
7
  import { ClassValue } from "clsx";
8
8
  import * as react_jsx_runtime8 from "react/jsx-runtime";
@@ -471,6 +471,7 @@ interface ImportResult {
471
471
  * Hook 返回值类型
472
472
  */
473
473
  interface UseAutoCrudResourceReturn<TSchema extends z.ZodObject<z.ZodRawShape>, TListItem> {
474
+ queryKeys?: Partial<QueryKeys>;
474
475
  /** 主键字段名(默认: "id") */
475
476
  idKey?: keyof TListItem & string;
476
477
  tableData: {
@@ -574,6 +575,8 @@ interface AutoQueryParams {
574
575
  interface UseAutoCrudResourceParams<TSchema extends z.ZodObject<z.ZodRawShape>, TListItem> {
575
576
  /** tRPC router (如 trpc.tasks) */
576
577
  router: CrudRouter;
578
+ /** URL keys shared with the table, for multiple resources on one page. */
579
+ queryKeys?: Partial<QueryKeys>;
577
580
  /** Zod schema */
578
581
  schema: TSchema;
579
582
  /** Field filter configuration shared with AutoCrudTable. */
@@ -602,6 +605,7 @@ interface UseAutoCrudResourceParams<TSchema extends z.ZodObject<z.ZodRawShape>,
602
605
  */
603
606
  declare function useAutoCrudResource<TSchema extends z.ZodObject<z.ZodRawShape>, TListItem = z.output<TSchema>>({
604
607
  router,
608
+ queryKeys,
605
609
  schema,
606
610
  query: queryTransform,
607
611
  fields,
@@ -1016,6 +1020,10 @@ interface AutoTableProps<T extends z.ZodObject<z.ZodRawShape>> extends Pick<Data
1016
1020
  /** 按元素分组的全局表格类名 */
1017
1021
  classNames?: DataTableClassNames;
1018
1022
  enableRowSelection?: boolean;
1023
+ /** URL keys shared with useAutoCrudResource. */
1024
+ queryKeys?: Partial<QueryKeys>;
1025
+ /** Initial visibility; users can still toggle columns in the view options. */
1026
+ initialColumnVisibility?: VisibilityState;
1019
1027
  exclude?: (keyof z.infer<T>)[];
1020
1028
  filterOnly?: string[];
1021
1029
  actions?: ActionsColumnConfig<z.infer<T>>;
@@ -1078,6 +1086,8 @@ declare function AutoTable<T extends z.ZodObject<z.ZodRawShape>>({
1078
1086
  overrides,
1079
1087
  classNames,
1080
1088
  enableRowSelection,
1089
+ queryKeys,
1090
+ initialColumnVisibility,
1081
1091
  exclude,
1082
1092
  filterOnly,
1083
1093
  actions,
@@ -1523,7 +1533,7 @@ interface AutoCrudTableProps<TSchema extends z.ZodObject<z.ZodRawShape>> {
1523
1533
  batchActions?: BatchActionConfig<z.output<TSchema>>;
1524
1534
  /** 默认排序 */
1525
1535
  defaultSort?: any[];
1526
- } & Pick<AutoTableProps<TSchema>, 'getRowId' | 'expandedRow' | 'defaultExpanded'>;
1536
+ } & Pick<AutoTableProps<TSchema>, 'getRowId' | 'expandedRow' | 'defaultExpanded' | 'enableRowSelection' | 'initialColumnVisibility' | 'queryKeys'>;
1527
1537
  /** 表单配置 */
1528
1538
  form?: {
1529
1539
  /** 表单覆盖配置(兼容旧 API,优先级低于 fieldOverrides) */
package/dist/index.js CHANGED
@@ -5106,7 +5106,7 @@ function useDataTable(props) {
5106
5106
  ...tableProps.defaultColumn,
5107
5107
  enableColumnFilter: false
5108
5108
  },
5109
- enableRowSelection: true,
5109
+ enableRowSelection: tableProps.enableRowSelection ?? true,
5110
5110
  onRowSelectionChange: setRowSelection,
5111
5111
  onPaginationChange,
5112
5112
  onSortingChange,
@@ -5172,7 +5172,7 @@ const filterModeConfig = {
5172
5172
  tooltip: "Linear-style command filters"
5173
5173
  }
5174
5174
  };
5175
- 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 }) {
5175
+ function AutoTable({ schema, data, getRowId, expandedRow, defaultExpanded = false, pageCount = 1, pagination, total, overrides, classNames, enableRowSelection = true, queryKeys, initialColumnVisibility, 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 }) {
5176
5176
  if (expandedRow && !getRowId) throw new Error("AutoTable requires getRowId when expandedRow is provided.");
5177
5177
  const enableExpansion = !!expandedRow;
5178
5178
  const modes = filterMode ? Array.isArray(filterMode) ? filterMode : [filterMode] : DEFAULT_MODES;
@@ -5182,7 +5182,7 @@ function AutoTable({ schema, data, getRowId, expandedRow, defaultExpanded = fals
5182
5182
  shallow: false,
5183
5183
  clearOnDefault: true
5184
5184
  });
5185
- const [, setSearchPage] = useUrlState("page", parseAsInteger.withDefault(1), {
5185
+ const [, setSearchPage] = useUrlState(queryKeys?.page ?? "page", parseAsInteger.withDefault(1), {
5186
5186
  shallow: false,
5187
5187
  clearOnDefault: true
5188
5188
  });
@@ -5249,6 +5249,7 @@ function AutoTable({ schema, data, getRowId, expandedRow, defaultExpanded = fals
5249
5249
  ]);
5250
5250
  const stableInitialState = useMemo(() => ({
5251
5251
  sorting: initialSorting,
5252
+ columnVisibility: initialColumnVisibility,
5252
5253
  expanded: enableExpansion && defaultExpanded ? true : {},
5253
5254
  columnPinning: pinnedColumns ?? {
5254
5255
  left: [...enableExpansion ? [expandColumnId] : [], ...enableRowSelection ? ["select"] : []],
@@ -5256,6 +5257,7 @@ function AutoTable({ schema, data, getRowId, expandedRow, defaultExpanded = fals
5256
5257
  }
5257
5258
  }), [
5258
5259
  initialSorting,
5260
+ initialColumnVisibility,
5259
5261
  pinnedColumns,
5260
5262
  enableRowSelection,
5261
5263
  enableExpansion,
@@ -5267,6 +5269,8 @@ function AutoTable({ schema, data, getRowId, expandedRow, defaultExpanded = fals
5267
5269
  const { table, shallow, debounceMs, throttleMs } = useDataTable({
5268
5270
  data,
5269
5271
  columns,
5272
+ queryKeys,
5273
+ enableRowSelection,
5270
5274
  getRowId,
5271
5275
  getRowCanExpand: () => enableExpansion,
5272
5276
  getExpandedRowModel: enableExpansion ? expandedRowModel : void 0,
@@ -8293,6 +8297,9 @@ function AutoCrudTable({ id, title, description, schema, resource, fields, table
8293
8297
  pageCount: resource.tableData.pageCount,
8294
8298
  pagination: paginationOptions,
8295
8299
  ...resource.tableData.total !== void 0 ? { total: resource.tableData.total } : {},
8300
+ queryKeys: tableConfig?.queryKeys ?? resource.queryKeys,
8301
+ enableRowSelection: tableConfig?.enableRowSelection,
8302
+ initialColumnVisibility: tableConfig?.initialColumnVisibility,
8296
8303
  getRowId: tableConfig?.getRowId,
8297
8304
  expandedRow: tableConfig?.expandedRow,
8298
8305
  defaultExpanded: tableConfig?.defaultExpanded,
@@ -9138,7 +9145,7 @@ function modalReducer(state, action$1) {
9138
9145
  /**
9139
9146
  * Hook 主函数
9140
9147
  */
9141
- function useAutoCrudResource({ router, schema, query: queryTransform, fields, options = {} }) {
9148
+ function useAutoCrudResource({ router, queryKeys, schema, query: queryTransform, fields, options = {} }) {
9142
9149
  const { idKey = "id", defaultVariant = "dialog", hooks, toast: toastAdapter, exportFetcher, defaultSort: defaultSortOption } = options;
9143
9150
  const toast$1 = useMemo(() => toastAdapter === false ? noopToastAdapter : toastAdapter ?? defaultToastAdapter, [toastAdapter]);
9144
9151
  const hooksRef = useRef(hooks);
@@ -9159,31 +9166,36 @@ function useAutoCrudResource({ router, schema, query: queryTransform, fields, op
9159
9166
  fields,
9160
9167
  metadataFields
9161
9168
  ]);
9162
- const [urlParams] = useQueryStates({
9163
- page: parseAsInteger.withDefault(1),
9164
- perPage: parseAsInteger.withDefault(10),
9165
- sort: getSortingStateParser().withDefault(defaultSorting),
9166
- joinOperator: parseAsStringEnum(["and", "or"]).withDefault("and"),
9167
- search: parseAsString.withDefault("")
9168
- }, { shallow: false });
9169
- const [filters] = useReadableFilters(columns, { shallow: false });
9169
+ const [page] = useQueryState(queryKeys?.page ?? "page", parseAsInteger.withDefault(1));
9170
+ const [perPage] = useQueryState(queryKeys?.perPage ?? "perPage", parseAsInteger.withDefault(10));
9171
+ const [sort] = useQueryState(queryKeys?.sort ?? "sort", getSortingStateParser().withDefault(defaultSorting));
9172
+ const [joinOperator] = useQueryState(queryKeys?.joinOperator ?? "joinOperator", parseAsStringEnum(["and", "or"]).withDefault("and"));
9173
+ const [search] = useQueryState("search", parseAsString.withDefault(""), { shallow: false });
9174
+ const [filters] = useReadableFilters(columns, {
9175
+ shallow: false,
9176
+ resetPageKey: queryKeys?.page ?? "page"
9177
+ });
9170
9178
  const metadataPending = Boolean(metadataQuery?.isPending);
9171
9179
  const [filtersReady, setFiltersReady] = useState(!metadataPending);
9172
9180
  useEffect(() => setFiltersReady(!metadataPending), [metadataPending]);
9173
9181
  const listReady = !metadataPending && filtersReady;
9174
9182
  const queryInput = useMemo(() => {
9175
9183
  const autoParams = {
9176
- page: urlParams.page,
9177
- perPage: urlParams.perPage,
9178
- sort: urlParams.sort,
9184
+ page,
9185
+ perPage,
9186
+ sort,
9179
9187
  filters,
9180
- joinOperator: urlParams.joinOperator,
9181
- ...urlParams.search.trim() ? { search: urlParams.search.trim() } : {}
9188
+ joinOperator,
9189
+ ...search.trim() ? { search: search.trim() } : {}
9182
9190
  };
9183
9191
  return queryTransform ? queryTransform(autoParams) : autoParams;
9184
9192
  }, [
9185
- urlParams,
9193
+ page,
9194
+ perPage,
9195
+ sort,
9186
9196
  filters,
9197
+ joinOperator,
9198
+ search,
9187
9199
  queryTransform
9188
9200
  ]);
9189
9201
  const hasReadyListData = useRef(false);
@@ -9459,6 +9471,7 @@ function useAutoCrudResource({ router, schema, query: queryTransform, fields, op
9459
9471
  }, [listQuery, listReady]);
9460
9472
  return {
9461
9473
  idKey,
9474
+ queryKeys,
9462
9475
  tableData: {
9463
9476
  data: tableRows,
9464
9477
  pageCount: listData?.pageCount ?? 0,
@@ -9746,18 +9759,21 @@ function createMemoryDataSource(initialData = []) {
9746
9759
  let filtered = [...data];
9747
9760
  if (params.filters) filtered = filtered.filter((item) => {
9748
9761
  return params.filters.every((filter) => {
9749
- return item[filter.id] === filter.value;
9762
+ const value = item[filter.id];
9763
+ if (filter.operator === "inArray") return Array.isArray(filter.value) && filter.value.includes(value);
9764
+ if (!filter.operator || filter.operator === "eq") return value === filter.value;
9765
+ throw new Error(`Unsupported memory filter operator: ${filter.operator}`);
9750
9766
  });
9751
9767
  });
9752
- if (params.sort && params.sort.length > 0) {
9753
- const sortItem = params.sort[0];
9754
- filtered.sort((a, b) => {
9768
+ if (params.sort && params.sort.length > 0) filtered.sort((a, b) => {
9769
+ for (const sortItem of params.sort) {
9755
9770
  const aVal = a[sortItem.id];
9756
9771
  const bVal = b[sortItem.id];
9757
9772
  const result = aVal > bVal ? 1 : aVal < bVal ? -1 : 0;
9758
- return sortItem.desc ? -result : result;
9759
- });
9760
- }
9773
+ if (result) return sortItem.desc ? -result : result;
9774
+ }
9775
+ return 0;
9776
+ });
9761
9777
  const start = (params.page - 1) * params.perPage;
9762
9778
  const end = start + params.perPage;
9763
9779
  return {
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@wordrhyme/auto-crud",
3
3
  "type": "module",
4
- "version": "1.13.0",
4
+ "version": "1.14.0",
5
5
  "description": "Schema-first CRUD components with auto-generated tables and forms",
6
6
  "author": "wordrhyme",
7
7
  "license": "MIT",
@@ -96,8 +96,8 @@
96
96
  "vitest": "^3.2.4",
97
97
  "@internal/eslint-config": "0.3.0",
98
98
  "@internal/prettier-config": "0.0.1",
99
- "@internal/tsdown-config": "0.1.0",
100
99
  "@internal/tsconfig": "0.1.0",
100
+ "@internal/tsdown-config": "0.1.0",
101
101
  "@internal/vitest-config": "0.1.0"
102
102
  },
103
103
  "prettier": "@internal/prettier-config",