@wordrhyme/auto-crud 1.2.1 → 1.2.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -277,7 +277,9 @@ interface AutoCrudTableProps<TSchema> {
277
277
  * - 只传 custom 项:内置保持默认,custom 追加到首/尾
278
278
  * - 包含任意内置 type:数组完全接管,未列出的内置项自动隐藏
279
279
  */
280
- toolbarActions?: ToolbarActionItem[];
280
+ toolbar?: ToolbarActionConfig;
281
+ /** @deprecated 新代码请使用 toolbar */
282
+ toolbarActions?: ToolbarActionConfig;
281
283
  /**
282
284
  * 行操作配置
283
285
  * - 只传 custom 项:内置保持默认,custom 追加到首/尾
@@ -304,6 +306,9 @@ interface UseAutoCrudResourceConfig<TData> {
304
306
 
305
307
  ```typescript
306
308
  interface UseAutoCrudResourceReturn<TSchema, TData> {
309
+ // 主键字段名;未提供时 AutoCrudTable 默认按 "id" 读取
310
+ idKey?: keyof TData & string;
311
+
307
312
  // 表格数据
308
313
  tableData: {
309
314
  data: TData[];
@@ -529,7 +534,7 @@ table={{
529
534
 
530
535
  ## 🔧 工具栏操作配置
531
536
 
532
- `toolbarActions` 数组控制页面顶部右侧工具栏的按钮,设计思路完全对齐行操作 `actions`。
537
+ `toolbar` 数组控制页面顶部右侧工具栏的按钮,设计思路完全对齐行操作 `actions`。旧 prop `toolbarActions` 仍兼容读取,但新代码请使用 `toolbar`。
533
538
 
534
539
  内置操作类型:`create`(新建)、`import`(导入)、`export`(导出)。
535
540
 
@@ -539,7 +544,7 @@ table={{
539
544
 
540
545
  ```tsx
541
546
  <AutoCrudTable
542
- toolbarActions={[
547
+ toolbar={[
543
548
  {
544
549
  type: 'custom',
545
550
  component: <Button variant="outline">分类管理</Button>,
@@ -557,7 +562,7 @@ table={{
557
562
 
558
563
  ```tsx
559
564
  <AutoCrudTable
560
- toolbarActions={[
565
+ toolbar={[
561
566
  { type: 'create' }, // 新建移到最前
562
567
  { type: 'export', label: '导出 CSV' }, // 覆盖文案
563
568
  // import 未列出 → 隐藏
@@ -570,7 +575,7 @@ table={{
570
575
 
571
576
  ```tsx
572
577
  <AutoCrudTable
573
- toolbarActions={[
578
+ toolbar={[
574
579
  { type: 'import' },
575
580
  { type: 'export' },
576
581
  {
@@ -586,7 +591,7 @@ table={{
586
591
 
587
592
  ```tsx
588
593
  <AutoCrudTable
589
- toolbarActions={[
594
+ toolbar={[
590
595
  { type: 'export' },
591
596
  {
592
597
  type: 'create',
@@ -614,7 +619,7 @@ table={{
614
619
 
615
620
  ```tsx
616
621
  <AutoCrudTable
617
- toolbarActions={[
622
+ toolbar={[
618
623
  { type: 'custom', component: <CategoryFilter onChange={setCategory} /> },
619
624
  { type: 'export' },
620
625
  { type: 'create', label: '发布', onClick: () => router.push('/products/new') },
@@ -625,7 +630,7 @@ table={{
625
630
 
626
631
  ### 与 `permissions` 的交互
627
632
 
628
- `toolbarActions` 与 `permissions` 的交互规则如下:
633
+ `toolbar` 与 `permissions` 的交互规则如下:
629
634
 
630
635
  - `permissions.can.create = false` → 即使配置了 `{ type: "create" }`,新建按钮仍不渲染
631
636
  - `permissions.can.export = false` → 即使配置了 `{ type: "export" }`,导出按钮仍不渲染
@@ -637,7 +642,7 @@ table={{
637
642
  permissions={{
638
643
  can: { create: isAdmin, export: true, delete: isAdmin },
639
644
  }}
640
- toolbarActions={[
645
+ toolbar={[
641
646
  { type: 'export' }, // ✅ 始终渲染
642
647
  { type: 'create', label: '发布商品' }, // 🔒 仅 isAdmin 时渲染
643
648
  {
@@ -648,14 +653,14 @@ table={{
648
653
  />
649
654
  ```
650
655
 
651
- ### `toolbarActions` 传参高级语法(函数式)
656
+ ### `toolbar` 传参高级语法(函数式)
652
657
 
653
658
  如果你的目标是拦截并修改基于原顺序的所有内容,你还可以传递一个函数:
654
659
 
655
660
  ```tsx
656
661
  <AutoCrudTable
657
662
  // defaults 即内置三个按钮的信息,在此数组里你可以随意 map、过滤或插值
658
- toolbarActions={(defaults) =>
663
+ toolbar={(defaults) =>
659
664
  defaults.map((btn) =>
660
665
  btn.type === 'create'
661
666
  ? { ...btn, label: '发布商品', onClick: () => router.push('/products/new') }
@@ -671,6 +676,10 @@ table={{
671
676
  ```typescript
672
677
  type ToolbarActionItem = ToolbarBuiltinActionItem | ToolbarCustomActionItem;
673
678
 
679
+ type ToolbarActionConfig =
680
+ | ToolbarActionItem[]
681
+ | ((defaults: ToolbarBuiltinActionItem[]) => ToolbarActionItem[]);
682
+
674
683
  type ToolbarBuiltinActionItem = {
675
684
  type: 'create' | 'import' | 'export';
676
685
  onClick?: () => void; // 覆盖默认行为
@@ -685,6 +694,29 @@ type ToolbarCustomActionItem = {
685
694
  };
686
695
  ```
687
696
 
697
+ ### 工具栏扩展 Resolver
698
+
699
+ 宿主应用可以通过 `setAutoCrudToolbarResolver` 为指定 CRUD 注入额外工具栏动作。resolver 是普通纯函数,不是 React Hook;不要在 resolver 内调用 `useRouter`、`useMemo` 等 React Hooks。
700
+
701
+ ```tsx
702
+ import { AutoCrudTable, setAutoCrudToolbarResolver } from '@wordrhyme/auto-crud';
703
+ import type { AutoCrudToolbarResolver } from '@wordrhyme/auto-crud';
704
+
705
+ const toolbarResolver: AutoCrudToolbarResolver = (targetId, ownerActions, context) => {
706
+ if (targetId !== 'com.wordrhyme.shop.products') return [...ownerActions];
707
+
708
+ return [
709
+ ...ownerActions,
710
+ {
711
+ type: 'custom',
712
+ component: <Button disabled={context.selectedCount === 0}>批量发布</Button>,
713
+ },
714
+ ];
715
+ };
716
+
717
+ setAutoCrudToolbarResolver(toolbarResolver);
718
+ ```
719
+
688
720
  ---
689
721
 
690
722
  ## 🎬 行操作配置
@@ -790,7 +822,7 @@ type ActionItem<T> =
790
822
 
791
823
  ### 批量悬浮栏操作顺序
792
824
 
793
- `table.batchActions` 控制多选后悬浮操作栏,设计思路对齐行操作 `actions` 和顶部 `toolbarActions`。
825
+ `table.batchActions` 控制多选后悬浮操作栏,设计思路对齐行操作 `actions` 和顶部 `toolbar`。
794
826
 
795
827
  内置操作类型:`batchUpdate`(批量更新字段下拉)、`export`(导出选中行)、`delete`(删除选中行)。
796
828
 
package/dist/index.cjs CHANGED
@@ -4907,7 +4907,7 @@ const filterModeConfig = {
4907
4907
  tooltip: "Linear-style command filters"
4908
4908
  }
4909
4909
  };
4910
- function AutoTable({ schema, data, pageCount = 1, overrides, enableRowSelection = true, exclude, actions, pinnedColumns, filterMode = "simple", search = false, onDeleteSelected, onUpdateSelected, batchUpdateFields, actionBarExtra, actionBarActions, initialSorting, enableExport = true, showDefaultExport = true, onSelectedCountChange, getSelectedRows }) {
4910
+ function AutoTable({ schema, data, pageCount = 1, overrides, enableRowSelection = true, exclude, actions, pinnedColumns, filterMode = "simple", search = false, onDeleteSelected, onUpdateSelected, batchUpdateFields, actionBarExtra, actionBarActions, initialSorting, enableExport = true, showDefaultExport = true, onSelectedCountChange, onSelectedRowsChange, getSelectedRows }) {
4911
4911
  const modes = filterMode ? Array.isArray(filterMode) ? filterMode : [filterMode] : DEFAULT_MODES;
4912
4912
  const [currentMode, setCurrentMode] = useQueryState("filterMode", parseAsStringEnum(modes).withDefault(modes[0] ?? "simple"));
4913
4913
  const showToggle = modes.length > 1;
@@ -4956,17 +4956,28 @@ function AutoTable({ schema, data, pageCount = 1, overrides, enableRowSelection
4956
4956
  shallow: false,
4957
4957
  clearOnDefault: true
4958
4958
  });
4959
- const getSelectedRowsFn = (0, react.useCallback)(() => table.getFilteredSelectedRowModel().rows.map((row) => row.original), [table]);
4959
+ const rowSelection = table.getState().rowSelection;
4960
+ const columnFilters = table.getState().columnFilters;
4961
+ const selectedRows = (0, react.useMemo)(() => table.getFilteredSelectedRowModel().rows.map((row) => row.original), [
4962
+ table,
4963
+ rowSelection,
4964
+ columnFilters,
4965
+ data
4966
+ ]);
4967
+ const getSelectedRowsFn = (0, react.useCallback)(() => selectedRows, [selectedRows]);
4960
4968
  (0, react.useEffect)(() => {
4961
4969
  if (getSelectedRows) getSelectedRows.current = getSelectedRowsFn;
4962
4970
  return () => {
4963
4971
  if (getSelectedRows) getSelectedRows.current = null;
4964
4972
  };
4965
4973
  }, [getSelectedRows, getSelectedRowsFn]);
4966
- const selectedRowCount = table.getFilteredSelectedRowModel().rows.length;
4974
+ const selectedRowCount = selectedRows.length;
4967
4975
  (0, react.useEffect)(() => {
4968
4976
  onSelectedCountChange?.(selectedRowCount);
4969
4977
  }, [selectedRowCount, onSelectedCountChange]);
4978
+ (0, react.useEffect)(() => {
4979
+ onSelectedRowsChange?.(selectedRows);
4980
+ }, [selectedRows, onSelectedRowsChange]);
4970
4981
  const FilterModeSelect = showToggle ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(__pixpilot_shadcn.DropdownMenu, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(__pixpilot_shadcn.DropdownMenuTrigger, {
4971
4982
  asChild: true,
4972
4983
  children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__pixpilot_shadcn.Button, {
@@ -5235,8 +5246,12 @@ function createEditFormSchema(schema, options) {
5235
5246
  function createRegistry(label) {
5236
5247
  const entries = /* @__PURE__ */ new Map();
5237
5248
  const listeners = /* @__PURE__ */ new Set();
5249
+ let notifyScheduled = false;
5238
5250
  const notify = () => {
5251
+ if (notifyScheduled) return;
5252
+ notifyScheduled = true;
5239
5253
  queueMicrotask(() => {
5254
+ notifyScheduled = false;
5240
5255
  for (const listener of listeners) listener();
5241
5256
  });
5242
5257
  };
@@ -5266,20 +5281,32 @@ function createRegistry(label) {
5266
5281
  };
5267
5282
  }
