stk-table-vue 0.11.5 → 0.11.6

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.
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * name: stk-table-vue
3
- * version: v0.11.4
3
+ * version: v0.11.6
4
4
  * description: High performance realtime virtual table for vue3 and vue2.7
5
5
  * author: japlus
6
6
  * homepage: https://ja-plus.github.io/stk-table-vue/
@@ -1,5 +1,5 @@
1
1
  import { Ref, ShallowRef } from 'vue';
2
- import { CellKeyGen, ColKeyGen, StkTableColumn, UniqKey } from '../types';
2
+ import { AreaSelectionConfig, CellKeyGen, ColKeyGen, StkTableColumn, UniqKey } from '../types';
3
3
  import { VirtualScrollStore, VirtualScrollXStore } from '../useVirtualScroll';
4
4
 
5
5
  /**
@@ -8,6 +8,7 @@ import { VirtualScrollStore, VirtualScrollXStore } from '../useVirtualScroll';
8
8
  * en: Cell area selection feature with mouse drag, keyboard navigation, copy-paste, etc.
9
9
  */
10
10
  export declare function useAreaSelection<DT extends Record<string, any>>(props: any, emits: any, tableContainerRef: Ref<HTMLDivElement | undefined>, dataSourceCopy: ShallowRef<DT[]>, tableHeaderLast: ShallowRef<StkTableColumn<DT>[]>, colKeyGen: ColKeyGen, cellKeyGen: CellKeyGen, scrollTo: (top: number | null, left: number | null) => void, virtualScroll: Ref<VirtualScrollStore>, virtualScrollX: Ref<VirtualScrollXStore>): {
11
+ config: import('vue').ComputedRef<AreaSelectionConfig>;
11
12
  isSelecting: Ref<boolean, boolean>;
12
13
  getClass: (cellKey: string, absoluteRowIndex: number, colKey: UniqKey) => string[];
13
14
  get: () => {
@@ -325,6 +325,7 @@ export type AreaSelectionRange = {
325
325
  };
326
326
  /** 单元格选区配置 */
327
327
  export type AreaSelectionConfig<T extends Record<string, any> = any> = {
328
+ enabled?: boolean;
328
329
  /**
329
330
  * 复制时的单元格文本格式化回调。
330
331
  * 如果你使用了 customCell 自定义渲染,应该提供此回调以确保复制内容与展示内容一致。
@@ -1,5 +1,5 @@
1
1
  import { ComputedRef, Ref, ShallowRef } from 'vue';
2
- import { StkTableColumn } from './types';
2
+ import { AreaSelectionConfig, StkTableColumn } from './types';
3
3
  import { VirtualScrollStore, VirtualScrollXStore } from './useVirtualScroll';
4
4
 
5
5
  /**
@@ -7,4 +7,4 @@ import { VirtualScrollStore, VirtualScrollXStore } from './useVirtualScroll';
7
7
  *
8
8
  * 在低版本浏览器中,虚拟滚动时,使用键盘滚动,等选中的行消失在视口外时,滚动会失效。
9
9
  */
10
- export declare function useKeyboardArrowScroll<DT extends Record<string, any>>(targetElement: Ref<HTMLElement | undefined>, props: any, scrollTo: (y: number | null, x: number | null) => void, virtualScroll: Ref<VirtualScrollStore>, virtualScrollX: Ref<VirtualScrollXStore>, tableHeaders: ShallowRef<StkTableColumn<DT>[][]>, virtual_on: ComputedRef<boolean>): void;
10
+ export declare function useKeyboardArrowScroll<DT extends Record<string, any>>(targetElement: Ref<HTMLElement | undefined>, props: any, scrollTo: (y: number | null, x: number | null) => void, virtualScroll: Ref<VirtualScrollStore>, virtualScrollX: Ref<VirtualScrollXStore>, tableHeaders: ShallowRef<StkTableColumn<DT>[][]>, virtual_on: ComputedRef<boolean>, areaSelectionConfig: ComputedRef<AreaSelectionConfig>): void;
@@ -41,7 +41,7 @@ export type VirtualScrollXStore = {
41
41
  * virtual scroll
42
42
  * @returns
43
43
  */
44
- export declare function useVirtualScroll<DT extends Record<string, any>>(props: any, tableContainerRef: Ref<HTMLElement | undefined>, trRef: Ref<HTMLTableRowElement[] | undefined>, dataSourceCopy: ShallowRef<PrivateRowDT[]>, tableHeaderLast: ShallowRef<PrivateStkTableColumn<PrivateRowDT>[]>, tableHeaders: ShallowRef<PrivateStkTableColumn<PrivateRowDT>[][]>, rowKeyGen: RowKeyGen, maxRowSpan: Map<UniqKey, number>, scrollbarOptions: Ref<Required<ScrollbarOptions>>, isExperimentalScrollY: Ref<boolean | undefined>): readonly [Ref<{
44
+ export declare function useVirtualScroll(props: any, tableContainerRef: Ref<HTMLElement | undefined>, trRef: Ref<HTMLTableRowElement[] | undefined>, dataSourceCopy: ShallowRef<PrivateRowDT[]>, tableHeaderLast: ShallowRef<PrivateStkTableColumn<PrivateRowDT>[]>, tableHeaders: ShallowRef<PrivateStkTableColumn<PrivateRowDT>[][]>, rowKeyGen: RowKeyGen, maxRowSpan: Map<UniqKey, number>, scrollbarOptions: Ref<Required<ScrollbarOptions>>, isExperimentalScrollY: Ref<boolean | undefined>): readonly [Ref<{
45
45
  containerHeight: number;
46
46
  pageSize: number;
47
47
  startIndex: number;
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * name: stk-table-vue
3
- * version: v0.11.4
3
+ * version: v0.11.6
4
4
  * description: High performance realtime virtual table for vue3 and vue2.7
5
5
  * author: japlus
6
6
  * homepage: https://ja-plus.github.io/stk-table-vue/
@@ -357,6 +357,13 @@ function useAreaSelection(props, emits, tableContainerRef, dataSourceCopy, table
357
357
  let autoScrollRafId = 0;
358
358
  let lastMouseClientX = 0;
359
359
  let lastMouseClientY = 0;
360
+ const config = computed(() => {
361
+ if (typeof props.areaSelection === "boolean") {
362
+ const b = props.areaSelection;
363
+ return { enabled: b, keyboard: b };
364
+ }
365
+ return props.areaSelection;
366
+ });
360
367
  const colKeyToIndexMap = computed(() => {
361
368
  const headers = tableHeaderLast.value;
362
369
  const map = /* @__PURE__ */ new Map();
@@ -533,7 +540,7 @@ function useAreaSelection(props, emits, tableContainerRef, dataSourceCopy, table
533
540
  return { deltaX, deltaY };
534
541
  }
535
542
  function onSelectionMouseDown(e) {
536
- if (!props.areaSelection || e.button !== 0) return;
543
+ if (!config.value.enabled || e.button !== 0) return;
537
544
  const rowIndex = getClosestTrIndex(e.target);
538
545
  const colKey = getClosestColKey(e.target);
539
546
  const colIndex = getColIndexByKey(colKey);
@@ -663,12 +670,11 @@ function useAreaSelection(props, emits, tableContainerRef, dataSourceCopy, table
663
670
  emits("area-selection-change", range, { rows, cols: selectedCols });
664
671
  }
665
672
  function getFormatCellFn() {
666
- const cfg = props.areaSelection;
667
- return cfg && typeof cfg === "object" && typeof cfg.formatCellForClipboard === "function" ? cfg.formatCellForClipboard : null;
673
+ const cfg = config.value;
674
+ return typeof cfg.formatCellForClipboard === "function" ? cfg.formatCellForClipboard : null;
668
675
  }
669
676
  const keyboardEnabled = computed(() => {
670
- const cfg = props.areaSelection;
671
- return cfg && typeof cfg === "object" && cfg.keyboard === true;
677
+ return config.value.keyboard;
672
678
  });
673
679
  function copySelectedArea() {
674
680
  if (!selectionRange.value) return "";
@@ -700,7 +706,7 @@ function useAreaSelection(props, emits, tableContainerRef, dataSourceCopy, table
700
706
  return text;
701
707
  }
702
708
  function onKeydown(e) {
703
- if (!props.areaSelection) return;
709
+ if (!config.value.enabled) return;
704
710
  const key = e.key;
705
711
  if (key === KEY_ESCAPE || key === KEY_ESC) {
706
712
  if (selectionRange.value) {
@@ -785,10 +791,11 @@ function useAreaSelection(props, emits, tableContainerRef, dataSourceCopy, table
785
791
  const footerHeight = tfoot ? tfoot.offsetHeight : 0;
786
792
  const vs = virtualScroll.value;
787
793
  const vsx = virtualScrollX.value;
794
+ const isScrollRowByRow = props.scrollRowByRow;
788
795
  const rowHeight = vs.rowHeight;
789
796
  const targetRowTop = rowIndex * rowHeight;
790
797
  const targetRowBottom = targetRowTop + rowHeight;
791
- const visibleTop = container.scrollTop;
798
+ const visibleTop = isScrollRowByRow ? vs.scrollTop : container.scrollTop;
792
799
  const visibleBottom = visibleTop + vs.containerHeight - headerHeight - footerHeight;
793
800
  let newScrollTop = null;
794
801
  if (targetRowTop < visibleTop) {
@@ -840,6 +847,7 @@ function useAreaSelection(props, emits, tableContainerRef, dataSourceCopy, table
840
847
  isSelecting.value = false;
841
848
  }
842
849
  return {
850
+ config,
843
851
  isSelecting,
844
852
  getClass: getAreaSelectionClasses,
845
853
  get: getSelectedArea,
@@ -852,6 +860,7 @@ const ON_DEMAND_FEATURE = {
852
860
  [useAreaSelection.name]: () => {
853
861
  console.warn("useAreaSelection is not registered");
854
862
  return {
863
+ config: computed(() => ({ enabled: false })),
855
864
  isSelecting: ref(false),
856
865
  onMD: () => {
857
866
  },
@@ -1403,7 +1412,7 @@ var ScrollCodes = /* @__PURE__ */ ((ScrollCodes2) => {
1403
1412
  return ScrollCodes2;
1404
1413
  })(ScrollCodes || {});
1405
1414
  const ScrollCodesValues = Object.values(ScrollCodes);
1406
- function useKeyboardArrowScroll(targetElement, props, scrollTo, virtualScroll, virtualScrollX, tableHeaders, virtual_on) {
1415
+ function useKeyboardArrowScroll(targetElement, props, scrollTo, virtualScroll, virtualScrollX, tableHeaders, virtual_on, areaSelectionConfig) {
1407
1416
  let isMouseOver = false;
1408
1417
  watch(virtual_on, (val) => {
1409
1418
  if (!val) {
@@ -1430,8 +1439,7 @@ function useKeyboardArrowScroll(targetElement, props, scrollTo, virtualScroll, v
1430
1439
  }
1431
1440
  function handleKeydown(e) {
1432
1441
  if (!virtual_on.value) return;
1433
- const areaSelection = props.areaSelection;
1434
- if (areaSelection && typeof areaSelection === "object" && areaSelection.keyboard) return;
1442
+ if (areaSelectionConfig.value.keyboard) return;
1435
1443
  const keyCode = e.code;
1436
1444
  if (!ScrollCodesValues.includes(keyCode)) return;
1437
1445
  if (!isMouseOver) return;
@@ -2249,7 +2257,7 @@ function useVirtualScroll(props, tableContainerRef, trRef, dataSourceCopy, table
2249
2257
  height = 0;
2250
2258
  }
2251
2259
  const { clientHeight, scrollHeight } = tableContainerRef.value || {};
2252
- let scrollTop = ((_a = tableContainerRef.value) == null ? void 0 : _a.scrollTop) || 0;
2260
+ let scrollTop = isExperimentalScrollY.value ? virtualScroll.value.scrollTop : ((_a = tableContainerRef.value) == null ? void 0 : _a.scrollTop) || 0;
2253
2261
  const rowHeight = getRowHeightFn.value();
2254
2262
  const containerHeight = height || clientHeight || DEFAULT_TABLE_HEIGHT;
2255
2263
  const { headless } = props;
@@ -2306,13 +2314,7 @@ function useVirtualScroll(props, tableContainerRef, trRef, dataSourceCopy, table
2306
2314
  const dataSourceCopyTemp = dataSourceCopy.value;
2307
2315
  const dataLength = dataSourceCopyTemp.length;
2308
2316
  const rowHeight = getRowHeightFn.value();
2309
- const vsValue = {
2310
- startIndex: 0,
2311
- // github #34 init
2312
- // endIndex: , // FIXME: endIndex may be old value
2313
- offsetTop: 0
2314
- // github #34 init
2315
- };
2317
+ const vsValue = {};
2316
2318
  const scrollHeight = dataLength * rowHeight + tableHeaderHeight.value;
2317
2319
  const { enabled: scrollbarEnable } = scrollbarOptions.value;
2318
2320
  if (scrollbarEnable) {
@@ -2326,6 +2328,7 @@ function useVirtualScroll(props, tableContainerRef, trRef, dataSourceCopy, table
2326
2328
  vsValue.scrollTop = sTop;
2327
2329
  Object.assign(virtualScroll.value, vsValue);
2328
2330
  if (!virtual_on.value) {
2331
+ Object.assign(virtualScroll.value, { startIndex: 0, endIndex: 0, offsetTop: 0 });
2329
2332
  return;
2330
2333
  }
2331
2334
  const { autoRowHeight, stripe, optimizeVue2Scroll } = props;
@@ -2389,7 +2392,7 @@ function useVirtualScroll(props, tableContainerRef, trRef, dataSourceCopy, table
2389
2392
  startIndex = correctedStartIndex;
2390
2393
  endIndex = correctedEndIndex;
2391
2394
  }
2392
- if (stripe && startIndex > 0 && startIndex % 2) {
2395
+ if (stripe && !isExperimentalScrollY.value && startIndex > 0 && startIndex % 2) {
2393
2396
  startIndex -= 1;
2394
2397
  if (autoRowHeight || hasExpandCol.value) {
2395
2398
  const height = getRowHeightFn.value(dataSourceCopyTemp[startIndex]);
@@ -2895,9 +2898,7 @@ const _sfc_main$1 = /* @__PURE__ */ defineComponent({
2895
2898
  scrollbarOptions,
2896
2899
  isExperimentalScrollY
2897
2900
  );
2898
- const rafUpdateVirtualScrollYForWheel = rafThrottle((scrollTop) => {
2899
- updateVirtualScrollY(scrollTop);
2900
- });
2901
+ const rafUpdateVirtualScrollYForWheel = rafThrottle(updateVirtualScrollY);
2901
2902
  const [scrollbar, showScrollbar, onVerticalScrollbarMouseDown, onHorizontalScrollbarMouseDown, updateCustomScrollbar] = useScrollbar(
2902
2903
  props,
2903
2904
  tableContainerRef,
@@ -2920,27 +2921,8 @@ const _sfc_main$1 = /* @__PURE__ */ defineComponent({
2920
2921
  if (props.autoResize) {
2921
2922
  useAutoResize(tableContainerRef, initVirtualScroll, props, 200);
2922
2923
  }
2923
- useKeyboardArrowScroll(tableContainerRef, props, scrollTo, virtualScroll, virtualScrollX, tableHeaders, virtual_on);
2924
- const [fixedCols, fixedColClassMap, updateFixedShadow] = useFixedCol(
2925
- props,
2926
- colKeyGen,
2927
- getFixedColPosition,
2928
- tableHeaders,
2929
- tableHeadersForCalc,
2930
- tableContainerRef
2931
- );
2932
- const [colResizeOn, isColResizing, onThResizeMouseDown] = useColResize(
2933
- props,
2934
- emits,
2935
- tableContainerRef,
2936
- tableHeaderLast,
2937
- colResizeIndicatorRef,
2938
- colKeyGen,
2939
- fixedCols
2940
- );
2941
- const [toggleExpandRow, setRowExpand] = useRowExpand(emits, dataSourceCopy, rowKeyGen);
2942
- const [toggleTreeNode, setTreeExpand, flatTreeData] = useTree(props, dataSourceCopy, rowKeyGen, emits);
2943
2924
  const {
2925
+ config: areaSelectionConfig,
2944
2926
  isSelecting: isAreaSelecting,
2945
2927
  onMD: onSelectionMouseDown,
2946
2928
  getClass: getAreaSelectionClasses,
@@ -2959,6 +2941,26 @@ const _sfc_main$1 = /* @__PURE__ */ defineComponent({
2959
2941
  virtualScroll,
2960
2942
  virtualScrollX
2961
2943
  );
2944
+ useKeyboardArrowScroll(tableContainerRef, props, scrollTo, virtualScroll, virtualScrollX, tableHeaders, virtual_on, areaSelectionConfig);
2945
+ const [fixedCols, fixedColClassMap, updateFixedShadow] = useFixedCol(
2946
+ props,
2947
+ colKeyGen,
2948
+ getFixedColPosition,
2949
+ tableHeaders,
2950
+ tableHeadersForCalc,
2951
+ tableContainerRef
2952
+ );
2953
+ const [colResizeOn, isColResizing, onThResizeMouseDown] = useColResize(
2954
+ props,
2955
+ emits,
2956
+ tableContainerRef,
2957
+ tableHeaderLast,
2958
+ colResizeIndicatorRef,
2959
+ colKeyGen,
2960
+ fixedCols
2961
+ );
2962
+ const [toggleExpandRow, setRowExpand] = useRowExpand(emits, dataSourceCopy, rowKeyGen);
2963
+ const [toggleTreeNode, setTreeExpand, flatTreeData] = useTree(props, dataSourceCopy, rowKeyGen, emits);
2962
2964
  watch(
2963
2965
  () => props.columns,
2964
2966
  () => {
@@ -3204,7 +3206,7 @@ const _sfc_main$1 = /* @__PURE__ */ defineComponent({
3204
3206
  if (props.cellActive && currentSelectedCellKey.value === cellKey) {
3205
3207
  classList.push("active");
3206
3208
  }
3207
- if (props.areaSelection) {
3209
+ if (areaSelectionConfig.value.enabled) {
3208
3210
  const absRowIndex = getRowIndex(rowIndex);
3209
3211
  classList.push(...getAreaSelectionClasses(cellKey, absRowIndex, colKey));
3210
3212
  }
@@ -3310,7 +3312,7 @@ const _sfc_main$1 = /* @__PURE__ */ defineComponent({
3310
3312
  function onCellMouseDown(e) {
3311
3313
  const { row, col, rowIndex } = getCellEventData(e);
3312
3314
  emits("cell-mousedown", e, row, col, { rowIndex });
3313
- if (props.areaSelection) {
3315
+ if (areaSelectionConfig.value.enabled) {
3314
3316
  onSelectionMouseDown(e);
3315
3317
  }
3316
3318
  }
@@ -3473,7 +3475,14 @@ const _sfc_main$1 = /* @__PURE__ */ defineComponent({
3473
3475
  }
3474
3476
  function scrollTo(top = 0, left = 0) {
3475
3477
  if (!tableContainerRef.value) return;
3476
- if (top !== null) tableContainerRef.value.scrollTop = top;
3478
+ if (top !== null) {
3479
+ if (isExperimentalScrollY.value) {
3480
+ updateVirtualScrollY(top);
3481
+ updateCustomScrollbar();
3482
+ } else {
3483
+ tableContainerRef.value.scrollTop = top;
3484
+ }
3485
+ }
3477
3486
  if (left !== null) tableContainerRef.value.scrollLeft = left;
3478
3487
  }
3479
3488
  function getTableData() {
@@ -3662,7 +3671,7 @@ const _sfc_main$1 = /* @__PURE__ */ defineComponent({
3662
3671
  "is-area-selecting": unref(isAreaSelecting),
3663
3672
  "exp-scroll-y": isExperimentalScrollY.value
3664
3673
  }]),
3665
- tabindex: props.areaSelection ? 0 : void 0,
3674
+ tabindex: unref(areaSelectionConfig).enabled ? 0 : void 0,
3666
3675
  style: normalizeStyle({
3667
3676
  "--row-height": props.autoRowHeight ? void 0 : unref(virtualScroll).rowHeight + "px",
3668
3677
  "--header-row-height": props.headerRowHeight + "px",
package/lib/style.css CHANGED
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * name: stk-table-vue
3
- * version: v0.11.4
3
+ * version: v0.11.6
4
4
  * description: High performance realtime virtual table for vue3 and vue2.7
5
5
  * author: japlus
6
6
  * homepage: https://ja-plus.github.io/stk-table-vue/
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "stk-table-vue",
3
- "version": "0.11.5",
3
+ "version": "0.11.6",
4
4
  "description": "High performance realtime virtual table for vue3 and vue2.7",
5
5
  "main": "./lib/stk-table-vue.js",
6
6
  "types": "./lib/src/StkTable/index.d.ts",
@@ -28,7 +28,7 @@
28
28
  'is-area-selecting': isAreaSelecting,
29
29
  'exp-scroll-y': isExperimentalScrollY,
30
30
  }"
31
- :tabindex="props.areaSelection ? 0 : void 0"
31
+ :tabindex="areaSelectionConfig.enabled ? 0 : void 0"
32
32
  :style="{
33
33
  '--row-height': props.autoRowHeight ? void 0 : virtualScroll.rowHeight + 'px',
34
34
  '--header-row-height': props.headerRowHeight + 'px',
@@ -864,9 +864,7 @@ const [
864
864
  );
865
865
 
866
866
  /** requestAnimationFrame throttled version of updateVirtualScrollY for smoother wheel scrolling */
867
- const rafUpdateVirtualScrollYForWheel = rafThrottle((scrollTop: number) => {
868
- updateVirtualScrollY(scrollTop);
869
- });
867
+ const rafUpdateVirtualScrollYForWheel = rafThrottle(updateVirtualScrollY);
870
868
 
871
869
  const [scrollbar, showScrollbar, onVerticalScrollbarMouseDown, onHorizontalScrollbarMouseDown, updateCustomScrollbar] = useScrollbar(
872
870
  props,
@@ -896,8 +894,29 @@ if (props.autoResize) {
896
894
  useAutoResize(tableContainerRef, initVirtualScroll, props, 200);
897
895
  }
898
896
 
897
+ const {
898
+ config: areaSelectionConfig,
899
+ isSelecting: isAreaSelecting,
900
+ onMD: onSelectionMouseDown,
901
+ getClass: getAreaSelectionClasses,
902
+ get: getSelectedArea,
903
+ clear: clearSelectedArea,
904
+ copy: copySelectedArea,
905
+ } = ON_DEMAND_FEATURE[useAreaSelection.name](
906
+ props,
907
+ emits,
908
+ tableContainerRef,
909
+ dataSourceCopy,
910
+ tableHeaderLast,
911
+ colKeyGen,
912
+ cellKeyGen,
913
+ scrollTo,
914
+ virtualScroll,
915
+ virtualScrollX,
916
+ );
917
+
899
918
  /** 键盘箭头滚动 */
900
- useKeyboardArrowScroll(tableContainerRef, props, scrollTo, virtualScroll, virtualScrollX, tableHeaders, virtual_on);
919
+ useKeyboardArrowScroll(tableContainerRef, props, scrollTo, virtualScroll, virtualScrollX, tableHeaders, virtual_on, areaSelectionConfig);
901
920
 
902
921
  /** 固定列处理 */
903
922
  const [fixedCols, fixedColClassMap, updateFixedShadow] = useFixedCol(
@@ -923,26 +942,6 @@ const [toggleExpandRow, setRowExpand] = useRowExpand(emits, dataSourceCopy, rowK
923
942
 
924
943
  const [toggleTreeNode, setTreeExpand, flatTreeData] = useTree(props, dataSourceCopy, rowKeyGen, emits);
925
944
 
926
- const {
927
- isSelecting: isAreaSelecting,
928
- onMD: onSelectionMouseDown,
929
- getClass: getAreaSelectionClasses,
930
- get: getSelectedArea,
931
- clear: clearSelectedArea,
932
- copy: copySelectedArea,
933
- } = ON_DEMAND_FEATURE[useAreaSelection.name](
934
- props,
935
- emits,
936
- tableContainerRef,
937
- dataSourceCopy,
938
- tableHeaderLast,
939
- colKeyGen,
940
- cellKeyGen,
941
- scrollTo,
942
- virtualScroll,
943
- virtualScrollX,
944
- );
945
-
946
945
  watch(
947
946
  () => props.columns,
948
947
  () => {
@@ -1240,7 +1239,7 @@ function getTDProps(row: PrivateRowDT | null | undefined, col: StkTableColumn<Pr
1240
1239
  }
1241
1240
 
1242
1241
  // 单元格拖选选区样式
1243
- if (props.areaSelection) {
1242
+ if (areaSelectionConfig.value.enabled) {
1244
1243
  const absRowIndex = getRowIndex(rowIndex);
1245
1244
  classList.push(...getAreaSelectionClasses(cellKey, absRowIndex, colKey));
1246
1245
  }
@@ -1363,7 +1362,7 @@ function onCellMouseDown(e: MouseEvent) {
1363
1362
  const { row, col, rowIndex } = getCellEventData(e);
1364
1363
  emits('cell-mousedown', e, row, col, { rowIndex });
1365
1364
  // 单元格拖选
1366
- if (props.areaSelection) {
1365
+ if (areaSelectionConfig.value.enabled) {
1367
1366
  onSelectionMouseDown(e);
1368
1367
  }
1369
1368
  }
@@ -1570,7 +1569,14 @@ function setSelectedCell(row?: DT, col?: StkTableColumn<DT>, option = { silent:
1570
1569
  */
1571
1570
  function scrollTo(top: number | null = 0, left: number | null = 0) {
1572
1571
  if (!tableContainerRef.value) return;
1573
- if (top !== null) tableContainerRef.value.scrollTop = top;
1572
+ if (top !== null) {
1573
+ if (isExperimentalScrollY.value) {
1574
+ updateVirtualScrollY(top);
1575
+ updateCustomScrollbar();
1576
+ } else {
1577
+ tableContainerRef.value.scrollTop = top;
1578
+ }
1579
+ }
1574
1580
  if (left !== null) tableContainerRef.value.scrollLeft = left;
1575
1581
  }
1576
1582
 
@@ -1,5 +1,5 @@
1
1
  import { Ref, ShallowRef, computed, onBeforeUnmount, onMounted, ref } from 'vue';
2
- import { AreaSelectionRange, CellKeyGen, ColKeyGen, StkTableColumn, UniqKey } from '../types';
2
+ import { AreaSelectionConfig, AreaSelectionRange, CellKeyGen, ColKeyGen, StkTableColumn, UniqKey } from '../types';
3
3
  import { VirtualScrollStore, VirtualScrollXStore } from '../useVirtualScroll';
4
4
  import { getClosestColKey, getClosestTrIndex } from '../utils';
5
5
  import { getCalculatedColWidth } from '../utils/constRefUtils';
@@ -62,6 +62,14 @@ export function useAreaSelection<DT extends Record<string, any>>(
62
62
  let lastMouseClientX = 0;
63
63
  let lastMouseClientY = 0;
64
64
 
65
+ const config = computed<AreaSelectionConfig>(() => {
66
+ if (typeof props.areaSelection === 'boolean') {
67
+ const b = props.areaSelection;
68
+ return { enabled: b, keyboard: b };
69
+ }
70
+ return props.areaSelection;
71
+ });
72
+
65
73
  /** colKey → absolute index 映射 */
66
74
  const colKeyToIndexMap = computed(() => {
67
75
  const headers = tableHeaderLast.value;
@@ -292,7 +300,7 @@ export function useAreaSelection<DT extends Record<string, any>>(
292
300
 
293
301
  /** mousedown 处理:设置锚点,开始拖选 */
294
302
  function onSelectionMouseDown(e: MouseEvent) {
295
- if (!props.areaSelection || e.button !== 0) return;
303
+ if (!config.value.enabled || e.button !== 0) return;
296
304
 
297
305
  const rowIndex = getClosestTrIndex(e.target as HTMLElement);
298
306
  const colKey = getClosestColKey(e.target as HTMLElement);
@@ -483,14 +491,13 @@ export function useAreaSelection<DT extends Record<string, any>>(
483
491
 
484
492
  /** 获取 areaSelection 配置中的格式化回调 */
485
493
  function getFormatCellFn() {
486
- const cfg = props.areaSelection;
487
- return cfg && typeof cfg === 'object' && typeof cfg.formatCellForClipboard === 'function' ? cfg.formatCellForClipboard : null;
494
+ const cfg = config.value;
495
+ return typeof cfg.formatCellForClipboard === 'function' ? cfg.formatCellForClipboard : null;
488
496
  }
489
497
 
490
498
  /** 是否启用键盘控制选区移动 */
491
499
  const keyboardEnabled = computed(() => {
492
- const cfg = props.areaSelection;
493
- return cfg && typeof cfg === 'object' && cfg.keyboard === true;
500
+ return config.value.keyboard;
494
501
  });
495
502
 
496
503
  /**
@@ -538,7 +545,7 @@ export function useAreaSelection<DT extends Record<string, any>>(
538
545
  * Arrow keys / Tab 移动选区(keyboard=true时)
539
546
  **/
540
547
  function onKeydown(e: KeyboardEvent) {
541
- if (!props.areaSelection) return;
548
+ if (!config.value.enabled) return;
542
549
 
543
550
  const key = e.key;
544
551
 
@@ -666,13 +673,17 @@ export function useAreaSelection<DT extends Record<string, any>>(
666
673
  const vs = virtualScroll.value;
667
674
  const vsx = virtualScrollX.value;
668
675
 
676
+ // 是否开启按行滚动模式(experimental.scrollY 模式)
677
+ const isScrollRowByRow = props.scrollRowByRow;
678
+
669
679
  // 计算目标行的位置(基于虚拟滚动数据)
670
680
  const rowHeight = vs.rowHeight;
671
681
  const targetRowTop = rowIndex * rowHeight;
672
682
  const targetRowBottom = targetRowTop + rowHeight;
673
683
 
674
684
  // 计算可视区域
675
- const visibleTop = container.scrollTop;
685
+ // experimental.scrollY 模式下,容器 scrollTop 始终为 0,需要使用 virtualScroll.scrollTop
686
+ const visibleTop = isScrollRowByRow ? vs.scrollTop : container.scrollTop;
676
687
  const visibleBottom = visibleTop + vs.containerHeight - headerHeight - footerHeight;
677
688
 
678
689
  // 计算需要的垂直滚动位置
@@ -681,7 +692,7 @@ export function useAreaSelection<DT extends Record<string, any>>(
681
692
  // 目标行在可视区域上方,滚动到使目标行位于顶部
682
693
  newScrollTop = targetRowTop;
683
694
  } else if (targetRowBottom > visibleBottom) {
684
- // 目标行在可视区域下方,滚动到使目标行位于底部
695
+ // 目标行在可视区域下方
685
696
  newScrollTop = targetRowBottom - (vs.containerHeight - headerHeight - footerHeight);
686
697
  }
687
698
 
@@ -704,7 +715,6 @@ export function useAreaSelection<DT extends Record<string, any>>(
704
715
  newScrollLeft = targetColRight - vsx.containerWidth + rightFixedWidth;
705
716
  }
706
717
 
707
- // 执行滚动
708
718
  if (newScrollTop !== null || newScrollLeft !== null) {
709
719
  scrollTo(newScrollTop, newScrollLeft);
710
720
  }
@@ -755,6 +765,7 @@ export function useAreaSelection<DT extends Record<string, any>>(
755
765
  }
756
766
 
757
767
  return {
768
+ config,
758
769
  isSelecting,
759
770
  getClass: getAreaSelectionClasses,
760
771
  get: getSelectedArea,
@@ -1,4 +1,4 @@
1
- import { ref } from 'vue';
1
+ import { computed, ref } from 'vue';
2
2
  import { useAreaSelection } from './features';
3
3
 
4
4
  type OnDemandFeature = {
@@ -9,6 +9,7 @@ export const ON_DEMAND_FEATURE: OnDemandFeature = {
9
9
  [useAreaSelection.name]: (() => {
10
10
  console.warn('useAreaSelection is not registered');
11
11
  return {
12
+ config: computed(() => ({ enabled: false })),
12
13
  isSelecting: ref(false),
13
14
  onMD: () => {},
14
15
  getClass: () => [],
@@ -354,6 +354,7 @@ export type AreaSelectionRange = {
354
354
 
355
355
  /** 单元格选区配置 */
356
356
  export type AreaSelectionConfig<T extends Record<string, any> = any> = {
357
+ enabled?: boolean;
357
358
  /**
358
359
  * 复制时的单元格文本格式化回调。
359
360
  * 如果你使用了 customCell 自定义渲染,应该提供此回调以确保复制内容与展示内容一致。
@@ -1,5 +1,5 @@
1
1
  import { ComputedRef, Ref, ShallowRef, onBeforeUnmount, onMounted, watch } from 'vue';
2
- import { StkTableColumn } from './types';
2
+ import { AreaSelectionConfig, StkTableColumn } from './types';
3
3
  import { VirtualScrollStore, VirtualScrollXStore } from './useVirtualScroll';
4
4
 
5
5
  /** 翻页按键 */
@@ -29,6 +29,7 @@ export function useKeyboardArrowScroll<DT extends Record<string, any>>(
29
29
  virtualScrollX: Ref<VirtualScrollXStore>,
30
30
  tableHeaders: ShallowRef<StkTableColumn<DT>[][]>,
31
31
  virtual_on: ComputedRef<boolean>,
32
+ areaSelectionConfig: ComputedRef<AreaSelectionConfig>,
32
33
  ) {
33
34
  /** 检测鼠标是否悬浮在表格体上 */
34
35
  let isMouseOver = false;
@@ -62,8 +63,7 @@ export function useKeyboardArrowScroll<DT extends Record<string, any>>(
62
63
  function handleKeydown(e: KeyboardEvent) {
63
64
  if (!virtual_on.value) return; // 非虚拟滚动使用浏览器默认滚动
64
65
  // 如果单元格选区键盘控制已启用,则不处理滚动,交给 useAreaSelection 处理
65
- const areaSelection = props.areaSelection;
66
- if (areaSelection && typeof areaSelection === 'object' && areaSelection.keyboard) return;
66
+ if (areaSelectionConfig.value.keyboard) return;
67
67
  const keyCode = e.code;
68
68
  if (!ScrollCodesValues.includes(keyCode as any)) return;
69
69
  if (!isMouseOver) return; // 不悬浮还是要触发键盘事件的
@@ -47,7 +47,7 @@ const VUE2_SCROLL_TIMEOUT_MS = 200;
47
47
  * virtual scroll
48
48
  * @returns
49
49
  */
50
- export function useVirtualScroll<DT extends Record<string, any>>(
50
+ export function useVirtualScroll(
51
51
  props: any,
52
52
  tableContainerRef: Ref<HTMLElement | undefined>,
53
53
  trRef: Ref<HTMLTableRowElement[] | undefined>,
@@ -201,7 +201,9 @@ export function useVirtualScroll<DT extends Record<string, any>>(
201
201
  height = 0;
202
202
  }
203
203
  const { clientHeight, scrollHeight } = tableContainerRef.value || {};
204
- let scrollTop = tableContainerRef.value?.scrollTop || 0;
204
+ // isExperimentalScrollY 为 true 时,DOM 的 scrollTop 始终为 0(纵向滚动通过 transform 模拟)
205
+ // 此时应该使用 virtualScroll 中保存的 scrollTop 值
206
+ let scrollTop = isExperimentalScrollY.value ? virtualScroll.value.scrollTop : tableContainerRef.value?.scrollTop || 0;
205
207
 
206
208
  const rowHeight = getRowHeightFn.value();
207
209
  const containerHeight = height || clientHeight || DEFAULT_TABLE_HEIGHT;
@@ -272,11 +274,7 @@ export function useVirtualScroll<DT extends Record<string, any>>(
272
274
  const dataLength = dataSourceCopyTemp.length;
273
275
  const rowHeight = getRowHeightFn.value();
274
276
 
275
- const vsValue: any = {
276
- startIndex: 0, // github #34 init
277
- // endIndex: , // FIXME: endIndex may be old value
278
- offsetTop: 0, // github #34 init
279
- };
277
+ const vsValue: any = {};
280
278
  const scrollHeight = dataLength * rowHeight + tableHeaderHeight.value;
281
279
  const { enabled: scrollbarEnable } = scrollbarOptions.value;
282
280
  if (scrollbarEnable) {
@@ -292,6 +290,8 @@ export function useVirtualScroll<DT extends Record<string, any>>(
292
290
  Object.assign(virtualScroll.value, vsValue);
293
291
 
294
292
  if (!virtual_on.value) {
293
+ // github #34 init
294
+ Object.assign(virtualScroll.value, { startIndex: 0, endIndex: 0, offsetTop: 0 });
295
295
  return;
296
296
  }
297
297
 
@@ -372,7 +372,7 @@ export function useVirtualScroll<DT extends Record<string, any>>(
372
372
  endIndex = correctedEndIndex;
373
373
  }
374
374
 
375
- if (stripe && startIndex > 0 && startIndex % 2) {
375
+ if (stripe && !isExperimentalScrollY.value && startIndex > 0 && startIndex % 2) {
376
376
  // 斑马纹情况下,每滚动偶数行才加载。防止斑马纹错位。
377
377
  startIndex -= 1; // 奇数-1变成偶数
378
378
  if (autoRowHeight || hasExpandCol.value) {