@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.js CHANGED
@@ -1,4 +1,4 @@
1
- import { stringifyDataIndex, getValueByDataIndex, clamp, resolveCanvasScrollbarAxis, copyTextSync, copyText, exportTableToXlsx, formatPreciseNumber, addPreciseNumbers, comparePreciseNumbers, isSameDataIndex, dividePreciseNumber, exportTableToTxt, exportTableToCsv, downloadJsonFile, mapRowsToExportRecords, copyJson, copyRows, parseNumberValue, formatNumberValue, formatPercentValue, formatScientificValue, formatChineseNumber, parseChineseNumber, normalizeDecimalPlaces, roundPreciseNumber, isDateTimeFormatPattern, formatDateTimeValue, formatCustomNumberPattern, isPointInRect as isPointInRect$1 } from '@canvas-components/utils';
1
+ import { stringifyDataIndex, getValueByDataIndex, clamp, resolveCanvasScrollbarAxis, copyTextSync, copyText, resolveCanvasScrollbarHoldSpeed, exportTableToXlsx, formatPreciseNumber, addPreciseNumbers, comparePreciseNumbers, isSameDataIndex, dividePreciseNumber, exportTableToTxt, exportTableToCsv, mapRowsToExportRecords, downloadJsonFile, copyJson, copyRows, parseNumberValue, formatNumberValue, formatPercentValue, formatScientificValue, formatChineseNumber, parseChineseNumber, normalizeDecimalPlaces, roundPreciseNumber, isDateTimeFormatPattern, formatDateTimeValue, formatCustomNumberPattern, isPointInRect as isPointInRect$1 } from '@canvas-components/utils';
2
2
  import { drawBarcodeToCanvas } from '@canvas-components/barcode';
3
3
  import { drawCanvasChartToCanvas, resolveCanvasChartOption, getCanvasChartRenderer, resolveCanvasChartTooltipText } from '@canvas-components/chart';
4
4
  import * as antDesignIcons from '@ant-design/icons-svg';
@@ -1036,6 +1036,11 @@ function getNextRightFixedInsertIndex(columns) {
1036
1036
  }
1037
1037
 
1038
1038
  // src/features/header-settings/header-settings-feature.ts