5268
5283
  const formComponents = createRegistry("form component");
5269
- const dataSources = createRegistry("data source");
5270
- function normalizeDataSourceConfig(config) {
5271
- if (typeof config === "string") return config.length > 0 ? {
5272
- key: config,
5273
- dependsOn: [],
5274
- resetOnChange: false
5275
- } : void 0;
5276
- if (!config?.key) return void 0;
5284
+ const components = formComponents;
5285
+ function normalizeDataSourceRegistration(registration) {
5286
+ if (typeof registration === "function") return {
5287
+ dependencies: [],
5288
+ reset: false,
5289
+ load: registration
5290
+ };
5277
5291
  return {
5278
- key: config.key,
5279
- dependsOn: Array.isArray(config.dependsOn) ? config.dependsOn : [],
5280
- resetOnChange: config.resetOnChange === true
5292
+ dependencies: Array.isArray(registration.dependencies) ? registration.dependencies : [],
5293
+ reset: registration.reset === true,
5294
+ load: registration.load
5281
5295
  };
5282
5296
  }
5297
+ const dataSourceRegistry = createRegistry("data source");
5298
+ const dataSources = {
5299
+ register(name, registration) {
5300
+ dataSourceRegistry.register(name, normalizeDataSourceRegistration(registration));
5301
+ },
5302
+ unregister: dataSourceRegistry.unregister,
5303
+ get: dataSourceRegistry.get,
5304
+ all: dataSourceRegistry.all,
5305
+ subscribe: dataSourceRegistry.subscribe
5306
+ };
5307
+ function normalizeDataSourceConfig(config) {
5308
+ if (typeof config === "string") return config.length > 0 ? { key: config } : void 0;
5309
+ }
5283
5310
  function normalizeOptions(result) {
5284
5311
  const options = Array.isArray(result) ? result : result?.options;
5285
5312
  if (!Array.isArray(options)) return [];
@@ -5345,9 +5372,7 @@ function useRegistryVersion$1(subscribe) {
5345
5372
  return version;
5346
5373
  }
5347
5374
  function isDataSourceConfig(value) {
5348
- if (typeof value === "string") return value.length > 0;
5349
- if (!value || typeof value !== "object" || Array.isArray(value)) return false;
5350
- return typeof value.key === "string";
5375
+ return typeof value === "string" && value.length > 0;
5351
5376
  }
5352
5377
  function collectDataSourceConfigs(schema, result = {}) {
5353
5378
  const properties = schema.properties;
@@ -5377,17 +5402,17 @@ function useFormDataSources(form, schema) {
5377
5402
  const loadFieldOptions = async (fieldName, source) => {
5378
5403
  const version = (loadVersions.get(fieldName) ?? 0) + 1;
5379
5404
  loadVersions.set(fieldName, version);
5380
- const loader = dataSources.get(source.key);
5381
- if (!loader) {
5405
+ const entry = dataSources.get(source.key);
5406
+ if (!entry) {
5382
5407
  form.setFieldState(fieldName, (state) => {
5383
5408
  state.dataSource = [];
5384
5409
  });
5385
5410
  return;
5386
5411
  }
5387
5412
  try {
5388
- const options = normalizeOptions(await loader({
5413
+ const options = normalizeOptions(await entry.load({
5389
5414
  field: fieldName,
5390
- values: Object.fromEntries(source.dependsOn.map((dependency) => [dependency, form.getValuesIn(dependency)])),
5415
+ values: Object.fromEntries(entry.dependencies.map((dependency) => [dependency, form.getValuesIn(dependency)])),
5391
5416
  signal: controller?.signal
5392
5417
  }));
5393
5418
  if (!active || loadVersions.get(fieldName) !== version) return;
@@ -5404,10 +5429,11 @@ function useFormDataSources(form, schema) {
5404
5429
  };
5405
5430
  for (const [fieldName, source] of sourceEntries) loadFieldOptions(fieldName, source);
5406
5431
  form.addEffects(effectId, () => {
5407
- for (const dependency of new Set(sourceEntries.flatMap(([, source]) => source.dependsOn))) (0, __formily_core.onFieldValueChange)(dependency, () => {
5432
+ for (const dependency of new Set(sourceEntries.flatMap(([, source]) => dataSources.get(source.key)?.dependencies ?? []))) (0, __formily_core.onFieldValueChange)(dependency, () => {
5408
5433
  for (const [fieldName, source] of sourceEntries) {
5409
- if (!source.dependsOn.includes(dependency)) continue;
5410
- if (source.resetOnChange) form.setValuesIn(fieldName, void 0);
5434
+ const entry = dataSources.get(source.key);
5435
+ if (!entry?.dependencies.includes(dependency)) continue;
5436
+ if (entry.reset) form.setValuesIn(fieldName, void 0);
5411
5437
  loadFieldOptions(fieldName, source);
5412
5438
  }
5413
5439
  });
@@ -5446,8 +5472,8 @@ function AutoFormInner({ schema: zodSchema, initialValues, onSubmit, overrides,
5446
5472
  }, [labelAlign, labelWidth]);
5447
5473
  const fieldComponents = (0, react.useMemo)(() => ({ fields: {
5448
5474
  ...defaultFieldComponents,
5449
- ...formComponents.all()
5450
- } }), [useRegistryVersion$1(formComponents.subscribe)]);
5475
+ ...components.all()
5476
+ } }), [useRegistryVersion$1(components.subscribe)]);
5451
5477
  useFormDataSources(form, formSchema);
5452
5478
  const handleSubmit = async () => {
5453
5479
  await form.validate();
@@ -6260,6 +6286,44 @@ function ImportDialog({ open, onOpenChange, onImport, columns = [], title, local
6260
6286
 
6261
6287
  //#endregion
6262
6288
  //#region src/components/auto-crud/auto-crud-table.tsx
6289
+ let autoCrudToolbarResolver = null;
6290
+ function setAutoCrudToolbarResolver(resolver) {
6291
+ autoCrudToolbarResolver = resolver;
6292
+ }
6293
+ function getDefaultToolbarActions() {
6294
+ return [
6295
+ { type: "import" },
6296
+ { type: "export" },
6297
+ { type: "create" }
6298
+ ];
6299
+ }
6300
+ function resolveOwnerToolbarActions(toolbar) {
6301
+ const resolved = typeof toolbar === "function" ? toolbar(getDefaultToolbarActions()) : toolbar;
6302
+ return resolved !== void 0 && resolved.length > 0 ? [...resolved] : getDefaultToolbarActions();
6303
+ }
6304
+ function readRowIds(rows, idKey) {
6305
+ return rows.flatMap((row) => {
6306
+ const value = row[idKey];
6307
+ return value === null || value === void 0 ? [] : [String(value)];
6308
+ });
6309
+ }
6310
+ function areStringArraysEqual(left, right) {
6311
+ return left.length === right.length && left.every((value, index) => value === right[index]);
6312
+ }
6313
+ function haveSameRowSelection(left, right, idKey) {
6314
+ if (left.length !== right.length) return false;
6315
+ const leftIds = readRowIds(left, idKey);
6316
+ const rightIds = readRowIds(right, idKey);
6317
+ if (leftIds.length === left.length && rightIds.length === right.length) return areStringArraysEqual(leftIds, rightIds);
6318
+ return left.every((row, index) => row === right[index]);
6319
+ }
6320
+ function readCreateAction(actions) {
6321
+ return actions.find((action) => action.type === "create");
6322
+ }
6323
+ function resolveToolbarActionsWithResolver(targetId, ownerActions, context) {
6324
+ const resolver = autoCrudToolbarResolver;
6325
+ return targetId !== void 0 && targetId.length > 0 && resolver !== null ? resolver(targetId, ownerActions, context) : ownerActions;
6326
+ }
6263
6327
  function mergeFieldPart(base, override) {
6264
6328
  if (override === void 0) return base;
6265
6329
  if (override === false || base === false || typeof base !== "object" || typeof override !== "object" || base === null || override === null || Array.isArray(base) || Array.isArray(override)) return override;
@@ -6346,10 +6410,10 @@ function useDynamicFilterOptions(fields) {
6346
6410
  let active = true;
6347
6411
  const controller = typeof AbortController === "undefined" ? void 0 : new AbortController();
6348
6412
  Promise.all(sourceEntries.map(async ({ field, source }) => {
6349
- const loader = dataSources.get(source.key);
6350
- if (!loader) return [field, []];
6413
+ const entry = dataSources.get(source.key);
6414
+ if (!entry) return [field, []];
6351
6415
  try {
6352
- return [field, normalizeOptions(await loader({
6416
+ return [field, normalizeOptions(await entry.load({
6353
6417
  field,
6354
6418
  values: {},
6355
6419
  signal: controller?.signal
@@ -6583,7 +6647,7 @@ function renderFieldValue(value, type, booleanLocale, options) {
6583
6647
  /**
6584
6648
  * ViewModal 组件 - 详情查看弹窗
6585
6649
  */
6586
- function ViewModal({ open, onOpenChange, variant, data, schema, fields: fieldConfig, denyFields, locale }) {
6650
+ function ViewModal({ open, onOpenChange, variant, data, schema, fields: fieldConfig, dynamicOptions, denyFields, locale }) {
6587
6651
  if (!data) return null;
6588
6652
  const shape = schema.shape;
6589
6653
  const denySet = new Set(denyFields ?? []);
@@ -6596,7 +6660,7 @@ function ViewModal({ open, onOpenChange, variant, data, schema, fields: fieldCon
6596
6660
  const parsed = parseZodField(fieldSchema);
6597
6661
  const label = fieldConfig?.[key]?.label ?? humanize(key);
6598
6662
  const value = data[key];
6599
- const options = normalizeFieldOptions(fieldConfig?.[key]?.enum);
6663
+ const options = normalizeFieldOptions(fieldConfig?.[key]?.enum) ?? normalizeFieldOptions(dynamicOptions?.[key]);
6600
6664
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
6601
6665
  className: "grid grid-cols-3 items-start gap-4",
6602
6666
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("dt", {
@@ -6618,6 +6682,7 @@ function ViewModal({ open, onOpenChange, variant, data, schema, fields: fieldCon
6618
6682
  open,
6619
6683
  onOpenChange,
6620
6684
  children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(__pixpilot_shadcn.DialogContent, {
6685
+ "aria-describedby": void 0,
6621
6686
  className: "max-w-2xl max-h-[80vh] overflow-y-auto",
6622
6687
  children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(__pixpilot_shadcn.DialogHeader, { children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(__pixpilot_shadcn.DialogTitle, { children: locale.viewModal.title }) }), content]
6623
6688
  })
@@ -6709,7 +6774,7 @@ function resolveActions(actionsOrFn, defaults, rowActionsLocale) {
6709
6774
  *
6710
6775
  * 高级 CRUD 表格组件,封装了完整的增删改查流程
6711
6776
  */
6712
- function AutoCrudTable({ title, description, schema, resource, fields, table: tableConfig, form: formConfig, permissions, actions: actionItems, toolbarActions, locale: localeProp, onCreate }) {
6777
+ function AutoCrudTable({ id, title, description, schema, resource, fields, table: tableConfig, form: formConfig, permissions, actions: actionItems, toolbar, toolbarActions, locale: localeProp, onCreate }) {
6713
6778
  const locale = resolveLocale(localeProp);
6714
6779
  const resolvedSchema = resource.schema ?? schema;
6715
6780
  const resolvedFields = react.useMemo(() => mergeFields(resource.fields, fields) ?? {}, [fields, resource.fields]);
@@ -6724,10 +6789,35 @@ function AutoCrudTable({ title, description, schema, resource, fields, table: ta
6724
6789
  const canImport = can.import && !!resource.handlers.import;
6725
6790
  const canExport = can.export;
6726
6791
  const [importOpen, setImportOpen] = react.useState(false);
6727
- const [selectedCount, setSelectedCount] = react.useState(0);
6792
+ const [selectedRows, setSelectedRows] = react.useState([]);
6728
6793
  const [exporting, setExporting] = react.useState(false);
6729
6794
  const getSelectedRowsRef = react.useRef(null);
6730
6795
  const dynamicFilterOptions = useDynamicFilterOptions(resolvedFields);
6796
+ const resourceIdKey = resource.idKey ?? "id";
6797
+ const ownerToolbarActions = react.useMemo(() => resolveOwnerToolbarActions(toolbar ?? toolbarActions), [toolbar, toolbarActions]);
6798
+ const selectedCount = selectedRows.length;
6799
+ const selectedRowIds = react.useMemo(() => readRowIds(selectedRows, resourceIdKey), [selectedRows, resourceIdKey]);
6800
+ const rowIds = react.useMemo(() => readRowIds(resource.tableData.data, resourceIdKey), [resource.tableData.data, resourceIdKey]);
6801
+ const handleSelectedRowsChange = react.useCallback((rows) => {
6802
+ setSelectedRows((currentRows) => haveSameRowSelection(currentRows, rows, resourceIdKey) ? currentRows : rows);
6803
+ }, [resourceIdKey]);
6804
+ const ownerCreateAction = react.useMemo(() => readCreateAction(ownerToolbarActions), [ownerToolbarActions]);
6805
+ const toolbarOpenCreate = can.create ? ownerCreateAction?.onClick ?? onCreate ?? resource.handlers.openCreate : void 0;
6806
+ const resolvedToolbarActions = resolveToolbarActionsWithResolver(id, ownerToolbarActions, react.useMemo(() => ({
6807
+ crudId: id ?? "",
6808
+ idKey: resourceIdKey,
6809
+ rowIds,
6810
+ selectedRowIds,
6811
+ selectedCount,
6812
+ ...toolbarOpenCreate ? { openCreate: toolbarOpenCreate } : {}
6813
+ }), [
6814
+ id,
6815
+ resourceIdKey,
6816
+ rowIds,
6817
+ selectedRowIds,
6818
+ selectedCount,
6819
+ toolbarOpenCreate
6820
+ ]));
6731
6821
  const importColumns = react.useMemo(() => {
6732
6822
  const shape = resolvedSchema.shape;
6733
6823
  const denySet = new Set(denyFields ?? []);
@@ -6737,9 +6827,9 @@ function AutoCrudTable({ title, description, schema, resource, fields, table: ta
6737
6827
  const filename = title ?? "export";
6738
6828
  const excludeColumns = denyFields;
6739
6829
  if (mode === "selected") {
6740
- const selectedRows = getSelectedRowsRef.current?.();
6741
- if (!selectedRows || selectedRows.length === 0) return;
6742
- exportAllToCSV(selectedRows, {
6830
+ const selectedRows$1 = getSelectedRowsRef.current?.();
6831
+ if (!selectedRows$1 || selectedRows$1.length === 0) return;
6832
+ exportAllToCSV(selectedRows$1, {
6743
6833
  filename,
6744
6834
  excludeColumns
6745
6835
  });
@@ -6833,11 +6923,6 @@ function AutoCrudTable({ title, description, schema, resource, fields, table: ta
6833
6923
  }, "create");
6834
6924
  return null;
6835
6925
  };
6836
- const resolvedToolbarActions = typeof toolbarActions === "function" ? toolbarActions([
6837
- { type: "import" },
6838
- { type: "export" },
6839
- { type: "create" }
6840
- ]) : toolbarActions;
6841
6926
  if (!resolvedToolbarActions || resolvedToolbarActions.length === 0) return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
6842
6927
  className: "flex items-center gap-2",
6843
6928
  children: [
@@ -6895,7 +6980,7 @@ function AutoCrudTable({ title, description, schema, resource, fields, table: ta
6895
6980
  enableExport: canExport,
6896
6981
  showDefaultExport: false,
6897
6982
  initialSorting: tableConfig?.defaultSort,
6898
- onSelectedCountChange: setSelectedCount,
6983
+ onSelectedRowsChange: handleSelectedRowsChange,
6899
6984
  getSelectedRows: getSelectedRowsRef
6900
6985
  }),
6901
6986
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)(CrudFormModal, {
@@ -6922,6 +7007,7 @@ function AutoCrudTable({ title, description, schema, resource, fields, table: ta
6922
7007
  data: resource.modal.selected,
6923
7008
  schema: resolvedSchema,
6924
7009
  fields: resolvedFields,
7010
+ dynamicOptions: dynamicFilterOptions,
6925
7011
  denyFields,
6926
7012
  locale
6927
7013
  }),
@@ -7969,6 +8055,7 @@ function useAutoCrudResource({ router, schema, query: queryTransform, options =
7969
8055
  ]);
7970
8056
  const canExport = !!(exportFetcher || exportQuery);
7971
8057
  return {
8058
+ idKey,
7972
8059
  tableData: {
7973
8060
  data: tableRows,
7974
8061
  pageCount: listQuery.data?.pageCount ?? 0,
@@ -8327,6 +8414,7 @@ exports.MultiCombobox = MultiCombobox;
8327
8414
  exports.SchemaAdapter = SchemaAdapter;
8328
8415
  exports.cn = cn;
8329
8416
  exports.coerceRowValues = coerceRowValues;
8417
+ exports.components = components;
8330
8418
  exports.createActionsColumn = createActionsColumn;
8331
8419
  exports.createEditFormSchema = createEditFormSchema;
8332
8420
  exports.createFormSchema = createFormSchema;
@@ -8362,6 +8450,7 @@ exports.parseImportFile = parseImportFile;
8362
8450
  exports.parseJSON = parseJSON;
8363
8451
  exports.parseZodField = parseZodField;
8364
8452
  exports.resolveLocale = resolveLocale;
8453
+ exports.setAutoCrudToolbarResolver = setAutoCrudToolbarResolver;
8365
8454
  exports.setSearchParams = setSearchParams;
8366
8455
  exports.useAutoCrudResource = useAutoCrudResource;
8367
8456
  exports.useDataTable = useDataTable;