@canvas-components/list-table 0.3.25 → 0.3.27

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -1058,6 +1058,11 @@ function getNextRightFixedInsertIndex(columns) {
1058
1058
  }
1059
1059
 
1060
1060
  // src/features/header-settings/header-settings-feature.ts
1061
+ function haveVisibleLeafColumnsChanged(previous, next) {
1062
+ if (previous.length !== next.length) return true;
1063
+ const previousKeys = new Set(previous.map((column) => column.key));
1064
+ return next.some((column) => !previousKeys.has(column.key));
1065
+ }
1061
1066
  function cloneColumns(columns) {
1062
1067
  return columns.map((column) => ({
1063
1068
  ...column,
@@ -1184,6 +1189,7 @@ function saveHeaderSettingsColumn(columns, draft) {
1184
1189
  existing.minWidth = minWidth;
1185
1190
  existing.maxWidth = maxWidth;
1186
1191
  if (draft.order != null) moveColumnToOrder(siblings, existing, draft.order);
1192
+ draft.key = resolveColumnKey(existing);
1187
1193
  return null;
1188
1194
  }
1189
1195
  const dataIndex = draft.dataIndex?.trim();
@@ -1209,6 +1215,7 @@ function saveHeaderSettingsColumn(columns, draft) {
1209
1215
  } else if (!existing) {
1210
1216
  siblings.splice((draft.order ?? maxOrder) - 1, 0, column);
1211
1217
  }
1218
+ draft.key = resolveColumnKey(column);
1212
1219
  return null;
1213
1220
  }
1214
1221
  function findSiblingColumnsByKey(columns, key) {
@@ -1306,6 +1313,9 @@ function toggleHeaderColumnVisibility(params) {
1306
1313
  };
1307
1314
  }
1308
1315
  function resolveCollapsedHeaderColumnMarkers(params) {
1316
+ if (params.showCollapsedColumnIcon === false) {
1317
+ return [];
1318
+ }
1309
1319
  ensureColumnKeysInPlace(params.columns);
1310
1320
  const allLeafKeys = collectLeafColumnKeys(params.columns);
1311
1321
  const excludedKeySet = new Set(params.excludedColumnKeys ?? []);
@@ -1482,6 +1492,7 @@ function buildHeaderSettingsItem(column, totalVisibleLeafCount, parentHidden, or
1482
1492
  minWidth: column.minWidth,
1483
1493
  maxWidth: column.maxWidth,
1484
1494
  fixed: column.fixed,
1495
+ sortable: !column.children?.length && column.allowSort !== false && column.sorter !== false,
1485
1496
  order,
1486
1497
  siblingCount,
1487
1498
  checked,
@@ -1642,6 +1653,23 @@ function applyThemeVariables(wrapper, theme) {
1642
1653
  );
1643
1654
  }
1644
1655
 
1656
+ // src/interaction/pointer.ts
1657
+ function getCanvasPointerPosition(canvas, clientX, clientY) {
1658
+ const rect = canvas.getBoundingClientRect();
1659
+ const scale = getElementViewportScale(canvas, rect);
1660
+ return {
1661
+ x: (clientX - rect.left) / scale.x,
1662
+ y: (clientY - rect.top) / scale.y
1663
+ };
1664
+ }
1665
+ function getElementViewportScale(element, rect = element.getBoundingClientRect()) {
1666
+ const resolveScale = (viewportSize, layoutSize) => layoutSize > 0 && Math.abs(viewportSize - layoutSize) >= 1 ? viewportSize / layoutSize : 1;
1667
+ return {
1668
+ x: resolveScale(rect.width, element.clientWidth),
1669
+ y: resolveScale(rect.height, element.clientHeight)
1670
+ };
1671
+ }
1672
+
1645
1673
  // src/adapters/dom-host.ts
1646
1674
  var RESIZE_RENDER_SETTLE_DELAY = 240;
1647
1675
  var ListTableDomHost = class {
@@ -1811,23 +1839,27 @@ var ListTableDomHost = class {
1811
1839
  return this.nodes;
1812
1840
  }
1813
1841
  toViewportPoint(x, y) {
1814
- const hostRect = this.nodes?.canvasHostElement.getBoundingClientRect();
1815
- if (!hostRect) {
1842
+ const hostElement = this.nodes?.canvasHostElement;
1843
+ const hostRect = hostElement?.getBoundingClientRect();
1844
+ if (!hostElement || !hostRect) {
1816
1845
  return { x, y };
1817
1846
  }
1847
+ const scale = getElementViewportScale(hostElement, hostRect);
1818
1848
  return {
1819
- x: hostRect.left + x * this.contentZoom,
1820
- y: hostRect.top + y * this.contentZoom
1849
+ x: hostRect.left + x * this.contentZoom * scale.x,
1850
+ y: hostRect.top + y * this.contentZoom * scale.y
1821
1851
  };
1822
1852
  }
1823
1853
  toViewportRect(rect) {
1824
1854
  const point = this.toViewportPoint(rect.x, rect.y);
1855
+ const hostElement = this.nodes?.canvasHostElement;
1856
+ const scale = hostElement ? getElementViewportScale(hostElement) : { x: 1, y: 1 };
1825
1857
  return {
1826
1858
  ...rect,
1827
1859
  x: point.x,
1828
1860
  y: point.y,
1829
- width: rect.width * this.contentZoom,
1830
- height: rect.height * this.contentZoom
1861
+ width: rect.width * this.contentZoom * scale.x,
1862
+ height: rect.height * this.contentZoom * scale.y
1831
1863
  };
1832
1864
  }
1833
1865
  getCanvasViewportRect() {
@@ -1881,15 +1913,6 @@ var ListTableDomHost = class {
1881
1913
  }
1882
1914
  };
1883
1915
 
1884
- // src/interaction/pointer.ts
1885
- function getCanvasPointerPosition(canvas, clientX, clientY) {
1886
- const rect = canvas.getBoundingClientRect();
1887
- return {
1888
- x: clientX - rect.left,
1889
- y: clientY - rect.top
1890
- };
1891
- }
1892
-
1893
1916
  // src/adapters/canvas-host.ts
1894
1917
  var ListTableCanvasHost = class {
1895
1918
  constructor() {
@@ -3298,14 +3321,17 @@ function mapKindToArea(kind, actionType) {
3298
3321
  }
3299
3322
  function getPriority(descriptor) {
3300
3323
  let priority = getBasePriority(descriptor.kind);
3324
+ if (descriptor.debugNodeType === "header-edit-icon" || descriptor.debugNodeType === "nested-table-header-edit-icon") {
3325
+ priority += 1;
3326
+ }
3301
3327
  if (descriptor.kind === "summary" && descriptor.columnKey) {
3302
3328
  priority += 1;
3303
3329
  }
3304
3330
  if ((descriptor.kind === "body" || descriptor.kind === "body-content" || descriptor.kind === "body-control") && descriptor.frozen) {
3305
3331
  priority += 2;
3306
3332
  }
3307
- if (descriptor.kind === "body" && descriptor.fixed) {
3308
- priority += 1;
3333
+ if ((descriptor.kind === "body" || descriptor.kind === "body-content" || descriptor.kind === "body-control") && descriptor.fixed) {
3334
+ priority += 2;
3309
3335
  }
3310
3336
  if (isHeaderDescriptor(descriptor.kind) && descriptor.fixed) {
3311
3337
  priority += 3;
@@ -3325,6 +3351,9 @@ function getBasePriority(kind) {
3325
3351
  return 2;
3326
3352
  case "resize-handle":
3327
3353
  return 4;
3354
+ // 折叠列图标覆盖列宽热区,但仍低于固定列表头的遮挡层(优先级 5)。
3355
+ case "column-collapse-trigger":
3356
+ return 4.5;
3328
3357
  case "header-action":
3329
3358
  return 3;
3330
3359
  case "header-selection":
@@ -3337,7 +3366,6 @@ function getBasePriority(kind) {
3337
3366
  return 2;
3338
3367
  case "expand-trigger":
3339
3368
  case "collapse-trigger":
3340
- case "column-collapse-trigger":
3341
3369
  return 3;
3342
3370
  case "expanded-row":
3343
3371
  case "body":
@@ -3496,6 +3524,60 @@ function applyListTableRenderPassResult(params) {
3496
3524
  params.setScrollPageSize(projection.scrollPageSize);
3497
3525
  }
3498
3526
 
3527
+ // src/rendering/cell-content/text-measure-cache.ts
3528
+ var MAX_ENTRIES = 2048;
3529
+ var MAX_TEXT_LENGTH = 512;
3530
+ var fontRevisions = /* @__PURE__ */ new WeakMap();
3531
+ var contextCaches = /* @__PURE__ */ new WeakMap();
3532
+ function measureCachedText(ctx, text) {
3533
+ const fonts = ctx.canvas?.ownerDocument?.fonts;
3534
+ let fontRevision = 0;
3535
+ if (fonts) {
3536
+ let revision = fontRevisions.get(fonts);
3537
+ if (!revision) {
3538
+ revision = { value: 0 };
3539
+ fontRevisions.set(fonts, revision);
3540
+ const invalidate = () => {
3541
+ revision.value += 1;
3542
+ };
3543
+ fonts.addEventListener("loadingdone", invalidate);
3544
+ fonts.addEventListener("loadingerror", invalidate);
3545
+ }
3546
+ fontRevision = revision.value;
3547
+ if (fonts.status === "loading") {
3548
+ contextCaches.delete(ctx);
3549
+ return ctx.measureText(text);
3550
+ }
3551
+ }
3552
+ if (text.length > MAX_TEXT_LENGTH) return ctx.measureText(text);
3553
+ let cache = contextCaches.get(ctx);
3554
+ if (!cache || cache.fontRevision !== fontRevision) {
3555
+ cache = { fontRevision, entries: /* @__PURE__ */ new Map() };
3556
+ contextCaches.set(ctx, cache);
3557
+ }
3558
+ const key = JSON.stringify([
3559
+ ctx.font,
3560
+ ctx.textAlign,
3561
+ ctx.textBaseline,
3562
+ ctx.direction,
3563
+ ctx.fontKerning,
3564
+ ctx.fontStretch,
3565
+ ctx.fontVariantCaps,
3566
+ ctx.letterSpacing,
3567
+ ctx.wordSpacing,
3568
+ ctx.textRendering,
3569
+ text
3570
+ ]);
3571
+ const cached = cache.entries.get(key);
3572
+ if (cached) return cached;
3573
+ const metrics = ctx.measureText(text);
3574
+ if (cache.entries.size >= MAX_ENTRIES) {
3575
+ cache.entries.delete(cache.entries.keys().next().value);
3576
+ }
3577
+ cache.entries.set(key, metrics);
3578
+ return metrics;
3579
+ }
3580
+
3499
3581
  // src/rendering/cell-content/content-style.ts
3500
3582
  function resolveContentFontSize(content, fallbackFontSize) {
3501
3583
  return content.style?.fontSize ?? content.fontSize ?? fallbackFontSize;
@@ -3556,7 +3638,7 @@ function buildCanvasFont(params) {
3556
3638
  return `${fontWeight} ${fontSize}px ${fontFamily}`.trim();
3557
3639
  }
3558
3640
  function measureTextBounds(ctx, text) {
3559
- const metrics = ctx.measureText(text);
3641
+ const metrics = measureCachedText(ctx, text);
3560
3642
  const ascent = metrics.actualBoundingBoxAscent || 0;
3561
3643
  const descent = metrics.actualBoundingBoxDescent || 0;
3562
3644
  const width = metrics.width;
@@ -5211,11 +5293,41 @@ function drawHeaderActionGlyph(ctx, rect, actionType, color = "#8c8c8c", sortOrd
5211
5293
  ctx.fill();
5212
5294
  } else if (actionType === "filter") {
5213
5295
  drawFilterIcon(ctx, rect);
5214
- } else {
5296
+ } else if (actionType === "fixed") {
5215
5297
  drawFixedPinIcon(ctx, rect);
5298
+ } else {
5299
+ drawEditIcon(ctx, rect);
5216
5300
  }
5217
5301
  ctx.restore();
5218
5302
  }
5303
+ function drawEditIcon(ctx, rect) {
5304
+ const scale = Math.min(rect.width, rect.height) / 14;
5305
+ const x = rect.x + (rect.width - 14 * scale) / 2;
5306
+ const y = rect.y + (rect.height - 14 * scale) / 2;
5307
+ ctx.save();
5308
+ ctx.translate(x, y);
5309
+ ctx.scale(scale, scale);
5310
+ ctx.beginPath();
5311
+ ctx.moveTo(1.5, 10.15);
5312
+ ctx.lineTo(9.4, 2.25);
5313
+ ctx.lineTo(11.75, 4.6);
5314
+ ctx.lineTo(3.85, 12.5);
5315
+ ctx.lineTo(1.3, 12.7);
5316
+ ctx.closePath();
5317
+ ctx.fill();
5318
+ ctx.beginPath();
5319
+ ctx.moveTo(10.05, 1.6);
5320
+ ctx.lineTo(10.7, 0.95);
5321
+ ctx.lineTo(11.4, 0.6);
5322
+ ctx.lineTo(12.1, 0.95);
5323
+ ctx.lineTo(13.05, 1.9);
5324
+ ctx.lineTo(13.4, 2.6);
5325
+ ctx.lineTo(13.05, 3.3);
5326
+ ctx.lineTo(12.4, 3.95);
5327
+ ctx.closePath();
5328
+ ctx.fill();
5329
+ ctx.restore();
5330
+ }
5219
5331
  function drawFilterIcon(ctx, rect) {
5220
5332
  const iconWidth = rect.width * 0.92;
5221
5333
  const iconHeight = rect.height * 0.7;
@@ -7466,6 +7578,19 @@ function sortRows(rows, sortState, leafColumns) {
7466
7578
  return 0;
7467
7579
  });
7468
7580
  }
7581
+ async function sortRowsAsync(params) {
7582
+ const { rows, signal } = params;
7583
+ const indices = await sortRowIndicesAsync(params);
7584
+ if (!indices) {
7585
+ return rows;
7586
+ }
7587
+ return materializeRows({
7588
+ rows,
7589
+ indices,
7590
+ signal,
7591
+ abortMessage: "sort rows aborted"
7592
+ });
7593
+ }
7469
7594
  async function sortRowIndicesAsync(params) {
7470
7595
  let rowIndices = params.rowIndices ?? null;
7471
7596
  const sorters = orderSortingState(params.sortState);
@@ -7709,6 +7834,21 @@ async function mapToSourceRowIndices(params) {
7709
7834
  recordQueryStageDuration(params.trace, "indexMapping", mappingStartedAt);
7710
7835
  return result;
7711
7836
  }
7837
+ async function materializeRows(params) {
7838
+ const { rows, indices, signal, abortMessage } = params;
7839
+ const result = Array(indices.length);
7840
+ let sliceStartedAt = getTaskNow();
7841
+ for (let index = 0; index < indices.length; index += 1) {
7842
+ throwIfTaskAborted(signal, abortMessage);
7843
+ result[index] = rows[indices[index]];
7844
+ if ((index + 1) % ASYNC_QUERY_SLICE_ITEM_LIMIT === 0 || (index + 1) % 256 === 0 && isTaskSliceExpired(sliceStartedAt)) {
7845
+ await yieldToMainThread();
7846
+ throwIfTaskAborted(signal, abortMessage);
7847
+ sliceStartedAt = getTaskNow();
7848
+ }
7849
+ }
7850
+ return result;
7851
+ }
7712
7852
  function isAbortError(error) {
7713
7853
  return error instanceof Error && error.name === "AbortError";
7714
7854
  }
@@ -8101,12 +8241,17 @@ function toQueryFilters(filters) {
8101
8241
  );
8102
8242
  }
8103
8243
  function toQuerySorter(sortState, leafColumns) {
8104
- return orderSortingState(sortState).map((item) => ({
8105
- columnKey: item.key,
8106
- field: leafColumns.find((column) => column.key === item.key)?.dataIndex,
8107
- order: item.order,
8108
- priority: item.priority
8109
- }));
8244
+ return orderSortingState(sortState).map((item) => {
8245
+ const column = leafColumns.find((candidate) => candidate.key === item.key);
8246
+ const animation = typeof column?.sorter === "object" ? column.sorter.animation : void 0;
8247
+ return {
8248
+ columnKey: item.key,
8249
+ field: column?.dataIndex,
8250
+ order: item.order,
8251
+ priority: item.priority,
8252
+ ...animation === void 0 ? {} : { animation }
8253
+ };
8254
+ });
8110
8255
  }
8111
8256
  function getActiveFilterColumnKeys(filters) {
8112
8257
  return Object.entries(filters).filter(([, value]) => isMeaningfulFilterValue(value)).map(([columnKey]) => columnKey);
@@ -8479,6 +8624,7 @@ function resolveHeaderTextLayout(params) {
8479
8624
  actionVisibilityState,
8480
8625
  allowActions,
8481
8626
  hasFilter,
8627
+ hasEdit = false,
8482
8628
  contentInsetStart = 0,
8483
8629
  contentInsetEnd = 0,
8484
8630
  paddingInline = HEADER_TEXT_PADDING
@@ -8510,14 +8656,23 @@ function resolveHeaderTextLayout(params) {
8510
8656
  maxWidth: Math.max(contentVisibleRect.width - paddingInline * 2, 12),
8511
8657
  x: baseX
8512
8658
  };
8513
- if (!allowActions || !actionVisibilityState.visible || align === "left") {
8659
+ if (!allowActions || !actionVisibilityState.visible) {
8514
8660
  return baseLayout;
8515
8661
  }
8516
8662
  const actionLayout = getHeaderActionLayout({
8517
8663
  rect,
8518
8664
  hasFilter,
8665
+ hasEdit,
8666
+ visibilityState: actionVisibilityState,
8519
8667
  trailingInset: contentInsetEnd
8520
8668
  });
8669
+ if (align === "left") {
8670
+ if (!hasEdit) return baseLayout;
8671
+ return {
8672
+ ...baseLayout,
8673
+ maxWidth: Math.max(actionLayout.startX - baseX - paddingInline, 0)
8674
+ };
8675
+ }
8521
8676
  const textRight = Math.min(
8522
8677
  contentVisibleRect.x + contentVisibleRect.width - paddingInline,
8523
8678
  actionLayout.startX - paddingInline
@@ -8548,11 +8703,14 @@ function resolveHeaderTextLayout(params) {
8548
8703
  };
8549
8704
  }
8550
8705
  function getHeaderActionLayout(params) {
8551
- const actionTypes = ["sort"];
8552
- if (params.hasFilter) {
8553
- actionTypes.push("filter");
8554
- }
8555
- actionTypes.push("fixed");
8706
+ const editOnly = params.hasEdit && params.visibilityState != null && !params.visibilityState.hovered && !params.visibilityState.sorted && !params.visibilityState.filtered && !params.visibilityState.fixed;
8707
+ const actionTypes = [];
8708
+ if (!editOnly) {
8709
+ actionTypes.push("sort");
8710
+ if (params.hasFilter) actionTypes.push("filter");
8711
+ actionTypes.push("fixed");
8712
+ }
8713
+ if (params.hasEdit) actionTypes.push("edit");
8556
8714
  const totalWidth = ACTION_SIZE * actionTypes.length + ACTION_GAP * Math.max(actionTypes.length - 1, 0);
8557
8715
  return {
8558
8716
  actionTypes,
@@ -8603,6 +8761,9 @@ function drawHeaderLayer(params, layer) {
8603
8761
  if (cell.layer !== layer) {
8604
8762
  return;
8605
8763
  }
8764
+ if (layer === "center" && centerClipRect && (cell.x + cell.width < centerClipRect.x - 1 || cell.x > centerClipRect.x + centerClipRect.width + 1)) {
8765
+ return;
8766
+ }
8606
8767
  if (layer === "center" && centerClipRect) {
8607
8768
  params.ctx.save();
8608
8769
  params.ctx.beginPath();
@@ -8648,7 +8809,11 @@ function drawHeaderCell(params, cell, clipRect) {
8648
8809
  };
8649
8810
  ctx.save();
8650
8811
  const isSelected = isHeaderCellSelected(params, cell.node.key, rect);
8651
- const isRangeSelected = isHeaderCellInRange(params.rangeHighlight, cell);
8812
+ const isRangeSelected = isHeaderCellInRange(
8813
+ params.rangeHighlight,
8814
+ cell,
8815
+ params.leafColumnKeys
8816
+ );
8652
8817
  ctx.fillStyle = isSelected || isRangeSelected ? theme.selectionBackgroundColor : cell.node.style?.backgroundColor ?? theme.headerBackgroundColor;
8653
8818
  ctx.fillRect(rect.x, rect.y, rect.width, rect.height);
8654
8819
  drawGridRect(
@@ -8672,13 +8837,15 @@ function drawHeaderCell(params, cell, clipRect) {
8672
8837
  );
8673
8838
  const isExpandSystemColumn = cell.node.children.length === 0 && cell.node.column.meta?.systemType === "expandable";
8674
8839
  const isRowSelectorSystemColumn = cell.node.children.length === 0 && cell.node.column.meta?.systemType === "rowSelector";
8840
+ const isEditableLeaf = cell.node.children.length === 0 && !!cell.node.column.editable;
8675
8841
  const visibleState = getHeaderActionVisibilityState(
8676
8842
  cell.node.key,
8677
8843
  params.hoveredColumnKey,
8678
8844
  params.sortState.find((item) => item.key === cell.node.key)?.key ?? null,
8679
8845
  params.filteredColumnKeys,
8680
8846
  params.filterableColumnKeys,
8681
- params.fixedColumnKeys
8847
+ params.fixedColumnKeys,
8848
+ isEditableLeaf
8682
8849
  );
8683
8850
  const titleContent = cell.node.titleContent ?? [
8684
8851
  { type: "text", text: cell.node.title }
@@ -8690,6 +8857,7 @@ function drawHeaderCell(params, cell, clipRect) {
8690
8857
  actionVisibilityState: visibleState,
8691
8858
  allowActions: cell.node.children.length === 0,
8692
8859
  hasFilter: params.filterableColumnKeys.has(cell.node.key),
8860
+ hasEdit: isEditableLeaf,
8693
8861
  contentWidth: measureHeaderContentWidth(ctx, titleContent),
8694
8862
  paddingInline: cell.node.style?.paddingInline,
8695
8863
  contentInsetStart: resolveHeaderContentInsetStart({
@@ -8813,7 +8981,9 @@ function drawHeaderCell(params, cell, clipRect) {
8813
8981
  cell.node.key,
8814
8982
  visibleState,
8815
8983
  cell.node.fixed,
8816
- visibleRect
8984
+ visibleRect,
8985
+ isEditableLeaf,
8986
+ isSelected || isRangeSelected
8817
8987
  );
8818
8988
  drawResizeHandles(params, rect, cell.node.key, cell.node.fixed);
8819
8989
  }
@@ -8849,11 +9019,11 @@ function resolveHeaderContentCenterY(rect, contents, fontSize, paddingBlock, ver
8849
9019
  function measureHeaderContentWidth(ctx, contents) {
8850
9020
  const gap = 4;
8851
9021
  return contents.reduce(
8852
- (sum, content) => sum + (content.type === "img" ? Math.max(content.width ?? content.height ?? 16, 1) : ctx.measureText(content.text).width),
9022
+ (sum, content) => sum + (content.type === "img" ? Math.max(content.width ?? content.height ?? 16, 1) : measureCachedText(ctx, content.text).width),
8853
9023
  0
8854
9024
  ) + Math.max(contents.length - 1, 0) * gap;
8855
9025
  }
8856
- function drawHeaderActions(params, rect, columnKey, visibilityState, fixed, visibleRect = rect) {
9026
+ function drawHeaderActions(params, rect, columnKey, visibilityState, fixed, visibleRect = rect, hasEdit = false, selected = false) {
8857
9027
  if (!visibilityState.visible) {
8858
9028
  return;
8859
9029
  }
@@ -8865,6 +9035,8 @@ function drawHeaderActions(params, rect, columnKey, visibilityState, fixed, visi
8865
9035
  const actionLayout = getHeaderActionLayout({
8866
9036
  rect,
8867
9037
  hasFilter: params.filterableColumnKeys.has(columnKey),
9038
+ hasEdit,
9039
+ visibilityState,
8868
9040
  trailingInset: resolveCollapsedColumnMarkerSet(
8869
9041
  params.collapsedColumnMarkers,
8870
9042
  columnKey
@@ -8872,7 +9044,7 @@ function drawHeaderActions(params, rect, columnKey, visibilityState, fixed, visi
8872
9044
  });
8873
9045
  const { actionTypes, startX, totalWidth } = actionLayout;
8874
9046
  const centerY = rect.y + rect.height / 2;
8875
- const headerSelected = isHeaderCellSelected(params, columnKey, rect);
9047
+ const headerSelected = selected || isHeaderCellSelected(params, columnKey, rect);
8876
9048
  ctx.save();
8877
9049
  ctx.fillStyle = headerSelected ? params.theme.selectionBackgroundColor : params.theme.headerBackgroundColor;
8878
9050
  ctx.beginPath();
@@ -8894,6 +9066,28 @@ function drawHeaderActions(params, rect, columnKey, visibilityState, fixed, visi
8894
9066
  width: ACTION_SIZE2,
8895
9067
  height: ACTION_SIZE2
8896
9068
  };
9069
+ if (actionType === "edit") {
9070
+ drawHeaderActionIcon(
9071
+ ctx,
9072
+ actionRect,
9073
+ "edit",
9074
+ true,
9075
+ false,
9076
+ headerSelected,
9077
+ params.theme.selectionBackgroundColor
9078
+ );
9079
+ const hitRect2 = intersectRects(actionRect, visibleRect);
9080
+ if (hitRect2)
9081
+ descriptors.push({
9082
+ kind: "header",
9083
+ rect: hitRect2,
9084
+ columnKey,
9085
+ fixed,
9086
+ debugNodeType: "header-edit-icon",
9087
+ debugText: "\u5F53\u524D\u5217\u53EF\u7F16\u8F91"
9088
+ });
9089
+ return;
9090
+ }
8897
9091
  const iconHovered = params.hoveredColumnKey === columnKey && params.hoveredActionType === (actionType === "sort" ? "sort-trigger" : actionType === "filter" ? "filter-trigger" : "fixed-trigger");
8898
9092
  drawHeaderActionIcon(
8899
9093
  ctx,
@@ -8972,11 +9166,16 @@ function isHeaderCellSelected(params, columnKey, rect) {
8972
9166
  }
8973
9167
  return isSameRect(params.selectedHeaderRect, rect);
8974
9168
  }
8975
- function isHeaderCellInRange(rangeHighlight, cell) {
8976
- const range = (rangeHighlight ?? EMPTY_RANGE_HIGHLIGHT).selectedColumnRange;
8977
- if (!range) {
8978
- return false;
9169
+ function isHeaderCellInRange(rangeHighlight, cell, leafColumnKeys) {
9170
+ const state = rangeHighlight ?? EMPTY_RANGE_HIGHLIGHT;
9171
+ if (state.selectedColumnKeys != null) {
9172
+ if (leafColumnKeys) {
9173
+ return leafColumnKeys.slice(cell.node.leafStartIndex, cell.node.leafEndIndex + 1).some((key) => state.selectedColumnKeys?.includes(key));
9174
+ }
9175
+ return state.selectedColumnKeys.includes(cell.node.key);
8979
9176
  }
9177
+ const range = state.selectedColumnRange;
9178
+ if (!range) return false;
8980
9179
  return cell.node.leafStartIndex <= range.end && cell.node.leafEndIndex >= range.start;
8981
9180
  }
8982
9181
  function isSameRect(left, right) {
@@ -9041,7 +9240,7 @@ function getHeaderSelectionRect(rect) {
9041
9240
  height: size
9042
9241
  };
9043
9242
  }
9044
- function getHeaderActionVisibilityState(columnKey, hoveredColumnKey, sortedColumnKey, filteredColumnKeys, filterableColumnKeys, fixedColumnKeys) {
9243
+ function getHeaderActionVisibilityState(columnKey, hoveredColumnKey, sortedColumnKey, filteredColumnKeys, filterableColumnKeys, fixedColumnKeys, editable) {
9045
9244
  const hovered = hoveredColumnKey === columnKey;
9046
9245
  const sorted = sortedColumnKey === columnKey;
9047
9246
  const filtered = filterableColumnKeys.has(columnKey) && filteredColumnKeys.has(columnKey);
@@ -9051,7 +9250,7 @@ function getHeaderActionVisibilityState(columnKey, hoveredColumnKey, sortedColum
9051
9250
  sorted,
9052
9251
  filtered,
9053
9252
  fixed,
9054
- visible: hovered || sorted || filtered || fixed
9253
+ visible: hovered || sorted || filtered || fixed || editable
9055
9254
  };
9056
9255
  }
9057
9256
  function getActionActive(actionType, visibilityState) {
@@ -9086,7 +9285,7 @@ function drawHeaderActionIcon(ctx, rect, actionType, active, hovered = false, se
9086
9285
  function drawHeaderContent(ctx, contents, x, y, maxWidth, align, requestRender) {
9087
9286
  const gap = 4;
9088
9287
  const widths = contents.map(
9089
- (content) => content.type === "img" ? Math.max(content.width ?? content.height ?? 16, 1) : ctx.measureText(content.text).width
9288
+ (content) => content.type === "img" ? Math.max(content.width ?? content.height ?? 16, 1) : measureCachedText(ctx, content.text).width
9090
9289
  );
9091
9290
  const totalWidth = widths.reduce((sum, width) => sum + width, 0) + Math.max(contents.length - 1, 0) * gap;
9092
9291
  const displayWidth = Math.min(totalWidth, Math.max(maxWidth, 0));
@@ -9111,19 +9310,19 @@ function drawHeaderContent(ctx, contents, x, y, maxWidth, align, requestRender)
9111
9310
  ctx.textAlign = "left";
9112
9311
  const text = ellipsisText(ctx, content.text, availableWidth);
9113
9312
  ctx.fillText(text, cursorX, y);
9114
- cursorX += Math.min(ctx.measureText(text).width, availableWidth) + gap;
9313
+ cursorX += Math.min(measureCachedText(ctx, text).width, availableWidth) + gap;
9115
9314
  ctx.restore();
9116
9315
  });
9117
9316
  }
9118
9317
  function ellipsisText(ctx, text, maxWidth) {
9119
- if (ctx.measureText(text).width <= maxWidth) {
9318
+ if (measureCachedText(ctx, text).width <= maxWidth) {
9120
9319
  return text;
9121
9320
  }
9122
9321
  const ellipsis = "...";
9123
9322
  let content = text;
9124
9323
  while (content.length > 0) {
9125
9324
  content = content.slice(0, -1);
9126
- const measured = ctx.measureText(`${content}${ellipsis}`).width;
9325
+ const measured = measureCachedText(ctx, `${content}${ellipsis}`).width;
9127
9326
  if (measured <= maxWidth) {
9128
9327
  return `${content}${ellipsis}`;
9129
9328
  }
@@ -9227,6 +9426,544 @@ function findCellByKey(headerRows, columnKey) {
9227
9426
  }
9228
9427
  return null;
9229
9428
  }
9429
+
9430
+ // src/services/range-highlight-service.ts
9431
+ var EMPTY_RANGE_HIGHLIGHT_STATE = {
9432
+ selectedRowAnchorIndex: null,
9433
+ selectedRowRange: null,
9434
+ selectedColumnAnchorKey: null,
9435
+ selectedColumnRange: null
9436
+ };
9437
+ function isIndexInHighlightRange(index, range) {
9438
+ return Boolean(range && index >= range.start && index <= range.end);
9439
+ }
9440
+ function isColumnInHighlight(state, columnKey, columnIndex) {
9441
+ return state.selectedColumnKeys != null ? state.selectedColumnKeys.includes(columnKey) : isIndexInHighlightRange(columnIndex, state.selectedColumnRange);
9442
+ }
9443
+ function isRowInHighlight(state, rowIndex) {
9444
+ const ranges = state.selectedRowRanges ?? (state.selectedRowRange ? [state.selectedRowRange] : []);
9445
+ return ranges.some((range) => isIndexInHighlightRange(rowIndex, range));
9446
+ }
9447
+ function isCellInHighlight(state, rowIndex, columnIndex) {
9448
+ const ranges = state.selectedCellRanges ?? (state.selectedCellRange ? [state.selectedCellRange] : []);
9449
+ return ranges.some(
9450
+ (range) => rowIndex >= range.startRowIndex && rowIndex <= range.endRowIndex && columnIndex >= range.startColumnIndex && columnIndex <= range.endColumnIndex
9451
+ );
9452
+ }
9453
+ function resolveRowHighlightForSelectorClick(params) {
9454
+ const rowIndex = Math.max(0, Math.floor(params.rowIndex));
9455
+ const base = {
9456
+ ...params.current,
9457
+ selectedColumnAnchorKey: null,
9458
+ selectedColumnRange: null,
9459
+ selectedColumnKeys: null,
9460
+ selectedCellRange: params.ctrlKey ? params.current.selectedCellRange ?? null : null,
9461
+ selectedCellRanges: params.ctrlKey ? params.current.selectedCellRanges ?? null : null,
9462
+ selectedCellAnchor: params.ctrlKey ? params.current.selectedCellAnchor ?? null : null
9463
+ };
9464
+ if (params.ctrlKey) {
9465
+ const ranges = params.current.selectedRowRanges ?? (params.current.selectedRowRange ? [params.current.selectedRowRange] : []);
9466
+ const nextRanges = ranges.flatMap((range) => {
9467
+ if (!isIndexInHighlightRange(rowIndex, range)) return [range];
9468
+ return [
9469
+ ...range.start < rowIndex ? [{ start: range.start, end: rowIndex - 1 }] : [],
9470
+ ...rowIndex < range.end ? [{ start: rowIndex + 1, end: range.end }] : []
9471
+ ];
9472
+ });
9473
+ if (!ranges.some((range) => isIndexInHighlightRange(rowIndex, range))) {
9474
+ nextRanges.push({ start: rowIndex, end: rowIndex });
9475
+ }
9476
+ nextRanges.sort((left, right) => left.start - right.start);
9477
+ return {
9478
+ ...base,
9479
+ selectedRowAnchorIndex: nextRanges.length ? isRowInHighlight(
9480
+ { selectedRowRange: null, selectedRowRanges: nextRanges },
9481
+ rowIndex
9482
+ ) ? rowIndex : nextRanges[nextRanges.length - 1].end : null,
9483
+ selectedRowRange: null,
9484
+ selectedRowRanges: nextRanges
9485
+ };
9486
+ }
9487
+ const anchor = params.shiftKey ? params.current.selectedRowAnchorIndex ?? rowIndex : rowIndex;
9488
+ return {
9489
+ ...base,
9490
+ selectedRowAnchorIndex: anchor,
9491
+ selectedRowRange: createHighlightRange(anchor, rowIndex),
9492
+ selectedRowRanges: null
9493
+ };
9494
+ }
9495
+ function resolveCellHighlightForClick(params) {
9496
+ const columnIndex = params.leafColumnKeys.indexOf(params.columnKey);
9497
+ if (columnIndex < 0) return params.current;
9498
+ const base = {
9499
+ ...params.current,
9500
+ selectedRowAnchorIndex: params.ctrlKey && params.preserveRowSelection ? params.current.selectedRowAnchorIndex : null,
9501
+ selectedRowRange: params.ctrlKey && params.preserveRowSelection ? params.current.selectedRowRange : null,
9502
+ selectedRowRanges: params.ctrlKey && params.preserveRowSelection ? params.current.selectedRowRanges ?? null : null,
9503
+ selectedColumnAnchorKey: null,
9504
+ selectedColumnRange: null,
9505
+ selectedColumnKeys: null
9506
+ };
9507
+ if (params.shiftKey && !params.ctrlKey) {
9508
+ const anchor = params.current.selectedCellAnchor ?? params.previousCell ?? {
9509
+ rowIndex: params.rowIndex,
9510
+ columnKey: params.columnKey
9511
+ };
9512
+ const anchorColumnIndex = params.leafColumnKeys.indexOf(anchor.columnKey);
9513
+ return {
9514
+ ...base,
9515
+ selectedCellAnchor: anchor,
9516
+ selectedCellRange: {
9517
+ startRowIndex: Math.min(anchor.rowIndex, params.rowIndex),
9518
+ endRowIndex: Math.max(anchor.rowIndex, params.rowIndex),
9519
+ startColumnIndex: Math.min(
9520
+ anchorColumnIndex < 0 ? columnIndex : anchorColumnIndex,
9521
+ columnIndex
9522
+ ),
9523
+ endColumnIndex: Math.max(
9524
+ anchorColumnIndex < 0 ? columnIndex : anchorColumnIndex,
9525
+ columnIndex
9526
+ )
9527
+ },
9528
+ selectedCellRanges: null
9529
+ };
9530
+ }
9531
+ if (params.ctrlKey) {
9532
+ const previous = params.previousCell;
9533
+ const previousColumnIndex = previous ? params.leafColumnKeys.indexOf(previous.columnKey) : -1;
9534
+ const ranges = params.current.selectedCellRanges ?? (params.current.selectedCellRange ? [params.current.selectedCellRange] : previous && previousColumnIndex >= 0 ? [
9535
+ {
9536
+ startRowIndex: previous.rowIndex,
9537
+ endRowIndex: previous.rowIndex,
9538
+ startColumnIndex: previousColumnIndex,
9539
+ endColumnIndex: previousColumnIndex
9540
+ }
9541
+ ] : []);
9542
+ const selected = ranges.some(
9543
+ (range) => isCellInHighlight(
9544
+ { selectedCellRange: range },
9545
+ params.rowIndex,
9546
+ columnIndex
9547
+ )
9548
+ );
9549
+ const nextRanges = selected ? ranges.flatMap(
9550
+ (range) => removeCellFromHighlightRange(range, params.rowIndex, columnIndex)
9551
+ ) : [
9552
+ ...ranges,
9553
+ {
9554
+ startRowIndex: params.rowIndex,
9555
+ endRowIndex: params.rowIndex,
9556
+ startColumnIndex: columnIndex,
9557
+ endColumnIndex: columnIndex
9558
+ }
9559
+ ];
9560
+ return {
9561
+ ...base,
9562
+ selectedCellAnchor: selected ? params.current.selectedCellAnchor ?? null : {
9563
+ rowIndex: params.rowIndex,
9564
+ columnKey: params.columnKey
9565
+ },
9566
+ selectedCellRange: null,
9567
+ selectedCellRanges: nextRanges
9568
+ };
9569
+ }
9570
+ return {
9571
+ ...params.current,
9572
+ selectedCellAnchor: {
9573
+ rowIndex: params.rowIndex,
9574
+ columnKey: params.columnKey
9575
+ },
9576
+ selectedCellRange: null,
9577
+ selectedCellRanges: null
9578
+ };
9579
+ }
9580
+ function removeCellFromHighlightRange(range, rowIndex, columnIndex) {
9581
+ return subtractCellHighlightRange(range, {
9582
+ startRowIndex: rowIndex,
9583
+ endRowIndex: rowIndex,
9584
+ startColumnIndex: columnIndex,
9585
+ endColumnIndex: columnIndex
9586
+ });
9587
+ }
9588
+ function subtractCellHighlightRange(range, cut) {
9589
+ const top = Math.max(range.startRowIndex, cut.startRowIndex);
9590
+ const bottom = Math.min(range.endRowIndex, cut.endRowIndex);
9591
+ const left = Math.max(range.startColumnIndex, cut.startColumnIndex);
9592
+ const right = Math.min(range.endColumnIndex, cut.endColumnIndex);
9593
+ if (top > bottom || left > right) {
9594
+ return [range];
9595
+ }
9596
+ return [
9597
+ ...range.startRowIndex < top ? [{ ...range, endRowIndex: top - 1 }] : [],
9598
+ ...bottom < range.endRowIndex ? [{ ...range, startRowIndex: bottom + 1 }] : [],
9599
+ ...range.startColumnIndex < left ? [
9600
+ {
9601
+ startRowIndex: top,
9602
+ endRowIndex: bottom,
9603
+ startColumnIndex: range.startColumnIndex,
9604
+ endColumnIndex: left - 1
9605
+ }
9606
+ ] : [],
9607
+ ...right < range.endColumnIndex ? [
9608
+ {
9609
+ startRowIndex: top,
9610
+ endRowIndex: bottom,
9611
+ startColumnIndex: right + 1,
9612
+ endColumnIndex: range.endColumnIndex
9613
+ }
9614
+ ] : []
9615
+ ];
9616
+ }
9617
+ function resolveRowHighlightForDrag(params) {
9618
+ const target = createHighlightRange(params.startRowIndex, params.endRowIndex);
9619
+ const ranges = params.current.selectedRowRanges ?? (params.current.selectedRowRange ? [params.current.selectedRowRange] : []);
9620
+ const nextRanges = params.remove ? ranges.flatMap(
9621
+ (range) => target.end < range.start || target.start > range.end ? [range] : [
9622
+ ...range.start < target.start ? [{ start: range.start, end: target.start - 1 }] : [],
9623
+ ...target.end < range.end ? [{ start: target.end + 1, end: range.end }] : []
9624
+ ]
9625
+ ) : [...ranges, target];
9626
+ nextRanges.sort((left, right) => left.start - right.start);
9627
+ const mergedRanges = nextRanges.reduce(
9628
+ (merged, range) => {
9629
+ const previous = merged[merged.length - 1];
9630
+ if (previous && range.start <= previous.end + 1) {
9631
+ previous.end = Math.max(previous.end, range.end);
9632
+ } else {
9633
+ merged.push({ ...range });
9634
+ }
9635
+ return merged;
9636
+ },
9637
+ []
9638
+ );
9639
+ return {
9640
+ ...params.current,
9641
+ selectedRowAnchorIndex: params.remove ? mergedRanges[mergedRanges.length - 1]?.end ?? null : params.startRowIndex,
9642
+ selectedRowRange: null,
9643
+ selectedRowRanges: mergedRanges,
9644
+ selectedColumnAnchorKey: null,
9645
+ selectedColumnRange: null,
9646
+ selectedColumnKeys: null
9647
+ };
9648
+ }
9649
+ function resolveCellHighlightForDrag(params) {
9650
+ const target = {
9651
+ startRowIndex: Math.min(params.startRowIndex, params.endRowIndex),
9652
+ endRowIndex: Math.max(params.startRowIndex, params.endRowIndex),
9653
+ startColumnIndex: Math.min(params.startColumnIndex, params.endColumnIndex),
9654
+ endColumnIndex: Math.max(params.startColumnIndex, params.endColumnIndex)
9655
+ };
9656
+ const ranges = params.current.selectedCellRanges ?? (params.current.selectedCellRange ? [params.current.selectedCellRange] : []);
9657
+ return {
9658
+ ...params.current,
9659
+ selectedRowAnchorIndex: params.preserveRowSelection ? params.current.selectedRowAnchorIndex : null,
9660
+ selectedRowRange: params.preserveRowSelection ? params.current.selectedRowRange : null,
9661
+ selectedRowRanges: params.preserveRowSelection ? params.current.selectedRowRanges ?? null : null,
9662
+ selectedColumnAnchorKey: null,
9663
+ selectedColumnRange: null,
9664
+ selectedColumnKeys: null,
9665
+ selectedCellAnchor: {
9666
+ rowIndex: params.startRowIndex,
9667
+ columnKey: params.columnKey
9668
+ },
9669
+ selectedCellRange: null,
9670
+ selectedCellRanges: params.remove ? ranges.flatMap((range) => subtractCellHighlightRange(range, target)) : [...ranges, target]
9671
+ };
9672
+ }
9673
+ function resolveNextRowHighlight(params) {
9674
+ return resolveNextRowHighlightByIndexRange({
9675
+ current: params.current,
9676
+ rangeStartIndex: params.rowIndex,
9677
+ rangeEndIndex: params.rowIndex,
9678
+ shiftKey: params.shiftKey
9679
+ });
9680
+ }
9681
+ function resolveNextRowHighlightByIndexRange(params) {
9682
+ const rangeStartIndex = Math.max(0, Math.floor(params.rangeStartIndex));
9683
+ const rangeEndIndex = Math.max(
9684
+ rangeStartIndex,
9685
+ Math.floor(params.rangeEndIndex)
9686
+ );
9687
+ const anchor = params.shiftKey && params.current.selectedRowAnchorIndex != null ? params.current.selectedRowAnchorIndex : rangeStartIndex;
9688
+ return {
9689
+ ...params.current,
9690
+ selectedRowAnchorIndex: anchor,
9691
+ selectedRowRange: createHighlightRange(anchor, rangeEndIndex),
9692
+ selectedRowRanges: null
9693
+ };
9694
+ }
9695
+ function resolveNextRowHighlightForContextMenu(params) {
9696
+ if (isRowInHighlight(params.current, params.rowIndex)) {
9697
+ return params.current;
9698
+ }
9699
+ return resolveNextRowHighlight({
9700
+ current: params.current,
9701
+ rowIndex: params.rowIndex,
9702
+ shiftKey: false
9703
+ });
9704
+ }
9705
+ function resolveNextColumnHighlightForContextMenu(params) {
9706
+ const currentRange = params.current.selectedColumnRange;
9707
+ const selectedKeys = params.current.selectedColumnKeys;
9708
+ if (selectedKeys?.length && params.leafColumnKeys.slice(params.rangeStartIndex, params.rangeEndIndex + 1).every((key) => selectedKeys.includes(key))) {
9709
+ return {
9710
+ ...params.current,
9711
+ selectedRowAnchorIndex: null,
9712
+ selectedRowRange: null
9713
+ };
9714
+ }
9715
+ if (currentRange && params.rangeStartIndex >= currentRange.start && params.rangeEndIndex <= currentRange.end) {
9716
+ return {
9717
+ ...params.current,
9718
+ selectedRowAnchorIndex: null,
9719
+ selectedRowRange: null
9720
+ };
9721
+ }
9722
+ return resolveNextColumnHighlightForHeaderClick({
9723
+ ...params,
9724
+ shiftKey: false
9725
+ });
9726
+ }
9727
+ function resolveColumnKeysForContextMenuAction(params) {
9728
+ const columnIndex = params.leafColumnKeys.indexOf(params.columnKey);
9729
+ const rangeStartIndex = params.rangeStartIndex ?? columnIndex;
9730
+ const rangeEndIndex = params.rangeEndIndex ?? columnIndex;
9731
+ const hasValidTargetRange = rangeStartIndex >= 0 && rangeEndIndex >= rangeStartIndex && rangeEndIndex < params.leafColumnKeys.length;
9732
+ const currentRange = params.current.selectedColumnRange;
9733
+ const selectedKeys = params.current.selectedColumnKeys;
9734
+ if (selectedKeys?.length && hasValidTargetRange && params.leafColumnKeys.slice(rangeStartIndex, rangeEndIndex + 1).every((key) => selectedKeys.includes(key))) {
9735
+ return params.leafColumnKeys.filter((key) => selectedKeys.includes(key));
9736
+ }
9737
+ if (currentRange && hasValidTargetRange && rangeStartIndex >= currentRange.start && rangeEndIndex <= currentRange.end) {
9738
+ return params.leafColumnKeys.slice(
9739
+ currentRange.start,
9740
+ currentRange.end + 1
9741
+ );
9742
+ }
9743
+ if (hasValidTargetRange) {
9744
+ return params.leafColumnKeys.slice(rangeStartIndex, rangeEndIndex + 1);
9745
+ }
9746
+ return [];
9747
+ }
9748
+ function resolveNextColumnHighlight(params) {
9749
+ const columnIndex = params.leafColumnKeys.indexOf(params.columnKey);
9750
+ if (columnIndex < 0) {
9751
+ return params.current;
9752
+ }
9753
+ return resolveNextColumnHighlightByIndexRange({
9754
+ current: params.current,
9755
+ columnKey: params.columnKey,
9756
+ leafColumnKeys: params.leafColumnKeys,
9757
+ rangeStartIndex: columnIndex,
9758
+ rangeEndIndex: columnIndex,
9759
+ shiftKey: params.shiftKey
9760
+ });
9761
+ }
9762
+ function resolveNextColumnHighlightForHeaderClick(params) {
9763
+ const nextCurrent = {
9764
+ ...params.current,
9765
+ selectedRowAnchorIndex: null,
9766
+ selectedRowRange: null,
9767
+ ...params.current.selectedRowRanges !== void 0 ? { selectedRowRanges: null } : {},
9768
+ selectedCellRange: null,
9769
+ selectedCellRanges: null,
9770
+ selectedCellAnchor: null
9771
+ };
9772
+ const hasValidRange = params.rangeStartIndex >= 0 && params.rangeEndIndex >= 0 && params.rangeStartIndex < params.leafColumnKeys.length && params.rangeEndIndex < params.leafColumnKeys.length;
9773
+ if (params.ctrlKey) {
9774
+ const leafIndex = params.leafColumnKeys.indexOf(params.columnKey);
9775
+ const targetKeys = hasValidRange ? params.leafColumnKeys.slice(
9776
+ params.rangeStartIndex,
9777
+ params.rangeEndIndex + 1
9778
+ ) : leafIndex >= 0 ? [params.columnKey] : [];
9779
+ if (!targetKeys.length) return nextCurrent;
9780
+ const selected = new Set(
9781
+ params.current.selectedColumnKeys ?? (params.current.selectedColumnRange ? params.leafColumnKeys.slice(
9782
+ params.current.selectedColumnRange.start,
9783
+ params.current.selectedColumnRange.end + 1
9784
+ ) : [])
9785
+ );
9786
+ if (targetKeys.every((key) => selected.has(key))) {
9787
+ targetKeys.forEach((key) => selected.delete(key));
9788
+ } else {
9789
+ targetKeys.forEach((key) => selected.add(key));
9790
+ }
9791
+ const selectedColumnKeys = params.leafColumnKeys.filter(
9792
+ (key) => selected.has(key)
9793
+ );
9794
+ return {
9795
+ ...nextCurrent,
9796
+ selectedColumnAnchorKey: selectedColumnKeys.length ? selected.has(params.columnKey) ? params.columnKey : selectedColumnKeys[selectedColumnKeys.length - 1] : null,
9797
+ selectedColumnRange: null,
9798
+ selectedColumnKeys
9799
+ };
9800
+ }
9801
+ if (!params.shiftKey) {
9802
+ const leafColumnIndex = params.leafColumnKeys.indexOf(params.columnKey);
9803
+ if (leafColumnIndex >= 0) {
9804
+ return resolveNextColumnHighlightByIndexRange({
9805
+ ...params,
9806
+ current: nextCurrent,
9807
+ rangeStartIndex: leafColumnIndex,
9808
+ rangeEndIndex: leafColumnIndex
9809
+ });
9810
+ }
9811
+ if (hasValidRange) {
9812
+ return resolveNextColumnHighlightByIndexRange({
9813
+ ...params,
9814
+ current: nextCurrent
9815
+ });
9816
+ }
9817
+ return {
9818
+ ...nextCurrent,
9819
+ selectedColumnAnchorKey: params.columnKey,
9820
+ selectedColumnRange: null,
9821
+ selectedColumnKeys: null
9822
+ };
9823
+ }
9824
+ if (hasValidRange) {
9825
+ return resolveNextColumnHighlightByIndexRange({
9826
+ ...params,
9827
+ current: nextCurrent
9828
+ });
9829
+ }
9830
+ return resolveNextColumnHighlight({
9831
+ current: nextCurrent,
9832
+ columnKey: params.columnKey,
9833
+ leafColumnKeys: params.leafColumnKeys,
9834
+ shiftKey: params.shiftKey
9835
+ });
9836
+ }
9837
+ function resolveNextColumnHighlightByIndexRange(params) {
9838
+ if (params.rangeStartIndex < 0 || params.rangeEndIndex < 0 || params.rangeStartIndex >= params.leafColumnKeys.length || params.rangeEndIndex >= params.leafColumnKeys.length) {
9839
+ return params.current;
9840
+ }
9841
+ const anchorIndex = resolveColumnAnchorIndex({
9842
+ anchorKey: params.current.selectedColumnAnchorKey,
9843
+ leafColumnKeys: params.leafColumnKeys
9844
+ });
9845
+ const nextAnchorIndex = params.shiftKey && anchorIndex != null ? anchorIndex : params.rangeStartIndex;
9846
+ return {
9847
+ ...params.current,
9848
+ selectedColumnAnchorKey: params.leafColumnKeys[nextAnchorIndex] ?? params.columnKey,
9849
+ selectedColumnRange: createHighlightRange(
9850
+ nextAnchorIndex,
9851
+ params.shiftKey ? params.rangeEndIndex : params.rangeEndIndex
9852
+ ),
9853
+ selectedColumnKeys: null
9854
+ };
9855
+ }
9856
+ function resolveColumnAnchorIndex(params) {
9857
+ if (!params.anchorKey) {
9858
+ return null;
9859
+ }
9860
+ const anchorIndex = params.leafColumnKeys.indexOf(params.anchorKey);
9861
+ return anchorIndex >= 0 ? anchorIndex : null;
9862
+ }
9863
+ function createHighlightRange(start, end) {
9864
+ return {
9865
+ start: Math.min(start, end),
9866
+ end: Math.max(start, end)
9867
+ };
9868
+ }
9869
+ function resolveHighlightMode(mode) {
9870
+ if (mode === "cell" || mode === "column" || mode === "cross") {
9871
+ return mode;
9872
+ }
9873
+ return "row";
9874
+ }
9875
+ function resolveRangeHighlightForHeaderSelection(params) {
9876
+ const nextRangeHighlight = {
9877
+ ...params.current,
9878
+ selectedRowAnchorIndex: null,
9879
+ selectedRowRange: null,
9880
+ ...params.current.selectedRowRanges !== void 0 ? { selectedRowRanges: null } : {}
9881
+ };
9882
+ if (!params.shiftKey && !params.ctrlKey) {
9883
+ nextRangeHighlight.selectedColumnAnchorKey = EMPTY_RANGE_HIGHLIGHT_STATE.selectedColumnAnchorKey;
9884
+ nextRangeHighlight.selectedColumnRange = EMPTY_RANGE_HIGHLIGHT_STATE.selectedColumnRange;
9885
+ if (params.current.selectedColumnKeys !== void 0) {
9886
+ nextRangeHighlight.selectedColumnKeys = null;
9887
+ }
9888
+ }
9889
+ return nextRangeHighlight;
9890
+ }
9891
+ function resolveRangeHighlightForBodyInteraction(params) {
9892
+ const mode = resolveHighlightMode(params.mode);
9893
+ const canHighlightColumn = params.columnKey !== params.selectionColumnKey && params.columnKey !== params.expandColumnKey;
9894
+ const rangeEndIndex = params.rowIndex + Math.max(Math.floor(params.rowSpan ?? 1), 1) - 1;
9895
+ if (mode === "cell") {
9896
+ return EMPTY_RANGE_HIGHLIGHT_STATE;
9897
+ }
9898
+ if (mode === "row") {
9899
+ return {
9900
+ ...resolveNextRowHighlightByIndexRange({
9901
+ current: params.current,
9902
+ rangeStartIndex: params.rowIndex,
9903
+ rangeEndIndex,
9904
+ shiftKey: params.shiftKey
9905
+ }),
9906
+ selectedColumnAnchorKey: null,
9907
+ selectedColumnRange: null,
9908
+ selectedColumnKeys: null,
9909
+ selectedCellRange: null,
9910
+ selectedCellRanges: null
9911
+ };
9912
+ }
9913
+ if (mode === "column") {
9914
+ if (!canHighlightColumn || !params.columnRange) {
9915
+ return {
9916
+ ...params.current,
9917
+ selectedRowAnchorIndex: null,
9918
+ selectedRowRange: null
9919
+ };
9920
+ }
9921
+ return {
9922
+ ...resolveNextColumnHighlightByIndexRange({
9923
+ current: params.current,
9924
+ columnKey: params.columnKey,
9925
+ leafColumnKeys: params.leafColumnKeys,
9926
+ rangeStartIndex: params.columnRange.start,
9927
+ rangeEndIndex: params.columnRange.end,
9928
+ shiftKey: params.shiftKey
9929
+ }),
9930
+ selectedRowAnchorIndex: null,
9931
+ selectedRowRange: null
9932
+ };
9933
+ }
9934
+ let next = resolveNextRowHighlightByIndexRange({
9935
+ current: params.current,
9936
+ rangeStartIndex: params.rowIndex,
9937
+ rangeEndIndex,
9938
+ shiftKey: params.shiftKey
9939
+ });
9940
+ if (canHighlightColumn && params.columnRange) {
9941
+ next = resolveNextColumnHighlightByIndexRange({
9942
+ current: next,
9943
+ columnKey: params.columnKey,
9944
+ leafColumnKeys: params.leafColumnKeys,
9945
+ rangeStartIndex: params.columnRange.start,
9946
+ rangeEndIndex: params.columnRange.end,
9947
+ shiftKey: false
9948
+ });
9949
+ }
9950
+ return next;
9951
+ }
9952
+ function isCellHighlightedByMode(params) {
9953
+ const mode = resolveHighlightMode(params.mode);
9954
+ if (mode === "cell") {
9955
+ return params.isCellSelected;
9956
+ }
9957
+ if (mode === "row") {
9958
+ return params.isRowRangeSelected;
9959
+ }
9960
+ if (mode === "column") {
9961
+ return params.isColumnRangeSelected;
9962
+ }
9963
+ return params.isRowRangeSelected || params.isColumnRangeSelected;
9964
+ }
9965
+
9966
+ // src/rendering/primitives/draw-summary.ts
9230
9967
  var EMPTY_RANGE_HIGHLIGHT2 = {
9231
9968
  selectedColumnRange: null
9232
9969
  };
@@ -9275,6 +10012,9 @@ function drawSummarySegment(params, columns, clipRect, defaultTitleColumnKey) {
9275
10012
  ctx.rect(minX, summaryTop, totalWidth, summaryHeight);
9276
10013
  ctx.clip();
9277
10014
  columns.forEach((column) => {
10015
+ if (clipRect && (column.drawX + column.width < clipRect.x - 1 || column.drawX > clipRect.x + clipRect.width + 1)) {
10016
+ return;
10017
+ }
9278
10018
  const rect = {
9279
10019
  x: column.drawX,
9280
10020
  y: summaryTop,
@@ -9312,9 +10052,10 @@ function drawSummaryCell(ctx, rect, column, theme, summaryValues, selectedHeader
9312
10052
  return;
9313
10053
  }
9314
10054
  const isHeaderColumnSelected = selectedHeaderKey != null && column.key === selectedHeaderKey;
9315
- const isColumnRangeSelected = isIndexInHighlightRange(
9316
- column.leafIndex,
9317
- (rangeHighlight ?? EMPTY_RANGE_HIGHLIGHT2).selectedColumnRange
10055
+ const isColumnRangeSelected = isColumnInHighlight(
10056
+ rangeHighlight ?? EMPTY_RANGE_HIGHLIGHT2,
10057
+ column.key,
10058
+ column.leafIndex
9318
10059
  );
9319
10060
  ctx.fillStyle = summarySelected || isHeaderColumnSelected || isColumnRangeSelected ? theme.selectionBackgroundColor : style?.backgroundColor ?? theme.headerBackgroundColor;
9320
10061
  ctx.fillRect(rect.x, rect.y, rect.width, rect.height);
@@ -9527,9 +10268,6 @@ function intersectRects2(left, right) {
9527
10268
  height: maxY - y
9528
10269
  };
9529
10270
  }
9530
- function isIndexInHighlightRange(index, range) {
9531
- return Boolean(range && index >= range.start && index <= range.end);
9532
- }
9533
10271
 
9534
10272
  // src/rendering/cell-content/draw-nested-table-content.ts
9535
10273
  var DEFAULT_COLUMN_WIDTH2 = 140;
@@ -9599,7 +10337,8 @@ function drawBodyNestedTableContent(params) {
9599
10337
  align: column.align ?? "left",
9600
10338
  actionVisibilityState,
9601
10339
  allowActions: true,
9602
- hasFilter: column.allowFilter === true
10340
+ hasFilter: column.allowFilter === true,
10341
+ hasEdit: !!column.editable
9603
10342
  });
9604
10343
  const headerHighlighted = isNestedTableHeaderHighlighted(
9605
10344
  content,
@@ -9979,7 +10718,9 @@ function drawNestedTableHeaderActions(params) {
9979
10718
  if (!visibilityState.visible) return;
9980
10719
  const { actionTypes, startX, totalWidth } = getHeaderActionLayout({
9981
10720
  rect,
9982
- hasFilter: column.allowFilter === true
10721
+ hasFilter: column.allowFilter === true,
10722
+ hasEdit: !!column.editable,
10723
+ visibilityState
9983
10724
  });
9984
10725
  drawParams.ctx.save();
9985
10726
  drawParams.ctx.fillStyle = headerSelected ? drawParams.theme.selectionBackgroundColor : drawParams.theme.headerBackgroundColor;
@@ -10000,6 +10741,33 @@ function drawNestedTableHeaderActions(params) {
10000
10741
  width: size,
10001
10742
  height: size
10002
10743
  };
10744
+ if (actionType === "edit") {
10745
+ drawHeaderActionIcon(
10746
+ drawParams.ctx,
10747
+ actionRect,
10748
+ "edit",
10749
+ true,
10750
+ false,
10751
+ headerSelected,
10752
+ drawParams.theme.selectionBackgroundColor
10753
+ );
10754
+ drawParams.descriptors.push({
10755
+ kind: "body-content",
10756
+ rect: actionRect,
10757
+ columnKey: drawParams.column.key,
10758
+ rowIndex: drawParams.rowIndex,
10759
+ contentIndex: drawParams.contentIndex,
10760
+ contentKey: "nested-table",
10761
+ nestedColumnDataIndex: columnKey,
10762
+ nestedColumnIndex: columnIndex,
10763
+ fixed: drawParams.column.fixed,
10764
+ frozen: drawParams.frozen,
10765
+ debugNodeType: "nested-table-header-edit-icon",
10766
+ debugText: "\u5F53\u524D\u5217\u53EF\u7F16\u8F91"
10767
+ });
10768
+ actionX += size + gap;
10769
+ return;
10770
+ }
10003
10771
  const active = actionType === "sort" && sortOrder != null || actionType === "filter" && filtered || actionType === "fixed" && fixed != null;
10004
10772
  drawHeaderActionIcon(
10005
10773
  drawParams.ctx,
@@ -10043,7 +10811,7 @@ function resolveNestedTableHeaderActionVisibility(params) {
10043
10811
  sorted,
10044
10812
  filtered,
10045
10813
  fixed,
10046
- visible: hovered || sorted || filtered || fixed
10814
+ visible: hovered || sorted || filtered || fixed || !!column.editable
10047
10815
  };
10048
10816
  }
10049
10817
  function isNestedTableRowHighlighted(content, record, rowIndex, highlight, highlightMode) {
@@ -11619,7 +12387,7 @@ function drawBodyTagContent(params) {
11619
12387
  function measureTagContentWidth(ctx, content, theme) {
11620
12388
  applyTagTextStyle(ctx, content, theme);
11621
12389
  const intrinsicWidth = Math.max(
11622
- ctx.measureText(content.text).width + resolveContentTextIndent(content),
12390
+ measureCachedText(ctx, content.text).width + resolveContentTextIndent(content),
11623
12391
  0
11624
12392
  ) + (content.paddingInline ?? TAG_HORIZONTAL_PADDING) * 2;
11625
12393
  return resolveContentLayoutWidth(content, intrinsicWidth);
@@ -11759,8 +12527,8 @@ function drawBodyTextContent(params) {
11759
12527
  if (searchMatch && searchMatch.rowIndex === rowIndex && searchMatch.columnKey === column.key && (searchMatch.contentIndex == null || searchMatch.contentIndex === contentIndex) && searchMatch.keyword) {
11760
12528
  const start = Math.max(0, Math.min(searchMatch.start, content.text.length));
11761
12529
  const end = Math.max(start, Math.min(searchMatch.end, content.text.length));
11762
- const prefixWidth = ctx.measureText(content.text.slice(0, start)).width;
11763
- const matchWidth = ctx.measureText(content.text.slice(start, end)).width;
12530
+ const prefixWidth = measureCachedText(ctx, content.text.slice(0, start)).width;
12531
+ const matchWidth = measureCachedText(ctx, content.text.slice(start, end)).width;
11764
12532
  ctx.save();
11765
12533
  ctx.fillStyle = "#f59e0b";
11766
12534
  ctx.globalAlpha = 0.35;
@@ -11833,7 +12601,7 @@ function measureTextContentWidth(ctx, content, theme) {
11833
12601
  }
11834
12602
  applyTextContentStyle(ctx, content, theme.textColor, theme);
11835
12603
  const intrinsicWidth = Math.max(
11836
- ctx.measureText(content.text).width + resolveContentTextIndent(content),
12604
+ measureCachedText(ctx, content.text).width + resolveContentTextIndent(content),
11837
12605
  0
11838
12606
  );
11839
12607
  return resolveContentLayoutWidth(content, intrinsicWidth);
@@ -15132,6 +15900,7 @@ function createListTableAnimationRuntime() {
15132
15900
  lastExpandedRowKeys: /* @__PURE__ */ new Set(),
15133
15901
  expandAnimationFrameId: null,
15134
15902
  collapseRowAnimations: /* @__PURE__ */ new Map(),
15903
+ sortRowAnimations: /* @__PURE__ */ new Map(),
15135
15904
  collapseCellHeightAnimations: /* @__PURE__ */ new Map(),
15136
15905
  collapseAnimationFrameId: null,
15137
15906
  nestedTableHighlights: /* @__PURE__ */ new Map(),
@@ -16201,8 +16970,11 @@ function resolveCollapsedRowRange(params) {
16201
16970
 
16202
16971
  // src/engine/controllers/animation-controller.ts
16203
16972
  var ROW_EXPAND_ANIMATION_DURATION = 240;
16973
+ var ROW_SORT_ANIMATION_DURATION = 420;
16204
16974
  var ListTableAnimationController = class {
16205
16975
  constructor(options) {
16976
+ __publicField(this, "pendingSortRowPositions", null);
16977
+ __publicField(this, "sortRowsReady", false);
16206
16978
  __publicField(this, "runtime");
16207
16979
  __publicField(this, "getScrollbarVisibleOption");
16208
16980
  __publicField(this, "isScrollbarVisible");
@@ -16349,6 +17121,37 @@ var ListTableAnimationController = class {
16349
17121
  setPendingCollapseRowPositions(positions) {
16350
17122
  this.runtime.pendingCollapseRowPositions = positions;
16351
17123
  }
17124
+ /** 在排序前捕获可视行;异步查询完成之前不消费旧位置。 */
17125
+ setPendingSortRowPositions(positions, ready = false) {
17126
+ this.pendingSortRowPositions = positions;
17127
+ this.sortRowsReady = ready;
17128
+ }
17129
+ markSortRowsReady() {
17130
+ if (this.pendingSortRowPositions) this.sortRowsReady = true;
17131
+ }
17132
+ hasPendingSortRows() {
17133
+ return this.pendingSortRowPositions != null;
17134
+ }
17135
+ reconcileSortAnimations(snapshot) {
17136
+ if (!this.sortRowsReady || !this.pendingSortRowPositions) return false;
17137
+ const previousPositions = this.pendingSortRowPositions;
17138
+ this.pendingSortRowPositions = null;
17139
+ this.sortRowsReady = false;
17140
+ const now2 = this.scheduler.now();
17141
+ this.runtime.sortRowAnimations.clear();
17142
+ forEachRenderedVisibleRow(snapshot, (row, nextIndex) => {
17143
+ if (row.kind !== "data") return;
17144
+ const previousIndex = previousPositions.get(String(row.rowKey));
17145
+ if (previousIndex == null || previousIndex === nextIndex) return;
17146
+ this.runtime.sortRowAnimations.set(String(row.rowKey), {
17147
+ from: (previousIndex - nextIndex) * snapshot.rowHeight,
17148
+ to: 0,
17149
+ startTime: now2,
17150
+ duration: ROW_SORT_ANIMATION_DURATION
17151
+ });
17152
+ });
17153
+ return true;
17154
+ }
16352
17155
  reconcileCollapseAnimations(snapshot) {
16353
17156
  const previousPositions = this.runtime.pendingCollapseRowPositions;
16354
17157
  if (!previousPositions) {
@@ -16417,11 +17220,12 @@ var ListTableAnimationController = class {
16417
17220
  return getRowAnimationValue(animation, this.scheduler.now());
16418
17221
  }
16419
17222
  getCollapseRowOffset(rowKey) {
16420
- const animation = this.runtime.collapseRowAnimations.get(String(rowKey));
16421
- if (!animation) {
16422
- return void 0;
16423
- }
16424
- return getRowAnimationValue(animation, this.scheduler.now());
17223
+ const key = String(rowKey);
17224
+ const now2 = this.scheduler.now();
17225
+ const collapse = this.runtime.collapseRowAnimations.get(key);
17226
+ const sort = this.runtime.sortRowAnimations.get(key);
17227
+ if (!collapse && !sort) return void 0;
17228
+ return (collapse ? getRowAnimationValue(collapse, now2) : 0) + (sort ? getRowAnimationValue(sort, now2) : 0);
16425
17229
  }
16426
17230
  getCollapseCellHeight(params) {
16427
17231
  const animation = this.runtime.collapseCellHeightAnimations.get(
@@ -16450,13 +17254,16 @@ var ListTableAnimationController = class {
16450
17254
  columnKey: this.runtime.controlAnimationColumnKey,
16451
17255
  displayRows: snapshot.displayRows,
16452
17256
  nextWindowKey: buildControlAnimationWindowKey(snapshot),
16453
- nextColumnKey: buildControlAnimationColumnKey(snapshot.leafColumns)
17257
+ nextColumnKey: buildControlAnimationColumnKey(
17258
+ collectVisibleControlColumns(snapshot)
17259
+ )
16454
17260
  });
16455
17261
  }
16456
17262
  reconcileControlAnimations(snapshot, resolveCellContents = resolveBodyCellContents) {
16457
17263
  const now2 = this.scheduler.now();
16458
17264
  this.pruneFinishedControlAnimations(now2);
16459
17265
  const nextValues = /* @__PURE__ */ new Map();
17266
+ const visibleColumns = collectVisibleControlColumns(snapshot);
16460
17267
  forEachVisibleSnapshotRow(
16461
17268
  {
16462
17269
  frozenRows: snapshot.frozenRows,
@@ -16466,7 +17273,7 @@ var ListTableAnimationController = class {
16466
17273
  },
16467
17274
  (record, rowIndex) => {
16468
17275
  const rowKey = this.getRowKey(record, rowIndex);
16469
- collectVisibleControlColumns(snapshot).forEach((column) => {
17276
+ visibleColumns.forEach((column) => {
16470
17277
  const contents = resolveCellContents({
16471
17278
  record,
16472
17279
  rowIndex,
@@ -16509,9 +17316,7 @@ var ListTableAnimationController = class {
16509
17316
  this.runtime.lastControlValues = nextValues;
16510
17317
  this.runtime.controlAnimationRowsRef = snapshot.displayRows;
16511
17318
  this.runtime.controlAnimationWindowKey = buildControlAnimationWindowKey(snapshot);
16512
- this.runtime.controlAnimationColumnKey = buildControlAnimationColumnKey(
16513
- snapshot.leafColumns
16514
- );
17319
+ this.runtime.controlAnimationColumnKey = buildControlAnimationColumnKey(visibleColumns);
16515
17320
  Array.from(this.runtime.controlAnimations.keys()).forEach((key) => {
16516
17321
  if (!nextValues.has(key)) {
16517
17322
  this.runtime.controlAnimations.delete(key);
@@ -16889,7 +17694,7 @@ var ListTableAnimationController = class {
16889
17694
  );
16890
17695
  }
16891
17696
  ensureCollapseAnimationFrame() {
16892
- if (this.runtime.collapseRowAnimations.size === 0 && this.runtime.collapseCellHeightAnimations.size === 0 || this.runtime.collapseAnimationFrameId != null) {
17697
+ if (this.runtime.collapseRowAnimations.size === 0 && this.runtime.sortRowAnimations.size === 0 && this.runtime.collapseCellHeightAnimations.size === 0 || this.runtime.collapseAnimationFrameId != null) {
16893
17698
  return;
16894
17699
  }
16895
17700
  this.runtime.collapseAnimationFrameId = this.scheduler.requestAnimationFrame(() => {
@@ -16913,8 +17718,11 @@ var ListTableAnimationController = class {
16913
17718
  this.runtime.collapseAnimationFrameId = null;
16914
17719
  }
16915
17720
  this.runtime.collapseRowAnimations.clear();
17721
+ this.runtime.sortRowAnimations.clear();
16916
17722
  this.runtime.collapseCellHeightAnimations.clear();
16917
17723
  this.runtime.pendingCollapseRowPositions = null;
17724
+ this.pendingSortRowPositions = null;
17725
+ this.sortRowsReady = false;
16918
17726
  }
16919
17727
  destroy() {
16920
17728
  this.stopScrollbarOpacityAnimation();
@@ -17021,6 +17829,7 @@ var ListTableAnimationController = class {
17021
17829
  }
17022
17830
  pruneFinishedCollapseAnimations(now2) {
17023
17831
  pruneFinishedAnimations(this.runtime.collapseRowAnimations, now2);
17832
+ pruneFinishedAnimations(this.runtime.sortRowAnimations, now2);
17024
17833
  pruneFinishedAnimations(this.runtime.collapseCellHeightAnimations, now2);
17025
17834
  }
17026
17835
  };
@@ -17031,7 +17840,11 @@ function collectVisibleControlColumns(snapshot) {
17031
17840
  }
17032
17841
  const columns = [
17033
17842
  ...segments.leftSegment,
17034
- ...segments.centerSegment,
17843
+ ...segments.centerSegment.filter((column) => {
17844
+ const metrics = snapshot.columnMetrics;
17845
+ if (!metrics) return true;
17846
+ return metrics.availableCenterWidth > 0 && column.drawX + column.width >= metrics.fixedWidthLeft - 1 && column.drawX <= metrics.fixedWidthLeft + metrics.availableCenterWidth + 1;
17847
+ }),
17035
17848
  ...segments.rightSegment
17036
17849
  ];
17037
17850
  const seen = /* @__PURE__ */ new Set();
@@ -17850,8 +18663,11 @@ function createListTableSelectionCopyText(params) {
17850
18663
  (column2) => column2.key !== ROW_SELECTION_COLUMN_KEY && column2.key !== ROW_SELECTOR_COLUMN_KEY && column2.key !== EXPAND_COLUMN_KEY
17851
18664
  );
17852
18665
  const columnRange = params.rangeHighlight.selectedColumnRange;
18666
+ const selectedColumnKeys = params.rangeHighlight.selectedColumnKeys;
17853
18667
  const rowRange = params.rangeHighlight.selectedRowRange;
18668
+ const rowRanges = params.rangeHighlight.selectedRowRanges;
17854
18669
  const cellRange = params.rangeHighlight.selectedCellRange;
18670
+ const cellRanges = params.rangeHighlight.selectedCellRanges;
17855
18671
  if (cellRange) {
17856
18672
  const selectedColumns2 = params.columns.filter(
17857
18673
  (column2, index) => index >= cellRange.startColumnIndex && index <= cellRange.endColumnIndex && column2.key !== ROW_SELECTION_COLUMN_KEY && column2.key !== ROW_SELECTOR_COLUMN_KEY && column2.key !== EXPAND_COLUMN_KEY
@@ -17872,22 +18688,57 @@ function createListTableSelectionCopyText(params) {
17872
18688
  return `${headerText}
17873
18689
  ${bodyText}`;
17874
18690
  }
17875
- const mode = params.highlightMode ?? (rowRange && columnRange ? "cross" : rowRange ? "row" : "column");
17876
- const selectedColumns = columnRange ? params.columns.filter(
18691
+ if (cellRanges != null) {
18692
+ const lines = [];
18693
+ const fullRowRanges = rowRanges ?? (rowRange ? [rowRange] : []);
18694
+ const selectedRowSpans = [
18695
+ ...cellRanges.map((range) => ({
18696
+ start: range.startRowIndex,
18697
+ end: range.endRowIndex
18698
+ })),
18699
+ ...fullRowRanges
18700
+ ].sort((left, right) => left.start - right.start);
18701
+ let lastRowIndex = -1;
18702
+ for (const range of selectedRowSpans) {
18703
+ for (let rowIndex2 = Math.max(range.start, lastRowIndex + 1, 0); rowIndex2 <= Math.min(range.end, params.rows.length - 1); rowIndex2++) {
18704
+ const fullRowSelected = fullRowRanges.some(
18705
+ (rowRange2) => rowIndex2 >= rowRange2.start && rowIndex2 <= rowRange2.end
18706
+ );
18707
+ const selectedColumns2 = params.columns.filter(
18708
+ (column2, columnIndex) => column2.key !== ROW_SELECTION_COLUMN_KEY && column2.key !== ROW_SELECTOR_COLUMN_KEY && column2.key !== EXPAND_COLUMN_KEY && (fullRowSelected || cellRanges.some(
18709
+ (cellRange2) => rowIndex2 >= cellRange2.startRowIndex && rowIndex2 <= cellRange2.endRowIndex && columnIndex >= cellRange2.startColumnIndex && columnIndex <= cellRange2.endColumnIndex
18710
+ ))
18711
+ );
18712
+ if (selectedColumns2.length) {
18713
+ lines.push(
18714
+ selectedColumns2.map(
18715
+ (column2) => getCellClipboardText(params.rows[rowIndex2], rowIndex2, column2)
18716
+ ).join(" ")
18717
+ );
18718
+ }
18719
+ lastRowIndex = rowIndex2;
18720
+ }
18721
+ }
18722
+ return lines.length ? lines.join("\n") : null;
18723
+ }
18724
+ const mode = params.highlightMode ?? (rowRange && (columnRange || selectedColumnKeys?.length) ? "cross" : rowRange ? "row" : "column");
18725
+ const selectedColumns = selectedColumnKeys != null ? columns.filter((column2) => selectedColumnKeys.includes(column2.key)) : columnRange ? params.columns.filter(
17877
18726
  (column2, index) => index >= columnRange.start && index <= columnRange.end
17878
18727
  ).filter(
17879
18728
  (column2) => column2.key !== ROW_SELECTION_COLUMN_KEY && column2.key !== ROW_SELECTOR_COLUMN_KEY && column2.key !== EXPAND_COLUMN_KEY
17880
18729
  ) : columns;
17881
18730
  const startRow = rowRange ? Math.max(rowRange.start, 0) : 0;
17882
18731
  const endRow = rowRange ? Math.min(rowRange.end, params.rows.length - 1) : params.rows.length - 1;
17883
- if (rowRange && params.selection.columnKey === ROW_SELECTOR_COLUMN_KEY) {
17884
- return params.rows.slice(startRow, endRow + 1).map(
17885
- (record2, offset) => columns.map(
17886
- (column2) => getCellClipboardText(record2, startRow + offset, column2)
17887
- ).join(" ")
18732
+ if ((rowRange || rowRanges?.length) && params.selection.columnKey === ROW_SELECTOR_COLUMN_KEY) {
18733
+ const selectedRows = rowRanges != null ? rowRanges.flatMap((range) => {
18734
+ const start = Math.max(range.start, 0);
18735
+ return params.rows.slice(start, range.end + 1).map((record2, offset) => ({ record: record2, rowIndex: start + offset }));
18736
+ }) : params.rows.slice(startRow, endRow + 1).map((record2, offset) => ({ record: record2, rowIndex: startRow + offset }));
18737
+ return selectedRows.map(
18738
+ ({ record: record2, rowIndex: rowIndex2 }) => columns.map((column2) => getCellClipboardText(record2, rowIndex2, column2)).join(" ")
17888
18739
  ).join("\n");
17889
18740
  }
17890
- if (mode === "column" && columnRange && params.selection.rowIndex == null) {
18741
+ if ((mode === "column" || selectedColumnKeys != null) && (columnRange || selectedColumnKeys?.length) && params.selection.rowIndex == null) {
17891
18742
  return params.rows.map(
17892
18743
  (record2, rowIndex2) => selectedColumns.map((column2) => getCellClipboardText(record2, rowIndex2, column2)).join(" ")
17893
18744
  ).join("\n");
@@ -17987,42 +18838,99 @@ async function copyRowJsonContent(params) {
17987
18838
  }
17988
18839
  }
17989
18840
  async function exportAllRowsJsonContent(params) {
18841
+ const columns = resolveListTableExportColumns(
18842
+ params.columns,
18843
+ params.settings
18844
+ );
18845
+ const rows = await resolveListTableExportRows(
18846
+ params.rows,
18847
+ columns,
18848
+ params.settings
18849
+ );
18850
+ const data = utils.mapRowsToExportRecords({
18851
+ rows,
18852
+ columns: buildExportColumns(columns)
18853
+ });
18854
+ const footerRows = await buildListTableExportFooterRows(
18855
+ rows,
18856
+ columns,
18857
+ params.settings
18858
+ );
18859
+ if (footerRows.length) {
18860
+ data.push(
18861
+ Object.fromEntries(
18862
+ columns.map((column, index) => [column.title, footerRows[0][index]])
18863
+ )
18864
+ );
18865
+ }
17990
18866
  await utils.downloadJsonFile({
17991
18867
  fileName: params.fileName?.trim() || buildTableExportFileName("json"),
17992
- data: utils.mapRowsToExportRecords({
17993
- rows: params.rows,
17994
- columns: buildExportColumns(params.columns)
17995
- })
18868
+ data
17996
18869
  });
17997
18870
  }
17998
18871
  async function exportAllRowsCsvContent(params) {
18872
+ const columns = resolveListTableExportColumns(
18873
+ params.columns,
18874
+ params.settings
18875
+ );
18876
+ const rows = await resolveListTableExportRows(
18877
+ params.rows,
18878
+ columns,
18879
+ params.settings
18880
+ );
17999
18881
  await utils.exportTableToCsv({
18000
18882
  fileName: params.fileName?.trim() || buildTableExportFileName("csv"),
18001
- rows: params.rows,
18002
- columns: buildExportColumns(params.columns)
18883
+ rows,
18884
+ columns: buildExportColumns(columns),
18885
+ footerRows: await buildListTableExportFooterRows(
18886
+ rows,
18887
+ columns,
18888
+ params.settings
18889
+ )
18003
18890
  });
18004
18891
  }
18005
18892
  async function exportAllRowsTxtContent(params) {
18893
+ const columns = resolveListTableExportColumns(
18894
+ params.columns,
18895
+ params.settings
18896
+ );
18897
+ const rows = await resolveListTableExportRows(
18898
+ params.rows,
18899
+ columns,
18900
+ params.settings
18901
+ );
18006
18902
  await utils.exportTableToTxt({
18007
18903
  fileName: params.fileName?.trim() || buildTableExportFileName("txt"),
18008
- rows: params.rows,
18009
- columns: buildExportColumns(params.columns)
18904
+ rows,
18905
+ columns: buildExportColumns(columns),
18906
+ footerRows: await buildListTableExportFooterRows(
18907
+ rows,
18908
+ columns,
18909
+ params.settings
18910
+ )
18010
18911
  });
18011
18912
  }
18012
18913
  async function exportAllRowsXlsxContent(params) {
18013
- const columns = params.columns.filter(isExportableColumn);
18014
- const footerRows = [];
18015
- if (params.includeSummary) {
18016
- const values = await computeSummaryValuesAsync({
18017
- rows: params.rows,
18018
- columns,
18019
- signal: params.signal
18020
- });
18021
- footerRows.push(columns.map((column) => values.get(column.key) ?? ""));
18022
- }
18914
+ const columns = resolveListTableExportColumns(
18915
+ params.columns,
18916
+ params.settings
18917
+ );
18918
+ const rows = await resolveListTableExportRows(
18919
+ params.rows,
18920
+ columns,
18921
+ params.settings,
18922
+ params.signal
18923
+ );
18924
+ const footerRows = await buildListTableExportFooterRows(
18925
+ rows,
18926
+ columns,
18927
+ params.settings,
18928
+ params.includeSummary,
18929
+ params.signal
18930
+ );
18023
18931
  await utils.exportTableToXlsx({
18024
18932
  fileName: params.fileName?.trim() || buildTableExportFileName("xlsx"),
18025
- rows: params.rows,
18933
+ rows,
18026
18934
  footerRows,
18027
18935
  worker: params.worker,
18028
18936
  signal: params.signal,
@@ -18038,6 +18946,95 @@ async function exportAllRowsXlsxContent(params) {
18038
18946
  sheetName: "TableData"
18039
18947
  });
18040
18948
  }
18949
+ function getListTableExportCandidateColumns(columns) {
18950
+ const hiddenByKey = /* @__PURE__ */ new Map();
18951
+ const exportableByKey = /* @__PURE__ */ new Map();
18952
+ const reveal = (items, parentHidden = false) => items.map((column) => {
18953
+ const hidden = parentHidden || column.hidden === true;
18954
+ const key = resolveColumnKey(column);
18955
+ hiddenByKey.set(key, hidden);
18956
+ exportableByKey.set(key, column.exportable);
18957
+ return {
18958
+ ...column,
18959
+ hidden: false,
18960
+ ...column.children?.length ? { children: reveal(column.children, hidden) } : null
18961
+ };
18962
+ });
18963
+ return flattenLeafColumns(normalizeColumns(reveal(columns))).map(
18964
+ (column) => ({
18965
+ ...column,
18966
+ hidden: hiddenByKey.get(column.key) ?? false,
18967
+ exportable: exportableByKey.get(column.key)
18968
+ })
18969
+ );
18970
+ }
18971
+ function getListTableExportColumnChoices(columns, sortState = []) {
18972
+ return columns.filter(isExportableColumn).map((column) => ({
18973
+ key: column.key,
18974
+ title: column.title ?? column.key,
18975
+ selected: column.hidden !== true,
18976
+ sortOrder: sortState.find((item) => item.key === column.key)?.order ?? "default",
18977
+ sortIndex: sortState.findIndex((item) => item.key === column.key),
18978
+ sortPriority: sortState.find((item) => item.key === column.key)?.priority,
18979
+ summary: {
18980
+ type: column.summaryTitle != null ? "title" : column.summary === true ? "sum" : column.summary && typeof column.summary === "object" ? column.summary.type ?? "sum" : "none",
18981
+ showTypePrefix: column.summary === true || typeof column.summary === "object" && column.summary?.showTypePrefix !== false,
18982
+ precision: typeof column.summary === "object" && column.summary ? column.summary.precision : void 0,
18983
+ roundingMode: typeof column.summary === "object" && column.summary ? column.summary.roundingMode ?? "round" : "round",
18984
+ preserveTrailingZeros: typeof column.summary === "object" && column.summary ? column.summary.preserveTrailingZeros === true : false
18985
+ },
18986
+ ...column.summaryTitle != null ? { summaryTitle: column.summaryTitle } : null
18987
+ }));
18988
+ }
18989
+ function resolveListTableExportColumns(columns, settings) {
18990
+ const exportable = columns.filter(isExportableColumn);
18991
+ if (!settings) return exportable.filter((column) => !column.hidden);
18992
+ const byKey = new Map(exportable.map((column) => [column.key, column]));
18993
+ const selected = [...new Set(settings.columnKeys)].flatMap((key) => {
18994
+ const column = byKey.get(key);
18995
+ if (!column) return [];
18996
+ const summary = settings.summaries[key];
18997
+ if (!summary || summary.type === "original") return [column];
18998
+ const result = { ...column };
18999
+ if (summary.type === "none") {
19000
+ result.summary = false;
19001
+ delete result.summaryTitle;
19002
+ } else if (summary.type === "title") {
19003
+ result.summary = false;
19004
+ } else {
19005
+ result.summary = {
19006
+ ...typeof column.summary === "object" ? column.summary : null,
19007
+ type: summary.type,
19008
+ showTypePrefix: summary.showTypePrefix,
19009
+ precision: summary.precision,
19010
+ roundingMode: summary.roundingMode,
19011
+ preserveTrailingZeros: summary.preserveTrailingZeros
19012
+ };
19013
+ delete result.summaryTitle;
19014
+ }
19015
+ return [result];
19016
+ });
19017
+ if (selected.length === 0) throw new Error("\u8BF7\u81F3\u5C11\u9009\u62E9\u4E00\u5217\u5BFC\u51FA\u3002");
19018
+ return selected;
19019
+ }
19020
+ async function resolveListTableExportRows(rows, columns, settings, signal) {
19021
+ if (!settings?.sortState.length) return rows;
19022
+ return sortRowsAsync({
19023
+ rows,
19024
+ leafColumns: columns,
19025
+ sortState: settings.sortState,
19026
+ signal
19027
+ });
19028
+ }
19029
+ async function buildListTableExportFooterRows(rows, columns, settings, includeSummary = false, signal) {
19030
+ if (!(settings?.includeSummary ?? includeSummary)) return [];
19031
+ const values = await computeSummaryValuesAsync({ rows, columns, signal });
19032
+ return [
19033
+ columns.map(
19034
+ (column) => resolveSummaryCellDisplayValue(column, values.get(column.key))
19035
+ )
19036
+ ];
19037
+ }
18041
19038
  function renderResultToClipboardText(result) {
18042
19039
  if (result == null) {
18043
19040
  return "";
@@ -18302,11 +19299,14 @@ var ListTableContentActionController = class {
18302
19299
  const includeSummary = options?.includeSummary ?? this.options.getSummaryVisible?.() ?? false;
18303
19300
  options = { ...this.options.getOptions().exportOptions, ...options };
18304
19301
  this.startExportTask(
18305
- (fileName) => exportAllRowsXlsxContent({
19302
+ (fileName, settings) => exportAllRowsXlsxContent({
18306
19303
  rows: this.options.getOptions().dataSource,
18307
- columns: this.options.getLeafColumns(),
19304
+ columns: getListTableExportCandidateColumns(
19305
+ this.options.getOptions().columns
19306
+ ),
18308
19307
  ...options,
18309
19308
  includeSummary,
19309
+ ...settings ? { settings } : null,
18310
19310
  fileName: fileName ?? options?.fileName,
18311
19311
  onProgress: (progress) => {
18312
19312
  const fileLabel = progress.fileCount && progress.fileCount > 1 ? `\uFF08\u7B2C ${progress.fileIndex}/${progress.fileCount} \u4EFD\uFF09` : "";
@@ -18319,10 +19319,17 @@ var ListTableContentActionController = class {
18319
19319
  "\u6B63\u5728\u5BFC\u51FA XLSX\u2026",
18320
19320
  options?.onError,
18321
19321
  options?.fileName?.trim() ? `${options.fileName.trim().replace(/\.xlsx$/i, "")}.xlsx` : buildTableExportFileName("xlsx"),
18322
- options?.promptFileName
19322
+ options?.promptFileName,
19323
+ {
19324
+ columns: getListTableExportColumnChoices(
19325
+ getListTableExportCandidateColumns(this.options.getOptions().columns),
19326
+ this.options.getSortState?.()
19327
+ ),
19328
+ includeSummary
19329
+ }
18323
19330
  );
18324
19331
  }
18325
- startExportTask(task, text, onError, defaultFileName, promptFileName = this.options.getOptions().exportOptions?.promptFileName ?? true) {
19332
+ startExportTask(task, text, onError, defaultFileName, promptFileName = this.options.getOptions().exportOptions?.promptFileName ?? true, dialog) {
18326
19333
  if (this.exportTaskRunning || this.exportNamePending) {
18327
19334
  return;
18328
19335
  }
@@ -18337,14 +19344,15 @@ var ListTableContentActionController = class {
18337
19344
  };
18338
19345
  const request = {
18339
19346
  fileName: defaultFileName,
18340
- onConfirm: (value) => {
19347
+ ...dialog,
19348
+ onConfirm: (value, settings) => {
18341
19349
  if (settled || !value.trim()) return;
18342
19350
  close();
18343
19351
  const extension = defaultFileName.match(
18344
19352
  /\.(xlsx|csv|txt|json)$/i
18345
19353
  )?.[0];
18346
19354
  const name = extension && !value.trim().toLowerCase().endsWith(extension.toLowerCase()) ? `${value.trim()}${extension}` : value.trim();
18347
- this.startExportTask(() => task(name), text, onError);
19355
+ this.startExportTask(() => task(name, settings), text, onError);
18348
19356
  },
18349
19357
  onCancel: () => {
18350
19358
  if (!settled) close();
@@ -19727,257 +20735,6 @@ function setColumnConditionalFormats(columns, key, rules, cache) {
19727
20735
  }
19728
20736
  return false;
19729
20737
  }
19730
-
19731
- // src/services/range-highlight-service.ts
19732
- var EMPTY_RANGE_HIGHLIGHT_STATE = {
19733
- selectedRowAnchorIndex: null,
19734
- selectedRowRange: null,
19735
- selectedColumnAnchorKey: null,
19736
- selectedColumnRange: null
19737
- };
19738
- function isIndexInHighlightRange2(index, range) {
19739
- return Boolean(range && index >= range.start && index <= range.end);
19740
- }
19741
- function resolveNextRowHighlight(params) {
19742
- return resolveNextRowHighlightByIndexRange({
19743
- current: params.current,
19744
- rangeStartIndex: params.rowIndex,
19745
- rangeEndIndex: params.rowIndex,
19746
- shiftKey: params.shiftKey
19747
- });
19748
- }
19749
- function resolveNextRowHighlightByIndexRange(params) {
19750
- const rangeStartIndex = Math.max(0, Math.floor(params.rangeStartIndex));
19751
- const rangeEndIndex = Math.max(
19752
- rangeStartIndex,
19753
- Math.floor(params.rangeEndIndex)
19754
- );
19755
- const anchor = params.shiftKey && params.current.selectedRowAnchorIndex != null ? params.current.selectedRowAnchorIndex : rangeStartIndex;
19756
- return {
19757
- ...params.current,
19758
- selectedRowAnchorIndex: anchor,
19759
- selectedRowRange: createHighlightRange(anchor, rangeEndIndex)
19760
- };
19761
- }
19762
- function resolveNextRowHighlightForContextMenu(params) {
19763
- if (isIndexInHighlightRange2(params.rowIndex, params.current.selectedRowRange)) {
19764
- return params.current;
19765
- }
19766
- return resolveNextRowHighlight({
19767
- current: params.current,
19768
- rowIndex: params.rowIndex,
19769
- shiftKey: false
19770
- });
19771
- }
19772
- function resolveNextColumnHighlightForContextMenu(params) {
19773
- const currentRange = params.current.selectedColumnRange;
19774
- if (currentRange && params.rangeStartIndex >= currentRange.start && params.rangeEndIndex <= currentRange.end) {
19775
- return {
19776
- ...params.current,
19777
- selectedRowAnchorIndex: null,
19778
- selectedRowRange: null
19779
- };
19780
- }
19781
- return resolveNextColumnHighlightForHeaderClick({
19782
- ...params,
19783
- shiftKey: false
19784
- });
19785
- }
19786
- function resolveColumnKeysForContextMenuAction(params) {
19787
- const columnIndex = params.leafColumnKeys.indexOf(params.columnKey);
19788
- const rangeStartIndex = params.rangeStartIndex ?? columnIndex;
19789
- const rangeEndIndex = params.rangeEndIndex ?? columnIndex;
19790
- const hasValidTargetRange = rangeStartIndex >= 0 && rangeEndIndex >= rangeStartIndex && rangeEndIndex < params.leafColumnKeys.length;
19791
- const currentRange = params.current.selectedColumnRange;
19792
- if (currentRange && hasValidTargetRange && rangeStartIndex >= currentRange.start && rangeEndIndex <= currentRange.end) {
19793
- return params.leafColumnKeys.slice(
19794
- currentRange.start,
19795
- currentRange.end + 1
19796
- );
19797
- }
19798
- if (hasValidTargetRange) {
19799
- return params.leafColumnKeys.slice(rangeStartIndex, rangeEndIndex + 1);
19800
- }
19801
- return [];
19802
- }
19803
- function resolveNextColumnHighlight(params) {
19804
- const columnIndex = params.leafColumnKeys.indexOf(params.columnKey);
19805
- if (columnIndex < 0) {
19806
- return params.current;
19807
- }
19808
- return resolveNextColumnHighlightByIndexRange({
19809
- current: params.current,
19810
- columnKey: params.columnKey,
19811
- leafColumnKeys: params.leafColumnKeys,
19812
- rangeStartIndex: columnIndex,
19813
- rangeEndIndex: columnIndex,
19814
- shiftKey: params.shiftKey
19815
- });
19816
- }
19817
- function resolveNextColumnHighlightForHeaderClick(params) {
19818
- const nextCurrent = {
19819
- ...params.current,
19820
- selectedRowAnchorIndex: null,
19821
- selectedRowRange: null
19822
- };
19823
- const hasValidRange = params.rangeStartIndex >= 0 && params.rangeEndIndex >= 0 && params.rangeStartIndex < params.leafColumnKeys.length && params.rangeEndIndex < params.leafColumnKeys.length;
19824
- if (!params.shiftKey) {
19825
- const leafColumnIndex = params.leafColumnKeys.indexOf(params.columnKey);
19826
- if (leafColumnIndex >= 0) {
19827
- return resolveNextColumnHighlightByIndexRange({
19828
- ...params,
19829
- current: nextCurrent,
19830
- rangeStartIndex: leafColumnIndex,
19831
- rangeEndIndex: leafColumnIndex
19832
- });
19833
- }
19834
- if (hasValidRange) {
19835
- return resolveNextColumnHighlightByIndexRange({
19836
- ...params,
19837
- current: nextCurrent
19838
- });
19839
- }
19840
- return {
19841
- ...nextCurrent,
19842
- selectedColumnAnchorKey: params.columnKey,
19843
- selectedColumnRange: null
19844
- };
19845
- }
19846
- if (hasValidRange) {
19847
- return resolveNextColumnHighlightByIndexRange({
19848
- ...params,
19849
- current: nextCurrent
19850
- });
19851
- }
19852
- return resolveNextColumnHighlight({
19853
- current: nextCurrent,
19854
- columnKey: params.columnKey,
19855
- leafColumnKeys: params.leafColumnKeys,
19856
- shiftKey: params.shiftKey
19857
- });
19858
- }
19859
- function resolveNextColumnHighlightByIndexRange(params) {
19860
- if (params.rangeStartIndex < 0 || params.rangeEndIndex < 0 || params.rangeStartIndex >= params.leafColumnKeys.length || params.rangeEndIndex >= params.leafColumnKeys.length) {
19861
- return params.current;
19862
- }
19863
- const anchorIndex = resolveColumnAnchorIndex({
19864
- anchorKey: params.current.selectedColumnAnchorKey,
19865
- leafColumnKeys: params.leafColumnKeys
19866
- });
19867
- const nextAnchorIndex = params.shiftKey && anchorIndex != null ? anchorIndex : params.rangeStartIndex;
19868
- return {
19869
- ...params.current,
19870
- selectedColumnAnchorKey: params.leafColumnKeys[nextAnchorIndex] ?? params.columnKey,
19871
- selectedColumnRange: createHighlightRange(
19872
- nextAnchorIndex,
19873
- params.shiftKey ? params.rangeEndIndex : params.rangeEndIndex
19874
- )
19875
- };
19876
- }
19877
- function resolveColumnAnchorIndex(params) {
19878
- if (!params.anchorKey) {
19879
- return null;
19880
- }
19881
- const anchorIndex = params.leafColumnKeys.indexOf(params.anchorKey);
19882
- return anchorIndex >= 0 ? anchorIndex : null;
19883
- }
19884
- function createHighlightRange(start, end) {
19885
- return {
19886
- start: Math.min(start, end),
19887
- end: Math.max(start, end)
19888
- };
19889
- }
19890
- function resolveHighlightMode(mode) {
19891
- if (mode === "cell" || mode === "column" || mode === "cross") {
19892
- return mode;
19893
- }
19894
- return "row";
19895
- }
19896
- function resolveRangeHighlightForHeaderSelection(params) {
19897
- const nextRangeHighlight = {
19898
- ...params.current,
19899
- selectedRowAnchorIndex: null,
19900
- selectedRowRange: null
19901
- };
19902
- if (!params.shiftKey) {
19903
- nextRangeHighlight.selectedColumnAnchorKey = EMPTY_RANGE_HIGHLIGHT_STATE.selectedColumnAnchorKey;
19904
- nextRangeHighlight.selectedColumnRange = EMPTY_RANGE_HIGHLIGHT_STATE.selectedColumnRange;
19905
- }
19906
- return nextRangeHighlight;
19907
- }
19908
- function resolveRangeHighlightForBodyInteraction(params) {
19909
- const mode = resolveHighlightMode(params.mode);
19910
- const canHighlightColumn = params.columnKey !== params.selectionColumnKey && params.columnKey !== params.expandColumnKey;
19911
- const rangeEndIndex = params.rowIndex + Math.max(Math.floor(params.rowSpan ?? 1), 1) - 1;
19912
- if (mode === "cell") {
19913
- return EMPTY_RANGE_HIGHLIGHT_STATE;
19914
- }
19915
- if (mode === "row") {
19916
- return {
19917
- ...resolveNextRowHighlightByIndexRange({
19918
- current: params.current,
19919
- rangeStartIndex: params.rowIndex,
19920
- rangeEndIndex,
19921
- shiftKey: params.shiftKey
19922
- }),
19923
- selectedColumnAnchorKey: null,
19924
- selectedColumnRange: null
19925
- };
19926
- }
19927
- if (mode === "column") {
19928
- if (!canHighlightColumn || !params.columnRange) {
19929
- return {
19930
- ...params.current,
19931
- selectedRowAnchorIndex: null,
19932
- selectedRowRange: null
19933
- };
19934
- }
19935
- return {
19936
- ...resolveNextColumnHighlightByIndexRange({
19937
- current: params.current,
19938
- columnKey: params.columnKey,
19939
- leafColumnKeys: params.leafColumnKeys,
19940
- rangeStartIndex: params.columnRange.start,
19941
- rangeEndIndex: params.columnRange.end,
19942
- shiftKey: params.shiftKey
19943
- }),
19944
- selectedRowAnchorIndex: null,
19945
- selectedRowRange: null
19946
- };
19947
- }
19948
- let next = resolveNextRowHighlightByIndexRange({
19949
- current: params.current,
19950
- rangeStartIndex: params.rowIndex,
19951
- rangeEndIndex,
19952
- shiftKey: params.shiftKey
19953
- });
19954
- if (canHighlightColumn && params.columnRange) {
19955
- next = resolveNextColumnHighlightByIndexRange({
19956
- current: next,
19957
- columnKey: params.columnKey,
19958
- leafColumnKeys: params.leafColumnKeys,
19959
- rangeStartIndex: params.columnRange.start,
19960
- rangeEndIndex: params.columnRange.end,
19961
- shiftKey: false
19962
- });
19963
- }
19964
- return next;
19965
- }
19966
- function isCellHighlightedByMode(params) {
19967
- const mode = resolveHighlightMode(params.mode);
19968
- if (mode === "cell") {
19969
- return params.isCellSelected;
19970
- }
19971
- if (mode === "row") {
19972
- return params.isRowRangeSelected;
19973
- }
19974
- if (mode === "column") {
19975
- return params.isColumnRangeSelected;
19976
- }
19977
- return params.isRowRangeSelected || params.isColumnRangeSelected;
19978
- }
19979
-
19980
- // src/rendering/primitives/draw-body.ts
19981
20738
  var VALIDATION_ERROR_COLOR = "#ff4d4f";
19982
20739
  var VALIDATION_TEXT_COLOR = "#ffffff";
19983
20740
  var VALIDATION_MUTED_TEXT_COLOR = "rgba(255, 255, 255, 0.72)";
@@ -20061,7 +20818,8 @@ function drawSegmentRows(params, segment, columns, startRowIndex, endRowIndex, r
20061
20818
  columns,
20062
20819
  startRowIndex,
20063
20820
  endRowIndex,
20064
- rowIndexMap: params.rowIndexMap
20821
+ rowIndexMap: params.rowIndexMap,
20822
+ horizontalClip: clipRect
20065
20823
  });
20066
20824
  const animatedCellHeights = /* @__PURE__ */ new Map();
20067
20825
  let collapseAnimationOverflow = 0;
@@ -20097,7 +20855,7 @@ function drawSegmentRows(params, segment, columns, startRowIndex, endRowIndex, r
20097
20855
  renderClipRect.height
20098
20856
  );
20099
20857
  ctx.clip();
20100
- const orderedCells = [...visibleCells].sort(
20858
+ const orderedCells = animatedCellHeights.size === 0 ? visibleCells : [...visibleCells].sort(
20101
20859
  (left, right) => Number(animatedCellHeights.has(left)) - Number(animatedCellHeights.has(right))
20102
20860
  );
20103
20861
  orderedCells.forEach((cell) => {
@@ -20123,7 +20881,7 @@ function drawSegmentRows(params, segment, columns, startRowIndex, endRowIndex, r
20123
20881
  width: getMergedCellWidth(columns, cell.columnIndex, cell.colSpan),
20124
20882
  height: animatedCellHeights.get(cell) ?? baseHeight
20125
20883
  };
20126
- if (rect.y + rect.height <= params.bodyTop || rect.y >= params.bodyTop + params.bodyHeight) {
20884
+ if (rect.x + rect.width < clipRect.x - 1 || rect.x > clipRect.x + clipRect.width + 1 || rect.y + rect.height <= params.bodyTop || rect.y >= params.bodyTop + params.bodyHeight) {
20127
20885
  return;
20128
20886
  }
20129
20887
  const isHovered = params.hover.area === "body" && params.hover.rowIndex === cell.sourceRowIndex && params.hover.columnKey === cell.column.key;
@@ -20131,24 +20889,28 @@ function drawSegmentRows(params, segment, columns, startRowIndex, endRowIndex, r
20131
20889
  const hasCellTextSelection = params.selection.textSelection?.fragments.some(
20132
20890
  (fragment) => fragment.rowIndex === cell.sourceRowIndex && fragment.columnKey === cell.column.key
20133
20891
  ) === true;
20134
- const isRowRangeSelected = cell.row.kind === "data" && isRowSpanInHighlightRange(
20135
- cell.sourceRowIndex,
20136
- cell.rowSpan,
20892
+ const isRowRangeSelected = cell.row.kind === "data" && (params.rangeHighlight?.selectedRowRanges ?? ((params.rangeHighlight ?? EMPTY_RANGE_HIGHLIGHT3).selectedRowRange ? [
20137
20893
  (params.rangeHighlight ?? EMPTY_RANGE_HIGHLIGHT3).selectedRowRange
20894
+ ] : [])).some(
20895
+ (range) => isRowSpanInHighlightRange(cell.sourceRowIndex, cell.rowSpan, range)
20138
20896
  );
20139
- const isColumnRangeSelected = cell.row.kind === "data" && isIndexInHighlightRange3(
20140
- cell.column.leafIndex,
20141
- (params.rangeHighlight ?? EMPTY_RANGE_HIGHLIGHT3).selectedColumnRange
20897
+ const isColumnRangeSelected = cell.row.kind === "data" && isColumnInHighlight(
20898
+ params.rangeHighlight ?? EMPTY_RANGE_HIGHLIGHT3,
20899
+ cell.column.key,
20900
+ cell.column.leafIndex
20901
+ );
20902
+ const isSelectedCellRange = cell.row.kind === "data" && isCellInHighlight(
20903
+ params.rangeHighlight ?? EMPTY_RANGE_HIGHLIGHT3,
20904
+ cell.sourceRowIndex,
20905
+ cell.column.leafIndex
20142
20906
  );
20143
- const selectedCellRange = (params.rangeHighlight ?? EMPTY_RANGE_HIGHLIGHT3).selectedCellRange;
20144
- const isSelectedCellRange = cell.row.kind === "data" && selectedCellRange != null && cell.sourceRowIndex >= selectedCellRange.startRowIndex && cell.sourceRowIndex <= selectedCellRange.endRowIndex && cell.column.leafIndex >= selectedCellRange.startColumnIndex && cell.column.leafIndex <= selectedCellRange.endColumnIndex;
20145
20907
  const isHeaderColumnSelected = cell.row.kind === "data" && params.selectedHeaderKey != null && cell.column.key === params.selectedHeaderKey;
20146
- const isRangeHighlighted = isCellHighlightedByMode({
20908
+ const isRangeHighlighted = isSelectedCellRange || isCellHighlightedByMode({
20147
20909
  mode: params.highlightMode,
20148
20910
  isCellSelected: isSelected || isSelectedCellRange,
20149
20911
  isRowRangeSelected,
20150
20912
  isColumnRangeSelected
20151
- });
20913
+ }) || params.selection.rowIndex == null && (params.selectedHeaderKey != null || (params.rangeHighlight ?? EMPTY_RANGE_HIGHLIGHT3).selectedColumnKeys != null) && isColumnRangeSelected;
20152
20914
  const validationMessage = cell.row.kind === "data" ? params.getValidationMessage?.({
20153
20915
  record: cell.record,
20154
20916
  rowIndex: cell.sourceRowIndex,
@@ -20480,9 +21242,6 @@ function findVisibleDataRow(rows, startIndex, step) {
20480
21242
  }
20481
21243
  return null;
20482
21244
  }
20483
- function isIndexInHighlightRange3(index, range) {
20484
- return Boolean(range && index >= range.start && index <= range.end);
20485
- }
20486
21245
  function isRowSpanInHighlightRange(rowIndex, rowSpan, range) {
20487
21246
  if (!range) {
20488
21247
  return false;
@@ -20577,6 +21336,14 @@ function buildMergedBodyCells(rows, columns, mergeCell, rowIndexMap) {
20577
21336
  function buildPlainVisibleBodyCells(params) {
20578
21337
  const { rows, columns, startRowIndex, endRowIndex } = params;
20579
21338
  const visibleCells = [];
21339
+ const clip = params.horizontalClip;
21340
+ const visibleColumnIndexes = [];
21341
+ for (let index = 0; index < columns.length; index += 1) {
21342
+ const column = columns[index];
21343
+ if (column && (!clip || column.drawX + column.width >= clip.x - 1 && column.drawX <= clip.x + clip.width + 1)) {
21344
+ visibleColumnIndexes.push(index);
21345
+ }
21346
+ }
20580
21347
  for (let rowIndex = startRowIndex; rowIndex < endRowIndex; rowIndex += 1) {
20581
21348
  const row = rows[rowIndex];
20582
21349
  if (!row) {
@@ -20600,7 +21367,7 @@ function buildPlainVisibleBodyCells(params) {
20600
21367
  continue;
20601
21368
  }
20602
21369
  const sourceRowIndex = row.dataRowIndex;
20603
- for (let columnIndex = 0; columnIndex < columns.length; columnIndex += 1) {
21370
+ for (const columnIndex of visibleColumnIndexes) {
20604
21371
  const column = columns[columnIndex];
20605
21372
  if (!column) {
20606
21373
  continue;
@@ -21582,7 +22349,7 @@ function getMaxDepth(columns) {
21582
22349
  var DEFAULT_HORIZONTAL_LEFT = 0;
21583
22350
  var DEFAULT_HORIZONTAL_RIGHT = 0;
21584
22351
  var DEFAULT_HORIZONTAL_BOTTOM = 0;
21585
- var DEFAULT_VERTICAL_TOP = 6;
22352
+ var DEFAULT_VERTICAL_TOP = 0;
21586
22353
  var DEFAULT_VERTICAL_RIGHT = 0;
21587
22354
  var DEFAULT_VERTICAL_BOTTOM = 0;
21588
22355
  var MIN_SCROLLBAR_THUMB_SIZE = 36;
@@ -21592,6 +22359,7 @@ function computeScrollbarLayout(params) {
21592
22359
  const vertical = utils.resolveCanvasScrollbarAxis(params.scrollbar, "vertical");
21593
22360
  const horizontalLeft = Math.max(
21594
22361
  params.scrollbar?.horizontalLeft ?? DEFAULT_HORIZONTAL_LEFT,
22362
+ params.rowSelectorWidth ?? 0,
21595
22363
  0
21596
22364
  );
21597
22365
  const horizontalRight = Math.max(
@@ -21621,43 +22389,69 @@ function computeScrollbarLayout(params) {
21621
22389
  const horizontalTrackY = params.height - horizontal.thickness - horizontalBottom;
21622
22390
  if (horizontal.visible && params.maxScrollLeft > 0 && horizontalTrackWidth > 0) {
21623
22391
  const thickness = horizontal.thickness;
21624
- const buttonSize = Math.min(thickness, horizontalTrackWidth / 3);
21625
- const trackX = horizontalLeft + buttonSize;
21626
- const trackWidth = Math.max(horizontalTrackWidth - buttonSize * 2, 0);
21627
- const thumbWidth = Math.min(
21628
- Math.max(
21629
- params.centerVisibleWidth / params.centerContentWidth * trackWidth,
21630
- MIN_SCROLLBAR_THUMB_SIZE
21631
- ),
21632
- trackWidth
21633
- );
21634
- const thumbOffset = params.maxScrollLeft === 0 ? 0 : params.scrollLeft / params.maxScrollLeft * (trackWidth - thumbWidth);
21635
- layout.horizontal = {
21636
- decreaseButton: {
21637
- x: horizontalLeft,
21638
- y: horizontalTrackY,
21639
- width: buttonSize,
21640
- height: thickness
21641
- },
21642
- increaseButton: {
21643
- x: horizontalLeft + horizontalTrackWidth - buttonSize,
22392
+ if (horizontal.mode === "button") {
22393
+ const halfWidth = horizontalTrackWidth / 2;
22394
+ const empty = {
22395
+ x: horizontalLeft + halfWidth,
21644
22396
  y: horizontalTrackY,
21645
- width: buttonSize,
21646
- height: thickness
21647
- },
21648
- track: {
21649
- x: trackX,
21650
- y: horizontalTrackY,
21651
- width: trackWidth,
21652
- height: thickness
21653
- },
21654
- thumb: {
21655
- x: trackX + thumbOffset,
21656
- y: horizontalTrackY,
21657
- width: thumbWidth,
21658
- height: thickness
21659
- }
21660
- };
22397
+ width: 0,
22398
+ height: 0
22399
+ };
22400
+ layout.horizontal = {
22401
+ decreaseButton: {
22402
+ x: horizontalLeft,
22403
+ y: horizontalTrackY,
22404
+ width: halfWidth,
22405
+ height: thickness
22406
+ },
22407
+ increaseButton: {
22408
+ x: horizontalLeft + halfWidth,
22409
+ y: horizontalTrackY,
22410
+ width: horizontalTrackWidth - halfWidth,
22411
+ height: thickness
22412
+ },
22413
+ track: empty,
22414
+ thumb: empty
22415
+ };
22416
+ } else {
22417
+ const buttonSize = Math.min(thickness, horizontalTrackWidth / 3);
22418
+ const trackX = horizontalLeft + buttonSize;
22419
+ const trackWidth = Math.max(horizontalTrackWidth - buttonSize * 2, 0);
22420
+ const thumbWidth = Math.min(
22421
+ Math.max(
22422
+ params.centerVisibleWidth / params.centerContentWidth * trackWidth,
22423
+ MIN_SCROLLBAR_THUMB_SIZE
22424
+ ),
22425
+ trackWidth
22426
+ );
22427
+ const thumbOffset = params.maxScrollLeft === 0 ? 0 : params.scrollLeft / params.maxScrollLeft * (trackWidth - thumbWidth);
22428
+ layout.horizontal = {
22429
+ decreaseButton: {
22430
+ x: horizontalLeft,
22431
+ y: horizontalTrackY,
22432
+ width: buttonSize,
22433
+ height: thickness
22434
+ },
22435
+ increaseButton: {
22436
+ x: horizontalLeft + horizontalTrackWidth - buttonSize,
22437
+ y: horizontalTrackY,
22438
+ width: buttonSize,
22439
+ height: thickness
22440
+ },
22441
+ track: {
22442
+ x: trackX,
22443
+ y: horizontalTrackY,
22444
+ width: trackWidth,
22445
+ height: thickness
22446
+ },
22447
+ thumb: {
22448
+ x: trackX + thumbOffset,
22449
+ y: horizontalTrackY,
22450
+ width: thumbWidth,
22451
+ height: thickness
22452
+ }
22453
+ };
22454
+ }
21661
22455
  }
21662
22456
  const verticalTrackHeight = Math.max(
21663
22457
  params.bodyHeight - verticalTop - verticalBottom,
@@ -21667,43 +22461,69 @@ function computeScrollbarLayout(params) {
21667
22461
  const verticalTrackY = params.bodyTop + verticalTop;
21668
22462
  if (vertical.visible && params.maxScrollTop > 0 && verticalTrackHeight > 0) {
21669
22463
  const thickness = vertical.thickness;
21670
- const buttonSize = Math.min(thickness, verticalTrackHeight / 3);
21671
- const trackY = verticalTrackY + buttonSize;
21672
- const trackHeight = Math.max(verticalTrackHeight - buttonSize * 2, 0);
21673
- const thumbHeight = Math.min(
21674
- Math.max(
21675
- params.bodyHeight / params.totalBodyHeight * trackHeight,
21676
- MIN_SCROLLBAR_THUMB_SIZE
21677
- ),
21678
- trackHeight
21679
- );
21680
- const thumbOffset = params.maxScrollTop === 0 ? 0 : params.scrollTop / params.maxScrollTop * (trackHeight - thumbHeight);
21681
- layout.vertical = {
21682
- decreaseButton: {
21683
- x: verticalTrackX,
21684
- y: verticalTrackY,
21685
- width: thickness,
21686
- height: buttonSize
21687
- },
21688
- increaseButton: {
21689
- x: verticalTrackX,
21690
- y: verticalTrackY + verticalTrackHeight - buttonSize,
21691
- width: thickness,
21692
- height: buttonSize
21693
- },
21694
- track: {
21695
- x: verticalTrackX,
21696
- y: trackY,
21697
- width: thickness,
21698
- height: trackHeight
21699
- },
21700
- thumb: {
22464
+ if (vertical.mode === "button") {
22465
+ const halfHeight = verticalTrackHeight / 2;
22466
+ const empty = {
21701
22467
  x: verticalTrackX,
21702
- y: trackY + thumbOffset,
21703
- width: thickness,
21704
- height: thumbHeight
21705
- }
21706
- };
22468
+ y: verticalTrackY + halfHeight,
22469
+ width: 0,
22470
+ height: 0
22471
+ };
22472
+ layout.vertical = {
22473
+ decreaseButton: {
22474
+ x: verticalTrackX,
22475
+ y: verticalTrackY,
22476
+ width: thickness,
22477
+ height: halfHeight
22478
+ },
22479
+ increaseButton: {
22480
+ x: verticalTrackX,
22481
+ y: verticalTrackY + halfHeight,
22482
+ width: thickness,
22483
+ height: verticalTrackHeight - halfHeight
22484
+ },
22485
+ track: empty,
22486
+ thumb: empty
22487
+ };
22488
+ } else {
22489
+ const buttonSize = Math.min(thickness, verticalTrackHeight / 3);
22490
+ const trackY = verticalTrackY + buttonSize;
22491
+ const trackHeight = Math.max(verticalTrackHeight - buttonSize * 2, 0);
22492
+ const thumbHeight = Math.min(
22493
+ Math.max(
22494
+ params.bodyHeight / params.totalBodyHeight * trackHeight,
22495
+ MIN_SCROLLBAR_THUMB_SIZE
22496
+ ),
22497
+ trackHeight
22498
+ );
22499
+ const thumbOffset = params.maxScrollTop === 0 ? 0 : params.scrollTop / params.maxScrollTop * (trackHeight - thumbHeight);
22500
+ layout.vertical = {
22501
+ decreaseButton: {
22502
+ x: verticalTrackX,
22503
+ y: verticalTrackY,
22504
+ width: thickness,
22505
+ height: buttonSize
22506
+ },
22507
+ increaseButton: {
22508
+ x: verticalTrackX,
22509
+ y: verticalTrackY + verticalTrackHeight - buttonSize,
22510
+ width: thickness,
22511
+ height: buttonSize
22512
+ },
22513
+ track: {
22514
+ x: verticalTrackX,
22515
+ y: trackY,
22516
+ width: thickness,
22517
+ height: trackHeight
22518
+ },
22519
+ thumb: {
22520
+ x: verticalTrackX,
22521
+ y: trackY + thumbOffset,
22522
+ width: thickness,
22523
+ height: thumbHeight
22524
+ }
22525
+ };
22526
+ }
21707
22527
  }
21708
22528
  return layout;
21709
22529
  }
@@ -21921,30 +22741,25 @@ function resolveColumnSegments(params) {
21921
22741
  // src/domain/columns/header-layout.ts
21922
22742
  function buildHeaderLayouts(nodes, leafColumns, rightStartX, headerRowHeight, fixedWidthLeft, fixedWidthRight, scrollLeft) {
21923
22743
  const rows = /* @__PURE__ */ new Map();
21924
- const leftCols = leafColumns.filter((c) => c.fixed === "left");
21925
- const centerCols = leafColumns.filter((c) => !c.fixed);
21926
- const rightCols = leafColumns.filter((c) => c.fixed === "right");
21927
- function segmentOffset(cols, targetKey) {
21928
- let offset = 0;
21929
- for (const c of cols) {
21930
- if (c.key === targetKey) return offset;
21931
- offset += c.width;
21932
- }
21933
- return offset;
21934
- }
21935
- const getLeafX = (leafIndex) => {
21936
- const leaf = leafColumns[leafIndex];
21937
- if (!leaf) {
21938
- return 0;
21939
- }
22744
+ let leftX = 0;
22745
+ let centerX = fixedWidthLeft - scrollLeft;
22746
+ let rightX = rightStartX;
22747
+ const leafPositions = leafColumns.map((leaf) => {
21940
22748
  if (leaf.fixed === "left") {
21941
- return segmentOffset(leftCols, leaf.key);
22749
+ const x2 = leftX;
22750
+ leftX += leaf.width;
22751
+ return x2;
21942
22752
  }
21943
22753
  if (leaf.fixed === "right") {
21944
- return rightStartX + segmentOffset(rightCols, leaf.key);
22754
+ const x2 = rightX;
22755
+ rightX += leaf.width;
22756
+ return x2;
21945
22757
  }
21946
- return fixedWidthLeft + segmentOffset(centerCols, leaf.key) - scrollLeft;
21947
- };
22758
+ const x = centerX;
22759
+ centerX += leaf.width;
22760
+ return x;
22761
+ });
22762
+ const getLeafX = (leafIndex) => leafPositions[leafIndex] ?? 0;
21948
22763
  const visit = (node) => {
21949
22764
  const rowItems = rows.get(node.depth) ?? [];
21950
22765
  buildHeaderCellSegments(node, leafColumns, {
@@ -23545,13 +24360,15 @@ var AUTO_ROW_HEIGHT_SAMPLE_COUNT = 200;
23545
24360
  function buildListTableRenderSnapshot(params) {
23546
24361
  if (params.cache?.snapshot && params.cache.snapshot.groupIndex === params.cache.groupIndex && isSameRenderCacheInputs(params.cache.inputs, params)) {
23547
24362
  const snapshotStartedAt2 = resolvePerformanceNow3();
23548
- const snapshot2 = updateListTableRenderSnapshotScroll({
24363
+ const cached = params.cache.snapshot;
24364
+ const snapshot2 = !cached.swapAnimation && cached.scroll.left === params.scroll.left && cached.scroll.top === params.scroll.top ? cached : updateListTableRenderSnapshotScroll({
23549
24365
  snapshot: params.cache.snapshot,
23550
24366
  width: params.width,
23551
24367
  height: params.height,
23552
24368
  scroll: params.scroll,
23553
24369
  options: params.options
23554
24370
  });
24371
+ params.cache.snapshot = snapshot2;
23555
24372
  params.options.debug?.onPerformance?.({
23556
24373
  phase: "snapshot",
23557
24374
  durationMs: resolvePerformanceNow3() - snapshotStartedAt2,
@@ -23914,6 +24731,7 @@ function buildListTableRenderSnapshot(params) {
23914
24731
  });
23915
24732
  const scrollbarLayout = computeScrollbarLayout({
23916
24733
  width: hostWidth,
24734
+ rowSelectorWidth: leafColumns.find((column) => column.key === ROW_SELECTOR_COLUMN_KEY)?.width ?? 0,
23917
24735
  height: hostHeight,
23918
24736
  bodyTop: bodyState.bodyLayout.scrollBodyTop,
23919
24737
  bodyHeight: bodyState.bodyLayout.scrollBodyHeight,
@@ -24126,12 +24944,13 @@ function updateListTableRenderSnapshotScroll(params) {
24126
24944
  scroll
24127
24945
  });
24128
24946
  const nextScroll = viewportResult.scroll;
24129
- const columnSegments = resolveColumnSegments({
24947
+ const reuseHorizontalLayout = nextScroll.left === snapshot.scroll.left && !snapshot.swapAnimation;
24948
+ const columnSegments = reuseHorizontalLayout ? snapshot.columnSegments : resolveColumnSegments({
24130
24949
  metrics: snapshot.columnMetrics,
24131
24950
  scrollLeft: nextScroll.left,
24132
24951
  swapAnimation: snapshot.swapAnimation
24133
24952
  });
24134
- const headerRows = buildHeaderLayouts(
24953
+ const headerRows = reuseHorizontalLayout ? snapshot.headerRows : buildHeaderLayouts(
24135
24954
  snapshot.headerTree,
24136
24955
  snapshot.leafColumns,
24137
24956
  snapshot.columnMetrics.rightStartX,
@@ -24140,7 +24959,8 @@ function updateListTableRenderSnapshotScroll(params) {
24140
24959
  snapshot.columnMetrics.fixedWidthRight,
24141
24960
  nextScroll.left
24142
24961
  );
24143
- offsetHeaderRows(headerRows, snapshot.groupingBarHeight);
24962
+ if (!reuseHorizontalLayout)
24963
+ offsetHeaderRows(headerRows, snapshot.groupingBarHeight);
24144
24964
  if (snapshot.swapAnimation) {
24145
24965
  applyColumnSegmentPositionsToHeaderRows({
24146
24966
  headerRows,
@@ -24150,6 +24970,9 @@ function updateListTableRenderSnapshotScroll(params) {
24150
24970
  }
24151
24971
  const scrollbarLayout = computeScrollbarLayout({
24152
24972
  width,
24973
+ rowSelectorWidth: snapshot.leafColumns.find(
24974
+ (column) => column.key === ROW_SELECTOR_COLUMN_KEY
24975
+ )?.width ?? 0,
24153
24976
  height,
24154
24977
  bodyTop: snapshot.bodyLayout.scrollBodyTop,
24155
24978
  bodyHeight: snapshot.bodyLayout.scrollBodyHeight,
@@ -24488,7 +25311,7 @@ function buildListTableDebugSnapshot(params) {
24488
25311
  hitTest: hitTest2.area === "empty" ? void 0 : hitTest2
24489
25312
  };
24490
25313
  }
24491
- function drawScrollbars(ctx, layout, descriptors, borderColor, opacity = 1, interactive = true, verticalFrame, options) {
25314
+ function drawScrollbars(ctx, layout, descriptors, borderColor, opacity = 1, interactive = true, verticalFrame, options, hover) {
24492
25315
  const alpha = Number.isFinite(opacity) ? Math.min(Math.max(opacity, 0), 1) : 1;
24493
25316
  if (alpha <= 1e-3) {
24494
25317
  return;
@@ -24513,7 +25336,8 @@ function drawScrollbars(ctx, layout, descriptors, borderColor, opacity = 1, inte
24513
25336
  descriptors,
24514
25337
  borderColor,
24515
25338
  interactive,
24516
- utils.resolveCanvasScrollbarAxis(options, "horizontal")
25339
+ utils.resolveCanvasScrollbarAxis(options, "horizontal"),
25340
+ hover
24517
25341
  );
24518
25342
  }
24519
25343
  if (layout.vertical) {
@@ -24524,7 +25348,8 @@ function drawScrollbars(ctx, layout, descriptors, borderColor, opacity = 1, inte
24524
25348
  descriptors,
24525
25349
  borderColor,
24526
25350
  interactive,
24527
- utils.resolveCanvasScrollbarAxis(options, "vertical")
25351
+ utils.resolveCanvasScrollbarAxis(options, "vertical"),
25352
+ hover
24528
25353
  );
24529
25354
  }
24530
25355
  if (layout.horizontal) {
@@ -24535,10 +25360,13 @@ function drawScrollbars(ctx, layout, descriptors, borderColor, opacity = 1, inte
24535
25360
  borderColor
24536
25361
  );
24537
25362
  }
25363
+ if (layout.vertical) {
25364
+ drawVerticalAxisFrame(ctx, layout.vertical, borderColor, verticalFrame);
25365
+ }
24538
25366
  ctx.restore();
24539
25367
  }
24540
25368
  function drawHorizontalScrollbarMask(ctx, layout, backgroundColor) {
24541
- const left = layout.decreaseButton.x;
25369
+ const left = 0;
24542
25370
  const right = layout.increaseButton.x + layout.increaseButton.width;
24543
25371
  const top = layout.decreaseButton.y;
24544
25372
  const height = layout.decreaseButton.height;
@@ -24551,7 +25379,7 @@ function drawHorizontalScrollbarMask(ctx, layout, backgroundColor) {
24551
25379
  ctx.restore();
24552
25380
  }
24553
25381
  function drawHorizontalScrollbarTopBorder(ctx, horizontal, vertical, borderColor) {
24554
- const left = Math.round(horizontal.decreaseButton.x) + 0.5;
25382
+ const left = 0.5;
24555
25383
  const horizontalRight = horizontal.increaseButton.x + horizontal.increaseButton.width;
24556
25384
  const verticalRight = vertical ? vertical.decreaseButton.x + vertical.decreaseButton.width : horizontalRight;
24557
25385
  const right = Math.round(Math.max(horizontalRight, verticalRight)) - 0.5;
@@ -24568,21 +25396,23 @@ function drawHorizontalScrollbarTopBorder(ctx, horizontal, vertical, borderColor
24568
25396
  ctx.stroke();
24569
25397
  ctx.restore();
24570
25398
  }
24571
- function drawAxisScrollbar(ctx, layout, axis, descriptors, borderColor, interactive, style) {
25399
+ function drawAxisScrollbar(ctx, layout, axis, descriptors, borderColor, interactive, style, hover) {
24572
25400
  const kind = axis === "horizontal" ? "horizontal-scrollbar" : "vertical-scrollbar";
24573
25401
  if (interactive) {
24574
- descriptors.push({
24575
- kind,
24576
- rect: layout.thumb,
24577
- columnKey: `__${axis}_scrollbar_thumb__`,
24578
- scrollbarRole: "thumb"
24579
- });
24580
- descriptors.push({
24581
- kind,
24582
- rect: layout.track,
24583
- columnKey: `__${axis}_scrollbar_track__`,
24584
- scrollbarRole: "track"
24585
- });
25402
+ if (style.mode === "scrollbar") {
25403
+ descriptors.push({
25404
+ kind,
25405
+ rect: layout.thumb,
25406
+ columnKey: `__${axis}_scrollbar_thumb__`,
25407
+ scrollbarRole: "thumb"
25408
+ });
25409
+ descriptors.push({
25410
+ kind,
25411
+ rect: layout.track,
25412
+ columnKey: `__${axis}_scrollbar_track__`,
25413
+ scrollbarRole: "track"
25414
+ });
25415
+ }
24586
25416
  descriptors.push({
24587
25417
  kind,
24588
25418
  rect: layout.decreaseButton,
@@ -24608,10 +25438,101 @@ function drawAxisScrollbar(ctx, layout, axis, descriptors, borderColor, interact
24608
25438
  }
24609
25439
  ctx.restore();
24610
25440
  }
25441
+ if (style.mode === "scrollbar") {
25442
+ const pressedTrack = hover?.pressedScrollbarPart?.axis === axis && hover.pressedScrollbarPart.role === "track";
25443
+ const hoveredTrack = hover?.area === `${axis}-scrollbar` && hover.scrollbarRole === "track";
25444
+ if (pressedTrack || hoveredTrack) {
25445
+ ctx.fillStyle = pressedTrack ? style.buttonPressedBackgroundColor : style.buttonHoverBackgroundColor;
25446
+ ctx.fillRect(
25447
+ layout.track.x,
25448
+ layout.track.y,
25449
+ layout.track.width,
25450
+ layout.track.height
25451
+ );
25452
+ }
25453
+ }
25454
+ for (const [role, rect] of [
25455
+ ["decrease-button", layout.decreaseButton],
25456
+ ["increase-button", layout.increaseButton]
25457
+ ]) {
25458
+ const pressed = hover?.pressedScrollbarButton?.axis === axis && hover.pressedScrollbarButton.role === role;
25459
+ const hovered = hover?.area === `${axis}-scrollbar` && hover.scrollbarRole === role;
25460
+ const background = pressed ? style.buttonPressedBackgroundColor : hovered ? style.buttonHoverBackgroundColor : style.buttonBackgroundColor;
25461
+ if (background) {
25462
+ ctx.fillStyle = background;
25463
+ ctx.fillRect(rect.x, rect.y, rect.width, rect.height);
25464
+ }
25465
+ }
24611
25466
  drawButton(ctx, layout.decreaseButton, axis, "decrease", style.buttonColor);
24612
25467
  drawButton(ctx, layout.increaseButton, axis, "increase", style.buttonColor);
24613
- drawThumb(ctx, layout.thumb, style);
24614
- drawTrackBorder(ctx, layout.track, axis, borderColor);
25468
+ if (style.mode === "button") {
25469
+ drawButtonDivider(ctx, layout.increaseButton, axis, borderColor);
25470
+ if (axis === "horizontal") {
25471
+ drawButtonModeFrame(ctx, layout.decreaseButton, borderColor);
25472
+ }
25473
+ } else {
25474
+ const pressedThumb = hover?.pressedScrollbarPart?.axis === axis && hover.pressedScrollbarPart.role === "thumb";
25475
+ const hoveredThumb = hover?.area === `${axis}-scrollbar` && hover.scrollbarRole === "thumb";
25476
+ drawThumb(
25477
+ ctx,
25478
+ layout.thumb,
25479
+ style,
25480
+ pressedThumb ? style.buttonPressedBackgroundColor : hoveredThumb ? style.buttonHoverBackgroundColor : void 0
25481
+ );
25482
+ drawTrackBorder(ctx, layout.track, axis, borderColor);
25483
+ if (axis === "horizontal") {
25484
+ drawButtonModeFrame(ctx, layout.decreaseButton, borderColor);
25485
+ }
25486
+ }
25487
+ }
25488
+ function drawButtonModeFrame(ctx, first, borderColor) {
25489
+ const left = Math.round(first.x) + 0.5;
25490
+ const top = Math.round(first.y) + 0.5;
25491
+ ctx.save();
25492
+ ctx.strokeStyle = borderColor;
25493
+ ctx.lineWidth = 1;
25494
+ ctx.beginPath();
25495
+ ctx.moveTo(left, top);
25496
+ ctx.lineTo(left, Math.round(first.y + first.height) - 0.5);
25497
+ ctx.stroke();
25498
+ ctx.restore();
25499
+ }
25500
+ function drawVerticalAxisFrame(ctx, layout, borderColor, frame) {
25501
+ const first = layout.decreaseButton;
25502
+ const last = layout.increaseButton;
25503
+ const left = Math.round(first.x) + 0.5;
25504
+ const right = Math.round(first.x + first.width) - 0.5;
25505
+ const top = Math.round(frame?.top ?? first.y) + 0.5;
25506
+ const buttonTop = Math.round(first.y) + 0.5;
25507
+ const bottom = Math.round(frame ? frame.top + frame.height : last.y + last.height) - 0.5;
25508
+ if (right <= left || bottom <= top) return;
25509
+ ctx.save();
25510
+ ctx.strokeStyle = borderColor;
25511
+ ctx.lineWidth = 1;
25512
+ ctx.beginPath();
25513
+ ctx.moveTo(left, bottom);
25514
+ ctx.lineTo(left, top);
25515
+ ctx.moveTo(left, buttonTop);
25516
+ ctx.lineTo(right, buttonTop);
25517
+ ctx.stroke();
25518
+ ctx.restore();
25519
+ }
25520
+ function drawButtonDivider(ctx, second, axis, borderColor) {
25521
+ ctx.save();
25522
+ ctx.strokeStyle = borderColor;
25523
+ ctx.lineWidth = 1;
25524
+ ctx.beginPath();
25525
+ if (axis === "horizontal") {
25526
+ const x = Math.round(second.x) + 0.5;
25527
+ ctx.moveTo(x, second.y);
25528
+ ctx.lineTo(x, second.y + second.height);
25529
+ } else {
25530
+ const y = Math.round(second.y) + 0.5;
25531
+ ctx.moveTo(Math.round(second.x) + 0.5, y);
25532
+ ctx.lineTo(Math.round(second.x + second.width) - 0.5, y);
25533
+ }
25534
+ ctx.stroke();
25535
+ ctx.restore();
24615
25536
  }
24616
25537
  function drawTrackBorder(ctx, rect, axis, borderColor) {
24617
25538
  const left = Math.round(rect.x) + 0.5;
@@ -24673,7 +25594,7 @@ function drawVerticalScrollbarFrame(ctx, frame, borderColor) {
24673
25594
  }
24674
25595
  ctx.restore();
24675
25596
  }
24676
- function drawThumb(ctx, rect, style) {
25597
+ function drawThumb(ctx, rect, style, highlightColor) {
24677
25598
  ctx.save();
24678
25599
  ctx.fillStyle = style.thumbColor;
24679
25600
  roundRect2(
@@ -24685,6 +25606,18 @@ function drawThumb(ctx, rect, style) {
24685
25606
  style.thumbRadius ?? rect.height / 2
24686
25607
  );
24687
25608
  ctx.fill();
25609
+ if (highlightColor) {
25610
+ ctx.fillStyle = highlightColor;
25611
+ roundRect2(
25612
+ ctx,
25613
+ rect.x,
25614
+ rect.y,
25615
+ rect.width,
25616
+ rect.height,
25617
+ style.thumbRadius ?? rect.height / 2
25618
+ );
25619
+ ctx.fill();
25620
+ }
24688
25621
  ctx.restore();
24689
25622
  }
24690
25623
  function drawButton(ctx, rect, axis, direction, color) {
@@ -25016,6 +25949,7 @@ function drawListTableHeader(params) {
25016
25949
  const collapsedColumnMarkers = resolveCollapsedHeaderColumnMarkers({
25017
25950
  columns: options.columns,
25018
25951
  visibleColumnKeys: leafColumns.map((column) => column.key),
25952
+ showCollapsedColumnIcon: options.showCollapsedColumnIcon,
25019
25953
  // 系统列是否存在由功能配置决定,不属于用户隐藏的业务列,不能绘制恢复标记。
25020
25954
  excludedColumnKeys: [
25021
25955
  ...groupingColumnKeys ?? [],
@@ -25114,7 +26048,7 @@ function resolveActiveCellOverlayLayout(params) {
25114
26048
  highlightMode: params.highlightMode,
25115
26049
  columnKeys: params.columnKeys ?? [],
25116
26050
  allowColumnRange: selection.rowIndex == null,
25117
- allowRowRange: selection.columnKey === ROW_SELECTOR_COLUMN_KEY,
26051
+ allowRowRange: selection.columnKey === ROW_SELECTOR_COLUMN_KEY || params.rangeHighlight?.selectedRowRanges != null || params.rangeHighlight?.selectedCellRanges != null && params.rangeHighlight.selectedRowRange != null,
25118
26052
  resolveClipRect: (item) => resolveActiveCellSectionClipRect(item, params),
25119
26053
  width: params.width,
25120
26054
  height: params.height
@@ -25205,10 +26139,13 @@ function drawActiveCellOverlay(params) {
25205
26139
  }
25206
26140
  function resolveRangeHighlightLayouts(params) {
25207
26141
  const rowRange = params.rangeHighlight?.selectedRowRange;
26142
+ const rowRanges = params.rangeHighlight?.selectedRowRanges ?? (rowRange ? [rowRange] : []);
25208
26143
  const columnRange = params.rangeHighlight?.selectedColumnRange;
26144
+ const selectedColumnKeys = params.rangeHighlight?.selectedColumnKeys;
25209
26145
  const cellRange = params.rangeHighlight?.selectedCellRange;
25210
- const rowSelectorRange = rowRange && params.allowRowRange;
25211
- if (!cellRange && !rowSelectorRange && (!columnRange || !params.allowColumnRange)) {
26146
+ const cellRanges = params.rangeHighlight?.selectedCellRanges ?? (cellRange ? [cellRange] : []);
26147
+ const rowSelectorRange = rowRanges.length > 0 && params.allowRowRange;
26148
+ if (cellRanges.length === 0 && !rowSelectorRange && (!(columnRange || selectedColumnKeys?.length) || !params.allowColumnRange)) {
25212
26149
  return { rects: [], segments: [] };
25213
26150
  }
25214
26151
  const bodyDescriptors = params.descriptors.filter(
@@ -25217,20 +26154,33 @@ function resolveRangeHighlightLayouts(params) {
25217
26154
  const selected = bodyDescriptors.filter((item) => {
25218
26155
  const itemRowStart = item.rowIndex;
25219
26156
  const itemRowEnd = itemRowStart + Math.max(item.rowSpan ?? 1, 1) - 1;
25220
- const rowSelected = rowRange != null && itemRowEnd >= rowRange.start && itemRowStart <= rowRange.end;
26157
+ const rowSelected = rowRanges.some(
26158
+ (range) => itemRowEnd >= range.start && itemRowStart <= range.end
26159
+ );
25221
26160
  const columnIndex = params.columnKeys.indexOf(item.columnKey);
25222
- const columnSelected = columnRange != null && columnIndex >= columnRange.start && columnIndex <= columnRange.end;
25223
- if (cellRange) {
25224
- return itemRowEnd >= cellRange.startRowIndex && itemRowStart <= cellRange.endRowIndex && columnIndex >= cellRange.startColumnIndex && columnIndex <= cellRange.endColumnIndex;
25225
- }
25226
- if (rowSelectorRange) {
25227
- return rowSelected;
25228
- }
25229
- return params.allowColumnRange && columnSelected;
26161
+ const columnSelected = selectedColumnKeys != null ? selectedColumnKeys.includes(item.columnKey) : columnRange != null && columnIndex >= columnRange.start && columnIndex <= columnRange.end;
26162
+ const cellSelected = cellRanges.some(
26163
+ (range) => itemRowEnd >= range.startRowIndex && itemRowStart <= range.endRowIndex && columnIndex >= range.startColumnIndex && columnIndex <= range.endColumnIndex
26164
+ );
26165
+ return cellSelected || rowSelectorRange && rowSelected || params.allowColumnRange && columnSelected;
25230
26166
  });
25231
26167
  const groups = /* @__PURE__ */ new Map();
26168
+ const selectedKeySet = new Set(selectedColumnKeys ?? []);
25232
26169
  selected.forEach((descriptor) => {
25233
- const groupKey = cellRange || rowSelectorRange ? `${descriptor.fixed ?? "center"}:${descriptor.frozen ? "frozen" : "scroll"}` : `column:${descriptor.fixed ?? "center"}:${descriptor.frozen ? "frozen" : "scroll"}`;
26170
+ const columnIndex = params.columnKeys.indexOf(descriptor.columnKey);
26171
+ let runStart = columnIndex;
26172
+ if (selectedColumnKeys != null) {
26173
+ while (runStart > 0 && selectedKeySet.has(params.columnKeys[runStart - 1])) {
26174
+ runStart--;
26175
+ }
26176
+ }
26177
+ const cellRangeIndex = cellRanges.findIndex(
26178
+ (range) => descriptor.rowIndex >= range.startRowIndex && descriptor.rowIndex <= range.endRowIndex && columnIndex >= range.startColumnIndex && columnIndex <= range.endColumnIndex
26179
+ );
26180
+ const rowRangeIndex = rowRanges.findIndex(
26181
+ (range) => descriptor.rowIndex >= range.start && descriptor.rowIndex <= range.end
26182
+ );
26183
+ const groupKey = rowSelectorRange && rowRangeIndex >= 0 ? `row:${rowRangeIndex}:${descriptor.fixed ?? "center"}:${descriptor.frozen ? "frozen" : "scroll"}` : cellRangeIndex >= 0 ? `cell:${cellRangeIndex}:${descriptor.fixed ?? "center"}:${descriptor.frozen ? "frozen" : "scroll"}` : `column:${selectedColumnKeys != null ? runStart : "range"}:${descriptor.fixed ?? "center"}:${descriptor.frozen ? "frozen" : "scroll"}`;
25234
26184
  const current = groups.get(groupKey);
25235
26185
  const rect = current ? {
25236
26186
  x: Math.min(current.rect.x, descriptor.rect.x),
@@ -25265,8 +26215,8 @@ function resolveRangeHighlightLayouts(params) {
25265
26215
  );
25266
26216
  return {
25267
26217
  ...range,
25268
- omitLeft: range.rect.x > minX,
25269
- omitRight: range.rect.x + range.rect.width < maxX
26218
+ omitLeft: selectedColumnKeys == null && range.rect.x > minX,
26219
+ omitRight: selectedColumnKeys == null && range.rect.x + range.rect.width < maxX
25270
26220
  };
25271
26221
  });
25272
26222
  const rangeSegments = resolveRangeBorderSegments(
@@ -25783,7 +26733,8 @@ function executeListTableRenderPipeline(params) {
25783
26733
  headerBackgroundColor: theme.headerBackgroundColor,
25784
26734
  bodyBackgroundColor: theme.backgroundColor
25785
26735
  },
25786
- options.scrollbar
26736
+ options.scrollbar,
26737
+ hover
25787
26738
  );
25788
26739
  drawActiveCellOverlay({
25789
26740
  ctx,
@@ -25836,8 +26787,8 @@ function drawGroupingBar(params) {
25836
26787
  ctx.fillRect(0, 0, width, height);
25837
26788
  ctx.strokeStyle = theme.borderColor;
25838
26789
  ctx.beginPath();
25839
- ctx.moveTo(0, height - 0.5);
25840
- ctx.lineTo(width, height - 0.5);
26790
+ ctx.moveTo(0, height + 0.5);
26791
+ ctx.lineTo(width, height + 0.5);
25841
26792
  ctx.stroke();
25842
26793
  ctx.fillStyle = theme.headerTextColor;
25843
26794
  ctx.font = `${theme.headerFontWeight} ${theme.fontSize}px ${theme.fontFamily}`;
@@ -25929,6 +26880,16 @@ function drawColumnResizeIndicator(params) {
25929
26880
  // src/services/image-clipboard-service.ts
25930
26881
  var MAX_IMAGE_SIDE = 16384;
25931
26882
  var MAX_IMAGE_PIXELS = 16e6;
26883
+ function collectSelectedIndices(ranges, count) {
26884
+ const indices = /* @__PURE__ */ new Set();
26885
+ for (const range of ranges) {
26886
+ for (let index = Math.max(range.start, 0); index <= Math.min(range.end, count - 1); index++) {
26887
+ indices.add(index);
26888
+ if (indices.size > MAX_IMAGE_SIDE) return null;
26889
+ }
26890
+ }
26891
+ return [...indices].sort((left, right) => left - right);
26892
+ }
25932
26893
  function assertTableImageSize(width, height) {
25933
26894
  if (!Number.isFinite(width) || !Number.isFinite(height) || width <= 0 || height <= 0) {
25934
26895
  throw new Error("\u6CA1\u6709\u53EF\u590D\u5236\u7684\u8868\u683C\u533A\u57DF\u3002");
@@ -25939,8 +26900,96 @@ function assertTableImageSize(width, height) {
25939
26900
  }
25940
26901
  function resolveImageSelection(host) {
25941
26902
  const state = host.rangeHighlight;
25942
- if (state.selectedCellRange) return state.selectedCellRange;
25943
26903
  const rowCount = host.getDisplayData().length;
26904
+ const columnCount = host.leafColumns.length;
26905
+ if (state.selectedCellRanges != null) {
26906
+ const rowRanges = state.selectedRowRanges ?? (state.selectedRowRange ? [state.selectedRowRange] : []);
26907
+ if (!state.selectedCellRanges.length && !rowRanges.length) return null;
26908
+ const rowIndices = collectSelectedIndices(
26909
+ [
26910
+ ...state.selectedCellRanges.map((range) => ({
26911
+ start: range.startRowIndex,
26912
+ end: range.endRowIndex
26913
+ })),
26914
+ ...rowRanges
26915
+ ],
26916
+ rowCount
26917
+ );
26918
+ if (rowIndices === null) {
26919
+ return {
26920
+ startRowIndex: 0,
26921
+ endRowIndex: rowCount - 1,
26922
+ startColumnIndex: 0,
26923
+ endColumnIndex: columnCount - 1,
26924
+ tooLarge: true
26925
+ };
26926
+ }
26927
+ const columnIndices = collectSelectedIndices(
26928
+ rowRanges.length ? [{ start: 0, end: columnCount - 1 }] : state.selectedCellRanges.map((range) => ({
26929
+ start: range.startColumnIndex,
26930
+ end: range.endColumnIndex
26931
+ })),
26932
+ columnCount
26933
+ );
26934
+ if (columnIndices === null) {
26935
+ return {
26936
+ startRowIndex: 0,
26937
+ endRowIndex: rowCount - 1,
26938
+ startColumnIndex: 0,
26939
+ endColumnIndex: columnCount - 1,
26940
+ tooLarge: true
26941
+ };
26942
+ }
26943
+ if (!rowIndices.length || !columnIndices.length) return null;
26944
+ return {
26945
+ startRowIndex: rowIndices[0],
26946
+ endRowIndex: rowIndices[rowIndices.length - 1],
26947
+ startColumnIndex: columnIndices[0],
26948
+ endColumnIndex: columnIndices[columnIndices.length - 1],
26949
+ rowIndices,
26950
+ columnIndices,
26951
+ cellRanges: state.selectedCellRanges,
26952
+ rowRanges
26953
+ };
26954
+ }
26955
+ if (state.selectedCellRange) return state.selectedCellRange;
26956
+ if (state.selectedColumnKeys != null) {
26957
+ const columnIndices = host.leafColumns.flatMap(
26958
+ (column, index) => state.selectedColumnKeys?.includes(column.key) ? [index] : []
26959
+ );
26960
+ if (!columnIndices.length || !rowCount) return null;
26961
+ return {
26962
+ startRowIndex: 0,
26963
+ endRowIndex: rowCount - 1,
26964
+ startColumnIndex: columnIndices[0],
26965
+ endColumnIndex: columnIndices[columnIndices.length - 1],
26966
+ columnIndices
26967
+ };
26968
+ }
26969
+ if (state.selectedRowRanges != null) {
26970
+ if (!state.selectedRowRanges.length) return null;
26971
+ const rowIndices = collectSelectedIndices(
26972
+ state.selectedRowRanges,
26973
+ rowCount
26974
+ );
26975
+ if (rowIndices === null) {
26976
+ return {
26977
+ startRowIndex: state.selectedRowRanges[0].start,
26978
+ endRowIndex: state.selectedRowRanges[0].end,
26979
+ startColumnIndex: 0,
26980
+ endColumnIndex: columnCount - 1,
26981
+ tooLarge: true
26982
+ };
26983
+ }
26984
+ if (!rowIndices.length || !columnCount) return null;
26985
+ return {
26986
+ startRowIndex: rowIndices[0],
26987
+ endRowIndex: rowIndices[rowIndices.length - 1],
26988
+ startColumnIndex: 0,
26989
+ endColumnIndex: columnCount - 1,
26990
+ rowIndices
26991
+ };
26992
+ }
25944
26993
  const columnIndex = host.leafColumns.findIndex(
25945
26994
  (column) => column.key === host.selection.columnKey
25946
26995
  );
@@ -25956,7 +27005,7 @@ function resolveImageSelection(host) {
25956
27005
  startRowIndex: state.selectedRowRange.start,
25957
27006
  endRowIndex: state.selectedRowRange.end,
25958
27007
  startColumnIndex: 0,
25959
- endColumnIndex: host.leafColumns.length - 1
27008
+ endColumnIndex: columnCount - 1
25960
27009
  };
25961
27010
  if (host.selection.rowIndex != null && columnIndex >= 0)
25962
27011
  return {
@@ -25988,23 +27037,50 @@ function renderTableCopyImage(host, scope) {
25988
27037
  const range = scope === "selection" ? resolveImageSelection(host) : null;
25989
27038
  if (scope === "selection" && !range)
25990
27039
  throw new Error("\u8BF7\u5148\u9009\u4E2D\u9700\u8981\u590D\u5236\u7684\u5355\u5143\u683C\u533A\u57DF\u3002");
27040
+ if (range?.tooLarge)
27041
+ throw new Error("\u56FE\u7247\u533A\u57DF\u8FC7\u5927\uFF0C\u8BF7\u7F29\u5C0F\u9009\u533A\u6216\u9009\u62E9\u201C\u89C6\u53E3\u533A\u57DF\u201D\u3002");
25991
27042
  const startRow = range ? Math.max(Math.min(range.startRowIndex, range.endRowIndex), 0) : 0;
25992
27043
  const endRow = range ? Math.max(range.startRowIndex, range.endRowIndex) : current.baseRows.length - 1;
25993
27044
  const startColumn = range ? Math.max(Math.min(range.startColumnIndex, range.endColumnIndex), 0) : 0;
25994
27045
  const endColumn = range ? Math.max(range.startColumnIndex, range.endColumnIndex) : current.leafColumns.length - 1;
25995
- const columns = current.leafColumns.slice(startColumn, endColumn + 1);
27046
+ if (range && ((range.rowIndices?.length ?? endRow - startRow + 1) > MAX_IMAGE_SIDE || (range.columnIndices?.length ?? endColumn - startColumn + 1) > MAX_IMAGE_SIDE)) {
27047
+ throw new Error("\u56FE\u7247\u533A\u57DF\u8FC7\u5927\uFF0C\u8BF7\u7F29\u5C0F\u9009\u533A\u6216\u9009\u62E9\u201C\u89C6\u53E3\u533A\u57DF\u201D\u3002");
27048
+ }
27049
+ const selectedRowIndices = range?.rowIndices ?? (range ? Array.from(
27050
+ { length: endRow - startRow + 1 },
27051
+ (_, index) => startRow + index
27052
+ ) : null);
27053
+ const selectedColumnIndices = range?.columnIndices ?? (range ? Array.from(
27054
+ { length: endColumn - startColumn + 1 },
27055
+ (_, index) => startColumn + index
27056
+ ) : null);
27057
+ const columns = selectedColumnIndices ? selectedColumnIndices.map((index) => current.leafColumns[index]) : current.leafColumns;
25996
27058
  const width = columns.reduce((total, column) => total + column.width, 0);
25997
- const rowCount = range ? endRow - startRow + 1 : current.baseRows.length;
27059
+ const rowCount = selectedRowIndices?.length ?? current.baseRows.length;
25998
27060
  const dpr = source.ownerDocument.defaultView?.devicePixelRatio || 1;
25999
27061
  const zoom = host.tableZoom || 1;
26000
27062
  assertTableImageSize(
26001
27063
  Math.ceil(width * zoom * dpr),
26002
27064
  Math.ceil(Math.max(rowCount, 1) * current.rowHeight * zoom * dpr)
26003
27065
  );
26004
- const rows = range ? current.displayRows.slice(startRow, endRow + 1) : current.baseRows;
27066
+ const rows = selectedRowIndices ? selectedRowIndices.map((index) => current.displayRows[index]) : current.baseRows;
26005
27067
  const keys = new Set(columns.map((column) => column.key));
26006
27068
  if (!range) state?.groupColumnKeys.forEach((key) => keys.add(key));
26007
27069
  const widths = new Map(columns.map((column) => [column.key, column.width]));
27070
+ const originalColumnIndices = new Map(
27071
+ current.leafColumns.map((column, index) => [column.key, index])
27072
+ );
27073
+ const isCellSelected = (rowIndex, columnKey) => {
27074
+ if (!range?.cellRanges) return true;
27075
+ if (range.rowRanges?.some(
27076
+ (rowRange) => rowIndex >= rowRange.start && rowIndex <= rowRange.end
27077
+ ))
27078
+ return true;
27079
+ const columnIndex = originalColumnIndices.get(columnKey);
27080
+ return columnIndex != null && range.cellRanges.some(
27081
+ (cellRange) => rowIndex >= cellRange.startRowIndex && rowIndex <= cellRange.endRowIndex && columnIndex >= cellRange.startColumnIndex && columnIndex <= cellRange.endColumnIndex
27082
+ );
27083
+ };
26008
27084
  const cloneColumns2 = (items) => items.flatMap((column) => {
26009
27085
  const children = column.children ? cloneColumns2(column.children) : void 0;
26010
27086
  if (children ? !children.length : !keys.has(String(column.key)))
@@ -26017,8 +27093,21 @@ function renderTableCopyImage(host, scope) {
26017
27093
  children,
26018
27094
  fixed: void 0,
26019
27095
  width: widths.get(String(column.key)) ?? column.width,
26020
- onCell: onCell && range ? (value, record, index, context) => onCell(value, record, index + startRow, context) : onCell,
26021
- render: render && range ? (value, record, index, context) => render(value, record, index + startRow, context) : render
27096
+ onCell: onCell && range ? (value, record, index, context) => onCell(
27097
+ value,
27098
+ record,
27099
+ selectedRowIndices?.[index] ?? index + startRow,
27100
+ context
27101
+ ) : onCell,
27102
+ render: range?.cellRanges && !children ? (value, record, index, context) => {
27103
+ const sourceRowIndex = selectedRowIndices?.[index] ?? index + startRow;
27104
+ return isCellSelected(sourceRowIndex, String(column.key)) ? render?.(value, record, sourceRowIndex, context) ?? null : "";
27105
+ } : render && range ? (value, record, index, context) => render(
27106
+ value,
27107
+ record,
27108
+ selectedRowIndices?.[index] ?? index + startRow,
27109
+ context
27110
+ ) : render
26022
27111
  }
26023
27112
  ];
26024
27113
  });
@@ -26107,7 +27196,7 @@ function renderTableCopyImage(host, scope) {
26107
27196
  fixedLeftBoundarySkips: [],
26108
27197
  getNestedTableHighlight: (params) => host.animationController.getNestedTableHighlight({
26109
27198
  ...params,
26110
- rowIndex: params.rowIndex + startRow
27199
+ rowIndex: selectedRowIndices?.[params.rowIndex] ?? params.rowIndex + startRow
26111
27200
  })
26112
27201
  });
26113
27202
  return canvas;
@@ -26557,6 +27646,7 @@ function buildHeaderDataIndexOptions(rows, columns) {
26557
27646
 
26558
27647
  // src/features/overlay/context-menu-builder.ts
26559
27648
  function createListTableContextMenuActions(params) {
27649
+ const getExportColumns = () => params.getExportColumns?.() ?? params.getColumns();
26560
27650
  return {
26561
27651
  onCopyHeaderCell: (columnKey) => {
26562
27652
  params.hideMenu();
@@ -26612,50 +27702,82 @@ function createListTableContextMenuActions(params) {
26612
27702
  onExportJson: () => {
26613
27703
  params.hideMenu();
26614
27704
  params.startExportTask(
26615
- (fileName) => exportAllRowsJsonContent({
27705
+ (fileName, settings) => exportAllRowsJsonContent({
26616
27706
  fileName,
26617
27707
  rows: params.getAllRows(),
26618
- columns: params.getColumns()
27708
+ columns: getExportColumns(),
27709
+ settings
26619
27710
  }),
26620
27711
  "\u6B63\u5728\u5BFC\u51FA JSON\u2026",
26621
- buildTableExportFileName("json")
27712
+ buildTableExportFileName("json"),
27713
+ {
27714
+ columns: getListTableExportColumnChoices(
27715
+ getExportColumns(),
27716
+ params.getExportSortState?.()
27717
+ ),
27718
+ includeSummary: params.getExportSummaryEnabled?.() ?? false
27719
+ }
26622
27720
  );
26623
27721
  },
26624
27722
  onExportCsv: () => {
26625
27723
  params.hideMenu();
26626
27724
  params.startExportTask(
26627
- (fileName) => exportAllRowsCsvContent({
27725
+ (fileName, settings) => exportAllRowsCsvContent({
26628
27726
  fileName,
26629
27727
  rows: params.getAllRows(),
26630
- columns: params.getColumns()
27728
+ columns: getExportColumns(),
27729
+ settings
26631
27730
  }),
26632
27731
  "\u6B63\u5728\u5BFC\u51FA CSV\u2026",
26633
- buildTableExportFileName("csv")
27732
+ buildTableExportFileName("csv"),
27733
+ {
27734
+ columns: getListTableExportColumnChoices(
27735
+ getExportColumns(),
27736
+ params.getExportSortState?.()
27737
+ ),
27738
+ includeSummary: params.getExportSummaryEnabled?.() ?? false
27739
+ }
26634
27740
  );
26635
27741
  },
26636
27742
  onExportTxt: () => {
26637
27743
  params.hideMenu();
26638
27744
  params.startExportTask(
26639
- (fileName) => exportAllRowsTxtContent({
27745
+ (fileName, settings) => exportAllRowsTxtContent({
26640
27746
  fileName,
26641
27747
  rows: params.getAllRows(),
26642
- columns: params.getColumns()
27748
+ columns: getExportColumns(),
27749
+ settings
26643
27750
  }),
26644
27751
  "\u6B63\u5728\u5BFC\u51FA TXT\u2026",
26645
- buildTableExportFileName("txt")
27752
+ buildTableExportFileName("txt"),
27753
+ {
27754
+ columns: getListTableExportColumnChoices(
27755
+ getExportColumns(),
27756
+ params.getExportSortState?.()
27757
+ ),
27758
+ includeSummary: params.getExportSummaryEnabled?.() ?? false
27759
+ }
26646
27760
  );
26647
27761
  },
26648
27762
  onExportXlsx: () => {
26649
27763
  params.hideMenu();
26650
27764
  params.startExportTask(
26651
- (fileName) => exportAllRowsXlsxContent({
27765
+ (fileName, settings) => exportAllRowsXlsxContent({
26652
27766
  includeSummary: params.getExportSummaryEnabled?.() ?? false,
26653
27767
  fileName,
26654
27768
  rows: params.getAllRows(),
26655
- columns: params.getColumns()
27769
+ columns: getExportColumns(),
27770
+ settings
26656
27771
  }),
26657
27772
  "\u6B63\u5728\u5BFC\u51FA XLSX\u2026",
26658
- buildTableExportFileName("xlsx")
27773
+ buildTableExportFileName("xlsx"),
27774
+ {
27775
+ columns: getListTableExportColumnChoices(
27776
+ getExportColumns(),
27777
+ params.getExportSortState?.()
27778
+ ),
27779
+ includeSummary: params.getExportSummaryEnabled?.() ?? false
27780
+ }
26659
27781
  );
26660
27782
  },
26661
27783
  onFreezeRow: params.onFreezeRow,
@@ -26810,6 +27932,7 @@ function buildListTableContextMenuConfig(params) {
26810
27932
  const enabledFeatures = resolveEnabledContextMenuFeatures(contextMenu);
26811
27933
  const isTableSettingsArea = isHeaderArea || isExpandedArea;
26812
27934
  const isBodyArea = bodyRowIndex != null;
27935
+ const canResetTable = isTableSettingsArea || isBodyArea;
26813
27936
  const isSelectionColumn = columnKey === ROW_SELECTION_COLUMN_KEY || columnKey === ROW_SELECTOR_COLUMN_KEY;
26814
27937
  const enableCopy = enabledFeatures.has("copy");
26815
27938
  const enableChart = enabledFeatures.has("chart");
@@ -26928,19 +28051,19 @@ function buildListTableContextMenuConfig(params) {
26928
28051
  onUnfreezeRow: enableFreeze && isBodyArea && frozenRowPosition != null ? () => {
26929
28052
  actions.onUnfreezeRow(bodyRowIndex);
26930
28053
  } : void 0,
26931
- onResetFilter: enableReset && isTableSettingsArea ? actions.onResetFilter : void 0,
26932
- onResetSort: enableReset && isTableSettingsArea ? actions.onResetSort : void 0,
28054
+ onResetFilter: enableReset && canResetTable ? actions.onResetFilter : void 0,
28055
+ onResetSort: enableReset && canResetTable ? actions.onResetSort : void 0,
26933
28056
  onSortAscend: canSortCurrentColumn && actions.onSortAscend ? () => actions.onSortAscend?.(columnKey) : void 0,
26934
28057
  onSortDescend: canSortCurrentColumn && actions.onSortDescend ? () => actions.onSortDescend?.(columnKey) : void 0,
26935
28058
  onClearSort: canSortCurrentColumn && actions.onClearSort ? () => actions.onClearSort?.(columnKey) : void 0,
26936
28059
  onFixLeft: enableFreeze && isHeaderArea && !isSelectionColumn && actions.onFixLeft ? () => actions.onFixLeft?.(columnKey) : void 0,
26937
28060
  onFixRight: enableFreeze && isHeaderArea && !isSelectionColumn && actions.onFixRight ? () => actions.onFixRight?.(columnKey) : void 0,
26938
28061
  onUnfix: enableFreeze && isHeaderArea && !isSelectionColumn && actions.onUnfix ? () => actions.onUnfix?.(columnKey) : void 0,
26939
- onResetHeader: enableReset && isTableSettingsArea ? actions.onResetHeader : void 0,
26940
- onResetGrouping: enableReset && isTableSettingsArea ? actions.onResetGrouping : void 0,
26941
- onResetSummary: enableReset && isTableSettingsArea ? actions.onResetSummary : void 0,
26942
- onResetAlignment: enableReset && isTableSettingsArea ? actions.onResetAlignment : void 0,
26943
- onResetDataFormat: enableReset && isTableSettingsArea ? actions.onResetDataFormat : void 0,
28062
+ onResetHeader: enableReset && canResetTable ? actions.onResetHeader : void 0,
28063
+ onResetGrouping: enableReset && canResetTable ? actions.onResetGrouping : void 0,
28064
+ onResetSummary: enableReset && canResetTable ? actions.onResetSummary : void 0,
28065
+ onResetAlignment: enableReset && canResetTable ? actions.onResetAlignment : void 0,
28066
+ onResetDataFormat: enableReset && canResetTable ? actions.onResetDataFormat : void 0,
26944
28067
  onAutoFitCurrentColumn: enableColumnWidth && isHeaderArea && !isSelectionColumn && actions.onAutoFitCurrentColumn ? () => actions.onAutoFitCurrentColumn?.(columnKey) : void 0,
26945
28068
  onAutoFitAllColumns: enableColumnWidth && isTableSettingsArea && !isSelectionColumn && actions.onAutoFitAllColumns ? actions.onAutoFitAllColumns : void 0,
26946
28069
  onAutoFitFillContainer: enableColumnWidth && isTableSettingsArea && !isSelectionColumn && actions.onAutoFitFillContainer ? actions.onAutoFitFillContainer : void 0,
@@ -27181,6 +28304,8 @@ function showHeaderContextMenu(params) {
27181
28304
  onChangeSummaryShowTypePrefix,
27182
28305
  onChangeColumnSettingsCache,
27183
28306
  onChangeCachePreference,
28307
+ onChangeScrollbar,
28308
+ onResetScrollbar,
27184
28309
  summaryVisible,
27185
28310
  getSelectionRange,
27186
28311
  onCopySelection,
@@ -27194,6 +28319,40 @@ function showHeaderContextMenu(params) {
27194
28319
  const isExpandedArea = result.area === "expanded-row" || result.columnKey === "__expanded_row__";
27195
28320
  const isSelectionColumn = result.columnKey === ROW_SELECTION_COLUMN_KEY || result.columnKey === ROW_SELECTOR_COLUMN_KEY;
27196
28321
  const isChartCorner = isHeaderArea && result.columnKey === ROW_SELECTOR_COLUMN_KEY && resolveEnabledContextMenuFeatures(options.contextMenu).has("chart");
28322
+ if (result.area === "horizontal-scrollbar" || result.area === "vertical-scrollbar") {
28323
+ if (options.contextMenu === false) {
28324
+ headerContextMenu.hide();
28325
+ return false;
28326
+ }
28327
+ event.preventDefault();
28328
+ filterPanel.hide();
28329
+ fixedPopup.hide();
28330
+ hideBodyTooltip();
28331
+ headerContextMenu.show({
28332
+ x: event.clientX,
28333
+ y: event.clientY,
28334
+ scrollbarArea: true,
28335
+ scrollbarRowCount: getRows().length,
28336
+ scrollbarColumnCount: params.scrollbarColumnCount ?? getColumns().length,
28337
+ onScrollToPosition: params.onScrollToPosition,
28338
+ onResetFilter,
28339
+ onResetSort,
28340
+ onResetHeader,
28341
+ onResetGrouping,
28342
+ onResetSummary,
28343
+ onResetAlignment,
28344
+ onResetDataFormat,
28345
+ scrollbar: options.scrollbar,
28346
+ onChangeScrollbar,
28347
+ onResetScrollbar,
28348
+ columnSettingsCacheAvailable: Boolean(options.storageKey),
28349
+ cacheColumnSettings: options.cacheColumnSettings,
28350
+ cachePreferences: options.cachePreferences,
28351
+ onChangeCachePreference,
28352
+ onClose
28353
+ });
28354
+ return true;
28355
+ }
27197
28356
  if (!isHeaderArea && !isBodyArea && !groupingBarArea && !isExpandedArea || !result.columnKey && !groupingBarArea && !isExpandedArea || isHeaderArea && isSelectionColumn && !isChartCorner) {
27198
28357
  headerContextMenu.hide();
27199
28358
  return false;
@@ -27215,8 +28374,8 @@ function showHeaderContextMenu(params) {
27215
28374
  onSelectBodyRow?.(bodyRowIndex, contextColumnKey);
27216
28375
  }
27217
28376
  const frozenRowPosition = bodyRowIndex == null ? null : getFrozenRowPosition2(bodyRowIndex);
27218
- headerContextMenu.show(
27219
- buildListTableContextMenuConfig({
28377
+ headerContextMenu.show({
28378
+ ...buildListTableContextMenuConfig({
27220
28379
  storageKey: options.storageKey,
27221
28380
  cacheColumnSettings: options.cacheColumnSettings,
27222
28381
  cachePreferences: options.cachePreferences,
@@ -27248,9 +28407,11 @@ function showHeaderContextMenu(params) {
27248
28407
  columnCount: getColumns().length,
27249
28408
  actions: createListTableContextMenuActions({
27250
28409
  getExportSummaryEnabled: params.getExportSummaryEnabled,
28410
+ getExportSortState: params.getExportSortState,
27251
28411
  getRows,
27252
28412
  getAllRows,
27253
28413
  getColumns,
28414
+ getExportColumns: params.getExportColumns,
27254
28415
  getHeaderTree,
27255
28416
  hideMenu: () => {
27256
28417
  headerContextMenu.hide();
@@ -27302,8 +28463,15 @@ function showHeaderContextMenu(params) {
27302
28463
  onOpenSearch,
27303
28464
  onCopyCustomSelection
27304
28465
  })
27305
- })
27306
- );
28466
+ }),
28467
+ sortState: params.getExportSortState?.() ?? [],
28468
+ scrollbarRowCount: getRows().length,
28469
+ scrollbarColumnCount: params.scrollbarColumnCount ?? getColumns().length,
28470
+ onScrollToPosition: params.onScrollToPosition,
28471
+ scrollbar: options.scrollbar,
28472
+ onChangeScrollbar,
28473
+ onResetScrollbar
28474
+ });
27307
28475
  return true;
27308
28476
  }
27309
28477
  function resolveContextMenuDensity(rowHeight) {
@@ -27331,7 +28499,9 @@ function createListTableChartData(params) {
27331
28499
  rowIndex,
27332
28500
  selectionRange,
27333
28501
  selectedRowRange,
27334
- selectedColumnRange
28502
+ selectedColumnRange,
28503
+ selectedColumnKeys,
28504
+ getRowKey: getRowKey2
27335
28505
  } = params;
27336
28506
  const startRow = Math.max(
27337
28507
  selectionRange?.startRowIndex ?? selectedRowRange?.start ?? Math.max((rowIndex ?? 0) - 10, 0),
@@ -27344,7 +28514,7 @@ function createListTableChartData(params) {
27344
28514
  const selectedColumns = selectionRange ? columns.slice(
27345
28515
  Math.max(selectionRange.startColumnIndex, 0),
27346
28516
  selectionRange.endColumnIndex + 1
27347
- ) : selectedRowRange ? columns : selectedColumnRange ? columns.slice(
28517
+ ) : selectedRowRange ? columns : selectedColumnKeys != null ? columns.filter((column) => selectedColumnKeys.includes(column.key)) : selectedColumnRange ? columns.slice(
27348
28518
  Math.max(selectedColumnRange.start, 0),
27349
28519
  selectedColumnRange.end + 1
27350
28520
  ) : columns.filter((column) => column.key === columnKey);
@@ -27383,7 +28553,8 @@ function createListTableChartData(params) {
27383
28553
  const labels = sampledRows.map(
27384
28554
  (_, index) => labelIndex >= 0 ? values[labelIndex]?.[index] || String(startRow + index + 1) : String(startRow + index + 1)
27385
28555
  );
27386
- return { labels, series: numericColumns };
28556
+ const keys = getRowKey2 ? sampledRows.map((record, index) => getRowKey2(record, startRow + index)) : void 0;
28557
+ return { labels, ...keys ? { keys } : {}, series: numericColumns };
27387
28558
  }
27388
28559
  function parseChartNumber(value) {
27389
28560
  const normalized = value.trim().replace(/[,,\s¥¥$]/g, "");
@@ -27396,6 +28567,9 @@ function parseChartNumber(value) {
27396
28567
  // src/features/tooltip/tooltip-feature.ts
27397
28568
  function getHeaderActionTooltipText(result, sortState) {
27398
28569
  const area = result.area;
28570
+ if (result.debugNodeType === "header-edit-icon" || result.debugNodeType === "nested-table-header-edit-icon") {
28571
+ return "\u5F53\u524D\u5217\u53EF\u7F16\u8F91";
28572
+ }
27399
28573
  if (area !== "sort-trigger" && area !== "filter-trigger" && area !== "fixed-trigger") {
27400
28574
  return null;
27401
28575
  }
@@ -28440,6 +29614,8 @@ var ListTableEventController = class {
28440
29614
  __publicField(this, "cellSelectionAutoScrollVelocity", { x: 0, y: 0 });
28441
29615
  __publicField(this, "cellSelectionAutoScrollTargetVelocity", { x: 0, y: 0 });
28442
29616
  __publicField(this, "continuousScrollTimer", null);
29617
+ __publicField(this, "scrollbarButtonFeedbackTimer", null);
29618
+ __publicField(this, "scrollbarPartFeedbackTimer", null);
28443
29619
  __publicField(this, "pendingEditableCellRowSelectionTimer", null);
28444
29620
  __publicField(this, "outsideClickDocument", null);
28445
29621
  __publicField(this, "outsideClickStarted", null);
@@ -28450,7 +29626,13 @@ var ListTableEventController = class {
28450
29626
  __publicField(this, "handleDocumentClick", (event) => {
28451
29627
  const startedOutside = this.outsideClickStarted ?? !this.isEventTargetInsideTable(event.target);
28452
29628
  this.outsideClickStarted = null;
28453
- if (!startedOutside || !this.host.selection.textSelection) return;
29629
+ if (!startedOutside) return;
29630
+ if (this.host.options.clearSelectionOnClick?.outside) {
29631
+ this.clearTableHighlight();
29632
+ this.host.render();
29633
+ return;
29634
+ }
29635
+ if (!this.host.selection.textSelection) return;
28454
29636
  this.host.clearBodySelection();
28455
29637
  this.host.render();
28456
29638
  });
@@ -28614,6 +29796,9 @@ var ListTableEventController = class {
28614
29796
  result.area === "resize-handle" ? "col-resize" : result.cursor ?? ""
28615
29797
  );
28616
29798
  const nextHover = {
29799
+ scrollbarRole: result.scrollbarRole,
29800
+ pressedScrollbarButton: this.host.hover.pressedScrollbarButton,
29801
+ pressedScrollbarPart: this.host.hover.pressedScrollbarPart,
28617
29802
  rowIndex: result.rowIndex ?? null,
28618
29803
  columnKey: result.columnKey ?? null,
28619
29804
  contentIndex: result.contentIndex,
@@ -28623,7 +29808,7 @@ var ListTableEventController = class {
28623
29808
  nestedColumnIndex: result.nestedColumnIndex,
28624
29809
  area: result.area
28625
29810
  };
28626
- const hoverChanged = this.host.hover.rowIndex !== nextHover.rowIndex || this.host.hover.columnKey !== nextHover.columnKey || this.host.hover.contentIndex !== nextHover.contentIndex || this.host.hover.nestedRowKey !== nextHover.nestedRowKey || this.host.hover.nestedRowIndex !== nextHover.nestedRowIndex || this.host.hover.nestedColumnDataIndex !== nextHover.nestedColumnDataIndex || this.host.hover.nestedColumnIndex !== nextHover.nestedColumnIndex || this.host.hover.area !== nextHover.area;
29811
+ const hoverChanged = this.host.hover.rowIndex !== nextHover.rowIndex || this.host.hover.columnKey !== nextHover.columnKey || this.host.hover.contentIndex !== nextHover.contentIndex || this.host.hover.nestedRowKey !== nextHover.nestedRowKey || this.host.hover.nestedRowIndex !== nextHover.nestedRowIndex || this.host.hover.nestedColumnDataIndex !== nextHover.nestedColumnDataIndex || this.host.hover.nestedColumnIndex !== nextHover.nestedColumnIndex || this.host.hover.area !== nextHover.area || this.host.hover.scrollbarRole !== nextHover.scrollbarRole || this.host.hover.pressedScrollbarButton !== nextHover.pressedScrollbarButton || this.host.hover.pressedScrollbarPart !== nextHover.pressedScrollbarPart;
28627
29812
  const hitChanged = didHitResultChange(
28628
29813
  this.host.debugManager.getSnapshot()?.hitTest,
28629
29814
  result
@@ -28666,7 +29851,8 @@ var ListTableEventController = class {
28666
29851
  zoom: this.host.tableZoom
28667
29852
  });
28668
29853
  const result = this.host.resolvePointerHit(position.x, position.y);
28669
- if (this.startTextSelection(result, position.x, position.y)) {
29854
+ const modifyingSelection = event.shiftKey || event.ctrlKey || event.metaKey;
29855
+ if (!modifyingSelection && this.startTextSelection(result, position.x, position.y)) {
28670
29856
  return;
28671
29857
  }
28672
29858
  if (this.host.selection.textSelection) {
@@ -28698,6 +29884,28 @@ var ListTableEventController = class {
28698
29884
  if ((result.area === "body" || result.area === "body-content") && result.columnKey === ROW_SELECTOR_COLUMN_KEY && typeof result.rowIndex === "number") {
28699
29885
  event.preventDefault();
28700
29886
  this.clearNestedTableHighlight();
29887
+ if (modifyingSelection) {
29888
+ const mode = event.ctrlKey || event.metaKey ? isRowInHighlight(this.host.rangeHighlight, result.rowIndex) ? "remove" : "add" : "extend";
29889
+ this.cellSelectionDrag = {
29890
+ kind: "row",
29891
+ startRowIndex: mode === "extend" ? this.host.rangeHighlight.selectedRowAnchorIndex ?? result.rowIndex : result.rowIndex,
29892
+ startColumnIndex: -1,
29893
+ pointerStartRowIndex: result.rowIndex,
29894
+ pointerStartColumnIndex: -1,
29895
+ mode,
29896
+ baseRangeHighlight: this.host.rangeHighlight
29897
+ };
29898
+ this.cellSelectionDragged = false;
29899
+ this.cellSelectionPointer = {
29900
+ clientX: event.clientX,
29901
+ clientY: event.clientY
29902
+ };
29903
+ this.host.canvasHost.bindWindowDragEvents(
29904
+ this.handleWindowMouseMove,
29905
+ this.handleWindowMouseUp
29906
+ );
29907
+ return;
29908
+ }
28701
29909
  this.host.selection = {
28702
29910
  rowIndex: result.rowIndex,
28703
29911
  columnKey: ROW_SELECTOR_COLUMN_KEY
@@ -28708,7 +29916,11 @@ var ListTableEventController = class {
28708
29916
  this.cellSelectionDrag = {
28709
29917
  kind: "row",
28710
29918
  startRowIndex: result.rowIndex,
28711
- startColumnIndex: -1
29919
+ startColumnIndex: -1,
29920
+ pointerStartRowIndex: result.rowIndex,
29921
+ pointerStartColumnIndex: -1,
29922
+ mode: "replace",
29923
+ baseRangeHighlight: this.host.rangeHighlight
28712
29924
  };
28713
29925
  this.cellSelectionDragged = false;
28714
29926
  this.cellSelectionPointer = {
@@ -28722,20 +29934,41 @@ var ListTableEventController = class {
28722
29934
  this.host.render(result);
28723
29935
  return;
28724
29936
  }
28725
- if (!this.host.options.rowDrag?.enabled && (result.area === "body" || result.area === "body-content") && typeof result.rowIndex === "number" && result.columnKey) {
29937
+ if ((!this.host.options.rowDrag?.enabled || modifyingSelection) && (result.area === "body" || result.area === "body-content") && typeof result.rowIndex === "number" && result.columnKey) {
28726
29938
  const columnIndex = this.host.leafColumns.findIndex(
28727
29939
  (column) => column.key === result.columnKey
28728
29940
  );
28729
29941
  if (columnIndex >= 0) {
28730
29942
  const nestedSelectionCleared = this.clearNestedTableHighlight();
28731
- this.host.rangeHighlight = {
28732
- ...this.host.rangeHighlight,
28733
- selectedCellRange: null
28734
- };
29943
+ if (!modifyingSelection) {
29944
+ this.host.rangeHighlight = {
29945
+ ...this.host.rangeHighlight,
29946
+ selectedCellRange: null,
29947
+ selectedCellRanges: null
29948
+ };
29949
+ }
29950
+ const ctrlSelection = event.ctrlKey || event.metaKey;
29951
+ const mode = ctrlSelection ? isCellInHighlight(
29952
+ this.host.rangeHighlight,
29953
+ result.rowIndex,
29954
+ columnIndex
29955
+ ) ? "remove" : "add" : event.shiftKey ? "extend" : "replace";
29956
+ const cellAnchor = this.host.rangeHighlight.selectedCellAnchor;
29957
+ const anchorColumnIndex = cellAnchor ? this.host.leafColumns.findIndex(
29958
+ (column) => column.key === cellAnchor.columnKey
29959
+ ) : -1;
29960
+ const previousColumnIndex = this.host.leafColumns.findIndex(
29961
+ (column) => column.key === this.host.selection.columnKey
29962
+ );
28735
29963
  this.cellSelectionDrag = {
28736
29964
  kind: "cell",
28737
- startRowIndex: result.rowIndex,
28738
- startColumnIndex: columnIndex
29965
+ startRowIndex: mode === "extend" ? cellAnchor?.rowIndex ?? this.host.selection.rowIndex ?? result.rowIndex : result.rowIndex,
29966
+ startColumnIndex: mode === "extend" ? anchorColumnIndex >= 0 ? anchorColumnIndex : previousColumnIndex >= 0 ? previousColumnIndex : columnIndex : columnIndex,
29967
+ pointerStartRowIndex: result.rowIndex,
29968
+ pointerStartColumnIndex: columnIndex,
29969
+ mode,
29970
+ baseRangeHighlight: this.host.rangeHighlight,
29971
+ preserveRowSelection: this.host.selection.columnKey === ROW_SELECTOR_COLUMN_KEY || this.host.rangeHighlight.selectedRowRanges != null
28739
29972
  };
28740
29973
  this.cellSelectionDragged = false;
28741
29974
  this.cellSelectionPointer = {
@@ -28749,6 +29982,7 @@ var ListTableEventController = class {
28749
29982
  if (nestedSelectionCleared) {
28750
29983
  this.host.render(result);
28751
29984
  }
29985
+ if (modifyingSelection) return;
28752
29986
  }
28753
29987
  }
28754
29988
  if (result.area === "resize-handle" && result.columnKey) {
@@ -28839,11 +30073,39 @@ var ListTableEventController = class {
28839
30073
  result.scrollbarRole === "thumb" ? "grabbing" : "pointer"
28840
30074
  );
28841
30075
  if (result.scrollbarRole === "decrease-button") {
30076
+ if (this.scrollbarButtonFeedbackTimer != null) {
30077
+ window.clearTimeout(this.scrollbarButtonFeedbackTimer);
30078
+ this.scrollbarButtonFeedbackTimer = null;
30079
+ }
30080
+ this.host.hover = {
30081
+ ...this.host.hover,
30082
+ area: result.area,
30083
+ scrollbarRole: result.scrollbarRole,
30084
+ pressedScrollbarButton: {
30085
+ axis: result.area === "horizontal-scrollbar" ? "horizontal" : "vertical",
30086
+ role: result.scrollbarRole
30087
+ }
30088
+ };
30089
+ this.host.scheduleRender();
28842
30090
  this.stepScrollbar(result.area, -1, viewport);
28843
30091
  this.startContinuousScroll(result.area, -1, viewport);
28844
30092
  return;
28845
30093
  }
28846
30094
  if (result.scrollbarRole === "increase-button") {
30095
+ if (this.scrollbarButtonFeedbackTimer != null) {
30096
+ window.clearTimeout(this.scrollbarButtonFeedbackTimer);
30097
+ this.scrollbarButtonFeedbackTimer = null;
30098
+ }
30099
+ this.host.hover = {
30100
+ ...this.host.hover,
30101
+ area: result.area,
30102
+ scrollbarRole: result.scrollbarRole,
30103
+ pressedScrollbarButton: {
30104
+ axis: result.area === "horizontal-scrollbar" ? "horizontal" : "vertical",
30105
+ role: result.scrollbarRole
30106
+ }
30107
+ };
30108
+ this.host.scheduleRender();
28847
30109
  this.stepScrollbar(result.area, 1, viewport);
28848
30110
  this.startContinuousScroll(result.area, 1, viewport);
28849
30111
  return;
@@ -28855,9 +30117,15 @@ var ListTableEventController = class {
28855
30117
  }
28856
30118
  const { thumb, track } = horizontalScrollbar;
28857
30119
  if (result.scrollbarRole === "track") {
30120
+ this.flashScrollbarPart("horizontal", "track");
28858
30121
  this.jumpScrollbar("horizontal", position.x, track, thumb, viewport);
28859
30122
  return;
28860
30123
  }
30124
+ this.host.hover = {
30125
+ ...this.host.hover,
30126
+ pressedScrollbarPart: { axis: "horizontal", role: "thumb" }
30127
+ };
30128
+ this.host.scheduleRender();
28861
30129
  this.host.draggingScrollbar = {
28862
30130
  axis: "horizontal",
28863
30131
  pointerOffset: position.x - thumb.x,
@@ -28873,9 +30141,15 @@ var ListTableEventController = class {
28873
30141
  }
28874
30142
  const { thumb, track } = verticalScrollbar;
28875
30143
  if (result.scrollbarRole === "track") {
30144
+ this.flashScrollbarPart("vertical", "track");
28876
30145
  this.jumpScrollbar("vertical", position.y, track, thumb, viewport);
28877
30146
  return;
28878
30147
  }
30148
+ this.host.hover = {
30149
+ ...this.host.hover,
30150
+ pressedScrollbarPart: { axis: "vertical", role: "thumb" }
30151
+ };
30152
+ this.host.scheduleRender();
28879
30153
  this.host.draggingScrollbar = {
28880
30154
  axis: "vertical",
28881
30155
  pointerOffset: position.y - thumb.y,
@@ -28988,15 +30262,47 @@ var ListTableEventController = class {
28988
30262
  getRows: () => this.host.getDisplayData(),
28989
30263
  getAllRows: () => this.host.options.dataSource,
28990
30264
  getExportSummaryEnabled: () => Boolean(this.host.currentRenderSnapshot?.hasSummary),
30265
+ getExportSortState: () => this.host.getSortState(),
30266
+ getExportColumns: () => getListTableExportCandidateColumns(this.host.options.columns),
28991
30267
  onCopyImage: (scope) => this.copyImage(scope, event),
30268
+ scrollbarColumnCount: this.host.leafColumns.filter(
30269
+ (column) => !isSystemColumnKey(column.key)
30270
+ ).length,
30271
+ onScrollToPosition: (row, column) => {
30272
+ const columns = this.host.leafColumns.filter(
30273
+ (item) => !isSystemColumnKey(item.key)
30274
+ );
30275
+ const targetColumn = column == null ? null : columns[column - 1];
30276
+ const colIndex = targetColumn ? this.host.leafColumns.findIndex(
30277
+ (item) => item.key === targetColumn.key
30278
+ ) : void 0;
30279
+ this.host.scrollTo({
30280
+ rowIndex: row == null ? void 0 : row - 1,
30281
+ colIndex,
30282
+ behavior: "smooth"
30283
+ });
30284
+ },
30285
+ onChangeScrollbar: (value) => {
30286
+ this.host.options.scrollbar = value;
30287
+ this.host.options.ui?.onScrollbarChange?.(value);
30288
+ this.host.persistColumnConfig();
30289
+ this.host.render();
30290
+ },
30291
+ onResetScrollbar: () => {
30292
+ this.host.options.scrollbar = void 0;
30293
+ this.host.persistColumnConfig();
30294
+ this.host.render();
30295
+ },
28992
30296
  onCreateChart: () => createListTableChartData({
28993
30297
  rows: this.host.getDisplayData(),
28994
30298
  columns: this.host.leafColumns,
28995
30299
  columnKey: contextResult.columnKey ?? "",
28996
- rowIndex: typeof contextResult.rowIndex === "number" ? contextResult.rowIndex : null,
30300
+ rowIndex: this.host.selection.rowIndex ?? (typeof contextResult.rowIndex === "number" ? contextResult.rowIndex : null),
28997
30301
  selectionRange: this.host.rangeHighlight.selectedCellRange,
28998
30302
  selectedRowRange: this.host.rangeHighlight.selectedRowRange,
28999
- selectedColumnRange: this.host.rangeHighlight.selectedColumnRange
30303
+ selectedColumnRange: this.host.rangeHighlight.selectedColumnRange,
30304
+ selectedColumnKeys: this.host.rangeHighlight.selectedColumnKeys,
30305
+ getRowKey: this.host.options.rowKey != null ? (record, index) => getRowKey(this.host.options, record, index) : void 0
29000
30306
  }),
29001
30307
  onExportImage: (scope) => this.copyImage(scope, event, true),
29002
30308
  canCopySelectionImage: resolveImageSelection(this.host) != null,
@@ -29020,8 +30326,8 @@ var ListTableEventController = class {
29020
30326
  getColumns: () => this.host.leafColumns,
29021
30327
  getHeaderTree: () => this.host.headerTree,
29022
30328
  getFrozenRowPosition: (rowIndex) => this.host.getFrozenRowPosition(rowIndex),
29023
- startExportTask: (task, text, defaultFileName) => {
29024
- this.host.startExportTask(task, text, defaultFileName);
30329
+ startExportTask: (task, text, defaultFileName, dialog) => {
30330
+ this.host.startExportTask(task, text, defaultFileName, dialog);
29025
30331
  },
29026
30332
  contentDownload: this.host.resolveContentDownloadAction(hitContent),
29027
30333
  onSelectHeader: (columnKey, rect) => {
@@ -29030,18 +30336,22 @@ var ListTableEventController = class {
29030
30336
  this.host.selectHeader(columnKey, rect);
29031
30337
  },
29032
30338
  onSelectBodyRow: (rowIndex, columnKey) => {
29033
- const selectedCellRange = this.host.rangeHighlight.selectedCellRange;
29034
30339
  const columnIndex = this.host.leafColumns.findIndex(
29035
30340
  (column) => column.key === columnKey
29036
30341
  );
30342
+ const isSelectedCell = columnIndex >= 0 && isCellInHighlight(this.host.rangeHighlight, rowIndex, columnIndex);
29037
30343
  this.host.selection = {
29038
30344
  rowIndex,
29039
30345
  columnKey
29040
30346
  };
29041
30347
  this.host.clearSelectedHeader();
29042
- if (!selectedCellRange || rowIndex < selectedCellRange.startRowIndex || rowIndex > selectedCellRange.endRowIndex || columnIndex < selectedCellRange.startColumnIndex || columnIndex > selectedCellRange.endColumnIndex) {
30348
+ if (!isSelectedCell && !isRowInHighlight(this.host.rangeHighlight, rowIndex)) {
29043
30349
  this.host.rangeHighlight = resolveNextRowHighlightForContextMenu({
29044
- current: this.host.rangeHighlight,
30350
+ current: {
30351
+ ...this.host.rangeHighlight,
30352
+ selectedCellRange: null,
30353
+ selectedCellRanges: null
30354
+ },
29045
30355
  rowIndex
29046
30356
  });
29047
30357
  }
@@ -29337,6 +30647,14 @@ var ListTableEventController = class {
29337
30647
  const result = event.shiftKey && rawResult.area === "column-collapse-trigger" ? this.host.resolvePointerHit(position.x, position.y, {
29338
30648
  excludeKinds: ["column-collapse-trigger"]
29339
30649
  }) : rawResult;
30650
+ const toggleColumn = event.ctrlKey || event.metaKey;
30651
+ const preserveAnimatedSortSelection = result.area === "sort-trigger" && this.host.leafColumns.some(
30652
+ (column) => column.key === result.columnKey && typeof column.sorter === "object" && column.sorter.animation === true
30653
+ );
30654
+ const previousCell = {
30655
+ rowIndex: this.host.selection.rowIndex,
30656
+ columnKey: this.host.selection.columnKey
30657
+ };
29340
30658
  if (result.area === "grouping-remove" && result.columnKey) {
29341
30659
  this.host.removeGroupingColumn?.(result.columnKey);
29342
30660
  this.host.suppressNextClick = true;
@@ -29359,6 +30677,11 @@ var ListTableEventController = class {
29359
30677
  this.host.suppressNextClick = false;
29360
30678
  return;
29361
30679
  }
30680
+ if (result.area === "empty" && this.host.options.clearSelectionOnClick?.blankCanvas) {
30681
+ this.clearTableHighlight();
30682
+ this.host.render(result);
30683
+ return;
30684
+ }
29362
30685
  const isNestedTableHit = result.contentKey === "nested-table";
29363
30686
  if (isNestedTableHit) {
29364
30687
  this.clearParentTableHighlight();
@@ -29390,6 +30713,7 @@ var ListTableEventController = class {
29390
30713
  selectedRowRange: rowCount > 0 ? { start: 0, end: rowCount - 1 } : null,
29391
30714
  selectedColumnAnchorKey: this.host.leafColumns[0]?.key ?? null,
29392
30715
  selectedColumnRange: columnRangeEnd >= 0 ? { start: 0, end: columnRangeEnd } : null,
30716
+ selectedColumnKeys: null,
29393
30717
  selectedCellRange: rowCount > 0 && columnCount > 0 ? {
29394
30718
  startRowIndex: 0,
29395
30719
  endRowIndex: rowCount - 1,
@@ -29414,6 +30738,7 @@ var ListTableEventController = class {
29414
30738
  selectedRowRange: rowCount > 0 ? { start: 0, end: rowCount - 1 } : null,
29415
30739
  selectedColumnAnchorKey: this.host.leafColumns[0]?.key ?? null,
29416
30740
  selectedColumnRange: columnCount > 0 ? { start: 0, end: columnCount - 1 } : null,
30741
+ selectedColumnKeys: null,
29417
30742
  selectedCellRange: rowCount > 0 && columnCount > 0 ? {
29418
30743
  startRowIndex: 0,
29419
30744
  endRowIndex: rowCount - 1,
@@ -29434,7 +30759,48 @@ var ListTableEventController = class {
29434
30759
  if ((result.area === "body" || result.area === "body-content") && result.columnKey === ROW_SELECTOR_COLUMN_KEY && typeof result.rowIndex === "number") {
29435
30760
  this.host.clearSelectedHeader();
29436
30761
  this.host.summarySelected = false;
29437
- this.selectRowRange(result.rowIndex, result.rowIndex);
30762
+ this.host.rangeHighlight = resolveRowHighlightForSelectorClick({
30763
+ current: this.host.rangeHighlight,
30764
+ rowIndex: result.rowIndex,
30765
+ shiftKey: event.shiftKey,
30766
+ ctrlKey: toggleColumn
30767
+ });
30768
+ if (!isRowInHighlight(this.host.rangeHighlight, result.rowIndex)) {
30769
+ const hasSelectedRows = this.host.rangeHighlight.selectedRowRanges?.length || this.host.rangeHighlight.selectedRowRange;
30770
+ this.host.selection = {
30771
+ rowIndex: null,
30772
+ columnKey: hasSelectedRows ? ROW_SELECTOR_COLUMN_KEY : null
30773
+ };
30774
+ }
30775
+ this.host.render(result);
30776
+ this.host.updateTooltip(result);
30777
+ return;
30778
+ }
30779
+ if ((event.shiftKey || toggleColumn) && (result.area === "body" || result.area === "body-content") && typeof result.rowIndex === "number" && result.columnKey && !isSystemColumnKey(result.columnKey)) {
30780
+ const columnIndex = this.host.leafColumns.findIndex(
30781
+ (column) => column.key === result.columnKey
30782
+ );
30783
+ this.host.clearSelectedHeader();
30784
+ this.host.rangeHighlight = resolveCellHighlightForClick({
30785
+ current: this.host.rangeHighlight,
30786
+ rowIndex: result.rowIndex,
30787
+ columnKey: result.columnKey,
30788
+ leafColumnKeys: this.host.leafColumns.map((column) => column.key),
30789
+ previousCell: previousCell.rowIndex != null && previousCell.columnKey ? {
30790
+ rowIndex: previousCell.rowIndex,
30791
+ columnKey: previousCell.columnKey
30792
+ } : null,
30793
+ preserveRowSelection: previousCell.columnKey === ROW_SELECTOR_COLUMN_KEY || this.host.rangeHighlight.selectedRowRanges != null,
30794
+ shiftKey: event.shiftKey,
30795
+ ctrlKey: toggleColumn
30796
+ });
30797
+ if (columnIndex >= 0 && !isCellInHighlight(
30798
+ this.host.rangeHighlight,
30799
+ result.rowIndex,
30800
+ columnIndex
30801
+ )) {
30802
+ this.host.selection = { rowIndex: null, columnKey: null };
30803
+ }
29438
30804
  this.host.render(result);
29439
30805
  this.host.updateTooltip(result);
29440
30806
  return;
@@ -29446,13 +30812,20 @@ var ListTableEventController = class {
29446
30812
  }
29447
30813
  }
29448
30814
  this.host.summarySelected = result.area === "summary";
29449
- if ((result.area === "header" || result.area === "header-selection" || result.area === "sort-trigger" || result.area === "filter-trigger" || result.area === "fixed-trigger" || event.shiftKey && result.area === "column-collapse-trigger") && result.columnKey) {
30815
+ if ((result.area === "header" || result.area === "header-selection" || result.area === "sort-trigger" && !preserveAnimatedSortSelection || result.area === "filter-trigger" || result.area === "fixed-trigger" || event.shiftKey && result.area === "column-collapse-trigger") && result.columnKey) {
29450
30816
  this.host.clearBodySelection();
29451
- this.host.resetRangeHighlightForHeaderSelection(event.shiftKey);
29452
- this.host.selectHeader(
29453
- result.columnKey,
29454
- result.area === "header" ? result.rect : null
30817
+ this.host.resetRangeHighlightForHeaderSelection(
30818
+ event.shiftKey,
30819
+ toggleColumn
29455
30820
  );
30821
+ if (toggleColumn) {
30822
+ this.host.clearSelectedHeader();
30823
+ } else {
30824
+ this.host.selectHeader(
30825
+ result.columnKey,
30826
+ result.area === "header" ? result.rect : null
30827
+ );
30828
+ }
29456
30829
  }
29457
30830
  if (result.area === "summary") {
29458
30831
  if (!result.columnKey) {
@@ -29470,7 +30843,7 @@ var ListTableEventController = class {
29470
30843
  this.host.updateTooltip(result);
29471
30844
  return;
29472
30845
  }
29473
- const isHeaderRangeGesture = event.shiftKey && result.columnKey && (result.area === "header" || result.area === "sort-trigger" || result.area === "filter-trigger" || result.area === "fixed-trigger" || result.area === "column-collapse-trigger");
30846
+ const isHeaderRangeGesture = (event.shiftKey || toggleColumn) && result.columnKey && (result.area === "header" || result.area === "sort-trigger" && !preserveAnimatedSortSelection || result.area === "filter-trigger" || result.area === "fixed-trigger" || result.area === "column-collapse-trigger");
29474
30847
  if ((result.area === "body" || result.area === "body-content") && typeof result.rowIndex === "number" && result.columnKey) {
29475
30848
  this.host.clearSelectedHeader();
29476
30849
  this.host.updateRangeHighlightForBodyInteraction({
@@ -29479,15 +30852,26 @@ var ListTableEventController = class {
29479
30852
  shiftKey: event.shiftKey,
29480
30853
  rowSpan: result.rowSpan
29481
30854
  });
30855
+ if (!isSystemColumnKey(result.columnKey)) {
30856
+ this.host.rangeHighlight = resolveCellHighlightForClick({
30857
+ current: this.host.rangeHighlight,
30858
+ rowIndex: result.rowIndex,
30859
+ columnKey: result.columnKey,
30860
+ leafColumnKeys: this.host.leafColumns.map((column) => column.key),
30861
+ shiftKey: false,
30862
+ ctrlKey: false
30863
+ });
30864
+ }
29482
30865
  }
29483
- if ((result.area === "header" || result.area === "sort-trigger" || result.area === "filter-trigger" || result.area === "fixed-trigger" || isHeaderRangeGesture) && result.columnKey) {
30866
+ if ((result.area === "header" || result.area === "sort-trigger" && !preserveAnimatedSortSelection || result.area === "filter-trigger" || result.area === "fixed-trigger" || isHeaderRangeGesture) && result.columnKey) {
29484
30867
  this.host.rangeHighlight = {
29485
30868
  ...this.host.rangeHighlight,
29486
30869
  selectedCellRange: null
29487
30870
  };
29488
30871
  this.host.updateRangeHighlightForHeaderClick(
29489
30872
  result.columnKey,
29490
- event.shiftKey
30873
+ event.shiftKey,
30874
+ toggleColumn
29491
30875
  );
29492
30876
  }
29493
30877
  if (isHeaderRangeGesture) {
@@ -29823,12 +31207,24 @@ var ListTableEventController = class {
29823
31207
  } else if (!activated && this.host.draggingReorder.columnKey) {
29824
31208
  const columnKey = this.host.draggingReorder.columnKey;
29825
31209
  this.host.clearBodySelection();
29826
- this.host.resetRangeHighlightForHeaderSelection(event.shiftKey);
29827
- this.host.selectHeader(
31210
+ const toggleColumn = event.ctrlKey || event.metaKey;
31211
+ this.host.resetRangeHighlightForHeaderSelection(
31212
+ event.shiftKey,
31213
+ toggleColumn
31214
+ );
31215
+ if (toggleColumn) {
31216
+ this.host.clearSelectedHeader();
31217
+ } else {
31218
+ this.host.selectHeader(
31219
+ columnKey,
31220
+ this.host.draggingReorder.selectedHeaderRect
31221
+ );
31222
+ }
31223
+ this.host.updateRangeHighlightForHeaderClick(
29828
31224
  columnKey,
29829
- this.host.draggingReorder.selectedHeaderRect
31225
+ event.shiftKey,
31226
+ toggleColumn
29830
31227
  );
29831
- this.host.updateRangeHighlightForHeaderClick(columnKey, event.shiftKey);
29832
31228
  this.host.suppressNextClick = true;
29833
31229
  this.host.render();
29834
31230
  }
@@ -30055,6 +31451,14 @@ var ListTableEventController = class {
30055
31451
  this.host.render();
30056
31452
  }
30057
31453
  destroy() {
31454
+ if (this.scrollbarButtonFeedbackTimer != null) {
31455
+ window.clearTimeout(this.scrollbarButtonFeedbackTimer);
31456
+ this.scrollbarButtonFeedbackTimer = null;
31457
+ }
31458
+ if (this.scrollbarPartFeedbackTimer != null) {
31459
+ window.clearTimeout(this.scrollbarPartFeedbackTimer);
31460
+ this.scrollbarPartFeedbackTimer = null;
31461
+ }
30058
31462
  this.cancelPendingEditableCellRowSelection();
30059
31463
  this.stopContinuousScroll();
30060
31464
  this.stopTextSelectionAutoScroll();
@@ -30184,10 +31588,11 @@ var ListTableEventController = class {
30184
31588
  getRows: () => resolveNestedTableDisplayRows(content, getNestedState()),
30185
31589
  getAllRows: () => content.dataSource,
30186
31590
  getExportSummaryEnabled: () => getNestedState()?.summaryVisible === true,
31591
+ getExportSortState: () => getNestedState()?.sortState ?? [],
30187
31592
  getColumns: () => columns,
30188
31593
  getHeaderTree: () => headerTree,
30189
31594
  hideMenu,
30190
- startExportTask: (task, text, defaultFileName) => this.host.startExportTask(task, text, defaultFileName),
31595
+ startExportTask: (task, text, defaultFileName, dialog) => this.host.startExportTask(task, text, defaultFileName, dialog),
30191
31596
  onFreezeRow: () => {
30192
31597
  },
30193
31598
  onUnfreezeRow: () => {
@@ -30658,6 +32063,11 @@ var ListTableEventController = class {
30658
32063
  searchMatch: this.host.rangeHighlight.searchMatch
30659
32064
  };
30660
32065
  }
32066
+ /** 清除可视选区,但保留查找结果与业务行选择数据。 */
32067
+ clearTableHighlight() {
32068
+ this.clearParentTableHighlight();
32069
+ this.clearNestedTableHighlight();
32070
+ }
30661
32071
  /** 父表格进入选区交互时清理全部子表格选区。 */
30662
32072
  clearNestedTableHighlight() {
30663
32073
  this.activeNestedTableHit = null;
@@ -30665,9 +32075,31 @@ var ListTableEventController = class {
30665
32075
  }
30666
32076
  startContinuousScroll(area, direction, viewport) {
30667
32077
  this.stopContinuousScroll();
32078
+ const axis = area === "horizontal-scrollbar" ? "horizontal" : "vertical";
32079
+ const style = utils.resolveCanvasScrollbarAxis(this.host.options.scrollbar, axis);
32080
+ const startedAt = Date.now();
32081
+ let previousAt = startedAt;
30668
32082
  this.continuousScrollTimer = window.setInterval(() => {
30669
- this.stepScrollbar(area, direction, viewport);
30670
- }, 80);
32083
+ const now2 = Date.now();
32084
+ const elapsed = Math.max(now2 - previousAt, 0);
32085
+ previousAt = now2;
32086
+ const distance = utils.resolveCanvasScrollbarHoldSpeed(
32087
+ now2 - startedAt,
32088
+ style.maxScrollSpeed
32089
+ ) * elapsed * direction / 1e3;
32090
+ const base = this.host.animationController.getWheelScrollTarget() ?? this.host.scroll;
32091
+ const next = {
32092
+ ...base,
32093
+ left: axis === "horizontal" ? Math.min(
32094
+ Math.max(base.left + distance, 0),
32095
+ viewport.maxScrollLeft
32096
+ ) : base.left,
32097
+ top: axis === "vertical" ? Math.min(Math.max(base.top + distance, 0), viewport.maxScrollTop) : base.top
32098
+ };
32099
+ if (next.left !== base.left || next.top !== base.top) {
32100
+ this.host.animationController.startWheelScrollSmoothing(next);
32101
+ }
32102
+ }, 32);
30671
32103
  this.host.canvasHost.bindWindowDragEvents(
30672
32104
  this.handleWindowMouseMove,
30673
32105
  this.handleWindowMouseUp
@@ -30680,22 +32112,19 @@ var ListTableEventController = class {
30680
32112
  }
30681
32113
  }
30682
32114
  stepScrollbar(area, direction, viewport) {
32115
+ const base = this.host.animationController.getWheelScrollTarget() ?? this.host.scroll;
30683
32116
  const nextScroll = stepScrollbarScroll({
30684
- current: this.host.scroll,
32117
+ current: base,
30685
32118
  area,
30686
32119
  direction,
30687
32120
  viewport,
30688
32121
  rowHeight: this.host.currentRenderSnapshot?.rowHeight ?? DEFAULT_ROW_HEIGHT,
30689
32122
  horizontalStep: HORIZONTAL_SCROLL_STEP
30690
32123
  });
30691
- if (!this.host.applyUserScroll(nextScroll)) {
32124
+ if (nextScroll.left === base.left && nextScroll.top === base.top) {
30692
32125
  return;
30693
32126
  }
30694
- this.host.markScrollbarActive();
30695
- this.host.scheduleRender();
30696
- if (area === "vertical-scrollbar") {
30697
- this.host.maybeLoadNextScrollPage();
30698
- }
32127
+ this.host.animationController.startWheelScrollSmoothing(nextScroll);
30699
32128
  }
30700
32129
  jumpScrollbar(axis, pointerValue, track, thumb, viewport) {
30701
32130
  const nextScroll = jumpScrollbarScroll({
@@ -30716,6 +32145,22 @@ var ListTableEventController = class {
30716
32145
  }
30717
32146
  }
30718
32147
  finishWindowDragCleanup(options) {
32148
+ if (this.host.hover.pressedScrollbarButton) {
32149
+ this.scrollbarButtonFeedbackTimer = window.setTimeout(() => {
32150
+ this.scrollbarButtonFeedbackTimer = null;
32151
+ this.host.hover = {
32152
+ ...this.host.hover,
32153
+ pressedScrollbarButton: void 0
32154
+ };
32155
+ this.host.scheduleRender();
32156
+ }, 140);
32157
+ }
32158
+ if (this.host.hover.pressedScrollbarPart?.role === "thumb") {
32159
+ this.host.hover = {
32160
+ ...this.host.hover,
32161
+ pressedScrollbarPart: void 0
32162
+ };
32163
+ }
30719
32164
  this.host.draggingScrollbar = null;
30720
32165
  this.host.draggingResize = null;
30721
32166
  this.host.draggingReorder = null;
@@ -30729,6 +32174,24 @@ var ListTableEventController = class {
30729
32174
  this.host.markScrollbarActive();
30730
32175
  this.host.render();
30731
32176
  }
32177
+ flashScrollbarPart(axis, role) {
32178
+ if (this.scrollbarPartFeedbackTimer != null) {
32179
+ window.clearTimeout(this.scrollbarPartFeedbackTimer);
32180
+ }
32181
+ this.host.hover = {
32182
+ ...this.host.hover,
32183
+ pressedScrollbarPart: { axis, role }
32184
+ };
32185
+ this.host.scheduleRender();
32186
+ this.scrollbarPartFeedbackTimer = window.setTimeout(() => {
32187
+ this.scrollbarPartFeedbackTimer = null;
32188
+ this.host.hover = {
32189
+ ...this.host.hover,
32190
+ pressedScrollbarPart: void 0
32191
+ };
32192
+ this.host.scheduleRender();
32193
+ }, 140);
32194
+ }
30732
32195
  startTextSelection(result, pointerX, pointerY) {
30733
32196
  if (result.area !== "body-content" && result.area !== "header" && result.area !== "summary" || !result.columnKey || !result.textSelectionText || !result.textSelectionFont || !result.textSelectionFragmentKey || !result.rect) {
30734
32197
  return false;
@@ -31076,16 +32539,46 @@ var ListTableEventController = class {
31076
32539
  return false;
31077
32540
  }
31078
32541
  if (drag.kind === "row") {
32542
+ if (drag.mode !== "replace" && !this.cellSelectionDragged && result.rowIndex === drag.pointerStartRowIndex || drag.lastRowIndex === result.rowIndex) {
32543
+ return false;
32544
+ }
31079
32545
  const nextRange2 = {
31080
32546
  start: Math.min(drag.startRowIndex, result.rowIndex),
31081
32547
  end: Math.max(drag.startRowIndex, result.rowIndex)
31082
32548
  };
31083
32549
  const currentRange2 = this.host.rangeHighlight.selectedRowRange;
31084
- if (currentRange2?.start === nextRange2.start && currentRange2.end === nextRange2.end) {
32550
+ if (drag.mode === "replace" && currentRange2?.start === nextRange2.start && currentRange2.end === nextRange2.end) {
31085
32551
  return false;
31086
32552
  }
31087
- this.cellSelectionDragged = this.cellSelectionDragged || result.rowIndex !== drag.startRowIndex;
31088
- this.selectRowRange(drag.startRowIndex, result.rowIndex);
32553
+ this.cellSelectionDragged = this.cellSelectionDragged || result.rowIndex !== drag.pointerStartRowIndex;
32554
+ drag.lastRowIndex = result.rowIndex;
32555
+ if (drag.mode === "replace") {
32556
+ this.selectRowRange(drag.startRowIndex, result.rowIndex);
32557
+ } else if (drag.mode === "extend") {
32558
+ this.host.rangeHighlight = resolveRowHighlightForSelectorClick({
32559
+ current: {
32560
+ ...drag.baseRangeHighlight,
32561
+ selectedRowAnchorIndex: drag.startRowIndex
32562
+ },
32563
+ rowIndex: result.rowIndex,
32564
+ shiftKey: true,
32565
+ ctrlKey: false
32566
+ });
32567
+ } else {
32568
+ this.host.rangeHighlight = resolveRowHighlightForDrag({
32569
+ current: drag.baseRangeHighlight,
32570
+ startRowIndex: drag.startRowIndex,
32571
+ endRowIndex: result.rowIndex,
32572
+ remove: drag.mode === "remove"
32573
+ });
32574
+ }
32575
+ if (drag.mode !== "replace") {
32576
+ this.host.clearSelectedHeader();
32577
+ this.host.selection = {
32578
+ rowIndex: drag.mode === "remove" ? null : result.rowIndex,
32579
+ columnKey: ROW_SELECTOR_COLUMN_KEY
32580
+ };
32581
+ }
31089
32582
  if (options?.scheduleRender !== false) {
31090
32583
  this.host.scheduleRender(result);
31091
32584
  }
@@ -31097,7 +32590,7 @@ var ListTableEventController = class {
31097
32590
  if (columnIndex < 0) {
31098
32591
  return false;
31099
32592
  }
31100
- if (result.rowIndex === drag.startRowIndex && columnIndex === drag.startColumnIndex) {
32593
+ if (!this.cellSelectionDragged && result.rowIndex === drag.pointerStartRowIndex && columnIndex === drag.pointerStartColumnIndex || drag.lastRowIndex === result.rowIndex && drag.lastColumnIndex === columnIndex) {
31101
32594
  return false;
31102
32595
  }
31103
32596
  const nextRange = {
@@ -31107,14 +32600,53 @@ var ListTableEventController = class {
31107
32600
  endColumnIndex: Math.max(drag.startColumnIndex, columnIndex)
31108
32601
  };
31109
32602
  const currentRange = this.host.rangeHighlight.selectedCellRange;
31110
- if (currentRange?.startRowIndex === nextRange.startRowIndex && currentRange.endRowIndex === nextRange.endRowIndex && currentRange.startColumnIndex === nextRange.startColumnIndex && currentRange.endColumnIndex === nextRange.endColumnIndex) {
32603
+ if (drag.mode === "replace" && currentRange?.startRowIndex === nextRange.startRowIndex && currentRange.endRowIndex === nextRange.endRowIndex && currentRange.startColumnIndex === nextRange.startColumnIndex && currentRange.endColumnIndex === nextRange.endColumnIndex) {
31111
32604
  return false;
31112
32605
  }
31113
32606
  this.cellSelectionDragged = true;
31114
- this.host.rangeHighlight = {
31115
- ...this.host.rangeHighlight,
31116
- selectedCellRange: nextRange
31117
- };
32607
+ drag.lastRowIndex = result.rowIndex;
32608
+ drag.lastColumnIndex = columnIndex;
32609
+ if (drag.mode === "replace") {
32610
+ this.host.rangeHighlight = {
32611
+ ...this.host.rangeHighlight,
32612
+ selectedCellRange: nextRange,
32613
+ selectedCellRanges: null,
32614
+ selectedCellAnchor: {
32615
+ rowIndex: drag.startRowIndex,
32616
+ columnKey: this.host.leafColumns[drag.startColumnIndex]?.key ?? result.columnKey
32617
+ }
32618
+ };
32619
+ } else if (drag.mode === "extend") {
32620
+ this.host.rangeHighlight = resolveCellHighlightForClick({
32621
+ current: {
32622
+ ...drag.baseRangeHighlight,
32623
+ selectedCellAnchor: {
32624
+ rowIndex: drag.startRowIndex,
32625
+ columnKey: this.host.leafColumns[drag.startColumnIndex]?.key ?? result.columnKey
32626
+ }
32627
+ },
32628
+ rowIndex: result.rowIndex,
32629
+ columnKey: result.columnKey,
32630
+ leafColumnKeys: this.host.leafColumns.map((column) => column.key),
32631
+ shiftKey: true,
32632
+ ctrlKey: false
32633
+ });
32634
+ } else {
32635
+ this.host.rangeHighlight = resolveCellHighlightForDrag({
32636
+ current: drag.baseRangeHighlight,
32637
+ startRowIndex: drag.startRowIndex,
32638
+ endRowIndex: result.rowIndex,
32639
+ startColumnIndex: drag.startColumnIndex,
32640
+ endColumnIndex: columnIndex,
32641
+ columnKey: this.host.leafColumns[drag.startColumnIndex]?.key ?? result.columnKey,
32642
+ remove: drag.mode === "remove",
32643
+ preserveRowSelection: drag.preserveRowSelection === true
32644
+ });
32645
+ }
32646
+ if (drag.mode !== "replace") {
32647
+ this.host.clearSelectedHeader();
32648
+ this.host.selection = drag.mode === "remove" ? { rowIndex: null, columnKey: null } : { rowIndex: result.rowIndex, columnKey: result.columnKey };
32649
+ }
31118
32650
  if (options?.scheduleRender !== false) {
31119
32651
  this.host.scheduleRender(result);
31120
32652
  }
@@ -31198,9 +32730,13 @@ var ListTableEventController = class {
31198
32730
  start: Math.min(startRowIndex, endRowIndex),
31199
32731
  end: Math.max(startRowIndex, endRowIndex)
31200
32732
  },
32733
+ selectedRowRanges: null,
31201
32734
  selectedColumnAnchorKey: null,
31202
32735
  selectedColumnRange: null,
31203
- selectedCellRange: null
32736
+ selectedColumnKeys: null,
32737
+ selectedCellRange: null,
32738
+ selectedCellRanges: null,
32739
+ selectedCellAnchor: null
31204
32740
  };
31205
32741
  }
31206
32742
  };
@@ -31519,6 +33055,7 @@ function createListTableEventHost(core) {
31519
33055
  // 可见行在百万行窗口化后只包含当前视口;事件层需要完整的查询结果,
31520
33056
  // 否则自动列宽等基于全局 dataRowIndex 的逻辑会在深滚动时丢失采样行。
31521
33057
  getDisplayData: () => core.queryController.getDisplayData(),
33058
+ getSortState: () => core.storeFacade.sortState,
31522
33059
  getSummaryValueMap: () => core.summaryController.getSummaryValueMap(),
31523
33060
  getSummaryValuesAsync: (snapshot) => core.summaryController.getSummaryValuesAsync(snapshot),
31524
33061
  scrollTo: (options) => core.scrollTo(options),
@@ -31536,11 +33073,12 @@ function createListTableEventHost(core) {
31536
33073
  clearBodySelection: () => core.selectionController.clearBodySelection(),
31537
33074
  clearSelectedHeader: () => core.selectionController.clearSelectedHeader(),
31538
33075
  selectHeader: (columnKey, rect) => core.selectionController.selectHeader(columnKey, rect),
31539
- resetRangeHighlightForHeaderSelection: (shiftKey) => core.selectionController.resetRangeHighlightForHeaderSelection(shiftKey),
33076
+ resetRangeHighlightForHeaderSelection: (shiftKey, ctrlKey) => core.selectionController.resetRangeHighlightForHeaderSelection(shiftKey, ctrlKey),
31540
33077
  updateRangeHighlightForBodyInteraction: (params) => core.selectionController.updateRangeHighlightForBodyInteraction(params),
31541
- updateRangeHighlightForHeaderClick: (columnKey, shiftKey) => core.selectionController.updateRangeHighlightForHeaderClick(
33078
+ updateRangeHighlightForHeaderClick: (columnKey, shiftKey, ctrlKey) => core.selectionController.updateRangeHighlightForHeaderClick(
31542
33079
  columnKey,
31543
- shiftKey
33080
+ shiftKey,
33081
+ ctrlKey
31544
33082
  ),
31545
33083
  updateRangeHighlightForHeaderContextMenu: (columnKey) => core.selectionController.updateRangeHighlightForHeaderContextMenu(
31546
33084
  columnKey
@@ -31583,7 +33121,7 @@ function createListTableEventHost(core) {
31583
33121
  return snapshot?.columnSegments ?? core.columnSegments;
31584
33122
  },
31585
33123
  persistColumnConfig: () => core.persistenceController.persistColumnConfig(),
31586
- startExportTask: (task, text, defaultFileName) => {
33124
+ startExportTask: (task, text, defaultFileName, dialog) => {
31587
33125
  const configured = core.options.exportOptions?.fileName?.trim();
31588
33126
  const extension = defaultFileName?.match(/\.(xlsx|csv|txt|json)$/i)?.[0];
31589
33127
  const fileName = configured && extension ? configured.replace(/\.(xlsx|csv|txt|json)$/i, "") + extension : defaultFileName;
@@ -31591,7 +33129,9 @@ function createListTableEventHost(core) {
31591
33129
  task,
31592
33130
  text,
31593
33131
  void 0,
31594
- fileName
33132
+ fileName,
33133
+ void 0,
33134
+ dialog
31595
33135
  );
31596
33136
  },
31597
33137
  resolveContentDownloadAction: (content) => core.contentActionController.resolveContentDownloadAction(
@@ -31686,6 +33226,7 @@ var ListTableHeaderActionController = class {
31686
33226
  if (this.options.isEditing() || this.options.getLeafColumns().find((column) => column.key === columnKey)?.allowSort === false) {
31687
33227
  return;
31688
33228
  }
33229
+ this.options.captureSortAnimation?.(columnKey);
31689
33230
  this.options.prepareLocalQuery("sort");
31690
33231
  this.options.setSortState(
31691
33232
  toggleSortingState(
@@ -31703,6 +33244,7 @@ var ListTableHeaderActionController = class {
31703
33244
  if (this.options.isEditing() || this.options.getLeafColumns().find((column) => column.key === columnKey)?.allowSort === false) {
31704
33245
  return;
31705
33246
  }
33247
+ this.options.captureSortAnimation?.(columnKey);
31706
33248
  this.options.prepareLocalQuery("sort");
31707
33249
  this.options.setSortState(
31708
33250
  setSortingOrder(
@@ -31873,6 +33415,7 @@ var ListTableHeaderActionController = class {
31873
33415
  if (this.options.isEditing()) {
31874
33416
  return;
31875
33417
  }
33418
+ this.options.captureSortAnimation?.();
31876
33419
  this.options.prepareLocalQuery("sort");
31877
33420
  const next = resolveResetSortAction(this.options.getFilterState());
31878
33421
  this.options.setSortState(next.sortState);
@@ -31910,6 +33453,7 @@ var ListTableHeaderActionController = class {
31910
33453
  if (!resolved) {
31911
33454
  return;
31912
33455
  }
33456
+ this.options.overlayController.preserveHeaderContextMenuForColumnVisibility();
31913
33457
  this.options.setSelectedHeaderState(
31914
33458
  resolved.selectedHeaderKey,
31915
33459
  resolved.selectedHeaderRect
@@ -31934,6 +33478,9 @@ var ListTableHeaderActionController = class {
31934
33478
  buildHeaderSettingsItems(columns)
31935
33479
  );
31936
33480
  this.options.requestRender();
33481
+ if (draft.key && draft.sortOrder !== void 0 && (this.options.getSortState().find((entry) => entry.key === draft.key)?.order ?? null) !== draft.sortOrder) {
33482
+ this.setSortOrder(draft.key, draft.sortOrder);
33483
+ }
31937
33484
  return null;
31938
33485
  }
31939
33486
  deleteHeaderColumn(columnKey) {
@@ -32025,6 +33572,7 @@ var ListTableOverlayController = class {
32025
33572
  __publicField(this, "componentPortal");
32026
33573
  __publicField(this, "getContentZoom");
32027
33574
  __publicField(this, "runtime");
33575
+ __publicField(this, "preserveColumnVisibilityMenu", false);
32028
33576
  __publicField(this, "tooltipAdapter");
32029
33577
  __publicField(this, "filterPanelAdapter");
32030
33578
  __publicField(this, "fixedPopupAdapter");
@@ -32162,6 +33710,7 @@ var ListTableOverlayController = class {
32162
33710
  return this.componentPortal.fixedPopup.isVisible();
32163
33711
  }
32164
33712
  showHeaderContextMenu(config) {
33713
+ this.preserveColumnVisibilityMenu = false;
32165
33714
  this.runtime.headerContextMenuVisible = true;
32166
33715
  this.runtime.headerContextMenuConfig = config;
32167
33716
  const options = this.getOptions();
@@ -32171,6 +33720,7 @@ var ListTableOverlayController = class {
32171
33720
  }
32172
33721
  }
32173
33722
  hideHeaderContextMenu() {
33723
+ this.preserveColumnVisibilityMenu = false;
32174
33724
  this.runtime.headerContextMenuVisible = false;
32175
33725
  this.runtime.headerContextMenuConfig = null;
32176
33726
  this.getOptions().ui?.onHeaderContextMenuRequest?.(null);
@@ -32182,6 +33732,15 @@ var ListTableOverlayController = class {
32182
33732
  }
32183
33733
  return this.componentPortal.headerContextMenu.isVisible();
32184
33734
  }
33735
+ /** 连续修改列显隐时,保留菜单直到用户显式关闭。 */
33736
+ preserveHeaderContextMenuForColumnVisibility() {
33737
+ if (this.runtime.headerContextMenuVisible) {
33738
+ this.preserveColumnVisibilityMenu = true;
33739
+ }
33740
+ }
33741
+ shouldPreserveHeaderContextMenuOnOptionsUpdate() {
33742
+ return this.runtime.headerContextMenuVisible && this.preserveColumnVisibilityMenu;
33743
+ }
32185
33744
  updateHeaderContextMenuColumns(columns) {
32186
33745
  const options = this.getOptions();
32187
33746
  if (options.ui?.onHeaderContextMenuRequest && this.runtime.headerContextMenuConfig) {
@@ -32402,7 +33961,7 @@ function applyStoredColumnConfig(columns, storedColumns, cacheColumnSettings = t
32402
33961
  applyToColumns(columns);
32403
33962
  return columnWidthsChanged;
32404
33963
  }
32405
- function buildStoredColumnConfig(columns, sortState, filterState, zoom, summaryRowHeight, density, cacheColumnSettings = true, cachePreferences) {
33964
+ function buildStoredColumnConfig(columns, sortState, filterState, zoom, summaryRowHeight, density, cacheColumnSettings = true, cachePreferences, scrollbar) {
32406
33965
  const result = {};
32407
33966
  const columnOrder = [];
32408
33967
  const collectColumns = (cols, parentKey) => {
@@ -32459,6 +34018,7 @@ function buildStoredColumnConfig(columns, sortState, filterState, zoom, summaryR
32459
34018
  {
32460
34019
  columns: result,
32461
34020
  cacheColumnSettings,
34021
+ ...scrollbar ? { scrollbar } : null,
32462
34022
  ...cacheColumnSettings ? { columnOrder } : null,
32463
34023
  sort: sortState.map((item) => ({ ...item })),
32464
34024
  filters: filterState,
@@ -32498,6 +34058,7 @@ function filterStoredColumnConfig(stored, preferences = {}) {
32498
34058
  if (preferences.summary === false) delete result.summaryRowHeight;
32499
34059
  if (preferences.density === false) delete result.density;
32500
34060
  if (preferences.zoom === false) delete result.zoom;
34061
+ if (preferences.scrollbar === false) delete result.scrollbar;
32501
34062
  return result;
32502
34063
  }
32503
34064
 
@@ -32512,7 +34073,8 @@ function persistListTableColumnConfig(params) {
32512
34073
  summaryRowHeight,
32513
34074
  density,
32514
34075
  cacheColumnSettings,
32515
- cachePreferences
34076
+ cachePreferences,
34077
+ scrollbar
32516
34078
  } = params;
32517
34079
  if (!storageKey) {
32518
34080
  return;
@@ -32528,7 +34090,8 @@ function persistListTableColumnConfig(params) {
32528
34090
  summaryRowHeight,
32529
34091
  density,
32530
34092
  cacheColumnSettings,
32531
- cachePreferences
34093
+ cachePreferences,
34094
+ scrollbar
32532
34095
  )
32533
34096
  );
32534
34097
  }
@@ -32597,6 +34160,7 @@ async function loadListTableStoredConfig(params) {
32597
34160
  stored.columnOrder
32598
34161
  );
32599
34162
  return {
34163
+ scrollbar: stored.scrollbar,
32600
34164
  cacheColumnSettings,
32601
34165
  cachePreferences,
32602
34166
  sortState: cachePreferences.sort !== false && Array.isArray(stored.sort) ? stored.sort.filter(
@@ -32681,6 +34245,10 @@ var ListTablePersistenceController = class {
32681
34245
  this.options.canvasHost.setContentZoom(nextZoom);
32682
34246
  }
32683
34247
  let currentOptions = this.options.getOptions();
34248
+ if (stored.scrollbar) {
34249
+ currentOptions = { ...currentOptions, scrollbar: stored.scrollbar };
34250
+ currentOptions.ui?.onScrollbarChange?.(stored.scrollbar);
34251
+ }
32684
34252
  if (stored.cachePreferences) {
32685
34253
  currentOptions = {
32686
34254
  ...currentOptions,
@@ -32725,10 +34293,16 @@ var ListTablePersistenceController = class {
32725
34293
  }
32726
34294
  }
32727
34295
  this.options.refreshRuntimeColumns();
34296
+ currentOptions.ui?.onColumnSettingsChange?.(
34297
+ cloneColumns(currentOptions.columns)
34298
+ );
32728
34299
  }
32729
34300
  persistColumnConfig() {
32730
34301
  this.invalidate();
32731
34302
  const tableOptions = this.options.getOptions();
34303
+ tableOptions.ui?.onColumnSettingsChange?.(
34304
+ cloneColumns(tableOptions.columns)
34305
+ );
32732
34306
  this.restoredStorageKey = tableOptions.storageKey;
32733
34307
  this.hasRestoredPreferences = true;
32734
34308
  this.pendingWrite = Promise.resolve(
@@ -32736,6 +34310,7 @@ var ListTablePersistenceController = class {
32736
34310
  storageKey: tableOptions.storageKey,
32737
34311
  cacheColumnSettings: tableOptions.cacheColumnSettings,
32738
34312
  cachePreferences: tableOptions.cachePreferences,
34313
+ scrollbar: tableOptions.scrollbar,
32739
34314
  columns: tableOptions.columns,
32740
34315
  sortState: this.options.getSortState(),
32741
34316
  filterState: this.options.getFilterState(),
@@ -32970,6 +34545,7 @@ var ListTableQueryController = class {
32970
34545
  this.invalidateDataCache();
32971
34546
  this.options.hideLoading();
32972
34547
  this.emitQueryChangeWithResolvedRows(action, baseRows);
34548
+ if (action === "sort") this.options.onSortResolved?.();
32973
34549
  this.options.requestRender();
32974
34550
  }).catch(handleFailure);
32975
34551
  };
@@ -34494,6 +36070,7 @@ var ListTableSelectionController = class {
34494
36070
  __publicField(this, "selectedHeaderKey", null);
34495
36071
  __publicField(this, "selectedHeaderRect", null);
34496
36072
  __publicField(this, "summarySelected", false);
36073
+ __publicField(this, "pendingSortSelection", null);
34497
36074
  this.options = options;
34498
36075
  }
34499
36076
  getSelectedHeaderKey() {
@@ -34517,20 +36094,62 @@ var ListTableSelectionController = class {
34517
36094
  this.selectedHeaderRect = null;
34518
36095
  }
34519
36096
  clearBodySelection() {
36097
+ this.pendingSortSelection = null;
34520
36098
  this.options.setSelection({
34521
36099
  rowIndex: null,
34522
36100
  columnKey: null
34523
36101
  });
34524
36102
  }
36103
+ /** 排序前按稳定行标识记录活动行,避免行号被新排序复用。 */
36104
+ captureSortSelection(rows) {
36105
+ const options = this.options.getOptions();
36106
+ const rowIndex = this.options.getSelection().rowIndex;
36107
+ const record = rowIndex == null ? void 0 : rows[rowIndex];
36108
+ this.pendingSortSelection = options.rowKey && record !== void 0 && rowIndex != null ? { rowKey: getRowKey(options, record, rowIndex), rowIndex } : null;
36109
+ }
36110
+ /** 排序完成后将活动行及其单行选区映射到新行号。 */
36111
+ reconcileSortSelection(rows) {
36112
+ const pending = this.pendingSortSelection;
36113
+ this.pendingSortSelection = null;
36114
+ if (!pending) return;
36115
+ const options = this.options.getOptions();
36116
+ const sameIndexRecord = rows[pending.rowIndex];
36117
+ const nextIndex = sameIndexRecord !== void 0 && getRowKey(options, sameIndexRecord, pending.rowIndex) === pending.rowKey ? pending.rowIndex : rows.findIndex(
36118
+ (record, index) => getRowKey(options, record, index) === pending.rowKey
36119
+ );
36120
+ const selection = this.options.getSelection();
36121
+ if (selection.rowIndex !== pending.rowIndex) return;
36122
+ if (nextIndex === pending.rowIndex) return;
36123
+ this.options.setSelection({
36124
+ ...selection,
36125
+ rowIndex: nextIndex >= 0 ? nextIndex : null,
36126
+ columnKey: nextIndex >= 0 ? selection.columnKey : null
36127
+ });
36128
+ const range = this.options.getRangeHighlight();
36129
+ const singleRowRange = range.selectedRowRange?.start === pending.rowIndex && range.selectedRowRange.end === pending.rowIndex;
36130
+ const singleCellRange = range.selectedCellRange?.startRowIndex === pending.rowIndex && range.selectedCellRange.endRowIndex === pending.rowIndex;
36131
+ this.options.setRangeHighlight({
36132
+ ...range,
36133
+ selectedRowAnchorIndex: range.selectedRowAnchorIndex === pending.rowIndex ? nextIndex >= 0 ? nextIndex : null : range.selectedRowAnchorIndex,
36134
+ selectedRowRange: singleRowRange ? nextIndex >= 0 ? { start: nextIndex, end: nextIndex } : null : range.selectedRowRange,
36135
+ selectedCellAnchor: range.selectedCellAnchor?.rowIndex === pending.rowIndex ? nextIndex >= 0 ? { ...range.selectedCellAnchor, rowIndex: nextIndex } : null : range.selectedCellAnchor,
36136
+ selectedCellRange: singleCellRange ? nextIndex >= 0 ? {
36137
+ ...range.selectedCellRange,
36138
+ startRowIndex: nextIndex,
36139
+ endRowIndex: nextIndex
36140
+ } : null : range.selectedCellRange
36141
+ });
36142
+ }
34525
36143
  setSelectedHeaderState(selectedHeaderKey, selectedHeaderRect) {
34526
36144
  this.selectedHeaderKey = selectedHeaderKey;
34527
36145
  this.selectedHeaderRect = selectedHeaderRect;
34528
36146
  }
34529
- resetRangeHighlightForHeaderSelection(shiftKey) {
36147
+ resetRangeHighlightForHeaderSelection(shiftKey, ctrlKey = false) {
34530
36148
  this.options.setRangeHighlight(
34531
36149
  resolveRangeHighlightForHeaderSelection({
34532
36150
  current: this.options.getRangeHighlight(),
34533
- shiftKey
36151
+ shiftKey,
36152
+ ctrlKey
34534
36153
  })
34535
36154
  );
34536
36155
  }
@@ -34598,7 +36217,7 @@ var ListTableSelectionController = class {
34598
36217
  })
34599
36218
  );
34600
36219
  }
34601
- updateRangeHighlightForHeaderClick(columnKey, shiftKey) {
36220
+ updateRangeHighlightForHeaderClick(columnKey, shiftKey, ctrlKey = false) {
34602
36221
  const leafColumns = this.options.getLeafColumns();
34603
36222
  const targetRange = findHeaderLeafRange({
34604
36223
  columnKey,
@@ -34615,7 +36234,8 @@ var ListTableSelectionController = class {
34615
36234
  leafColumnKeys: leafColumns.map((column) => column.key),
34616
36235
  rangeStartIndex: targetRange.start,
34617
36236
  rangeEndIndex: targetRange.end,
34618
- shiftKey
36237
+ shiftKey,
36238
+ ctrlKey
34619
36239
  })
34620
36240
  );
34621
36241
  }
@@ -34659,7 +36279,8 @@ var ListTableSelectionController = class {
34659
36279
  this.options.setRangeHighlight({
34660
36280
  ...current,
34661
36281
  selectedColumnAnchorKey: null,
34662
- selectedColumnRange: null
36282
+ selectedColumnRange: null,
36283
+ selectedColumnKeys: null
34663
36284
  });
34664
36285
  }
34665
36286
  destroy() {
@@ -34997,6 +36618,44 @@ function createDefaultSummaryScheduler() {
34997
36618
  };
34998
36619
  }
34999
36620
 
36621
+ // src/engine/sort-animation.ts
36622
+ function hasAnimatedSortColumn(columns, sortState) {
36623
+ if (!sortState.length) return false;
36624
+ const activeKeys = new Set(sortState.map((item) => item.key));
36625
+ const visit = (items) => items.some(
36626
+ (column) => activeKeys.has(resolveColumnKey(column)) && typeof column.sorter === "object" && column.sorter.animation === true || (column.children?.length ? visit(column.children) : false)
36627
+ );
36628
+ return visit(columns);
36629
+ }
36630
+ function shouldAnimateSortedDataUpdate(params) {
36631
+ const { previous, next, sortState, expandedRowsChanged } = params;
36632
+ return Boolean(
36633
+ !expandedRowsChanged && next.rowKey && previous.rowKey && previous.rowHeight !== "auto" && next.rowHeight !== "auto" && next.rowHeight === previous.rowHeight && next.dataSource !== previous.dataSource && hasAnimatedSortColumn(next.columns, sortState)
36634
+ );
36635
+ }
36636
+
36637
+ // src/features/grouping/default-grouping.ts
36638
+ function resolveDefaultGroupingColumnKeys(columns) {
36639
+ const candidates = [];
36640
+ const visit = (items) => {
36641
+ items.forEach((column) => {
36642
+ if (column.children?.length) {
36643
+ visit(column.children);
36644
+ return;
36645
+ }
36646
+ if (!column.grouping) return;
36647
+ const order = typeof column.grouping === "object" && Number.isFinite(column.grouping.order) ? column.grouping.order : Number.POSITIVE_INFINITY;
36648
+ candidates.push({
36649
+ key: column.key ?? String(column.dataIndex),
36650
+ order,
36651
+ index: candidates.length
36652
+ });
36653
+ });
36654
+ };
36655
+ visit(columns);
36656
+ return candidates.sort((left, right) => left.order - right.order || left.index - right.index).map((item) => item.key);
36657
+ }
36658
+
35000
36659
  // src/engine/controller-assembly.ts
35001
36660
  function assembleListTableCoreControllers(core, options) {
35002
36661
  const getCanvasHostElement = () => core.domHost.getNodes()?.canvasHostElement ?? null;
@@ -35120,14 +36779,24 @@ function assembleListTableCoreControllers(core, options) {
35120
36779
  ...core.storeFacade.rangeHighlight,
35121
36780
  selectedColumnAnchorKey: null,
35122
36781
  selectedColumnRange: null,
36782
+ selectedColumnKeys: null,
35123
36783
  selectedCellRange: null
35124
36784
  };
35125
36785
  };
35126
36786
  core.syncGroupingBar = syncGroupingBar;
35127
36787
  core.syncGroupingFromOptions = () => {
35128
36788
  const previousColumnKeys = core.groupColumnKeys;
36789
+ const nextDefaultKeys = resolveDefaultGroupingColumnKeys(
36790
+ core.options.columns
36791
+ );
36792
+ const defaultsChanged = nextDefaultKeys.length !== core.defaultGroupColumnKeys.length || nextDefaultKeys.some(
36793
+ (key, index) => key !== core.defaultGroupColumnKeys[index]
36794
+ );
36795
+ core.defaultGroupColumnKeys = nextDefaultKeys;
35129
36796
  if (core.options.grouping?.visible !== void 0) {
35130
36797
  core.groupBarVisible = core.options.grouping.visible;
36798
+ } else if (defaultsChanged) {
36799
+ core.groupBarVisible = nextDefaultKeys.length > 0;
35131
36800
  }
35132
36801
  if (core.options.grouping?.columnKeys !== void 0) {
35133
36802
  const validKeys = new Set(
@@ -35138,6 +36807,8 @@ function assembleListTableCoreControllers(core, options) {
35138
36807
  core.groupColumnKeys = core.options.grouping.columnKeys.filter(
35139
36808
  (key, index, keys) => validKeys.has(key) && keys.indexOf(key) === index
35140
36809
  );
36810
+ } else if (defaultsChanged) {
36811
+ core.groupColumnKeys = nextDefaultKeys;
35141
36812
  }
35142
36813
  if (previousColumnKeys.length !== core.groupColumnKeys.length || previousColumnKeys.some(
35143
36814
  (key, index) => key !== core.groupColumnKeys[index]
@@ -35218,7 +36889,8 @@ function assembleListTableCoreControllers(core, options) {
35218
36889
  getQueryRuntimeState,
35219
36890
  showLoading: (text) => core.overlayController.showLoading(text),
35220
36891
  hideLoading: () => core.overlayController.hideLoading(),
35221
- requestRender: () => render()
36892
+ requestRender: () => render(),
36893
+ onSortResolved: () => core.animationController.markSortRowsReady()
35222
36894
  });
35223
36895
  core.summaryController = new ListTableSummaryController({
35224
36896
  getOptions: () => core.options,
@@ -35373,6 +37045,18 @@ function assembleListTableCoreControllers(core, options) {
35373
37045
  persistColumnConfig: () => core.persistenceController.persistColumnConfig(),
35374
37046
  emitQueryChange,
35375
37047
  prepareLocalQuery,
37048
+ captureSortAnimation: (columnKey) => {
37049
+ const currentSort = core.storeFacade.sortState;
37050
+ const nextSort = columnKey ? [...currentSort, { key: columnKey, order: "ascend" }] : currentSort;
37051
+ if (!core.options.rowKey || core.options.rowHeight === "auto" || !hasAnimatedSortColumn(core.options.columns, nextSort)) {
37052
+ core.animationController.setPendingSortRowPositions(null);
37053
+ return;
37054
+ }
37055
+ core.selectionController.captureSortSelection(getDisplayData());
37056
+ core.animationController.setPendingSortRowPositions(
37057
+ captureRenderedVisibleRowPositions(getCurrentRenderSnapshot())
37058
+ );
37059
+ },
35376
37060
  applyFilterQueryAsync,
35377
37061
  applySortQueryAsync,
35378
37062
  refreshRuntimeColumns,
@@ -35388,6 +37072,7 @@ function assembleListTableCoreControllers(core, options) {
35388
37072
  });
35389
37073
  core.contentActionController = new ListTableContentActionController({
35390
37074
  getSummaryVisible: () => Boolean(getCurrentRenderSnapshot()?.hasSummary),
37075
+ getSortState: () => core.storeFacade.sortState,
35391
37076
  getOptions: () => core.options,
35392
37077
  getLeafColumns: () => core.leafColumns,
35393
37078
  overlayController: core.overlayController,
@@ -35532,6 +37217,9 @@ function assembleListTableCoreControllers(core, options) {
35532
37217
  expandedRowKeys: core.storeFacade.expandedRowKeys
35533
37218
  });
35534
37219
  core.animationController.reconcileCollapseAnimations(snapshot);
37220
+ if (core.animationController.reconcileSortAnimations(snapshot)) {
37221
+ core.selectionController.reconcileSortSelection(snapshot.displayRows);
37222
+ }
35535
37223
  },
35536
37224
  getPublishAccessors: () => ({
35537
37225
  nestedTableLayoutRevision: core.animationController.getNestedTableLayoutRevision(),
@@ -35700,8 +37388,13 @@ function assembleListTableCoreControllers(core, options) {
35700
37388
  core.draggingResize = null;
35701
37389
  core.draggingReorder = null;
35702
37390
  core.suppressNextClick = false;
35703
- core.groupBarVisible = options.grouping?.visible ?? false;
35704
- core.groupColumnKeys = [...options.grouping?.columnKeys ?? []];
37391
+ core.defaultGroupColumnKeys = resolveDefaultGroupingColumnKeys(
37392
+ options.columns
37393
+ );
37394
+ core.groupBarVisible = options.grouping?.visible ?? core.defaultGroupColumnKeys.length > 0;
37395
+ core.groupColumnKeys = [
37396
+ ...options.grouping?.columnKeys ?? core.defaultGroupColumnKeys
37397
+ ];
35705
37398
  core.tableZoom = 1;
35706
37399
  }
35707
37400
  function collectGroupingColumnItems(columns) {
@@ -35816,6 +37509,7 @@ var ListTableCore = class {
35816
37509
  * @param options 新配置。
35817
37510
  */
35818
37511
  updateOptions(options) {
37512
+ const preserveContextMenu = options.dataSource !== this.options.dataSource || this.overlayController.shouldPreserveHeaderContextMenuOnOptionsUpdate();
35819
37513
  this.persistenceController.invalidate();
35820
37514
  if (options.storageKey === this.options.storageKey) {
35821
37515
  options = {
@@ -35827,6 +37521,8 @@ var ListTableCore = class {
35827
37521
  options = resolveListTableStyleOptions(options);
35828
37522
  this.animationController.stopCarouselScroll();
35829
37523
  const host = this.asAssemblyHost();
37524
+ const previousLeafColumns = host.leafColumns;
37525
+ const previousSegments = host.columnSegments;
35830
37526
  const shouldResetColumnConfig = shouldPersistIncomingColumnConfig(
35831
37527
  this.options.columnConfigResetRevision,
35832
37528
  options.columnConfigResetRevision
@@ -35855,15 +37551,37 @@ var ListTableCore = class {
35855
37551
  }
35856
37552
  applyInitialColumnVisibility(options.columns);
35857
37553
  ensureColumnKeysInPlace(options.columns);
35858
- if (options.expandable?.expandedRowKeys !== void 0 && !areSameRowKeySet(
37554
+ const expandedRowsChanged = options.expandable?.expandedRowKeys !== void 0 && !areSameRowKeySet(
35859
37555
  this.storeFacade.expandedRowKeys,
35860
37556
  options.expandable.expandedRowKeys
35861
- )) {
37557
+ );
37558
+ if (expandedRowsChanged) {
35862
37559
  this.animationController.setPendingCollapseRowPositions(
35863
37560
  captureRenderedVisibleRowPositions(
35864
37561
  this.renderController.getCurrentRenderSnapshot()
35865
37562
  )
35866
37563
  );
37564
+ } else if (shouldAnimateSortedDataUpdate({
37565
+ previous: this.options,
37566
+ next: options,
37567
+ sortState: this.storeFacade.sortState,
37568
+ expandedRowsChanged
37569
+ })) {
37570
+ if (this.animationController.hasPendingSortRows()) {
37571
+ this.animationController.markSortRowsReady();
37572
+ } else {
37573
+ this.selectionController.captureSortSelection(
37574
+ this.queryController.getDisplayData()
37575
+ );
37576
+ this.animationController.setPendingSortRowPositions(
37577
+ captureRenderedVisibleRowPositions(
37578
+ this.renderController.getCurrentRenderSnapshot()
37579
+ ),
37580
+ true
37581
+ );
37582
+ }
37583
+ } else if (options.dataSource !== this.options.dataSource) {
37584
+ this.animationController.markSortRowsReady();
35867
37585
  }
35868
37586
  this.queryController.reset();
35869
37587
  this.options = options;
@@ -35881,7 +37599,10 @@ var ListTableCore = class {
35881
37599
  });
35882
37600
  this.debugManager = new DebugManager(options.debug);
35883
37601
  host.renderer?.updateDebugOptions(options.debug ?? {});
35884
- this.hideFloatingOverlays({ includeLoading: true });
37602
+ this.hideFloatingOverlays({
37603
+ includeLoading: true,
37604
+ preserveContextMenu
37605
+ });
35885
37606
  this.refreshRuntimeColumns();
35886
37607
  if (shouldPersistColumnConfig) {
35887
37608
  this.persistenceController.persistColumnConfig();
@@ -35889,6 +37610,12 @@ var ListTableCore = class {
35889
37610
  this.persistenceController.loadStoredConfig({ restoreQueryState: false });
35890
37611
  }
35891
37612
  this.renderController.render();
37613
+ if (shouldPersistColumnConfig && haveVisibleLeafColumnsChanged(previousLeafColumns, host.leafColumns)) {
37614
+ host.rowActionController.startColumnVisibilityAnimation({
37615
+ previousLeafColumns,
37616
+ previousSegments
37617
+ });
37618
+ }
35892
37619
  host.tableId = options.tableId?.trim() ? options.tableId : host.tableId;
35893
37620
  host.rowDragController.syncRegistration();
35894
37621
  }
@@ -35926,6 +37653,14 @@ var ListTableCore = class {
35926
37653
  resetSorter() {
35927
37654
  this.headerActionController.resetSort();
35928
37655
  }
37656
+ /** 获取当前排序状态,供外部设置面板回显。 */
37657
+ getSortState() {
37658
+ return [...this.storeFacade.sortState];
37659
+ }
37660
+ /** 使用与表头一致的排序逻辑设置指定列。 */
37661
+ setSortOrder(columnKey, order) {
37662
+ this.headerActionController.setSortOrder(columnKey, order);
37663
+ }
35929
37664
  /**
35930
37665
  * 重置筛选状态。
35931
37666
  */
@@ -36031,14 +37766,15 @@ var ListTableCore = class {
36031
37766
  return this;
36032
37767
  }
36033
37768
  hideFloatingOverlays(params) {
36034
- this.componentPortal.hideFloatingLayers();
36035
37769
  if (params?.includeLoading) {
36036
37770
  this.overlayController.hideLoading();
36037
37771
  }
36038
37772
  this.overlayController.hideTooltip();
36039
37773
  this.overlayController.hideFilterPanel();
36040
37774
  this.overlayController.hideFixedPopup();
36041
- this.overlayController.hideHeaderContextMenu();
37775
+ if (!params?.preserveContextMenu) {
37776
+ this.overlayController.hideHeaderContextMenu();
37777
+ }
36042
37778
  }
36043
37779
  refreshRuntimeColumns() {
36044
37780
  this.storeFacade.runtimeColumns = buildRuntimeColumnsMap({
@@ -36120,6 +37856,14 @@ var ListTable = class {
36120
37856
  resetSorter() {
36121
37857
  this.core?.resetSorter();
36122
37858
  }
37859
+ /** 获取当前排序状态。 */
37860
+ getSortState() {
37861
+ return this.core?.getSortState() ?? [];
37862
+ }
37863
+ /** 设置指定列的排序方式。 */
37864
+ setSortOrder(columnKey, order) {
37865
+ this.core?.setSortOrder(columnKey, order);
37866
+ }
36123
37867
  /**
36124
37868
  * 重置筛选状态。
36125
37869
  */