1039
+ function haveVisibleLeafColumnsChanged(previous, next) {
1040
+ if (previous.length !== next.length) return true;
1041
+ const previousKeys = new Set(previous.map((column) => column.key));
1042
+ return next.some((column) => !previousKeys.has(column.key));
1043
+ }
1039
1044
  function cloneColumns(columns) {
1040
1045
  return columns.map((column) => ({
1041
1046
  ...column,
@@ -1162,6 +1167,7 @@ function saveHeaderSettingsColumn(columns, draft) {
1162
1167
  existing.minWidth = minWidth;
1163
1168
  existing.maxWidth = maxWidth;
1164
1169
  if (draft.order != null) moveColumnToOrder(siblings, existing, draft.order);
1170
+ draft.key = resolveColumnKey(existing);
1165
1171
  return null;
1166
1172
  }
1167
1173
  const dataIndex = draft.dataIndex?.trim();
@@ -1187,6 +1193,7 @@ function saveHeaderSettingsColumn(columns, draft) {
1187
1193
  } else if (!existing) {
1188
1194
  siblings.splice((draft.order ?? maxOrder) - 1, 0, column);
1189
1195
  }
1196
+ draft.key = resolveColumnKey(column);
1190
1197
  return null;
1191
1198
  }
1192
1199
  function findSiblingColumnsByKey(columns, key) {
@@ -1284,6 +1291,9 @@ function toggleHeaderColumnVisibility(params) {
1284
1291
  };
1285
1292
  }
1286
1293
  function resolveCollapsedHeaderColumnMarkers(params) {
1294
+ if (params.showCollapsedColumnIcon === false) {
1295
+ return [];
1296
+ }
1287
1297
  ensureColumnKeysInPlace(params.columns);
1288
1298
  const allLeafKeys = collectLeafColumnKeys(params.columns);
1289
1299
  const excludedKeySet = new Set(params.excludedColumnKeys ?? []);
@@ -1460,6 +1470,7 @@ function buildHeaderSettingsItem(column, totalVisibleLeafCount, parentHidden, or
1460
1470
  minWidth: column.minWidth,
1461
1471
  maxWidth: column.maxWidth,
1462
1472
  fixed: column.fixed,
1473
+ sortable: !column.children?.length && column.allowSort !== false && column.sorter !== false,
1463
1474
  order,
1464
1475
  siblingCount,
1465
1476
  checked,
@@ -1620,6 +1631,23 @@ function applyThemeVariables(wrapper, theme) {
1620
1631
  );
1621
1632
  }
1622
1633
 
1634
+ // src/interaction/pointer.ts
1635
+ function getCanvasPointerPosition(canvas, clientX, clientY) {
1636
+ const rect = canvas.getBoundingClientRect();
1637
+ const scale = getElementViewportScale(canvas, rect);
1638
+ return {
1639
+ x: (clientX - rect.left) / scale.x,
1640
+ y: (clientY - rect.top) / scale.y
1641
+ };
1642
+ }
1643
+ function getElementViewportScale(element, rect = element.getBoundingClientRect()) {
1644
+ const resolveScale = (viewportSize, layoutSize) => layoutSize > 0 && Math.abs(viewportSize - layoutSize) >= 1 ? viewportSize / layoutSize : 1;
1645
+ return {
1646
+ x: resolveScale(rect.width, element.clientWidth),
1647
+ y: resolveScale(rect.height, element.clientHeight)
1648
+ };
1649
+ }
1650
+
1623
1651
  // src/adapters/dom-host.ts
1624
1652
  var RESIZE_RENDER_SETTLE_DELAY = 240;
1625
1653
  var ListTableDomHost = class {
@@ -1789,23 +1817,27 @@ var ListTableDomHost = class {
1789
1817
  return this.nodes;
1790
1818
  }
1791
1819
  toViewportPoint(x, y) {
1792
- const hostRect = this.nodes?.canvasHostElement.getBoundingClientRect();
1793
- if (!hostRect) {
1820
+ const hostElement = this.nodes?.canvasHostElement;
1821
+ const hostRect = hostElement?.getBoundingClientRect();
1822
+ if (!hostElement || !hostRect) {
1794
1823
  return { x, y };
1795
1824
  }
1825
+ const scale = getElementViewportScale(hostElement, hostRect);
1796
1826
  return {
1797
- x: hostRect.left + x * this.contentZoom,
1798
- y: hostRect.top + y * this.contentZoom
1827
+ x: hostRect.left + x * this.contentZoom * scale.x,
1828
+ y: hostRect.top + y * this.contentZoom * scale.y
1799
1829
  };
1800
1830
  }
1801
1831
  toViewportRect(rect) {
1802
1832
  const point = this.toViewportPoint(rect.x, rect.y);
1833
+ const hostElement = this.nodes?.canvasHostElement;
1834
+ const scale = hostElement ? getElementViewportScale(hostElement) : { x: 1, y: 1 };
1803
1835
  return {
1804
1836
  ...rect,
1805
1837
  x: point.x,
1806
1838
  y: point.y,
1807
- width: rect.width * this.contentZoom,
1808
- height: rect.height * this.contentZoom
1839
+ width: rect.width * this.contentZoom * scale.x,
1840
+ height: rect.height * this.contentZoom * scale.y
1809
1841
  };
1810
1842
  }
1811
1843
  getCanvasViewportRect() {
@@ -1859,15 +1891,6 @@ var ListTableDomHost = class {
1859
1891
  }
1860
1892
  };
1861
1893
 
1862
- // src/interaction/pointer.ts
1863
- function getCanvasPointerPosition(canvas, clientX, clientY) {
1864
- const rect = canvas.getBoundingClientRect();
1865
- return {
1866
- x: clientX - rect.left,
1867
- y: clientY - rect.top
1868
- };
1869
- }
1870
-
1871
1894
  // src/adapters/canvas-host.ts
1872
1895
  var ListTableCanvasHost = class {
1873
1896
  constructor() {
@@ -2112,7 +2135,7 @@ function createEmptySelection() {
2112
2135
  };
2113
2136
  }
2114
2137
  function resolveAdjacentCellSelection(params) {
2115
- const { selection, rowCount, columnKeys, direction } = params;
2138
+ const { selection, rowCount, columnKeys, direction, verticalWrap = true } = params;
2116
2139
  if (selection.rowIndex == null || selection.columnKey == null || selection.rowIndex < 0 || selection.rowIndex >= rowCount) {
2117
2140
  return null;
2118
2141
  }
@@ -2121,6 +2144,10 @@ function resolveAdjacentCellSelection(params) {
2121
2144
  return null;
2122
2145
  }
2123
2146
  if (direction === "up" || direction === "down") {
2147
+ const nextRowIndex = selection.rowIndex + (direction === "up" ? -1 : 1);
2148
+ if (!verticalWrap) {
2149
+ return nextRowIndex < 0 || nextRowIndex >= rowCount ? null : { rowIndex: nextRowIndex, columnKey: selection.columnKey };
2150
+ }
2124
2151
  const flatIndex = columnIndex * rowCount + selection.rowIndex;
2125
2152
  const nextFlatIndex = flatIndex + (direction === "up" ? -1 : 1);
2126
2153
  const cellCount = rowCount * columnKeys.length;
@@ -2143,7 +2170,7 @@ function resolveAdjacentCellSelection(params) {
2143
2170
  };
2144
2171
  }
2145
2172
  function resolveTabCellSelection(params) {
2146
- const { selection, rowCount, columnKeys, reverse = false } = params;
2173
+ const { selection, rowCount, columnKeys, reverse = false, tabWrap = true } = params;
2147
2174
  if (selection.rowIndex == null || selection.columnKey == null || selection.rowIndex < 0 || selection.rowIndex >= rowCount || columnKeys.length === 0) {
2148
2175
  return null;
2149
2176
  }
@@ -2151,6 +2178,13 @@ function resolveTabCellSelection(params) {
2151
2178
  if (columnIndex < 0) {
2152
2179
  return null;
2153
2180
  }
2181
+ if (!tabWrap) {
2182
+ const nextColumnIndex = columnIndex + (reverse ? -1 : 1);
2183
+ return nextColumnIndex < 0 || nextColumnIndex >= columnKeys.length ? null : {
2184
+ rowIndex: selection.rowIndex,
2185
+ columnKey: columnKeys[nextColumnIndex]
2186
+ };
2187
+ }
2154
2188
  const flatIndex = selection.rowIndex * columnKeys.length + columnIndex;
2155
2189
  const nextFlatIndex = flatIndex + (reverse ? -1 : 1);
2156
2190
  const cellCount = rowCount * columnKeys.length;
@@ -3265,14 +3299,17 @@ function mapKindToArea(kind, actionType) {
3265
3299
  }
3266
3300
  function getPriority(descriptor) {
3267
3301
  let priority = getBasePriority(descriptor.kind);
3302
+ if (descriptor.debugNodeType === "header-edit-icon" || descriptor.debugNodeType === "nested-table-header-edit-icon") {
3303
+ priority += 1;
3304
+ }
3268
3305
  if (descriptor.kind === "summary" && descriptor.columnKey) {
3269
3306
  priority += 1;
3270
3307
  }
3271
3308
  if ((descriptor.kind === "body" || descriptor.kind === "body-content" || descriptor.kind === "body-control") && descriptor.frozen) {
3272
3309
  priority += 2;
3273
3310
  }
3274
- if (descriptor.kind === "body" && descriptor.fixed) {
3275
- priority += 1;
3311
+ if ((descriptor.kind === "body" || descriptor.kind === "body-content" || descriptor.kind === "body-control") && descriptor.fixed) {
3312
+ priority += 2;
3276
3313
  }
3277
3314
  if (isHeaderDescriptor(descriptor.kind) && descriptor.fixed) {
3278
3315
  priority += 3;
@@ -3292,6 +3329,9 @@ function getBasePriority(kind) {
3292
3329
  return 2;
3293
3330
  case "resize-handle":
3294
3331
  return 4;
3332
+ // 折叠列图标覆盖列宽热区,但仍低于固定列表头的遮挡层(优先级 5)。
3333
+ case "column-collapse-trigger":
3334
+ return 4.5;
3295
3335
  case "header-action":
3296
3336
  return 3;
3297
3337
  case "header-selection":
@@ -3304,7 +3344,6 @@ function getBasePriority(kind) {
3304
3344
  return 2;
3305
3345
  case "expand-trigger":
3306
3346
  case "collapse-trigger":
3307
- case "column-collapse-trigger":
3308
3347
  return 3;
3309
3348
  case "expanded-row":
3310
3349
  case "body":
@@ -3463,6 +3502,60 @@ function applyListTableRenderPassResult(params) {
3463
3502
  params.setScrollPageSize(projection.scrollPageSize);
3464
3503
  }
3465
3504
 
3505
+ // src/rendering/cell-content/text-measure-cache.ts
3506
+ var MAX_ENTRIES = 2048;
3507
+ var MAX_TEXT_LENGTH = 512;
3508
+ var fontRevisions = /* @__PURE__ */ new WeakMap();
3509
+ var contextCaches = /* @__PURE__ */ new WeakMap();
3510
+ function measureCachedText(ctx, text) {
3511
+ const fonts = ctx.canvas?.ownerDocument?.fonts;
3512
+ let fontRevision = 0;
3513
+ if (fonts) {
3514
+ let revision = fontRevisions.get(fonts);
3515
+ if (!revision) {
3516
+ revision = { value: 0 };
3517
+ fontRevisions.set(fonts, revision);
3518
+ const invalidate = () => {
3519
+ revision.value += 1;
3520
+ };
3521
+ fonts.addEventListener("loadingdone", invalidate);
3522
+ fonts.addEventListener("loadingerror", invalidate);
3523
+ }
3524
+ fontRevision = revision.value;
3525
+ if (fonts.status === "loading") {
3526
+ contextCaches.delete(ctx);
3527
+ return ctx.measureText(text);
3528
+ }
3529
+ }
3530
+ if (text.length > MAX_TEXT_LENGTH) return ctx.measureText(text);
3531
+ let cache = contextCaches.get(ctx);
3532
+ if (!cache || cache.fontRevision !== fontRevision) {
3533
+ cache = { fontRevision, entries: /* @__PURE__ */ new Map() };
3534
+ contextCaches.set(ctx, cache);
3535
+ }
3536
+ const key = JSON.stringify([
3537
+ ctx.font,
3538
+ ctx.textAlign,
3539
+ ctx.textBaseline,
3540
+ ctx.direction,
3541
+ ctx.fontKerning,
3542
+ ctx.fontStretch,
3543
+ ctx.fontVariantCaps,
3544
+ ctx.letterSpacing,
3545
+ ctx.wordSpacing,
3546
+ ctx.textRendering,
3547
+ text
3548
+ ]);
3549
+ const cached = cache.entries.get(key);
3550
+ if (cached) return cached;
3551
+ const metrics = ctx.measureText(text);
3552
+ if (cache.entries.size >= MAX_ENTRIES) {
3553
+ cache.entries.delete(cache.entries.keys().next().value);
3554
+ }
3555
+ cache.entries.set(key, metrics);
3556
+ return metrics;
3557
+ }
3558
+
3466
3559
  // src/rendering/cell-content/content-style.ts
3467
3560
  function resolveContentFontSize(content, fallbackFontSize) {
3468
3561
  return content.style?.fontSize ?? content.fontSize ?? fallbackFontSize;
@@ -3523,7 +3616,7 @@ function buildCanvasFont(params) {
3523
3616
  return `${fontWeight} ${fontSize}px ${fontFamily}`.trim();
3524
3617
  }
3525
3618
  function measureTextBounds(ctx, text) {
3526
- const metrics = ctx.measureText(text);
3619
+ const metrics = measureCachedText(ctx, text);
3527
3620
  const ascent = metrics.actualBoundingBoxAscent || 0;
3528
3621
  const descent = metrics.actualBoundingBoxDescent || 0;
3529
3622
  const width = metrics.width;
@@ -5178,11 +5271,41 @@ function drawHeaderActionGlyph(ctx, rect, actionType, color = "#8c8c8c", sortOrd
5178
5271
  ctx.fill();
5179
5272
  } else if (actionType === "filter") {
5180
5273
  drawFilterIcon(ctx, rect);
5181
- } else {
5274
+ } else if (actionType === "fixed") {
5182
5275
  drawFixedPinIcon(ctx, rect);
5276
+ } else {
5277
+ drawEditIcon(ctx, rect);
5183
5278
  }
5184
5279
  ctx.restore();
5185
5280
  }
5281
+ function drawEditIcon(ctx, rect) {
5282
+ const scale = Math.min(rect.width, rect.height) / 14;
5283
+ const x = rect.x + (rect.width - 14 * scale) / 2;
5284
+ const y = rect.y + (rect.height - 14 * scale) / 2;
5285
+ ctx.save();
5286
+ ctx.translate(x, y);
5287
+ ctx.scale(scale, scale);
5288
+ ctx.beginPath();
5289
+ ctx.moveTo(1.5, 10.15);
5290
+ ctx.lineTo(9.4, 2.25);
5291
+ ctx.lineTo(11.75, 4.6);
5292
+ ctx.lineTo(3.85, 12.5);
5293
+ ctx.lineTo(1.3, 12.7);
5294
+ ctx.closePath();
5295
+ ctx.fill();
5296
+ ctx.beginPath();
5297
+ ctx.moveTo(10.05, 1.6);
5298
+ ctx.lineTo(10.7, 0.95);
5299
+ ctx.lineTo(11.4, 0.6);
5300
+ ctx.lineTo(12.1, 0.95);
5301
+ ctx.lineTo(13.05, 1.9);
5302
+ ctx.lineTo(13.4, 2.6);
5303
+ ctx.lineTo(13.05, 3.3);
5304
+ ctx.lineTo(12.4, 3.95);
5305
+ ctx.closePath();
5306
+ ctx.fill();
5307
+ ctx.restore();
5308
+ }
5186
5309
  function drawFilterIcon(ctx, rect) {
5187
5310
  const iconWidth = rect.width * 0.92;
5188
5311
  const iconHeight = rect.height * 0.7;
@@ -7433,6 +7556,19 @@ function sortRows(rows, sortState, leafColumns) {
7433
7556
  return 0;
7434
7557
  });
7435
7558
  }
7559
+ async function sortRowsAsync(params) {
7560
+ const { rows, signal } = params;
7561
+ const indices = await sortRowIndicesAsync(params);
7562
+ if (!indices) {
7563
+ return rows;
7564
+ }
7565
+ return materializeRows({
7566
+ rows,
7567
+ indices,
7568
+ signal,
7569
+ abortMessage: "sort rows aborted"
7570
+ });
7571
+ }
7436
7572
  async function sortRowIndicesAsync(params) {
7437
7573
  let rowIndices = params.rowIndices ?? null;
7438
7574
  const sorters = orderSortingState(params.sortState);
@@ -7676,6 +7812,21 @@ async function mapToSourceRowIndices(params) {
7676
7812
  recordQueryStageDuration(params.trace, "indexMapping", mappingStartedAt);
7677
7813
  return result;
7678
7814
  }
7815
+ async function materializeRows(params) {
7816
+ const { rows, indices, signal, abortMessage } = params;
7817
+ const result = Array(indices.length);
7818
+ let sliceStartedAt = getTaskNow();
7819
+ for (let index = 0; index < indices.length; index += 1) {
7820
+ throwIfTaskAborted(signal, abortMessage);
7821
+ result[index] = rows[indices[index]];
7822
+ if ((index + 1) % ASYNC_QUERY_SLICE_ITEM_LIMIT === 0 || (index + 1) % 256 === 0 && isTaskSliceExpired(sliceStartedAt)) {
7823
+ await yieldToMainThread();
7824
+ throwIfTaskAborted(signal, abortMessage);
7825
+ sliceStartedAt = getTaskNow();
7826
+ }
7827
+ }
7828
+ return result;
7829
+ }
7679
7830
  function isAbortError(error) {
7680
7831
  return error instanceof Error && error.name === "AbortError";
7681
7832
  }
@@ -8068,12 +8219,17 @@ function toQueryFilters(filters) {
8068
8219
  );
8069
8220
  }
8070
8221
  function toQuerySorter(sortState, leafColumns) {
8071
- return orderSortingState(sortState).map((item) => ({
8072
- columnKey: item.key,
8073
- field: leafColumns.find((column) => column.key === item.key)?.dataIndex,
8074
- order: item.order,
8075
- priority: item.priority
8076
- }));
8222
+ return orderSortingState(sortState).map((item) => {
8223
+ const column = leafColumns.find((candidate) => candidate.key === item.key);
8224
+ const animation = typeof column?.sorter === "object" ? column.sorter.animation : void 0;
8225
+ return {
8226
+ columnKey: item.key,
8227
+ field: column?.dataIndex,
8228
+ order: item.order,
8229
+ priority: item.priority,
8230
+ ...animation === void 0 ? {} : { animation }
8231
+ };
8232
+ });
8077
8233
  }
8078
8234
  function getActiveFilterColumnKeys(filters) {
8079
8235
  return Object.entries(filters).filter(([, value]) => isMeaningfulFilterValue(value)).map(([columnKey]) => columnKey);
@@ -8446,6 +8602,7 @@ function resolveHeaderTextLayout(params) {
8446
8602
  actionVisibilityState,
8447
8603
  allowActions,
8448
8604
  hasFilter,
8605
+ hasEdit = false,
8449
8606
  contentInsetStart = 0,
8450
8607
  contentInsetEnd = 0,
8451
8608
  paddingInline = HEADER_TEXT_PADDING
@@ -8477,14 +8634,23 @@ function resolveHeaderTextLayout(params) {
8477
8634
  maxWidth: Math.max(contentVisibleRect.width - paddingInline * 2, 12),
8478
8635
  x: baseX
8479
8636
  };
8480
- if (!allowActions || !actionVisibilityState.visible || align === "left") {
8637
+ if (!allowActions || !actionVisibilityState.visible) {
8481
8638
  return baseLayout;
8482
8639
  }
8483
8640
  const actionLayout = getHeaderActionLayout({
8484
8641
  rect,
8485
8642
  hasFilter,
8643
+ hasEdit,
8644
+ visibilityState: actionVisibilityState,
8486
8645
  trailingInset: contentInsetEnd
8487
8646
  });
8647
+ if (align === "left") {
8648
+ if (!hasEdit) return baseLayout;
8649
+ return {
8650
+ ...baseLayout,
8651
+ maxWidth: Math.max(actionLayout.startX - baseX - paddingInline, 0)
8652
+ };
8653
+ }
8488
8654
  const textRight = Math.min(
8489
8655
  contentVisibleRect.x + contentVisibleRect.width - paddingInline,
8490
8656
  actionLayout.startX - paddingInline
@@ -8515,11 +8681,14 @@ function resolveHeaderTextLayout(params) {
8515
8681
  };
8516
8682
  }
8517
8683
  function getHeaderActionLayout(params) {
8518
- const actionTypes = ["sort"];
8519
- if (params.hasFilter) {
8520
- actionTypes.push("filter");
8521
- }
8522
- actionTypes.push("fixed");
8684
+ const editOnly = params.hasEdit && params.visibilityState != null && !params.visibilityState.hovered && !params.visibilityState.sorted && !params.visibilityState.filtered && !params.visibilityState.fixed;
8685
+ const actionTypes = [];
8686
+ if (!editOnly) {
8687
+ actionTypes.push("sort");
8688
+ if (params.hasFilter) actionTypes.push("filter");
8689
+ actionTypes.push("fixed");
8690
+ }
8691
+ if (params.hasEdit) actionTypes.push("edit");
8523
8692
  const totalWidth = ACTION_SIZE * actionTypes.length + ACTION_GAP * Math.max(actionTypes.length - 1, 0);
8524
8693
  return {
8525
8694
  actionTypes,
@@ -8570,6 +8739,9 @@ function drawHeaderLayer(params, layer) {
8570
8739
  if (cell.layer !== layer) {
8571
8740
  return;
8572
8741
  }
8742
+ if (layer === "center" && centerClipRect && (cell.x + cell.width < centerClipRect.x - 1 || cell.x > centerClipRect.x + centerClipRect.width + 1)) {
8743
+ return;
8744
+ }
8573
8745
  if (layer === "center" && centerClipRect) {
8574
8746
  params.ctx.save();
8575
8747
  params.ctx.beginPath();
@@ -8615,7 +8787,11 @@ function drawHeaderCell(params, cell, clipRect) {
8615
8787
  };
8616
8788
  ctx.save();
8617
8789
  const isSelected = isHeaderCellSelected(params, cell.node.key, rect);
8618
- const isRangeSelected = isHeaderCellInRange(params.rangeHighlight, cell);
8790
+ const isRangeSelected = isHeaderCellInRange(
8791
+ params.rangeHighlight,
8792
+ cell,
8793
+ params.leafColumnKeys
8794
+ );
8619
8795
  ctx.fillStyle = isSelected || isRangeSelected ? theme.selectionBackgroundColor : cell.node.style?.backgroundColor ?? theme.headerBackgroundColor;
8620
8796
  ctx.fillRect(rect.x, rect.y, rect.width, rect.height);
8621
8797
  drawGridRect(
@@ -8639,13 +8815,15 @@ function drawHeaderCell(params, cell, clipRect) {
8639
8815
  );
8640
8816
  const isExpandSystemColumn = cell.node.children.length === 0 && cell.node.column.meta?.systemType === "expandable";
8641
8817
  const isRowSelectorSystemColumn = cell.node.children.length === 0 && cell.node.column.meta?.systemType === "rowSelector";
8818
+ const isEditableLeaf = cell.node.children.length === 0 && !!cell.node.column.editable;
8642
8819
  const visibleState = getHeaderActionVisibilityState(
8643
8820
  cell.node.key,
8644
8821
  params.hoveredColumnKey,
8645
8822
  params.sortState.find((item) => item.key === cell.node.key)?.key ?? null,
8646
8823
  params.filteredColumnKeys,
8647
8824
  params.filterableColumnKeys,
8648
- params.fixedColumnKeys
8825
+ params.fixedColumnKeys,
8826
+ isEditableLeaf
8649
8827
  );
8650
8828
  const titleContent = cell.node.titleContent ?? [
8651
8829
  { type: "text", text: cell.node.title }
@@ -8657,6 +8835,7 @@ function drawHeaderCell(params, cell, clipRect) {
8657
8835
  actionVisibilityState: visibleState,
8658
8836
  allowActions: cell.node.children.length === 0,
8659
8837
  hasFilter: params.filterableColumnKeys.has(cell.node.key),
8838
+ hasEdit: isEditableLeaf,
8660
8839
  contentWidth: measureHeaderContentWidth(ctx, titleContent),
8661
8840
  paddingInline: cell.node.style?.paddingInline,
8662
8841
  contentInsetStart: resolveHeaderContentInsetStart({
@@ -8689,6 +8868,7 @@ function drawHeaderCell(params, cell, clipRect) {
8689
8868
  rect: getHeaderSelectionRect(rect),
8690
8869
  columnKey: cell.node.key,
8691
8870
  controlType: params.rowSelection.type,
8871
+ cursor: params.rowSelection.disabled ? "not-allowed" : "pointer",
8692
8872
  fixed: cell.node.fixed
8693
8873
  });
8694
8874
  }
@@ -8779,7 +8959,9 @@ function drawHeaderCell(params, cell, clipRect) {
8779
8959
  cell.node.key,
8780
8960
  visibleState,
8781
8961
  cell.node.fixed,
8782
- visibleRect
8962
+ visibleRect,
8963
+ isEditableLeaf,
8964
+ isSelected || isRangeSelected
8783
8965
  );
8784
8966
  drawResizeHandles(params, rect, cell.node.key, cell.node.fixed);
8785
8967
  }
@@ -8815,11 +8997,11 @@ function resolveHeaderContentCenterY(rect, contents, fontSize, paddingBlock, ver
8815
8997
  function measureHeaderContentWidth(ctx, contents) {
8816
8998
  const gap = 4;
8817
8999
  return contents.reduce(
8818
- (sum, content) => sum + (content.type === "img" ? Math.max(content.width ?? content.height ?? 16, 1) : ctx.measureText(content.text).width),
9000
+ (sum, content) => sum + (content.type === "img" ? Math.max(content.width ?? content.height ?? 16, 1) : measureCachedText(ctx, content.text).width),
8819
9001
  0
8820
9002
  ) + Math.max(contents.length - 1, 0) * gap;
8821
9003
  }
8822
- function drawHeaderActions(params, rect, columnKey, visibilityState, fixed, visibleRect = rect) {
9004
+ function drawHeaderActions(params, rect, columnKey, visibilityState, fixed, visibleRect = rect, hasEdit = false, selected = false) {
8823
9005
  if (!visibilityState.visible) {
8824
9006
  return;
8825
9007
  }
@@ -8831,6 +9013,8 @@ function drawHeaderActions(params, rect, columnKey, visibilityState, fixed, visi
8831
9013
  const actionLayout = getHeaderActionLayout({
8832
9014
  rect,
8833
9015
  hasFilter: params.filterableColumnKeys.has(columnKey),
9016
+ hasEdit,
9017
+ visibilityState,
8834
9018
  trailingInset: resolveCollapsedColumnMarkerSet(
8835
9019
  params.collapsedColumnMarkers,
8836
9020
  columnKey
@@ -8838,7 +9022,7 @@ function drawHeaderActions(params, rect, columnKey, visibilityState, fixed, visi
8838
9022
  });
8839
9023
  const { actionTypes, startX, totalWidth } = actionLayout;
8840
9024
  const centerY = rect.y + rect.height / 2;
8841
- const headerSelected = isHeaderCellSelected(params, columnKey, rect);
9025
+ const headerSelected = selected || isHeaderCellSelected(params, columnKey, rect);
8842
9026
  ctx.save();
8843
9027
  ctx.fillStyle = headerSelected ? params.theme.selectionBackgroundColor : params.theme.headerBackgroundColor;
8844
9028
  ctx.beginPath();
@@ -8860,6 +9044,28 @@ function drawHeaderActions(params, rect, columnKey, visibilityState, fixed, visi
8860
9044
  width: ACTION_SIZE2,
8861
9045
  height: ACTION_SIZE2
8862
9046
  };
9047
+ if (actionType === "edit") {
9048
+ drawHeaderActionIcon(
9049
+ ctx,
9050
+ actionRect,
9051
+ "edit",
9052
+ true,
9053
+ false,
9054
+ headerSelected,
9055
+ params.theme.selectionBackgroundColor
9056
+ );
9057
+ const hitRect2 = intersectRects(actionRect, visibleRect);
9058
+ if (hitRect2)
9059
+ descriptors.push({
9060
+ kind: "header",
9061
+ rect: hitRect2,
9062
+ columnKey,
9063
+ fixed,
9064
+ debugNodeType: "header-edit-icon",
9065
+ debugText: "\u5F53\u524D\u5217\u53EF\u7F16\u8F91"
9066
+ });
9067
+ return;
9068
+ }
8863
9069
  const iconHovered = params.hoveredColumnKey === columnKey && params.hoveredActionType === (actionType === "sort" ? "sort-trigger" : actionType === "filter" ? "filter-trigger" : "fixed-trigger");
8864
9070
  drawHeaderActionIcon(
8865
9071
  ctx,
@@ -8878,6 +9084,7 @@ function drawHeaderActions(params, rect, columnKey, visibilityState, fixed, visi
8878
9084
  rect: hitRect,
8879
9085
  columnKey,
8880
9086
  actionType,
9087
+ cursor: "pointer",
8881
9088
  visibilityState,
8882
9089
  fixed
8883
9090
  });
@@ -8937,11 +9144,16 @@ function isHeaderCellSelected(params, columnKey, rect) {
8937
9144
  }
8938
9145
  return isSameRect(params.selectedHeaderRect, rect);
8939
9146
  }
8940
- function isHeaderCellInRange(rangeHighlight, cell) {
8941
- const range = (rangeHighlight ?? EMPTY_RANGE_HIGHLIGHT).selectedColumnRange;
8942
- if (!range) {
8943
- return false;
9147
+ function isHeaderCellInRange(rangeHighlight, cell, leafColumnKeys) {
9148
+ const state = rangeHighlight ?? EMPTY_RANGE_HIGHLIGHT;
9149
+ if (state.selectedColumnKeys != null) {
9150
+ if (leafColumnKeys) {
9151
+ return leafColumnKeys.slice(cell.node.leafStartIndex, cell.node.leafEndIndex + 1).some((key) => state.selectedColumnKeys?.includes(key));
9152
+ }
9153
+ return state.selectedColumnKeys.includes(cell.node.key);
8944
9154
  }
9155
+ const range = state.selectedColumnRange;
9156
+ if (!range) return false;
8945
9157
  return cell.node.leafStartIndex <= range.end && cell.node.leafEndIndex >= range.start;
8946
9158
  }
8947
9159
  function isSameRect(left, right) {
@@ -9006,7 +9218,7 @@ function getHeaderSelectionRect(rect) {
9006
9218
  height: size
9007
9219
  };
9008
9220
  }
9009
- function getHeaderActionVisibilityState(columnKey, hoveredColumnKey, sortedColumnKey, filteredColumnKeys, filterableColumnKeys, fixedColumnKeys) {
9221
+ function getHeaderActionVisibilityState(columnKey, hoveredColumnKey, sortedColumnKey, filteredColumnKeys, filterableColumnKeys, fixedColumnKeys, editable) {
9010
9222
  const hovered = hoveredColumnKey === columnKey;
9011
9223
  const sorted = sortedColumnKey === columnKey;
9012
9224
  const filtered = filterableColumnKeys.has(columnKey) && filteredColumnKeys.has(columnKey);
@@ -9016,7 +9228,7 @@ function getHeaderActionVisibilityState(columnKey, hoveredColumnKey, sortedColum
9016
9228
  sorted,
9017
9229
  filtered,
9018
9230
  fixed,
9019
- visible: hovered || sorted || filtered || fixed
9231
+ visible: hovered || sorted || filtered || fixed || editable
9020
9232
  };
9021
9233
  }
9022
9234
  function getActionActive(actionType, visibilityState) {
@@ -9051,7 +9263,7 @@ function drawHeaderActionIcon(ctx, rect, actionType, active, hovered = false, se
9051
9263
  function drawHeaderContent(ctx, contents, x, y, maxWidth, align, requestRender) {
9052
9264
  const gap = 4;
9053
9265
  const widths = contents.map(
9054
- (content) => content.type === "img" ? Math.max(content.width ?? content.height ?? 16, 1) : ctx.measureText(content.text).width
9266
+ (content) => content.type === "img" ? Math.max(content.width ?? content.height ?? 16, 1) : measureCachedText(ctx, content.text).width
9055
9267
  );
9056
9268
  const totalWidth = widths.reduce((sum, width) => sum + width, 0) + Math.max(contents.length - 1, 0) * gap;
9057
9269
  const displayWidth = Math.min(totalWidth, Math.max(maxWidth, 0));
@@ -9076,19 +9288,19 @@ function drawHeaderContent(ctx, contents, x, y, maxWidth, align, requestRender)
9076
9288
  ctx.textAlign = "left";
9077
9289
  const text = ellipsisText(ctx, content.text, availableWidth);
9078
9290
  ctx.fillText(text, cursorX, y);
9079
- cursorX += Math.min(ctx.measureText(text).width, availableWidth) + gap;
9291
+ cursorX += Math.min(measureCachedText(ctx, text).width, availableWidth) + gap;
9080
9292
  ctx.restore();
9081
9293
  });
9082
9294
  }
9083
9295
  function ellipsisText(ctx, text, maxWidth) {
9084
- if (ctx.measureText(text).width <= maxWidth) {
9296
+ if (measureCachedText(ctx, text).width <= maxWidth) {
9085
9297
  return text;
9086
9298
  }
9087
9299
  const ellipsis = "...";
9088
9300
  let content = text;
9089
9301
  while (content.length > 0) {
9090
9302
  content = content.slice(0, -1);
9091
- const measured = ctx.measureText(`${content}${ellipsis}`).width;
9303
+ const measured = measureCachedText(ctx, `${content}${ellipsis}`).width;
9092
9304
  if (measured <= maxWidth) {
9093
9305
  return `${content}${ellipsis}`;
9094
9306
  }
@@ -9192,6 +9404,544 @@ function findCellByKey(headerRows, columnKey) {
9192
9404
  }
9193
9405
  return null;
9194
9406
  }
9407
+
9408
+ // src/services/range-highlight-service.ts
9409
+ var EMPTY_RANGE_HIGHLIGHT_STATE = {
9410
+ selectedRowAnchorIndex: null,
9411
+ selectedRowRange: null,
9412
+ selectedColumnAnchorKey: null,
9413
+ selectedColumnRange: null
9414
+ };
9415
+ function isIndexInHighlightRange(index, range) {
9416
+ return Boolean(range && index >= range.start && index <= range.end);
9417
+ }
9418
+ function isColumnInHighlight(state, columnKey, columnIndex) {
9419
+ return state.selectedColumnKeys != null ? state.selectedColumnKeys.includes(columnKey) : isIndexInHighlightRange(columnIndex, state.selectedColumnRange);
9420
+ }
9421
+ function isRowInHighlight(state, rowIndex) {
9422
+ const ranges = state.selectedRowRanges ?? (state.selectedRowRange ? [state.selectedRowRange] : []);
9423
+ return ranges.some((range) => isIndexInHighlightRange(rowIndex, range));
9424
+ }
9425
+ function isCellInHighlight(state, rowIndex, columnIndex) {
9426
+ const ranges = state.selectedCellRanges ?? (state.selectedCellRange ? [state.selectedCellRange] : []);
9427
+ return ranges.some(
9428
+ (range) => rowIndex >= range.startRowIndex && rowIndex <= range.endRowIndex && columnIndex >= range.startColumnIndex && columnIndex <= range.endColumnIndex
9429
+ );
9430
+ }
9431
+ function resolveRowHighlightForSelectorClick(params) {
9432
+ const rowIndex = Math.max(0, Math.floor(params.rowIndex));
9433
+ const base = {
9434
+ ...params.current,
9435
+ selectedColumnAnchorKey: null,
9436
+ selectedColumnRange: null,
9437
+ selectedColumnKeys: null,
9438
+ selectedCellRange: params.ctrlKey ? params.current.selectedCellRange ?? null : null,
9439
+ selectedCellRanges: params.ctrlKey ? params.current.selectedCellRanges ?? null : null,
9440
+ selectedCellAnchor: params.ctrlKey ? params.current.selectedCellAnchor ?? null : null
9441
+ };
9442
+ if (params.ctrlKey) {
9443
+ const ranges = params.current.selectedRowRanges ?? (params.current.selectedRowRange ? [params.current.selectedRowRange] : []);
9444
+ const nextRanges = ranges.flatMap((range) => {
9445
+ if (!isIndexInHighlightRange(rowIndex, range)) return [range];
9446
+ return [
9447
+ ...range.start < rowIndex ? [{ start: range.start, end: rowIndex - 1 }] : [],
9448
+ ...rowIndex < range.end ? [{ start: rowIndex + 1, end: range.end }] : []
9449
+ ];
9450
+ });
9451
+ if (!ranges.some((range) => isIndexInHighlightRange(rowIndex, range))) {
9452
+ nextRanges.push({ start: rowIndex, end: rowIndex });
9453
+ }
9454
+ nextRanges.sort((left, right) => left.start - right.start);
9455
+ return {
9456
+ ...base,
9457
+ selectedRowAnchorIndex: nextRanges.length ? isRowInHighlight(
9458
+ { selectedRowRange: null, selectedRowRanges: nextRanges },
9459
+ rowIndex
9460
+ ) ? rowIndex : nextRanges[nextRanges.length - 1].end : null,
9461
+ selectedRowRange: null,
9462
+ selectedRowRanges: nextRanges
9463
+ };
9464
+ }
9465
+ const anchor = params.shiftKey ? params.current.selectedRowAnchorIndex ?? rowIndex : rowIndex;
9466
+ return {
9467
+ ...base,
9468
+ selectedRowAnchorIndex: anchor,
9469
+ selectedRowRange: createHighlightRange(anchor, rowIndex),
9470
+ selectedRowRanges: null
9471
+ };
9472
+ }
9473
+ function resolveCellHighlightForClick(params) {
9474
+ const columnIndex = params.leafColumnKeys.indexOf(params.columnKey);
9475
+ if (columnIndex < 0) return params.current;
9476
+ const base = {
9477
+ ...params.current,
9478
+ selectedRowAnchorIndex: params.ctrlKey && params.preserveRowSelection ? params.current.selectedRowAnchorIndex : null,
9479
+ selectedRowRange: params.ctrlKey && params.preserveRowSelection ? params.current.selectedRowRange : null,
9480
+ selectedRowRanges: params.ctrlKey && params.preserveRowSelection ? params.current.selectedRowRanges ?? null : null,
9481
+ selectedColumnAnchorKey: null,
9482
+ selectedColumnRange: null,
9483
+ selectedColumnKeys: null
9484
+ };
9485
+ if (params.shiftKey && !params.ctrlKey) {
9486
+ const anchor = params.current.selectedCellAnchor ?? params.previousCell ?? {
9487
+ rowIndex: params.rowIndex,
9488
+ columnKey: params.columnKey
9489
+ };
9490
+ const anchorColumnIndex = params.leafColumnKeys.indexOf(anchor.columnKey);
9491
+ return {
9492
+ ...base,
9493
+ selectedCellAnchor: anchor,
9494
+ selectedCellRange: {
9495
+ startRowIndex: Math.min(anchor.rowIndex, params.rowIndex),
9496
+ endRowIndex: Math.max(anchor.rowIndex, params.rowIndex),
9497
+ startColumnIndex: Math.min(
9498
+ anchorColumnIndex < 0 ? columnIndex : anchorColumnIndex,
9499
+ columnIndex
9500
+ ),
9501
+ endColumnIndex: Math.max(
9502
+ anchorColumnIndex < 0 ? columnIndex : anchorColumnIndex,
9503
+ columnIndex
9504
+ )
9505
+ },
9506
+ selectedCellRanges: null
9507
+ };
9508
+ }
9509
+ if (params.ctrlKey) {
9510
+ const previous = params.previousCell;
9511
+ const previousColumnIndex = previous ? params.leafColumnKeys.indexOf(previous.columnKey) : -1;
9512
+ const ranges = params.current.selectedCellRanges ?? (params.current.selectedCellRange ? [params.current.selectedCellRange] : previous && previousColumnIndex >= 0 ? [
9513
+ {
9514
+ startRowIndex: previous.rowIndex,
9515
+ endRowIndex: previous.rowIndex,
9516
+ startColumnIndex: previousColumnIndex,
9517
+ endColumnIndex: previousColumnIndex
9518
+ }
9519
+ ] : []);
9520
+ const selected = ranges.some(
9521
+ (range) => isCellInHighlight(
9522
+ { selectedCellRange: range },
9523
+ params.rowIndex,
9524
+ columnIndex
9525
+ )
9526
+ );
9527
+ const nextRanges = selected ? ranges.flatMap(
9528
+ (range) => removeCellFromHighlightRange(range, params.rowIndex, columnIndex)
9529
+ ) : [
9530
+ ...ranges,
9531
+ {
9532
+ startRowIndex: params.rowIndex,
9533
+ endRowIndex: params.rowIndex,
9534
+ startColumnIndex: columnIndex,
9535
+ endColumnIndex: columnIndex
9536
+ }
9537
+ ];
9538
+ return {
9539
+ ...base,
9540
+ selectedCellAnchor: selected ? params.current.selectedCellAnchor ?? null : {
9541
+ rowIndex: params.rowIndex,
9542
+ columnKey: params.columnKey
9543
+ },
9544
+ selectedCellRange: null,
9545
+ selectedCellRanges: nextRanges
9546
+ };
9547
+ }
9548
+ return {
9549
+ ...params.current,
9550
+ selectedCellAnchor: {
9551
+ rowIndex: params.rowIndex,
9552
+ columnKey: params.columnKey
9553
+ },
9554
+ selectedCellRange: null,
9555
+ selectedCellRanges: null
9556
+ };
9557
+ }
9558
+ function removeCellFromHighlightRange(range, rowIndex, columnIndex) {
9559
+ return subtractCellHighlightRange(range, {
9560
+ startRowIndex: rowIndex,
9561
+ endRowIndex: rowIndex,
9562
+ startColumnIndex: columnIndex,
9563
+ endColumnIndex: columnIndex
9564
+ });
9565
+ }
9566
+ function subtractCellHighlightRange(range, cut) {
9567
+ const top = Math.max(range.startRowIndex, cut.startRowIndex);
9568
+ const bottom = Math.min(range.endRowIndex, cut.endRowIndex);
9569
+ const left = Math.max(range.startColumnIndex, cut.startColumnIndex);
9570
+ const right = Math.min(range.endColumnIndex, cut.endColumnIndex);
9571
+ if (top > bottom || left > right) {
9572
+ return [range];
9573
+ }
9574
+ return [
9575
+ ...range.startRowIndex < top ? [{ ...range, endRowIndex: top - 1 }] : [],
9576
+ ...bottom < range.endRowIndex ? [{ ...range, startRowIndex: bottom + 1 }] : [],
9577
+ ...range.startColumnIndex < left ? [
9578
+ {
9579
+ startRowIndex: top,
9580
+ endRowIndex: bottom,
9581
+ startColumnIndex: range.startColumnIndex,
9582
+ endColumnIndex: left - 1
9583
+ }
9584
+ ] : [],
9585
+ ...right < range.endColumnIndex ? [
9586
+ {
9587
+ startRowIndex: top,
9588
+ endRowIndex: bottom,
9589
+ startColumnIndex: right + 1,
9590
+ endColumnIndex: range.endColumnIndex
9591
+ }
9592
+ ] : []
9593
+ ];
9594
+ }
9595
+ function resolveRowHighlightForDrag(params) {
9596
+ const target = createHighlightRange(params.startRowIndex, params.endRowIndex);
9597
+ const ranges = params.current.selectedRowRanges ?? (params.current.selectedRowRange ? [params.current.selectedRowRange] : []);
9598
+ const nextRanges = params.remove ? ranges.flatMap(
9599
+ (range) => target.end < range.start || target.start > range.end ? [range] : [
9600
+ ...range.start < target.start ? [{ start: range.start, end: target.start - 1 }] : [],
9601
+ ...target.end < range.end ? [{ start: target.end + 1, end: range.end }] : []
9602
+ ]
9603
+ ) : [...ranges, target];
9604
+ nextRanges.sort((left, right) => left.start - right.start);
9605
+ const mergedRanges = nextRanges.reduce(
9606
+ (merged, range) => {
9607
+ const previous = merged[merged.length - 1];
9608
+ if (previous && range.start <= previous.end + 1) {
9609
+ previous.end = Math.max(previous.end, range.end);
9610
+ } else {
9611
+ merged.push({ ...range });
9612
+ }
9613
+ return merged;
9614
+ },
9615
+ []
9616
+ );
9617
+ return {
9618
+ ...params.current,
9619
+ selectedRowAnchorIndex: params.remove ? mergedRanges[mergedRanges.length - 1]?.end ?? null : params.startRowIndex,
9620
+ selectedRowRange: null,
9621
+ selectedRowRanges: mergedRanges,
9622
+ selectedColumnAnchorKey: null,
9623
+ selectedColumnRange: null,
9624
+ selectedColumnKeys: null
9625
+ };
9626
+ }
9627
+ function resolveCellHighlightForDrag(params) {
9628
+ const target = {
9629
+ startRowIndex: Math.min(params.startRowIndex, params.endRowIndex),
9630
+ endRowIndex: Math.max(params.startRowIndex, params.endRowIndex),
9631
+ startColumnIndex: Math.min(params.startColumnIndex, params.endColumnIndex),
9632
+ endColumnIndex: Math.max(params.startColumnIndex, params.endColumnIndex)
9633
+ };
9634
+ const ranges = params.current.selectedCellRanges ?? (params.current.selectedCellRange ? [params.current.selectedCellRange] : []);
9635
+ return {
9636
+ ...params.current,
9637
+ selectedRowAnchorIndex: params.preserveRowSelection ? params.current.selectedRowAnchorIndex : null,
9638
+ selectedRowRange: params.preserveRowSelection ? params.current.selectedRowRange : null,
9639
+ selectedRowRanges: params.preserveRowSelection ? params.current.selectedRowRanges ?? null : null,
9640
+ selectedColumnAnchorKey: null,
9641
+ selectedColumnRange: null,
9642
+ selectedColumnKeys: null,
9643
+ selectedCellAnchor: {
9644
+ rowIndex: params.startRowIndex,
9645
+ columnKey: params.columnKey
9646
+ },
9647
+ selectedCellRange: null,
9648
+ selectedCellRanges: params.remove ? ranges.flatMap((range) => subtractCellHighlightRange(range, target)) : [...ranges, target]
9649
+ };
9650
+ }
9651
+ function resolveNextRowHighlight(params) {
9652
+ return resolveNextRowHighlightByIndexRange({
9653
+ current: params.current,
9654
+ rangeStartIndex: params.rowIndex,
9655
+ rangeEndIndex: params.rowIndex,
9656
+ shiftKey: params.shiftKey
9657
+ });
9658
+ }
9659
+ function resolveNextRowHighlightByIndexRange(params) {
9660
+ const rangeStartIndex = Math.max(0, Math.floor(params.rangeStartIndex));
9661
+ const rangeEndIndex = Math.max(
9662
+ rangeStartIndex,
9663
+ Math.floor(params.rangeEndIndex)
9664
+ );
9665
+ const anchor = params.shiftKey && params.current.selectedRowAnchorIndex != null ? params.current.selectedRowAnchorIndex : rangeStartIndex;
9666
+ return {
9667
+ ...params.current,
9668
+ selectedRowAnchorIndex: anchor,
9669
+ selectedRowRange: createHighlightRange(anchor, rangeEndIndex),
9670
+ selectedRowRanges: null
9671
+ };
9672
+ }
9673
+ function resolveNextRowHighlightForContextMenu(params) {
9674
+ if (isRowInHighlight(params.current, params.rowIndex)) {
9675
+ return params.current;
9676
+ }
9677
+ return resolveNextRowHighlight({
9678
+ current: params.current,
9679
+ rowIndex: params.rowIndex,
9680
+ shiftKey: false
9681
+ });
9682
+ }
9683
+ function resolveNextColumnHighlightForContextMenu(params) {
9684
+ const currentRange = params.current.selectedColumnRange;
9685
+ const selectedKeys = params.current.selectedColumnKeys;
9686
+ if (selectedKeys?.length && params.leafColumnKeys.slice(params.rangeStartIndex, params.rangeEndIndex + 1).every((key) => selectedKeys.includes(key))) {
9687
+ return {
9688
+ ...params.current,
9689
+ selectedRowAnchorIndex: null,
9690
+ selectedRowRange: null
9691
+ };
9692
+ }
9693
+ if (currentRange && params.rangeStartIndex >= currentRange.start && params.rangeEndIndex <= currentRange.end) {
9694
+ return {
9695
+ ...params.current,
9696
+ selectedRowAnchorIndex: null,
9697
+ selectedRowRange: null
9698
+ };
9699
+ }
9700
+ return resolveNextColumnHighlightForHeaderClick({
9701
+ ...params,
9702
+ shiftKey: false
9703
+ });
9704
+ }
9705
+ function resolveColumnKeysForContextMenuAction(params) {
9706
+ const columnIndex = params.leafColumnKeys.indexOf(params.columnKey);
9707
+ const rangeStartIndex = params.rangeStartIndex ?? columnIndex;
9708
+ const rangeEndIndex = params.rangeEndIndex ?? columnIndex;
9709
+ const hasValidTargetRange = rangeStartIndex >= 0 && rangeEndIndex >= rangeStartIndex && rangeEndIndex < params.leafColumnKeys.length;
9710
+ const currentRange = params.current.selectedColumnRange;
9711
+ const selectedKeys = params.current.selectedColumnKeys;
9712
+ if (selectedKeys?.length && hasValidTargetRange && params.leafColumnKeys.slice(rangeStartIndex, rangeEndIndex + 1).every((key) => selectedKeys.includes(key))) {
9713
+ return params.leafColumnKeys.filter((key) => selectedKeys.includes(key));
9714
+ }
9715
+ if (currentRange && hasValidTargetRange && rangeStartIndex >= currentRange.start && rangeEndIndex <= currentRange.end) {
9716
+ return params.leafColumnKeys.slice(
9717
+ currentRange.start,
9718
+ currentRange.end + 1
9719
+ );
9720
+ }
9721
+ if (hasValidTargetRange) {
9722
+ return params.leafColumnKeys.slice(rangeStartIndex, rangeEndIndex + 1);
9723
+ }
9724
+ return [];
9725
+ }
9726
+ function resolveNextColumnHighlight(params) {
9727
+ const columnIndex = params.leafColumnKeys.indexOf(params.columnKey);
9728
+ if (columnIndex < 0) {
9729
+ return params.current;
9730
+ }
9731
+ return resolveNextColumnHighlightByIndexRange({
9732
+ current: params.current,
9733
+ columnKey: params.columnKey,
9734
+ leafColumnKeys: params.leafColumnKeys,
9735
+ rangeStartIndex: columnIndex,
9736
+ rangeEndIndex: columnIndex,
9737
+ shiftKey: params.shiftKey
9738
+ });
9739
+ }
9740
+ function resolveNextColumnHighlightForHeaderClick(params) {
9741
+ const nextCurrent = {
9742
+ ...params.current,
9743
+ selectedRowAnchorIndex: null,
9744
+ selectedRowRange: null,
9745
+ ...params.current.selectedRowRanges !== void 0 ? { selectedRowRanges: null } : {},
9746
+ selectedCellRange: null,
9747
+ selectedCellRanges: null,
9748
+ selectedCellAnchor: null
9749
+ };
9750
+ const hasValidRange = params.rangeStartIndex >= 0 && params.rangeEndIndex >= 0 && params.rangeStartIndex < params.leafColumnKeys.length && params.rangeEndIndex < params.leafColumnKeys.length;
9751
+ if (params.ctrlKey) {
9752
+ const leafIndex = params.leafColumnKeys.indexOf(params.columnKey);
9753
+ const targetKeys = hasValidRange ? params.leafColumnKeys.slice(
9754
+ params.rangeStartIndex,
9755
+ params.rangeEndIndex + 1
9756
+ ) : leafIndex >= 0 ? [params.columnKey] : [];
9757
+ if (!targetKeys.length) return nextCurrent;
9758
+ const selected = new Set(
9759
+ params.current.selectedColumnKeys ?? (params.current.selectedColumnRange ? params.leafColumnKeys.slice(
9760
+ params.current.selectedColumnRange.start,
9761
+ params.current.selectedColumnRange.end + 1
9762
+ ) : [])
9763
+ );
9764
+ if (targetKeys.every((key) => selected.has(key))) {
9765
+ targetKeys.forEach((key) => selected.delete(key));
9766
+ } else {
9767
+ targetKeys.forEach((key) => selected.add(key));
9768
+ }
9769
+ const selectedColumnKeys = params.leafColumnKeys.filter(
9770
+ (key) => selected.has(key)
9771
+ );
9772
+ return {
9773
+ ...nextCurrent,
9774
+ selectedColumnAnchorKey: selectedColumnKeys.length ? selected.has(params.columnKey) ? params.columnKey : selectedColumnKeys[selectedColumnKeys.length - 1] : null,
9775
+ selectedColumnRange: null,
9776
+ selectedColumnKeys
9777
+ };
9778
+ }
9779
+ if (!params.shiftKey) {
9780
+ const leafColumnIndex = params.leafColumnKeys.indexOf(params.columnKey);
9781
+ if (leafColumnIndex >= 0) {
9782
+ return resolveNextColumnHighlightByIndexRange({
9783
+ ...params,
9784
+ current: nextCurrent,
9785
+ rangeStartIndex: leafColumnIndex,
9786
+ rangeEndIndex: leafColumnIndex
9787
+ });
9788
+ }
9789
+ if (hasValidRange) {
9790
+ return resolveNextColumnHighlightByIndexRange({
9791
+ ...params,
9792
+ current: nextCurrent
9793
+ });
9794
+ }
9795
+ return {
9796
+ ...nextCurrent,
9797
+ selectedColumnAnchorKey: params.columnKey,
9798
+ selectedColumnRange: null,
9799
+ selectedColumnKeys: null
9800
+ };
9801
+ }
9802
+ if (hasValidRange) {
9803
+ return resolveNextColumnHighlightByIndexRange({
9804
+ ...params,
9805
+ current: nextCurrent
9806
+ });
9807
+ }
9808
+ return resolveNextColumnHighlight({
9809
+ current: nextCurrent,
9810
+ columnKey: params.columnKey,
9811
+ leafColumnKeys: params.leafColumnKeys,
9812
+ shiftKey: params.shiftKey
9813
+ });
9814
+ }
9815
+ function resolveNextColumnHighlightByIndexRange(params) {
9816
+ if (params.rangeStartIndex < 0 || params.rangeEndIndex < 0 || params.rangeStartIndex >= params.leafColumnKeys.length || params.rangeEndIndex >= params.leafColumnKeys.length) {
9817
+ return params.current;
9818
+ }
9819
+ const anchorIndex = resolveColumnAnchorIndex({
9820
+ anchorKey: params.current.selectedColumnAnchorKey,
9821
+ leafColumnKeys: params.leafColumnKeys
9822
+ });
9823
+ const nextAnchorIndex = params.shiftKey && anchorIndex != null ? anchorIndex : params.rangeStartIndex;
9824
+ return {
9825
+ ...params.current,
9826
+ selectedColumnAnchorKey: params.leafColumnKeys[nextAnchorIndex] ?? params.columnKey,
9827
+ selectedColumnRange: createHighlightRange(
9828
+ nextAnchorIndex,
9829
+ params.shiftKey ? params.rangeEndIndex : params.rangeEndIndex
9830
+ ),
9831
+ selectedColumnKeys: null
9832
+ };
9833
+ }
9834
+ function resolveColumnAnchorIndex(params) {
9835
+ if (!params.anchorKey) {
9836
+ return null;
9837
+ }
9838
+ const anchorIndex = params.leafColumnKeys.indexOf(params.anchorKey);
9839
+ return anchorIndex >= 0 ? anchorIndex : null;
9840
+ }
9841
+ function createHighlightRange(start, end) {
9842
+ return {
9843
+ start: Math.min(start, end),
9844
+ end: Math.max(start, end)
9845
+ };
9846
+ }
9847
+ function resolveHighlightMode(mode) {
9848
+ if (mode === "cell" || mode === "column" || mode === "cross") {
9849
+ return mode;
9850
+ }
9851
+ return "row";
9852
+ }
9853
+ function resolveRangeHighlightForHeaderSelection(params) {
9854
+ const nextRangeHighlight = {
9855
+ ...params.current,
9856
+ selectedRowAnchorIndex: null,
9857
+ selectedRowRange: null,
9858
+ ...params.current.selectedRowRanges !== void 0 ? { selectedRowRanges: null } : {}
9859
+ };
9860
+ if (!params.shiftKey && !params.ctrlKey) {
9861
+ nextRangeHighlight.selectedColumnAnchorKey = EMPTY_RANGE_HIGHLIGHT_STATE.selectedColumnAnchorKey;
9862
+ nextRangeHighlight.selectedColumnRange = EMPTY_RANGE_HIGHLIGHT_STATE.selectedColumnRange;
9863
+ if (params.current.selectedColumnKeys !== void 0) {
9864
+ nextRangeHighlight.selectedColumnKeys = null;
9865
+ }
9866
+ }
9867
+ return nextRangeHighlight;
9868
+ }
9869
+ function resolveRangeHighlightForBodyInteraction(params) {
9870
+ const mode = resolveHighlightMode(params.mode);
9871
+ const canHighlightColumn = params.columnKey !== params.selectionColumnKey && params.columnKey !== params.expandColumnKey;
9872
+ const rangeEndIndex = params.rowIndex + Math.max(Math.floor(params.rowSpan ?? 1), 1) - 1;
9873
+ if (mode === "cell") {
9874
+ return EMPTY_RANGE_HIGHLIGHT_STATE;
9875
+ }
9876
+ if (mode === "row") {
9877
+ return {
9878
+ ...resolveNextRowHighlightByIndexRange({
9879
+ current: params.current,
9880
+ rangeStartIndex: params.rowIndex,
9881
+ rangeEndIndex,
9882
+ shiftKey: params.shiftKey
9883
+ }),
9884
+ selectedColumnAnchorKey: null,
9885
+ selectedColumnRange: null,
9886
+ selectedColumnKeys: null,
9887
+ selectedCellRange: null,
9888
+ selectedCellRanges: null
9889
+ };
9890
+ }
9891
+ if (mode === "column") {
9892
+ if (!canHighlightColumn || !params.columnRange) {
9893
+ return {
9894
+ ...params.current,
9895
+ selectedRowAnchorIndex: null,
9896
+ selectedRowRange: null
9897
+ };
9898
+ }
9899
+ return {
9900
+ ...resolveNextColumnHighlightByIndexRange({
9901
+ current: params.current,
9902
+ columnKey: params.columnKey,
9903
+ leafColumnKeys: params.leafColumnKeys,
9904
+ rangeStartIndex: params.columnRange.start,
9905
+ rangeEndIndex: params.columnRange.end,
9906
+ shiftKey: params.shiftKey
9907
+ }),
9908
+ selectedRowAnchorIndex: null,
9909
+ selectedRowRange: null
9910
+ };
9911
+ }
9912
+ let next = resolveNextRowHighlightByIndexRange({
9913
+ current: params.current,
9914
+ rangeStartIndex: params.rowIndex,
9915
+ rangeEndIndex,
9916
+ shiftKey: params.shiftKey
9917
+ });
9918
+ if (canHighlightColumn && params.columnRange) {
9919
+ next = resolveNextColumnHighlightByIndexRange({
9920
+ current: next,
9921
+ columnKey: params.columnKey,
9922
+ leafColumnKeys: params.leafColumnKeys,
9923
+ rangeStartIndex: params.columnRange.start,
9924
+ rangeEndIndex: params.columnRange.end,
9925
+ shiftKey: false
9926
+ });
9927
+ }
9928
+ return next;
9929
+ }
9930
+ function isCellHighlightedByMode(params) {
9931
+ const mode = resolveHighlightMode(params.mode);
9932
+ if (mode === "cell") {
9933
+ return params.isCellSelected;
9934
+ }
9935
+ if (mode === "row") {
9936
+ return params.isRowRangeSelected;
9937
+ }
9938
+ if (mode === "column") {
9939
+ return params.isColumnRangeSelected;
9940
+ }
9941
+ return params.isRowRangeSelected || params.isColumnRangeSelected;
9942
+ }
9943
+
9944
+ // src/rendering/primitives/draw-summary.ts
9195
9945
  var EMPTY_RANGE_HIGHLIGHT2 = {
9196
9946
  selectedColumnRange: null
9197
9947
  };
@@ -9240,6 +9990,9 @@ function drawSummarySegment(params, columns, clipRect, defaultTitleColumnKey) {
9240
9990
  ctx.rect(minX, summaryTop, totalWidth, summaryHeight);
9241
9991
  ctx.clip();
9242
9992
  columns.forEach((column) => {
9993
+ if (clipRect && (column.drawX + column.width < clipRect.x - 1 || column.drawX > clipRect.x + clipRect.width + 1)) {
9994
+ return;
9995
+ }
9243
9996
  const rect = {
9244
9997
  x: column.drawX,
9245
9998
  y: summaryTop,
@@ -9277,9 +10030,10 @@ function drawSummaryCell(ctx, rect, column, theme, summaryValues, selectedHeader
9277
10030
  return;
9278
10031
  }
9279
10032
  const isHeaderColumnSelected = selectedHeaderKey != null && column.key === selectedHeaderKey;
9280
- const isColumnRangeSelected = isIndexInHighlightRange(
9281
- column.leafIndex,
9282
- (rangeHighlight ?? EMPTY_RANGE_HIGHLIGHT2).selectedColumnRange
10033
+ const isColumnRangeSelected = isColumnInHighlight(
10034
+ rangeHighlight ?? EMPTY_RANGE_HIGHLIGHT2,
10035
+ column.key,
10036
+ column.leafIndex
9283
10037
  );
9284
10038
  ctx.fillStyle = summarySelected || isHeaderColumnSelected || isColumnRangeSelected ? theme.selectionBackgroundColor : style?.backgroundColor ?? theme.headerBackgroundColor;
9285
10039
  ctx.fillRect(rect.x, rect.y, rect.width, rect.height);
@@ -9492,9 +10246,6 @@ function intersectRects2(left, right) {
9492
10246
  height: maxY - y
9493
10247
  };
9494
10248
  }
9495
- function isIndexInHighlightRange(index, range) {
9496
- return Boolean(range && index >= range.start && index <= range.end);
9497
- }
9498
10249
 
9499
10250
  // src/rendering/cell-content/draw-nested-table-content.ts
9500
10251
  var DEFAULT_COLUMN_WIDTH2 = 140;
@@ -9564,7 +10315,8 @@ function drawBodyNestedTableContent(params) {
9564
10315
  align: column.align ?? "left",
9565
10316
  actionVisibilityState,
9566
10317
  allowActions: true,
9567
- hasFilter: column.allowFilter === true
10318
+ hasFilter: column.allowFilter === true,
10319
+ hasEdit: !!column.editable
9568
10320
  });
9569
10321
  const headerHighlighted = isNestedTableHeaderHighlighted(
9570
10322
  content,
@@ -9944,7 +10696,9 @@ function drawNestedTableHeaderActions(params) {
9944
10696
  if (!visibilityState.visible) return;
9945
10697
  const { actionTypes, startX, totalWidth } = getHeaderActionLayout({
9946
10698
  rect,
9947
- hasFilter: column.allowFilter === true
10699
+ hasFilter: column.allowFilter === true,
10700
+ hasEdit: !!column.editable,
10701
+ visibilityState
9948
10702
  });
9949
10703
  drawParams.ctx.save();
9950
10704
  drawParams.ctx.fillStyle = headerSelected ? drawParams.theme.selectionBackgroundColor : drawParams.theme.headerBackgroundColor;
@@ -9965,6 +10719,33 @@ function drawNestedTableHeaderActions(params) {
9965
10719
  width: size,
9966
10720
  height: size
9967
10721
  };
10722
+ if (actionType === "edit") {
10723
+ drawHeaderActionIcon(
10724
+ drawParams.ctx,
10725
+ actionRect,
10726
+ "edit",
10727
+ true,
10728
+ false,
10729
+ headerSelected,
10730
+ drawParams.theme.selectionBackgroundColor
10731
+ );
10732
+ drawParams.descriptors.push({
10733
+ kind: "body-content",
10734
+ rect: actionRect,
10735
+ columnKey: drawParams.column.key,
10736
+ rowIndex: drawParams.rowIndex,
10737
+ contentIndex: drawParams.contentIndex,
10738
+ contentKey: "nested-table",
10739
+ nestedColumnDataIndex: columnKey,
10740
+ nestedColumnIndex: columnIndex,
10741
+ fixed: drawParams.column.fixed,
10742
+ frozen: drawParams.frozen,
10743
+ debugNodeType: "nested-table-header-edit-icon",
10744
+ debugText: "\u5F53\u524D\u5217\u53EF\u7F16\u8F91"
10745
+ });
10746
+ actionX += size + gap;
10747
+ return;
10748
+ }
9968
10749
  const active = actionType === "sort" && sortOrder != null || actionType === "filter" && filtered || actionType === "fixed" && fixed != null;
9969
10750
  drawHeaderActionIcon(
9970
10751
  drawParams.ctx,
@@ -10008,7 +10789,7 @@ function resolveNestedTableHeaderActionVisibility(params) {
10008
10789
  sorted,
10009
10790
  filtered,
10010
10791
  fixed,
10011
- visible: hovered || sorted || filtered || fixed
10792
+ visible: hovered || sorted || filtered || fixed || !!column.editable
10012
10793
  };
10013
10794
  }
10014
10795
  function isNestedTableRowHighlighted(content, record, rowIndex, highlight, highlightMode) {
@@ -11077,7 +11858,7 @@ function drawBodySelectionContent(params) {
11077
11858
  controlType: content.type,
11078
11859
  contentIndex,
11079
11860
  contentKey: content.key,
11080
- cursor: content.disabled === true ? "not-allowed" : content.cursor ?? (content.onClick ? "pointer" : void 0)
11861
+ cursor: content.disabled === true ? "not-allowed" : content.cursor ?? (content.onClick || column.key === ROW_SELECTION_COLUMN_KEY ? "pointer" : void 0)
11081
11862
  });
11082
11863
  return controlRect.width;
11083
11864
  }
@@ -11093,7 +11874,14 @@ function drawSelectionControl(ctx, rect, type, checked, disabled, indeterminate
11093
11874
  ctx.fillStyle = fillColor;
11094
11875
  ctx.strokeStyle = strokeColor;
11095
11876
  ctx.lineWidth = 1.5;
11096
- roundRectPath2(ctx, rect.x, rect.y, rect.width, rect.height, 4);
11877
+ roundRectPath2(
11878
+ ctx,
11879
+ rect.x + 1,
11880
+ rect.y + 1,
11881
+ Math.max(rect.width - 2, 0),
11882
+ Math.max(rect.height - 2, 0),
11883
+ 4
11884
+ );
11097
11885
  ctx.fill();
11098
11886
  ctx.stroke();
11099
11887
  if (indeterminate) {
@@ -11577,7 +12365,7 @@ function drawBodyTagContent(params) {
11577
12365
  function measureTagContentWidth(ctx, content, theme) {
11578
12366
  applyTagTextStyle(ctx, content, theme);
11579
12367
  const intrinsicWidth = Math.max(
11580
- ctx.measureText(content.text).width + resolveContentTextIndent(content),
12368
+ measureCachedText(ctx, content.text).width + resolveContentTextIndent(content),
11581
12369
  0
11582
12370
  ) + (content.paddingInline ?? TAG_HORIZONTAL_PADDING) * 2;
11583
12371
  return resolveContentLayoutWidth(content, intrinsicWidth);
@@ -11717,8 +12505,8 @@ function drawBodyTextContent(params) {
11717
12505
  if (searchMatch && searchMatch.rowIndex === rowIndex && searchMatch.columnKey === column.key && (searchMatch.contentIndex == null || searchMatch.contentIndex === contentIndex) && searchMatch.keyword) {
11718
12506
  const start = Math.max(0, Math.min(searchMatch.start, content.text.length));
11719
12507
  const end = Math.max(start, Math.min(searchMatch.end, content.text.length));
11720
- const prefixWidth = ctx.measureText(content.text.slice(0, start)).width;
11721
- const matchWidth = ctx.measureText(content.text.slice(start, end)).width;
12508
+ const prefixWidth = measureCachedText(ctx, content.text.slice(0, start)).width;
12509
+ const matchWidth = measureCachedText(ctx, content.text.slice(start, end)).width;
11722
12510
  ctx.save();
11723
12511
  ctx.fillStyle = "#f59e0b";
11724
12512
  ctx.globalAlpha = 0.35;
@@ -11791,7 +12579,7 @@ function measureTextContentWidth(ctx, content, theme) {
11791
12579
  }
11792
12580
  applyTextContentStyle(ctx, content, theme.textColor, theme);
11793
12581
  const intrinsicWidth = Math.max(
11794
- ctx.measureText(content.text).width + resolveContentTextIndent(content),
12582
+ measureCachedText(ctx, content.text).width + resolveContentTextIndent(content),
11795
12583
  0
11796
12584
  );
11797
12585
  return resolveContentLayoutWidth(content, intrinsicWidth);
@@ -15090,6 +15878,7 @@ function createListTableAnimationRuntime() {
15090
15878
  lastExpandedRowKeys: /* @__PURE__ */ new Set(),
15091
15879
  expandAnimationFrameId: null,
15092
15880
  collapseRowAnimations: /* @__PURE__ */ new Map(),
15881
+ sortRowAnimations: /* @__PURE__ */ new Map(),
15093
15882
  collapseCellHeightAnimations: /* @__PURE__ */ new Map(),
15094
15883
  collapseAnimationFrameId: null,
15095
15884
  nestedTableHighlights: /* @__PURE__ */ new Map(),
@@ -16159,8 +16948,11 @@ function resolveCollapsedRowRange(params) {
16159
16948
 
16160
16949
  // src/engine/controllers/animation-controller.ts
16161
16950
  var ROW_EXPAND_ANIMATION_DURATION = 240;
16951
+ var ROW_SORT_ANIMATION_DURATION = 420;
16162
16952
  var ListTableAnimationController = class {
16163
16953
  constructor(options) {
16954
+ __publicField(this, "pendingSortRowPositions", null);
16955
+ __publicField(this, "sortRowsReady", false);
16164
16956
  __publicField(this, "runtime");
16165
16957
  __publicField(this, "getScrollbarVisibleOption");
16166
16958
  __publicField(this, "isScrollbarVisible");
@@ -16307,6 +17099,37 @@ var ListTableAnimationController = class {
16307
17099
  setPendingCollapseRowPositions(positions) {
16308
17100
  this.runtime.pendingCollapseRowPositions = positions;
16309
17101
  }
17102
+ /** 在排序前捕获可视行;异步查询完成之前不消费旧位置。 */
17103
+ setPendingSortRowPositions(positions, ready = false) {
17104
+ this.pendingSortRowPositions = positions;
17105
+ this.sortRowsReady = ready;
17106
+ }
17107
+ markSortRowsReady() {
17108
+ if (this.pendingSortRowPositions) this.sortRowsReady = true;
17109
+ }
17110
+ hasPendingSortRows() {
17111
+ return this.pendingSortRowPositions != null;
17112
+ }
17113
+ reconcileSortAnimations(snapshot) {
17114
+ if (!this.sortRowsReady || !this.pendingSortRowPositions) return false;
17115
+ const previousPositions = this.pendingSortRowPositions;
17116
+ this.pendingSortRowPositions = null;
17117
+ this.sortRowsReady = false;
17118
+ const now2 = this.scheduler.now();
17119
+ this.runtime.sortRowAnimations.clear();
17120
+ forEachRenderedVisibleRow(snapshot, (row, nextIndex) => {
17121
+ if (row.kind !== "data") return;
17122
+ const previousIndex = previousPositions.get(String(row.rowKey));
17123
+ if (previousIndex == null || previousIndex === nextIndex) return;
17124
+ this.runtime.sortRowAnimations.set(String(row.rowKey), {
17125
+ from: (previousIndex - nextIndex) * snapshot.rowHeight,
17126
+ to: 0,
17127
+ startTime: now2,
17128
+ duration: ROW_SORT_ANIMATION_DURATION
17129
+ });
17130
+ });
17131
+ return true;
17132
+ }
16310
17133
  reconcileCollapseAnimations(snapshot) {
16311
17134
  const previousPositions = this.runtime.pendingCollapseRowPositions;
16312
17135
  if (!previousPositions) {
@@ -16375,11 +17198,12 @@ var ListTableAnimationController = class {
16375
17198
  return getRowAnimationValue(animation, this.scheduler.now());
16376
17199
  }
16377
17200
  getCollapseRowOffset(rowKey) {
16378
- const animation = this.runtime.collapseRowAnimations.get(String(rowKey));
16379
- if (!animation) {
16380
- return void 0;
16381
- }
16382
- return getRowAnimationValue(animation, this.scheduler.now());
17201
+ const key = String(rowKey);
17202
+ const now2 = this.scheduler.now();
17203
+ const collapse = this.runtime.collapseRowAnimations.get(key);
17204
+ const sort = this.runtime.sortRowAnimations.get(key);
17205
+ if (!collapse && !sort) return void 0;
17206
+ return (collapse ? getRowAnimationValue(collapse, now2) : 0) + (sort ? getRowAnimationValue(sort, now2) : 0);
16383
17207
  }
16384
17208
  getCollapseCellHeight(params) {
16385
17209
  const animation = this.runtime.collapseCellHeightAnimations.get(
@@ -16408,13 +17232,16 @@ var ListTableAnimationController = class {
16408
17232
  columnKey: this.runtime.controlAnimationColumnKey,
16409
17233
  displayRows: snapshot.displayRows,
16410
17234
  nextWindowKey: buildControlAnimationWindowKey(snapshot),
16411
- nextColumnKey: buildControlAnimationColumnKey(snapshot.leafColumns)
17235
+ nextColumnKey: buildControlAnimationColumnKey(
17236
+ collectVisibleControlColumns(snapshot)
17237
+ )
16412
17238
  });
16413
17239
  }
16414
17240
  reconcileControlAnimations(snapshot, resolveCellContents = resolveBodyCellContents) {
16415
17241
  const now2 = this.scheduler.now();
16416
17242
  this.pruneFinishedControlAnimations(now2);
16417
17243
  const nextValues = /* @__PURE__ */ new Map();
17244
+ const visibleColumns = collectVisibleControlColumns(snapshot);
16418
17245
  forEachVisibleSnapshotRow(
16419
17246
  {
16420
17247
  frozenRows: snapshot.frozenRows,
@@ -16424,7 +17251,7 @@ var ListTableAnimationController = class {
16424
17251
  },
16425
17252
  (record, rowIndex) => {
16426
17253
  const rowKey = this.getRowKey(record, rowIndex);
16427
- collectVisibleControlColumns(snapshot).forEach((column) => {
17254
+ visibleColumns.forEach((column) => {
16428
17255
  const contents = resolveCellContents({
16429
17256
  record,
16430
17257
  rowIndex,
@@ -16467,9 +17294,7 @@ var ListTableAnimationController = class {
16467
17294
  this.runtime.lastControlValues = nextValues;
16468
17295
  this.runtime.controlAnimationRowsRef = snapshot.displayRows;
16469
17296
  this.runtime.controlAnimationWindowKey = buildControlAnimationWindowKey(snapshot);
16470
- this.runtime.controlAnimationColumnKey = buildControlAnimationColumnKey(
16471
- snapshot.leafColumns
16472
- );
17297
+ this.runtime.controlAnimationColumnKey = buildControlAnimationColumnKey(visibleColumns);
16473
17298
  Array.from(this.runtime.controlAnimations.keys()).forEach((key) => {
16474
17299
  if (!nextValues.has(key)) {
16475
17300
  this.runtime.controlAnimations.delete(key);
@@ -16847,7 +17672,7 @@ var ListTableAnimationController = class {
16847
17672
  );
16848
17673
  }
16849
17674
  ensureCollapseAnimationFrame() {
16850
- if (this.runtime.collapseRowAnimations.size === 0 && this.runtime.collapseCellHeightAnimations.size === 0 || this.runtime.collapseAnimationFrameId != null) {
17675
+ if (this.runtime.collapseRowAnimations.size === 0 && this.runtime.sortRowAnimations.size === 0 && this.runtime.collapseCellHeightAnimations.size === 0 || this.runtime.collapseAnimationFrameId != null) {
16851
17676
  return;
16852
17677
  }
16853
17678
  this.runtime.collapseAnimationFrameId = this.scheduler.requestAnimationFrame(() => {
@@ -16871,8 +17696,11 @@ var ListTableAnimationController = class {
16871
17696
  this.runtime.collapseAnimationFrameId = null;
16872
17697
  }
16873
17698
  this.runtime.collapseRowAnimations.clear();
17699
+ this.runtime.sortRowAnimations.clear();
16874
17700
  this.runtime.collapseCellHeightAnimations.clear();
16875
17701
  this.runtime.pendingCollapseRowPositions = null;
17702
+ this.pendingSortRowPositions = null;
17703
+ this.sortRowsReady = false;
16876
17704
  }
16877
17705
  destroy() {
16878
17706
  this.stopScrollbarOpacityAnimation();
@@ -16979,6 +17807,7 @@ var ListTableAnimationController = class {
16979
17807
  }
16980
17808
  pruneFinishedCollapseAnimations(now2) {
16981
17809
  pruneFinishedAnimations(this.runtime.collapseRowAnimations, now2);
17810
+ pruneFinishedAnimations(this.runtime.sortRowAnimations, now2);
16982
17811
  pruneFinishedAnimations(this.runtime.collapseCellHeightAnimations, now2);
16983
17812
  }
16984
17813
  };
@@ -16989,7 +17818,11 @@ function collectVisibleControlColumns(snapshot) {
16989
17818
  }
16990
17819
  const columns = [
16991
17820
  ...segments.leftSegment,
16992
- ...segments.centerSegment,
17821
+ ...segments.centerSegment.filter((column) => {
17822
+ const metrics = snapshot.columnMetrics;
17823
+ if (!metrics) return true;
17824
+ return metrics.availableCenterWidth > 0 && column.drawX + column.width >= metrics.fixedWidthLeft - 1 && column.drawX <= metrics.fixedWidthLeft + metrics.availableCenterWidth + 1;
17825
+ }),
16993
17826
  ...segments.rightSegment
16994
17827
  ];
16995
17828
  const seen = /* @__PURE__ */ new Set();
@@ -17808,8 +18641,11 @@ function createListTableSelectionCopyText(params) {
17808
18641
  (column2) => column2.key !== ROW_SELECTION_COLUMN_KEY && column2.key !== ROW_SELECTOR_COLUMN_KEY && column2.key !== EXPAND_COLUMN_KEY
17809
18642
  );
17810
18643
  const columnRange = params.rangeHighlight.selectedColumnRange;
18644
+ const selectedColumnKeys = params.rangeHighlight.selectedColumnKeys;
17811
18645
  const rowRange = params.rangeHighlight.selectedRowRange;
18646
+ const rowRanges = params.rangeHighlight.selectedRowRanges;
17812
18647
  const cellRange = params.rangeHighlight.selectedCellRange;
18648
+ const cellRanges = params.rangeHighlight.selectedCellRanges;
17813
18649
  if (cellRange) {
17814
18650
  const selectedColumns2 = params.columns.filter(
17815
18651
  (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
@@ -17830,22 +18666,57 @@ function createListTableSelectionCopyText(params) {
17830
18666
  return `${headerText}
17831
18667
  ${bodyText}`;
17832
18668
  }
17833
- const mode = params.highlightMode ?? (rowRange && columnRange ? "cross" : rowRange ? "row" : "column");
17834
- const selectedColumns = columnRange ? params.columns.filter(
18669
+ if (cellRanges != null) {
18670
+ const lines = [];
18671
+ const fullRowRanges = rowRanges ?? (rowRange ? [rowRange] : []);
18672
+ const selectedRowSpans = [
18673
+ ...cellRanges.map((range) => ({
18674
+ start: range.startRowIndex,
18675
+ end: range.endRowIndex
18676
+ })),
18677
+ ...fullRowRanges
18678
+ ].sort((left, right) => left.start - right.start);
18679
+ let lastRowIndex = -1;
18680
+ for (const range of selectedRowSpans) {
18681
+ for (let rowIndex2 = Math.max(range.start, lastRowIndex + 1, 0); rowIndex2 <= Math.min(range.end, params.rows.length - 1); rowIndex2++) {
18682
+ const fullRowSelected = fullRowRanges.some(
18683
+ (rowRange2) => rowIndex2 >= rowRange2.start && rowIndex2 <= rowRange2.end
18684
+ );
18685
+ const selectedColumns2 = params.columns.filter(
18686
+ (column2, columnIndex) => column2.key !== ROW_SELECTION_COLUMN_KEY && column2.key !== ROW_SELECTOR_COLUMN_KEY && column2.key !== EXPAND_COLUMN_KEY && (fullRowSelected || cellRanges.some(
18687
+ (cellRange2) => rowIndex2 >= cellRange2.startRowIndex && rowIndex2 <= cellRange2.endRowIndex && columnIndex >= cellRange2.startColumnIndex && columnIndex <= cellRange2.endColumnIndex
18688
+ ))
18689
+ );
18690
+ if (selectedColumns2.length) {
18691
+ lines.push(
18692
+ selectedColumns2.map(
18693
+ (column2) => getCellClipboardText(params.rows[rowIndex2], rowIndex2, column2)
18694
+ ).join(" ")
18695
+ );
18696
+ }
18697
+ lastRowIndex = rowIndex2;
18698
+ }
18699
+ }
18700
+ return lines.length ? lines.join("\n") : null;
18701
+ }
18702
+ const mode = params.highlightMode ?? (rowRange && (columnRange || selectedColumnKeys?.length) ? "cross" : rowRange ? "row" : "column");
18703
+ const selectedColumns = selectedColumnKeys != null ? columns.filter((column2) => selectedColumnKeys.includes(column2.key)) : columnRange ? params.columns.filter(
17835
18704
  (column2, index) => index >= columnRange.start && index <= columnRange.end
17836
18705
  ).filter(
17837
18706
  (column2) => column2.key !== ROW_SELECTION_COLUMN_KEY && column2.key !== ROW_SELECTOR_COLUMN_KEY && column2.key !== EXPAND_COLUMN_KEY
17838
18707
  ) : columns;
17839
18708
  const startRow = rowRange ? Math.max(rowRange.start, 0) : 0;
17840
18709
  const endRow = rowRange ? Math.min(rowRange.end, params.rows.length - 1) : params.rows.length - 1;
17841
- if (rowRange && params.selection.columnKey === ROW_SELECTOR_COLUMN_KEY) {
17842
- return params.rows.slice(startRow, endRow + 1).map(
17843
- (record2, offset) => columns.map(
17844
- (column2) => getCellClipboardText(record2, startRow + offset, column2)
17845
- ).join(" ")
18710
+ if ((rowRange || rowRanges?.length) && params.selection.columnKey === ROW_SELECTOR_COLUMN_KEY) {
18711
+ const selectedRows = rowRanges != null ? rowRanges.flatMap((range) => {
18712
+ const start = Math.max(range.start, 0);
18713
+ return params.rows.slice(start, range.end + 1).map((record2, offset) => ({ record: record2, rowIndex: start + offset }));
18714
+ }) : params.rows.slice(startRow, endRow + 1).map((record2, offset) => ({ record: record2, rowIndex: startRow + offset }));
18715
+ return selectedRows.map(
18716
+ ({ record: record2, rowIndex: rowIndex2 }) => columns.map((column2) => getCellClipboardText(record2, rowIndex2, column2)).join(" ")
17846
18717
  ).join("\n");
17847
18718
  }
17848
- if (mode === "column" && columnRange && params.selection.rowIndex == null) {
18719
+ if ((mode === "column" || selectedColumnKeys != null) && (columnRange || selectedColumnKeys?.length) && params.selection.rowIndex == null) {
17849
18720
  return params.rows.map(
17850
18721
  (record2, rowIndex2) => selectedColumns.map((column2) => getCellClipboardText(record2, rowIndex2, column2)).join(" ")
17851
18722
  ).join("\n");
@@ -17945,42 +18816,99 @@ async function copyRowJsonContent(params) {
17945
18816
  }
17946
18817
  }
17947
18818
  async function exportAllRowsJsonContent(params) {
18819
+ const columns = resolveListTableExportColumns(
18820
+ params.columns,
18821
+ params.settings
18822
+ );
18823
+ const rows = await resolveListTableExportRows(
18824
+ params.rows,
18825
+ columns,
18826
+ params.settings
18827
+ );
18828
+ const data = mapRowsToExportRecords({
18829
+ rows,
18830
+ columns: buildExportColumns(columns)
18831
+ });
18832
+ const footerRows = await buildListTableExportFooterRows(
18833
+ rows,
18834
+ columns,
18835
+ params.settings
18836
+ );
18837
+ if (footerRows.length) {
18838
+ data.push(
18839
+ Object.fromEntries(
18840
+ columns.map((column, index) => [column.title, footerRows[0][index]])
18841
+ )
18842
+ );
18843
+ }
17948
18844
  await downloadJsonFile({
17949
18845
  fileName: params.fileName?.trim() || buildTableExportFileName("json"),
17950
- data: mapRowsToExportRecords({
17951
- rows: params.rows,
17952
- columns: buildExportColumns(params.columns)
17953
- })
18846
+ data
17954
18847
  });
17955
18848
  }
17956
18849
  async function exportAllRowsCsvContent(params) {
18850
+ const columns = resolveListTableExportColumns(
18851
+ params.columns,
18852
+ params.settings
18853
+ );
18854
+ const rows = await resolveListTableExportRows(
18855
+ params.rows,
18856
+ columns,
18857
+ params.settings
18858
+ );
17957
18859
  await exportTableToCsv({
17958
18860
  fileName: params.fileName?.trim() || buildTableExportFileName("csv"),
17959
- rows: params.rows,
17960
- columns: buildExportColumns(params.columns)
18861
+ rows,
18862
+ columns: buildExportColumns(columns),
18863
+ footerRows: await buildListTableExportFooterRows(
18864
+ rows,
18865
+ columns,
18866
+ params.settings
18867
+ )
17961
18868
  });
17962
18869
  }
17963
18870
  async function exportAllRowsTxtContent(params) {
18871
+ const columns = resolveListTableExportColumns(
18872
+ params.columns,
18873
+ params.settings
18874
+ );
18875
+ const rows = await resolveListTableExportRows(
18876
+ params.rows,
18877
+ columns,
18878
+ params.settings
18879
+ );
17964
18880
  await exportTableToTxt({
17965
18881
  fileName: params.fileName?.trim() || buildTableExportFileName("txt"),
17966
- rows: params.rows,
17967
- columns: buildExportColumns(params.columns)
18882
+ rows,
18883
+ columns: buildExportColumns(columns),
18884
+ footerRows: await buildListTableExportFooterRows(
18885
+ rows,
18886
+ columns,
18887
+ params.settings
18888
+ )
17968
18889
  });
17969
18890
  }
17970
18891
  async function exportAllRowsXlsxContent(params) {
17971
- const columns = params.columns.filter(isExportableColumn);
17972
- const footerRows = [];
17973
- if (params.includeSummary) {
17974
- const values = await computeSummaryValuesAsync({
17975
- rows: params.rows,
17976
- columns,
17977
- signal: params.signal
17978
- });
17979
- footerRows.push(columns.map((column) => values.get(column.key) ?? ""));
17980
- }
18892
+ const columns = resolveListTableExportColumns(
18893
+ params.columns,
18894
+ params.settings
18895
+ );
18896
+ const rows = await resolveListTableExportRows(
18897
+ params.rows,
18898
+ columns,
18899
+ params.settings,
18900
+ params.signal
18901
+ );
18902
+ const footerRows = await buildListTableExportFooterRows(
18903
+ rows,
18904
+ columns,
18905
+ params.settings,
18906
+ params.includeSummary,
18907
+ params.signal
18908
+ );
17981
18909
  await exportTableToXlsx({
17982
18910
  fileName: params.fileName?.trim() || buildTableExportFileName("xlsx"),
17983
- rows: params.rows,
18911
+ rows,
17984
18912
  footerRows,
17985
18913
  worker: params.worker,
17986
18914
  signal: params.signal,
@@ -17996,6 +18924,95 @@ async function exportAllRowsXlsxContent(params) {
17996
18924
  sheetName: "TableData"
17997
18925
  });
17998
18926
  }
18927
+ function getListTableExportCandidateColumns(columns) {
18928
+ const hiddenByKey = /* @__PURE__ */ new Map();
18929
+ const exportableByKey = /* @__PURE__ */ new Map();
18930
+ const reveal = (items, parentHidden = false) => items.map((column) => {
18931
+ const hidden = parentHidden || column.hidden === true;
18932
+ const key = resolveColumnKey(column);
18933
+ hiddenByKey.set(key, hidden);
18934
+ exportableByKey.set(key, column.exportable);
18935
+ return {
18936
+ ...column,
18937
+ hidden: false,
18938
+ ...column.children?.length ? { children: reveal(column.children, hidden) } : null
18939
+ };
18940
+ });
18941
+ return flattenLeafColumns(normalizeColumns(reveal(columns))).map(
18942
+ (column) => ({
18943
+ ...column,
18944
+ hidden: hiddenByKey.get(column.key) ?? false,
18945
+ exportable: exportableByKey.get(column.key)
18946
+ })
18947
+ );
18948
+ }
18949
+ function getListTableExportColumnChoices(columns, sortState = []) {
18950
+ return columns.filter(isExportableColumn).map((column) => ({
18951
+ key: column.key,
18952
+ title: column.title ?? column.key,
18953
+ selected: column.hidden !== true,
18954
+ sortOrder: sortState.find((item) => item.key === column.key)?.order ?? "default",
18955
+ sortIndex: sortState.findIndex((item) => item.key === column.key),
18956
+ sortPriority: sortState.find((item) => item.key === column.key)?.priority,
18957
+ summary: {
18958
+ type: column.summaryTitle != null ? "title" : column.summary === true ? "sum" : column.summary && typeof column.summary === "object" ? column.summary.type ?? "sum" : "none",
18959
+ showTypePrefix: column.summary === true || typeof column.summary === "object" && column.summary?.showTypePrefix !== false,
18960
+ precision: typeof column.summary === "object" && column.summary ? column.summary.precision : void 0,
18961
+ roundingMode: typeof column.summary === "object" && column.summary ? column.summary.roundingMode ?? "round" : "round",
18962
+ preserveTrailingZeros: typeof column.summary === "object" && column.summary ? column.summary.preserveTrailingZeros === true : false
18963
+ },
18964
+ ...column.summaryTitle != null ? { summaryTitle: column.summaryTitle } : null
18965
+ }));
18966
+ }
18967
+ function resolveListTableExportColumns(columns, settings) {
18968
+ const exportable = columns.filter(isExportableColumn);
18969
+ if (!settings) return exportable.filter((column) => !column.hidden);
18970
+ const byKey = new Map(exportable.map((column) => [column.key, column]));
18971
+ const selected = [...new Set(settings.columnKeys)].flatMap((key) => {
18972
+ const column = byKey.get(key);
18973
+ if (!column) return [];
18974
+ const summary = settings.summaries[key];
18975
+ if (!summary || summary.type === "original") return [column];
18976
+ const result = { ...column };
18977
+ if (summary.type === "none") {
18978
+ result.summary = false;
18979
+ delete result.summaryTitle;
18980
+ } else if (summary.type === "title") {
18981
+ result.summary = false;
18982
+ } else {
18983
+ result.summary = {
18984
+ ...typeof column.summary === "object" ? column.summary : null,
18985
+ type: summary.type,
18986
+ showTypePrefix: summary.showTypePrefix,
18987
+ precision: summary.precision,
18988
+ roundingMode: summary.roundingMode,
18989
+ preserveTrailingZeros: summary.preserveTrailingZeros
18990
+ };
18991
+ delete result.summaryTitle;
18992
+ }
18993
+ return [result];
18994
+ });
18995
+ if (selected.length === 0) throw new Error("\u8BF7\u81F3\u5C11\u9009\u62E9\u4E00\u5217\u5BFC\u51FA\u3002");
18996
+ return selected;
18997
+ }
18998
+ async function resolveListTableExportRows(rows, columns, settings, signal) {
18999
+ if (!settings?.sortState.length) return rows;
19000
+ return sortRowsAsync({
19001
+ rows,
19002
+ leafColumns: columns,
19003
+ sortState: settings.sortState,
19004
+ signal
19005
+ });
19006
+ }
19007
+ async function buildListTableExportFooterRows(rows, columns, settings, includeSummary = false, signal) {
19008
+ if (!(settings?.includeSummary ?? includeSummary)) return [];
19009
+ const values = await computeSummaryValuesAsync({ rows, columns, signal });
19010
+ return [
19011
+ columns.map(
19012
+ (column) => resolveSummaryCellDisplayValue(column, values.get(column.key))
19013
+ )
19014
+ ];
19015
+ }
17999
19016
  function renderResultToClipboardText(result) {
18000
19017
  if (result == null) {
18001
19018
  return "";
@@ -18260,11 +19277,14 @@ var ListTableContentActionController = class {
18260
19277
  const includeSummary = options?.includeSummary ?? this.options.getSummaryVisible?.() ?? false;
18261
19278
  options = { ...this.options.getOptions().exportOptions, ...options };
18262
19279
  this.startExportTask(
18263
- (fileName) => exportAllRowsXlsxContent({
19280
+ (fileName, settings) => exportAllRowsXlsxContent({
18264
19281
  rows: this.options.getOptions().dataSource,
18265
- columns: this.options.getLeafColumns(),
19282
+ columns: getListTableExportCandidateColumns(
19283
+ this.options.getOptions().columns
19284
+ ),
18266
19285
  ...options,
18267
19286
  includeSummary,
19287
+ ...settings ? { settings } : null,
18268
19288
  fileName: fileName ?? options?.fileName,
18269
19289
  onProgress: (progress) => {
18270
19290
  const fileLabel = progress.fileCount && progress.fileCount > 1 ? `\uFF08\u7B2C ${progress.fileIndex}/${progress.fileCount} \u4EFD\uFF09` : "";
@@ -18277,10 +19297,17 @@ var ListTableContentActionController = class {
18277
19297
  "\u6B63\u5728\u5BFC\u51FA XLSX\u2026",
18278
19298
  options?.onError,
18279
19299
  options?.fileName?.trim() ? `${options.fileName.trim().replace(/\.xlsx$/i, "")}.xlsx` : buildTableExportFileName("xlsx"),
18280
- options?.promptFileName
19300
+ options?.promptFileName,
19301
+ {
19302
+ columns: getListTableExportColumnChoices(
19303
+ getListTableExportCandidateColumns(this.options.getOptions().columns),
19304
+ this.options.getSortState?.()
19305
+ ),
19306
+ includeSummary
19307
+ }
18281
19308
  );
18282
19309
  }
18283
- startExportTask(task, text, onError, defaultFileName, promptFileName = this.options.getOptions().exportOptions?.promptFileName ?? true) {
19310
+ startExportTask(task, text, onError, defaultFileName, promptFileName = this.options.getOptions().exportOptions?.promptFileName ?? true, dialog) {
18284
19311
  if (this.exportTaskRunning || this.exportNamePending) {
18285
19312
  return;
18286
19313
  }
@@ -18295,14 +19322,15 @@ var ListTableContentActionController = class {
18295
19322
  };
18296
19323
  const request = {
18297
19324
  fileName: defaultFileName,
18298
- onConfirm: (value) => {
19325
+ ...dialog,
19326
+ onConfirm: (value, settings) => {
18299
19327
  if (settled || !value.trim()) return;
18300
19328
  close();
18301
19329
  const extension = defaultFileName.match(
18302
19330
  /\.(xlsx|csv|txt|json)$/i
18303
19331
  )?.[0];
18304
19332
  const name = extension && !value.trim().toLowerCase().endsWith(extension.toLowerCase()) ? `${value.trim()}${extension}` : value.trim();
18305
- this.startExportTask(() => task(name), text, onError);
19333
+ this.startExportTask(() => task(name, settings), text, onError);
18306
19334
  },
18307
19335
  onCancel: () => {
18308
19336
  if (!settled) close();
@@ -19223,7 +20251,7 @@ function resolveScrollTopToRevealRow(params) {
19223
20251
  if (frozenRows.top.rowIndexMap?.includes(rowIndex) || frozenRows.bottom.rowIndexMap?.includes(rowIndex)) {
19224
20252
  return null;
19225
20253
  }
19226
- const scrollSectionIndex = frozenRows.scroll.rowIndexMap ? frozenRows.scroll.rowIndexMap.indexOf(rowIndex) : rowIndex;
20254
+ const scrollSectionIndex = frozenRows.scroll.dataRowIndexAligned ? rowIndex : frozenRows.scroll.rowIndexMap ? frozenRows.scroll.rowIndexMap.indexOf(rowIndex) : rowIndex;
19227
20255
  if (scrollSectionIndex < 0) {
19228
20256
  return null;
19229
20257
  }
@@ -19466,7 +20494,7 @@ var ListTableEditingController = class {
19466
20494
  });
19467
20495
  const nextTop = resolveScrollTopToRevealRow({
19468
20496
  rowIndex,
19469
- rowCount: snapshot.visibleRows.length,
20497
+ rowCount: snapshot.frozenRows.scroll.dataRowIndexAligned ? snapshot.dataRowCount : snapshot.visibleRows.length,
19470
20498
  rowHeight: snapshot.rowHeight,
19471
20499
  bodyHeight: snapshot.bodyLayout.scrollBodyHeight,
19472
20500
  scrollTop: scroll.top,
@@ -19685,257 +20713,6 @@ function setColumnConditionalFormats(columns, key, rules, cache) {
19685
20713
  }
19686
20714
  return false;
19687
20715
  }
19688
-
19689
- // src/services/range-highlight-service.ts
19690
- var EMPTY_RANGE_HIGHLIGHT_STATE = {
19691
- selectedRowAnchorIndex: null,
19692
- selectedRowRange: null,
19693
- selectedColumnAnchorKey: null,
19694
- selectedColumnRange: null
19695
- };
19696
- function isIndexInHighlightRange2(index, range) {
19697
- return Boolean(range && index >= range.start && index <= range.end);
19698
- }
19699
- function resolveNextRowHighlight(params) {
19700
- return resolveNextRowHighlightByIndexRange({
19701
- current: params.current,
19702
- rangeStartIndex: params.rowIndex,
19703
- rangeEndIndex: params.rowIndex,
19704
- shiftKey: params.shiftKey
19705
- });
19706
- }
19707
- function resolveNextRowHighlightByIndexRange(params) {
19708
- const rangeStartIndex = Math.max(0, Math.floor(params.rangeStartIndex));
19709
- const rangeEndIndex = Math.max(
19710
- rangeStartIndex,
19711
- Math.floor(params.rangeEndIndex)
19712
- );
19713
- const anchor = params.shiftKey && params.current.selectedRowAnchorIndex != null ? params.current.selectedRowAnchorIndex : rangeStartIndex;
19714
- return {
19715
- ...params.current,
19716
- selectedRowAnchorIndex: anchor,
19717
- selectedRowRange: createHighlightRange(anchor, rangeEndIndex)
19718
- };
19719
- }
19720
- function resolveNextRowHighlightForContextMenu(params) {
19721
- if (isIndexInHighlightRange2(params.rowIndex, params.current.selectedRowRange)) {
19722
- return params.current;
19723
- }
19724
- return resolveNextRowHighlight({
19725
- current: params.current,
19726
- rowIndex: params.rowIndex,
19727
- shiftKey: false
19728
- });
19729
- }
19730
- function resolveNextColumnHighlightForContextMenu(params) {
19731
- const currentRange = params.current.selectedColumnRange;
19732
- if (currentRange && params.rangeStartIndex >= currentRange.start && params.rangeEndIndex <= currentRange.end) {
19733
- return {
19734
- ...params.current,
19735
- selectedRowAnchorIndex: null,
19736
- selectedRowRange: null
19737
- };
19738
- }
19739
- return resolveNextColumnHighlightForHeaderClick({
19740
- ...params,
19741
- shiftKey: false
19742
- });
19743
- }
19744
- function resolveColumnKeysForContextMenuAction(params) {
19745
- const columnIndex = params.leafColumnKeys.indexOf(params.columnKey);
19746
- const rangeStartIndex = params.rangeStartIndex ?? columnIndex;
19747
- const rangeEndIndex = params.rangeEndIndex ?? columnIndex;
19748
- const hasValidTargetRange = rangeStartIndex >= 0 && rangeEndIndex >= rangeStartIndex && rangeEndIndex < params.leafColumnKeys.length;
19749
- const currentRange = params.current.selectedColumnRange;
19750
- if (currentRange && hasValidTargetRange && rangeStartIndex >= currentRange.start && rangeEndIndex <= currentRange.end) {
19751
- return params.leafColumnKeys.slice(
19752
- currentRange.start,
19753
- currentRange.end + 1
19754
- );
19755
- }
19756
- if (hasValidTargetRange) {
19757
- return params.leafColumnKeys.slice(rangeStartIndex, rangeEndIndex + 1);
19758
- }
19759
- return [];
19760
- }
19761
- function resolveNextColumnHighlight(params) {
19762
- const columnIndex = params.leafColumnKeys.indexOf(params.columnKey);
19763
- if (columnIndex < 0) {
19764
- return params.current;
19765
- }
19766
- return resolveNextColumnHighlightByIndexRange({
19767
- current: params.current,
19768
- columnKey: params.columnKey,
19769
- leafColumnKeys: params.leafColumnKeys,
19770
- rangeStartIndex: columnIndex,
19771
- rangeEndIndex: columnIndex,
19772
- shiftKey: params.shiftKey
19773
- });
19774
- }
19775
- function resolveNextColumnHighlightForHeaderClick(params) {
19776
- const nextCurrent = {
19777
- ...params.current,
19778
- selectedRowAnchorIndex: null,
19779
- selectedRowRange: null
19780
- };
19781
- const hasValidRange = params.rangeStartIndex >= 0 && params.rangeEndIndex >= 0 && params.rangeStartIndex < params.leafColumnKeys.length && params.rangeEndIndex < params.leafColumnKeys.length;
19782
- if (!params.shiftKey) {
19783
- const leafColumnIndex = params.leafColumnKeys.indexOf(params.columnKey);
19784
- if (leafColumnIndex >= 0) {
19785
- return resolveNextColumnHighlightByIndexRange({
19786
- ...params,
19787
- current: nextCurrent,
19788
- rangeStartIndex: leafColumnIndex,
19789
- rangeEndIndex: leafColumnIndex
19790
- });
19791
- }
19792
- if (hasValidRange) {
19793
- return resolveNextColumnHighlightByIndexRange({
19794
- ...params,
19795
- current: nextCurrent
19796
- });
19797
- }
19798
- return {
19799
- ...nextCurrent,
19800
- selectedColumnAnchorKey: params.columnKey,
19801
- selectedColumnRange: null
19802
- };
19803
- }
19804
- if (hasValidRange) {
19805
- return resolveNextColumnHighlightByIndexRange({
19806
- ...params,
19807
- current: nextCurrent
19808
- });
19809
- }
19810
- return resolveNextColumnHighlight({
19811
- current: nextCurrent,
19812
- columnKey: params.columnKey,
19813
- leafColumnKeys: params.leafColumnKeys,
19814
- shiftKey: params.shiftKey
19815
- });
19816
- }
19817
- function resolveNextColumnHighlightByIndexRange(params) {
19818
- if (params.rangeStartIndex < 0 || params.rangeEndIndex < 0 || params.rangeStartIndex >= params.leafColumnKeys.length || params.rangeEndIndex >= params.leafColumnKeys.length) {
19819
- return params.current;
19820
- }
19821
- const anchorIndex = resolveColumnAnchorIndex({
19822
- anchorKey: params.current.selectedColumnAnchorKey,
19823
- leafColumnKeys: params.leafColumnKeys
19824
- });
19825
- const nextAnchorIndex = params.shiftKey && anchorIndex != null ? anchorIndex : params.rangeStartIndex;
19826
- return {
19827
- ...params.current,
19828
- selectedColumnAnchorKey: params.leafColumnKeys[nextAnchorIndex] ?? params.columnKey,
19829
- selectedColumnRange: createHighlightRange(
19830
- nextAnchorIndex,
19831
- params.shiftKey ? params.rangeEndIndex : params.rangeEndIndex
19832
- )
19833
- };
19834
- }
19835
- function resolveColumnAnchorIndex(params) {
19836
- if (!params.anchorKey) {
19837
- return null;
19838
- }
19839
- const anchorIndex = params.leafColumnKeys.indexOf(params.anchorKey);
19840
- return anchorIndex >= 0 ? anchorIndex : null;
19841
- }
19842
- function createHighlightRange(start, end) {
19843
- return {
19844
- start: Math.min(start, end),
19845
- end: Math.max(start, end)
19846
- };
19847
- }
19848
- function resolveHighlightMode(mode) {
19849
- if (mode === "cell" || mode === "column" || mode === "cross") {
19850
- return mode;
19851
- }
19852
- return "row";
19853
- }
19854
- function resolveRangeHighlightForHeaderSelection(params) {
19855
- const nextRangeHighlight = {
19856
- ...params.current,
19857
- selectedRowAnchorIndex: null,
19858
- selectedRowRange: null
19859
- };
19860
- if (!params.shiftKey) {
19861
- nextRangeHighlight.selectedColumnAnchorKey = EMPTY_RANGE_HIGHLIGHT_STATE.selectedColumnAnchorKey;
19862
- nextRangeHighlight.selectedColumnRange = EMPTY_RANGE_HIGHLIGHT_STATE.selectedColumnRange;
19863
- }
19864
- return nextRangeHighlight;
19865
- }
19866
- function resolveRangeHighlightForBodyInteraction(params) {
19867
- const mode = resolveHighlightMode(params.mode);
19868
- const canHighlightColumn = params.columnKey !== params.selectionColumnKey && params.columnKey !== params.expandColumnKey;
19869
- const rangeEndIndex = params.rowIndex + Math.max(Math.floor(params.rowSpan ?? 1), 1) - 1;
19870
- if (mode === "cell") {
19871
- return EMPTY_RANGE_HIGHLIGHT_STATE;
19872
- }
19873
- if (mode === "row") {
19874
- return {
19875
- ...resolveNextRowHighlightByIndexRange({
19876
- current: params.current,
19877
- rangeStartIndex: params.rowIndex,
19878
- rangeEndIndex,
19879
- shiftKey: params.shiftKey
19880
- }),
19881
- selectedColumnAnchorKey: null,
19882
- selectedColumnRange: null
19883
- };
19884
- }
19885
- if (mode === "column") {
19886
- if (!canHighlightColumn || !params.columnRange) {
19887
- return {
19888
- ...params.current,
19889
- selectedRowAnchorIndex: null,
19890
- selectedRowRange: null
19891
- };
19892
- }
19893
- return {
19894
- ...resolveNextColumnHighlightByIndexRange({
19895
- current: params.current,
19896
- columnKey: params.columnKey,
19897
- leafColumnKeys: params.leafColumnKeys,
19898
- rangeStartIndex: params.columnRange.start,
19899
- rangeEndIndex: params.columnRange.end,
19900
- shiftKey: params.shiftKey
19901
- }),
19902
- selectedRowAnchorIndex: null,
19903
- selectedRowRange: null
19904
- };
19905
- }
19906
- let next = resolveNextRowHighlightByIndexRange({
19907
- current: params.current,
19908
- rangeStartIndex: params.rowIndex,
19909
- rangeEndIndex,
19910
- shiftKey: params.shiftKey
19911
- });
19912
- if (canHighlightColumn && params.columnRange) {
19913
- next = resolveNextColumnHighlightByIndexRange({
19914
- current: next,
19915
- columnKey: params.columnKey,
19916
- leafColumnKeys: params.leafColumnKeys,
19917
- rangeStartIndex: params.columnRange.start,
19918
- rangeEndIndex: params.columnRange.end,
19919
- shiftKey: false
19920
- });
19921
- }
19922
- return next;
19923
- }
19924
- function isCellHighlightedByMode(params) {
19925
- const mode = resolveHighlightMode(params.mode);
19926
- if (mode === "cell") {
19927
- return params.isCellSelected;
19928
- }
19929
- if (mode === "row") {
19930
- return params.isRowRangeSelected;
19931
- }
19932
- if (mode === "column") {
19933
- return params.isColumnRangeSelected;
19934
- }
19935
- return params.isRowRangeSelected || params.isColumnRangeSelected;
19936
- }
19937
-
19938
- // src/rendering/primitives/draw-body.ts
19939
20716
  var VALIDATION_ERROR_COLOR = "#ff4d4f";
19940
20717
  var VALIDATION_TEXT_COLOR = "#ffffff";
19941
20718
  var VALIDATION_MUTED_TEXT_COLOR = "rgba(255, 255, 255, 0.72)";
@@ -20019,7 +20796,8 @@ function drawSegmentRows(params, segment, columns, startRowIndex, endRowIndex, r
20019
20796
  columns,
20020
20797
  startRowIndex,
20021
20798
  endRowIndex,
20022
- rowIndexMap: params.rowIndexMap
20799
+ rowIndexMap: params.rowIndexMap,
20800
+ horizontalClip: clipRect
20023
20801
  });
20024
20802
  const animatedCellHeights = /* @__PURE__ */ new Map();
20025
20803
  let collapseAnimationOverflow = 0;
@@ -20055,7 +20833,7 @@ function drawSegmentRows(params, segment, columns, startRowIndex, endRowIndex, r
20055
20833
  renderClipRect.height
20056
20834
  );
20057
20835
  ctx.clip();
20058
- const orderedCells = [...visibleCells].sort(
20836
+ const orderedCells = animatedCellHeights.size === 0 ? visibleCells : [...visibleCells].sort(
20059
20837
  (left, right) => Number(animatedCellHeights.has(left)) - Number(animatedCellHeights.has(right))
20060
20838
  );
20061
20839
  orderedCells.forEach((cell) => {
@@ -20081,7 +20859,7 @@ function drawSegmentRows(params, segment, columns, startRowIndex, endRowIndex, r
20081
20859
  width: getMergedCellWidth(columns, cell.columnIndex, cell.colSpan),
20082
20860
  height: animatedCellHeights.get(cell) ?? baseHeight
20083
20861
  };
20084
- if (rect.y + rect.height <= params.bodyTop || rect.y >= params.bodyTop + params.bodyHeight) {
20862
+ 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) {
20085
20863
  return;
20086
20864
  }
20087
20865
  const isHovered = params.hover.area === "body" && params.hover.rowIndex === cell.sourceRowIndex && params.hover.columnKey === cell.column.key;
@@ -20089,24 +20867,28 @@ function drawSegmentRows(params, segment, columns, startRowIndex, endRowIndex, r
20089
20867
  const hasCellTextSelection = params.selection.textSelection?.fragments.some(
20090
20868
  (fragment) => fragment.rowIndex === cell.sourceRowIndex && fragment.columnKey === cell.column.key
20091
20869
  ) === true;
20092
- const isRowRangeSelected = cell.row.kind === "data" && isRowSpanInHighlightRange(
20093
- cell.sourceRowIndex,
20094
- cell.rowSpan,
20870
+ const isRowRangeSelected = cell.row.kind === "data" && (params.rangeHighlight?.selectedRowRanges ?? ((params.rangeHighlight ?? EMPTY_RANGE_HIGHLIGHT3).selectedRowRange ? [
20095
20871
  (params.rangeHighlight ?? EMPTY_RANGE_HIGHLIGHT3).selectedRowRange
20872
+ ] : [])).some(
20873
+ (range) => isRowSpanInHighlightRange(cell.sourceRowIndex, cell.rowSpan, range)
20096
20874
  );
20097
- const isColumnRangeSelected = cell.row.kind === "data" && isIndexInHighlightRange3(
20098
- cell.column.leafIndex,
20099
- (params.rangeHighlight ?? EMPTY_RANGE_HIGHLIGHT3).selectedColumnRange
20875
+ const isColumnRangeSelected = cell.row.kind === "data" && isColumnInHighlight(
20876
+ params.rangeHighlight ?? EMPTY_RANGE_HIGHLIGHT3,
20877
+ cell.column.key,
20878
+ cell.column.leafIndex
20879
+ );
20880
+ const isSelectedCellRange = cell.row.kind === "data" && isCellInHighlight(
20881
+ params.rangeHighlight ?? EMPTY_RANGE_HIGHLIGHT3,
20882
+ cell.sourceRowIndex,
20883
+ cell.column.leafIndex
20100
20884
  );
20101
- const selectedCellRange = (params.rangeHighlight ?? EMPTY_RANGE_HIGHLIGHT3).selectedCellRange;
20102
- 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;
20103
20885
  const isHeaderColumnSelected = cell.row.kind === "data" && params.selectedHeaderKey != null && cell.column.key === params.selectedHeaderKey;
20104
- const isRangeHighlighted = isCellHighlightedByMode({
20886
+ const isRangeHighlighted = isSelectedCellRange || isCellHighlightedByMode({
20105
20887
  mode: params.highlightMode,
20106
20888
  isCellSelected: isSelected || isSelectedCellRange,
20107
20889
  isRowRangeSelected,
20108
20890
  isColumnRangeSelected
20109
- });
20891
+ }) || params.selection.rowIndex == null && (params.selectedHeaderKey != null || (params.rangeHighlight ?? EMPTY_RANGE_HIGHLIGHT3).selectedColumnKeys != null) && isColumnRangeSelected;
20110
20892
  const validationMessage = cell.row.kind === "data" ? params.getValidationMessage?.({
20111
20893
  record: cell.record,
20112
20894
  rowIndex: cell.sourceRowIndex,
@@ -20171,6 +20953,7 @@ function drawSegmentRows(params, segment, columns, startRowIndex, endRowIndex, r
20171
20953
  kind: "body",
20172
20954
  rect,
20173
20955
  columnKey: cell.column.key,
20956
+ cursor: cell.column.key === ROW_SELECTOR_COLUMN_KEY ? "pointer" : void 0,
20174
20957
  rowIndex: cell.sourceRowIndex,
20175
20958
  rowSpan: cell.rowSpan,
20176
20959
  fixed: cell.column.fixed,
@@ -20437,9 +21220,6 @@ function findVisibleDataRow(rows, startIndex, step) {
20437
21220
  }
20438
21221
  return null;
20439
21222
  }
20440
- function isIndexInHighlightRange3(index, range) {
20441
- return Boolean(range && index >= range.start && index <= range.end);
20442
- }
20443
21223
  function isRowSpanInHighlightRange(rowIndex, rowSpan, range) {
20444
21224
  if (!range) {
20445
21225
  return false;
@@ -20534,6 +21314,14 @@ function buildMergedBodyCells(rows, columns, mergeCell, rowIndexMap) {
20534
21314
  function buildPlainVisibleBodyCells(params) {
20535
21315
  const { rows, columns, startRowIndex, endRowIndex } = params;
20536
21316
  const visibleCells = [];
21317
+ const clip = params.horizontalClip;
21318
+ const visibleColumnIndexes = [];
21319
+ for (let index = 0; index < columns.length; index += 1) {
21320
+ const column = columns[index];
21321
+ if (column && (!clip || column.drawX + column.width >= clip.x - 1 && column.drawX <= clip.x + clip.width + 1)) {
21322
+ visibleColumnIndexes.push(index);
21323
+ }
21324
+ }
20537
21325
  for (let rowIndex = startRowIndex; rowIndex < endRowIndex; rowIndex += 1) {
20538
21326
  const row = rows[rowIndex];
20539
21327
  if (!row) {
@@ -20557,7 +21345,7 @@ function buildPlainVisibleBodyCells(params) {
20557
21345
  continue;
20558
21346
  }
20559
21347
  const sourceRowIndex = row.dataRowIndex;
20560
- for (let columnIndex = 0; columnIndex < columns.length; columnIndex += 1) {
21348
+ for (const columnIndex of visibleColumnIndexes) {
20561
21349
  const column = columns[columnIndex];
20562
21350
  if (!column) {
20563
21351
  continue;
@@ -21539,7 +22327,7 @@ function getMaxDepth(columns) {
21539
22327
  var DEFAULT_HORIZONTAL_LEFT = 0;
21540
22328
  var DEFAULT_HORIZONTAL_RIGHT = 0;
21541
22329
  var DEFAULT_HORIZONTAL_BOTTOM = 0;
21542
- var DEFAULT_VERTICAL_TOP = 6;
22330
+ var DEFAULT_VERTICAL_TOP = 0;
21543
22331
  var DEFAULT_VERTICAL_RIGHT = 0;
21544
22332
  var DEFAULT_VERTICAL_BOTTOM = 0;
21545
22333
  var MIN_SCROLLBAR_THUMB_SIZE = 36;
@@ -21549,6 +22337,7 @@ function computeScrollbarLayout(params) {
21549
22337
  const vertical = resolveCanvasScrollbarAxis(params.scrollbar, "vertical");
21550
22338
  const horizontalLeft = Math.max(
21551
22339
  params.scrollbar?.horizontalLeft ?? DEFAULT_HORIZONTAL_LEFT,
22340
+ params.rowSelectorWidth ?? 0,
21552
22341
  0
21553
22342
  );
21554
22343
  const horizontalRight = Math.max(
@@ -21578,43 +22367,69 @@ function computeScrollbarLayout(params) {
21578
22367
  const horizontalTrackY = params.height - horizontal.thickness - horizontalBottom;
21579
22368
  if (horizontal.visible && params.maxScrollLeft > 0 && horizontalTrackWidth > 0) {
21580
22369
  const thickness = horizontal.thickness;
21581
- const buttonSize = Math.min(thickness, horizontalTrackWidth / 3);
21582
- const trackX = horizontalLeft + buttonSize;
21583
- const trackWidth = Math.max(horizontalTrackWidth - buttonSize * 2, 0);
21584
- const thumbWidth = Math.min(
21585
- Math.max(
21586
- params.centerVisibleWidth / params.centerContentWidth * trackWidth,
21587
- MIN_SCROLLBAR_THUMB_SIZE
21588
- ),
21589
- trackWidth
21590
- );
21591
- const thumbOffset = params.maxScrollLeft === 0 ? 0 : params.scrollLeft / params.maxScrollLeft * (trackWidth - thumbWidth);
21592
- layout.horizontal = {
21593
- decreaseButton: {
21594
- x: horizontalLeft,
21595
- y: horizontalTrackY,
21596
- width: buttonSize,
21597
- height: thickness
21598
- },
21599
- increaseButton: {
21600
- x: horizontalLeft + horizontalTrackWidth - buttonSize,
22370
+ if (horizontal.mode === "button") {
22371
+ const halfWidth = horizontalTrackWidth / 2;
22372
+ const empty = {
22373
+ x: horizontalLeft + halfWidth,
21601
22374
  y: horizontalTrackY,
21602
- width: buttonSize,
21603
- height: thickness
21604
- },
21605
- track: {
21606
- x: trackX,
21607
- y: horizontalTrackY,
21608
- width: trackWidth,
21609
- height: thickness
21610
- },
21611
- thumb: {
21612
- x: trackX + thumbOffset,
21613
- y: horizontalTrackY,
21614
- width: thumbWidth,
21615
- height: thickness
21616
- }
21617
- };
22375
+ width: 0,
22376
+ height: 0
22377
+ };
22378
+ layout.horizontal = {
22379
+ decreaseButton: {
22380
+ x: horizontalLeft,
22381
+ y: horizontalTrackY,
22382
+ width: halfWidth,
22383
+ height: thickness
22384
+ },
22385
+ increaseButton: {
22386
+ x: horizontalLeft + halfWidth,
22387
+ y: horizontalTrackY,
22388
+ width: horizontalTrackWidth - halfWidth,
22389
+ height: thickness
22390
+ },
22391
+ track: empty,
22392
+ thumb: empty
22393
+ };
22394
+ } else {
22395
+ const buttonSize = Math.min(thickness, horizontalTrackWidth / 3);
22396
+ const trackX = horizontalLeft + buttonSize;
22397
+ const trackWidth = Math.max(horizontalTrackWidth - buttonSize * 2, 0);
22398
+ const thumbWidth = Math.min(
22399
+ Math.max(
22400
+ params.centerVisibleWidth / params.centerContentWidth * trackWidth,
22401
+ MIN_SCROLLBAR_THUMB_SIZE
22402
+ ),
22403
+ trackWidth
22404
+ );
22405
+ const thumbOffset = params.maxScrollLeft === 0 ? 0 : params.scrollLeft / params.maxScrollLeft * (trackWidth - thumbWidth);
22406
+ layout.horizontal = {
22407
+ decreaseButton: {
22408
+ x: horizontalLeft,
22409
+ y: horizontalTrackY,
22410
+ width: buttonSize,
22411
+ height: thickness
22412
+ },
22413
+ increaseButton: {
22414
+ x: horizontalLeft + horizontalTrackWidth - buttonSize,
22415
+ y: horizontalTrackY,
22416
+ width: buttonSize,
22417
+ height: thickness
22418
+ },
22419
+ track: {
22420
+ x: trackX,
22421
+ y: horizontalTrackY,
22422
+ width: trackWidth,
22423
+ height: thickness
22424
+ },
22425
+ thumb: {
22426
+ x: trackX + thumbOffset,
22427
+ y: horizontalTrackY,
22428
+ width: thumbWidth,
22429
+ height: thickness
22430
+ }
22431
+ };
22432
+ }
21618
22433
  }
21619
22434
  const verticalTrackHeight = Math.max(
21620
22435
  params.bodyHeight - verticalTop - verticalBottom,
@@ -21624,43 +22439,69 @@ function computeScrollbarLayout(params) {
21624
22439
  const verticalTrackY = params.bodyTop + verticalTop;
21625
22440
  if (vertical.visible && params.maxScrollTop > 0 && verticalTrackHeight > 0) {
21626
22441
  const thickness = vertical.thickness;
21627
- const buttonSize = Math.min(thickness, verticalTrackHeight / 3);
21628
- const trackY = verticalTrackY + buttonSize;
21629
- const trackHeight = Math.max(verticalTrackHeight - buttonSize * 2, 0);
21630
- const thumbHeight = Math.min(
21631
- Math.max(
21632
- params.bodyHeight / params.totalBodyHeight * trackHeight,
21633
- MIN_SCROLLBAR_THUMB_SIZE
21634
- ),
21635
- trackHeight
21636
- );
21637
- const thumbOffset = params.maxScrollTop === 0 ? 0 : params.scrollTop / params.maxScrollTop * (trackHeight - thumbHeight);
21638
- layout.vertical = {
21639
- decreaseButton: {
21640
- x: verticalTrackX,
21641
- y: verticalTrackY,
21642
- width: thickness,
21643
- height: buttonSize
21644
- },
21645
- increaseButton: {
21646
- x: verticalTrackX,
21647
- y: verticalTrackY + verticalTrackHeight - buttonSize,
21648
- width: thickness,
21649
- height: buttonSize
21650
- },
21651
- track: {
21652
- x: verticalTrackX,
21653
- y: trackY,
21654
- width: thickness,
21655
- height: trackHeight
21656
- },
21657
- thumb: {
22442
+ if (vertical.mode === "button") {
22443
+ const halfHeight = verticalTrackHeight / 2;
22444
+ const empty = {
21658
22445
  x: verticalTrackX,
21659
- y: trackY + thumbOffset,
21660
- width: thickness,
21661
- height: thumbHeight
21662
- }
21663
- };
22446
+ y: verticalTrackY + halfHeight,
22447
+ width: 0,
22448
+ height: 0
22449
+ };
22450
+ layout.vertical = {
22451
+ decreaseButton: {
22452
+ x: verticalTrackX,
22453
+ y: verticalTrackY,
22454
+ width: thickness,
22455
+ height: halfHeight
22456
+ },
22457
+ increaseButton: {
22458
+ x: verticalTrackX,
22459
+ y: verticalTrackY + halfHeight,
22460
+ width: thickness,
22461
+ height: verticalTrackHeight - halfHeight
22462
+ },
22463
+ track: empty,
22464
+ thumb: empty
22465
+ };
22466
+ } else {
22467
+ const buttonSize = Math.min(thickness, verticalTrackHeight / 3);
22468
+ const trackY = verticalTrackY + buttonSize;
22469
+ const trackHeight = Math.max(verticalTrackHeight - buttonSize * 2, 0);
22470
+ const thumbHeight = Math.min(
22471
+ Math.max(
22472
+ params.bodyHeight / params.totalBodyHeight * trackHeight,
22473
+ MIN_SCROLLBAR_THUMB_SIZE
22474
+ ),
22475
+ trackHeight
22476
+ );
22477
+ const thumbOffset = params.maxScrollTop === 0 ? 0 : params.scrollTop / params.maxScrollTop * (trackHeight - thumbHeight);
22478
+ layout.vertical = {
22479
+ decreaseButton: {
22480
+ x: verticalTrackX,
22481
+ y: verticalTrackY,
22482
+ width: thickness,
22483
+ height: buttonSize
22484
+ },
22485
+ increaseButton: {
22486
+ x: verticalTrackX,
22487
+ y: verticalTrackY + verticalTrackHeight - buttonSize,
22488
+ width: thickness,
22489
+ height: buttonSize
22490
+ },
22491
+ track: {
22492
+ x: verticalTrackX,
22493
+ y: trackY,
22494
+ width: thickness,
22495
+ height: trackHeight
22496
+ },
22497
+ thumb: {
22498
+ x: verticalTrackX,
22499
+ y: trackY + thumbOffset,
22500
+ width: thickness,
22501
+ height: thumbHeight
22502
+ }
22503
+ };
22504
+ }
21664
22505
  }
21665
22506
  return layout;
21666
22507
  }
@@ -21878,30 +22719,25 @@ function resolveColumnSegments(params) {
21878
22719
  // src/domain/columns/header-layout.ts
21879
22720
  function buildHeaderLayouts(nodes, leafColumns, rightStartX, headerRowHeight, fixedWidthLeft, fixedWidthRight, scrollLeft) {
21880
22721
  const rows = /* @__PURE__ */ new Map();
21881
- const leftCols = leafColumns.filter((c) => c.fixed === "left");
21882
- const centerCols = leafColumns.filter((c) => !c.fixed);
21883
- const rightCols = leafColumns.filter((c) => c.fixed === "right");
21884
- function segmentOffset(cols, targetKey) {
21885
- let offset = 0;
21886
- for (const c of cols) {
21887
- if (c.key === targetKey) return offset;
21888
- offset += c.width;
21889
- }
21890
- return offset;
21891
- }
21892
- const getLeafX = (leafIndex) => {
21893
- const leaf = leafColumns[leafIndex];
21894
- if (!leaf) {
21895
- return 0;
21896
- }
22722
+ let leftX = 0;
22723
+ let centerX = fixedWidthLeft - scrollLeft;
22724
+ let rightX = rightStartX;
22725
+ const leafPositions = leafColumns.map((leaf) => {
21897
22726
  if (leaf.fixed === "left") {
21898
- return segmentOffset(leftCols, leaf.key);
22727
+ const x2 = leftX;
22728
+ leftX += leaf.width;
22729
+ return x2;
21899
22730
  }
21900
22731
  if (leaf.fixed === "right") {
21901
- return rightStartX + segmentOffset(rightCols, leaf.key);
22732
+ const x2 = rightX;
22733
+ rightX += leaf.width;
22734
+ return x2;
21902
22735
  }
21903
- return fixedWidthLeft + segmentOffset(centerCols, leaf.key) - scrollLeft;
21904
- };
22736
+ const x = centerX;
22737
+ centerX += leaf.width;
22738
+ return x;
22739
+ });
22740
+ const getLeafX = (leafIndex) => leafPositions[leafIndex] ?? 0;
21905
22741
  const visit = (node) => {
21906
22742
  const rowItems = rows.get(node.depth) ?? [];
21907
22743
  buildHeaderCellSegments(node, leafColumns, {
@@ -23502,13 +24338,15 @@ var AUTO_ROW_HEIGHT_SAMPLE_COUNT = 200;
23502
24338
  function buildListTableRenderSnapshot(params) {
23503
24339
  if (params.cache?.snapshot && params.cache.snapshot.groupIndex === params.cache.groupIndex && isSameRenderCacheInputs(params.cache.inputs, params)) {
23504
24340
  const snapshotStartedAt2 = resolvePerformanceNow3();
23505
- const snapshot2 = updateListTableRenderSnapshotScroll({
24341
+ const cached = params.cache.snapshot;
24342
+ const snapshot2 = !cached.swapAnimation && cached.scroll.left === params.scroll.left && cached.scroll.top === params.scroll.top ? cached : updateListTableRenderSnapshotScroll({
23506
24343
  snapshot: params.cache.snapshot,
23507
24344
  width: params.width,
23508
24345
  height: params.height,
23509
24346
  scroll: params.scroll,
23510
24347
  options: params.options
23511
24348
  });
24349
+ params.cache.snapshot = snapshot2;
23512
24350
  params.options.debug?.onPerformance?.({
23513
24351
  phase: "snapshot",
23514
24352
  durationMs: resolvePerformanceNow3() - snapshotStartedAt2,
@@ -23871,6 +24709,7 @@ function buildListTableRenderSnapshot(params) {
23871
24709
  });
23872
24710
  const scrollbarLayout = computeScrollbarLayout({
23873
24711
  width: hostWidth,
24712
+ rowSelectorWidth: leafColumns.find((column) => column.key === ROW_SELECTOR_COLUMN_KEY)?.width ?? 0,
23874
24713
  height: hostHeight,
23875
24714
  bodyTop: bodyState.bodyLayout.scrollBodyTop,
23876
24715
  bodyHeight: bodyState.bodyLayout.scrollBodyHeight,
@@ -24083,12 +24922,13 @@ function updateListTableRenderSnapshotScroll(params) {
24083
24922
  scroll
24084
24923
  });
24085
24924
  const nextScroll = viewportResult.scroll;
24086
- const columnSegments = resolveColumnSegments({
24925
+ const reuseHorizontalLayout = nextScroll.left === snapshot.scroll.left && !snapshot.swapAnimation;
24926
+ const columnSegments = reuseHorizontalLayout ? snapshot.columnSegments : resolveColumnSegments({
24087
24927
  metrics: snapshot.columnMetrics,
24088
24928
  scrollLeft: nextScroll.left,
24089
24929
  swapAnimation: snapshot.swapAnimation
24090
24930
  });
24091
- const headerRows = buildHeaderLayouts(
24931
+ const headerRows = reuseHorizontalLayout ? snapshot.headerRows : buildHeaderLayouts(
24092
24932
  snapshot.headerTree,
24093
24933
  snapshot.leafColumns,
24094
24934
  snapshot.columnMetrics.rightStartX,
@@ -24097,7 +24937,8 @@ function updateListTableRenderSnapshotScroll(params) {
24097
24937
  snapshot.columnMetrics.fixedWidthRight,
24098
24938
  nextScroll.left
24099
24939
  );
24100
- offsetHeaderRows(headerRows, snapshot.groupingBarHeight);
24940
+ if (!reuseHorizontalLayout)
24941
+ offsetHeaderRows(headerRows, snapshot.groupingBarHeight);
24101
24942
  if (snapshot.swapAnimation) {
24102
24943
  applyColumnSegmentPositionsToHeaderRows({
24103
24944
  headerRows,
@@ -24107,6 +24948,9 @@ function updateListTableRenderSnapshotScroll(params) {
24107
24948
  }
24108
24949
  const scrollbarLayout = computeScrollbarLayout({
24109
24950
  width,
24951
+ rowSelectorWidth: snapshot.leafColumns.find(
24952
+ (column) => column.key === ROW_SELECTOR_COLUMN_KEY
24953
+ )?.width ?? 0,
24110
24954
  height,
24111
24955
  bodyTop: snapshot.bodyLayout.scrollBodyTop,
24112
24956
  bodyHeight: snapshot.bodyLayout.scrollBodyHeight,
@@ -24445,7 +25289,7 @@ function buildListTableDebugSnapshot(params) {
24445
25289
  hitTest: hitTest2.area === "empty" ? void 0 : hitTest2
24446
25290
  };
24447
25291
  }
24448
- function drawScrollbars(ctx, layout, descriptors, borderColor, opacity = 1, interactive = true, verticalFrame, options) {
25292
+ function drawScrollbars(ctx, layout, descriptors, borderColor, opacity = 1, interactive = true, verticalFrame, options, hover) {
24449
25293
  const alpha = Number.isFinite(opacity) ? Math.min(Math.max(opacity, 0), 1) : 1;
24450
25294
  if (alpha <= 1e-3) {
24451
25295
  return;
@@ -24470,7 +25314,8 @@ function drawScrollbars(ctx, layout, descriptors, borderColor, opacity = 1, inte
24470
25314
  descriptors,
24471
25315
  borderColor,
24472
25316
  interactive,
24473
- resolveCanvasScrollbarAxis(options, "horizontal")
25317
+ resolveCanvasScrollbarAxis(options, "horizontal"),
25318
+ hover
24474
25319
  );
24475
25320
  }
24476
25321
  if (layout.vertical) {
@@ -24481,7 +25326,8 @@ function drawScrollbars(ctx, layout, descriptors, borderColor, opacity = 1, inte
24481
25326
  descriptors,
24482
25327
  borderColor,
24483
25328
  interactive,
24484
- resolveCanvasScrollbarAxis(options, "vertical")
25329
+ resolveCanvasScrollbarAxis(options, "vertical"),
25330
+ hover
24485
25331
  );
24486
25332
  }
24487
25333
  if (layout.horizontal) {
@@ -24492,10 +25338,13 @@ function drawScrollbars(ctx, layout, descriptors, borderColor, opacity = 1, inte
24492
25338
  borderColor
24493
25339
  );
24494
25340
  }
25341
+ if (layout.vertical) {
25342
+ drawVerticalAxisFrame(ctx, layout.vertical, borderColor, verticalFrame);
25343
+ }
24495
25344
  ctx.restore();
24496
25345
  }
24497
25346
  function drawHorizontalScrollbarMask(ctx, layout, backgroundColor) {
24498
- const left = layout.decreaseButton.x;
25347
+ const left = 0;
24499
25348
  const right = layout.increaseButton.x + layout.increaseButton.width;
24500
25349
  const top = layout.decreaseButton.y;
24501
25350
  const height = layout.decreaseButton.height;
@@ -24508,7 +25357,7 @@ function drawHorizontalScrollbarMask(ctx, layout, backgroundColor) {
24508
25357
  ctx.restore();
24509
25358
  }
24510
25359
  function drawHorizontalScrollbarTopBorder(ctx, horizontal, vertical, borderColor) {
24511
- const left = Math.round(horizontal.decreaseButton.x) + 0.5;
25360
+ const left = 0.5;
24512
25361
  const horizontalRight = horizontal.increaseButton.x + horizontal.increaseButton.width;
24513
25362
  const verticalRight = vertical ? vertical.decreaseButton.x + vertical.decreaseButton.width : horizontalRight;
24514
25363
  const right = Math.round(Math.max(horizontalRight, verticalRight)) - 0.5;
@@ -24525,21 +25374,23 @@ function drawHorizontalScrollbarTopBorder(ctx, horizontal, vertical, borderColor
24525
25374
  ctx.stroke();
24526
25375
  ctx.restore();
24527
25376
  }
24528
- function drawAxisScrollbar(ctx, layout, axis, descriptors, borderColor, interactive, style) {
25377
+ function drawAxisScrollbar(ctx, layout, axis, descriptors, borderColor, interactive, style, hover) {
24529
25378
  const kind = axis === "horizontal" ? "horizontal-scrollbar" : "vertical-scrollbar";
24530
25379
  if (interactive) {
24531
- descriptors.push({
24532
- kind,
24533
- rect: layout.thumb,
24534
- columnKey: `__${axis}_scrollbar_thumb__`,
24535
- scrollbarRole: "thumb"
24536
- });
24537
- descriptors.push({
24538
- kind,
24539
- rect: layout.track,
24540
- columnKey: `__${axis}_scrollbar_track__`,
24541
- scrollbarRole: "track"
24542
- });
25380
+ if (style.mode === "scrollbar") {
25381
+ descriptors.push({
25382
+ kind,
25383
+ rect: layout.thumb,
25384
+ columnKey: `__${axis}_scrollbar_thumb__`,
25385
+ scrollbarRole: "thumb"
25386
+ });
25387
+ descriptors.push({
25388
+ kind,
25389
+ rect: layout.track,
25390
+ columnKey: `__${axis}_scrollbar_track__`,
25391
+ scrollbarRole: "track"
25392
+ });
25393
+ }
24543
25394
  descriptors.push({
24544
25395
  kind,
24545
25396
  rect: layout.decreaseButton,
@@ -24565,10 +25416,101 @@ function drawAxisScrollbar(ctx, layout, axis, descriptors, borderColor, interact
24565
25416
  }
24566
25417
  ctx.restore();
24567
25418
  }
25419
+ if (style.mode === "scrollbar") {
25420
+ const pressedTrack = hover?.pressedScrollbarPart?.axis === axis && hover.pressedScrollbarPart.role === "track";
25421
+ const hoveredTrack = hover?.area === `${axis}-scrollbar` && hover.scrollbarRole === "track";
25422
+ if (pressedTrack || hoveredTrack) {
25423
+ ctx.fillStyle = pressedTrack ? style.buttonPressedBackgroundColor : style.buttonHoverBackgroundColor;
25424
+ ctx.fillRect(
25425
+ layout.track.x,
25426
+ layout.track.y,
25427
+ layout.track.width,
25428
+ layout.track.height
25429
+ );
25430
+ }
25431
+ }
25432
+ for (const [role, rect] of [
25433
+ ["decrease-button", layout.decreaseButton],
25434
+ ["increase-button", layout.increaseButton]
25435
+ ]) {
25436
+ const pressed = hover?.pressedScrollbarButton?.axis === axis && hover.pressedScrollbarButton.role === role;
25437
+ const hovered = hover?.area === `${axis}-scrollbar` && hover.scrollbarRole === role;
25438
+ const background = pressed ? style.buttonPressedBackgroundColor : hovered ? style.buttonHoverBackgroundColor : style.buttonBackgroundColor;
25439
+ if (background) {
25440
+ ctx.fillStyle = background;
25441
+ ctx.fillRect(rect.x, rect.y, rect.width, rect.height);
25442
+ }
25443
+ }
24568
25444
  drawButton(ctx, layout.decreaseButton, axis, "decrease", style.buttonColor);
24569
25445
  drawButton(ctx, layout.increaseButton, axis, "increase", style.buttonColor);
24570
- drawThumb(ctx, layout.thumb, style);
24571
- drawTrackBorder(ctx, layout.track, axis, borderColor);
25446
+ if (style.mode === "button") {
25447
+ drawButtonDivider(ctx, layout.increaseButton, axis, borderColor);
25448
+ if (axis === "horizontal") {
25449
+ drawButtonModeFrame(ctx, layout.decreaseButton, borderColor);
25450
+ }
25451
+ } else {
25452
+ const pressedThumb = hover?.pressedScrollbarPart?.axis === axis && hover.pressedScrollbarPart.role === "thumb";
25453
+ const hoveredThumb = hover?.area === `${axis}-scrollbar` && hover.scrollbarRole === "thumb";
25454
+ drawThumb(
25455
+ ctx,
25456
+ layout.thumb,
25457
+ style,
25458
+ pressedThumb ? style.buttonPressedBackgroundColor : hoveredThumb ? style.buttonHoverBackgroundColor : void 0
25459
+ );
25460
+ drawTrackBorder(ctx, layout.track, axis, borderColor);
25461
+ if (axis === "horizontal") {
25462
+ drawButtonModeFrame(ctx, layout.decreaseButton, borderColor);
25463
+ }
25464
+ }
25465
+ }
25466
+ function drawButtonModeFrame(ctx, first, borderColor) {
25467
+ const left = Math.round(first.x) + 0.5;
25468
+ const top = Math.round(first.y) + 0.5;
25469
+ ctx.save();
25470
+ ctx.strokeStyle = borderColor;
25471
+ ctx.lineWidth = 1;
25472
+ ctx.beginPath();
25473
+ ctx.moveTo(left, top);
25474
+ ctx.lineTo(left, Math.round(first.y + first.height) - 0.5);
25475
+ ctx.stroke();
25476
+ ctx.restore();
25477
+ }
25478
+ function drawVerticalAxisFrame(ctx, layout, borderColor, frame) {
25479
+ const first = layout.decreaseButton;
25480
+ const last = layout.increaseButton;
25481
+ const left = Math.round(first.x) + 0.5;
25482
+ const right = Math.round(first.x + first.width) - 0.5;
25483
+ const top = Math.round(frame?.top ?? first.y) + 0.5;
25484
+ const buttonTop = Math.round(first.y) + 0.5;
25485
+ const bottom = Math.round(frame ? frame.top + frame.height : last.y + last.height) - 0.5;
25486
+ if (right <= left || bottom <= top) return;
25487
+ ctx.save();
25488
+ ctx.strokeStyle = borderColor;
25489
+ ctx.lineWidth = 1;
25490
+ ctx.beginPath();
25491
+ ctx.moveTo(left, bottom);
25492
+ ctx.lineTo(left, top);
25493
+ ctx.moveTo(left, buttonTop);
25494
+ ctx.lineTo(right, buttonTop);
25495
+ ctx.stroke();
25496
+ ctx.restore();
25497
+ }
25498
+ function drawButtonDivider(ctx, second, axis, borderColor) {
25499
+ ctx.save();
25500
+ ctx.strokeStyle = borderColor;
25501
+ ctx.lineWidth = 1;
25502
+ ctx.beginPath();
25503
+ if (axis === "horizontal") {
25504
+ const x = Math.round(second.x) + 0.5;
25505
+ ctx.moveTo(x, second.y);
25506
+ ctx.lineTo(x, second.y + second.height);
25507
+ } else {
25508
+ const y = Math.round(second.y) + 0.5;
25509
+ ctx.moveTo(Math.round(second.x) + 0.5, y);
25510
+ ctx.lineTo(Math.round(second.x + second.width) - 0.5, y);
25511
+ }
25512
+ ctx.stroke();
25513
+ ctx.restore();
24572
25514
  }
24573
25515
  function drawTrackBorder(ctx, rect, axis, borderColor) {
24574
25516
  const left = Math.round(rect.x) + 0.5;
@@ -24630,7 +25572,7 @@ function drawVerticalScrollbarFrame(ctx, frame, borderColor) {
24630
25572
  }
24631
25573
  ctx.restore();
24632
25574
  }
24633
- function drawThumb(ctx, rect, style) {
25575
+ function drawThumb(ctx, rect, style, highlightColor) {
24634
25576
  ctx.save();
24635
25577
  ctx.fillStyle = style.thumbColor;
24636
25578
  roundRect2(
@@ -24642,6 +25584,18 @@ function drawThumb(ctx, rect, style) {
24642
25584
  style.thumbRadius ?? rect.height / 2
24643
25585
  );
24644
25586
  ctx.fill();
25587
+ if (highlightColor) {
25588
+ ctx.fillStyle = highlightColor;
25589
+ roundRect2(
25590
+ ctx,
25591
+ rect.x,
25592
+ rect.y,
25593
+ rect.width,
25594
+ rect.height,
25595
+ style.thumbRadius ?? rect.height / 2
25596
+ );
25597
+ ctx.fill();
25598
+ }
24645
25599
  ctx.restore();
24646
25600
  }
24647
25601
  function drawButton(ctx, rect, axis, direction, color) {
@@ -24973,6 +25927,7 @@ function drawListTableHeader(params) {
24973
25927
  const collapsedColumnMarkers = resolveCollapsedHeaderColumnMarkers({
24974
25928
  columns: options.columns,
24975
25929
  visibleColumnKeys: leafColumns.map((column) => column.key),
25930
+ showCollapsedColumnIcon: options.showCollapsedColumnIcon,
24976
25931
  // 系统列是否存在由功能配置决定,不属于用户隐藏的业务列,不能绘制恢复标记。
24977
25932
  excludedColumnKeys: [
24978
25933
  ...groupingColumnKeys ?? [],
@@ -25071,7 +26026,7 @@ function resolveActiveCellOverlayLayout(params) {
25071
26026
  highlightMode: params.highlightMode,
25072
26027
  columnKeys: params.columnKeys ?? [],
25073
26028
  allowColumnRange: selection.rowIndex == null,
25074
- allowRowRange: selection.columnKey === ROW_SELECTOR_COLUMN_KEY,
26029
+ allowRowRange: selection.columnKey === ROW_SELECTOR_COLUMN_KEY || params.rangeHighlight?.selectedRowRanges != null || params.rangeHighlight?.selectedCellRanges != null && params.rangeHighlight.selectedRowRange != null,
25075
26030
  resolveClipRect: (item) => resolveActiveCellSectionClipRect(item, params),
25076
26031
  width: params.width,
25077
26032
  height: params.height
@@ -25162,10 +26117,13 @@ function drawActiveCellOverlay(params) {
25162
26117
  }
25163
26118
  function resolveRangeHighlightLayouts(params) {
25164
26119
  const rowRange = params.rangeHighlight?.selectedRowRange;
26120
+ const rowRanges = params.rangeHighlight?.selectedRowRanges ?? (rowRange ? [rowRange] : []);
25165
26121
  const columnRange = params.rangeHighlight?.selectedColumnRange;
26122
+ const selectedColumnKeys = params.rangeHighlight?.selectedColumnKeys;
25166
26123
  const cellRange = params.rangeHighlight?.selectedCellRange;
25167
- const rowSelectorRange = rowRange && params.allowRowRange;
25168
- if (!cellRange && !rowSelectorRange && (!columnRange || !params.allowColumnRange)) {
26124
+ const cellRanges = params.rangeHighlight?.selectedCellRanges ?? (cellRange ? [cellRange] : []);
26125
+ const rowSelectorRange = rowRanges.length > 0 && params.allowRowRange;
26126
+ if (cellRanges.length === 0 && !rowSelectorRange && (!(columnRange || selectedColumnKeys?.length) || !params.allowColumnRange)) {
25169
26127
  return { rects: [], segments: [] };
25170
26128
  }
25171
26129
  const bodyDescriptors = params.descriptors.filter(
@@ -25174,20 +26132,33 @@ function resolveRangeHighlightLayouts(params) {
25174
26132
  const selected = bodyDescriptors.filter((item) => {
25175
26133
  const itemRowStart = item.rowIndex;
25176
26134
  const itemRowEnd = itemRowStart + Math.max(item.rowSpan ?? 1, 1) - 1;
25177
- const rowSelected = rowRange != null && itemRowEnd >= rowRange.start && itemRowStart <= rowRange.end;
26135
+ const rowSelected = rowRanges.some(
26136
+ (range) => itemRowEnd >= range.start && itemRowStart <= range.end
26137
+ );
25178
26138
  const columnIndex = params.columnKeys.indexOf(item.columnKey);
25179
- const columnSelected = columnRange != null && columnIndex >= columnRange.start && columnIndex <= columnRange.end;
25180
- if (cellRange) {
25181
- return itemRowEnd >= cellRange.startRowIndex && itemRowStart <= cellRange.endRowIndex && columnIndex >= cellRange.startColumnIndex && columnIndex <= cellRange.endColumnIndex;
25182
- }
25183
- if (rowSelectorRange) {
25184
- return rowSelected;
25185
- }
25186
- return params.allowColumnRange && columnSelected;
26139
+ const columnSelected = selectedColumnKeys != null ? selectedColumnKeys.includes(item.columnKey) : columnRange != null && columnIndex >= columnRange.start && columnIndex <= columnRange.end;
26140
+ const cellSelected = cellRanges.some(
26141
+ (range) => itemRowEnd >= range.startRowIndex && itemRowStart <= range.endRowIndex && columnIndex >= range.startColumnIndex && columnIndex <= range.endColumnIndex
26142
+ );
26143
+ return cellSelected || rowSelectorRange && rowSelected || params.allowColumnRange && columnSelected;
25187
26144
  });
25188
26145
  const groups = /* @__PURE__ */ new Map();
26146
+ const selectedKeySet = new Set(selectedColumnKeys ?? []);
25189
26147
  selected.forEach((descriptor) => {
25190
- const groupKey = cellRange || rowSelectorRange ? `${descriptor.fixed ?? "center"}:${descriptor.frozen ? "frozen" : "scroll"}` : `column:${descriptor.fixed ?? "center"}:${descriptor.frozen ? "frozen" : "scroll"}`;
26148
+ const columnIndex = params.columnKeys.indexOf(descriptor.columnKey);
26149
+ let runStart = columnIndex;
26150
+ if (selectedColumnKeys != null) {
26151
+ while (runStart > 0 && selectedKeySet.has(params.columnKeys[runStart - 1])) {
26152
+ runStart--;
26153
+ }
26154
+ }
26155
+ const cellRangeIndex = cellRanges.findIndex(
26156
+ (range) => descriptor.rowIndex >= range.startRowIndex && descriptor.rowIndex <= range.endRowIndex && columnIndex >= range.startColumnIndex && columnIndex <= range.endColumnIndex
26157
+ );
26158
+ const rowRangeIndex = rowRanges.findIndex(
26159
+ (range) => descriptor.rowIndex >= range.start && descriptor.rowIndex <= range.end
26160
+ );
26161
+ 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"}`;
25191
26162
  const current = groups.get(groupKey);
25192
26163
  const rect = current ? {
25193
26164
  x: Math.min(current.rect.x, descriptor.rect.x),
@@ -25222,8 +26193,8 @@ function resolveRangeHighlightLayouts(params) {
25222
26193
  );
25223
26194
  return {
25224
26195
  ...range,
25225
- omitLeft: range.rect.x > minX,
25226
- omitRight: range.rect.x + range.rect.width < maxX
26196
+ omitLeft: selectedColumnKeys == null && range.rect.x > minX,
26197
+ omitRight: selectedColumnKeys == null && range.rect.x + range.rect.width < maxX
25227
26198
  };
25228
26199
  });
25229
26200
  const rangeSegments = resolveRangeBorderSegments(
@@ -25740,7 +26711,8 @@ function executeListTableRenderPipeline(params) {
25740
26711
  headerBackgroundColor: theme.headerBackgroundColor,
25741
26712
  bodyBackgroundColor: theme.backgroundColor
25742
26713
  },
25743
- options.scrollbar
26714
+ options.scrollbar,
26715
+ hover
25744
26716
  );
25745
26717
  drawActiveCellOverlay({
25746
26718
  ctx,
@@ -25793,8 +26765,8 @@ function drawGroupingBar(params) {
25793
26765
  ctx.fillRect(0, 0, width, height);
25794
26766
  ctx.strokeStyle = theme.borderColor;
25795
26767
  ctx.beginPath();
25796
- ctx.moveTo(0, height - 0.5);
25797
- ctx.lineTo(width, height - 0.5);
26768
+ ctx.moveTo(0, height + 0.5);
26769
+ ctx.lineTo(width, height + 0.5);
25798
26770
  ctx.stroke();
25799
26771
  ctx.fillStyle = theme.headerTextColor;
25800
26772
  ctx.font = `${theme.headerFontWeight} ${theme.fontSize}px ${theme.fontFamily}`;
@@ -25886,6 +26858,16 @@ function drawColumnResizeIndicator(params) {
25886
26858
  // src/services/image-clipboard-service.ts
25887
26859
  var MAX_IMAGE_SIDE = 16384;
25888
26860
  var MAX_IMAGE_PIXELS = 16e6;
26861
+ function collectSelectedIndices(ranges, count) {
26862
+ const indices = /* @__PURE__ */ new Set();
26863
+ for (const range of ranges) {
26864
+ for (let index = Math.max(range.start, 0); index <= Math.min(range.end, count - 1); index++) {
26865
+ indices.add(index);
26866
+ if (indices.size > MAX_IMAGE_SIDE) return null;
26867
+ }
26868
+ }
26869
+ return [...indices].sort((left, right) => left - right);
26870
+ }
25889
26871
  function assertTableImageSize(width, height) {
25890
26872
  if (!Number.isFinite(width) || !Number.isFinite(height) || width <= 0 || height <= 0) {
25891
26873
  throw new Error("\u6CA1\u6709\u53EF\u590D\u5236\u7684\u8868\u683C\u533A\u57DF\u3002");
@@ -25896,8 +26878,96 @@ function assertTableImageSize(width, height) {
25896
26878
  }
25897
26879
  function resolveImageSelection(host) {
25898
26880
  const state = host.rangeHighlight;
25899
- if (state.selectedCellRange) return state.selectedCellRange;
25900
26881
  const rowCount = host.getDisplayData().length;
26882
+ const columnCount = host.leafColumns.length;
26883
+ if (state.selectedCellRanges != null) {
26884
+ const rowRanges = state.selectedRowRanges ?? (state.selectedRowRange ? [state.selectedRowRange] : []);
26885
+ if (!state.selectedCellRanges.length && !rowRanges.length) return null;
26886
+ const rowIndices = collectSelectedIndices(
26887
+ [
26888
+ ...state.selectedCellRanges.map((range) => ({
26889
+ start: range.startRowIndex,
26890
+ end: range.endRowIndex
26891
+ })),
26892
+ ...rowRanges
26893
+ ],
26894
+ rowCount
26895
+ );
26896
+ if (rowIndices === null) {
26897
+ return {
26898
+ startRowIndex: 0,
26899
+ endRowIndex: rowCount - 1,
26900
+ startColumnIndex: 0,
26901
+ endColumnIndex: columnCount - 1,
26902
+ tooLarge: true
26903
+ };
26904
+ }
26905
+ const columnIndices = collectSelectedIndices(
26906
+ rowRanges.length ? [{ start: 0, end: columnCount - 1 }] : state.selectedCellRanges.map((range) => ({
26907
+ start: range.startColumnIndex,
26908
+ end: range.endColumnIndex
26909
+ })),
26910
+ columnCount
26911
+ );
26912
+ if (columnIndices === null) {
26913
+ return {
26914
+ startRowIndex: 0,
26915
+ endRowIndex: rowCount - 1,
26916
+ startColumnIndex: 0,
26917
+ endColumnIndex: columnCount - 1,
26918
+ tooLarge: true
26919
+ };
26920
+ }
26921
+ if (!rowIndices.length || !columnIndices.length) return null;
26922
+ return {
26923
+ startRowIndex: rowIndices[0],
26924
+ endRowIndex: rowIndices[rowIndices.length - 1],
26925
+ startColumnIndex: columnIndices[0],
26926
+ endColumnIndex: columnIndices[columnIndices.length - 1],
26927
+ rowIndices,
26928
+ columnIndices,
26929
+ cellRanges: state.selectedCellRanges,
26930
+ rowRanges
26931
+ };
26932
+ }
26933
+ if (state.selectedCellRange) return state.selectedCellRange;
26934
+ if (state.selectedColumnKeys != null) {
26935
+ const columnIndices = host.leafColumns.flatMap(
26936
+ (column, index) => state.selectedColumnKeys?.includes(column.key) ? [index] : []
26937
+ );
26938
+ if (!columnIndices.length || !rowCount) return null;
26939
+ return {
26940
+ startRowIndex: 0,
26941
+ endRowIndex: rowCount - 1,
26942
+ startColumnIndex: columnIndices[0],
26943
+ endColumnIndex: columnIndices[columnIndices.length - 1],
26944
+ columnIndices
26945
+ };
26946
+ }
26947
+ if (state.selectedRowRanges != null) {
26948
+ if (!state.selectedRowRanges.length) return null;
26949
+ const rowIndices = collectSelectedIndices(
26950
+ state.selectedRowRanges,
26951
+ rowCount
26952
+ );
26953
+ if (rowIndices === null) {
26954
+ return {
26955
+ startRowIndex: state.selectedRowRanges[0].start,
26956
+ endRowIndex: state.selectedRowRanges[0].end,
26957
+ startColumnIndex: 0,
26958
+ endColumnIndex: columnCount - 1,
26959
+ tooLarge: true
26960
+ };
26961
+ }
26962
+ if (!rowIndices.length || !columnCount) return null;
26963
+ return {
26964
+ startRowIndex: rowIndices[0],
26965
+ endRowIndex: rowIndices[rowIndices.length - 1],
26966
+ startColumnIndex: 0,
26967
+ endColumnIndex: columnCount - 1,
26968
+ rowIndices
26969
+ };
26970
+ }
25901
26971
  const columnIndex = host.leafColumns.findIndex(
25902
26972
  (column) => column.key === host.selection.columnKey
25903
26973
  );
@@ -25913,7 +26983,7 @@ function resolveImageSelection(host) {
25913
26983
  startRowIndex: state.selectedRowRange.start,
25914
26984
  endRowIndex: state.selectedRowRange.end,
25915
26985
  startColumnIndex: 0,
25916
- endColumnIndex: host.leafColumns.length - 1
26986
+ endColumnIndex: columnCount - 1
25917
26987
  };
25918
26988
  if (host.selection.rowIndex != null && columnIndex >= 0)
25919
26989
  return {
@@ -25945,23 +27015,50 @@ function renderTableCopyImage(host, scope) {
25945
27015
  const range = scope === "selection" ? resolveImageSelection(host) : null;
25946
27016
  if (scope === "selection" && !range)
25947
27017
  throw new Error("\u8BF7\u5148\u9009\u4E2D\u9700\u8981\u590D\u5236\u7684\u5355\u5143\u683C\u533A\u57DF\u3002");
27018
+ if (range?.tooLarge)
27019
+ 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");
25948
27020
  const startRow = range ? Math.max(Math.min(range.startRowIndex, range.endRowIndex), 0) : 0;
25949
27021
  const endRow = range ? Math.max(range.startRowIndex, range.endRowIndex) : current.baseRows.length - 1;
25950
27022
  const startColumn = range ? Math.max(Math.min(range.startColumnIndex, range.endColumnIndex), 0) : 0;
25951
27023
  const endColumn = range ? Math.max(range.startColumnIndex, range.endColumnIndex) : current.leafColumns.length - 1;
25952
- const columns = current.leafColumns.slice(startColumn, endColumn + 1);
27024
+ if (range && ((range.rowIndices?.length ?? endRow - startRow + 1) > MAX_IMAGE_SIDE || (range.columnIndices?.length ?? endColumn - startColumn + 1) > MAX_IMAGE_SIDE)) {
27025
+ 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");
27026
+ }
27027
+ const selectedRowIndices = range?.rowIndices ?? (range ? Array.from(
27028
+ { length: endRow - startRow + 1 },
27029
+ (_, index) => startRow + index
27030
+ ) : null);
27031
+ const selectedColumnIndices = range?.columnIndices ?? (range ? Array.from(
27032
+ { length: endColumn - startColumn + 1 },
27033
+ (_, index) => startColumn + index
27034
+ ) : null);
27035
+ const columns = selectedColumnIndices ? selectedColumnIndices.map((index) => current.leafColumns[index]) : current.leafColumns;
25953
27036
  const width = columns.reduce((total, column) => total + column.width, 0);
25954
- const rowCount = range ? endRow - startRow + 1 : current.baseRows.length;
27037
+ const rowCount = selectedRowIndices?.length ?? current.baseRows.length;
25955
27038
  const dpr = source.ownerDocument.defaultView?.devicePixelRatio || 1;
25956
27039
  const zoom = host.tableZoom || 1;
25957
27040
  assertTableImageSize(
25958
27041
  Math.ceil(width * zoom * dpr),
25959
27042
  Math.ceil(Math.max(rowCount, 1) * current.rowHeight * zoom * dpr)
25960
27043
  );
25961
- const rows = range ? current.displayRows.slice(startRow, endRow + 1) : current.baseRows;
27044
+ const rows = selectedRowIndices ? selectedRowIndices.map((index) => current.displayRows[index]) : current.baseRows;
25962
27045
  const keys = new Set(columns.map((column) => column.key));
25963
27046
  if (!range) state?.groupColumnKeys.forEach((key) => keys.add(key));
25964
27047
  const widths = new Map(columns.map((column) => [column.key, column.width]));
27048
+ const originalColumnIndices = new Map(
27049
+ current.leafColumns.map((column, index) => [column.key, index])
27050
+ );
27051
+ const isCellSelected = (rowIndex, columnKey) => {
27052
+ if (!range?.cellRanges) return true;
27053
+ if (range.rowRanges?.some(
27054
+ (rowRange) => rowIndex >= rowRange.start && rowIndex <= rowRange.end
27055
+ ))
27056
+ return true;
27057
+ const columnIndex = originalColumnIndices.get(columnKey);
27058
+ return columnIndex != null && range.cellRanges.some(
27059
+ (cellRange) => rowIndex >= cellRange.startRowIndex && rowIndex <= cellRange.endRowIndex && columnIndex >= cellRange.startColumnIndex && columnIndex <= cellRange.endColumnIndex
27060
+ );
27061
+ };
25965
27062
  const cloneColumns2 = (items) => items.flatMap((column) => {
25966
27063
  const children = column.children ? cloneColumns2(column.children) : void 0;
25967
27064
  if (children ? !children.length : !keys.has(String(column.key)))
@@ -25974,8 +27071,21 @@ function renderTableCopyImage(host, scope) {
25974
27071
  children,
25975
27072
  fixed: void 0,
25976
27073
  width: widths.get(String(column.key)) ?? column.width,
25977
- onCell: onCell && range ? (value, record, index, context) => onCell(value, record, index + startRow, context) : onCell,
25978
- render: render && range ? (value, record, index, context) => render(value, record, index + startRow, context) : render
27074
+ onCell: onCell && range ? (value, record, index, context) => onCell(
27075
+ value,
27076
+ record,
27077
+ selectedRowIndices?.[index] ?? index + startRow,
27078
+ context
27079
+ ) : onCell,
27080
+ render: range?.cellRanges && !children ? (value, record, index, context) => {
27081
+ const sourceRowIndex = selectedRowIndices?.[index] ?? index + startRow;
27082
+ return isCellSelected(sourceRowIndex, String(column.key)) ? render?.(value, record, sourceRowIndex, context) ?? null : "";
27083
+ } : render && range ? (value, record, index, context) => render(
27084
+ value,
27085
+ record,
27086
+ selectedRowIndices?.[index] ?? index + startRow,
27087
+ context
27088
+ ) : render
25979
27089
  }
25980
27090
  ];
25981
27091
  });
@@ -26064,7 +27174,7 @@ function renderTableCopyImage(host, scope) {
26064
27174
  fixedLeftBoundarySkips: [],
26065
27175
  getNestedTableHighlight: (params) => host.animationController.getNestedTableHighlight({
26066
27176
  ...params,
26067
- rowIndex: params.rowIndex + startRow
27177
+ rowIndex: selectedRowIndices?.[params.rowIndex] ?? params.rowIndex + startRow
26068
27178
  })
26069
27179
  });
26070
27180
  return canvas;
@@ -26514,6 +27624,7 @@ function buildHeaderDataIndexOptions(rows, columns) {
26514
27624
 
26515
27625
  // src/features/overlay/context-menu-builder.ts
26516
27626
  function createListTableContextMenuActions(params) {
27627
+ const getExportColumns = () => params.getExportColumns?.() ?? params.getColumns();
26517
27628
  return {
26518
27629
  onCopyHeaderCell: (columnKey) => {
26519
27630
  params.hideMenu();
@@ -26569,50 +27680,82 @@ function createListTableContextMenuActions(params) {
26569
27680
  onExportJson: () => {
26570
27681
  params.hideMenu();
26571
27682
  params.startExportTask(
26572
- (fileName) => exportAllRowsJsonContent({
27683
+ (fileName, settings) => exportAllRowsJsonContent({
26573
27684
  fileName,
26574
27685
  rows: params.getAllRows(),
26575
- columns: params.getColumns()
27686
+ columns: getExportColumns(),
27687
+ settings
26576
27688
  }),
26577
27689
  "\u6B63\u5728\u5BFC\u51FA JSON\u2026",
26578
- buildTableExportFileName("json")
27690
+ buildTableExportFileName("json"),
27691
+ {
27692
+ columns: getListTableExportColumnChoices(
27693
+ getExportColumns(),
27694
+ params.getExportSortState?.()
27695
+ ),
27696
+ includeSummary: params.getExportSummaryEnabled?.() ?? false
27697
+ }
26579
27698
  );
26580
27699
  },
26581
27700
  onExportCsv: () => {
26582
27701
  params.hideMenu();
26583
27702
  params.startExportTask(
26584
- (fileName) => exportAllRowsCsvContent({
27703
+ (fileName, settings) => exportAllRowsCsvContent({
26585
27704
  fileName,
26586
27705
  rows: params.getAllRows(),
26587
- columns: params.getColumns()
27706
+ columns: getExportColumns(),
27707
+ settings
26588
27708
  }),
26589
27709
  "\u6B63\u5728\u5BFC\u51FA CSV\u2026",
26590
- buildTableExportFileName("csv")
27710
+ buildTableExportFileName("csv"),
27711
+ {
27712
+ columns: getListTableExportColumnChoices(
27713
+ getExportColumns(),
27714
+ params.getExportSortState?.()
27715
+ ),
27716
+ includeSummary: params.getExportSummaryEnabled?.() ?? false
27717
+ }
26591
27718
  );
26592
27719
  },
26593
27720
  onExportTxt: () => {
26594
27721
  params.hideMenu();
26595
27722
  params.startExportTask(
26596
- (fileName) => exportAllRowsTxtContent({
27723
+ (fileName, settings) => exportAllRowsTxtContent({
26597
27724
  fileName,
26598
27725
  rows: params.getAllRows(),
26599
- columns: params.getColumns()
27726
+ columns: getExportColumns(),
27727
+ settings
26600
27728
  }),
26601
27729
  "\u6B63\u5728\u5BFC\u51FA TXT\u2026",
26602
- buildTableExportFileName("txt")
27730
+ buildTableExportFileName("txt"),
27731
+ {
27732
+ columns: getListTableExportColumnChoices(
27733
+ getExportColumns(),
27734
+ params.getExportSortState?.()
27735
+ ),
27736
+ includeSummary: params.getExportSummaryEnabled?.() ?? false
27737
+ }
26603
27738
  );
26604
27739
  },
26605
27740
  onExportXlsx: () => {
26606
27741
  params.hideMenu();
26607
27742
  params.startExportTask(
26608
- (fileName) => exportAllRowsXlsxContent({
27743
+ (fileName, settings) => exportAllRowsXlsxContent({
26609
27744
  includeSummary: params.getExportSummaryEnabled?.() ?? false,
26610
27745
  fileName,
26611
27746
  rows: params.getAllRows(),
26612
- columns: params.getColumns()
27747
+ columns: getExportColumns(),
27748
+ settings
26613
27749
  }),
26614
27750
  "\u6B63\u5728\u5BFC\u51FA XLSX\u2026",
26615
- buildTableExportFileName("xlsx")
27751
+ buildTableExportFileName("xlsx"),
27752
+ {
27753
+ columns: getListTableExportColumnChoices(
27754
+ getExportColumns(),
27755
+ params.getExportSortState?.()
27756
+ ),
27757
+ includeSummary: params.getExportSummaryEnabled?.() ?? false
27758
+ }
26616
27759
  );
26617
27760
  },
26618
27761
  onFreezeRow: params.onFreezeRow,
@@ -26767,6 +27910,7 @@ function buildListTableContextMenuConfig(params) {
26767
27910
  const enabledFeatures = resolveEnabledContextMenuFeatures(contextMenu);
26768
27911
  const isTableSettingsArea = isHeaderArea || isExpandedArea;
26769
27912
  const isBodyArea = bodyRowIndex != null;
27913
+ const canResetTable = isTableSettingsArea || isBodyArea;
26770
27914
  const isSelectionColumn = columnKey === ROW_SELECTION_COLUMN_KEY || columnKey === ROW_SELECTOR_COLUMN_KEY;
26771
27915
  const enableCopy = enabledFeatures.has("copy");
26772
27916
  const enableChart = enabledFeatures.has("chart");
@@ -26885,19 +28029,19 @@ function buildListTableContextMenuConfig(params) {
26885
28029
  onUnfreezeRow: enableFreeze && isBodyArea && frozenRowPosition != null ? () => {
26886
28030
  actions.onUnfreezeRow(bodyRowIndex);
26887
28031
  } : void 0,
26888
- onResetFilter: enableReset && isTableSettingsArea ? actions.onResetFilter : void 0,
26889
- onResetSort: enableReset && isTableSettingsArea ? actions.onResetSort : void 0,
28032
+ onResetFilter: enableReset && canResetTable ? actions.onResetFilter : void 0,
28033
+ onResetSort: enableReset && canResetTable ? actions.onResetSort : void 0,
26890
28034
  onSortAscend: canSortCurrentColumn && actions.onSortAscend ? () => actions.onSortAscend?.(columnKey) : void 0,
26891
28035
  onSortDescend: canSortCurrentColumn && actions.onSortDescend ? () => actions.onSortDescend?.(columnKey) : void 0,
26892
28036
  onClearSort: canSortCurrentColumn && actions.onClearSort ? () => actions.onClearSort?.(columnKey) : void 0,
26893
28037
  onFixLeft: enableFreeze && isHeaderArea && !isSelectionColumn && actions.onFixLeft ? () => actions.onFixLeft?.(columnKey) : void 0,
26894
28038
  onFixRight: enableFreeze && isHeaderArea && !isSelectionColumn && actions.onFixRight ? () => actions.onFixRight?.(columnKey) : void 0,
26895
28039
  onUnfix: enableFreeze && isHeaderArea && !isSelectionColumn && actions.onUnfix ? () => actions.onUnfix?.(columnKey) : void 0,
26896
- onResetHeader: enableReset && isTableSettingsArea ? actions.onResetHeader : void 0,
26897
- onResetGrouping: enableReset && isTableSettingsArea ? actions.onResetGrouping : void 0,
26898
- onResetSummary: enableReset && isTableSettingsArea ? actions.onResetSummary : void 0,
26899
- onResetAlignment: enableReset && isTableSettingsArea ? actions.onResetAlignment : void 0,
26900
- onResetDataFormat: enableReset && isTableSettingsArea ? actions.onResetDataFormat : void 0,
28040
+ onResetHeader: enableReset && canResetTable ? actions.onResetHeader : void 0,
28041
+ onResetGrouping: enableReset && canResetTable ? actions.onResetGrouping : void 0,
28042
+ onResetSummary: enableReset && canResetTable ? actions.onResetSummary : void 0,
28043
+ onResetAlignment: enableReset && canResetTable ? actions.onResetAlignment : void 0,
28044
+ onResetDataFormat: enableReset && canResetTable ? actions.onResetDataFormat : void 0,
26901
28045
  onAutoFitCurrentColumn: enableColumnWidth && isHeaderArea && !isSelectionColumn && actions.onAutoFitCurrentColumn ? () => actions.onAutoFitCurrentColumn?.(columnKey) : void 0,
26902
28046
  onAutoFitAllColumns: enableColumnWidth && isTableSettingsArea && !isSelectionColumn && actions.onAutoFitAllColumns ? actions.onAutoFitAllColumns : void 0,
26903
28047
  onAutoFitFillContainer: enableColumnWidth && isTableSettingsArea && !isSelectionColumn && actions.onAutoFitFillContainer ? actions.onAutoFitFillContainer : void 0,
@@ -27138,6 +28282,8 @@ function showHeaderContextMenu(params) {
27138
28282
  onChangeSummaryShowTypePrefix,
27139
28283
  onChangeColumnSettingsCache,
27140
28284
  onChangeCachePreference,
28285
+ onChangeScrollbar,
28286
+ onResetScrollbar,
27141
28287
  summaryVisible,
27142
28288
  getSelectionRange,
27143
28289
  onCopySelection,
@@ -27151,6 +28297,40 @@ function showHeaderContextMenu(params) {
27151
28297
  const isExpandedArea = result.area === "expanded-row" || result.columnKey === "__expanded_row__";
27152
28298
  const isSelectionColumn = result.columnKey === ROW_SELECTION_COLUMN_KEY || result.columnKey === ROW_SELECTOR_COLUMN_KEY;
27153
28299
  const isChartCorner = isHeaderArea && result.columnKey === ROW_SELECTOR_COLUMN_KEY && resolveEnabledContextMenuFeatures(options.contextMenu).has("chart");
28300
+ if (result.area === "horizontal-scrollbar" || result.area === "vertical-scrollbar") {
28301
+ if (options.contextMenu === false) {
28302
+ headerContextMenu.hide();
28303
+ return false;
28304
+ }
28305
+ event.preventDefault();
28306
+ filterPanel.hide();
28307
+ fixedPopup.hide();
28308
+ hideBodyTooltip();
28309
+ headerContextMenu.show({
28310
+ x: event.clientX,
28311
+ y: event.clientY,
28312
+ scrollbarArea: true,
28313
+ scrollbarRowCount: getRows().length,
28314
+ scrollbarColumnCount: params.scrollbarColumnCount ?? getColumns().length,
28315
+ onScrollToPosition: params.onScrollToPosition,
28316
+ onResetFilter,
28317
+ onResetSort,
28318
+ onResetHeader,
28319
+ onResetGrouping,
28320
+ onResetSummary,
28321
+ onResetAlignment,
28322
+ onResetDataFormat,
28323
+ scrollbar: options.scrollbar,
28324
+ onChangeScrollbar,
28325
+ onResetScrollbar,
28326
+ columnSettingsCacheAvailable: Boolean(options.storageKey),
28327
+ cacheColumnSettings: options.cacheColumnSettings,
28328
+ cachePreferences: options.cachePreferences,
28329
+ onChangeCachePreference,
28330
+ onClose
28331
+ });
28332
+ return true;
28333
+ }
27154
28334
  if (!isHeaderArea && !isBodyArea && !groupingBarArea && !isExpandedArea || !result.columnKey && !groupingBarArea && !isExpandedArea || isHeaderArea && isSelectionColumn && !isChartCorner) {
27155
28335
  headerContextMenu.hide();
27156
28336
  return false;
@@ -27172,8 +28352,8 @@ function showHeaderContextMenu(params) {
27172
28352
  onSelectBodyRow?.(bodyRowIndex, contextColumnKey);
27173
28353
  }
27174
28354
  const frozenRowPosition = bodyRowIndex == null ? null : getFrozenRowPosition2(bodyRowIndex);
27175
- headerContextMenu.show(
27176
- buildListTableContextMenuConfig({
28355
+ headerContextMenu.show({
28356
+ ...buildListTableContextMenuConfig({
27177
28357
  storageKey: options.storageKey,
27178
28358
  cacheColumnSettings: options.cacheColumnSettings,
27179
28359
  cachePreferences: options.cachePreferences,
@@ -27205,9 +28385,11 @@ function showHeaderContextMenu(params) {
27205
28385
  columnCount: getColumns().length,
27206
28386
  actions: createListTableContextMenuActions({
27207
28387
  getExportSummaryEnabled: params.getExportSummaryEnabled,
28388
+ getExportSortState: params.getExportSortState,
27208
28389
  getRows,
27209
28390
  getAllRows,
27210
28391
  getColumns,
28392
+ getExportColumns: params.getExportColumns,
27211
28393
  getHeaderTree,
27212
28394
  hideMenu: () => {
27213
28395
  headerContextMenu.hide();
@@ -27259,8 +28441,15 @@ function showHeaderContextMenu(params) {
27259
28441
  onOpenSearch,
27260
28442
  onCopyCustomSelection
27261
28443
  })
27262
- })
27263
- );
28444
+ }),
28445
+ sortState: params.getExportSortState?.() ?? [],
28446
+ scrollbarRowCount: getRows().length,
28447
+ scrollbarColumnCount: params.scrollbarColumnCount ?? getColumns().length,
28448
+ onScrollToPosition: params.onScrollToPosition,
28449
+ scrollbar: options.scrollbar,
28450
+ onChangeScrollbar,
28451
+ onResetScrollbar
28452
+ });
27264
28453
  return true;
27265
28454
  }
27266
28455
  function resolveContextMenuDensity(rowHeight) {
@@ -27288,7 +28477,9 @@ function createListTableChartData(params) {
27288
28477
  rowIndex,
27289
28478
  selectionRange,
27290
28479
  selectedRowRange,
27291
- selectedColumnRange
28480
+ selectedColumnRange,
28481
+ selectedColumnKeys,
28482
+ getRowKey: getRowKey2
27292
28483
  } = params;
27293
28484
  const startRow = Math.max(
27294
28485
  selectionRange?.startRowIndex ?? selectedRowRange?.start ?? Math.max((rowIndex ?? 0) - 10, 0),
@@ -27301,7 +28492,7 @@ function createListTableChartData(params) {
27301
28492
  const selectedColumns = selectionRange ? columns.slice(
27302
28493
  Math.max(selectionRange.startColumnIndex, 0),
27303
28494
  selectionRange.endColumnIndex + 1
27304
- ) : selectedRowRange ? columns : selectedColumnRange ? columns.slice(
28495
+ ) : selectedRowRange ? columns : selectedColumnKeys != null ? columns.filter((column) => selectedColumnKeys.includes(column.key)) : selectedColumnRange ? columns.slice(
27305
28496
  Math.max(selectedColumnRange.start, 0),
27306
28497
  selectedColumnRange.end + 1
27307
28498
  ) : columns.filter((column) => column.key === columnKey);
@@ -27340,7 +28531,8 @@ function createListTableChartData(params) {
27340
28531
  const labels = sampledRows.map(
27341
28532
  (_, index) => labelIndex >= 0 ? values[labelIndex]?.[index] || String(startRow + index + 1) : String(startRow + index + 1)
27342
28533
  );
27343
- return { labels, series: numericColumns };
28534
+ const keys = getRowKey2 ? sampledRows.map((record, index) => getRowKey2(record, startRow + index)) : void 0;
28535
+ return { labels, ...keys ? { keys } : {}, series: numericColumns };
27344
28536
  }
27345
28537
  function parseChartNumber(value) {
27346
28538
  const normalized = value.trim().replace(/[,,\s¥¥$]/g, "");
@@ -27353,6 +28545,9 @@ function parseChartNumber(value) {
27353
28545
  // src/features/tooltip/tooltip-feature.ts
27354
28546
  function getHeaderActionTooltipText(result, sortState) {
27355
28547
  const area = result.area;
28548
+ if (result.debugNodeType === "header-edit-icon" || result.debugNodeType === "nested-table-header-edit-icon") {
28549
+ return "\u5F53\u524D\u5217\u53EF\u7F16\u8F91";
28550
+ }
27356
28551
  if (area !== "sort-trigger" && area !== "filter-trigger" && area !== "fixed-trigger") {
27357
28552
  return null;
27358
28553
  }
@@ -28397,6 +29592,8 @@ var ListTableEventController = class {
28397
29592
  __publicField(this, "cellSelectionAutoScrollVelocity", { x: 0, y: 0 });
28398
29593
  __publicField(this, "cellSelectionAutoScrollTargetVelocity", { x: 0, y: 0 });
28399
29594
  __publicField(this, "continuousScrollTimer", null);
29595
+ __publicField(this, "scrollbarButtonFeedbackTimer", null);
29596
+ __publicField(this, "scrollbarPartFeedbackTimer", null);
28400
29597
  __publicField(this, "pendingEditableCellRowSelectionTimer", null);
28401
29598
  __publicField(this, "outsideClickDocument", null);
28402
29599
  __publicField(this, "outsideClickStarted", null);
@@ -28407,7 +29604,13 @@ var ListTableEventController = class {
28407
29604
  __publicField(this, "handleDocumentClick", (event) => {
28408
29605
  const startedOutside = this.outsideClickStarted ?? !this.isEventTargetInsideTable(event.target);
28409
29606
  this.outsideClickStarted = null;
28410
- if (!startedOutside || !this.host.selection.textSelection) return;
29607
+ if (!startedOutside) return;
29608
+ if (this.host.options.clearSelectionOnClick?.outside) {
29609
+ this.clearTableHighlight();
29610
+ this.host.render();
29611
+ return;
29612
+ }
29613
+ if (!this.host.selection.textSelection) return;
28411
29614
  this.host.clearBodySelection();
28412
29615
  this.host.render();
28413
29616
  });
@@ -28447,11 +29650,19 @@ var ListTableEventController = class {
28447
29650
  rowCount: this.host.getDisplayData().length,
28448
29651
  columnKeys: this.host.leafColumns.filter((column) => !isSystemColumnKey(column.key)).map((column) => column.key)
28449
29652
  };
28450
- const target = direction ? resolveAdjacentCellSelection({ ...navigationParams, direction }) : resolveTabCellSelection({
29653
+ const target = direction ? resolveAdjacentCellSelection({
28451
29654
  ...navigationParams,
28452
- reverse: event.shiftKey
29655
+ direction,
29656
+ verticalWrap: this.host.options.keyboardNavigation?.verticalWrap
29657
+ }) : resolveTabCellSelection({
29658
+ ...navigationParams,
29659
+ reverse: event.shiftKey,
29660
+ tabWrap: this.host.options.keyboardNavigation?.tabWrap
28453
29661
  });
28454
29662
  if (!target) {
29663
+ if (event.key === "Tab" && this.host.options.keyboardNavigation?.tabWrap === false) {
29664
+ event.preventDefault();
29665
+ }
28455
29666
  return;
28456
29667
  }
28457
29668
  event.preventDefault();
@@ -28563,6 +29774,9 @@ var ListTableEventController = class {
28563
29774
  result.area === "resize-handle" ? "col-resize" : result.cursor ?? ""
28564
29775
  );
28565
29776
  const nextHover = {
29777
+ scrollbarRole: result.scrollbarRole,
29778
+ pressedScrollbarButton: this.host.hover.pressedScrollbarButton,
29779
+ pressedScrollbarPart: this.host.hover.pressedScrollbarPart,
28566
29780
  rowIndex: result.rowIndex ?? null,
28567
29781
  columnKey: result.columnKey ?? null,
28568
29782
  contentIndex: result.contentIndex,
@@ -28572,7 +29786,7 @@ var ListTableEventController = class {
28572
29786
  nestedColumnIndex: result.nestedColumnIndex,
28573
29787
  area: result.area
28574
29788
  };
28575
- 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;
29789
+ 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;
28576
29790
  const hitChanged = didHitResultChange(
28577
29791
  this.host.debugManager.getSnapshot()?.hitTest,
28578
29792
  result
@@ -28615,7 +29829,8 @@ var ListTableEventController = class {
28615
29829
  zoom: this.host.tableZoom
28616
29830
  });
28617
29831
  const result = this.host.resolvePointerHit(position.x, position.y);
28618
- if (this.startTextSelection(result, position.x, position.y)) {
29832
+ const modifyingSelection = event.shiftKey || event.ctrlKey || event.metaKey;
29833
+ if (!modifyingSelection && this.startTextSelection(result, position.x, position.y)) {
28619
29834
  return;
28620
29835
  }
28621
29836
  if (this.host.selection.textSelection) {
@@ -28647,6 +29862,28 @@ var ListTableEventController = class {
28647
29862
  if ((result.area === "body" || result.area === "body-content") && result.columnKey === ROW_SELECTOR_COLUMN_KEY && typeof result.rowIndex === "number") {
28648
29863
  event.preventDefault();
28649
29864
  this.clearNestedTableHighlight();
29865
+ if (modifyingSelection) {
29866
+ const mode = event.ctrlKey || event.metaKey ? isRowInHighlight(this.host.rangeHighlight, result.rowIndex) ? "remove" : "add" : "extend";
29867
+ this.cellSelectionDrag = {
29868
+ kind: "row",
29869
+ startRowIndex: mode === "extend" ? this.host.rangeHighlight.selectedRowAnchorIndex ?? result.rowIndex : result.rowIndex,
29870
+ startColumnIndex: -1,
29871
+ pointerStartRowIndex: result.rowIndex,
29872
+ pointerStartColumnIndex: -1,
29873
+ mode,
29874
+ baseRangeHighlight: this.host.rangeHighlight
29875
+ };
29876
+ this.cellSelectionDragged = false;
29877
+ this.cellSelectionPointer = {
29878
+ clientX: event.clientX,
29879
+ clientY: event.clientY
29880
+ };
29881
+ this.host.canvasHost.bindWindowDragEvents(
29882
+ this.handleWindowMouseMove,
29883
+ this.handleWindowMouseUp
29884
+ );
29885
+ return;
29886
+ }
28650
29887
  this.host.selection = {
28651
29888
  rowIndex: result.rowIndex,
28652
29889
  columnKey: ROW_SELECTOR_COLUMN_KEY
@@ -28657,7 +29894,11 @@ var ListTableEventController = class {
28657
29894
  this.cellSelectionDrag = {
28658
29895
  kind: "row",
28659
29896
  startRowIndex: result.rowIndex,
28660
- startColumnIndex: -1
29897
+ startColumnIndex: -1,
29898
+ pointerStartRowIndex: result.rowIndex,
29899
+ pointerStartColumnIndex: -1,
29900
+ mode: "replace",
29901
+ baseRangeHighlight: this.host.rangeHighlight
28661
29902
  };
28662
29903
  this.cellSelectionDragged = false;
28663
29904
  this.cellSelectionPointer = {
@@ -28671,20 +29912,41 @@ var ListTableEventController = class {
28671
29912
  this.host.render(result);
28672
29913
  return;
28673
29914
  }
28674
- if (!this.host.options.rowDrag?.enabled && (result.area === "body" || result.area === "body-content") && typeof result.rowIndex === "number" && result.columnKey) {
29915
+ if ((!this.host.options.rowDrag?.enabled || modifyingSelection) && (result.area === "body" || result.area === "body-content") && typeof result.rowIndex === "number" && result.columnKey) {
28675
29916
  const columnIndex = this.host.leafColumns.findIndex(
28676
29917
  (column) => column.key === result.columnKey
28677
29918
  );
28678
29919
  if (columnIndex >= 0) {
28679
29920
  const nestedSelectionCleared = this.clearNestedTableHighlight();
28680
- this.host.rangeHighlight = {
28681
- ...this.host.rangeHighlight,
28682
- selectedCellRange: null
28683
- };
29921
+ if (!modifyingSelection) {
29922
+ this.host.rangeHighlight = {
29923
+ ...this.host.rangeHighlight,
29924
+ selectedCellRange: null,
29925
+ selectedCellRanges: null
29926
+ };
29927
+ }
29928
+ const ctrlSelection = event.ctrlKey || event.metaKey;
29929
+ const mode = ctrlSelection ? isCellInHighlight(
29930
+ this.host.rangeHighlight,
29931
+ result.rowIndex,
29932
+ columnIndex
29933
+ ) ? "remove" : "add" : event.shiftKey ? "extend" : "replace";
29934
+ const cellAnchor = this.host.rangeHighlight.selectedCellAnchor;
29935
+ const anchorColumnIndex = cellAnchor ? this.host.leafColumns.findIndex(
29936
+ (column) => column.key === cellAnchor.columnKey
29937
+ ) : -1;
29938
+ const previousColumnIndex = this.host.leafColumns.findIndex(
29939
+ (column) => column.key === this.host.selection.columnKey
29940
+ );
28684
29941
  this.cellSelectionDrag = {
28685
29942
  kind: "cell",
28686
- startRowIndex: result.rowIndex,
28687
- startColumnIndex: columnIndex
29943
+ startRowIndex: mode === "extend" ? cellAnchor?.rowIndex ?? this.host.selection.rowIndex ?? result.rowIndex : result.rowIndex,
29944
+ startColumnIndex: mode === "extend" ? anchorColumnIndex >= 0 ? anchorColumnIndex : previousColumnIndex >= 0 ? previousColumnIndex : columnIndex : columnIndex,
29945
+ pointerStartRowIndex: result.rowIndex,
29946
+ pointerStartColumnIndex: columnIndex,
29947
+ mode,
29948
+ baseRangeHighlight: this.host.rangeHighlight,
29949
+ preserveRowSelection: this.host.selection.columnKey === ROW_SELECTOR_COLUMN_KEY || this.host.rangeHighlight.selectedRowRanges != null
28688
29950
  };
28689
29951
  this.cellSelectionDragged = false;
28690
29952
  this.cellSelectionPointer = {
@@ -28698,6 +29960,7 @@ var ListTableEventController = class {
28698
29960
  if (nestedSelectionCleared) {
28699
29961
  this.host.render(result);
28700
29962
  }
29963
+ if (modifyingSelection) return;
28701
29964
  }
28702
29965
  }
28703
29966
  if (result.area === "resize-handle" && result.columnKey) {
@@ -28788,11 +30051,39 @@ var ListTableEventController = class {
28788
30051
  result.scrollbarRole === "thumb" ? "grabbing" : "pointer"
28789
30052
  );
28790
30053
  if (result.scrollbarRole === "decrease-button") {
30054
+ if (this.scrollbarButtonFeedbackTimer != null) {
30055
+ window.clearTimeout(this.scrollbarButtonFeedbackTimer);
30056
+ this.scrollbarButtonFeedbackTimer = null;
30057
+ }
30058
+ this.host.hover = {
30059
+ ...this.host.hover,
30060
+ area: result.area,
30061
+ scrollbarRole: result.scrollbarRole,
30062
+ pressedScrollbarButton: {
30063
+ axis: result.area === "horizontal-scrollbar" ? "horizontal" : "vertical",
30064
+ role: result.scrollbarRole
30065
+ }
30066
+ };
30067
+ this.host.scheduleRender();
28791
30068
  this.stepScrollbar(result.area, -1, viewport);
28792
30069
  this.startContinuousScroll(result.area, -1, viewport);
28793
30070
  return;
28794
30071
  }
28795
30072
  if (result.scrollbarRole === "increase-button") {
30073
+ if (this.scrollbarButtonFeedbackTimer != null) {
30074
+ window.clearTimeout(this.scrollbarButtonFeedbackTimer);
30075
+ this.scrollbarButtonFeedbackTimer = null;
30076
+ }
30077
+ this.host.hover = {
30078
+ ...this.host.hover,
30079
+ area: result.area,
30080
+ scrollbarRole: result.scrollbarRole,
30081
+ pressedScrollbarButton: {
30082
+ axis: result.area === "horizontal-scrollbar" ? "horizontal" : "vertical",
30083
+ role: result.scrollbarRole
30084
+ }
30085
+ };
30086
+ this.host.scheduleRender();
28796
30087
  this.stepScrollbar(result.area, 1, viewport);
28797
30088
  this.startContinuousScroll(result.area, 1, viewport);
28798
30089
  return;
@@ -28804,9 +30095,15 @@ var ListTableEventController = class {
28804
30095
  }
28805
30096
  const { thumb, track } = horizontalScrollbar;
28806
30097
  if (result.scrollbarRole === "track") {
30098
+ this.flashScrollbarPart("horizontal", "track");
28807
30099
  this.jumpScrollbar("horizontal", position.x, track, thumb, viewport);
28808
30100
  return;
28809
30101
  }
30102
+ this.host.hover = {
30103
+ ...this.host.hover,
30104
+ pressedScrollbarPart: { axis: "horizontal", role: "thumb" }
30105
+ };
30106
+ this.host.scheduleRender();
28810
30107
  this.host.draggingScrollbar = {
28811
30108
  axis: "horizontal",
28812
30109
  pointerOffset: position.x - thumb.x,
@@ -28822,9 +30119,15 @@ var ListTableEventController = class {
28822
30119
  }
28823
30120
  const { thumb, track } = verticalScrollbar;
28824
30121
  if (result.scrollbarRole === "track") {
30122
+ this.flashScrollbarPart("vertical", "track");
28825
30123
  this.jumpScrollbar("vertical", position.y, track, thumb, viewport);
28826
30124
  return;
28827
30125
  }
30126
+ this.host.hover = {
30127
+ ...this.host.hover,
30128
+ pressedScrollbarPart: { axis: "vertical", role: "thumb" }
30129
+ };
30130
+ this.host.scheduleRender();
28828
30131
  this.host.draggingScrollbar = {
28829
30132
  axis: "vertical",
28830
30133
  pointerOffset: position.y - thumb.y,
@@ -28937,15 +30240,47 @@ var ListTableEventController = class {
28937
30240
  getRows: () => this.host.getDisplayData(),
28938
30241
  getAllRows: () => this.host.options.dataSource,
28939
30242
  getExportSummaryEnabled: () => Boolean(this.host.currentRenderSnapshot?.hasSummary),
30243
+ getExportSortState: () => this.host.getSortState(),
30244
+ getExportColumns: () => getListTableExportCandidateColumns(this.host.options.columns),
28940
30245
  onCopyImage: (scope) => this.copyImage(scope, event),
30246
+ scrollbarColumnCount: this.host.leafColumns.filter(
30247
+ (column) => !isSystemColumnKey(column.key)
30248
+ ).length,
30249
+ onScrollToPosition: (row, column) => {
30250
+ const columns = this.host.leafColumns.filter(
30251
+ (item) => !isSystemColumnKey(item.key)
30252
+ );
30253
+ const targetColumn = column == null ? null : columns[column - 1];
30254
+ const colIndex = targetColumn ? this.host.leafColumns.findIndex(
30255
+ (item) => item.key === targetColumn.key
30256
+ ) : void 0;
30257
+ this.host.scrollTo({
30258
+ rowIndex: row == null ? void 0 : row - 1,
30259
+ colIndex,
30260
+ behavior: "smooth"
30261
+ });
30262
+ },
30263
+ onChangeScrollbar: (value) => {
30264
+ this.host.options.scrollbar = value;
30265
+ this.host.options.ui?.onScrollbarChange?.(value);
30266
+ this.host.persistColumnConfig();
30267
+ this.host.render();
30268
+ },
30269
+ onResetScrollbar: () => {
30270
+ this.host.options.scrollbar = void 0;
30271
+ this.host.persistColumnConfig();
30272
+ this.host.render();
30273
+ },
28941
30274
  onCreateChart: () => createListTableChartData({
28942
30275
  rows: this.host.getDisplayData(),
28943
30276
  columns: this.host.leafColumns,
28944
30277
  columnKey: contextResult.columnKey ?? "",
28945
- rowIndex: typeof contextResult.rowIndex === "number" ? contextResult.rowIndex : null,
30278
+ rowIndex: this.host.selection.rowIndex ?? (typeof contextResult.rowIndex === "number" ? contextResult.rowIndex : null),
28946
30279
  selectionRange: this.host.rangeHighlight.selectedCellRange,
28947
30280
  selectedRowRange: this.host.rangeHighlight.selectedRowRange,
28948
- selectedColumnRange: this.host.rangeHighlight.selectedColumnRange
30281
+ selectedColumnRange: this.host.rangeHighlight.selectedColumnRange,
30282
+ selectedColumnKeys: this.host.rangeHighlight.selectedColumnKeys,
30283
+ getRowKey: this.host.options.rowKey != null ? (record, index) => getRowKey(this.host.options, record, index) : void 0
28949
30284
  }),
28950
30285
  onExportImage: (scope) => this.copyImage(scope, event, true),
28951
30286
  canCopySelectionImage: resolveImageSelection(this.host) != null,
@@ -28969,8 +30304,8 @@ var ListTableEventController = class {
28969
30304
  getColumns: () => this.host.leafColumns,
28970
30305
  getHeaderTree: () => this.host.headerTree,
28971
30306
  getFrozenRowPosition: (rowIndex) => this.host.getFrozenRowPosition(rowIndex),
28972
- startExportTask: (task, text, defaultFileName) => {
28973
- this.host.startExportTask(task, text, defaultFileName);
30307
+ startExportTask: (task, text, defaultFileName, dialog) => {
30308
+ this.host.startExportTask(task, text, defaultFileName, dialog);
28974
30309
  },
28975
30310
  contentDownload: this.host.resolveContentDownloadAction(hitContent),
28976
30311
  onSelectHeader: (columnKey, rect) => {
@@ -28979,18 +30314,22 @@ var ListTableEventController = class {
28979
30314
  this.host.selectHeader(columnKey, rect);
28980
30315
  },
28981
30316
  onSelectBodyRow: (rowIndex, columnKey) => {
28982
- const selectedCellRange = this.host.rangeHighlight.selectedCellRange;
28983
30317
  const columnIndex = this.host.leafColumns.findIndex(
28984
30318
  (column) => column.key === columnKey
28985
30319
  );
30320
+ const isSelectedCell = columnIndex >= 0 && isCellInHighlight(this.host.rangeHighlight, rowIndex, columnIndex);
28986
30321
  this.host.selection = {
28987
30322
  rowIndex,
28988
30323
  columnKey
28989
30324
  };
28990
30325
  this.host.clearSelectedHeader();
28991
- if (!selectedCellRange || rowIndex < selectedCellRange.startRowIndex || rowIndex > selectedCellRange.endRowIndex || columnIndex < selectedCellRange.startColumnIndex || columnIndex > selectedCellRange.endColumnIndex) {
30326
+ if (!isSelectedCell && !isRowInHighlight(this.host.rangeHighlight, rowIndex)) {
28992
30327
  this.host.rangeHighlight = resolveNextRowHighlightForContextMenu({
28993
- current: this.host.rangeHighlight,
30328
+ current: {
30329
+ ...this.host.rangeHighlight,
30330
+ selectedCellRange: null,
30331
+ selectedCellRanges: null
30332
+ },
28994
30333
  rowIndex
28995
30334
  });
28996
30335
  }
@@ -29286,6 +30625,14 @@ var ListTableEventController = class {
29286
30625
  const result = event.shiftKey && rawResult.area === "column-collapse-trigger" ? this.host.resolvePointerHit(position.x, position.y, {
29287
30626
  excludeKinds: ["column-collapse-trigger"]
29288
30627
  }) : rawResult;
30628
+ const toggleColumn = event.ctrlKey || event.metaKey;
30629
+ const preserveAnimatedSortSelection = result.area === "sort-trigger" && this.host.leafColumns.some(
30630
+ (column) => column.key === result.columnKey && typeof column.sorter === "object" && column.sorter.animation === true
30631
+ );
30632
+ const previousCell = {
30633
+ rowIndex: this.host.selection.rowIndex,
30634
+ columnKey: this.host.selection.columnKey
30635
+ };
29289
30636
  if (result.area === "grouping-remove" && result.columnKey) {
29290
30637
  this.host.removeGroupingColumn?.(result.columnKey);
29291
30638
  this.host.suppressNextClick = true;
@@ -29308,6 +30655,11 @@ var ListTableEventController = class {
29308
30655
  this.host.suppressNextClick = false;
29309
30656
  return;
29310
30657
  }
30658
+ if (result.area === "empty" && this.host.options.clearSelectionOnClick?.blankCanvas) {
30659
+ this.clearTableHighlight();
30660
+ this.host.render(result);
30661
+ return;
30662
+ }
29311
30663
  const isNestedTableHit = result.contentKey === "nested-table";
29312
30664
  if (isNestedTableHit) {
29313
30665
  this.clearParentTableHighlight();
@@ -29339,6 +30691,7 @@ var ListTableEventController = class {
29339
30691
  selectedRowRange: rowCount > 0 ? { start: 0, end: rowCount - 1 } : null,
29340
30692
  selectedColumnAnchorKey: this.host.leafColumns[0]?.key ?? null,
29341
30693
  selectedColumnRange: columnRangeEnd >= 0 ? { start: 0, end: columnRangeEnd } : null,
30694
+ selectedColumnKeys: null,
29342
30695
  selectedCellRange: rowCount > 0 && columnCount > 0 ? {
29343
30696
  startRowIndex: 0,
29344
30697
  endRowIndex: rowCount - 1,
@@ -29363,6 +30716,7 @@ var ListTableEventController = class {
29363
30716
  selectedRowRange: rowCount > 0 ? { start: 0, end: rowCount - 1 } : null,
29364
30717
  selectedColumnAnchorKey: this.host.leafColumns[0]?.key ?? null,
29365
30718
  selectedColumnRange: columnCount > 0 ? { start: 0, end: columnCount - 1 } : null,
30719
+ selectedColumnKeys: null,
29366
30720
  selectedCellRange: rowCount > 0 && columnCount > 0 ? {
29367
30721
  startRowIndex: 0,
29368
30722
  endRowIndex: rowCount - 1,
@@ -29383,7 +30737,48 @@ var ListTableEventController = class {
29383
30737
  if ((result.area === "body" || result.area === "body-content") && result.columnKey === ROW_SELECTOR_COLUMN_KEY && typeof result.rowIndex === "number") {
29384
30738
  this.host.clearSelectedHeader();
29385
30739
  this.host.summarySelected = false;
29386
- this.selectRowRange(result.rowIndex, result.rowIndex);
30740
+ this.host.rangeHighlight = resolveRowHighlightForSelectorClick({
30741
+ current: this.host.rangeHighlight,
30742
+ rowIndex: result.rowIndex,
30743
+ shiftKey: event.shiftKey,
30744
+ ctrlKey: toggleColumn
30745
+ });
30746
+ if (!isRowInHighlight(this.host.rangeHighlight, result.rowIndex)) {
30747
+ const hasSelectedRows = this.host.rangeHighlight.selectedRowRanges?.length || this.host.rangeHighlight.selectedRowRange;
30748
+ this.host.selection = {
30749
+ rowIndex: null,
30750
+ columnKey: hasSelectedRows ? ROW_SELECTOR_COLUMN_KEY : null
30751
+ };
30752
+ }
30753
+ this.host.render(result);
30754
+ this.host.updateTooltip(result);
30755
+ return;
30756
+ }
30757
+ if ((event.shiftKey || toggleColumn) && (result.area === "body" || result.area === "body-content") && typeof result.rowIndex === "number" && result.columnKey && !isSystemColumnKey(result.columnKey)) {
30758
+ const columnIndex = this.host.leafColumns.findIndex(
30759
+ (column) => column.key === result.columnKey
30760
+ );
30761
+ this.host.clearSelectedHeader();
30762
+ this.host.rangeHighlight = resolveCellHighlightForClick({
30763
+ current: this.host.rangeHighlight,
30764
+ rowIndex: result.rowIndex,
30765
+ columnKey: result.columnKey,
30766
+ leafColumnKeys: this.host.leafColumns.map((column) => column.key),
30767
+ previousCell: previousCell.rowIndex != null && previousCell.columnKey ? {
30768
+ rowIndex: previousCell.rowIndex,
30769
+ columnKey: previousCell.columnKey
30770
+ } : null,
30771
+ preserveRowSelection: previousCell.columnKey === ROW_SELECTOR_COLUMN_KEY || this.host.rangeHighlight.selectedRowRanges != null,
30772
+ shiftKey: event.shiftKey,
30773
+ ctrlKey: toggleColumn
30774
+ });
30775
+ if (columnIndex >= 0 && !isCellInHighlight(
30776
+ this.host.rangeHighlight,
30777
+ result.rowIndex,
30778
+ columnIndex
30779
+ )) {
30780
+ this.host.selection = { rowIndex: null, columnKey: null };
30781
+ }
29387
30782
  this.host.render(result);
29388
30783
  this.host.updateTooltip(result);
29389
30784
  return;
@@ -29395,13 +30790,20 @@ var ListTableEventController = class {
29395
30790
  }
29396
30791
  }
29397
30792
  this.host.summarySelected = result.area === "summary";
29398
- 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) {
30793
+ 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) {
29399
30794
  this.host.clearBodySelection();
29400
- this.host.resetRangeHighlightForHeaderSelection(event.shiftKey);
29401
- this.host.selectHeader(
29402
- result.columnKey,
29403
- result.area === "header" ? result.rect : null
30795
+ this.host.resetRangeHighlightForHeaderSelection(
30796
+ event.shiftKey,
30797
+ toggleColumn
29404
30798
  );
30799
+ if (toggleColumn) {
30800
+ this.host.clearSelectedHeader();
30801
+ } else {
30802
+ this.host.selectHeader(
30803
+ result.columnKey,
30804
+ result.area === "header" ? result.rect : null
30805
+ );
30806
+ }
29405
30807
  }
29406
30808
  if (result.area === "summary") {
29407
30809
  if (!result.columnKey) {
@@ -29419,7 +30821,7 @@ var ListTableEventController = class {
29419
30821
  this.host.updateTooltip(result);
29420
30822
  return;
29421
30823
  }
29422
- 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");
30824
+ 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");
29423
30825
  if ((result.area === "body" || result.area === "body-content") && typeof result.rowIndex === "number" && result.columnKey) {
29424
30826
  this.host.clearSelectedHeader();
29425
30827
  this.host.updateRangeHighlightForBodyInteraction({
@@ -29428,15 +30830,26 @@ var ListTableEventController = class {
29428
30830
  shiftKey: event.shiftKey,
29429
30831
  rowSpan: result.rowSpan
29430
30832
  });
30833
+ if (!isSystemColumnKey(result.columnKey)) {
30834
+ this.host.rangeHighlight = resolveCellHighlightForClick({
30835
+ current: this.host.rangeHighlight,
30836
+ rowIndex: result.rowIndex,
30837
+ columnKey: result.columnKey,
30838
+ leafColumnKeys: this.host.leafColumns.map((column) => column.key),
30839
+ shiftKey: false,
30840
+ ctrlKey: false
30841
+ });
30842
+ }
29431
30843
  }
29432
- if ((result.area === "header" || result.area === "sort-trigger" || result.area === "filter-trigger" || result.area === "fixed-trigger" || isHeaderRangeGesture) && result.columnKey) {
30844
+ if ((result.area === "header" || result.area === "sort-trigger" && !preserveAnimatedSortSelection || result.area === "filter-trigger" || result.area === "fixed-trigger" || isHeaderRangeGesture) && result.columnKey) {
29433
30845
  this.host.rangeHighlight = {
29434
30846
  ...this.host.rangeHighlight,
29435
30847
  selectedCellRange: null
29436
30848
  };
29437
30849
  this.host.updateRangeHighlightForHeaderClick(
29438
30850
  result.columnKey,
29439
- event.shiftKey
30851
+ event.shiftKey,
30852
+ toggleColumn
29440
30853
  );
29441
30854
  }
29442
30855
  if (isHeaderRangeGesture) {
@@ -29772,12 +31185,24 @@ var ListTableEventController = class {
29772
31185
  } else if (!activated && this.host.draggingReorder.columnKey) {
29773
31186
  const columnKey = this.host.draggingReorder.columnKey;
29774
31187
  this.host.clearBodySelection();
29775
- this.host.resetRangeHighlightForHeaderSelection(event.shiftKey);
29776
- this.host.selectHeader(
31188
+ const toggleColumn = event.ctrlKey || event.metaKey;
31189
+ this.host.resetRangeHighlightForHeaderSelection(
31190
+ event.shiftKey,
31191
+ toggleColumn
31192
+ );
31193
+ if (toggleColumn) {
31194
+ this.host.clearSelectedHeader();
31195
+ } else {
31196
+ this.host.selectHeader(
31197
+ columnKey,
31198
+ this.host.draggingReorder.selectedHeaderRect
31199
+ );
31200
+ }
31201
+ this.host.updateRangeHighlightForHeaderClick(
29777
31202
  columnKey,
29778
- this.host.draggingReorder.selectedHeaderRect
31203
+ event.shiftKey,
31204
+ toggleColumn
29779
31205
  );
29780
- this.host.updateRangeHighlightForHeaderClick(columnKey, event.shiftKey);
29781
31206
  this.host.suppressNextClick = true;
29782
31207
  this.host.render();
29783
31208
  }
@@ -30004,6 +31429,14 @@ var ListTableEventController = class {
30004
31429
  this.host.render();
30005
31430
  }
30006
31431
  destroy() {
31432
+ if (this.scrollbarButtonFeedbackTimer != null) {
31433
+ window.clearTimeout(this.scrollbarButtonFeedbackTimer);
31434
+ this.scrollbarButtonFeedbackTimer = null;
31435
+ }
31436
+ if (this.scrollbarPartFeedbackTimer != null) {
31437
+ window.clearTimeout(this.scrollbarPartFeedbackTimer);
31438
+ this.scrollbarPartFeedbackTimer = null;
31439
+ }
30007
31440
  this.cancelPendingEditableCellRowSelection();
30008
31441
  this.stopContinuousScroll();
30009
31442
  this.stopTextSelectionAutoScroll();
@@ -30133,10 +31566,11 @@ var ListTableEventController = class {
30133
31566
  getRows: () => resolveNestedTableDisplayRows(content, getNestedState()),
30134
31567
  getAllRows: () => content.dataSource,
30135
31568
  getExportSummaryEnabled: () => getNestedState()?.summaryVisible === true,
31569
+ getExportSortState: () => getNestedState()?.sortState ?? [],
30136
31570
  getColumns: () => columns,
30137
31571
  getHeaderTree: () => headerTree,
30138
31572
  hideMenu,
30139
- startExportTask: (task, text, defaultFileName) => this.host.startExportTask(task, text, defaultFileName),
31573
+ startExportTask: (task, text, defaultFileName, dialog) => this.host.startExportTask(task, text, defaultFileName, dialog),
30140
31574
  onFreezeRow: () => {
30141
31575
  },
30142
31576
  onUnfreezeRow: () => {
@@ -30607,6 +32041,11 @@ var ListTableEventController = class {
30607
32041
  searchMatch: this.host.rangeHighlight.searchMatch
30608
32042
  };
30609
32043
  }
32044
+ /** 清除可视选区,但保留查找结果与业务行选择数据。 */
32045
+ clearTableHighlight() {
32046
+ this.clearParentTableHighlight();
32047
+ this.clearNestedTableHighlight();
32048
+ }
30610
32049
  /** 父表格进入选区交互时清理全部子表格选区。 */
30611
32050
  clearNestedTableHighlight() {
30612
32051
  this.activeNestedTableHit = null;
@@ -30614,9 +32053,31 @@ var ListTableEventController = class {
30614
32053
  }
30615
32054
  startContinuousScroll(area, direction, viewport) {
30616
32055
  this.stopContinuousScroll();
32056
+ const axis = area === "horizontal-scrollbar" ? "horizontal" : "vertical";
32057
+ const style = resolveCanvasScrollbarAxis(this.host.options.scrollbar, axis);
32058
+ const startedAt = Date.now();
32059
+ let previousAt = startedAt;
30617
32060
  this.continuousScrollTimer = window.setInterval(() => {
30618
- this.stepScrollbar(area, direction, viewport);
30619
- }, 80);
32061
+ const now2 = Date.now();
32062
+ const elapsed = Math.max(now2 - previousAt, 0);
32063
+ previousAt = now2;
32064
+ const distance = resolveCanvasScrollbarHoldSpeed(
32065
+ now2 - startedAt,
32066
+ style.maxScrollSpeed
32067
+ ) * elapsed * direction / 1e3;
32068
+ const base = this.host.animationController.getWheelScrollTarget() ?? this.host.scroll;
32069
+ const next = {
32070
+ ...base,
32071
+ left: axis === "horizontal" ? Math.min(
32072
+ Math.max(base.left + distance, 0),
32073
+ viewport.maxScrollLeft
32074
+ ) : base.left,
32075
+ top: axis === "vertical" ? Math.min(Math.max(base.top + distance, 0), viewport.maxScrollTop) : base.top
32076
+ };
32077
+ if (next.left !== base.left || next.top !== base.top) {
32078
+ this.host.animationController.startWheelScrollSmoothing(next);
32079
+ }
32080
+ }, 32);
30620
32081
  this.host.canvasHost.bindWindowDragEvents(
30621
32082
  this.handleWindowMouseMove,
30622
32083
  this.handleWindowMouseUp
@@ -30629,22 +32090,19 @@ var ListTableEventController = class {
30629
32090
  }
30630
32091
  }
30631
32092
  stepScrollbar(area, direction, viewport) {
32093
+ const base = this.host.animationController.getWheelScrollTarget() ?? this.host.scroll;
30632
32094
  const nextScroll = stepScrollbarScroll({
30633
- current: this.host.scroll,
32095
+ current: base,
30634
32096
  area,
30635
32097
  direction,
30636
32098
  viewport,
30637
32099
  rowHeight: this.host.currentRenderSnapshot?.rowHeight ?? DEFAULT_ROW_HEIGHT,
30638
32100
  horizontalStep: HORIZONTAL_SCROLL_STEP
30639
32101
  });
30640
- if (!this.host.applyUserScroll(nextScroll)) {
32102
+ if (nextScroll.left === base.left && nextScroll.top === base.top) {
30641
32103
  return;
30642
32104
  }
30643
- this.host.markScrollbarActive();
30644
- this.host.scheduleRender();
30645
- if (area === "vertical-scrollbar") {
30646
- this.host.maybeLoadNextScrollPage();
30647
- }
32105
+ this.host.animationController.startWheelScrollSmoothing(nextScroll);
30648
32106
  }
30649
32107
  jumpScrollbar(axis, pointerValue, track, thumb, viewport) {
30650
32108
  const nextScroll = jumpScrollbarScroll({
@@ -30665,6 +32123,22 @@ var ListTableEventController = class {
30665
32123
  }
30666
32124
  }
30667
32125
  finishWindowDragCleanup(options) {
32126
+ if (this.host.hover.pressedScrollbarButton) {
32127
+ this.scrollbarButtonFeedbackTimer = window.setTimeout(() => {
32128
+ this.scrollbarButtonFeedbackTimer = null;
32129
+ this.host.hover = {
32130
+ ...this.host.hover,
32131
+ pressedScrollbarButton: void 0
32132
+ };
32133
+ this.host.scheduleRender();
32134
+ }, 140);
32135
+ }
32136
+ if (this.host.hover.pressedScrollbarPart?.role === "thumb") {
32137
+ this.host.hover = {
32138
+ ...this.host.hover,
32139
+ pressedScrollbarPart: void 0
32140
+ };
32141
+ }
30668
32142
  this.host.draggingScrollbar = null;
30669
32143
  this.host.draggingResize = null;
30670
32144
  this.host.draggingReorder = null;
@@ -30678,6 +32152,24 @@ var ListTableEventController = class {
30678
32152
  this.host.markScrollbarActive();
30679
32153
  this.host.render();
30680
32154
  }
32155
+ flashScrollbarPart(axis, role) {
32156
+ if (this.scrollbarPartFeedbackTimer != null) {
32157
+ window.clearTimeout(this.scrollbarPartFeedbackTimer);
32158
+ }
32159
+ this.host.hover = {
32160
+ ...this.host.hover,
32161
+ pressedScrollbarPart: { axis, role }
32162
+ };
32163
+ this.host.scheduleRender();
32164
+ this.scrollbarPartFeedbackTimer = window.setTimeout(() => {
32165
+ this.scrollbarPartFeedbackTimer = null;
32166
+ this.host.hover = {
32167
+ ...this.host.hover,
32168
+ pressedScrollbarPart: void 0
32169
+ };
32170
+ this.host.scheduleRender();
32171
+ }, 140);
32172
+ }
30681
32173
  startTextSelection(result, pointerX, pointerY) {
30682
32174
  if (result.area !== "body-content" && result.area !== "header" && result.area !== "summary" || !result.columnKey || !result.textSelectionText || !result.textSelectionFont || !result.textSelectionFragmentKey || !result.rect) {
30683
32175
  return false;
@@ -31025,16 +32517,46 @@ var ListTableEventController = class {
31025
32517
  return false;
31026
32518
  }
31027
32519
  if (drag.kind === "row") {
32520
+ if (drag.mode !== "replace" && !this.cellSelectionDragged && result.rowIndex === drag.pointerStartRowIndex || drag.lastRowIndex === result.rowIndex) {
32521
+ return false;
32522
+ }
31028
32523
  const nextRange2 = {
31029
32524
  start: Math.min(drag.startRowIndex, result.rowIndex),
31030
32525
  end: Math.max(drag.startRowIndex, result.rowIndex)
31031
32526
  };
31032
32527
  const currentRange2 = this.host.rangeHighlight.selectedRowRange;
31033
- if (currentRange2?.start === nextRange2.start && currentRange2.end === nextRange2.end) {
32528
+ if (drag.mode === "replace" && currentRange2?.start === nextRange2.start && currentRange2.end === nextRange2.end) {
31034
32529
  return false;
31035
32530
  }
31036
- this.cellSelectionDragged = this.cellSelectionDragged || result.rowIndex !== drag.startRowIndex;
31037
- this.selectRowRange(drag.startRowIndex, result.rowIndex);
32531
+ this.cellSelectionDragged = this.cellSelectionDragged || result.rowIndex !== drag.pointerStartRowIndex;
32532
+ drag.lastRowIndex = result.rowIndex;
32533
+ if (drag.mode === "replace") {
32534
+ this.selectRowRange(drag.startRowIndex, result.rowIndex);
32535
+ } else if (drag.mode === "extend") {
32536
+ this.host.rangeHighlight = resolveRowHighlightForSelectorClick({
32537
+ current: {
32538
+ ...drag.baseRangeHighlight,
32539
+ selectedRowAnchorIndex: drag.startRowIndex
32540
+ },
32541
+ rowIndex: result.rowIndex,
32542
+ shiftKey: true,
32543
+ ctrlKey: false
32544
+ });
32545
+ } else {
32546
+ this.host.rangeHighlight = resolveRowHighlightForDrag({
32547
+ current: drag.baseRangeHighlight,
32548
+ startRowIndex: drag.startRowIndex,
32549
+ endRowIndex: result.rowIndex,
32550
+ remove: drag.mode === "remove"
32551
+ });
32552
+ }
32553
+ if (drag.mode !== "replace") {
32554
+ this.host.clearSelectedHeader();
32555
+ this.host.selection = {
32556
+ rowIndex: drag.mode === "remove" ? null : result.rowIndex,
32557
+ columnKey: ROW_SELECTOR_COLUMN_KEY
32558
+ };
32559
+ }
31038
32560
  if (options?.scheduleRender !== false) {
31039
32561
  this.host.scheduleRender(result);
31040
32562
  }
@@ -31046,7 +32568,7 @@ var ListTableEventController = class {
31046
32568
  if (columnIndex < 0) {
31047
32569
  return false;
31048
32570
  }
31049
- if (result.rowIndex === drag.startRowIndex && columnIndex === drag.startColumnIndex) {
32571
+ if (!this.cellSelectionDragged && result.rowIndex === drag.pointerStartRowIndex && columnIndex === drag.pointerStartColumnIndex || drag.lastRowIndex === result.rowIndex && drag.lastColumnIndex === columnIndex) {
31050
32572
  return false;
31051
32573
  }
31052
32574
  const nextRange = {
@@ -31056,14 +32578,53 @@ var ListTableEventController = class {
31056
32578
  endColumnIndex: Math.max(drag.startColumnIndex, columnIndex)
31057
32579
  };
31058
32580
  const currentRange = this.host.rangeHighlight.selectedCellRange;
31059
- if (currentRange?.startRowIndex === nextRange.startRowIndex && currentRange.endRowIndex === nextRange.endRowIndex && currentRange.startColumnIndex === nextRange.startColumnIndex && currentRange.endColumnIndex === nextRange.endColumnIndex) {
32581
+ if (drag.mode === "replace" && currentRange?.startRowIndex === nextRange.startRowIndex && currentRange.endRowIndex === nextRange.endRowIndex && currentRange.startColumnIndex === nextRange.startColumnIndex && currentRange.endColumnIndex === nextRange.endColumnIndex) {
31060
32582
  return false;
31061
32583
  }
31062
32584
  this.cellSelectionDragged = true;
31063
- this.host.rangeHighlight = {
31064
- ...this.host.rangeHighlight,
31065
- selectedCellRange: nextRange
31066
- };
32585
+ drag.lastRowIndex = result.rowIndex;
32586
+ drag.lastColumnIndex = columnIndex;
32587
+ if (drag.mode === "replace") {
32588
+ this.host.rangeHighlight = {
32589
+ ...this.host.rangeHighlight,
32590
+ selectedCellRange: nextRange,
32591
+ selectedCellRanges: null,
32592
+ selectedCellAnchor: {
32593
+ rowIndex: drag.startRowIndex,
32594
+ columnKey: this.host.leafColumns[drag.startColumnIndex]?.key ?? result.columnKey
32595
+ }
32596
+ };
32597
+ } else if (drag.mode === "extend") {
32598
+ this.host.rangeHighlight = resolveCellHighlightForClick({
32599
+ current: {
32600
+ ...drag.baseRangeHighlight,
32601
+ selectedCellAnchor: {
32602
+ rowIndex: drag.startRowIndex,
32603
+ columnKey: this.host.leafColumns[drag.startColumnIndex]?.key ?? result.columnKey
32604
+ }
32605
+ },
32606
+ rowIndex: result.rowIndex,
32607
+ columnKey: result.columnKey,
32608
+ leafColumnKeys: this.host.leafColumns.map((column) => column.key),
32609
+ shiftKey: true,
32610
+ ctrlKey: false
32611
+ });
32612
+ } else {
32613
+ this.host.rangeHighlight = resolveCellHighlightForDrag({
32614
+ current: drag.baseRangeHighlight,
32615
+ startRowIndex: drag.startRowIndex,
32616
+ endRowIndex: result.rowIndex,
32617
+ startColumnIndex: drag.startColumnIndex,
32618
+ endColumnIndex: columnIndex,
32619
+ columnKey: this.host.leafColumns[drag.startColumnIndex]?.key ?? result.columnKey,
32620
+ remove: drag.mode === "remove",
32621
+ preserveRowSelection: drag.preserveRowSelection === true
32622
+ });
32623
+ }
32624
+ if (drag.mode !== "replace") {
32625
+ this.host.clearSelectedHeader();
32626
+ this.host.selection = drag.mode === "remove" ? { rowIndex: null, columnKey: null } : { rowIndex: result.rowIndex, columnKey: result.columnKey };
32627
+ }
31067
32628
  if (options?.scheduleRender !== false) {
31068
32629
  this.host.scheduleRender(result);
31069
32630
  }
@@ -31147,9 +32708,13 @@ var ListTableEventController = class {
31147
32708
  start: Math.min(startRowIndex, endRowIndex),
31148
32709
  end: Math.max(startRowIndex, endRowIndex)
31149
32710
  },
32711
+ selectedRowRanges: null,
31150
32712
  selectedColumnAnchorKey: null,
31151
32713
  selectedColumnRange: null,
31152
- selectedCellRange: null
32714
+ selectedColumnKeys: null,
32715
+ selectedCellRange: null,
32716
+ selectedCellRanges: null,
32717
+ selectedCellAnchor: null
31153
32718
  };
31154
32719
  }
31155
32720
  };
@@ -31468,6 +33033,7 @@ function createListTableEventHost(core) {
31468
33033
  // 可见行在百万行窗口化后只包含当前视口;事件层需要完整的查询结果,
31469
33034
  // 否则自动列宽等基于全局 dataRowIndex 的逻辑会在深滚动时丢失采样行。
31470
33035
  getDisplayData: () => core.queryController.getDisplayData(),
33036
+ getSortState: () => core.storeFacade.sortState,
31471
33037
  getSummaryValueMap: () => core.summaryController.getSummaryValueMap(),
31472
33038
  getSummaryValuesAsync: (snapshot) => core.summaryController.getSummaryValuesAsync(snapshot),
31473
33039
  scrollTo: (options) => core.scrollTo(options),
@@ -31485,11 +33051,12 @@ function createListTableEventHost(core) {
31485
33051
  clearBodySelection: () => core.selectionController.clearBodySelection(),
31486
33052
  clearSelectedHeader: () => core.selectionController.clearSelectedHeader(),
31487
33053
  selectHeader: (columnKey, rect) => core.selectionController.selectHeader(columnKey, rect),
31488
- resetRangeHighlightForHeaderSelection: (shiftKey) => core.selectionController.resetRangeHighlightForHeaderSelection(shiftKey),
33054
+ resetRangeHighlightForHeaderSelection: (shiftKey, ctrlKey) => core.selectionController.resetRangeHighlightForHeaderSelection(shiftKey, ctrlKey),
31489
33055
  updateRangeHighlightForBodyInteraction: (params) => core.selectionController.updateRangeHighlightForBodyInteraction(params),
31490
- updateRangeHighlightForHeaderClick: (columnKey, shiftKey) => core.selectionController.updateRangeHighlightForHeaderClick(
33056
+ updateRangeHighlightForHeaderClick: (columnKey, shiftKey, ctrlKey) => core.selectionController.updateRangeHighlightForHeaderClick(
31491
33057
  columnKey,
31492
- shiftKey
33058
+ shiftKey,
33059
+ ctrlKey
31493
33060
  ),
31494
33061
  updateRangeHighlightForHeaderContextMenu: (columnKey) => core.selectionController.updateRangeHighlightForHeaderContextMenu(
31495
33062
  columnKey
@@ -31532,7 +33099,7 @@ function createListTableEventHost(core) {
31532
33099
  return snapshot?.columnSegments ?? core.columnSegments;
31533
33100
  },
31534
33101
  persistColumnConfig: () => core.persistenceController.persistColumnConfig(),
31535
- startExportTask: (task, text, defaultFileName) => {
33102
+ startExportTask: (task, text, defaultFileName, dialog) => {
31536
33103
  const configured = core.options.exportOptions?.fileName?.trim();
31537
33104
  const extension = defaultFileName?.match(/\.(xlsx|csv|txt|json)$/i)?.[0];
31538
33105
  const fileName = configured && extension ? configured.replace(/\.(xlsx|csv|txt|json)$/i, "") + extension : defaultFileName;
@@ -31540,7 +33107,9 @@ function createListTableEventHost(core) {
31540
33107
  task,
31541
33108
  text,
31542
33109
  void 0,
31543
- fileName
33110
+ fileName,
33111
+ void 0,
33112
+ dialog
31544
33113
  );
31545
33114
  },
31546
33115
  resolveContentDownloadAction: (content) => core.contentActionController.resolveContentDownloadAction(
@@ -31635,6 +33204,7 @@ var ListTableHeaderActionController = class {
31635
33204
  if (this.options.isEditing() || this.options.getLeafColumns().find((column) => column.key === columnKey)?.allowSort === false) {
31636
33205
  return;
31637
33206
  }
33207
+ this.options.captureSortAnimation?.(columnKey);
31638
33208
  this.options.prepareLocalQuery("sort");
31639
33209
  this.options.setSortState(
31640
33210
  toggleSortingState(
@@ -31652,6 +33222,7 @@ var ListTableHeaderActionController = class {
31652
33222
  if (this.options.isEditing() || this.options.getLeafColumns().find((column) => column.key === columnKey)?.allowSort === false) {
31653
33223
  return;
31654
33224
  }
33225
+ this.options.captureSortAnimation?.(columnKey);
31655
33226
  this.options.prepareLocalQuery("sort");
31656
33227
  this.options.setSortState(
31657
33228
  setSortingOrder(
@@ -31822,6 +33393,7 @@ var ListTableHeaderActionController = class {
31822
33393
  if (this.options.isEditing()) {
31823
33394
  return;
31824
33395
  }
33396
+ this.options.captureSortAnimation?.();
31825
33397
  this.options.prepareLocalQuery("sort");
31826
33398
  const next = resolveResetSortAction(this.options.getFilterState());
31827
33399
  this.options.setSortState(next.sortState);
@@ -31859,6 +33431,7 @@ var ListTableHeaderActionController = class {
31859
33431
  if (!resolved) {
31860
33432
  return;
31861
33433
  }
33434
+ this.options.overlayController.preserveHeaderContextMenuForColumnVisibility();
31862
33435
  this.options.setSelectedHeaderState(
31863
33436
  resolved.selectedHeaderKey,
31864
33437
  resolved.selectedHeaderRect
@@ -31883,6 +33456,9 @@ var ListTableHeaderActionController = class {
31883
33456
  buildHeaderSettingsItems(columns)
31884
33457
  );
31885
33458
  this.options.requestRender();
33459
+ if (draft.key && draft.sortOrder !== void 0 && (this.options.getSortState().find((entry) => entry.key === draft.key)?.order ?? null) !== draft.sortOrder) {
33460
+ this.setSortOrder(draft.key, draft.sortOrder);
33461
+ }
31886
33462
  return null;
31887
33463
  }
31888
33464
  deleteHeaderColumn(columnKey) {
@@ -31974,6 +33550,7 @@ var ListTableOverlayController = class {
31974
33550
  __publicField(this, "componentPortal");
31975
33551
  __publicField(this, "getContentZoom");
31976
33552
  __publicField(this, "runtime");
33553
+ __publicField(this, "preserveColumnVisibilityMenu", false);
31977
33554
  __publicField(this, "tooltipAdapter");
31978
33555
  __publicField(this, "filterPanelAdapter");
31979
33556
  __publicField(this, "fixedPopupAdapter");
@@ -32111,6 +33688,7 @@ var ListTableOverlayController = class {
32111
33688
  return this.componentPortal.fixedPopup.isVisible();
32112
33689
  }
32113
33690
  showHeaderContextMenu(config) {
33691
+ this.preserveColumnVisibilityMenu = false;
32114
33692
  this.runtime.headerContextMenuVisible = true;
32115
33693
  this.runtime.headerContextMenuConfig = config;
32116
33694
  const options = this.getOptions();
@@ -32120,6 +33698,7 @@ var ListTableOverlayController = class {
32120
33698
  }
32121
33699
  }
32122
33700
  hideHeaderContextMenu() {
33701
+ this.preserveColumnVisibilityMenu = false;
32123
33702
  this.runtime.headerContextMenuVisible = false;
32124
33703
  this.runtime.headerContextMenuConfig = null;
32125
33704
  this.getOptions().ui?.onHeaderContextMenuRequest?.(null);
@@ -32131,6 +33710,15 @@ var ListTableOverlayController = class {
32131
33710
  }
32132
33711
  return this.componentPortal.headerContextMenu.isVisible();
32133
33712
  }
33713
+ /** 连续修改列显隐时,保留菜单直到用户显式关闭。 */
33714
+ preserveHeaderContextMenuForColumnVisibility() {
33715
+ if (this.runtime.headerContextMenuVisible) {
33716
+ this.preserveColumnVisibilityMenu = true;
33717
+ }
33718
+ }
33719
+ shouldPreserveHeaderContextMenuOnOptionsUpdate() {
33720
+ return this.runtime.headerContextMenuVisible && this.preserveColumnVisibilityMenu;
33721
+ }
32134
33722
  updateHeaderContextMenuColumns(columns) {
32135
33723
  const options = this.getOptions();
32136
33724
  if (options.ui?.onHeaderContextMenuRequest && this.runtime.headerContextMenuConfig) {
@@ -32351,7 +33939,7 @@ function applyStoredColumnConfig(columns, storedColumns, cacheColumnSettings = t
32351
33939
  applyToColumns(columns);
32352
33940
  return columnWidthsChanged;
32353
33941
  }
32354
- function buildStoredColumnConfig(columns, sortState, filterState, zoom, summaryRowHeight, density, cacheColumnSettings = true, cachePreferences) {
33942
+ function buildStoredColumnConfig(columns, sortState, filterState, zoom, summaryRowHeight, density, cacheColumnSettings = true, cachePreferences, scrollbar) {
32355
33943
  const result = {};
32356
33944
  const columnOrder = [];
32357
33945
  const collectColumns = (cols, parentKey) => {
@@ -32408,6 +33996,7 @@ function buildStoredColumnConfig(columns, sortState, filterState, zoom, summaryR
32408
33996
  {
32409
33997
  columns: result,
32410
33998
  cacheColumnSettings,
33999
+ ...scrollbar ? { scrollbar } : null,
32411
34000
  ...cacheColumnSettings ? { columnOrder } : null,
32412
34001
  sort: sortState.map((item) => ({ ...item })),
32413
34002
  filters: filterState,
@@ -32447,6 +34036,7 @@ function filterStoredColumnConfig(stored, preferences = {}) {
32447
34036
  if (preferences.summary === false) delete result.summaryRowHeight;
32448
34037
  if (preferences.density === false) delete result.density;
32449
34038
  if (preferences.zoom === false) delete result.zoom;
34039
+ if (preferences.scrollbar === false) delete result.scrollbar;
32450
34040
  return result;
32451
34041
  }
32452
34042
 
@@ -32461,7 +34051,8 @@ function persistListTableColumnConfig(params) {
32461
34051
  summaryRowHeight,
32462
34052
  density,
32463
34053
  cacheColumnSettings,
32464
- cachePreferences
34054
+ cachePreferences,
34055
+ scrollbar
32465
34056
  } = params;
32466
34057
  if (!storageKey) {
32467
34058
  return;
@@ -32477,7 +34068,8 @@ function persistListTableColumnConfig(params) {
32477
34068
  summaryRowHeight,
32478
34069
  density,
32479
34070
  cacheColumnSettings,
32480
- cachePreferences
34071
+ cachePreferences,
34072
+ scrollbar
32481
34073
  )
32482
34074
  );
32483
34075
  }
@@ -32546,6 +34138,7 @@ async function loadListTableStoredConfig(params) {
32546
34138
  stored.columnOrder
32547
34139
  );
32548
34140
  return {
34141
+ scrollbar: stored.scrollbar,
32549
34142
  cacheColumnSettings,
32550
34143
  cachePreferences,
32551
34144
  sortState: cachePreferences.sort !== false && Array.isArray(stored.sort) ? stored.sort.filter(
@@ -32630,6 +34223,10 @@ var ListTablePersistenceController = class {
32630
34223
  this.options.canvasHost.setContentZoom(nextZoom);
32631
34224
  }
32632
34225
  let currentOptions = this.options.getOptions();
34226
+ if (stored.scrollbar) {
34227
+ currentOptions = { ...currentOptions, scrollbar: stored.scrollbar };
34228
+ currentOptions.ui?.onScrollbarChange?.(stored.scrollbar);
34229
+ }
32633
34230
  if (stored.cachePreferences) {
32634
34231
  currentOptions = {
32635
34232
  ...currentOptions,
@@ -32674,10 +34271,16 @@ var ListTablePersistenceController = class {
32674
34271
  }
32675
34272
  }
32676
34273
  this.options.refreshRuntimeColumns();
34274
+ currentOptions.ui?.onColumnSettingsChange?.(
34275
+ cloneColumns(currentOptions.columns)
34276
+ );
32677
34277
  }
32678
34278
  persistColumnConfig() {
32679
34279
  this.invalidate();
32680
34280
  const tableOptions = this.options.getOptions();
34281
+ tableOptions.ui?.onColumnSettingsChange?.(
34282
+ cloneColumns(tableOptions.columns)
34283
+ );
32681
34284
  this.restoredStorageKey = tableOptions.storageKey;
32682
34285
  this.hasRestoredPreferences = true;
32683
34286
  this.pendingWrite = Promise.resolve(
@@ -32685,6 +34288,7 @@ var ListTablePersistenceController = class {
32685
34288
  storageKey: tableOptions.storageKey,
32686
34289
  cacheColumnSettings: tableOptions.cacheColumnSettings,
32687
34290
  cachePreferences: tableOptions.cachePreferences,
34291
+ scrollbar: tableOptions.scrollbar,
32688
34292
  columns: tableOptions.columns,
32689
34293
  sortState: this.options.getSortState(),
32690
34294
  filterState: this.options.getFilterState(),
@@ -32919,6 +34523,7 @@ var ListTableQueryController = class {
32919
34523
  this.invalidateDataCache();
32920
34524
  this.options.hideLoading();
32921
34525
  this.emitQueryChangeWithResolvedRows(action, baseRows);
34526
+ if (action === "sort") this.options.onSortResolved?.();
32922
34527
  this.options.requestRender();
32923
34528
  }).catch(handleFailure);
32924
34529
  };
@@ -34443,6 +36048,7 @@ var ListTableSelectionController = class {
34443
36048
  __publicField(this, "selectedHeaderKey", null);
34444
36049
  __publicField(this, "selectedHeaderRect", null);
34445
36050
  __publicField(this, "summarySelected", false);
36051
+ __publicField(this, "pendingSortSelection", null);
34446
36052
  this.options = options;
34447
36053
  }
34448
36054
  getSelectedHeaderKey() {
@@ -34466,20 +36072,62 @@ var ListTableSelectionController = class {
34466
36072
  this.selectedHeaderRect = null;
34467
36073
  }
34468
36074
  clearBodySelection() {
36075
+ this.pendingSortSelection = null;
34469
36076
  this.options.setSelection({
34470
36077
  rowIndex: null,
34471
36078
  columnKey: null
34472
36079
  });
34473
36080
  }
36081
+ /** 排序前按稳定行标识记录活动行,避免行号被新排序复用。 */
36082
+ captureSortSelection(rows) {
36083
+ const options = this.options.getOptions();
36084
+ const rowIndex = this.options.getSelection().rowIndex;
36085
+ const record = rowIndex == null ? void 0 : rows[rowIndex];
36086
+ this.pendingSortSelection = options.rowKey && record !== void 0 && rowIndex != null ? { rowKey: getRowKey(options, record, rowIndex), rowIndex } : null;
36087
+ }
36088
+ /** 排序完成后将活动行及其单行选区映射到新行号。 */
36089
+ reconcileSortSelection(rows) {
36090
+ const pending = this.pendingSortSelection;
36091
+ this.pendingSortSelection = null;
36092
+ if (!pending) return;
36093
+ const options = this.options.getOptions();
36094
+ const sameIndexRecord = rows[pending.rowIndex];
36095
+ const nextIndex = sameIndexRecord !== void 0 && getRowKey(options, sameIndexRecord, pending.rowIndex) === pending.rowKey ? pending.rowIndex : rows.findIndex(
36096
+ (record, index) => getRowKey(options, record, index) === pending.rowKey
36097
+ );
36098
+ const selection = this.options.getSelection();
36099
+ if (selection.rowIndex !== pending.rowIndex) return;
36100
+ if (nextIndex === pending.rowIndex) return;
36101
+ this.options.setSelection({
36102
+ ...selection,
36103
+ rowIndex: nextIndex >= 0 ? nextIndex : null,
36104
+ columnKey: nextIndex >= 0 ? selection.columnKey : null
36105
+ });
36106
+ const range = this.options.getRangeHighlight();
36107
+ const singleRowRange = range.selectedRowRange?.start === pending.rowIndex && range.selectedRowRange.end === pending.rowIndex;
36108
+ const singleCellRange = range.selectedCellRange?.startRowIndex === pending.rowIndex && range.selectedCellRange.endRowIndex === pending.rowIndex;
36109
+ this.options.setRangeHighlight({
36110
+ ...range,
36111
+ selectedRowAnchorIndex: range.selectedRowAnchorIndex === pending.rowIndex ? nextIndex >= 0 ? nextIndex : null : range.selectedRowAnchorIndex,
36112
+ selectedRowRange: singleRowRange ? nextIndex >= 0 ? { start: nextIndex, end: nextIndex } : null : range.selectedRowRange,
36113
+ selectedCellAnchor: range.selectedCellAnchor?.rowIndex === pending.rowIndex ? nextIndex >= 0 ? { ...range.selectedCellAnchor, rowIndex: nextIndex } : null : range.selectedCellAnchor,
36114
+ selectedCellRange: singleCellRange ? nextIndex >= 0 ? {
36115
+ ...range.selectedCellRange,
36116
+ startRowIndex: nextIndex,
36117
+ endRowIndex: nextIndex
36118
+ } : null : range.selectedCellRange
36119
+ });
36120
+ }
34474
36121
  setSelectedHeaderState(selectedHeaderKey, selectedHeaderRect) {
34475
36122
  this.selectedHeaderKey = selectedHeaderKey;
34476
36123
  this.selectedHeaderRect = selectedHeaderRect;
34477
36124
  }
34478
- resetRangeHighlightForHeaderSelection(shiftKey) {
36125
+ resetRangeHighlightForHeaderSelection(shiftKey, ctrlKey = false) {
34479
36126
  this.options.setRangeHighlight(
34480
36127
  resolveRangeHighlightForHeaderSelection({
34481
36128
  current: this.options.getRangeHighlight(),
34482
- shiftKey
36129
+ shiftKey,
36130
+ ctrlKey
34483
36131
  })
34484
36132
  );
34485
36133
  }
@@ -34547,7 +36195,7 @@ var ListTableSelectionController = class {
34547
36195
  })
34548
36196
  );
34549
36197
  }
34550
- updateRangeHighlightForHeaderClick(columnKey, shiftKey) {
36198
+ updateRangeHighlightForHeaderClick(columnKey, shiftKey, ctrlKey = false) {
34551
36199
  const leafColumns = this.options.getLeafColumns();
34552
36200
  const targetRange = findHeaderLeafRange({
34553
36201
  columnKey,
@@ -34564,7 +36212,8 @@ var ListTableSelectionController = class {
34564
36212
  leafColumnKeys: leafColumns.map((column) => column.key),
34565
36213
  rangeStartIndex: targetRange.start,
34566
36214
  rangeEndIndex: targetRange.end,
34567
- shiftKey
36215
+ shiftKey,
36216
+ ctrlKey
34568
36217
  })
34569
36218
  );
34570
36219
  }
@@ -34608,7 +36257,8 @@ var ListTableSelectionController = class {
34608
36257
  this.options.setRangeHighlight({
34609
36258
  ...current,
34610
36259
  selectedColumnAnchorKey: null,
34611
- selectedColumnRange: null
36260
+ selectedColumnRange: null,
36261
+ selectedColumnKeys: null
34612
36262
  });
34613
36263
  }
34614
36264
  destroy() {
@@ -34946,6 +36596,44 @@ function createDefaultSummaryScheduler() {
34946
36596
  };
34947
36597
  }
34948
36598
 
36599
+ // src/engine/sort-animation.ts
36600
+ function hasAnimatedSortColumn(columns, sortState) {
36601
+ if (!sortState.length) return false;
36602
+ const activeKeys = new Set(sortState.map((item) => item.key));
36603
+ const visit = (items) => items.some(
36604
+ (column) => activeKeys.has(resolveColumnKey(column)) && typeof column.sorter === "object" && column.sorter.animation === true || (column.children?.length ? visit(column.children) : false)
36605
+ );
36606
+ return visit(columns);
36607
+ }
36608
+ function shouldAnimateSortedDataUpdate(params) {
36609
+ const { previous, next, sortState, expandedRowsChanged } = params;
36610
+ return Boolean(
36611
+ !expandedRowsChanged && next.rowKey && previous.rowKey && previous.rowHeight !== "auto" && next.rowHeight !== "auto" && next.rowHeight === previous.rowHeight && next.dataSource !== previous.dataSource && hasAnimatedSortColumn(next.columns, sortState)
36612
+ );
36613
+ }
36614
+
36615
+ // src/features/grouping/default-grouping.ts
36616
+ function resolveDefaultGroupingColumnKeys(columns) {
36617
+ const candidates = [];
36618
+ const visit = (items) => {
36619
+ items.forEach((column) => {
36620
+ if (column.children?.length) {
36621
+ visit(column.children);
36622
+ return;
36623
+ }
36624
+ if (!column.grouping) return;
36625
+ const order = typeof column.grouping === "object" && Number.isFinite(column.grouping.order) ? column.grouping.order : Number.POSITIVE_INFINITY;
36626
+ candidates.push({
36627
+ key: column.key ?? String(column.dataIndex),
36628
+ order,
36629
+ index: candidates.length
36630
+ });
36631
+ });
36632
+ };
36633
+ visit(columns);
36634
+ return candidates.sort((left, right) => left.order - right.order || left.index - right.index).map((item) => item.key);
36635
+ }
36636
+
34949
36637
  // src/engine/controller-assembly.ts
34950
36638
  function assembleListTableCoreControllers(core, options) {
34951
36639
  const getCanvasHostElement = () => core.domHost.getNodes()?.canvasHostElement ?? null;
@@ -35069,14 +36757,24 @@ function assembleListTableCoreControllers(core, options) {
35069
36757
  ...core.storeFacade.rangeHighlight,
35070
36758
  selectedColumnAnchorKey: null,
35071
36759
  selectedColumnRange: null,
36760
+ selectedColumnKeys: null,
35072
36761
  selectedCellRange: null
35073
36762
  };
35074
36763
  };
35075
36764
  core.syncGroupingBar = syncGroupingBar;
35076
36765
  core.syncGroupingFromOptions = () => {
35077
36766
  const previousColumnKeys = core.groupColumnKeys;
36767
+ const nextDefaultKeys = resolveDefaultGroupingColumnKeys(
36768
+ core.options.columns
36769
+ );
36770
+ const defaultsChanged = nextDefaultKeys.length !== core.defaultGroupColumnKeys.length || nextDefaultKeys.some(
36771
+ (key, index) => key !== core.defaultGroupColumnKeys[index]
36772
+ );
36773
+ core.defaultGroupColumnKeys = nextDefaultKeys;
35078
36774
  if (core.options.grouping?.visible !== void 0) {
35079
36775
  core.groupBarVisible = core.options.grouping.visible;
36776
+ } else if (defaultsChanged) {
36777
+ core.groupBarVisible = nextDefaultKeys.length > 0;
35080
36778
  }
35081
36779
  if (core.options.grouping?.columnKeys !== void 0) {
35082
36780
  const validKeys = new Set(
@@ -35087,6 +36785,8 @@ function assembleListTableCoreControllers(core, options) {
35087
36785
  core.groupColumnKeys = core.options.grouping.columnKeys.filter(
35088
36786
  (key, index, keys) => validKeys.has(key) && keys.indexOf(key) === index
35089
36787
  );
36788
+ } else if (defaultsChanged) {
36789
+ core.groupColumnKeys = nextDefaultKeys;
35090
36790
  }
35091
36791
  if (previousColumnKeys.length !== core.groupColumnKeys.length || previousColumnKeys.some(
35092
36792
  (key, index) => key !== core.groupColumnKeys[index]
@@ -35167,7 +36867,8 @@ function assembleListTableCoreControllers(core, options) {
35167
36867
  getQueryRuntimeState,
35168
36868
  showLoading: (text) => core.overlayController.showLoading(text),
35169
36869
  hideLoading: () => core.overlayController.hideLoading(),
35170
- requestRender: () => render()
36870
+ requestRender: () => render(),
36871
+ onSortResolved: () => core.animationController.markSortRowsReady()
35171
36872
  });
35172
36873
  core.summaryController = new ListTableSummaryController({
35173
36874
  getOptions: () => core.options,
@@ -35322,6 +37023,18 @@ function assembleListTableCoreControllers(core, options) {
35322
37023
  persistColumnConfig: () => core.persistenceController.persistColumnConfig(),
35323
37024
  emitQueryChange,
35324
37025
  prepareLocalQuery,
37026
+ captureSortAnimation: (columnKey) => {
37027
+ const currentSort = core.storeFacade.sortState;
37028
+ const nextSort = columnKey ? [...currentSort, { key: columnKey, order: "ascend" }] : currentSort;
37029
+ if (!core.options.rowKey || core.options.rowHeight === "auto" || !hasAnimatedSortColumn(core.options.columns, nextSort)) {
37030
+ core.animationController.setPendingSortRowPositions(null);
37031
+ return;
37032
+ }
37033
+ core.selectionController.captureSortSelection(getDisplayData());
37034
+ core.animationController.setPendingSortRowPositions(
37035
+ captureRenderedVisibleRowPositions(getCurrentRenderSnapshot())
37036
+ );
37037
+ },
35325
37038
  applyFilterQueryAsync,
35326
37039
  applySortQueryAsync,
35327
37040
  refreshRuntimeColumns,
@@ -35337,6 +37050,7 @@ function assembleListTableCoreControllers(core, options) {
35337
37050
  });
35338
37051
  core.contentActionController = new ListTableContentActionController({
35339
37052
  getSummaryVisible: () => Boolean(getCurrentRenderSnapshot()?.hasSummary),
37053
+ getSortState: () => core.storeFacade.sortState,
35340
37054
  getOptions: () => core.options,
35341
37055
  getLeafColumns: () => core.leafColumns,
35342
37056
  overlayController: core.overlayController,
@@ -35481,6 +37195,9 @@ function assembleListTableCoreControllers(core, options) {
35481
37195
  expandedRowKeys: core.storeFacade.expandedRowKeys
35482
37196
  });
35483
37197
  core.animationController.reconcileCollapseAnimations(snapshot);
37198
+ if (core.animationController.reconcileSortAnimations(snapshot)) {
37199
+ core.selectionController.reconcileSortSelection(snapshot.displayRows);
37200
+ }
35484
37201
  },
35485
37202
  getPublishAccessors: () => ({
35486
37203
  nestedTableLayoutRevision: core.animationController.getNestedTableLayoutRevision(),
@@ -35649,8 +37366,13 @@ function assembleListTableCoreControllers(core, options) {
35649
37366
  core.draggingResize = null;
35650
37367
  core.draggingReorder = null;
35651
37368
  core.suppressNextClick = false;
35652
- core.groupBarVisible = options.grouping?.visible ?? false;
35653
- core.groupColumnKeys = [...options.grouping?.columnKeys ?? []];
37369
+ core.defaultGroupColumnKeys = resolveDefaultGroupingColumnKeys(
37370
+ options.columns
37371
+ );
37372
+ core.groupBarVisible = options.grouping?.visible ?? core.defaultGroupColumnKeys.length > 0;
37373
+ core.groupColumnKeys = [
37374
+ ...options.grouping?.columnKeys ?? core.defaultGroupColumnKeys
37375
+ ];
35654
37376
  core.tableZoom = 1;
35655
37377
  }
35656
37378
  function collectGroupingColumnItems(columns) {
@@ -35765,6 +37487,7 @@ var ListTableCore = class {
35765
37487
  * @param options 新配置。
35766
37488
  */
35767
37489
  updateOptions(options) {
37490
+ const preserveContextMenu = options.dataSource !== this.options.dataSource || this.overlayController.shouldPreserveHeaderContextMenuOnOptionsUpdate();
35768
37491
  this.persistenceController.invalidate();
35769
37492
  if (options.storageKey === this.options.storageKey) {
35770
37493
  options = {
@@ -35776,6 +37499,8 @@ var ListTableCore = class {
35776
37499
  options = resolveListTableStyleOptions(options);
35777
37500
  this.animationController.stopCarouselScroll();
35778
37501
  const host = this.asAssemblyHost();
37502
+ const previousLeafColumns = host.leafColumns;
37503
+ const previousSegments = host.columnSegments;
35779
37504
  const shouldResetColumnConfig = shouldPersistIncomingColumnConfig(
35780
37505
  this.options.columnConfigResetRevision,
35781
37506
  options.columnConfigResetRevision
@@ -35804,15 +37529,37 @@ var ListTableCore = class {
35804
37529
  }
35805
37530
  applyInitialColumnVisibility(options.columns);
35806
37531
  ensureColumnKeysInPlace(options.columns);
35807
- if (options.expandable?.expandedRowKeys !== void 0 && !areSameRowKeySet(
37532
+ const expandedRowsChanged = options.expandable?.expandedRowKeys !== void 0 && !areSameRowKeySet(
35808
37533
  this.storeFacade.expandedRowKeys,
35809
37534
  options.expandable.expandedRowKeys
35810
- )) {
37535
+ );
37536
+ if (expandedRowsChanged) {
35811
37537
  this.animationController.setPendingCollapseRowPositions(
35812
37538
  captureRenderedVisibleRowPositions(
35813
37539
  this.renderController.getCurrentRenderSnapshot()
35814
37540
  )
35815
37541
  );
37542
+ } else if (shouldAnimateSortedDataUpdate({
37543
+ previous: this.options,
37544
+ next: options,
37545
+ sortState: this.storeFacade.sortState,
37546
+ expandedRowsChanged
37547
+ })) {
37548
+ if (this.animationController.hasPendingSortRows()) {
37549
+ this.animationController.markSortRowsReady();
37550
+ } else {
37551
+ this.selectionController.captureSortSelection(
37552
+ this.queryController.getDisplayData()
37553
+ );
37554
+ this.animationController.setPendingSortRowPositions(
37555
+ captureRenderedVisibleRowPositions(
37556
+ this.renderController.getCurrentRenderSnapshot()
37557
+ ),
37558
+ true
37559
+ );
37560
+ }
37561
+ } else if (options.dataSource !== this.options.dataSource) {
37562
+ this.animationController.markSortRowsReady();
35816
37563
  }
35817
37564
  this.queryController.reset();
35818
37565
  this.options = options;
@@ -35830,7 +37577,10 @@ var ListTableCore = class {
35830
37577
  });
35831
37578
  this.debugManager = new DebugManager(options.debug);
35832
37579
  host.renderer?.updateDebugOptions(options.debug ?? {});
35833
- this.hideFloatingOverlays({ includeLoading: true });
37580
+ this.hideFloatingOverlays({
37581
+ includeLoading: true,
37582
+ preserveContextMenu
37583
+ });
35834
37584
  this.refreshRuntimeColumns();
35835
37585
  if (shouldPersistColumnConfig) {
35836
37586
  this.persistenceController.persistColumnConfig();
@@ -35838,6 +37588,12 @@ var ListTableCore = class {
35838
37588
  this.persistenceController.loadStoredConfig({ restoreQueryState: false });
35839
37589
  }
35840
37590
  this.renderController.render();
37591
+ if (shouldPersistColumnConfig && haveVisibleLeafColumnsChanged(previousLeafColumns, host.leafColumns)) {
37592
+ host.rowActionController.startColumnVisibilityAnimation({
37593
+ previousLeafColumns,
37594
+ previousSegments
37595
+ });
37596
+ }
35841
37597
  host.tableId = options.tableId?.trim() ? options.tableId : host.tableId;
35842
37598
  host.rowDragController.syncRegistration();
35843
37599
  }
@@ -35875,6 +37631,14 @@ var ListTableCore = class {
35875
37631
  resetSorter() {
35876
37632
  this.headerActionController.resetSort();
35877
37633
  }
37634
+ /** 获取当前排序状态,供外部设置面板回显。 */
37635
+ getSortState() {
37636
+ return [...this.storeFacade.sortState];
37637
+ }
37638
+ /** 使用与表头一致的排序逻辑设置指定列。 */
37639
+ setSortOrder(columnKey, order) {
37640
+ this.headerActionController.setSortOrder(columnKey, order);
37641
+ }
35878
37642
  /**
35879
37643
  * 重置筛选状态。
35880
37644
  */
@@ -35980,14 +37744,15 @@ var ListTableCore = class {
35980
37744
  return this;
35981
37745
  }
35982
37746
  hideFloatingOverlays(params) {
35983
- this.componentPortal.hideFloatingLayers();
35984
37747
  if (params?.includeLoading) {
35985
37748
  this.overlayController.hideLoading();
35986
37749
  }
35987
37750
  this.overlayController.hideTooltip();
35988
37751
  this.overlayController.hideFilterPanel();
35989
37752
  this.overlayController.hideFixedPopup();
35990
- this.overlayController.hideHeaderContextMenu();
37753
+ if (!params?.preserveContextMenu) {
37754
+ this.overlayController.hideHeaderContextMenu();
37755
+ }
35991
37756
  }
35992
37757
  refreshRuntimeColumns() {
35993
37758
  this.storeFacade.runtimeColumns = buildRuntimeColumnsMap({
@@ -36069,6 +37834,14 @@ var ListTable = class {
36069
37834
  resetSorter() {
36070
37835
  this.core?.resetSorter();
36071
37836
  }
37837
+ /** 获取当前排序状态。 */
37838
+ getSortState() {
37839
+ return this.core?.getSortState() ?? [];
37840
+ }
37841
+ /** 设置指定列的排序方式。 */
37842
+ setSortOrder(columnKey, order) {
37843
+ this.core?.setSortOrder(columnKey, order);
37844
+ }
36072
37845
  /**
36073
37846
  * 重置筛选状态。
36074
37847
  */