@canvas-components/list-table 0.3.24 → 0.3.26

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() {
@@ -2134,7 +2157,7 @@ function createEmptySelection() {
2134
2157
  };
2135
2158
  }
2136
2159
  function resolveAdjacentCellSelection(params) {
2137
- const { selection, rowCount, columnKeys, direction } = params;
2160
+ const { selection, rowCount, columnKeys, direction, verticalWrap = true } = params;
2138
2161
  if (selection.rowIndex == null || selection.columnKey == null || selection.rowIndex < 0 || selection.rowIndex >= rowCount) {
2139
2162
  return null;
2140
2163
  }
@@ -2143,6 +2166,10 @@ function resolveAdjacentCellSelection(params) {
2143
2166
  return null;
2144
2167
  }
2145
2168
  if (direction === "up" || direction === "down") {
2169
+ const nextRowIndex = selection.rowIndex + (direction === "up" ? -1 : 1);
2170
+ if (!verticalWrap) {
2171
+ return nextRowIndex < 0 || nextRowIndex >= rowCount ? null : { rowIndex: nextRowIndex, columnKey: selection.columnKey };
2172
+ }
2146
2173
  const flatIndex = columnIndex * rowCount + selection.rowIndex;
2147
2174
  const nextFlatIndex = flatIndex + (direction === "up" ? -1 : 1);
2148
2175
  const cellCount = rowCount * columnKeys.length;
@@ -2165,7 +2192,7 @@ function resolveAdjacentCellSelection(params) {
2165
2192
  };
2166
2193
  }
2167
2194
  function resolveTabCellSelection(params) {
2168
- const { selection, rowCount, columnKeys, reverse = false } = params;
2195
+ const { selection, rowCount, columnKeys, reverse = false, tabWrap = true } = params;
2169
2196
  if (selection.rowIndex == null || selection.columnKey == null || selection.rowIndex < 0 || selection.rowIndex >= rowCount || columnKeys.length === 0) {
2170
2197
  return null;
2171
2198
  }
@@ -2173,6 +2200,13 @@ function resolveTabCellSelection(params) {
2173
2200
  if (columnIndex < 0) {
2174
2201
  return null;
2175
2202
  }
2203
+ if (!tabWrap) {
2204
+ const nextColumnIndex = columnIndex + (reverse ? -1 : 1);
2205
+ return nextColumnIndex < 0 || nextColumnIndex >= columnKeys.length ? null : {
2206
+ rowIndex: selection.rowIndex,
2207
+ columnKey: columnKeys[nextColumnIndex]
2208
+ };
2209
+ }
2176
2210
  const flatIndex = selection.rowIndex * columnKeys.length + columnIndex;
2177
2211
  const nextFlatIndex = flatIndex + (reverse ? -1 : 1);
2178
2212
  const cellCount = rowCount * columnKeys.length;
@@ -3287,14 +3321,17 @@ function mapKindToArea(kind, actionType) {
3287
3321
  }
3288
3322
  function getPriority(descriptor) {
3289
3323
  let priority = getBasePriority(descriptor.kind);
3324
+ if (descriptor.debugNodeType === "header-edit-icon" || descriptor.debugNodeType === "nested-table-header-edit-icon") {
3325
+ priority += 1;
3326
+ }
3290
3327
  if (descriptor.kind === "summary" && descriptor.columnKey) {
3291
3328
  priority += 1;
3292
3329
  }
3293
3330
  if ((descriptor.kind === "body" || descriptor.kind === "body-content" || descriptor.kind === "body-control") && descriptor.frozen) {
3294
3331
  priority += 2;
3295
3332
  }
3296
- if (descriptor.kind === "body" && descriptor.fixed) {
3297
- priority += 1;
3333
+ if ((descriptor.kind === "body" || descriptor.kind === "body-content" || descriptor.kind === "body-control") && descriptor.fixed) {
3334
+ priority += 2;
3298
3335
  }
3299
3336
  if (isHeaderDescriptor(descriptor.kind) && descriptor.fixed) {
3300
3337
  priority += 3;
@@ -3314,6 +3351,9 @@ function getBasePriority(kind) {
3314
3351
  return 2;
3315
3352
  case "resize-handle":
3316
3353
  return 4;
3354
+ // 折叠列图标覆盖列宽热区,但仍低于固定列表头的遮挡层(优先级 5)。
3355
+ case "column-collapse-trigger":
3356
+ return 4.5;
3317
3357
  case "header-action":
3318
3358
  return 3;
3319
3359
  case "header-selection":
@@ -3326,7 +3366,6 @@ function getBasePriority(kind) {
3326
3366
  return 2;
3327
3367
  case "expand-trigger":
3328
3368
  case "collapse-trigger":
3329
- case "column-collapse-trigger":
3330
3369
  return 3;
3331
3370
  case "expanded-row":
3332
3371
  case "body":
@@ -3485,6 +3524,60 @@ function applyListTableRenderPassResult(params) {
3485
3524
  params.setScrollPageSize(projection.scrollPageSize);
3486
3525
  }
3487
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
+
3488
3581
  // src/rendering/cell-content/content-style.ts
3489
3582
  function resolveContentFontSize(content, fallbackFontSize) {
3490
3583
  return content.style?.fontSize ?? content.fontSize ?? fallbackFontSize;
@@ -3545,7 +3638,7 @@ function buildCanvasFont(params) {
3545
3638
  return `${fontWeight} ${fontSize}px ${fontFamily}`.trim();
3546
3639
  }
3547
3640
  function measureTextBounds(ctx, text) {
3548
- const metrics = ctx.measureText(text);
3641
+ const metrics = measureCachedText(ctx, text);
3549
3642
  const ascent = metrics.actualBoundingBoxAscent || 0;
3550
3643
  const descent = metrics.actualBoundingBoxDescent || 0;
3551
3644
  const width = metrics.width;
@@ -5200,11 +5293,41 @@ function drawHeaderActionGlyph(ctx, rect, actionType, color = "#8c8c8c", sortOrd
5200
5293
  ctx.fill();
5201
5294
  } else if (actionType === "filter") {
5202
5295
  drawFilterIcon(ctx, rect);
5203
- } else {
5296
+ } else if (actionType === "fixed") {
5204
5297
  drawFixedPinIcon(ctx, rect);
5298
+ } else {
5299
+ drawEditIcon(ctx, rect);
5205
5300
  }
5206
5301
  ctx.restore();
5207
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
+ }
5208
5331
  function drawFilterIcon(ctx, rect) {
5209
5332
  const iconWidth = rect.width * 0.92;
5210
5333
  const iconHeight = rect.height * 0.7;
@@ -7455,6 +7578,19 @@ function sortRows(rows, sortState, leafColumns) {
7455
7578
  return 0;
7456
7579
  });
7457
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
+ }
7458
7594
  async function sortRowIndicesAsync(params) {
7459
7595
  let rowIndices = params.rowIndices ?? null;
7460
7596
  const sorters = orderSortingState(params.sortState);
@@ -7698,6 +7834,21 @@ async function mapToSourceRowIndices(params) {
7698
7834
  recordQueryStageDuration(params.trace, "indexMapping", mappingStartedAt);
7699
7835
  return result;
7700
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
+ }
7701
7852
  function isAbortError(error) {
7702
7853
  return error instanceof Error && error.name === "AbortError";
7703
7854
  }
@@ -8090,12 +8241,17 @@ function toQueryFilters(filters) {
8090
8241
  );
8091
8242
  }
8092
8243
  function toQuerySorter(sortState, leafColumns) {
8093
- return orderSortingState(sortState).map((item) => ({
8094
- columnKey: item.key,
8095
- field: leafColumns.find((column) => column.key === item.key)?.dataIndex,
8096
- order: item.order,
8097
- priority: item.priority
8098
- }));
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
+ });
8099
8255
  }
8100
8256
  function getActiveFilterColumnKeys(filters) {
8101
8257
  return Object.entries(filters).filter(([, value]) => isMeaningfulFilterValue(value)).map(([columnKey]) => columnKey);
@@ -8468,6 +8624,7 @@ function resolveHeaderTextLayout(params) {
8468
8624
  actionVisibilityState,
8469
8625
  allowActions,
8470
8626
  hasFilter,
8627
+ hasEdit = false,
8471
8628
  contentInsetStart = 0,
8472
8629
  contentInsetEnd = 0,
8473
8630
  paddingInline = HEADER_TEXT_PADDING
@@ -8499,14 +8656,23 @@ function resolveHeaderTextLayout(params) {
8499
8656
  maxWidth: Math.max(contentVisibleRect.width - paddingInline * 2, 12),
8500
8657
  x: baseX
8501
8658
  };
8502
- if (!allowActions || !actionVisibilityState.visible || align === "left") {
8659
+ if (!allowActions || !actionVisibilityState.visible) {
8503
8660
  return baseLayout;
8504
8661
  }
8505
8662
  const actionLayout = getHeaderActionLayout({
8506
8663
  rect,
8507
8664
  hasFilter,
8665
+ hasEdit,
8666
+ visibilityState: actionVisibilityState,
8508
8667
  trailingInset: contentInsetEnd
8509
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
+ }
8510
8676
  const textRight = Math.min(
8511
8677
  contentVisibleRect.x + contentVisibleRect.width - paddingInline,
8512
8678
  actionLayout.startX - paddingInline
@@ -8537,11 +8703,14 @@ function resolveHeaderTextLayout(params) {
8537
8703
  };
8538
8704
  }
8539
8705
  function getHeaderActionLayout(params) {
8540
- const actionTypes = ["sort"];
8541
- if (params.hasFilter) {
8542
- actionTypes.push("filter");
8543
- }
8544
- 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");
8545
8714
  const totalWidth = ACTION_SIZE * actionTypes.length + ACTION_GAP * Math.max(actionTypes.length - 1, 0);
8546
8715
  return {
8547
8716
  actionTypes,
@@ -8592,6 +8761,9 @@ function drawHeaderLayer(params, layer) {
8592
8761
  if (cell.layer !== layer) {
8593
8762
  return;
8594
8763
  }
8764
+ if (layer === "center" && centerClipRect && (cell.x + cell.width < centerClipRect.x - 1 || cell.x > centerClipRect.x + centerClipRect.width + 1)) {
8765
+ return;
8766
+ }
8595
8767
  if (layer === "center" && centerClipRect) {
8596
8768
  params.ctx.save();
8597
8769
  params.ctx.beginPath();
@@ -8637,7 +8809,11 @@ function drawHeaderCell(params, cell, clipRect) {
8637
8809
  };
8638
8810
  ctx.save();
8639
8811
  const isSelected = isHeaderCellSelected(params, cell.node.key, rect);
8640
- const isRangeSelected = isHeaderCellInRange(params.rangeHighlight, cell);
8812
+ const isRangeSelected = isHeaderCellInRange(
8813
+ params.rangeHighlight,
8814
+ cell,
8815
+ params.leafColumnKeys
8816
+ );
8641
8817
  ctx.fillStyle = isSelected || isRangeSelected ? theme.selectionBackgroundColor : cell.node.style?.backgroundColor ?? theme.headerBackgroundColor;
8642
8818
  ctx.fillRect(rect.x, rect.y, rect.width, rect.height);
8643
8819
  drawGridRect(
@@ -8661,13 +8837,15 @@ function drawHeaderCell(params, cell, clipRect) {
8661
8837
  );
8662
8838
  const isExpandSystemColumn = cell.node.children.length === 0 && cell.node.column.meta?.systemType === "expandable";
8663
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;
8664
8841
  const visibleState = getHeaderActionVisibilityState(
8665
8842
  cell.node.key,
8666
8843
  params.hoveredColumnKey,
8667
8844
  params.sortState.find((item) => item.key === cell.node.key)?.key ?? null,
8668
8845
  params.filteredColumnKeys,
8669
8846
  params.filterableColumnKeys,
8670
- params.fixedColumnKeys
8847
+ params.fixedColumnKeys,
8848
+ isEditableLeaf
8671
8849
  );
8672
8850
  const titleContent = cell.node.titleContent ?? [
8673
8851
  { type: "text", text: cell.node.title }
@@ -8679,6 +8857,7 @@ function drawHeaderCell(params, cell, clipRect) {
8679
8857
  actionVisibilityState: visibleState,
8680
8858
  allowActions: cell.node.children.length === 0,
8681
8859
  hasFilter: params.filterableColumnKeys.has(cell.node.key),
8860
+ hasEdit: isEditableLeaf,
8682
8861
  contentWidth: measureHeaderContentWidth(ctx, titleContent),
8683
8862
  paddingInline: cell.node.style?.paddingInline,
8684
8863
  contentInsetStart: resolveHeaderContentInsetStart({
@@ -8711,6 +8890,7 @@ function drawHeaderCell(params, cell, clipRect) {
8711
8890
  rect: getHeaderSelectionRect(rect),
8712
8891
  columnKey: cell.node.key,
8713
8892
  controlType: params.rowSelection.type,
8893
+ cursor: params.rowSelection.disabled ? "not-allowed" : "pointer",
8714
8894
  fixed: cell.node.fixed
8715
8895
  });
8716
8896
  }
@@ -8801,7 +8981,9 @@ function drawHeaderCell(params, cell, clipRect) {
8801
8981
  cell.node.key,
8802
8982
  visibleState,
8803
8983
  cell.node.fixed,
8804
- visibleRect
8984
+ visibleRect,
8985
+ isEditableLeaf,
8986
+ isSelected || isRangeSelected
8805
8987
  );
8806
8988
  drawResizeHandles(params, rect, cell.node.key, cell.node.fixed);
8807
8989
  }
@@ -8837,11 +9019,11 @@ function resolveHeaderContentCenterY(rect, contents, fontSize, paddingBlock, ver
8837
9019
  function measureHeaderContentWidth(ctx, contents) {
8838
9020
  const gap = 4;
8839
9021
  return contents.reduce(
8840
- (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),
8841
9023
  0
8842
9024
  ) + Math.max(contents.length - 1, 0) * gap;
8843
9025
  }
8844
- function drawHeaderActions(params, rect, columnKey, visibilityState, fixed, visibleRect = rect) {
9026
+ function drawHeaderActions(params, rect, columnKey, visibilityState, fixed, visibleRect = rect, hasEdit = false, selected = false) {
8845
9027
  if (!visibilityState.visible) {
8846
9028
  return;
8847
9029
  }
@@ -8853,6 +9035,8 @@ function drawHeaderActions(params, rect, columnKey, visibilityState, fixed, visi
8853
9035
  const actionLayout = getHeaderActionLayout({
8854
9036
  rect,
8855
9037
  hasFilter: params.filterableColumnKeys.has(columnKey),
9038
+ hasEdit,
9039
+ visibilityState,
8856
9040
  trailingInset: resolveCollapsedColumnMarkerSet(
8857
9041
  params.collapsedColumnMarkers,
8858
9042
  columnKey
@@ -8860,7 +9044,7 @@ function drawHeaderActions(params, rect, columnKey, visibilityState, fixed, visi
8860
9044
  });
8861
9045
  const { actionTypes, startX, totalWidth } = actionLayout;
8862
9046
  const centerY = rect.y + rect.height / 2;
8863
- const headerSelected = isHeaderCellSelected(params, columnKey, rect);
9047
+ const headerSelected = selected || isHeaderCellSelected(params, columnKey, rect);
8864
9048
  ctx.save();
8865
9049
  ctx.fillStyle = headerSelected ? params.theme.selectionBackgroundColor : params.theme.headerBackgroundColor;
8866
9050
  ctx.beginPath();
@@ -8882,6 +9066,28 @@ function drawHeaderActions(params, rect, columnKey, visibilityState, fixed, visi
8882
9066
  width: ACTION_SIZE2,
8883
9067
  height: ACTION_SIZE2
8884
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
+ }
8885
9091
  const iconHovered = params.hoveredColumnKey === columnKey && params.hoveredActionType === (actionType === "sort" ? "sort-trigger" : actionType === "filter" ? "filter-trigger" : "fixed-trigger");
8886
9092
  drawHeaderActionIcon(
8887
9093
  ctx,
@@ -8900,6 +9106,7 @@ function drawHeaderActions(params, rect, columnKey, visibilityState, fixed, visi
8900
9106
  rect: hitRect,
8901
9107
  columnKey,
8902
9108
  actionType,
9109
+ cursor: "pointer",
8903
9110
  visibilityState,
8904
9111
  fixed
8905
9112
  });
@@ -8959,11 +9166,16 @@ function isHeaderCellSelected(params, columnKey, rect) {
8959
9166
  }
8960
9167
  return isSameRect(params.selectedHeaderRect, rect);
8961
9168
  }
8962
- function isHeaderCellInRange(rangeHighlight, cell) {
8963
- const range = (rangeHighlight ?? EMPTY_RANGE_HIGHLIGHT).selectedColumnRange;
8964
- if (!range) {
8965
- 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);
8966
9176
  }
9177
+ const range = state.selectedColumnRange;
9178
+ if (!range) return false;
8967
9179
  return cell.node.leafStartIndex <= range.end && cell.node.leafEndIndex >= range.start;
8968
9180
  }
8969
9181
  function isSameRect(left, right) {
@@ -9028,7 +9240,7 @@ function getHeaderSelectionRect(rect) {
9028
9240
  height: size
9029
9241
  };
9030
9242
  }
9031
- function getHeaderActionVisibilityState(columnKey, hoveredColumnKey, sortedColumnKey, filteredColumnKeys, filterableColumnKeys, fixedColumnKeys) {
9243
+ function getHeaderActionVisibilityState(columnKey, hoveredColumnKey, sortedColumnKey, filteredColumnKeys, filterableColumnKeys, fixedColumnKeys, editable) {
9032
9244
  const hovered = hoveredColumnKey === columnKey;
9033
9245
  const sorted = sortedColumnKey === columnKey;
9034
9246
  const filtered = filterableColumnKeys.has(columnKey) && filteredColumnKeys.has(columnKey);
@@ -9038,7 +9250,7 @@ function getHeaderActionVisibilityState(columnKey, hoveredColumnKey, sortedColum
9038
9250
  sorted,
9039
9251
  filtered,
9040
9252
  fixed,
9041
- visible: hovered || sorted || filtered || fixed
9253
+ visible: hovered || sorted || filtered || fixed || editable
9042
9254
  };
9043
9255
  }
9044
9256
  function getActionActive(actionType, visibilityState) {
@@ -9073,7 +9285,7 @@ function drawHeaderActionIcon(ctx, rect, actionType, active, hovered = false, se
9073
9285
  function drawHeaderContent(ctx, contents, x, y, maxWidth, align, requestRender) {
9074
9286
  const gap = 4;
9075
9287
  const widths = contents.map(
9076
- (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
9077
9289
  );
9078
9290
  const totalWidth = widths.reduce((sum, width) => sum + width, 0) + Math.max(contents.length - 1, 0) * gap;
9079
9291
  const displayWidth = Math.min(totalWidth, Math.max(maxWidth, 0));
@@ -9098,19 +9310,19 @@ function drawHeaderContent(ctx, contents, x, y, maxWidth, align, requestRender)
9098
9310
  ctx.textAlign = "left";
9099
9311
  const text = ellipsisText(ctx, content.text, availableWidth);
9100
9312
  ctx.fillText(text, cursorX, y);
9101
- cursorX += Math.min(ctx.measureText(text).width, availableWidth) + gap;
9313
+ cursorX += Math.min(measureCachedText(ctx, text).width, availableWidth) + gap;
9102
9314
  ctx.restore();
9103
9315
  });
9104
9316
  }
9105
9317
  function ellipsisText(ctx, text, maxWidth) {
9106
- if (ctx.measureText(text).width <= maxWidth) {
9318
+ if (measureCachedText(ctx, text).width <= maxWidth) {
9107
9319
  return text;
9108
9320
  }
9109
9321
  const ellipsis = "...";
9110
9322
  let content = text;
9111
9323
  while (content.length > 0) {
9112
9324
  content = content.slice(0, -1);
9113
- const measured = ctx.measureText(`${content}${ellipsis}`).width;
9325
+ const measured = measureCachedText(ctx, `${content}${ellipsis}`).width;
9114
9326
  if (measured <= maxWidth) {
9115
9327
  return `${content}${ellipsis}`;
9116
9328
  }
@@ -9214,6 +9426,544 @@ function findCellByKey(headerRows, columnKey) {
9214
9426
  }
9215
9427
  return null;
9216
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
9217
9967
  var EMPTY_RANGE_HIGHLIGHT2 = {
9218
9968
  selectedColumnRange: null
9219
9969
  };
@@ -9262,6 +10012,9 @@ function drawSummarySegment(params, columns, clipRect, defaultTitleColumnKey) {
9262
10012
  ctx.rect(minX, summaryTop, totalWidth, summaryHeight);
9263
10013
  ctx.clip();
9264
10014
  columns.forEach((column) => {
10015
+ if (clipRect && (column.drawX + column.width < clipRect.x - 1 || column.drawX > clipRect.x + clipRect.width + 1)) {
10016
+ return;
10017
+ }
9265
10018
  const rect = {
9266
10019
  x: column.drawX,
9267
10020
  y: summaryTop,
@@ -9299,9 +10052,10 @@ function drawSummaryCell(ctx, rect, column, theme, summaryValues, selectedHeader
9299
10052
  return;
9300
10053
  }
9301
10054
  const isHeaderColumnSelected = selectedHeaderKey != null && column.key === selectedHeaderKey;
9302
- const isColumnRangeSelected = isIndexInHighlightRange(
9303
- column.leafIndex,
9304
- (rangeHighlight ?? EMPTY_RANGE_HIGHLIGHT2).selectedColumnRange
10055
+ const isColumnRangeSelected = isColumnInHighlight(
10056
+ rangeHighlight ?? EMPTY_RANGE_HIGHLIGHT2,
10057
+ column.key,
10058
+ column.leafIndex
9305
10059
  );
9306
10060
  ctx.fillStyle = summarySelected || isHeaderColumnSelected || isColumnRangeSelected ? theme.selectionBackgroundColor : style?.backgroundColor ?? theme.headerBackgroundColor;
9307
10061
  ctx.fillRect(rect.x, rect.y, rect.width, rect.height);
@@ -9514,9 +10268,6 @@ function intersectRects2(left, right) {
9514
10268
  height: maxY - y
9515
10269
  };
9516
10270
  }
9517
- function isIndexInHighlightRange(index, range) {
9518
- return Boolean(range && index >= range.start && index <= range.end);
9519
- }
9520
10271
 
9521
10272
  // src/rendering/cell-content/draw-nested-table-content.ts
9522
10273
  var DEFAULT_COLUMN_WIDTH2 = 140;
@@ -9586,7 +10337,8 @@ function drawBodyNestedTableContent(params) {
9586
10337
  align: column.align ?? "left",
9587
10338
  actionVisibilityState,
9588
10339
  allowActions: true,
9589
- hasFilter: column.allowFilter === true
10340
+ hasFilter: column.allowFilter === true,
10341
+ hasEdit: !!column.editable
9590
10342
  });
9591
10343
  const headerHighlighted = isNestedTableHeaderHighlighted(
9592
10344
  content,
@@ -9966,7 +10718,9 @@ function drawNestedTableHeaderActions(params) {
9966
10718
  if (!visibilityState.visible) return;
9967
10719
  const { actionTypes, startX, totalWidth } = getHeaderActionLayout({
9968
10720
  rect,
9969
- hasFilter: column.allowFilter === true
10721
+ hasFilter: column.allowFilter === true,
10722
+ hasEdit: !!column.editable,
10723
+ visibilityState
9970
10724
  });
9971
10725
  drawParams.ctx.save();
9972
10726
  drawParams.ctx.fillStyle = headerSelected ? drawParams.theme.selectionBackgroundColor : drawParams.theme.headerBackgroundColor;
@@ -9987,6 +10741,33 @@ function drawNestedTableHeaderActions(params) {
9987
10741
  width: size,
9988
10742
  height: size
9989
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
+ }
9990
10771
  const active = actionType === "sort" && sortOrder != null || actionType === "filter" && filtered || actionType === "fixed" && fixed != null;
9991
10772
  drawHeaderActionIcon(
9992
10773
  drawParams.ctx,
@@ -10030,7 +10811,7 @@ function resolveNestedTableHeaderActionVisibility(params) {
10030
10811
  sorted,
10031
10812
  filtered,
10032
10813
  fixed,
10033
- visible: hovered || sorted || filtered || fixed
10814
+ visible: hovered || sorted || filtered || fixed || !!column.editable
10034
10815
  };
10035
10816
  }
10036
10817
  function isNestedTableRowHighlighted(content, record, rowIndex, highlight, highlightMode) {
@@ -11099,7 +11880,7 @@ function drawBodySelectionContent(params) {
11099
11880
  controlType: content.type,
11100
11881
  contentIndex,
11101
11882
  contentKey: content.key,
11102
- cursor: content.disabled === true ? "not-allowed" : content.cursor ?? (content.onClick ? "pointer" : void 0)
11883
+ cursor: content.disabled === true ? "not-allowed" : content.cursor ?? (content.onClick || column.key === ROW_SELECTION_COLUMN_KEY ? "pointer" : void 0)
11103
11884
  });
11104
11885
  return controlRect.width;
11105
11886
  }
@@ -11115,7 +11896,14 @@ function drawSelectionControl(ctx, rect, type, checked, disabled, indeterminate
11115
11896
  ctx.fillStyle = fillColor;
11116
11897
  ctx.strokeStyle = strokeColor;
11117
11898
  ctx.lineWidth = 1.5;
11118
- roundRectPath2(ctx, rect.x, rect.y, rect.width, rect.height, 4);
11899
+ roundRectPath2(
11900
+ ctx,
11901
+ rect.x + 1,
11902
+ rect.y + 1,
11903
+ Math.max(rect.width - 2, 0),
11904
+ Math.max(rect.height - 2, 0),
11905
+ 4
11906
+ );
11119
11907
  ctx.fill();
11120
11908
  ctx.stroke();
11121
11909
  if (indeterminate) {
@@ -11599,7 +12387,7 @@ function drawBodyTagContent(params) {
11599
12387
  function measureTagContentWidth(ctx, content, theme) {
11600
12388
  applyTagTextStyle(ctx, content, theme);
11601
12389
  const intrinsicWidth = Math.max(
11602
- ctx.measureText(content.text).width + resolveContentTextIndent(content),
12390
+ measureCachedText(ctx, content.text).width + resolveContentTextIndent(content),
11603
12391
  0
11604
12392
  ) + (content.paddingInline ?? TAG_HORIZONTAL_PADDING) * 2;
11605
12393
  return resolveContentLayoutWidth(content, intrinsicWidth);
@@ -11739,8 +12527,8 @@ function drawBodyTextContent(params) {
11739
12527
  if (searchMatch && searchMatch.rowIndex === rowIndex && searchMatch.columnKey === column.key && (searchMatch.contentIndex == null || searchMatch.contentIndex === contentIndex) && searchMatch.keyword) {
11740
12528
  const start = Math.max(0, Math.min(searchMatch.start, content.text.length));
11741
12529
  const end = Math.max(start, Math.min(searchMatch.end, content.text.length));
11742
- const prefixWidth = ctx.measureText(content.text.slice(0, start)).width;
11743
- 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;
11744
12532
  ctx.save();
11745
12533
  ctx.fillStyle = "#f59e0b";
11746
12534
  ctx.globalAlpha = 0.35;
@@ -11813,7 +12601,7 @@ function measureTextContentWidth(ctx, content, theme) {
11813
12601
  }
11814
12602
  applyTextContentStyle(ctx, content, theme.textColor, theme);
11815
12603
  const intrinsicWidth = Math.max(
11816
- ctx.measureText(content.text).width + resolveContentTextIndent(content),
12604
+ measureCachedText(ctx, content.text).width + resolveContentTextIndent(content),
11817
12605
  0
11818
12606
  );
11819
12607
  return resolveContentLayoutWidth(content, intrinsicWidth);
@@ -15112,6 +15900,7 @@ function createListTableAnimationRuntime() {
15112
15900
  lastExpandedRowKeys: /* @__PURE__ */ new Set(),
15113
15901
  expandAnimationFrameId: null,
15114
15902
  collapseRowAnimations: /* @__PURE__ */ new Map(),
15903
+ sortRowAnimations: /* @__PURE__ */ new Map(),
15115
15904
  collapseCellHeightAnimations: /* @__PURE__ */ new Map(),
15116
15905
  collapseAnimationFrameId: null,
15117
15906
  nestedTableHighlights: /* @__PURE__ */ new Map(),
@@ -16181,8 +16970,11 @@ function resolveCollapsedRowRange(params) {
16181
16970
 
16182
16971
  // src/engine/controllers/animation-controller.ts
16183
16972
  var ROW_EXPAND_ANIMATION_DURATION = 240;
16973
+ var ROW_SORT_ANIMATION_DURATION = 420;
16184
16974
  var ListTableAnimationController = class {
16185
16975
  constructor(options) {
16976
+ __publicField(this, "pendingSortRowPositions", null);
16977
+ __publicField(this, "sortRowsReady", false);
16186
16978
  __publicField(this, "runtime");
16187
16979
  __publicField(this, "getScrollbarVisibleOption");
16188
16980
  __publicField(this, "isScrollbarVisible");
@@ -16329,6 +17121,37 @@ var ListTableAnimationController = class {
16329
17121
  setPendingCollapseRowPositions(positions) {
16330
17122
  this.runtime.pendingCollapseRowPositions = positions;
16331
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
+ }
16332
17155
  reconcileCollapseAnimations(snapshot) {
16333
17156
  const previousPositions = this.runtime.pendingCollapseRowPositions;
16334
17157
  if (!previousPositions) {
@@ -16397,11 +17220,12 @@ var ListTableAnimationController = class {
16397
17220
  return getRowAnimationValue(animation, this.scheduler.now());
16398
17221
  }
16399
17222
  getCollapseRowOffset(rowKey) {
16400
- const animation = this.runtime.collapseRowAnimations.get(String(rowKey));
16401
- if (!animation) {
16402
- return void 0;
16403
- }
16404
- 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);
16405
17229
  }
16406
17230
  getCollapseCellHeight(params) {
16407
17231
  const animation = this.runtime.collapseCellHeightAnimations.get(
@@ -16430,13 +17254,16 @@ var ListTableAnimationController = class {
16430
17254
  columnKey: this.runtime.controlAnimationColumnKey,
16431
17255
  displayRows: snapshot.displayRows,
16432
17256
  nextWindowKey: buildControlAnimationWindowKey(snapshot),
16433
- nextColumnKey: buildControlAnimationColumnKey(snapshot.leafColumns)
17257
+ nextColumnKey: buildControlAnimationColumnKey(
17258
+ collectVisibleControlColumns(snapshot)
17259
+ )
16434
17260
  });
16435
17261
  }
16436
17262
  reconcileControlAnimations(snapshot, resolveCellContents = resolveBodyCellContents) {
16437
17263
  const now2 = this.scheduler.now();
16438
17264
  this.pruneFinishedControlAnimations(now2);
16439
17265
  const nextValues = /* @__PURE__ */ new Map();
17266
+ const visibleColumns = collectVisibleControlColumns(snapshot);
16440
17267
  forEachVisibleSnapshotRow(
16441
17268
  {
16442
17269
  frozenRows: snapshot.frozenRows,
@@ -16446,7 +17273,7 @@ var ListTableAnimationController = class {
16446
17273
  },
16447
17274
  (record, rowIndex) => {
16448
17275
  const rowKey = this.getRowKey(record, rowIndex);
16449
- collectVisibleControlColumns(snapshot).forEach((column) => {
17276
+ visibleColumns.forEach((column) => {
16450
17277
  const contents = resolveCellContents({
16451
17278
  record,
16452
17279
  rowIndex,
@@ -16489,9 +17316,7 @@ var ListTableAnimationController = class {
16489
17316
  this.runtime.lastControlValues = nextValues;
16490
17317
  this.runtime.controlAnimationRowsRef = snapshot.displayRows;
16491
17318
  this.runtime.controlAnimationWindowKey = buildControlAnimationWindowKey(snapshot);
16492
- this.runtime.controlAnimationColumnKey = buildControlAnimationColumnKey(
16493
- snapshot.leafColumns
16494
- );
17319
+ this.runtime.controlAnimationColumnKey = buildControlAnimationColumnKey(visibleColumns);
16495
17320
  Array.from(this.runtime.controlAnimations.keys()).forEach((key) => {
16496
17321
  if (!nextValues.has(key)) {
16497
17322
  this.runtime.controlAnimations.delete(key);
@@ -16869,7 +17694,7 @@ var ListTableAnimationController = class {
16869
17694
  );
16870
17695
  }
16871
17696
  ensureCollapseAnimationFrame() {
16872
- 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) {
16873
17698
  return;
16874
17699
  }
16875
17700
  this.runtime.collapseAnimationFrameId = this.scheduler.requestAnimationFrame(() => {
@@ -16893,8 +17718,11 @@ var ListTableAnimationController = class {
16893
17718
  this.runtime.collapseAnimationFrameId = null;
16894
17719
  }
16895
17720
  this.runtime.collapseRowAnimations.clear();
17721
+ this.runtime.sortRowAnimations.clear();
16896
17722
  this.runtime.collapseCellHeightAnimations.clear();
16897
17723
  this.runtime.pendingCollapseRowPositions = null;
17724
+ this.pendingSortRowPositions = null;
17725
+ this.sortRowsReady = false;
16898
17726
  }
16899
17727
  destroy() {
16900
17728
  this.stopScrollbarOpacityAnimation();
@@ -17001,6 +17829,7 @@ var ListTableAnimationController = class {
17001
17829
  }
17002
17830
  pruneFinishedCollapseAnimations(now2) {
17003
17831
  pruneFinishedAnimations(this.runtime.collapseRowAnimations, now2);
17832
+ pruneFinishedAnimations(this.runtime.sortRowAnimations, now2);
17004
17833
  pruneFinishedAnimations(this.runtime.collapseCellHeightAnimations, now2);
17005
17834
  }
17006
17835
  };
@@ -17011,7 +17840,11 @@ function collectVisibleControlColumns(snapshot) {
17011
17840
  }
17012
17841
  const columns = [
17013
17842
  ...segments.leftSegment,
17014
- ...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
+ }),
17015
17848
  ...segments.rightSegment
17016
17849
  ];
17017
17850
  const seen = /* @__PURE__ */ new Set();
@@ -17830,8 +18663,11 @@ function createListTableSelectionCopyText(params) {
17830
18663
  (column2) => column2.key !== ROW_SELECTION_COLUMN_KEY && column2.key !== ROW_SELECTOR_COLUMN_KEY && column2.key !== EXPAND_COLUMN_KEY
17831
18664
  );
17832
18665
  const columnRange = params.rangeHighlight.selectedColumnRange;
18666
+ const selectedColumnKeys = params.rangeHighlight.selectedColumnKeys;
17833
18667
  const rowRange = params.rangeHighlight.selectedRowRange;
18668
+ const rowRanges = params.rangeHighlight.selectedRowRanges;
17834
18669
  const cellRange = params.rangeHighlight.selectedCellRange;
18670
+ const cellRanges = params.rangeHighlight.selectedCellRanges;
17835
18671
  if (cellRange) {
17836
18672
  const selectedColumns2 = params.columns.filter(
17837
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
@@ -17852,22 +18688,57 @@ function createListTableSelectionCopyText(params) {
17852
18688
  return `${headerText}
17853
18689
  ${bodyText}`;
17854
18690
  }
17855
- const mode = params.highlightMode ?? (rowRange && columnRange ? "cross" : rowRange ? "row" : "column");
17856
- 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(
17857
18726
  (column2, index) => index >= columnRange.start && index <= columnRange.end
17858
18727
  ).filter(
17859
18728
  (column2) => column2.key !== ROW_SELECTION_COLUMN_KEY && column2.key !== ROW_SELECTOR_COLUMN_KEY && column2.key !== EXPAND_COLUMN_KEY
17860
18729
  ) : columns;
17861
18730
  const startRow = rowRange ? Math.max(rowRange.start, 0) : 0;
17862
18731
  const endRow = rowRange ? Math.min(rowRange.end, params.rows.length - 1) : params.rows.length - 1;
17863
- if (rowRange && params.selection.columnKey === ROW_SELECTOR_COLUMN_KEY) {
17864
- return params.rows.slice(startRow, endRow + 1).map(
17865
- (record2, offset) => columns.map(
17866
- (column2) => getCellClipboardText(record2, startRow + offset, column2)
17867
- ).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(" ")
17868
18739
  ).join("\n");
17869
18740
  }
17870
- if (mode === "column" && columnRange && params.selection.rowIndex == null) {
18741
+ if ((mode === "column" || selectedColumnKeys != null) && (columnRange || selectedColumnKeys?.length) && params.selection.rowIndex == null) {
17871
18742
  return params.rows.map(
17872
18743
  (record2, rowIndex2) => selectedColumns.map((column2) => getCellClipboardText(record2, rowIndex2, column2)).join(" ")
17873
18744
  ).join("\n");
@@ -17967,42 +18838,99 @@ async function copyRowJsonContent(params) {
17967
18838
  }
17968
18839
  }
17969
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
+ }
17970
18866
  await utils.downloadJsonFile({
17971
18867
  fileName: params.fileName?.trim() || buildTableExportFileName("json"),
17972
- data: utils.mapRowsToExportRecords({
17973
- rows: params.rows,
17974
- columns: buildExportColumns(params.columns)
17975
- })
18868
+ data
17976
18869
  });
17977
18870
  }
17978
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
+ );
17979
18881
  await utils.exportTableToCsv({
17980
18882
  fileName: params.fileName?.trim() || buildTableExportFileName("csv"),
17981
- rows: params.rows,
17982
- columns: buildExportColumns(params.columns)
18883
+ rows,
18884
+ columns: buildExportColumns(columns),
18885
+ footerRows: await buildListTableExportFooterRows(
18886
+ rows,
18887
+ columns,
18888
+ params.settings
18889
+ )
17983
18890
  });
17984
18891
  }
17985
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
+ );
17986
18902
  await utils.exportTableToTxt({
17987
18903
  fileName: params.fileName?.trim() || buildTableExportFileName("txt"),
17988
- rows: params.rows,
17989
- columns: buildExportColumns(params.columns)
18904
+ rows,
18905
+ columns: buildExportColumns(columns),
18906
+ footerRows: await buildListTableExportFooterRows(
18907
+ rows,
18908
+ columns,
18909
+ params.settings
18910
+ )
17990
18911
  });
17991
18912
  }
17992
18913
  async function exportAllRowsXlsxContent(params) {
17993
- const columns = params.columns.filter(isExportableColumn);
17994
- const footerRows = [];
17995
- if (params.includeSummary) {
17996
- const values = await computeSummaryValuesAsync({
17997
- rows: params.rows,
17998
- columns,
17999
- signal: params.signal
18000
- });
18001
- footerRows.push(columns.map((column) => values.get(column.key) ?? ""));
18002
- }
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
+ );
18003
18931
  await utils.exportTableToXlsx({
18004
18932
  fileName: params.fileName?.trim() || buildTableExportFileName("xlsx"),
18005
- rows: params.rows,
18933
+ rows,
18006
18934
  footerRows,
18007
18935
  worker: params.worker,
18008
18936
  signal: params.signal,
@@ -18018,6 +18946,95 @@ async function exportAllRowsXlsxContent(params) {
18018
18946
  sheetName: "TableData"
18019
18947
  });
18020
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
+ }
18021
19038
  function renderResultToClipboardText(result) {
18022
19039
  if (result == null) {
18023
19040
  return "";
@@ -18282,11 +19299,14 @@ var ListTableContentActionController = class {
18282
19299
  const includeSummary = options?.includeSummary ?? this.options.getSummaryVisible?.() ?? false;
18283
19300
  options = { ...this.options.getOptions().exportOptions, ...options };
18284
19301
  this.startExportTask(
18285
- (fileName) => exportAllRowsXlsxContent({
19302
+ (fileName, settings) => exportAllRowsXlsxContent({
18286
19303
  rows: this.options.getOptions().dataSource,
18287
- columns: this.options.getLeafColumns(),
19304
+ columns: getListTableExportCandidateColumns(
19305
+ this.options.getOptions().columns
19306
+ ),
18288
19307
  ...options,
18289
19308
  includeSummary,
19309
+ ...settings ? { settings } : null,
18290
19310
  fileName: fileName ?? options?.fileName,
18291
19311
  onProgress: (progress) => {
18292
19312
  const fileLabel = progress.fileCount && progress.fileCount > 1 ? `\uFF08\u7B2C ${progress.fileIndex}/${progress.fileCount} \u4EFD\uFF09` : "";
@@ -18299,10 +19319,17 @@ var ListTableContentActionController = class {
18299
19319
  "\u6B63\u5728\u5BFC\u51FA XLSX\u2026",
18300
19320
  options?.onError,
18301
19321
  options?.fileName?.trim() ? `${options.fileName.trim().replace(/\.xlsx$/i, "")}.xlsx` : buildTableExportFileName("xlsx"),
18302
- options?.promptFileName
19322
+ options?.promptFileName,
19323
+ {
19324
+ columns: getListTableExportColumnChoices(
19325
+ getListTableExportCandidateColumns(this.options.getOptions().columns),
19326
+ this.options.getSortState?.()
19327
+ ),
19328
+ includeSummary
19329
+ }
18303
19330
  );
18304
19331
  }
18305
- 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) {
18306
19333
  if (this.exportTaskRunning || this.exportNamePending) {
18307
19334
  return;
18308
19335
  }
@@ -18317,14 +19344,15 @@ var ListTableContentActionController = class {
18317
19344
  };
18318
19345
  const request = {
18319
19346
  fileName: defaultFileName,
18320
- onConfirm: (value) => {
19347
+ ...dialog,
19348
+ onConfirm: (value, settings) => {
18321
19349
  if (settled || !value.trim()) return;
18322
19350
  close();
18323
19351
  const extension = defaultFileName.match(
18324
19352
  /\.(xlsx|csv|txt|json)$/i
18325
19353
  )?.[0];
18326
19354
  const name = extension && !value.trim().toLowerCase().endsWith(extension.toLowerCase()) ? `${value.trim()}${extension}` : value.trim();
18327
- this.startExportTask(() => task(name), text, onError);
19355
+ this.startExportTask(() => task(name, settings), text, onError);
18328
19356
  },
18329
19357
  onCancel: () => {
18330
19358
  if (!settled) close();
@@ -19245,7 +20273,7 @@ function resolveScrollTopToRevealRow(params) {
19245
20273
  if (frozenRows.top.rowIndexMap?.includes(rowIndex) || frozenRows.bottom.rowIndexMap?.includes(rowIndex)) {
19246
20274
  return null;
19247
20275
  }
19248
- const scrollSectionIndex = frozenRows.scroll.rowIndexMap ? frozenRows.scroll.rowIndexMap.indexOf(rowIndex) : rowIndex;
20276
+ const scrollSectionIndex = frozenRows.scroll.dataRowIndexAligned ? rowIndex : frozenRows.scroll.rowIndexMap ? frozenRows.scroll.rowIndexMap.indexOf(rowIndex) : rowIndex;
19249
20277
  if (scrollSectionIndex < 0) {
19250
20278
  return null;
19251
20279
  }
@@ -19488,7 +20516,7 @@ var ListTableEditingController = class {
19488
20516
  });
19489
20517
  const nextTop = resolveScrollTopToRevealRow({
19490
20518
  rowIndex,
19491
- rowCount: snapshot.visibleRows.length,
20519
+ rowCount: snapshot.frozenRows.scroll.dataRowIndexAligned ? snapshot.dataRowCount : snapshot.visibleRows.length,
19492
20520
  rowHeight: snapshot.rowHeight,
19493
20521
  bodyHeight: snapshot.bodyLayout.scrollBodyHeight,
19494
20522
  scrollTop: scroll.top,
@@ -19707,257 +20735,6 @@ function setColumnConditionalFormats(columns, key, rules, cache) {
19707
20735
  }
19708
20736
  return false;
19709
20737
  }
19710
-
19711
- // src/services/range-highlight-service.ts
19712
- var EMPTY_RANGE_HIGHLIGHT_STATE = {
19713
- selectedRowAnchorIndex: null,
19714
- selectedRowRange: null,
19715
- selectedColumnAnchorKey: null,
19716
- selectedColumnRange: null
19717
- };
19718
- function isIndexInHighlightRange2(index, range) {
19719
- return Boolean(range && index >= range.start && index <= range.end);
19720
- }
19721
- function resolveNextRowHighlight(params) {
19722
- return resolveNextRowHighlightByIndexRange({
19723
- current: params.current,
19724
- rangeStartIndex: params.rowIndex,
19725
- rangeEndIndex: params.rowIndex,
19726
- shiftKey: params.shiftKey
19727
- });
19728
- }
19729
- function resolveNextRowHighlightByIndexRange(params) {
19730
- const rangeStartIndex = Math.max(0, Math.floor(params.rangeStartIndex));
19731
- const rangeEndIndex = Math.max(
19732
- rangeStartIndex,
19733
- Math.floor(params.rangeEndIndex)
19734
- );
19735
- const anchor = params.shiftKey && params.current.selectedRowAnchorIndex != null ? params.current.selectedRowAnchorIndex : rangeStartIndex;
19736
- return {
19737
- ...params.current,
19738
- selectedRowAnchorIndex: anchor,
19739
- selectedRowRange: createHighlightRange(anchor, rangeEndIndex)
19740
- };
19741
- }
19742
- function resolveNextRowHighlightForContextMenu(params) {
19743
- if (isIndexInHighlightRange2(params.rowIndex, params.current.selectedRowRange)) {
19744
- return params.current;
19745
- }
19746
- return resolveNextRowHighlight({
19747
- current: params.current,
19748
- rowIndex: params.rowIndex,
19749
- shiftKey: false
19750
- });
19751
- }
19752
- function resolveNextColumnHighlightForContextMenu(params) {
19753
- const currentRange = params.current.selectedColumnRange;
19754
- if (currentRange && params.rangeStartIndex >= currentRange.start && params.rangeEndIndex <= currentRange.end) {
19755
- return {
19756
- ...params.current,
19757
- selectedRowAnchorIndex: null,
19758
- selectedRowRange: null
19759
- };
19760
- }
19761
- return resolveNextColumnHighlightForHeaderClick({
19762
- ...params,
19763
- shiftKey: false
19764
- });
19765
- }
19766
- function resolveColumnKeysForContextMenuAction(params) {
19767
- const columnIndex = params.leafColumnKeys.indexOf(params.columnKey);
19768
- const rangeStartIndex = params.rangeStartIndex ?? columnIndex;
19769
- const rangeEndIndex = params.rangeEndIndex ?? columnIndex;
19770
- const hasValidTargetRange = rangeStartIndex >= 0 && rangeEndIndex >= rangeStartIndex && rangeEndIndex < params.leafColumnKeys.length;
19771
- const currentRange = params.current.selectedColumnRange;
19772
- if (currentRange && hasValidTargetRange && rangeStartIndex >= currentRange.start && rangeEndIndex <= currentRange.end) {
19773
- return params.leafColumnKeys.slice(
19774
- currentRange.start,
19775
- currentRange.end + 1
19776
- );
19777
- }
19778
- if (hasValidTargetRange) {
19779
- return params.leafColumnKeys.slice(rangeStartIndex, rangeEndIndex + 1);
19780
- }
19781
- return [];
19782
- }
19783
- function resolveNextColumnHighlight(params) {
19784
- const columnIndex = params.leafColumnKeys.indexOf(params.columnKey);
19785
- if (columnIndex < 0) {
19786
- return params.current;
19787
- }
19788
- return resolveNextColumnHighlightByIndexRange({
19789
- current: params.current,
19790
- columnKey: params.columnKey,
19791
- leafColumnKeys: params.leafColumnKeys,
19792
- rangeStartIndex: columnIndex,
19793
- rangeEndIndex: columnIndex,
19794
- shiftKey: params.shiftKey
19795
- });
19796
- }
19797
- function resolveNextColumnHighlightForHeaderClick(params) {
19798
- const nextCurrent = {
19799
- ...params.current,
19800
- selectedRowAnchorIndex: null,
19801
- selectedRowRange: null
19802
- };
19803
- const hasValidRange = params.rangeStartIndex >= 0 && params.rangeEndIndex >= 0 && params.rangeStartIndex < params.leafColumnKeys.length && params.rangeEndIndex < params.leafColumnKeys.length;
19804
- if (!params.shiftKey) {
19805
- const leafColumnIndex = params.leafColumnKeys.indexOf(params.columnKey);
19806
- if (leafColumnIndex >= 0) {
19807
- return resolveNextColumnHighlightByIndexRange({
19808
- ...params,
19809
- current: nextCurrent,
19810
- rangeStartIndex: leafColumnIndex,
19811
- rangeEndIndex: leafColumnIndex
19812
- });
19813
- }
19814
- if (hasValidRange) {
19815
- return resolveNextColumnHighlightByIndexRange({
19816
- ...params,
19817
- current: nextCurrent
19818
- });
19819
- }
19820
- return {
19821
- ...nextCurrent,
19822
- selectedColumnAnchorKey: params.columnKey,
19823
- selectedColumnRange: null
19824
- };
19825
- }
19826
- if (hasValidRange) {
19827
- return resolveNextColumnHighlightByIndexRange({
19828
- ...params,
19829
- current: nextCurrent
19830
- });
19831
- }
19832
- return resolveNextColumnHighlight({
19833
- current: nextCurrent,
19834
- columnKey: params.columnKey,
19835
- leafColumnKeys: params.leafColumnKeys,
19836
- shiftKey: params.shiftKey
19837
- });
19838
- }
19839
- function resolveNextColumnHighlightByIndexRange(params) {
19840
- if (params.rangeStartIndex < 0 || params.rangeEndIndex < 0 || params.rangeStartIndex >= params.leafColumnKeys.length || params.rangeEndIndex >= params.leafColumnKeys.length) {
19841
- return params.current;
19842
- }
19843
- const anchorIndex = resolveColumnAnchorIndex({
19844
- anchorKey: params.current.selectedColumnAnchorKey,
19845
- leafColumnKeys: params.leafColumnKeys
19846
- });
19847
- const nextAnchorIndex = params.shiftKey && anchorIndex != null ? anchorIndex : params.rangeStartIndex;
19848
- return {
19849
- ...params.current,
19850
- selectedColumnAnchorKey: params.leafColumnKeys[nextAnchorIndex] ?? params.columnKey,
19851
- selectedColumnRange: createHighlightRange(
19852
- nextAnchorIndex,
19853
- params.shiftKey ? params.rangeEndIndex : params.rangeEndIndex
19854
- )
19855
- };
19856
- }
19857
- function resolveColumnAnchorIndex(params) {
19858
- if (!params.anchorKey) {
19859
- return null;
19860
- }
19861
- const anchorIndex = params.leafColumnKeys.indexOf(params.anchorKey);
19862
- return anchorIndex >= 0 ? anchorIndex : null;
19863
- }
19864
- function createHighlightRange(start, end) {
19865
- return {
19866
- start: Math.min(start, end),
19867
- end: Math.max(start, end)
19868
- };
19869
- }
19870
- function resolveHighlightMode(mode) {
19871
- if (mode === "cell" || mode === "column" || mode === "cross") {
19872
- return mode;
19873
- }
19874
- return "row";
19875
- }
19876
- function resolveRangeHighlightForHeaderSelection(params) {
19877
- const nextRangeHighlight = {
19878
- ...params.current,
19879
- selectedRowAnchorIndex: null,
19880
- selectedRowRange: null
19881
- };
19882
- if (!params.shiftKey) {
19883
- nextRangeHighlight.selectedColumnAnchorKey = EMPTY_RANGE_HIGHLIGHT_STATE.selectedColumnAnchorKey;
19884
- nextRangeHighlight.selectedColumnRange = EMPTY_RANGE_HIGHLIGHT_STATE.selectedColumnRange;
19885
- }
19886
- return nextRangeHighlight;
19887
- }
19888
- function resolveRangeHighlightForBodyInteraction(params) {
19889
- const mode = resolveHighlightMode(params.mode);
19890
- const canHighlightColumn = params.columnKey !== params.selectionColumnKey && params.columnKey !== params.expandColumnKey;
19891
- const rangeEndIndex = params.rowIndex + Math.max(Math.floor(params.rowSpan ?? 1), 1) - 1;
19892
- if (mode === "cell") {
19893
- return EMPTY_RANGE_HIGHLIGHT_STATE;
19894
- }
19895
- if (mode === "row") {
19896
- return {
19897
- ...resolveNextRowHighlightByIndexRange({
19898
- current: params.current,
19899
- rangeStartIndex: params.rowIndex,
19900
- rangeEndIndex,
19901
- shiftKey: params.shiftKey
19902
- }),
19903
- selectedColumnAnchorKey: null,
19904
- selectedColumnRange: null
19905
- };
19906
- }
19907
- if (mode === "column") {
19908
- if (!canHighlightColumn || !params.columnRange) {
19909
- return {
19910
- ...params.current,
19911
- selectedRowAnchorIndex: null,
19912
- selectedRowRange: null
19913
- };
19914
- }
19915
- return {
19916
- ...resolveNextColumnHighlightByIndexRange({
19917
- current: params.current,
19918
- columnKey: params.columnKey,
19919
- leafColumnKeys: params.leafColumnKeys,
19920
- rangeStartIndex: params.columnRange.start,
19921
- rangeEndIndex: params.columnRange.end,
19922
- shiftKey: params.shiftKey
19923
- }),
19924
- selectedRowAnchorIndex: null,
19925
- selectedRowRange: null
19926
- };
19927
- }
19928
- let next = resolveNextRowHighlightByIndexRange({
19929
- current: params.current,
19930
- rangeStartIndex: params.rowIndex,
19931
- rangeEndIndex,
19932
- shiftKey: params.shiftKey
19933
- });
19934
- if (canHighlightColumn && params.columnRange) {
19935
- next = resolveNextColumnHighlightByIndexRange({
19936
- current: next,
19937
- columnKey: params.columnKey,
19938
- leafColumnKeys: params.leafColumnKeys,
19939
- rangeStartIndex: params.columnRange.start,
19940
- rangeEndIndex: params.columnRange.end,
19941
- shiftKey: false
19942
- });
19943
- }
19944
- return next;
19945
- }
19946
- function isCellHighlightedByMode(params) {
19947
- const mode = resolveHighlightMode(params.mode);
19948
- if (mode === "cell") {
19949
- return params.isCellSelected;
19950
- }
19951
- if (mode === "row") {
19952
- return params.isRowRangeSelected;
19953
- }
19954
- if (mode === "column") {
19955
- return params.isColumnRangeSelected;
19956
- }
19957
- return params.isRowRangeSelected || params.isColumnRangeSelected;
19958
- }
19959
-
19960
- // src/rendering/primitives/draw-body.ts
19961
20738
  var VALIDATION_ERROR_COLOR = "#ff4d4f";
19962
20739
  var VALIDATION_TEXT_COLOR = "#ffffff";
19963
20740
  var VALIDATION_MUTED_TEXT_COLOR = "rgba(255, 255, 255, 0.72)";
@@ -20041,7 +20818,8 @@ function drawSegmentRows(params, segment, columns, startRowIndex, endRowIndex, r
20041
20818
  columns,
20042
20819
  startRowIndex,
20043
20820
  endRowIndex,
20044
- rowIndexMap: params.rowIndexMap
20821
+ rowIndexMap: params.rowIndexMap,
20822
+ horizontalClip: clipRect
20045
20823
  });
20046
20824
  const animatedCellHeights = /* @__PURE__ */ new Map();
20047
20825
  let collapseAnimationOverflow = 0;
@@ -20077,7 +20855,7 @@ function drawSegmentRows(params, segment, columns, startRowIndex, endRowIndex, r
20077
20855
  renderClipRect.height
20078
20856
  );
20079
20857
  ctx.clip();
20080
- const orderedCells = [...visibleCells].sort(
20858
+ const orderedCells = animatedCellHeights.size === 0 ? visibleCells : [...visibleCells].sort(
20081
20859
  (left, right) => Number(animatedCellHeights.has(left)) - Number(animatedCellHeights.has(right))
20082
20860
  );
20083
20861
  orderedCells.forEach((cell) => {
@@ -20103,7 +20881,7 @@ function drawSegmentRows(params, segment, columns, startRowIndex, endRowIndex, r
20103
20881
  width: getMergedCellWidth(columns, cell.columnIndex, cell.colSpan),
20104
20882
  height: animatedCellHeights.get(cell) ?? baseHeight
20105
20883
  };
20106
- 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) {
20107
20885
  return;
20108
20886
  }
20109
20887
  const isHovered = params.hover.area === "body" && params.hover.rowIndex === cell.sourceRowIndex && params.hover.columnKey === cell.column.key;
@@ -20111,24 +20889,28 @@ function drawSegmentRows(params, segment, columns, startRowIndex, endRowIndex, r
20111
20889
  const hasCellTextSelection = params.selection.textSelection?.fragments.some(
20112
20890
  (fragment) => fragment.rowIndex === cell.sourceRowIndex && fragment.columnKey === cell.column.key
20113
20891
  ) === true;
20114
- const isRowRangeSelected = cell.row.kind === "data" && isRowSpanInHighlightRange(
20115
- cell.sourceRowIndex,
20116
- cell.rowSpan,
20892
+ const isRowRangeSelected = cell.row.kind === "data" && (params.rangeHighlight?.selectedRowRanges ?? ((params.rangeHighlight ?? EMPTY_RANGE_HIGHLIGHT3).selectedRowRange ? [
20117
20893
  (params.rangeHighlight ?? EMPTY_RANGE_HIGHLIGHT3).selectedRowRange
20894
+ ] : [])).some(
20895
+ (range) => isRowSpanInHighlightRange(cell.sourceRowIndex, cell.rowSpan, range)
20118
20896
  );
20119
- const isColumnRangeSelected = cell.row.kind === "data" && isIndexInHighlightRange3(
20120
- cell.column.leafIndex,
20121
- (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
20122
20906
  );
20123
- const selectedCellRange = (params.rangeHighlight ?? EMPTY_RANGE_HIGHLIGHT3).selectedCellRange;
20124
- 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;
20125
20907
  const isHeaderColumnSelected = cell.row.kind === "data" && params.selectedHeaderKey != null && cell.column.key === params.selectedHeaderKey;
20126
- const isRangeHighlighted = isCellHighlightedByMode({
20908
+ const isRangeHighlighted = isSelectedCellRange || isCellHighlightedByMode({
20127
20909
  mode: params.highlightMode,
20128
20910
  isCellSelected: isSelected || isSelectedCellRange,
20129
20911
  isRowRangeSelected,
20130
20912
  isColumnRangeSelected
20131
- });
20913
+ }) || params.selection.rowIndex == null && (params.selectedHeaderKey != null || (params.rangeHighlight ?? EMPTY_RANGE_HIGHLIGHT3).selectedColumnKeys != null) && isColumnRangeSelected;
20132
20914
  const validationMessage = cell.row.kind === "data" ? params.getValidationMessage?.({
20133
20915
  record: cell.record,
20134
20916
  rowIndex: cell.sourceRowIndex,
@@ -20193,6 +20975,7 @@ function drawSegmentRows(params, segment, columns, startRowIndex, endRowIndex, r
20193
20975
  kind: "body",
20194
20976
  rect,
20195
20977
  columnKey: cell.column.key,
20978
+ cursor: cell.column.key === ROW_SELECTOR_COLUMN_KEY ? "pointer" : void 0,
20196
20979
  rowIndex: cell.sourceRowIndex,
20197
20980
  rowSpan: cell.rowSpan,
20198
20981
  fixed: cell.column.fixed,
@@ -20459,9 +21242,6 @@ function findVisibleDataRow(rows, startIndex, step) {
20459
21242
  }
20460
21243
  return null;
20461
21244
  }
20462
- function isIndexInHighlightRange3(index, range) {
20463
- return Boolean(range && index >= range.start && index <= range.end);
20464
- }
20465
21245
  function isRowSpanInHighlightRange(rowIndex, rowSpan, range) {
20466
21246
  if (!range) {
20467
21247
  return false;
@@ -20556,6 +21336,14 @@ function buildMergedBodyCells(rows, columns, mergeCell, rowIndexMap) {
20556
21336
  function buildPlainVisibleBodyCells(params) {
20557
21337
  const { rows, columns, startRowIndex, endRowIndex } = params;
20558
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
+ }
20559
21347
  for (let rowIndex = startRowIndex; rowIndex < endRowIndex; rowIndex += 1) {
20560
21348
  const row = rows[rowIndex];
20561
21349
  if (!row) {
@@ -20579,7 +21367,7 @@ function buildPlainVisibleBodyCells(params) {
20579
21367
  continue;
20580
21368
  }
20581
21369
  const sourceRowIndex = row.dataRowIndex;
20582
- for (let columnIndex = 0; columnIndex < columns.length; columnIndex += 1) {
21370
+ for (const columnIndex of visibleColumnIndexes) {
20583
21371
  const column = columns[columnIndex];
20584
21372
  if (!column) {
20585
21373
  continue;
@@ -21561,7 +22349,7 @@ function getMaxDepth(columns) {
21561
22349
  var DEFAULT_HORIZONTAL_LEFT = 0;
21562
22350
  var DEFAULT_HORIZONTAL_RIGHT = 0;
21563
22351
  var DEFAULT_HORIZONTAL_BOTTOM = 0;
21564
- var DEFAULT_VERTICAL_TOP = 6;
22352
+ var DEFAULT_VERTICAL_TOP = 0;
21565
22353
  var DEFAULT_VERTICAL_RIGHT = 0;
21566
22354
  var DEFAULT_VERTICAL_BOTTOM = 0;
21567
22355
  var MIN_SCROLLBAR_THUMB_SIZE = 36;
@@ -21571,6 +22359,7 @@ function computeScrollbarLayout(params) {
21571
22359
  const vertical = utils.resolveCanvasScrollbarAxis(params.scrollbar, "vertical");
21572
22360
  const horizontalLeft = Math.max(
21573
22361
  params.scrollbar?.horizontalLeft ?? DEFAULT_HORIZONTAL_LEFT,
22362
+ params.rowSelectorWidth ?? 0,
21574
22363
  0
21575
22364
  );
21576
22365
  const horizontalRight = Math.max(
@@ -21600,43 +22389,69 @@ function computeScrollbarLayout(params) {
21600
22389
  const horizontalTrackY = params.height - horizontal.thickness - horizontalBottom;
21601
22390
  if (horizontal.visible && params.maxScrollLeft > 0 && horizontalTrackWidth > 0) {
21602
22391
  const thickness = horizontal.thickness;
21603
- const buttonSize = Math.min(thickness, horizontalTrackWidth / 3);
21604
- const trackX = horizontalLeft + buttonSize;
21605
- const trackWidth = Math.max(horizontalTrackWidth - buttonSize * 2, 0);
21606
- const thumbWidth = Math.min(
21607
- Math.max(
21608
- params.centerVisibleWidth / params.centerContentWidth * trackWidth,
21609
- MIN_SCROLLBAR_THUMB_SIZE
21610
- ),
21611
- trackWidth
21612
- );
21613
- const thumbOffset = params.maxScrollLeft === 0 ? 0 : params.scrollLeft / params.maxScrollLeft * (trackWidth - thumbWidth);
21614
- layout.horizontal = {
21615
- decreaseButton: {
21616
- x: horizontalLeft,
21617
- y: horizontalTrackY,
21618
- width: buttonSize,
21619
- height: thickness
21620
- },
21621
- increaseButton: {
21622
- x: horizontalLeft + horizontalTrackWidth - buttonSize,
22392
+ if (horizontal.mode === "button") {
22393
+ const halfWidth = horizontalTrackWidth / 2;
22394
+ const empty = {
22395
+ x: horizontalLeft + halfWidth,
21623
22396
  y: horizontalTrackY,
21624
- width: buttonSize,
21625
- height: thickness
21626
- },
21627
- track: {
21628
- x: trackX,
21629
- y: horizontalTrackY,
21630
- width: trackWidth,
21631
- height: thickness
21632
- },
21633
- thumb: {
21634
- x: trackX + thumbOffset,
21635
- y: horizontalTrackY,
21636
- width: thumbWidth,
21637
- height: thickness
21638
- }
21639
- };
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
+ }
21640
22455
  }
21641
22456
  const verticalTrackHeight = Math.max(
21642
22457
  params.bodyHeight - verticalTop - verticalBottom,
@@ -21646,43 +22461,69 @@ function computeScrollbarLayout(params) {
21646
22461
  const verticalTrackY = params.bodyTop + verticalTop;
21647
22462
  if (vertical.visible && params.maxScrollTop > 0 && verticalTrackHeight > 0) {
21648
22463
  const thickness = vertical.thickness;
21649
- const buttonSize = Math.min(thickness, verticalTrackHeight / 3);
21650
- const trackY = verticalTrackY + buttonSize;
21651
- const trackHeight = Math.max(verticalTrackHeight - buttonSize * 2, 0);
21652
- const thumbHeight = Math.min(
21653
- Math.max(
21654
- params.bodyHeight / params.totalBodyHeight * trackHeight,
21655
- MIN_SCROLLBAR_THUMB_SIZE
21656
- ),
21657
- trackHeight
21658
- );
21659
- const thumbOffset = params.maxScrollTop === 0 ? 0 : params.scrollTop / params.maxScrollTop * (trackHeight - thumbHeight);
21660
- layout.vertical = {
21661
- decreaseButton: {
21662
- x: verticalTrackX,
21663
- y: verticalTrackY,
21664
- width: thickness,
21665
- height: buttonSize
21666
- },
21667
- increaseButton: {
21668
- x: verticalTrackX,
21669
- y: verticalTrackY + verticalTrackHeight - buttonSize,
21670
- width: thickness,
21671
- height: buttonSize
21672
- },
21673
- track: {
21674
- x: verticalTrackX,
21675
- y: trackY,
21676
- width: thickness,
21677
- height: trackHeight
21678
- },
21679
- thumb: {
22464
+ if (vertical.mode === "button") {
22465
+ const halfHeight = verticalTrackHeight / 2;
22466
+ const empty = {
21680
22467
  x: verticalTrackX,
21681
- y: trackY + thumbOffset,
21682
- width: thickness,
21683
- height: thumbHeight
21684
- }
21685
- };
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
+ }
21686
22527
  }
21687
22528
  return layout;
21688
22529
  }
@@ -21900,30 +22741,25 @@ function resolveColumnSegments(params) {
21900
22741
  // src/domain/columns/header-layout.ts
21901
22742
  function buildHeaderLayouts(nodes, leafColumns, rightStartX, headerRowHeight, fixedWidthLeft, fixedWidthRight, scrollLeft) {
21902
22743
  const rows = /* @__PURE__ */ new Map();
21903
- const leftCols = leafColumns.filter((c) => c.fixed === "left");
21904
- const centerCols = leafColumns.filter((c) => !c.fixed);
21905
- const rightCols = leafColumns.filter((c) => c.fixed === "right");
21906
- function segmentOffset(cols, targetKey) {
21907
- let offset = 0;
21908
- for (const c of cols) {
21909
- if (c.key === targetKey) return offset;
21910
- offset += c.width;
21911
- }
21912
- return offset;
21913
- }
21914
- const getLeafX = (leafIndex) => {
21915
- const leaf = leafColumns[leafIndex];
21916
- if (!leaf) {
21917
- return 0;
21918
- }
22744
+ let leftX = 0;
22745
+ let centerX = fixedWidthLeft - scrollLeft;
22746
+ let rightX = rightStartX;
22747
+ const leafPositions = leafColumns.map((leaf) => {
21919
22748
  if (leaf.fixed === "left") {
21920
- return segmentOffset(leftCols, leaf.key);
22749
+ const x2 = leftX;
22750
+ leftX += leaf.width;
22751
+ return x2;
21921
22752
  }
21922
22753
  if (leaf.fixed === "right") {
21923
- return rightStartX + segmentOffset(rightCols, leaf.key);
22754
+ const x2 = rightX;
22755
+ rightX += leaf.width;
22756
+ return x2;
21924
22757
  }
21925
- return fixedWidthLeft + segmentOffset(centerCols, leaf.key) - scrollLeft;
21926
- };
22758
+ const x = centerX;
22759
+ centerX += leaf.width;
22760
+ return x;
22761
+ });
22762
+ const getLeafX = (leafIndex) => leafPositions[leafIndex] ?? 0;
21927
22763
  const visit = (node) => {
21928
22764
  const rowItems = rows.get(node.depth) ?? [];
21929
22765
  buildHeaderCellSegments(node, leafColumns, {
@@ -23524,13 +24360,15 @@ var AUTO_ROW_HEIGHT_SAMPLE_COUNT = 200;
23524
24360
  function buildListTableRenderSnapshot(params) {
23525
24361
  if (params.cache?.snapshot && params.cache.snapshot.groupIndex === params.cache.groupIndex && isSameRenderCacheInputs(params.cache.inputs, params)) {
23526
24362
  const snapshotStartedAt2 = resolvePerformanceNow3();
23527
- 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({
23528
24365
  snapshot: params.cache.snapshot,
23529
24366
  width: params.width,
23530
24367
  height: params.height,
23531
24368
  scroll: params.scroll,
23532
24369
  options: params.options
23533
24370
  });
24371
+ params.cache.snapshot = snapshot2;
23534
24372
  params.options.debug?.onPerformance?.({
23535
24373
  phase: "snapshot",
23536
24374
  durationMs: resolvePerformanceNow3() - snapshotStartedAt2,
@@ -23893,6 +24731,7 @@ function buildListTableRenderSnapshot(params) {
23893
24731
  });
23894
24732
  const scrollbarLayout = computeScrollbarLayout({
23895
24733
  width: hostWidth,
24734
+ rowSelectorWidth: leafColumns.find((column) => column.key === ROW_SELECTOR_COLUMN_KEY)?.width ?? 0,
23896
24735
  height: hostHeight,
23897
24736
  bodyTop: bodyState.bodyLayout.scrollBodyTop,
23898
24737
  bodyHeight: bodyState.bodyLayout.scrollBodyHeight,
@@ -24105,12 +24944,13 @@ function updateListTableRenderSnapshotScroll(params) {
24105
24944
  scroll
24106
24945
  });
24107
24946
  const nextScroll = viewportResult.scroll;
24108
- const columnSegments = resolveColumnSegments({
24947
+ const reuseHorizontalLayout = nextScroll.left === snapshot.scroll.left && !snapshot.swapAnimation;
24948
+ const columnSegments = reuseHorizontalLayout ? snapshot.columnSegments : resolveColumnSegments({
24109
24949
  metrics: snapshot.columnMetrics,
24110
24950
  scrollLeft: nextScroll.left,
24111
24951
  swapAnimation: snapshot.swapAnimation
24112
24952
  });
24113
- const headerRows = buildHeaderLayouts(
24953
+ const headerRows = reuseHorizontalLayout ? snapshot.headerRows : buildHeaderLayouts(
24114
24954
  snapshot.headerTree,
24115
24955
  snapshot.leafColumns,
24116
24956
  snapshot.columnMetrics.rightStartX,
@@ -24119,7 +24959,8 @@ function updateListTableRenderSnapshotScroll(params) {
24119
24959
  snapshot.columnMetrics.fixedWidthRight,
24120
24960
  nextScroll.left
24121
24961
  );
24122
- offsetHeaderRows(headerRows, snapshot.groupingBarHeight);
24962
+ if (!reuseHorizontalLayout)
24963
+ offsetHeaderRows(headerRows, snapshot.groupingBarHeight);
24123
24964
  if (snapshot.swapAnimation) {
24124
24965
  applyColumnSegmentPositionsToHeaderRows({
24125
24966
  headerRows,
@@ -24129,6 +24970,9 @@ function updateListTableRenderSnapshotScroll(params) {
24129
24970
  }
24130
24971
  const scrollbarLayout = computeScrollbarLayout({
24131
24972
  width,
24973
+ rowSelectorWidth: snapshot.leafColumns.find(
24974
+ (column) => column.key === ROW_SELECTOR_COLUMN_KEY
24975
+ )?.width ?? 0,
24132
24976
  height,
24133
24977
  bodyTop: snapshot.bodyLayout.scrollBodyTop,
24134
24978
  bodyHeight: snapshot.bodyLayout.scrollBodyHeight,
@@ -24467,7 +25311,7 @@ function buildListTableDebugSnapshot(params) {
24467
25311
  hitTest: hitTest2.area === "empty" ? void 0 : hitTest2
24468
25312
  };
24469
25313
  }
24470
- 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) {
24471
25315
  const alpha = Number.isFinite(opacity) ? Math.min(Math.max(opacity, 0), 1) : 1;
24472
25316
  if (alpha <= 1e-3) {
24473
25317
  return;
@@ -24492,7 +25336,8 @@ function drawScrollbars(ctx, layout, descriptors, borderColor, opacity = 1, inte
24492
25336
  descriptors,
24493
25337
  borderColor,
24494
25338
  interactive,
24495
- utils.resolveCanvasScrollbarAxis(options, "horizontal")
25339
+ utils.resolveCanvasScrollbarAxis(options, "horizontal"),
25340
+ hover
24496
25341
  );
24497
25342
  }
24498
25343
  if (layout.vertical) {
@@ -24503,7 +25348,8 @@ function drawScrollbars(ctx, layout, descriptors, borderColor, opacity = 1, inte
24503
25348
  descriptors,
24504
25349
  borderColor,
24505
25350
  interactive,
24506
- utils.resolveCanvasScrollbarAxis(options, "vertical")
25351
+ utils.resolveCanvasScrollbarAxis(options, "vertical"),
25352
+ hover
24507
25353
  );
24508
25354
  }
24509
25355
  if (layout.horizontal) {
@@ -24514,10 +25360,13 @@ function drawScrollbars(ctx, layout, descriptors, borderColor, opacity = 1, inte
24514
25360
  borderColor
24515
25361
  );
24516
25362
  }
25363
+ if (layout.vertical) {
25364
+ drawVerticalAxisFrame(ctx, layout.vertical, borderColor, verticalFrame);
25365
+ }
24517
25366
  ctx.restore();
24518
25367
  }
24519
25368
  function drawHorizontalScrollbarMask(ctx, layout, backgroundColor) {
24520
- const left = layout.decreaseButton.x;
25369
+ const left = 0;
24521
25370
  const right = layout.increaseButton.x + layout.increaseButton.width;
24522
25371
  const top = layout.decreaseButton.y;
24523
25372
  const height = layout.decreaseButton.height;
@@ -24530,7 +25379,7 @@ function drawHorizontalScrollbarMask(ctx, layout, backgroundColor) {
24530
25379
  ctx.restore();
24531
25380
  }
24532
25381
  function drawHorizontalScrollbarTopBorder(ctx, horizontal, vertical, borderColor) {
24533
- const left = Math.round(horizontal.decreaseButton.x) + 0.5;
25382
+ const left = 0.5;
24534
25383
  const horizontalRight = horizontal.increaseButton.x + horizontal.increaseButton.width;
24535
25384
  const verticalRight = vertical ? vertical.decreaseButton.x + vertical.decreaseButton.width : horizontalRight;
24536
25385
  const right = Math.round(Math.max(horizontalRight, verticalRight)) - 0.5;
@@ -24547,21 +25396,23 @@ function drawHorizontalScrollbarTopBorder(ctx, horizontal, vertical, borderColor
24547
25396
  ctx.stroke();
24548
25397
  ctx.restore();
24549
25398
  }
24550
- function drawAxisScrollbar(ctx, layout, axis, descriptors, borderColor, interactive, style) {
25399
+ function drawAxisScrollbar(ctx, layout, axis, descriptors, borderColor, interactive, style, hover) {
24551
25400
  const kind = axis === "horizontal" ? "horizontal-scrollbar" : "vertical-scrollbar";
24552
25401
  if (interactive) {
24553
- descriptors.push({
24554
- kind,
24555
- rect: layout.thumb,
24556
- columnKey: `__${axis}_scrollbar_thumb__`,
24557
- scrollbarRole: "thumb"
24558
- });
24559
- descriptors.push({
24560
- kind,
24561
- rect: layout.track,
24562
- columnKey: `__${axis}_scrollbar_track__`,
24563
- scrollbarRole: "track"
24564
- });
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
+ }
24565
25416
  descriptors.push({
24566
25417
  kind,
24567
25418
  rect: layout.decreaseButton,
@@ -24587,10 +25438,101 @@ function drawAxisScrollbar(ctx, layout, axis, descriptors, borderColor, interact
24587
25438
  }
24588
25439
  ctx.restore();
24589
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
+ }
24590
25466
  drawButton(ctx, layout.decreaseButton, axis, "decrease", style.buttonColor);
24591
25467
  drawButton(ctx, layout.increaseButton, axis, "increase", style.buttonColor);
24592
- drawThumb(ctx, layout.thumb, style);
24593
- 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();
24594
25536
  }
24595
25537
  function drawTrackBorder(ctx, rect, axis, borderColor) {
24596
25538
  const left = Math.round(rect.x) + 0.5;
@@ -24652,7 +25594,7 @@ function drawVerticalScrollbarFrame(ctx, frame, borderColor) {
24652
25594
  }
24653
25595
  ctx.restore();
24654
25596
  }
24655
- function drawThumb(ctx, rect, style) {
25597
+ function drawThumb(ctx, rect, style, highlightColor) {
24656
25598
  ctx.save();
24657
25599
  ctx.fillStyle = style.thumbColor;
24658
25600
  roundRect2(
@@ -24664,6 +25606,18 @@ function drawThumb(ctx, rect, style) {
24664
25606
  style.thumbRadius ?? rect.height / 2
24665
25607
  );
24666
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
+ }
24667
25621
  ctx.restore();
24668
25622
  }
24669
25623
  function drawButton(ctx, rect, axis, direction, color) {
@@ -24995,6 +25949,7 @@ function drawListTableHeader(params) {
24995
25949
  const collapsedColumnMarkers = resolveCollapsedHeaderColumnMarkers({
24996
25950
  columns: options.columns,
24997
25951
  visibleColumnKeys: leafColumns.map((column) => column.key),
25952
+ showCollapsedColumnIcon: options.showCollapsedColumnIcon,
24998
25953
  // 系统列是否存在由功能配置决定,不属于用户隐藏的业务列,不能绘制恢复标记。
24999
25954
  excludedColumnKeys: [
25000
25955
  ...groupingColumnKeys ?? [],
@@ -25093,7 +26048,7 @@ function resolveActiveCellOverlayLayout(params) {
25093
26048
  highlightMode: params.highlightMode,
25094
26049
  columnKeys: params.columnKeys ?? [],
25095
26050
  allowColumnRange: selection.rowIndex == null,
25096
- 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,
25097
26052
  resolveClipRect: (item) => resolveActiveCellSectionClipRect(item, params),
25098
26053
  width: params.width,
25099
26054
  height: params.height
@@ -25184,10 +26139,13 @@ function drawActiveCellOverlay(params) {
25184
26139
  }
25185
26140
  function resolveRangeHighlightLayouts(params) {
25186
26141
  const rowRange = params.rangeHighlight?.selectedRowRange;
26142
+ const rowRanges = params.rangeHighlight?.selectedRowRanges ?? (rowRange ? [rowRange] : []);
25187
26143
  const columnRange = params.rangeHighlight?.selectedColumnRange;
26144
+ const selectedColumnKeys = params.rangeHighlight?.selectedColumnKeys;
25188
26145
  const cellRange = params.rangeHighlight?.selectedCellRange;
25189
- const rowSelectorRange = rowRange && params.allowRowRange;
25190
- 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)) {
25191
26149
  return { rects: [], segments: [] };
25192
26150
  }
25193
26151
  const bodyDescriptors = params.descriptors.filter(
@@ -25196,20 +26154,33 @@ function resolveRangeHighlightLayouts(params) {
25196
26154
  const selected = bodyDescriptors.filter((item) => {
25197
26155
  const itemRowStart = item.rowIndex;
25198
26156
  const itemRowEnd = itemRowStart + Math.max(item.rowSpan ?? 1, 1) - 1;
25199
- const rowSelected = rowRange != null && itemRowEnd >= rowRange.start && itemRowStart <= rowRange.end;
26157
+ const rowSelected = rowRanges.some(
26158
+ (range) => itemRowEnd >= range.start && itemRowStart <= range.end
26159
+ );
25200
26160
  const columnIndex = params.columnKeys.indexOf(item.columnKey);
25201
- const columnSelected = columnRange != null && columnIndex >= columnRange.start && columnIndex <= columnRange.end;
25202
- if (cellRange) {
25203
- return itemRowEnd >= cellRange.startRowIndex && itemRowStart <= cellRange.endRowIndex && columnIndex >= cellRange.startColumnIndex && columnIndex <= cellRange.endColumnIndex;
25204
- }
25205
- if (rowSelectorRange) {
25206
- return rowSelected;
25207
- }
25208
- 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;
25209
26166
  });
25210
26167
  const groups = /* @__PURE__ */ new Map();
26168
+ const selectedKeySet = new Set(selectedColumnKeys ?? []);
25211
26169
  selected.forEach((descriptor) => {
25212
- 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"}`;
25213
26184
  const current = groups.get(groupKey);
25214
26185
  const rect = current ? {
25215
26186
  x: Math.min(current.rect.x, descriptor.rect.x),
@@ -25244,8 +26215,8 @@ function resolveRangeHighlightLayouts(params) {
25244
26215
  );
25245
26216
  return {
25246
26217
  ...range,
25247
- omitLeft: range.rect.x > minX,
25248
- 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
25249
26220
  };
25250
26221
  });
25251
26222
  const rangeSegments = resolveRangeBorderSegments(
@@ -25762,7 +26733,8 @@ function executeListTableRenderPipeline(params) {
25762
26733
  headerBackgroundColor: theme.headerBackgroundColor,
25763
26734
  bodyBackgroundColor: theme.backgroundColor
25764
26735
  },
25765
- options.scrollbar
26736
+ options.scrollbar,
26737
+ hover
25766
26738
  );
25767
26739
  drawActiveCellOverlay({
25768
26740
  ctx,
@@ -25815,8 +26787,8 @@ function drawGroupingBar(params) {
25815
26787
  ctx.fillRect(0, 0, width, height);
25816
26788
  ctx.strokeStyle = theme.borderColor;
25817
26789
  ctx.beginPath();
25818
- ctx.moveTo(0, height - 0.5);
25819
- ctx.lineTo(width, height - 0.5);
26790
+ ctx.moveTo(0, height + 0.5);
26791
+ ctx.lineTo(width, height + 0.5);
25820
26792
  ctx.stroke();
25821
26793
  ctx.fillStyle = theme.headerTextColor;
25822
26794
  ctx.font = `${theme.headerFontWeight} ${theme.fontSize}px ${theme.fontFamily}`;
@@ -25908,6 +26880,16 @@ function drawColumnResizeIndicator(params) {
25908
26880
  // src/services/image-clipboard-service.ts
25909
26881
  var MAX_IMAGE_SIDE = 16384;
25910
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
+ }
25911
26893
  function assertTableImageSize(width, height) {
25912
26894
  if (!Number.isFinite(width) || !Number.isFinite(height) || width <= 0 || height <= 0) {
25913
26895
  throw new Error("\u6CA1\u6709\u53EF\u590D\u5236\u7684\u8868\u683C\u533A\u57DF\u3002");
@@ -25918,8 +26900,96 @@ function assertTableImageSize(width, height) {
25918
26900
  }
25919
26901
  function resolveImageSelection(host) {
25920
26902
  const state = host.rangeHighlight;
25921
- if (state.selectedCellRange) return state.selectedCellRange;
25922
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
+ }
25923
26993
  const columnIndex = host.leafColumns.findIndex(
25924
26994
  (column) => column.key === host.selection.columnKey
25925
26995
  );
@@ -25935,7 +27005,7 @@ function resolveImageSelection(host) {
25935
27005
  startRowIndex: state.selectedRowRange.start,
25936
27006
  endRowIndex: state.selectedRowRange.end,
25937
27007
  startColumnIndex: 0,
25938
- endColumnIndex: host.leafColumns.length - 1
27008
+ endColumnIndex: columnCount - 1
25939
27009
  };
25940
27010
  if (host.selection.rowIndex != null && columnIndex >= 0)
25941
27011
  return {
@@ -25967,23 +27037,50 @@ function renderTableCopyImage(host, scope) {
25967
27037
  const range = scope === "selection" ? resolveImageSelection(host) : null;
25968
27038
  if (scope === "selection" && !range)
25969
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");
25970
27042
  const startRow = range ? Math.max(Math.min(range.startRowIndex, range.endRowIndex), 0) : 0;
25971
27043
  const endRow = range ? Math.max(range.startRowIndex, range.endRowIndex) : current.baseRows.length - 1;
25972
27044
  const startColumn = range ? Math.max(Math.min(range.startColumnIndex, range.endColumnIndex), 0) : 0;
25973
27045
  const endColumn = range ? Math.max(range.startColumnIndex, range.endColumnIndex) : current.leafColumns.length - 1;
25974
- 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;
25975
27058
  const width = columns.reduce((total, column) => total + column.width, 0);
25976
- const rowCount = range ? endRow - startRow + 1 : current.baseRows.length;
27059
+ const rowCount = selectedRowIndices?.length ?? current.baseRows.length;
25977
27060
  const dpr = source.ownerDocument.defaultView?.devicePixelRatio || 1;
25978
27061
  const zoom = host.tableZoom || 1;
25979
27062
  assertTableImageSize(
25980
27063
  Math.ceil(width * zoom * dpr),
25981
27064
  Math.ceil(Math.max(rowCount, 1) * current.rowHeight * zoom * dpr)
25982
27065
  );
25983
- const rows = range ? current.displayRows.slice(startRow, endRow + 1) : current.baseRows;
27066
+ const rows = selectedRowIndices ? selectedRowIndices.map((index) => current.displayRows[index]) : current.baseRows;
25984
27067
  const keys = new Set(columns.map((column) => column.key));
25985
27068
  if (!range) state?.groupColumnKeys.forEach((key) => keys.add(key));
25986
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
+ };
25987
27084
  const cloneColumns2 = (items) => items.flatMap((column) => {
25988
27085
  const children = column.children ? cloneColumns2(column.children) : void 0;
25989
27086
  if (children ? !children.length : !keys.has(String(column.key)))
@@ -25996,8 +27093,21 @@ function renderTableCopyImage(host, scope) {
25996
27093
  children,
25997
27094
  fixed: void 0,
25998
27095
  width: widths.get(String(column.key)) ?? column.width,
25999
- onCell: onCell && range ? (value, record, index, context) => onCell(value, record, index + startRow, context) : onCell,
26000
- 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
26001
27111
  }
26002
27112
  ];
26003
27113
  });
@@ -26086,7 +27196,7 @@ function renderTableCopyImage(host, scope) {
26086
27196
  fixedLeftBoundarySkips: [],
26087
27197
  getNestedTableHighlight: (params) => host.animationController.getNestedTableHighlight({
26088
27198
  ...params,
26089
- rowIndex: params.rowIndex + startRow
27199
+ rowIndex: selectedRowIndices?.[params.rowIndex] ?? params.rowIndex + startRow
26090
27200
  })
26091
27201
  });
26092
27202
  return canvas;
@@ -26536,6 +27646,7 @@ function buildHeaderDataIndexOptions(rows, columns) {
26536
27646
 
26537
27647
  // src/features/overlay/context-menu-builder.ts
26538
27648
  function createListTableContextMenuActions(params) {
27649
+ const getExportColumns = () => params.getExportColumns?.() ?? params.getColumns();
26539
27650
  return {
26540
27651
  onCopyHeaderCell: (columnKey) => {
26541
27652
  params.hideMenu();
@@ -26591,50 +27702,82 @@ function createListTableContextMenuActions(params) {
26591
27702
  onExportJson: () => {
26592
27703
  params.hideMenu();
26593
27704
  params.startExportTask(
26594
- (fileName) => exportAllRowsJsonContent({
27705
+ (fileName, settings) => exportAllRowsJsonContent({
26595
27706
  fileName,
26596
27707
  rows: params.getAllRows(),
26597
- columns: params.getColumns()
27708
+ columns: getExportColumns(),
27709
+ settings
26598
27710
  }),
26599
27711
  "\u6B63\u5728\u5BFC\u51FA JSON\u2026",
26600
- buildTableExportFileName("json")
27712
+ buildTableExportFileName("json"),
27713
+ {
27714
+ columns: getListTableExportColumnChoices(
27715
+ getExportColumns(),
27716
+ params.getExportSortState?.()
27717
+ ),
27718
+ includeSummary: params.getExportSummaryEnabled?.() ?? false
27719
+ }
26601
27720
  );
26602
27721
  },
26603
27722
  onExportCsv: () => {
26604
27723
  params.hideMenu();
26605
27724
  params.startExportTask(
26606
- (fileName) => exportAllRowsCsvContent({
27725
+ (fileName, settings) => exportAllRowsCsvContent({
26607
27726
  fileName,
26608
27727
  rows: params.getAllRows(),
26609
- columns: params.getColumns()
27728
+ columns: getExportColumns(),
27729
+ settings
26610
27730
  }),
26611
27731
  "\u6B63\u5728\u5BFC\u51FA CSV\u2026",
26612
- buildTableExportFileName("csv")
27732
+ buildTableExportFileName("csv"),
27733
+ {
27734
+ columns: getListTableExportColumnChoices(
27735
+ getExportColumns(),
27736
+ params.getExportSortState?.()
27737
+ ),
27738
+ includeSummary: params.getExportSummaryEnabled?.() ?? false
27739
+ }
26613
27740
  );
26614
27741
  },
26615
27742
  onExportTxt: () => {
26616
27743
  params.hideMenu();
26617
27744
  params.startExportTask(
26618
- (fileName) => exportAllRowsTxtContent({
27745
+ (fileName, settings) => exportAllRowsTxtContent({
26619
27746
  fileName,
26620
27747
  rows: params.getAllRows(),
26621
- columns: params.getColumns()
27748
+ columns: getExportColumns(),
27749
+ settings
26622
27750
  }),
26623
27751
  "\u6B63\u5728\u5BFC\u51FA TXT\u2026",
26624
- buildTableExportFileName("txt")
27752
+ buildTableExportFileName("txt"),
27753
+ {
27754
+ columns: getListTableExportColumnChoices(
27755
+ getExportColumns(),
27756
+ params.getExportSortState?.()
27757
+ ),
27758
+ includeSummary: params.getExportSummaryEnabled?.() ?? false
27759
+ }
26625
27760
  );
26626
27761
  },
26627
27762
  onExportXlsx: () => {
26628
27763
  params.hideMenu();
26629
27764
  params.startExportTask(
26630
- (fileName) => exportAllRowsXlsxContent({
27765
+ (fileName, settings) => exportAllRowsXlsxContent({
26631
27766
  includeSummary: params.getExportSummaryEnabled?.() ?? false,
26632
27767
  fileName,
26633
27768
  rows: params.getAllRows(),
26634
- columns: params.getColumns()
27769
+ columns: getExportColumns(),
27770
+ settings
26635
27771
  }),
26636
27772
  "\u6B63\u5728\u5BFC\u51FA XLSX\u2026",
26637
- buildTableExportFileName("xlsx")
27773
+ buildTableExportFileName("xlsx"),
27774
+ {
27775
+ columns: getListTableExportColumnChoices(
27776
+ getExportColumns(),
27777
+ params.getExportSortState?.()
27778
+ ),
27779
+ includeSummary: params.getExportSummaryEnabled?.() ?? false
27780
+ }
26638
27781
  );
26639
27782
  },
26640
27783
  onFreezeRow: params.onFreezeRow,
@@ -26789,6 +27932,7 @@ function buildListTableContextMenuConfig(params) {
26789
27932
  const enabledFeatures = resolveEnabledContextMenuFeatures(contextMenu);
26790
27933
  const isTableSettingsArea = isHeaderArea || isExpandedArea;
26791
27934
  const isBodyArea = bodyRowIndex != null;
27935
+ const canResetTable = isTableSettingsArea || isBodyArea;
26792
27936
  const isSelectionColumn = columnKey === ROW_SELECTION_COLUMN_KEY || columnKey === ROW_SELECTOR_COLUMN_KEY;
26793
27937
  const enableCopy = enabledFeatures.has("copy");
26794
27938
  const enableChart = enabledFeatures.has("chart");
@@ -26907,19 +28051,19 @@ function buildListTableContextMenuConfig(params) {
26907
28051
  onUnfreezeRow: enableFreeze && isBodyArea && frozenRowPosition != null ? () => {
26908
28052
  actions.onUnfreezeRow(bodyRowIndex);
26909
28053
  } : void 0,
26910
- onResetFilter: enableReset && isTableSettingsArea ? actions.onResetFilter : void 0,
26911
- onResetSort: enableReset && isTableSettingsArea ? actions.onResetSort : void 0,
28054
+ onResetFilter: enableReset && canResetTable ? actions.onResetFilter : void 0,
28055
+ onResetSort: enableReset && canResetTable ? actions.onResetSort : void 0,
26912
28056
  onSortAscend: canSortCurrentColumn && actions.onSortAscend ? () => actions.onSortAscend?.(columnKey) : void 0,
26913
28057
  onSortDescend: canSortCurrentColumn && actions.onSortDescend ? () => actions.onSortDescend?.(columnKey) : void 0,
26914
28058
  onClearSort: canSortCurrentColumn && actions.onClearSort ? () => actions.onClearSort?.(columnKey) : void 0,
26915
28059
  onFixLeft: enableFreeze && isHeaderArea && !isSelectionColumn && actions.onFixLeft ? () => actions.onFixLeft?.(columnKey) : void 0,
26916
28060
  onFixRight: enableFreeze && isHeaderArea && !isSelectionColumn && actions.onFixRight ? () => actions.onFixRight?.(columnKey) : void 0,
26917
28061
  onUnfix: enableFreeze && isHeaderArea && !isSelectionColumn && actions.onUnfix ? () => actions.onUnfix?.(columnKey) : void 0,
26918
- onResetHeader: enableReset && isTableSettingsArea ? actions.onResetHeader : void 0,
26919
- onResetGrouping: enableReset && isTableSettingsArea ? actions.onResetGrouping : void 0,
26920
- onResetSummary: enableReset && isTableSettingsArea ? actions.onResetSummary : void 0,
26921
- onResetAlignment: enableReset && isTableSettingsArea ? actions.onResetAlignment : void 0,
26922
- 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,
26923
28067
  onAutoFitCurrentColumn: enableColumnWidth && isHeaderArea && !isSelectionColumn && actions.onAutoFitCurrentColumn ? () => actions.onAutoFitCurrentColumn?.(columnKey) : void 0,
26924
28068
  onAutoFitAllColumns: enableColumnWidth && isTableSettingsArea && !isSelectionColumn && actions.onAutoFitAllColumns ? actions.onAutoFitAllColumns : void 0,
26925
28069
  onAutoFitFillContainer: enableColumnWidth && isTableSettingsArea && !isSelectionColumn && actions.onAutoFitFillContainer ? actions.onAutoFitFillContainer : void 0,
@@ -27160,6 +28304,8 @@ function showHeaderContextMenu(params) {
27160
28304
  onChangeSummaryShowTypePrefix,
27161
28305
  onChangeColumnSettingsCache,
27162
28306
  onChangeCachePreference,
28307
+ onChangeScrollbar,
28308
+ onResetScrollbar,
27163
28309
  summaryVisible,
27164
28310
  getSelectionRange,
27165
28311
  onCopySelection,
@@ -27173,6 +28319,40 @@ function showHeaderContextMenu(params) {
27173
28319
  const isExpandedArea = result.area === "expanded-row" || result.columnKey === "__expanded_row__";
27174
28320
  const isSelectionColumn = result.columnKey === ROW_SELECTION_COLUMN_KEY || result.columnKey === ROW_SELECTOR_COLUMN_KEY;
27175
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
+ }
27176
28356
  if (!isHeaderArea && !isBodyArea && !groupingBarArea && !isExpandedArea || !result.columnKey && !groupingBarArea && !isExpandedArea || isHeaderArea && isSelectionColumn && !isChartCorner) {
27177
28357
  headerContextMenu.hide();
27178
28358
  return false;
@@ -27194,8 +28374,8 @@ function showHeaderContextMenu(params) {
27194
28374
  onSelectBodyRow?.(bodyRowIndex, contextColumnKey);
27195
28375
  }
27196
28376
  const frozenRowPosition = bodyRowIndex == null ? null : getFrozenRowPosition2(bodyRowIndex);
27197
- headerContextMenu.show(
27198
- buildListTableContextMenuConfig({
28377
+ headerContextMenu.show({
28378
+ ...buildListTableContextMenuConfig({
27199
28379
  storageKey: options.storageKey,
27200
28380
  cacheColumnSettings: options.cacheColumnSettings,
27201
28381
  cachePreferences: options.cachePreferences,
@@ -27227,9 +28407,11 @@ function showHeaderContextMenu(params) {
27227
28407
  columnCount: getColumns().length,
27228
28408
  actions: createListTableContextMenuActions({
27229
28409
  getExportSummaryEnabled: params.getExportSummaryEnabled,
28410
+ getExportSortState: params.getExportSortState,
27230
28411
  getRows,
27231
28412
  getAllRows,
27232
28413
  getColumns,
28414
+ getExportColumns: params.getExportColumns,
27233
28415
  getHeaderTree,
27234
28416
  hideMenu: () => {
27235
28417
  headerContextMenu.hide();
@@ -27281,8 +28463,15 @@ function showHeaderContextMenu(params) {
27281
28463
  onOpenSearch,
27282
28464
  onCopyCustomSelection
27283
28465
  })
27284
- })
27285
- );
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
+ });
27286
28475
  return true;
27287
28476
  }
27288
28477
  function resolveContextMenuDensity(rowHeight) {
@@ -27310,7 +28499,9 @@ function createListTableChartData(params) {
27310
28499
  rowIndex,
27311
28500
  selectionRange,
27312
28501
  selectedRowRange,
27313
- selectedColumnRange
28502
+ selectedColumnRange,
28503
+ selectedColumnKeys,
28504
+ getRowKey: getRowKey2
27314
28505
  } = params;
27315
28506
  const startRow = Math.max(
27316
28507
  selectionRange?.startRowIndex ?? selectedRowRange?.start ?? Math.max((rowIndex ?? 0) - 10, 0),
@@ -27323,7 +28514,7 @@ function createListTableChartData(params) {
27323
28514
  const selectedColumns = selectionRange ? columns.slice(
27324
28515
  Math.max(selectionRange.startColumnIndex, 0),
27325
28516
  selectionRange.endColumnIndex + 1
27326
- ) : selectedRowRange ? columns : selectedColumnRange ? columns.slice(
28517
+ ) : selectedRowRange ? columns : selectedColumnKeys != null ? columns.filter((column) => selectedColumnKeys.includes(column.key)) : selectedColumnRange ? columns.slice(
27327
28518
  Math.max(selectedColumnRange.start, 0),
27328
28519
  selectedColumnRange.end + 1
27329
28520
  ) : columns.filter((column) => column.key === columnKey);
@@ -27362,7 +28553,8 @@ function createListTableChartData(params) {
27362
28553
  const labels = sampledRows.map(
27363
28554
  (_, index) => labelIndex >= 0 ? values[labelIndex]?.[index] || String(startRow + index + 1) : String(startRow + index + 1)
27364
28555
  );
27365
- 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 };
27366
28558
  }
27367
28559
  function parseChartNumber(value) {
27368
28560
  const normalized = value.trim().replace(/[,,\s¥¥$]/g, "");
@@ -27375,6 +28567,9 @@ function parseChartNumber(value) {
27375
28567
  // src/features/tooltip/tooltip-feature.ts
27376
28568
  function getHeaderActionTooltipText(result, sortState) {
27377
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
+ }
27378
28573
  if (area !== "sort-trigger" && area !== "filter-trigger" && area !== "fixed-trigger") {
27379
28574
  return null;
27380
28575
  }
@@ -28419,6 +29614,8 @@ var ListTableEventController = class {
28419
29614
  __publicField(this, "cellSelectionAutoScrollVelocity", { x: 0, y: 0 });
28420
29615
  __publicField(this, "cellSelectionAutoScrollTargetVelocity", { x: 0, y: 0 });
28421
29616
  __publicField(this, "continuousScrollTimer", null);
29617
+ __publicField(this, "scrollbarButtonFeedbackTimer", null);
29618
+ __publicField(this, "scrollbarPartFeedbackTimer", null);
28422
29619
  __publicField(this, "pendingEditableCellRowSelectionTimer", null);
28423
29620
  __publicField(this, "outsideClickDocument", null);
28424
29621
  __publicField(this, "outsideClickStarted", null);
@@ -28429,7 +29626,13 @@ var ListTableEventController = class {
28429
29626
  __publicField(this, "handleDocumentClick", (event) => {
28430
29627
  const startedOutside = this.outsideClickStarted ?? !this.isEventTargetInsideTable(event.target);
28431
29628
  this.outsideClickStarted = null;
28432
- 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;
28433
29636
  this.host.clearBodySelection();
28434
29637
  this.host.render();
28435
29638
  });
@@ -28469,11 +29672,19 @@ var ListTableEventController = class {
28469
29672
  rowCount: this.host.getDisplayData().length,
28470
29673
  columnKeys: this.host.leafColumns.filter((column) => !isSystemColumnKey(column.key)).map((column) => column.key)
28471
29674
  };
28472
- const target = direction ? resolveAdjacentCellSelection({ ...navigationParams, direction }) : resolveTabCellSelection({
29675
+ const target = direction ? resolveAdjacentCellSelection({
28473
29676
  ...navigationParams,
28474
- reverse: event.shiftKey
29677
+ direction,
29678
+ verticalWrap: this.host.options.keyboardNavigation?.verticalWrap
29679
+ }) : resolveTabCellSelection({
29680
+ ...navigationParams,
29681
+ reverse: event.shiftKey,
29682
+ tabWrap: this.host.options.keyboardNavigation?.tabWrap
28475
29683
  });
28476
29684
  if (!target) {
29685
+ if (event.key === "Tab" && this.host.options.keyboardNavigation?.tabWrap === false) {
29686
+ event.preventDefault();
29687
+ }
28477
29688
  return;
28478
29689
  }
28479
29690
  event.preventDefault();
@@ -28585,6 +29796,9 @@ var ListTableEventController = class {
28585
29796
  result.area === "resize-handle" ? "col-resize" : result.cursor ?? ""
28586
29797
  );
28587
29798
  const nextHover = {
29799
+ scrollbarRole: result.scrollbarRole,
29800
+ pressedScrollbarButton: this.host.hover.pressedScrollbarButton,
29801
+ pressedScrollbarPart: this.host.hover.pressedScrollbarPart,
28588
29802
  rowIndex: result.rowIndex ?? null,
28589
29803
  columnKey: result.columnKey ?? null,
28590
29804
  contentIndex: result.contentIndex,
@@ -28594,7 +29808,7 @@ var ListTableEventController = class {
28594
29808
  nestedColumnIndex: result.nestedColumnIndex,
28595
29809
  area: result.area
28596
29810
  };
28597
- 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;
28598
29812
  const hitChanged = didHitResultChange(
28599
29813
  this.host.debugManager.getSnapshot()?.hitTest,
28600
29814
  result
@@ -28637,7 +29851,8 @@ var ListTableEventController = class {
28637
29851
  zoom: this.host.tableZoom
28638
29852
  });
28639
29853
  const result = this.host.resolvePointerHit(position.x, position.y);
28640
- 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)) {
28641
29856
  return;
28642
29857
  }
28643
29858
  if (this.host.selection.textSelection) {
@@ -28669,6 +29884,28 @@ var ListTableEventController = class {
28669
29884
  if ((result.area === "body" || result.area === "body-content") && result.columnKey === ROW_SELECTOR_COLUMN_KEY && typeof result.rowIndex === "number") {
28670
29885
  event.preventDefault();
28671
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
+ }
28672
29909
  this.host.selection = {
28673
29910
  rowIndex: result.rowIndex,
28674
29911
  columnKey: ROW_SELECTOR_COLUMN_KEY
@@ -28679,7 +29916,11 @@ var ListTableEventController = class {
28679
29916
  this.cellSelectionDrag = {
28680
29917
  kind: "row",
28681
29918
  startRowIndex: result.rowIndex,
28682
- startColumnIndex: -1
29919
+ startColumnIndex: -1,
29920
+ pointerStartRowIndex: result.rowIndex,
29921
+ pointerStartColumnIndex: -1,
29922
+ mode: "replace",
29923
+ baseRangeHighlight: this.host.rangeHighlight
28683
29924
  };
28684
29925
  this.cellSelectionDragged = false;
28685
29926
  this.cellSelectionPointer = {
@@ -28693,20 +29934,41 @@ var ListTableEventController = class {
28693
29934
  this.host.render(result);
28694
29935
  return;
28695
29936
  }
28696
- 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) {
28697
29938
  const columnIndex = this.host.leafColumns.findIndex(
28698
29939
  (column) => column.key === result.columnKey
28699
29940
  );
28700
29941
  if (columnIndex >= 0) {
28701
29942
  const nestedSelectionCleared = this.clearNestedTableHighlight();
28702
- this.host.rangeHighlight = {
28703
- ...this.host.rangeHighlight,
28704
- selectedCellRange: null
28705
- };
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
+ );
28706
29963
  this.cellSelectionDrag = {
28707
29964
  kind: "cell",
28708
- startRowIndex: result.rowIndex,
28709
- 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
28710
29972
  };
28711
29973
  this.cellSelectionDragged = false;
28712
29974
  this.cellSelectionPointer = {
@@ -28720,6 +29982,7 @@ var ListTableEventController = class {
28720
29982
  if (nestedSelectionCleared) {
28721
29983
  this.host.render(result);
28722
29984
  }
29985
+ if (modifyingSelection) return;
28723
29986
  }
28724
29987
  }
28725
29988
  if (result.area === "resize-handle" && result.columnKey) {
@@ -28810,11 +30073,39 @@ var ListTableEventController = class {
28810
30073
  result.scrollbarRole === "thumb" ? "grabbing" : "pointer"
28811
30074
  );
28812
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();
28813
30090
  this.stepScrollbar(result.area, -1, viewport);
28814
30091
  this.startContinuousScroll(result.area, -1, viewport);
28815
30092
  return;
28816
30093
  }
28817
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();
28818
30109
  this.stepScrollbar(result.area, 1, viewport);
28819
30110
  this.startContinuousScroll(result.area, 1, viewport);
28820
30111
  return;
@@ -28826,9 +30117,15 @@ var ListTableEventController = class {
28826
30117
  }
28827
30118
  const { thumb, track } = horizontalScrollbar;
28828
30119
  if (result.scrollbarRole === "track") {
30120
+ this.flashScrollbarPart("horizontal", "track");
28829
30121
  this.jumpScrollbar("horizontal", position.x, track, thumb, viewport);
28830
30122
  return;
28831
30123
  }
30124
+ this.host.hover = {
30125
+ ...this.host.hover,
30126
+ pressedScrollbarPart: { axis: "horizontal", role: "thumb" }
30127
+ };
30128
+ this.host.scheduleRender();
28832
30129
  this.host.draggingScrollbar = {
28833
30130
  axis: "horizontal",
28834
30131
  pointerOffset: position.x - thumb.x,
@@ -28844,9 +30141,15 @@ var ListTableEventController = class {
28844
30141
  }
28845
30142
  const { thumb, track } = verticalScrollbar;
28846
30143
  if (result.scrollbarRole === "track") {
30144
+ this.flashScrollbarPart("vertical", "track");
28847
30145
  this.jumpScrollbar("vertical", position.y, track, thumb, viewport);
28848
30146
  return;
28849
30147
  }
30148
+ this.host.hover = {
30149
+ ...this.host.hover,
30150
+ pressedScrollbarPart: { axis: "vertical", role: "thumb" }
30151
+ };
30152
+ this.host.scheduleRender();
28850
30153
  this.host.draggingScrollbar = {
28851
30154
  axis: "vertical",
28852
30155
  pointerOffset: position.y - thumb.y,
@@ -28959,15 +30262,47 @@ var ListTableEventController = class {
28959
30262
  getRows: () => this.host.getDisplayData(),
28960
30263
  getAllRows: () => this.host.options.dataSource,
28961
30264
  getExportSummaryEnabled: () => Boolean(this.host.currentRenderSnapshot?.hasSummary),
30265
+ getExportSortState: () => this.host.getSortState(),
30266
+ getExportColumns: () => getListTableExportCandidateColumns(this.host.options.columns),
28962
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
+ },
28963
30296
  onCreateChart: () => createListTableChartData({
28964
30297
  rows: this.host.getDisplayData(),
28965
30298
  columns: this.host.leafColumns,
28966
30299
  columnKey: contextResult.columnKey ?? "",
28967
- rowIndex: typeof contextResult.rowIndex === "number" ? contextResult.rowIndex : null,
30300
+ rowIndex: this.host.selection.rowIndex ?? (typeof contextResult.rowIndex === "number" ? contextResult.rowIndex : null),
28968
30301
  selectionRange: this.host.rangeHighlight.selectedCellRange,
28969
30302
  selectedRowRange: this.host.rangeHighlight.selectedRowRange,
28970
- 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
28971
30306
  }),
28972
30307
  onExportImage: (scope) => this.copyImage(scope, event, true),
28973
30308
  canCopySelectionImage: resolveImageSelection(this.host) != null,
@@ -28991,8 +30326,8 @@ var ListTableEventController = class {
28991
30326
  getColumns: () => this.host.leafColumns,
28992
30327
  getHeaderTree: () => this.host.headerTree,
28993
30328
  getFrozenRowPosition: (rowIndex) => this.host.getFrozenRowPosition(rowIndex),
28994
- startExportTask: (task, text, defaultFileName) => {
28995
- this.host.startExportTask(task, text, defaultFileName);
30329
+ startExportTask: (task, text, defaultFileName, dialog) => {
30330
+ this.host.startExportTask(task, text, defaultFileName, dialog);
28996
30331
  },
28997
30332
  contentDownload: this.host.resolveContentDownloadAction(hitContent),
28998
30333
  onSelectHeader: (columnKey, rect) => {
@@ -29001,18 +30336,22 @@ var ListTableEventController = class {
29001
30336
  this.host.selectHeader(columnKey, rect);
29002
30337
  },
29003
30338
  onSelectBodyRow: (rowIndex, columnKey) => {
29004
- const selectedCellRange = this.host.rangeHighlight.selectedCellRange;
29005
30339
  const columnIndex = this.host.leafColumns.findIndex(
29006
30340
  (column) => column.key === columnKey
29007
30341
  );
30342
+ const isSelectedCell = columnIndex >= 0 && isCellInHighlight(this.host.rangeHighlight, rowIndex, columnIndex);
29008
30343
  this.host.selection = {
29009
30344
  rowIndex,
29010
30345
  columnKey
29011
30346
  };
29012
30347
  this.host.clearSelectedHeader();
29013
- if (!selectedCellRange || rowIndex < selectedCellRange.startRowIndex || rowIndex > selectedCellRange.endRowIndex || columnIndex < selectedCellRange.startColumnIndex || columnIndex > selectedCellRange.endColumnIndex) {
30348
+ if (!isSelectedCell && !isRowInHighlight(this.host.rangeHighlight, rowIndex)) {
29014
30349
  this.host.rangeHighlight = resolveNextRowHighlightForContextMenu({
29015
- current: this.host.rangeHighlight,
30350
+ current: {
30351
+ ...this.host.rangeHighlight,
30352
+ selectedCellRange: null,
30353
+ selectedCellRanges: null
30354
+ },
29016
30355
  rowIndex
29017
30356
  });
29018
30357
  }
@@ -29308,6 +30647,14 @@ var ListTableEventController = class {
29308
30647
  const result = event.shiftKey && rawResult.area === "column-collapse-trigger" ? this.host.resolvePointerHit(position.x, position.y, {
29309
30648
  excludeKinds: ["column-collapse-trigger"]
29310
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
+ };
29311
30658
  if (result.area === "grouping-remove" && result.columnKey) {
29312
30659
  this.host.removeGroupingColumn?.(result.columnKey);
29313
30660
  this.host.suppressNextClick = true;
@@ -29330,6 +30677,11 @@ var ListTableEventController = class {
29330
30677
  this.host.suppressNextClick = false;
29331
30678
  return;
29332
30679
  }
30680
+ if (result.area === "empty" && this.host.options.clearSelectionOnClick?.blankCanvas) {
30681
+ this.clearTableHighlight();
30682
+ this.host.render(result);
30683
+ return;
30684
+ }
29333
30685
  const isNestedTableHit = result.contentKey === "nested-table";
29334
30686
  if (isNestedTableHit) {
29335
30687
  this.clearParentTableHighlight();
@@ -29361,6 +30713,7 @@ var ListTableEventController = class {
29361
30713
  selectedRowRange: rowCount > 0 ? { start: 0, end: rowCount - 1 } : null,
29362
30714
  selectedColumnAnchorKey: this.host.leafColumns[0]?.key ?? null,
29363
30715
  selectedColumnRange: columnRangeEnd >= 0 ? { start: 0, end: columnRangeEnd } : null,
30716
+ selectedColumnKeys: null,
29364
30717
  selectedCellRange: rowCount > 0 && columnCount > 0 ? {
29365
30718
  startRowIndex: 0,
29366
30719
  endRowIndex: rowCount - 1,
@@ -29385,6 +30738,7 @@ var ListTableEventController = class {
29385
30738
  selectedRowRange: rowCount > 0 ? { start: 0, end: rowCount - 1 } : null,
29386
30739
  selectedColumnAnchorKey: this.host.leafColumns[0]?.key ?? null,
29387
30740
  selectedColumnRange: columnCount > 0 ? { start: 0, end: columnCount - 1 } : null,
30741
+ selectedColumnKeys: null,
29388
30742
  selectedCellRange: rowCount > 0 && columnCount > 0 ? {
29389
30743
  startRowIndex: 0,
29390
30744
  endRowIndex: rowCount - 1,
@@ -29405,7 +30759,48 @@ var ListTableEventController = class {
29405
30759
  if ((result.area === "body" || result.area === "body-content") && result.columnKey === ROW_SELECTOR_COLUMN_KEY && typeof result.rowIndex === "number") {
29406
30760
  this.host.clearSelectedHeader();
29407
30761
  this.host.summarySelected = false;
29408
- 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
+ }
29409
30804
  this.host.render(result);
29410
30805
  this.host.updateTooltip(result);
29411
30806
  return;
@@ -29417,13 +30812,20 @@ var ListTableEventController = class {
29417
30812
  }
29418
30813
  }
29419
30814
  this.host.summarySelected = result.area === "summary";
29420
- 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) {
29421
30816
  this.host.clearBodySelection();
29422
- this.host.resetRangeHighlightForHeaderSelection(event.shiftKey);
29423
- this.host.selectHeader(
29424
- result.columnKey,
29425
- result.area === "header" ? result.rect : null
30817
+ this.host.resetRangeHighlightForHeaderSelection(
30818
+ event.shiftKey,
30819
+ toggleColumn
29426
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
+ }
29427
30829
  }
29428
30830
  if (result.area === "summary") {
29429
30831
  if (!result.columnKey) {
@@ -29441,7 +30843,7 @@ var ListTableEventController = class {
29441
30843
  this.host.updateTooltip(result);
29442
30844
  return;
29443
30845
  }
29444
- 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");
29445
30847
  if ((result.area === "body" || result.area === "body-content") && typeof result.rowIndex === "number" && result.columnKey) {
29446
30848
  this.host.clearSelectedHeader();
29447
30849
  this.host.updateRangeHighlightForBodyInteraction({
@@ -29450,15 +30852,26 @@ var ListTableEventController = class {
29450
30852
  shiftKey: event.shiftKey,
29451
30853
  rowSpan: result.rowSpan
29452
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
+ }
29453
30865
  }
29454
- 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) {
29455
30867
  this.host.rangeHighlight = {
29456
30868
  ...this.host.rangeHighlight,
29457
30869
  selectedCellRange: null
29458
30870
  };
29459
30871
  this.host.updateRangeHighlightForHeaderClick(
29460
30872
  result.columnKey,
29461
- event.shiftKey
30873
+ event.shiftKey,
30874
+ toggleColumn
29462
30875
  );
29463
30876
  }
29464
30877
  if (isHeaderRangeGesture) {
@@ -29794,12 +31207,24 @@ var ListTableEventController = class {
29794
31207
  } else if (!activated && this.host.draggingReorder.columnKey) {
29795
31208
  const columnKey = this.host.draggingReorder.columnKey;
29796
31209
  this.host.clearBodySelection();
29797
- this.host.resetRangeHighlightForHeaderSelection(event.shiftKey);
29798
- 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(
29799
31224
  columnKey,
29800
- this.host.draggingReorder.selectedHeaderRect
31225
+ event.shiftKey,
31226
+ toggleColumn
29801
31227
  );
29802
- this.host.updateRangeHighlightForHeaderClick(columnKey, event.shiftKey);
29803
31228
  this.host.suppressNextClick = true;
29804
31229
  this.host.render();
29805
31230
  }
@@ -30026,6 +31451,14 @@ var ListTableEventController = class {
30026
31451
  this.host.render();
30027
31452
  }
30028
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
+ }
30029
31462
  this.cancelPendingEditableCellRowSelection();
30030
31463
  this.stopContinuousScroll();
30031
31464
  this.stopTextSelectionAutoScroll();
@@ -30155,10 +31588,11 @@ var ListTableEventController = class {
30155
31588
  getRows: () => resolveNestedTableDisplayRows(content, getNestedState()),
30156
31589
  getAllRows: () => content.dataSource,
30157
31590
  getExportSummaryEnabled: () => getNestedState()?.summaryVisible === true,
31591
+ getExportSortState: () => getNestedState()?.sortState ?? [],
30158
31592
  getColumns: () => columns,
30159
31593
  getHeaderTree: () => headerTree,
30160
31594
  hideMenu,
30161
- startExportTask: (task, text, defaultFileName) => this.host.startExportTask(task, text, defaultFileName),
31595
+ startExportTask: (task, text, defaultFileName, dialog) => this.host.startExportTask(task, text, defaultFileName, dialog),
30162
31596
  onFreezeRow: () => {
30163
31597
  },
30164
31598
  onUnfreezeRow: () => {
@@ -30629,6 +32063,11 @@ var ListTableEventController = class {
30629
32063
  searchMatch: this.host.rangeHighlight.searchMatch
30630
32064
  };
30631
32065
  }
32066
+ /** 清除可视选区,但保留查找结果与业务行选择数据。 */
32067
+ clearTableHighlight() {
32068
+ this.clearParentTableHighlight();
32069
+ this.clearNestedTableHighlight();
32070
+ }
30632
32071
  /** 父表格进入选区交互时清理全部子表格选区。 */
30633
32072
  clearNestedTableHighlight() {
30634
32073
  this.activeNestedTableHit = null;
@@ -30636,9 +32075,31 @@ var ListTableEventController = class {
30636
32075
  }
30637
32076
  startContinuousScroll(area, direction, viewport) {
30638
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;
30639
32082
  this.continuousScrollTimer = window.setInterval(() => {
30640
- this.stepScrollbar(area, direction, viewport);
30641
- }, 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);
30642
32103
  this.host.canvasHost.bindWindowDragEvents(
30643
32104
  this.handleWindowMouseMove,
30644
32105
  this.handleWindowMouseUp
@@ -30651,22 +32112,19 @@ var ListTableEventController = class {
30651
32112
  }
30652
32113
  }
30653
32114
  stepScrollbar(area, direction, viewport) {
32115
+ const base = this.host.animationController.getWheelScrollTarget() ?? this.host.scroll;
30654
32116
  const nextScroll = stepScrollbarScroll({
30655
- current: this.host.scroll,
32117
+ current: base,
30656
32118
  area,
30657
32119
  direction,
30658
32120
  viewport,
30659
32121
  rowHeight: this.host.currentRenderSnapshot?.rowHeight ?? DEFAULT_ROW_HEIGHT,
30660
32122
  horizontalStep: HORIZONTAL_SCROLL_STEP
30661
32123
  });
30662
- if (!this.host.applyUserScroll(nextScroll)) {
32124
+ if (nextScroll.left === base.left && nextScroll.top === base.top) {
30663
32125
  return;
30664
32126
  }
30665
- this.host.markScrollbarActive();
30666
- this.host.scheduleRender();
30667
- if (area === "vertical-scrollbar") {
30668
- this.host.maybeLoadNextScrollPage();
30669
- }
32127
+ this.host.animationController.startWheelScrollSmoothing(nextScroll);
30670
32128
  }
30671
32129
  jumpScrollbar(axis, pointerValue, track, thumb, viewport) {
30672
32130
  const nextScroll = jumpScrollbarScroll({
@@ -30687,6 +32145,22 @@ var ListTableEventController = class {
30687
32145
  }
30688
32146
  }
30689
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
+ }
30690
32164
  this.host.draggingScrollbar = null;
30691
32165
  this.host.draggingResize = null;
30692
32166
  this.host.draggingReorder = null;
@@ -30700,6 +32174,24 @@ var ListTableEventController = class {
30700
32174
  this.host.markScrollbarActive();
30701
32175
  this.host.render();
30702
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
+ }
30703
32195
  startTextSelection(result, pointerX, pointerY) {
30704
32196
  if (result.area !== "body-content" && result.area !== "header" && result.area !== "summary" || !result.columnKey || !result.textSelectionText || !result.textSelectionFont || !result.textSelectionFragmentKey || !result.rect) {
30705
32197
  return false;
@@ -31047,16 +32539,46 @@ var ListTableEventController = class {
31047
32539
  return false;
31048
32540
  }
31049
32541
  if (drag.kind === "row") {
32542
+ if (drag.mode !== "replace" && !this.cellSelectionDragged && result.rowIndex === drag.pointerStartRowIndex || drag.lastRowIndex === result.rowIndex) {
32543
+ return false;
32544
+ }
31050
32545
  const nextRange2 = {
31051
32546
  start: Math.min(drag.startRowIndex, result.rowIndex),
31052
32547
  end: Math.max(drag.startRowIndex, result.rowIndex)
31053
32548
  };
31054
32549
  const currentRange2 = this.host.rangeHighlight.selectedRowRange;
31055
- if (currentRange2?.start === nextRange2.start && currentRange2.end === nextRange2.end) {
32550
+ if (drag.mode === "replace" && currentRange2?.start === nextRange2.start && currentRange2.end === nextRange2.end) {
31056
32551
  return false;
31057
32552
  }
31058
- this.cellSelectionDragged = this.cellSelectionDragged || result.rowIndex !== drag.startRowIndex;
31059
- 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
+ }
31060
32582
  if (options?.scheduleRender !== false) {
31061
32583
  this.host.scheduleRender(result);
31062
32584
  }
@@ -31068,7 +32590,7 @@ var ListTableEventController = class {
31068
32590
  if (columnIndex < 0) {
31069
32591
  return false;
31070
32592
  }
31071
- 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) {
31072
32594
  return false;
31073
32595
  }
31074
32596
  const nextRange = {
@@ -31078,14 +32600,53 @@ var ListTableEventController = class {
31078
32600
  endColumnIndex: Math.max(drag.startColumnIndex, columnIndex)
31079
32601
  };
31080
32602
  const currentRange = this.host.rangeHighlight.selectedCellRange;
31081
- 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) {
31082
32604
  return false;
31083
32605
  }
31084
32606
  this.cellSelectionDragged = true;
31085
- this.host.rangeHighlight = {
31086
- ...this.host.rangeHighlight,
31087
- selectedCellRange: nextRange
31088
- };
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
+ }
31089
32650
  if (options?.scheduleRender !== false) {
31090
32651
  this.host.scheduleRender(result);
31091
32652
  }
@@ -31169,9 +32730,13 @@ var ListTableEventController = class {
31169
32730
  start: Math.min(startRowIndex, endRowIndex),
31170
32731
  end: Math.max(startRowIndex, endRowIndex)
31171
32732
  },
32733
+ selectedRowRanges: null,
31172
32734
  selectedColumnAnchorKey: null,
31173
32735
  selectedColumnRange: null,
31174
- selectedCellRange: null
32736
+ selectedColumnKeys: null,
32737
+ selectedCellRange: null,
32738
+ selectedCellRanges: null,
32739
+ selectedCellAnchor: null
31175
32740
  };
31176
32741
  }
31177
32742
  };
@@ -31490,6 +33055,7 @@ function createListTableEventHost(core) {
31490
33055
  // 可见行在百万行窗口化后只包含当前视口;事件层需要完整的查询结果,
31491
33056
  // 否则自动列宽等基于全局 dataRowIndex 的逻辑会在深滚动时丢失采样行。
31492
33057
  getDisplayData: () => core.queryController.getDisplayData(),
33058
+ getSortState: () => core.storeFacade.sortState,
31493
33059
  getSummaryValueMap: () => core.summaryController.getSummaryValueMap(),
31494
33060
  getSummaryValuesAsync: (snapshot) => core.summaryController.getSummaryValuesAsync(snapshot),
31495
33061
  scrollTo: (options) => core.scrollTo(options),
@@ -31507,11 +33073,12 @@ function createListTableEventHost(core) {
31507
33073
  clearBodySelection: () => core.selectionController.clearBodySelection(),
31508
33074
  clearSelectedHeader: () => core.selectionController.clearSelectedHeader(),
31509
33075
  selectHeader: (columnKey, rect) => core.selectionController.selectHeader(columnKey, rect),
31510
- resetRangeHighlightForHeaderSelection: (shiftKey) => core.selectionController.resetRangeHighlightForHeaderSelection(shiftKey),
33076
+ resetRangeHighlightForHeaderSelection: (shiftKey, ctrlKey) => core.selectionController.resetRangeHighlightForHeaderSelection(shiftKey, ctrlKey),
31511
33077
  updateRangeHighlightForBodyInteraction: (params) => core.selectionController.updateRangeHighlightForBodyInteraction(params),
31512
- updateRangeHighlightForHeaderClick: (columnKey, shiftKey) => core.selectionController.updateRangeHighlightForHeaderClick(
33078
+ updateRangeHighlightForHeaderClick: (columnKey, shiftKey, ctrlKey) => core.selectionController.updateRangeHighlightForHeaderClick(
31513
33079
  columnKey,
31514
- shiftKey
33080
+ shiftKey,
33081
+ ctrlKey
31515
33082
  ),
31516
33083
  updateRangeHighlightForHeaderContextMenu: (columnKey) => core.selectionController.updateRangeHighlightForHeaderContextMenu(
31517
33084
  columnKey
@@ -31554,7 +33121,7 @@ function createListTableEventHost(core) {
31554
33121
  return snapshot?.columnSegments ?? core.columnSegments;
31555
33122
  },
31556
33123
  persistColumnConfig: () => core.persistenceController.persistColumnConfig(),
31557
- startExportTask: (task, text, defaultFileName) => {
33124
+ startExportTask: (task, text, defaultFileName, dialog) => {
31558
33125
  const configured = core.options.exportOptions?.fileName?.trim();
31559
33126
  const extension = defaultFileName?.match(/\.(xlsx|csv|txt|json)$/i)?.[0];
31560
33127
  const fileName = configured && extension ? configured.replace(/\.(xlsx|csv|txt|json)$/i, "") + extension : defaultFileName;
@@ -31562,7 +33129,9 @@ function createListTableEventHost(core) {
31562
33129
  task,
31563
33130
  text,
31564
33131
  void 0,
31565
- fileName
33132
+ fileName,
33133
+ void 0,
33134
+ dialog
31566
33135
  );
31567
33136
  },
31568
33137
  resolveContentDownloadAction: (content) => core.contentActionController.resolveContentDownloadAction(
@@ -31657,6 +33226,7 @@ var ListTableHeaderActionController = class {
31657
33226
  if (this.options.isEditing() || this.options.getLeafColumns().find((column) => column.key === columnKey)?.allowSort === false) {
31658
33227
  return;
31659
33228
  }
33229
+ this.options.captureSortAnimation?.(columnKey);
31660
33230
  this.options.prepareLocalQuery("sort");
31661
33231
  this.options.setSortState(
31662
33232
  toggleSortingState(
@@ -31674,6 +33244,7 @@ var ListTableHeaderActionController = class {
31674
33244
  if (this.options.isEditing() || this.options.getLeafColumns().find((column) => column.key === columnKey)?.allowSort === false) {
31675
33245
  return;
31676
33246
  }
33247
+ this.options.captureSortAnimation?.(columnKey);
31677
33248
  this.options.prepareLocalQuery("sort");
31678
33249
  this.options.setSortState(
31679
33250
  setSortingOrder(
@@ -31844,6 +33415,7 @@ var ListTableHeaderActionController = class {
31844
33415
  if (this.options.isEditing()) {
31845
33416
  return;
31846
33417
  }
33418
+ this.options.captureSortAnimation?.();
31847
33419
  this.options.prepareLocalQuery("sort");
31848
33420
  const next = resolveResetSortAction(this.options.getFilterState());
31849
33421
  this.options.setSortState(next.sortState);
@@ -31881,6 +33453,7 @@ var ListTableHeaderActionController = class {
31881
33453
  if (!resolved) {
31882
33454
  return;
31883
33455
  }
33456
+ this.options.overlayController.preserveHeaderContextMenuForColumnVisibility();
31884
33457
  this.options.setSelectedHeaderState(
31885
33458
  resolved.selectedHeaderKey,
31886
33459
  resolved.selectedHeaderRect
@@ -31905,6 +33478,9 @@ var ListTableHeaderActionController = class {
31905
33478
  buildHeaderSettingsItems(columns)
31906
33479
  );
31907
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
+ }
31908
33484
  return null;
31909
33485
  }
31910
33486
  deleteHeaderColumn(columnKey) {
@@ -31996,6 +33572,7 @@ var ListTableOverlayController = class {
31996
33572
  __publicField(this, "componentPortal");
31997
33573
  __publicField(this, "getContentZoom");
31998
33574
  __publicField(this, "runtime");
33575
+ __publicField(this, "preserveColumnVisibilityMenu", false);
31999
33576
  __publicField(this, "tooltipAdapter");
32000
33577
  __publicField(this, "filterPanelAdapter");
32001
33578
  __publicField(this, "fixedPopupAdapter");
@@ -32133,6 +33710,7 @@ var ListTableOverlayController = class {
32133
33710
  return this.componentPortal.fixedPopup.isVisible();
32134
33711
  }
32135
33712
  showHeaderContextMenu(config) {
33713
+ this.preserveColumnVisibilityMenu = false;
32136
33714
  this.runtime.headerContextMenuVisible = true;
32137
33715
  this.runtime.headerContextMenuConfig = config;
32138
33716
  const options = this.getOptions();
@@ -32142,6 +33720,7 @@ var ListTableOverlayController = class {
32142
33720
  }
32143
33721
  }
32144
33722
  hideHeaderContextMenu() {
33723
+ this.preserveColumnVisibilityMenu = false;
32145
33724
  this.runtime.headerContextMenuVisible = false;
32146
33725
  this.runtime.headerContextMenuConfig = null;
32147
33726
  this.getOptions().ui?.onHeaderContextMenuRequest?.(null);
@@ -32153,6 +33732,15 @@ var ListTableOverlayController = class {
32153
33732
  }
32154
33733
  return this.componentPortal.headerContextMenu.isVisible();
32155
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
+ }
32156
33744
  updateHeaderContextMenuColumns(columns) {
32157
33745
  const options = this.getOptions();
32158
33746
  if (options.ui?.onHeaderContextMenuRequest && this.runtime.headerContextMenuConfig) {
@@ -32373,7 +33961,7 @@ function applyStoredColumnConfig(columns, storedColumns, cacheColumnSettings = t
32373
33961
  applyToColumns(columns);
32374
33962
  return columnWidthsChanged;
32375
33963
  }
32376
- function buildStoredColumnConfig(columns, sortState, filterState, zoom, summaryRowHeight, density, cacheColumnSettings = true, cachePreferences) {
33964
+ function buildStoredColumnConfig(columns, sortState, filterState, zoom, summaryRowHeight, density, cacheColumnSettings = true, cachePreferences, scrollbar) {
32377
33965
  const result = {};
32378
33966
  const columnOrder = [];
32379
33967
  const collectColumns = (cols, parentKey) => {
@@ -32430,6 +34018,7 @@ function buildStoredColumnConfig(columns, sortState, filterState, zoom, summaryR
32430
34018
  {
32431
34019
  columns: result,
32432
34020
  cacheColumnSettings,
34021
+ ...scrollbar ? { scrollbar } : null,
32433
34022
  ...cacheColumnSettings ? { columnOrder } : null,
32434
34023
  sort: sortState.map((item) => ({ ...item })),
32435
34024
  filters: filterState,
@@ -32469,6 +34058,7 @@ function filterStoredColumnConfig(stored, preferences = {}) {
32469
34058
  if (preferences.summary === false) delete result.summaryRowHeight;
32470
34059
  if (preferences.density === false) delete result.density;
32471
34060
  if (preferences.zoom === false) delete result.zoom;
34061
+ if (preferences.scrollbar === false) delete result.scrollbar;
32472
34062
  return result;
32473
34063
  }
32474
34064
 
@@ -32483,7 +34073,8 @@ function persistListTableColumnConfig(params) {
32483
34073
  summaryRowHeight,
32484
34074
  density,
32485
34075
  cacheColumnSettings,
32486
- cachePreferences
34076
+ cachePreferences,
34077
+ scrollbar
32487
34078
  } = params;
32488
34079
  if (!storageKey) {
32489
34080
  return;
@@ -32499,7 +34090,8 @@ function persistListTableColumnConfig(params) {
32499
34090
  summaryRowHeight,
32500
34091
  density,
32501
34092
  cacheColumnSettings,
32502
- cachePreferences
34093
+ cachePreferences,
34094
+ scrollbar
32503
34095
  )
32504
34096
  );
32505
34097
  }
@@ -32568,6 +34160,7 @@ async function loadListTableStoredConfig(params) {
32568
34160
  stored.columnOrder
32569
34161
  );
32570
34162
  return {
34163
+ scrollbar: stored.scrollbar,
32571
34164
  cacheColumnSettings,
32572
34165
  cachePreferences,
32573
34166
  sortState: cachePreferences.sort !== false && Array.isArray(stored.sort) ? stored.sort.filter(
@@ -32652,6 +34245,10 @@ var ListTablePersistenceController = class {
32652
34245
  this.options.canvasHost.setContentZoom(nextZoom);
32653
34246
  }
32654
34247
  let currentOptions = this.options.getOptions();
34248
+ if (stored.scrollbar) {
34249
+ currentOptions = { ...currentOptions, scrollbar: stored.scrollbar };
34250
+ currentOptions.ui?.onScrollbarChange?.(stored.scrollbar);
34251
+ }
32655
34252
  if (stored.cachePreferences) {
32656
34253
  currentOptions = {
32657
34254
  ...currentOptions,
@@ -32696,10 +34293,16 @@ var ListTablePersistenceController = class {
32696
34293
  }
32697
34294
  }
32698
34295
  this.options.refreshRuntimeColumns();
34296
+ currentOptions.ui?.onColumnSettingsChange?.(
34297
+ cloneColumns(currentOptions.columns)
34298
+ );
32699
34299
  }
32700
34300
  persistColumnConfig() {
32701
34301
  this.invalidate();
32702
34302
  const tableOptions = this.options.getOptions();
34303
+ tableOptions.ui?.onColumnSettingsChange?.(
34304
+ cloneColumns(tableOptions.columns)
34305
+ );
32703
34306
  this.restoredStorageKey = tableOptions.storageKey;
32704
34307
  this.hasRestoredPreferences = true;
32705
34308
  this.pendingWrite = Promise.resolve(
@@ -32707,6 +34310,7 @@ var ListTablePersistenceController = class {
32707
34310
  storageKey: tableOptions.storageKey,
32708
34311
  cacheColumnSettings: tableOptions.cacheColumnSettings,
32709
34312
  cachePreferences: tableOptions.cachePreferences,
34313
+ scrollbar: tableOptions.scrollbar,
32710
34314
  columns: tableOptions.columns,
32711
34315
  sortState: this.options.getSortState(),
32712
34316
  filterState: this.options.getFilterState(),
@@ -32941,6 +34545,7 @@ var ListTableQueryController = class {
32941
34545
  this.invalidateDataCache();
32942
34546
  this.options.hideLoading();
32943
34547
  this.emitQueryChangeWithResolvedRows(action, baseRows);
34548
+ if (action === "sort") this.options.onSortResolved?.();
32944
34549
  this.options.requestRender();
32945
34550
  }).catch(handleFailure);
32946
34551
  };
@@ -34465,6 +36070,7 @@ var ListTableSelectionController = class {
34465
36070
  __publicField(this, "selectedHeaderKey", null);
34466
36071
  __publicField(this, "selectedHeaderRect", null);
34467
36072
  __publicField(this, "summarySelected", false);
36073
+ __publicField(this, "pendingSortSelection", null);
34468
36074
  this.options = options;
34469
36075
  }
34470
36076
  getSelectedHeaderKey() {
@@ -34488,20 +36094,62 @@ var ListTableSelectionController = class {
34488
36094
  this.selectedHeaderRect = null;
34489
36095
  }
34490
36096
  clearBodySelection() {
36097
+ this.pendingSortSelection = null;
34491
36098
  this.options.setSelection({
34492
36099
  rowIndex: null,
34493
36100
  columnKey: null
34494
36101
  });
34495
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
+ }
34496
36143
  setSelectedHeaderState(selectedHeaderKey, selectedHeaderRect) {
34497
36144
  this.selectedHeaderKey = selectedHeaderKey;
34498
36145
  this.selectedHeaderRect = selectedHeaderRect;
34499
36146
  }
34500
- resetRangeHighlightForHeaderSelection(shiftKey) {
36147
+ resetRangeHighlightForHeaderSelection(shiftKey, ctrlKey = false) {
34501
36148
  this.options.setRangeHighlight(
34502
36149
  resolveRangeHighlightForHeaderSelection({
34503
36150
  current: this.options.getRangeHighlight(),
34504
- shiftKey
36151
+ shiftKey,
36152
+ ctrlKey
34505
36153
  })
34506
36154
  );
34507
36155
  }
@@ -34569,7 +36217,7 @@ var ListTableSelectionController = class {
34569
36217
  })
34570
36218
  );
34571
36219
  }
34572
- updateRangeHighlightForHeaderClick(columnKey, shiftKey) {
36220
+ updateRangeHighlightForHeaderClick(columnKey, shiftKey, ctrlKey = false) {
34573
36221
  const leafColumns = this.options.getLeafColumns();
34574
36222
  const targetRange = findHeaderLeafRange({
34575
36223
  columnKey,
@@ -34586,7 +36234,8 @@ var ListTableSelectionController = class {
34586
36234
  leafColumnKeys: leafColumns.map((column) => column.key),
34587
36235
  rangeStartIndex: targetRange.start,
34588
36236
  rangeEndIndex: targetRange.end,
34589
- shiftKey
36237
+ shiftKey,
36238
+ ctrlKey
34590
36239
  })
34591
36240
  );
34592
36241
  }
@@ -34630,7 +36279,8 @@ var ListTableSelectionController = class {
34630
36279
  this.options.setRangeHighlight({
34631
36280
  ...current,
34632
36281
  selectedColumnAnchorKey: null,
34633
- selectedColumnRange: null
36282
+ selectedColumnRange: null,
36283
+ selectedColumnKeys: null
34634
36284
  });
34635
36285
  }
34636
36286
  destroy() {
@@ -34968,6 +36618,44 @@ function createDefaultSummaryScheduler() {
34968
36618
  };
34969
36619
  }
34970
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
+
34971
36659
  // src/engine/controller-assembly.ts
34972
36660
  function assembleListTableCoreControllers(core, options) {
34973
36661
  const getCanvasHostElement = () => core.domHost.getNodes()?.canvasHostElement ?? null;
@@ -35091,14 +36779,24 @@ function assembleListTableCoreControllers(core, options) {
35091
36779
  ...core.storeFacade.rangeHighlight,
35092
36780
  selectedColumnAnchorKey: null,
35093
36781
  selectedColumnRange: null,
36782
+ selectedColumnKeys: null,
35094
36783
  selectedCellRange: null
35095
36784
  };
35096
36785
  };
35097
36786
  core.syncGroupingBar = syncGroupingBar;
35098
36787
  core.syncGroupingFromOptions = () => {
35099
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;
35100
36796
  if (core.options.grouping?.visible !== void 0) {
35101
36797
  core.groupBarVisible = core.options.grouping.visible;
36798
+ } else if (defaultsChanged) {
36799
+ core.groupBarVisible = nextDefaultKeys.length > 0;
35102
36800
  }
35103
36801
  if (core.options.grouping?.columnKeys !== void 0) {
35104
36802
  const validKeys = new Set(
@@ -35109,6 +36807,8 @@ function assembleListTableCoreControllers(core, options) {
35109
36807
  core.groupColumnKeys = core.options.grouping.columnKeys.filter(
35110
36808
  (key, index, keys) => validKeys.has(key) && keys.indexOf(key) === index
35111
36809
  );
36810
+ } else if (defaultsChanged) {
36811
+ core.groupColumnKeys = nextDefaultKeys;
35112
36812
  }
35113
36813
  if (previousColumnKeys.length !== core.groupColumnKeys.length || previousColumnKeys.some(
35114
36814
  (key, index) => key !== core.groupColumnKeys[index]
@@ -35189,7 +36889,8 @@ function assembleListTableCoreControllers(core, options) {
35189
36889
  getQueryRuntimeState,
35190
36890
  showLoading: (text) => core.overlayController.showLoading(text),
35191
36891
  hideLoading: () => core.overlayController.hideLoading(),
35192
- requestRender: () => render()
36892
+ requestRender: () => render(),
36893
+ onSortResolved: () => core.animationController.markSortRowsReady()
35193
36894
  });
35194
36895
  core.summaryController = new ListTableSummaryController({
35195
36896
  getOptions: () => core.options,
@@ -35344,6 +37045,18 @@ function assembleListTableCoreControllers(core, options) {
35344
37045
  persistColumnConfig: () => core.persistenceController.persistColumnConfig(),
35345
37046
  emitQueryChange,
35346
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
+ },
35347
37060
  applyFilterQueryAsync,
35348
37061
  applySortQueryAsync,
35349
37062
  refreshRuntimeColumns,
@@ -35359,6 +37072,7 @@ function assembleListTableCoreControllers(core, options) {
35359
37072
  });
35360
37073
  core.contentActionController = new ListTableContentActionController({
35361
37074
  getSummaryVisible: () => Boolean(getCurrentRenderSnapshot()?.hasSummary),
37075
+ getSortState: () => core.storeFacade.sortState,
35362
37076
  getOptions: () => core.options,
35363
37077
  getLeafColumns: () => core.leafColumns,
35364
37078
  overlayController: core.overlayController,
@@ -35503,6 +37217,9 @@ function assembleListTableCoreControllers(core, options) {
35503
37217
  expandedRowKeys: core.storeFacade.expandedRowKeys
35504
37218
  });
35505
37219
  core.animationController.reconcileCollapseAnimations(snapshot);
37220
+ if (core.animationController.reconcileSortAnimations(snapshot)) {
37221
+ core.selectionController.reconcileSortSelection(snapshot.displayRows);
37222
+ }
35506
37223
  },
35507
37224
  getPublishAccessors: () => ({
35508
37225
  nestedTableLayoutRevision: core.animationController.getNestedTableLayoutRevision(),
@@ -35671,8 +37388,13 @@ function assembleListTableCoreControllers(core, options) {
35671
37388
  core.draggingResize = null;
35672
37389
  core.draggingReorder = null;
35673
37390
  core.suppressNextClick = false;
35674
- core.groupBarVisible = options.grouping?.visible ?? false;
35675
- 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
+ ];
35676
37398
  core.tableZoom = 1;
35677
37399
  }
35678
37400
  function collectGroupingColumnItems(columns) {
@@ -35787,6 +37509,7 @@ var ListTableCore = class {
35787
37509
  * @param options 新配置。
35788
37510
  */
35789
37511
  updateOptions(options) {
37512
+ const preserveContextMenu = options.dataSource !== this.options.dataSource || this.overlayController.shouldPreserveHeaderContextMenuOnOptionsUpdate();
35790
37513
  this.persistenceController.invalidate();
35791
37514
  if (options.storageKey === this.options.storageKey) {
35792
37515
  options = {
@@ -35798,6 +37521,8 @@ var ListTableCore = class {
35798
37521
  options = resolveListTableStyleOptions(options);
35799
37522
  this.animationController.stopCarouselScroll();
35800
37523
  const host = this.asAssemblyHost();
37524
+ const previousLeafColumns = host.leafColumns;
37525
+ const previousSegments = host.columnSegments;
35801
37526
  const shouldResetColumnConfig = shouldPersistIncomingColumnConfig(
35802
37527
  this.options.columnConfigResetRevision,
35803
37528
  options.columnConfigResetRevision
@@ -35826,15 +37551,37 @@ var ListTableCore = class {
35826
37551
  }
35827
37552
  applyInitialColumnVisibility(options.columns);
35828
37553
  ensureColumnKeysInPlace(options.columns);
35829
- if (options.expandable?.expandedRowKeys !== void 0 && !areSameRowKeySet(
37554
+ const expandedRowsChanged = options.expandable?.expandedRowKeys !== void 0 && !areSameRowKeySet(
35830
37555
  this.storeFacade.expandedRowKeys,
35831
37556
  options.expandable.expandedRowKeys
35832
- )) {
37557
+ );
37558
+ if (expandedRowsChanged) {
35833
37559
  this.animationController.setPendingCollapseRowPositions(
35834
37560
  captureRenderedVisibleRowPositions(
35835
37561
  this.renderController.getCurrentRenderSnapshot()
35836
37562
  )
35837
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();
35838
37585
  }
35839
37586
  this.queryController.reset();
35840
37587
  this.options = options;
@@ -35852,7 +37599,10 @@ var ListTableCore = class {
35852
37599
  });
35853
37600
  this.debugManager = new DebugManager(options.debug);
35854
37601
  host.renderer?.updateDebugOptions(options.debug ?? {});
35855
- this.hideFloatingOverlays({ includeLoading: true });
37602
+ this.hideFloatingOverlays({
37603
+ includeLoading: true,
37604
+ preserveContextMenu
37605
+ });
35856
37606
  this.refreshRuntimeColumns();
35857
37607
  if (shouldPersistColumnConfig) {
35858
37608
  this.persistenceController.persistColumnConfig();
@@ -35860,6 +37610,12 @@ var ListTableCore = class {
35860
37610
  this.persistenceController.loadStoredConfig({ restoreQueryState: false });
35861
37611
  }
35862
37612
  this.renderController.render();
37613
+ if (shouldPersistColumnConfig && haveVisibleLeafColumnsChanged(previousLeafColumns, host.leafColumns)) {
37614
+ host.rowActionController.startColumnVisibilityAnimation({
37615
+ previousLeafColumns,
37616
+ previousSegments
37617
+ });
37618
+ }
35863
37619
  host.tableId = options.tableId?.trim() ? options.tableId : host.tableId;
35864
37620
  host.rowDragController.syncRegistration();
35865
37621
  }
@@ -35897,6 +37653,14 @@ var ListTableCore = class {
35897
37653
  resetSorter() {
35898
37654
  this.headerActionController.resetSort();
35899
37655
  }
37656
+ /** 获取当前排序状态,供外部设置面板回显。 */
37657
+ getSortState() {
37658
+ return [...this.storeFacade.sortState];
37659
+ }
37660
+ /** 使用与表头一致的排序逻辑设置指定列。 */
37661
+ setSortOrder(columnKey, order) {
37662
+ this.headerActionController.setSortOrder(columnKey, order);
37663
+ }
35900
37664
  /**
35901
37665
  * 重置筛选状态。
35902
37666
  */
@@ -36002,14 +37766,15 @@ var ListTableCore = class {
36002
37766
  return this;
36003
37767
  }
36004
37768
  hideFloatingOverlays(params) {
36005
- this.componentPortal.hideFloatingLayers();
36006
37769
  if (params?.includeLoading) {
36007
37770
  this.overlayController.hideLoading();
36008
37771
  }
36009
37772
  this.overlayController.hideTooltip();
36010
37773
  this.overlayController.hideFilterPanel();
36011
37774
  this.overlayController.hideFixedPopup();
36012
- this.overlayController.hideHeaderContextMenu();
37775
+ if (!params?.preserveContextMenu) {
37776
+ this.overlayController.hideHeaderContextMenu();
37777
+ }
36013
37778
  }
36014
37779
  refreshRuntimeColumns() {
36015
37780
  this.storeFacade.runtimeColumns = buildRuntimeColumnsMap({
@@ -36091,6 +37856,14 @@ var ListTable = class {
36091
37856
  resetSorter() {
36092
37857
  this.core?.resetSorter();
36093
37858
  }
37859
+ /** 获取当前排序状态。 */
37860
+ getSortState() {
37861
+ return this.core?.getSortState() ?? [];
37862
+ }
37863
+ /** 设置指定列的排序方式。 */
37864
+ setSortOrder(columnKey, order) {
37865
+ this.core?.setSortOrder(columnKey, order);
37866
+ }
36094
37867
  /**
36095
37868
  * 重置筛选状态。
36096
37869
  */