@stcn52/pro 0.2.0 → 0.2.1

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.d.cts CHANGED
@@ -2491,7 +2491,7 @@ type AttachmentUploadProps = {
2491
2491
  size: number;
2492
2492
  }, progress: (pct: number) => void): Promise<void> | void;
2493
2493
  onRemove(relationId: string): void;
2494
- /** Names the record in the remove copy; the host already holds it. */
2494
+ /** Record identity: changing it cancels pending reads and resets attachment dialogs. */
2495
2495
  entityId?: string;
2496
2496
  /** Raised for the two failures no callback can carry: a refused file and a broken read. */
2497
2497
  onNotify?: Notify;
@@ -2502,7 +2502,8 @@ type AttachmentUploadProps = {
2502
2502
  * already linked to this record with a confirmed unlink. It reads the bytes
2503
2503
  * itself — the bar cannot pass 88% until the read finishes — and hands the
2504
2504
  * finished file to the host, which owns the store write.
2505
- */ declare function AttachmentUpload({ files, label, maxSizeMb, onUpload, onRemove, entityId, onNotify, }: AttachmentUploadProps): react.JSX.Element;
2505
+ */
2506
+ declare function AttachmentUpload(props: AttachmentUploadProps): react.JSX.Element;
2506
2507
 
2507
2508
  /** One kind the picker can link to; `kind` is the host's own collection name. */
2508
2509
  type LinkTarget = {
package/dist/index.d.ts CHANGED
@@ -2491,7 +2491,7 @@ type AttachmentUploadProps = {
2491
2491
  size: number;
2492
2492
  }, progress: (pct: number) => void): Promise<void> | void;
2493
2493
  onRemove(relationId: string): void;
2494
- /** Names the record in the remove copy; the host already holds it. */
2494
+ /** Record identity: changing it cancels pending reads and resets attachment dialogs. */
2495
2495
  entityId?: string;
2496
2496
  /** Raised for the two failures no callback can carry: a refused file and a broken read. */
2497
2497
  onNotify?: Notify;
@@ -2502,7 +2502,8 @@ type AttachmentUploadProps = {
2502
2502
  * already linked to this record with a confirmed unlink. It reads the bytes
2503
2503
  * itself — the bar cannot pass 88% until the read finishes — and hands the
2504
2504
  * finished file to the host, which owns the store write.
2505
- */ declare function AttachmentUpload({ files, label, maxSizeMb, onUpload, onRemove, entityId, onNotify, }: AttachmentUploadProps): react.JSX.Element;
2505
+ */
2506
+ declare function AttachmentUpload(props: AttachmentUploadProps): react.JSX.Element;
2506
2507
 
2507
2508
  /** One kind the picker can link to; `kind` is the host's own collection name. */
2508
2509
  type LinkTarget = {
package/dist/index.js CHANGED
@@ -89,7 +89,10 @@ function useControllableState(options) {
89
89
  });
90
90
  const setValue = useCallback((next) => {
91
91
  const resolved = typeof next === "function" ? next(currentRef.current) : next;
92
- if (!controlledRef.current) setUncontrolled(resolved);
92
+ if (!controlledRef.current) {
93
+ currentRef.current = resolved;
94
+ setUncontrolled(resolved);
95
+ }
93
96
  onChangeRef.current?.(resolved);
94
97
  }, []);
95
98
  return [current, setValue];
@@ -2326,7 +2329,7 @@ function SelectionBar({
2326
2329
  "aria-expanded": moreOpen,
2327
2330
  onClick: () => setMoreOpen(true),
2328
2331
  children: [
2329
- /* @__PURE__ */ jsx(Icon, { name: "menu", size: 14 }),
2332
+ /* @__PURE__ */ jsx(Icon, { name: "more", size: 14 }),
2330
2333
  " ",
2331
2334
  locale === "zh-CN" ? "\u66F4\u591A" : "More"
2332
2335
  ]
@@ -2356,7 +2359,8 @@ function buildBulkCommands({
2356
2359
  more,
2357
2360
  onNotify,
2358
2361
  onEditProperties,
2359
- onClear
2362
+ onClear,
2363
+ onComplete
2360
2364
  }) {
2361
2365
  const count = keys.length;
2362
2366
  const many = count === 1 ? noun.one : noun.many;
@@ -2364,7 +2368,8 @@ function buildBulkCommands({
2364
2368
  if (!count) return;
2365
2369
  const finish = () => {
2366
2370
  onNotify?.(message2);
2367
- onClear();
2371
+ if (onComplete) onComplete(keys);
2372
+ else onClear();
2368
2373
  };
2369
2374
  const result = run();
2370
2375
  if (result) return result.then(finish);
@@ -4069,6 +4074,7 @@ function BulkBar({
4069
4074
  someSelected,
4070
4075
  onToggleAll,
4071
4076
  onClear,
4077
+ onComplete,
4072
4078
  onEditProperties,
4073
4079
  onExportSelection,
4074
4080
  onDelete,
@@ -4100,7 +4106,8 @@ function BulkBar({
4100
4106
  ],
4101
4107
  onNotify,
4102
4108
  onEditProperties,
4103
- onClear
4109
+ onClear,
4110
+ onComplete
4104
4111
  });
4105
4112
  return /* @__PURE__ */ jsx(
4106
4113
  SelectionBar,
@@ -4122,6 +4129,7 @@ function BulkBar({
4122
4129
 
4123
4130
  // src/components/table/geometry.ts
4124
4131
  var LEADING_WIDTH = 44;
4132
+ var COMPACT_ACTIONS_WIDTH = 96;
4125
4133
  function widthStyle(width) {
4126
4134
  if (width === void 0) return void 0;
4127
4135
  return { width: typeof width === "number" ? `${width}px` : width };
@@ -4156,6 +4164,7 @@ function pinStyle(pin, direction = "ltr") {
4156
4164
  }
4157
4165
  function SkeletonRows({
4158
4166
  columns,
4167
+ compact,
4159
4168
  rows,
4160
4169
  widths,
4161
4170
  selectable,
@@ -4212,7 +4221,16 @@ function SkeletonRows({
4212
4221
  },
4213
4222
  column.id
4214
4223
  )),
4215
- hasRowActions ? /* @__PURE__ */ jsx("td", { className: cx("pro-table__actions-col", pinned && "is-fixed-right-first") }) : null,
4224
+ hasRowActions ? /* @__PURE__ */ jsx(
4225
+ "td",
4226
+ {
4227
+ className: cx(
4228
+ "pro-table__actions-col",
4229
+ pinned && (compact ? pinClass({ side: "right"}, true) : "is-fixed-right-first")
4230
+ ),
4231
+ style: pinned && compact ? pinStyle({ side: "right", offset: 0 }, direction) : void 0
4232
+ }
4233
+ ) : null,
4216
4234
  /* @__PURE__ */ jsx("td", { className: "pro-table__filler" }),
4217
4235
  hasHeaderTools ? /* @__PURE__ */ jsx("td", { className: "pro-table__tool-col" }) : null
4218
4236
  ] }, `skeleton-${rowIndex}`)) });
@@ -4235,7 +4253,7 @@ function ColumnMenu({
4235
4253
  close();
4236
4254
  };
4237
4255
  const items = [
4238
- ...column.sortable && onSortChange ? [
4256
+ ...(column.sortable || column.sorter) && onSortChange ? [
4239
4257
  {
4240
4258
  id: "sort",
4241
4259
  label: "Sort by",
@@ -4489,10 +4507,10 @@ function TableHead({
4489
4507
  "aria-label": `Sort by ${typeof column.header === "string" ? column.header : column.id}`,
4490
4508
  children: [
4491
4509
  column.header,
4492
- /* @__PURE__ */ jsxs("span", { className: "pro-table__sort-icon", "aria-hidden": "true", children: [
4493
- /* @__PURE__ */ jsx(Icon, { name: sorted && current?.direction === "desc" ? "angle-down" : "angle-up", size: 9 }),
4494
- /* @__PURE__ */ jsx(Icon, { name: sorted && current?.direction === "asc" ? "angle-up" : "angle-down", size: 9 })
4495
- ] })
4510
+ /* @__PURE__ */ jsx("span", { className: "pro-table__sort-icon", "aria-hidden": "true", children: current ? /* @__PURE__ */ jsx(Icon, { name: current.direction === "asc" ? "angle-up" : "angle-down", size: 9 }) : /* @__PURE__ */ jsxs(Fragment, { children: [
4511
+ /* @__PURE__ */ jsx(Icon, { name: "angle-up", size: 9 }),
4512
+ /* @__PURE__ */ jsx(Icon, { name: "angle-down", size: 9 })
4513
+ ] }) })
4496
4514
  ]
4497
4515
  }
4498
4516
  ) : column.header,
@@ -4639,17 +4657,22 @@ function nextCell(key, cell, rowCount, colCount) {
4639
4657
  else return null;
4640
4658
  return next;
4641
4659
  }
4642
- function CellCommands({ actions, row }) {
4660
+ function CellCommands({
4661
+ actions,
4662
+ row,
4663
+ disabled
4664
+ }) {
4643
4665
  if (actions.length === 0) return null;
4644
4666
  return /* @__PURE__ */ jsx("span", { className: "pro-table__cell-actions", children: actions.map((action) => /* @__PURE__ */ jsx(Tooltip, { label: action.label, children: /* @__PURE__ */ jsx(
4645
4667
  "button",
4646
4668
  {
4647
4669
  type: "button",
4670
+ disabled,
4648
4671
  className: "pro-table__cell-action",
4649
4672
  "aria-label": action.label,
4650
4673
  onClick: (event) => {
4651
4674
  event.stopPropagation();
4652
- action.onSelect(row);
4675
+ if (!disabled) action.onSelect(row);
4653
4676
  },
4654
4677
  children: /* @__PURE__ */ jsx(Icon, { name: action.icon, size: 14 })
4655
4678
  }
@@ -4833,12 +4856,13 @@ function TableRow({
4833
4856
  {
4834
4857
  className: cx(
4835
4858
  "pro-table__cell",
4859
+ column.ellipsis === false && "pro-table__cell--wrap",
4836
4860
  column.align === "center" && "pro-table__cell--center",
4837
4861
  column.align === "right" && "pro-table__cell--right"
4838
4862
  ),
4839
4863
  children: [
4840
4864
  /* @__PURE__ */ jsx(CellContent, { value: column.render(row, index), ellipsis: column.ellipsis }),
4841
- column.actions?.length ? /* @__PURE__ */ jsx(CellCommands, { actions: column.actions, row }) : null
4865
+ column.actions?.length ? /* @__PURE__ */ jsx(CellCommands, { actions: column.actions, row, disabled }) : null
4842
4866
  ]
4843
4867
  }
4844
4868
  )
@@ -4850,7 +4874,11 @@ function TableRow({
4850
4874
  /* @__PURE__ */ jsx(
4851
4875
  "td",
4852
4876
  {
4853
- className: cx("pro-table__actions-col", pinned && "is-fixed-right-first"),
4877
+ className: cx(
4878
+ "pro-table__actions-col",
4879
+ pinned && (compact ? pinClass({ side: "right"}, true) : "is-fixed-right-first")
4880
+ ),
4881
+ style: pinned && compact ? pinStyle({ side: "right", offset: 0 }, direction) : void 0,
4854
4882
  onClick: (event) => event.stopPropagation(),
4855
4883
  children: compact ? /* @__PURE__ */ jsx("div", { className: "pro-table__cell pro-table__cell--actions", children: rowActions(row) }) : /* @__PURE__ */ jsx("span", { className: "pro-table__row-actions", children: rowActions(row) })
4856
4884
  }
@@ -5012,6 +5040,44 @@ function DataTable({
5012
5040
  const { direction, locale } = useProConfig();
5013
5041
  const { leaves: columns, headers } = useMemo(() => tableColumns(suppliedColumns), [suppliedColumns]);
5014
5042
  const selectionName = useId();
5043
+ const selectionMounted = useRef(false);
5044
+ useLayoutEffect(() => {
5045
+ selectionMounted.current = true;
5046
+ return () => {
5047
+ selectionMounted.current = false;
5048
+ };
5049
+ }, []);
5050
+ const latestSelection = useRef({
5051
+ selectedKeys,
5052
+ onSelectedKeysChange,
5053
+ suppliedRows,
5054
+ rowKey,
5055
+ getChildren,
5056
+ isRowSelectable,
5057
+ onNotify
5058
+ });
5059
+ useLayoutEffect(() => {
5060
+ latestSelection.current = {
5061
+ selectedKeys,
5062
+ onSelectedKeysChange,
5063
+ suppliedRows,
5064
+ rowKey,
5065
+ getChildren,
5066
+ isRowSelectable,
5067
+ onNotify
5068
+ };
5069
+ });
5070
+ const clearSelection = (completed) => {
5071
+ if (!selectionMounted.current) return;
5072
+ const current = latestSelection.current;
5073
+ const locked = new Set(
5074
+ flattenRecords(tableRecords(current.suppliedRows, current.rowKey, current.getChildren), null).filter(({ row }) => current.isRowSelectable?.(row) === false).map(({ key }) => key)
5075
+ );
5076
+ const processed2 = completed ? new Set(completed) : void 0;
5077
+ current.onSelectedKeysChange?.(
5078
+ current.selectedKeys.filter((key) => locked.has(key) || processed2 !== void 0 && !processed2.has(key))
5079
+ );
5080
+ };
5015
5081
  const [scrollTop, setScrollTop] = useState(0);
5016
5082
  const [focusedKey, setFocusedKey] = useState(null);
5017
5083
  const [headerHeight, setHeaderHeight] = useState(44);
@@ -5134,7 +5200,7 @@ function DataTable({
5134
5200
  const columnsCount = columns.length + (selectable ? 1 : 0) + (showIndex ? 1 : 0) + (rowActions ? 1 : 0) + (headerTools ? 1 : 0) + (hasExpansion ? 1 : 0) + 1;
5135
5201
  const pinned = columns.some((column) => column.fixed !== void 0);
5136
5202
  const leadingWidth = (selectable ? LEADING_WIDTH : 0) + (showIndex ? LEADING_WIDTH : 0) + (hasExpansion ? LEADING_WIDTH : 0);
5137
- const pins = pinOffsets(columns, leadingWidth, 0);
5203
+ const pins = pinOffsets(columns, leadingWidth, compact && rowActions ? COMPACT_ACTIONS_WIDTH : 0);
5138
5204
  const lastLeft = [...columns].reverse().find((column) => column.fixed === "left")?.id;
5139
5205
  const firstRight = columns.find((column) => column.fixed === "right")?.id;
5140
5206
  const rootRef = useRef(null);
@@ -5195,7 +5261,9 @@ function DataTable({
5195
5261
  const rowHeight = suppliedHeight !== void 0 && Number.isFinite(suppliedHeight) && suppliedHeight > 0 ? suppliedHeight : compact ? 36 : 45;
5196
5262
  const overscan = suppliedOverscan !== void 0 && Number.isFinite(suppliedOverscan) ? Math.max(0, Math.floor(suppliedOverscan)) : 3;
5197
5263
  const viewportHeight = typeof scroll?.y === "number" && Number.isFinite(scroll.y) && scroll.y > 0 ? scroll.y : 300;
5198
- const windowed = Boolean(virtual && !groupBy && !expandedRowRender && !columns.some((column) => column.onCell));
5264
+ const windowed = Boolean(
5265
+ virtual && !groupBy && !expandedRowRender && !columns.some((column) => column.onCell || column.ellipsis === false)
5266
+ );
5199
5267
  const start = windowed ? Math.min(
5200
5268
  Math.max(0, rows.length - Math.ceil(viewportHeight / rowHeight)),
5201
5269
  Math.max(0, Math.floor((scrollTop - (showHeader ? headerHeight : 0)) / rowHeight) - overscan)
@@ -5282,9 +5350,10 @@ function DataTable({
5282
5350
  widths: loadingColumns,
5283
5351
  selectable,
5284
5352
  showIndex,
5353
+ compact,
5285
5354
  hasRowActions: Boolean(rowActions),
5286
5355
  hasHeaderTools: Boolean(headerTools),
5287
- hasExpansion: Boolean(expandedRowRender),
5356
+ hasExpansion,
5288
5357
  pinned,
5289
5358
  pins,
5290
5359
  lastLeft,
@@ -5321,6 +5390,7 @@ function DataTable({
5321
5390
  const indexes = Array.from({ length: end - start }, (_, index) => start + index);
5322
5391
  const focusedIndex = focusedKey === null ? -1 : visibleRecords.findIndex((record) => record.key === focusedKey);
5323
5392
  if (windowed && focusedIndex >= 0 && !indexes.includes(focusedIndex)) indexes.push(focusedIndex);
5393
+ if (windowed && cursor && rows.length > 0 && !indexes.includes(cursorRow)) indexes.push(cursorRow);
5324
5394
  indexes.sort((a, b) => a - b);
5325
5395
  const spacer = (height, key) => /* @__PURE__ */ jsx("tr", { "aria-hidden": "true", children: /* @__PURE__ */ jsx("td", { colSpan: columnsCount, style: { height, padding: 0, border: 0 } }) }, key);
5326
5396
  body = /* @__PURE__ */ jsxs("tbody", { children: [
@@ -5368,17 +5438,14 @@ function DataTable({
5368
5438
  allSelected,
5369
5439
  someSelected,
5370
5440
  onToggleAll: toggleAll,
5371
- onClear: () => onSelectedKeysChange?.(
5372
- selectedKeys.filter(
5373
- (key) => flattenRecords(records, null).some(
5374
- ({ row }) => rowKey(row) === key && isRowSelectable?.(row) === false
5375
- )
5376
- )
5377
- ),
5441
+ onClear: () => clearSelection(),
5442
+ onComplete: clearSelection,
5378
5443
  onEditProperties,
5379
5444
  onExportSelection,
5380
5445
  onDelete,
5381
- onNotify
5446
+ onNotify: (message2) => {
5447
+ if (selectionMounted.current) latestSelection.current.onNotify?.(message2);
5448
+ }
5382
5449
  }
5383
5450
  ) : null,
5384
5451
  /* @__PURE__ */ jsxs(
@@ -5401,7 +5468,7 @@ function DataTable({
5401
5468
  showIndex ? /* @__PURE__ */ jsx("col", { style: { width: LEADING_WIDTH } }) : null,
5402
5469
  hasExpansion ? /* @__PURE__ */ jsx("col", { style: { width: LEADING_WIDTH } }) : null,
5403
5470
  columns.map((column) => /* @__PURE__ */ jsx("col", { style: { width: column.width } }, column.id)),
5404
- rowActions ? /* @__PURE__ */ jsx("col", { style: { width: compact ? 96 : 0 } }) : null,
5471
+ rowActions ? /* @__PURE__ */ jsx("col", { style: { width: compact ? COMPACT_ACTIONS_WIDTH : 0 } }) : null,
5405
5472
  /* @__PURE__ */ jsx("col", {}),
5406
5473
  headerTools ? /* @__PURE__ */ jsx("col", { style: { width: LEADING_WIDTH } }) : null
5407
5474
  ] }),
@@ -10458,7 +10525,10 @@ function isPromise(value) {
10458
10525
  function clamp(pct) {
10459
10526
  return Number.isFinite(pct) ? Math.max(0, Math.min(100, Math.round(pct))) : 0;
10460
10527
  }
10461
- function AttachmentUpload({
10528
+ function AttachmentUpload(props) {
10529
+ return /* @__PURE__ */ jsx(AttachmentUploadSession, { ...props }, props.entityId);
10530
+ }
10531
+ function AttachmentUploadSession({
10462
10532
  files,
10463
10533
  label,
10464
10534
  maxSizeMb = DEFAULT_MAX_MB,
@@ -11380,7 +11450,14 @@ function EntityDialog({
11380
11450
  const fieldValue = (id) => Object.hasOwn(values, id) && typeof values[id] === "string" ? values[id] : "";
11381
11451
  const dirty = Object.keys(values).some((key) => values[key] !== baseline[key]);
11382
11452
  const errors = Object.fromEntries(
11383
- [...fields, ...asideFields].filter((field) => field.required && !fieldValue(field.id).trim()).map((field) => [field.id, zh ? `\u8BF7\u586B\u5199${field.label}` : `${field.label} is required`])
11453
+ [...fields, ...asideFields].flatMap((field) => {
11454
+ const value = fieldValue(field.id);
11455
+ if (field.required && !value.trim())
11456
+ return [[field.id, zh ? `\u8BF7\u586B\u5199${field.label}` : `${field.label} is required`]];
11457
+ if (field.kind === "select" && value && !field.options?.some((option) => option.value === value))
11458
+ return [[field.id, zh ? `\u8BF7\u9009\u62E9\u6709\u6548\u7684${field.label}` : `Choose a valid ${field.label}`]];
11459
+ return [];
11460
+ })
11384
11461
  );
11385
11462
  const invalid = Object.keys(errors).length > 0;
11386
11463
  const idPrefix = useId();
@@ -13056,7 +13133,7 @@ function AssigneeCell({ field, row, rowKey }) {
13056
13133
  disabled,
13057
13134
  className: "pro-assignee-cell__assign",
13058
13135
  "aria-label": field.assignLabel?.(row) ?? `Assign ${rowKey}`,
13059
- tabIndex: -1,
13136
+ tabIndex: field.onAssign ? 0 : -1,
13060
13137
  onClick: (event) => {
13061
13138
  event.stopPropagation();
13062
13139
  if (field.onAssign) {
@@ -13249,6 +13326,7 @@ function ListWorkbench({
13249
13326
  const [deleteError, setDeleteError] = useState(false);
13250
13327
  const [sessionIdentity, setSessionIdentity] = useState(identity);
13251
13328
  const [hiddenColumns, setHiddenColumns] = useState([]);
13329
+ const [unfrozenColumns, setUnfrozenColumns] = useState([]);
13252
13330
  const [columnsOpen, setColumnsOpen] = useState(false);
13253
13331
  const [columnAnchor, setColumnAnchor] = useState(null);
13254
13332
  const [settingsOpen, setSettingsOpen] = useState(false);
@@ -13263,6 +13341,12 @@ function ListWorkbench({
13263
13341
  const editable = Boolean(editFields && entityKind && onEdit);
13264
13342
  const createSheet = Boolean(editFields && entityKind && onCreate);
13265
13343
  const filteredEmpty = Boolean(view.q || view.rules.length);
13344
+ const [session, setSession] = useState({ identity });
13345
+ if (session.identity !== identity) setSession({ identity });
13346
+ const boardSession = useRef(session);
13347
+ useLayoutEffect(() => {
13348
+ boardSession.current = session;
13349
+ }, [session]);
13266
13350
  const latest = useRef({ onImport, onNotify, onSelectionChange, selection, disabled });
13267
13351
  useLayoutEffect(() => {
13268
13352
  latest.current = { onImport, onNotify, onSelectionChange, selection, disabled };
@@ -13291,6 +13375,7 @@ function ListWorkbench({
13291
13375
  setBulkEditOpen(false);
13292
13376
  setBulkConfirm(false);
13293
13377
  setHiddenColumns([]);
13378
+ setUnfrozenColumns([]);
13294
13379
  }
13295
13380
  if (disabled && columnsOpen) setColumnsOpen(false);
13296
13381
  if (disabled && settingsOpen) setSettingsOpen(false);
@@ -13356,7 +13441,9 @@ function ListWorkbench({
13356
13441
  };
13357
13442
  }
13358
13443
  return column;
13359
- }).map((column) => frozen.includes(column.id) ? { ...column, fixed: "left" } : column);
13444
+ }).map(
13445
+ (column) => frozen.includes(column.id) ? { ...column, fixed: "left" } : unfrozenColumns.includes(column.id) ? { ...column, fixed: void 0 } : column
13446
+ );
13360
13447
  const groupValue = useMemo(() => {
13361
13448
  if (!view.group) return void 0;
13362
13449
  return (row) => {
@@ -13367,7 +13454,15 @@ function ListWorkbench({
13367
13454
  }, [view.group]);
13368
13455
  const exportCsv = (keys = []) => {
13369
13456
  if (disabled) return;
13370
- const source = keys.length > 0 ? sorted.filter((row) => keys.includes(rowKey(row))) : sorted;
13457
+ const selected = new Set(keys);
13458
+ const source = selected.size > 0 ? rows.filter((row) => selected.has(rowKey(row))) : sorted;
13459
+ if (selected.size > 0 && source.length !== selected.size) {
13460
+ notify(
13461
+ "error",
13462
+ zh ? "\u90E8\u5206\u6240\u9009\u8BB0\u5F55\u672A\u52A0\u8F7D\uFF0C\u8BF7\u52A0\u8F7D\u5B8C\u6574\u6570\u636E\u540E\u91CD\u8BD5\u3002" : "Some selected records are not loaded. Load all selected records and retry."
13463
+ );
13464
+ return;
13465
+ }
13371
13466
  try {
13372
13467
  if (onExport) download(`${slug}.csv`, onExport(source));
13373
13468
  else exportRowsToCsv(slug, source);
@@ -13650,7 +13745,7 @@ function ListWorkbench({
13650
13745
  {
13651
13746
  title: `No ${noun.many} to show`,
13652
13747
  hint: emptyBoardText ?? "Nothing matches the current filters. Clear them to see the full board.",
13653
- action: /* @__PURE__ */ jsx(Button, { variant: "default", onClick: () => update({ rules: [], page: 1 }), children: "Clear filters" })
13748
+ action: /* @__PURE__ */ jsx(Button, { variant: "default", onClick: () => update({ q: "", rules: [], page: 1 }), children: "Clear filters" })
13654
13749
  }
13655
13750
  ) : /* @__PURE__ */ jsxs(Fragment, { children: [
13656
13751
  selection.length > 0 ? /* @__PURE__ */ jsx(
@@ -13660,9 +13755,14 @@ function ListWorkbench({
13660
13755
  count: selection.length,
13661
13756
  clearLabel: "Cancel",
13662
13757
  onClear: () => onSelectionChange([]),
13663
- allSelected: selection.length === rows.length,
13664
- someSelected: selection.some((key) => rows.some((row) => rowKey(row) === key)),
13665
- onToggleAll: () => onSelectionChange(selection.length === rows.length ? [] : rows.map(rowKey)),
13758
+ allSelected: sorted.length > 0 && sorted.every((row) => selection.includes(rowKey(row))),
13759
+ someSelected: !sorted.every((row) => selection.includes(rowKey(row))) && selection.some((key) => sorted.some((row) => rowKey(row) === key)),
13760
+ onToggleAll: () => {
13761
+ const keys = sorted.map(rowKey);
13762
+ onSelectionChange(
13763
+ keys.every((key) => selection.includes(key)) ? selection.filter((key) => !keys.includes(key)) : [.../* @__PURE__ */ new Set([...selection, ...keys])]
13764
+ );
13765
+ },
13666
13766
  ...buildBulkCommands({
13667
13767
  bulk,
13668
13768
  keys: selection,
@@ -13676,9 +13776,16 @@ function ListWorkbench({
13676
13776
  onSelect: () => exportCsv(selection)
13677
13777
  }
13678
13778
  ],
13679
- onNotify: (message2) => notify("success", message2),
13779
+ onNotify: (message2) => {
13780
+ if (live.current && boardSession.current === session) notify("success", message2);
13781
+ },
13680
13782
  onEditProperties: bulk?.properties ? () => setBulkEditOpen(true) : void 0,
13681
- onClear: () => onSelectionChange([])
13783
+ onClear: () => onSelectionChange([]),
13784
+ onComplete: (keys) => {
13785
+ if (!live.current || boardSession.current !== session) return;
13786
+ const processed = new Set(keys);
13787
+ latest.current.onSelectionChange(latest.current.selection.filter((key) => !processed.has(key)));
13788
+ }
13682
13789
  })
13683
13790
  }
13684
13791
  ) : null,
@@ -13718,6 +13825,10 @@ function ListWorkbench({
13718
13825
  onSortChange: sortFields.length ? (sort) => update({ sort }) : void 0,
13719
13826
  onColumnCommand: (command) => {
13720
13827
  if (command.kind === "freeze") {
13828
+ if (command.fixed === null && columns.find((column) => column.id === command.columnId)?.fixed) {
13829
+ if (onColumnCommand) onColumnCommand(command);
13830
+ else setUnfrozenColumns((current) => [.../* @__PURE__ */ new Set([...current, command.columnId])]);
13831
+ }
13721
13832
  update({
13722
13833
  frozen: command.fixed === null ? frozen.filter((id) => id !== command.columnId) : [...frozen.filter((id) => id !== command.columnId), command.columnId]
13723
13834
  });
@@ -13779,7 +13890,10 @@ function ListWorkbench({
13779
13890
  )
13780
13891
  }
13781
13892
  ),
13782
- /* @__PURE__ */ jsx(
13893
+ view.group || view.view === "board" && boardView ? /* @__PURE__ */ jsxs("div", { className: "pro-row", style: { justifyContent: "space-between" }, children: [
13894
+ footerLeft ?? /* @__PURE__ */ jsx("span", { className: "pro-text-sm", children: title }),
13895
+ /* @__PURE__ */ jsx("span", { className: "pro-text-sm", children: zh ? `\u5171 ${total} \u6761` : `Total: ${total}` })
13896
+ ] }) : /* @__PURE__ */ jsx(
13783
13897
  Pagination,
13784
13898
  {
13785
13899
  page,
@@ -13846,6 +13960,7 @@ function ListWorkbench({
13846
13960
  onSelect: () => {
13847
13961
  update({ q: "", rules: [], sort: null, group: null, preview: "auto", page: 1 });
13848
13962
  setHiddenColumns([]);
13963
+ setUnfrozenColumns([]);
13849
13964
  }
13850
13965
  }
13851
13966
  ]
@@ -16897,6 +17012,10 @@ function FormRoot({
16897
17012
  const store = stores.get(instance);
16898
17013
  if (!store) throw new Error("Form requires an instance created by useForm");
16899
17014
  const config = useProConfig();
17015
+ const latestSubmit = useRef({ disabled: disabled ?? config.disabled, onFinish });
17016
+ useLayoutEffect(() => {
17017
+ latestSubmit.current = { disabled: disabled ?? config.disabled, onFinish };
17018
+ }, [disabled, config.disabled, onFinish]);
16900
17019
  const initial = useRef(initialValues);
16901
17020
  const submitting = useRef(false);
16902
17021
  const generation = useRef(0);
@@ -16938,9 +17057,9 @@ function FormRoot({
16938
17057
  }
16939
17058
  return;
16940
17059
  }
16941
- if (generation.current !== current) return;
17060
+ if (generation.current !== current || latestSubmit.current.disabled) return;
16942
17061
  try {
16943
- await onFinish?.(values);
17062
+ await latestSubmit.current.onFinish?.(values);
16944
17063
  } catch (error) {
16945
17064
  if (generation.current !== current) return;
16946
17065
  setSubmitError(true);