stk-table-vue 0.8.2 → 0.8.4

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/README.md CHANGED
@@ -12,57 +12,27 @@
12
12
  </p>
13
13
  </p>
14
14
 
15
- > StK Table (Sticky Table) 是一个基于Vue 的高性能虚拟列表组件。
16
- > 用于实时数据展示,数据高亮动效。
15
+ > Stk Table Vue(Sticky Table) is a high-performance virtual list component based on Vue.
16
+ >
17
+ > Used for real-time data display, with data highlighting and dynamic effects
18
+ >
19
+ > Support Vue3 and Vue2.7
17
20
 
18
- > Vue2.7支持引入源码(**ts**)使用。
19
21
 
20
- ## Doc
21
- 文档: [Stk Table Vue 高性能虚拟表格](https://ja-plus.github.io/stk-table-vue/)
22
+ ## Documentation
23
+ ### [Stk Table Vue Official CN](https://ja-plus.github.io/stk-table-vue/)
24
+
22
25
 
23
26
  ## Repo:
24
27
  - [Github](https://github.com/ja-plus/stk-table-vue)
25
28
  - [Gitee](https://gitee.com/japlus/stk-table-vue) 🇨🇳
26
29
 
27
30
  ## Demo
31
+ [<span style="font-size: 16px;font-weight: bold;">Online Demo in stackblitz</span>](https://stackblitz.com/edit/vitejs-vite-ad91hh?file=src%2FDemo%2Findex.vue)
32
+
33
+ ## Compare
34
+ Compare performance with other vue table [vue-table-compare](https://github.com/ja-plus/vue-table-compare)
28
35
 
29
- [<span style="font-size: 16px;font-weight: bold;">Online Demo</span>](https://stackblitz.com/edit/vitejs-vite-ad91hh?file=src%2FDemo%2Findex.vue)
30
-
31
- ## Feature TODO:
32
- * [x] 高亮行,单元格。
33
- - [x] 使用 `Web Animations API` 实现高亮。(`v0.3.4` 变更为默认值)
34
- - [x] 支持配置高亮参数(持续时间,颜色,频率)。(`v0.2.9`)
35
- - [x] `setHighlightDimRow`/`setHighlightCellRow`支持自定义高亮css类名。(`v0.2.9`)
36
- * [x] 虚拟列表。
37
- - [x] 纵向。
38
- - [x] 横向(必须设置列宽)。
39
- - [x] 支持不定行高。(`v0.6.0`)
40
- * [x] 列固定。
41
- - [x] 固定列阴影。
42
- - [x] 多级表头固定列阴影。
43
- - [x] sticky column 动态计算阴影位置。(`v0.4.0`)
44
- * [x] 行展开。(`v0.5.0`)
45
- * [x] 行拖动。(`v0.5.0`)
46
- * [x] 树形。(`v0.7.0`)
47
- * [x] 单元格合并。(`v0.8.0`)
48
- * [] 列筛选。
49
- * [x] 斑马纹。
50
- * [x] 拖动更改列顺序。
51
- * [x] 拖动调整列宽。
52
- * [x] 排序
53
- - [x] 支持配置 `null` | `undefined` 永远排最后。
54
- - [x] 支持配置 string 使用 `String.prototype.localCompare` 排序。
55
- * [x] 多级表头。
56
- - [] 横向虚拟滚动。
57
- * [x] 支持table-layout: fixed 配置。
58
- * [x] 鼠标悬浮在表格上,键盘滚动虚拟表格。
59
- - [x] 键盘 `ArrowUp`/`ArrowDown`/`ArrowLeft`/`ArrowRight`/`PageUp`/ `PageDown` 按键支持。
60
- * [] 非虚拟滚动时,大数据分批加载。
61
- * [x] vue2.7支持(引入源码使用)。
62
- - [x] `props.optimizeVue2Scroll` 优化vue2虚拟滚动流畅度。(`v0.2.0`)
63
- * [x] 支持配置序号列。`StkTableColumn['type']`。(`v0.3.0`)
64
- * [x] `props.cellHover`单元格悬浮样式。(`v0.3.2`)
65
- * [] 惯性滚动优化。
66
36
 
67
37
 
68
38
  ## Usage
@@ -78,17 +48,17 @@ const stkTableRef = useTemplateRef('stkTableRef');
78
48
 
79
49
  // highlight row
80
50
  stkTableRef.value.setHighlightDimRow([rowKey],{
81
- method: 'css'|'animation',// 默认 animation。
82
- className: 'custom-class-name', // method css 时生效。
51
+ method: 'css'|'animation',// default animation。
52
+ className: 'custom-class-name', // method css
83
53
  keyframe: [{backgroundColor:'#aaa'}, {backgroundColor: '#222'}],//same as https://developer.mozilla.org/zh-CN/docs/Web/API/Web_Animations_API/Keyframe_Formats
84
- duration: 2000,// 动画时长。
54
+ duration: 2000,。
85
55
  });
86
56
  // highlight cell
87
57
  stkTableRef.value.setHighlightDimCell(rowKey, colDataIndex, {
88
58
  method: 'css'|'animation',
89
- className:'custom-class-name', // method css 时生效。
90
- keyframe: [{backgroundColor:'#aaa'}, {backgroundColor: '#222'}], // method animation 时生效。
91
- duration: 2000,// 动画时长。
59
+ className:'custom-class-name', // method css
60
+ keyframe: [{backgroundColor:'#aaa'}, {backgroundColor: '#222'}], // method animation
61
+ duration: 2000,。
92
62
  });
93
63
 
94
64
  const columns = [
@@ -114,10 +84,7 @@ const dataSource = [
114
84
  ```
115
85
 
116
86
  ### Vue2.7 Usage
117
- [在vue2中使用](https://ja-plus.github.io/stk-table-vue/main/start/vue2-usage.html)
118
-
119
- ## Notice
120
- 注意,组件会改动 `props.columns` 中的对象。如果多个组件 `columns` 数组元素存在引用同一个 `StkTableColumn` 对象。则考虑赋值 `columns` 前进行深拷贝。
87
+ [Vue2.7 Usage](https://ja-plus.github.io/stk-table-vue/main/start/vue2-usage.html)
121
88
 
122
89
  ## API
123
90
  ### Props
@@ -136,7 +103,7 @@ const dataSource = [
136
103
  [StkTableColumn 列配置](https://ja-plus.github.io/stk-table-vue/main/api/stk-table-column.html)
137
104
 
138
105
  ### setHighlightDimCell & setHighlightDimRow
139
- [高亮使用文档](https://ja-plus.github.io/stk-table-vue/main/api/expose.html#sethighlightdimcell)
106
+ [Highlight 高亮](https://ja-plus.github.io/stk-table-vue/main/api/expose.html#sethighlightdimcell)
140
107
 
141
108
 
142
109
  ### Example
@@ -209,4 +176,4 @@ const dataSource = [
209
176
 
210
177
 
211
178
  ## Other
212
- * `$*$` 兼容注释
179
+ * `$*$`
@@ -347,7 +347,7 @@ declare const __VLS_component: import('vue').DefineComponent<import('vue').Extra
347
347
  * en: Set expanded rows
348
348
  * @see {@link setRowExpand}
349
349
  */
350
- setRowExpand: (rowKeyOrRow: string | undefined | PrivateRowDT, expand?: boolean, data?: {
350
+ setRowExpand: (rowKeyOrRow: string | undefined | PrivateRowDT, expand?: boolean | null, data?: {
351
351
  col?: StkTableColumn<PrivateRowDT>;
352
352
  silent?: boolean;
353
353
  }) => void;
@@ -0,0 +1,14 @@
1
+ import { ShallowRef } from 'vue';
2
+ import { PrivateStkTableColumn, RowKeyGen, UniqKey } from './types';
3
+
4
+ type Options = {
5
+ props: any;
6
+ tableHeaderLast: ShallowRef<PrivateStkTableColumn<any>[]>;
7
+ rowKeyGen: RowKeyGen;
8
+ dataSourceCopy: ShallowRef<any[]>;
9
+ };
10
+ export declare function useMaxRowSpan({ props, tableHeaderLast, rowKeyGen, dataSourceCopy }: Options): {
11
+ maxRowSpan: Map<UniqKey, number>;
12
+ updateMaxRowSpan: () => void;
13
+ };
14
+ export {};
@@ -1,13 +1,14 @@
1
1
  import { ShallowRef } from 'vue';
2
2
  import { ColKeyGen, MergeCellsParam, PrivateStkTableColumn, RowKeyGen, UniqKey } from './types';
3
3
 
4
- export declare function useMergeCells({ props, tableHeaderLast, rowKeyGen, colKeyGen, virtual_dataSourcePart, }: {
4
+ type Options = {
5
5
  props: any;
6
6
  tableHeaderLast: ShallowRef<PrivateStkTableColumn<any>[]>;
7
7
  rowKeyGen: RowKeyGen;
8
8
  colKeyGen: ColKeyGen;
9
9
  virtual_dataSourcePart: ShallowRef<any[]>;
10
- }): {
10
+ };
11
+ export declare function useMergeCells({ props, tableHeaderLast, rowKeyGen, colKeyGen, virtual_dataSourcePart, }: Options): {
11
12
  hiddenCellMap: import('vue').Ref<Record<UniqKey, Set<UniqKey>>, Record<UniqKey, Set<UniqKey>>>;
12
13
  mergeCellsWrapper: (row: MergeCellsParam<any>["row"], col: MergeCellsParam<any>["col"], rowIndex: MergeCellsParam<any>["rowIndex"], colIndex: MergeCellsParam<any>["colIndex"]) => {
13
14
  colspan?: number;
@@ -18,3 +19,4 @@ export declare function useMergeCells({ props, tableHeaderLast, rowKeyGen, colKe
18
19
  activeMergedCells: import('vue').Ref<Set<string> & Omit<Set<string>, keyof Set<any>>, Set<string> | (Set<string> & Omit<Set<string>, keyof Set<any>>)>;
19
20
  updateActiveMergedCells: (clear?: boolean) => void;
20
21
  };
22
+ export {};
@@ -9,7 +9,7 @@ type Option<DT extends Record<string, any>> = {
9
9
  };
10
10
  export declare function useRowExpand({ dataSourceCopy, rowKeyGen, emits }: Option<DT>): {
11
11
  toggleExpandRow: (row: DT, col: StkTableColumn<DT>) => void;
12
- setRowExpand: (rowKeyOrRow: string | undefined | DT, expand?: boolean, data?: {
12
+ setRowExpand: (rowKeyOrRow: string | undefined | DT, expand?: boolean | null, data?: {
13
13
  col?: StkTableColumn<DT>;
14
14
  silent?: boolean;
15
15
  }) => void;
@@ -9,6 +9,7 @@ type Option<DT extends Record<string, any>> = {
9
9
  tableHeaderLast: ShallowRef<PrivateStkTableColumn<DT>[]>;
10
10
  tableHeaders: ShallowRef<PrivateStkTableColumn<DT>[][]>;
11
11
  rowKeyGen: RowKeyGen;
12
+ maxRowSpan: Map<UniqKey, number>;
12
13
  };
13
14
  /** 暂存纵向虚拟滚动的数据 */
14
15
  export type VirtualScrollStore = {
@@ -45,11 +46,11 @@ export type VirtualScrollXStore = {
45
46
  scrollLeft: number;
46
47
  };
47
48
  /**
48
- * 虚拟滚动
49
+ * virtual scroll
49
50
  * @param param0
50
51
  * @returns
51
52
  */
52
- export declare function useVirtualScroll<DT extends Record<string, any>>({ props, tableContainerRef, trRef, dataSourceCopy, tableHeaderLast, tableHeaders, rowKeyGen, }: Option<DT>): {
53
+ export declare function useVirtualScroll<DT extends Record<string, any>>({ props, tableContainerRef, trRef, dataSourceCopy, tableHeaderLast, tableHeaders, rowKeyGen, maxRowSpan, }: Option<DT>): {
53
54
  virtualScroll: Ref<{
54
55
  containerHeight: number;
55
56
  pageSize: number;
@@ -853,9 +853,114 @@ function useKeyboardArrowScroll(targetElement, { props, scrollTo, virtualScroll,
853
853
  if (!isMouseOver) isMouseOver = true;
854
854
  }
855
855
  }
856
+ function useMaxRowSpan({ props, tableHeaderLast, rowKeyGen, dataSourceCopy }) {
857
+ const maxRowSpan = /* @__PURE__ */ new Map();
858
+ function updateMaxRowSpan() {
859
+ if (!props.virtual) {
860
+ if (maxRowSpan.size) maxRowSpan.clear();
861
+ return;
862
+ }
863
+ maxRowSpan.clear();
864
+ const data = dataSourceCopy.value;
865
+ const columns = tableHeaderLast.value;
866
+ const columnsWithMerge = columns.filter((col) => col.mergeCells);
867
+ if (!columnsWithMerge.length) return;
868
+ const dataLength = data.length;
869
+ const mergeColumnsLength = columnsWithMerge.length;
870
+ for (let rowIndex = 0; rowIndex < dataLength; rowIndex++) {
871
+ const row = data[rowIndex];
872
+ const rowKey = rowKeyGen(row);
873
+ let currentMax = maxRowSpan.get(rowKey) || 0;
874
+ for (let colIndex = 0; colIndex < mergeColumnsLength; colIndex++) {
875
+ const col = columnsWithMerge[colIndex];
876
+ const { rowspan = 1 } = col.mergeCells({ row, col, rowIndex, colIndex }) || {};
877
+ if (rowspan > 1 && rowspan > currentMax) {
878
+ currentMax = rowspan;
879
+ maxRowSpan.set(rowKey, currentMax);
880
+ }
881
+ }
882
+ }
883
+ }
884
+ return {
885
+ maxRowSpan,
886
+ updateMaxRowSpan
887
+ };
888
+ }
889
+ function useMergeCells({
890
+ props,
891
+ tableHeaderLast,
892
+ rowKeyGen,
893
+ colKeyGen,
894
+ virtual_dataSourcePart
895
+ }) {
896
+ const hiddenCellMap = ref({});
897
+ const hoverRowMap = ref({});
898
+ const hoverMergedCells = ref(/* @__PURE__ */ new Set());
899
+ const activeMergedCells = ref(/* @__PURE__ */ new Set());
900
+ watch([virtual_dataSourcePart, tableHeaderLast], () => {
901
+ hiddenCellMap.value = {};
902
+ hoverRowMap.value = {};
903
+ });
904
+ function hideCells(rowKey, startIndex, count, isSelfRow = false, mergeCellKey) {
905
+ for (let i = startIndex; i < startIndex + count; i++) {
906
+ if (!isSelfRow || i !== startIndex) {
907
+ const nextCol = tableHeaderLast.value[i];
908
+ if (!nextCol) break;
909
+ const nextColKey = colKeyGen.value(nextCol);
910
+ if (!hiddenCellMap.value[rowKey]) hiddenCellMap.value[rowKey] = /* @__PURE__ */ new Set();
911
+ hiddenCellMap.value[rowKey].add(nextColKey);
912
+ }
913
+ if (!hoverRowMap.value[rowKey]) hoverRowMap.value[rowKey] = /* @__PURE__ */ new Set();
914
+ hoverRowMap.value[rowKey].add(mergeCellKey);
915
+ }
916
+ }
917
+ function mergeCellsWrapper(row, col, rowIndex, colIndex) {
918
+ if (!col.mergeCells) return;
919
+ let { colspan, rowspan } = col.mergeCells({ row, col, rowIndex, colIndex }) || {};
920
+ colspan = colspan || 1;
921
+ rowspan = rowspan || 1;
922
+ if (colspan === 1 && rowspan === 1) return;
923
+ const rowKey = rowKeyGen(row);
924
+ const colKey = colKeyGen.value(col);
925
+ const curColIndex = tableHeaderLast.value.findIndex((item) => colKeyGen.value(item) === colKey);
926
+ const curRowIndex = virtual_dataSourcePart.value.findIndex((item) => rowKeyGen(item) === rowKey);
927
+ const mergedCellKey = pureCellKeyGen(rowKey, colKey);
928
+ if (curRowIndex === -1) return;
929
+ for (let i = curRowIndex; i < curRowIndex + rowspan; i++) {
930
+ const row2 = virtual_dataSourcePart.value[i];
931
+ if (!row2) break;
932
+ hideCells(rowKeyGen(row2), curColIndex, colspan, i === curRowIndex, mergedCellKey);
933
+ }
934
+ return { colspan, rowspan };
935
+ }
936
+ function updateHoverMergedCells(rowKey) {
937
+ const set = rowKey === void 0 ? null : hoverRowMap.value[rowKey];
938
+ hoverMergedCells.value = set || /* @__PURE__ */ new Set();
939
+ }
940
+ function updateActiveMergedCells(clear) {
941
+ if (!props.rowActive) return;
942
+ if (clear) {
943
+ activeMergedCells.value.clear();
944
+ } else {
945
+ activeMergedCells.value = new Set(hoverMergedCells.value);
946
+ }
947
+ }
948
+ return {
949
+ hiddenCellMap,
950
+ mergeCellsWrapper,
951
+ hoverMergedCells,
952
+ updateHoverMergedCells,
953
+ activeMergedCells,
954
+ updateActiveMergedCells
955
+ };
956
+ }
856
957
  function useRowExpand({ dataSourceCopy, rowKeyGen, emits }) {
958
+ const expandedKey = "__EXPANDED__";
959
+ function isExpanded(row, col) {
960
+ return (row == null ? void 0 : row[expandedKey]) === col ? !(row == null ? void 0 : row[expandedKey]) : true;
961
+ }
857
962
  function toggleExpandRow(row, col) {
858
- const isExpand = (row == null ? void 0 : row.__EXPANDED__) === col ? !(row == null ? void 0 : row.__EXPANDED__) : true;
963
+ const isExpand = isExpanded(row, col);
859
964
  setRowExpand(row, isExpand, { col });
860
965
  }
861
966
  function setRowExpand(rowKeyOrRow, expand, data) {
@@ -883,6 +988,9 @@ function useRowExpand({ dataSourceCopy, rowKeyGen, emits }) {
883
988
  }
884
989
  const row = tempData[index];
885
990
  const col = (data == null ? void 0 : data.col) || null;
991
+ if (expand == null) {
992
+ expand = isExpanded(row, col);
993
+ }
886
994
  if (expand) {
887
995
  const newExpandRow = {
888
996
  __ROW_KEY__: EXPANDED_ROW_KEY_PREFIX + rowKey,
@@ -1253,7 +1361,8 @@ function useVirtualScroll({
1253
1361
  dataSourceCopy,
1254
1362
  tableHeaderLast,
1255
1363
  tableHeaders,
1256
- rowKeyGen
1364
+ rowKeyGen,
1365
+ maxRowSpan
1257
1366
  }) {
1258
1367
  const tableHeaderHeight = ref(props.headerRowHeight);
1259
1368
  const virtualScroll = ref({
@@ -1458,6 +1567,32 @@ function useVirtualScroll({
1458
1567
  startIndex = Math.floor(sTop / rowHeight);
1459
1568
  endIndex = startIndex + pageSize;
1460
1569
  }
1570
+ if (maxRowSpan.size) {
1571
+ let correctedStartIndex = startIndex;
1572
+ let correctedEndIndex = endIndex;
1573
+ for (let i = 0; i < startIndex; i++) {
1574
+ const row = dataSourceCopyTemp[i];
1575
+ if (!row) continue;
1576
+ const spanEndIndex = i + (maxRowSpan.get(rowKeyGen(row)) || 1);
1577
+ if (spanEndIndex > startIndex) {
1578
+ correctedStartIndex = i;
1579
+ if (spanEndIndex > endIndex) {
1580
+ correctedEndIndex = spanEndIndex;
1581
+ }
1582
+ break;
1583
+ }
1584
+ }
1585
+ for (let i = correctedStartIndex; i < endIndex; i++) {
1586
+ const row = dataSourceCopyTemp[i];
1587
+ if (!row) continue;
1588
+ const spanEndIndex = i + (maxRowSpan.get(rowKeyGen(row)) || 1);
1589
+ if (spanEndIndex > correctedEndIndex) {
1590
+ correctedEndIndex = Math.max(spanEndIndex, correctedEndIndex);
1591
+ }
1592
+ }
1593
+ startIndex = correctedStartIndex;
1594
+ endIndex = correctedEndIndex;
1595
+ }
1461
1596
  if (stripe && startIndex > 0 && startIndex % 2) {
1462
1597
  startIndex -= 1;
1463
1598
  if (autoRowHeight || hasExpandCol.value) {
@@ -1561,77 +1696,6 @@ function useVirtualScroll({
1561
1696
  clearAllAutoHeight
1562
1697
  };
1563
1698
  }
1564
- function useMergeCells({
1565
- props,
1566
- tableHeaderLast,
1567
- rowKeyGen,
1568
- colKeyGen,
1569
- virtual_dataSourcePart
1570
- }) {
1571
- const hiddenCellMap = ref({});
1572
- const hoverRowMap = ref({});
1573
- const hoverMergedCells = ref(/* @__PURE__ */ new Set());
1574
- const activeMergedCells = ref(/* @__PURE__ */ new Set());
1575
- watch([virtual_dataSourcePart, tableHeaderLast], () => {
1576
- hiddenCellMap.value = {};
1577
- hoverRowMap.value = {};
1578
- });
1579
- function hideCells(rowKey, startIndex, count, isSelfRow = false, mergeCellKey) {
1580
- for (let i = startIndex; i < startIndex + count; i++) {
1581
- if (!isSelfRow || i !== startIndex) {
1582
- const nextCol = tableHeaderLast.value[i];
1583
- if (!nextCol) break;
1584
- const nextColKey = colKeyGen.value(nextCol);
1585
- if (!hiddenCellMap.value[rowKey]) hiddenCellMap.value[rowKey] = /* @__PURE__ */ new Set();
1586
- hiddenCellMap.value[rowKey].add(nextColKey);
1587
- }
1588
- if (!hoverRowMap.value[rowKey]) hoverRowMap.value[rowKey] = /* @__PURE__ */ new Set();
1589
- hoverRowMap.value[rowKey].add(mergeCellKey);
1590
- }
1591
- }
1592
- function mergeCellsWrapper(row, col, rowIndex, colIndex) {
1593
- if (!col.mergeCells) return;
1594
- let { colspan, rowspan } = col.mergeCells({ row, col, rowIndex, colIndex }) || {};
1595
- colspan = colspan || 1;
1596
- rowspan = rowspan || 1;
1597
- if (colspan === 1 && rowspan === 1) return;
1598
- const rowKey = rowKeyGen(row);
1599
- const colKey = colKeyGen.value(col);
1600
- const dataSourceSlice = virtual_dataSourcePart.value.slice();
1601
- const curColIndex = tableHeaderLast.value.findIndex((item) => colKeyGen.value(item) === colKey);
1602
- const curRowIndex = dataSourceSlice.findIndex((item) => rowKeyGen(item) === rowKey);
1603
- const mergedCellKey = pureCellKeyGen(rowKey, colKey);
1604
- if (curRowIndex === -1) return;
1605
- for (let i = curRowIndex; i < curRowIndex + rowspan; i++) {
1606
- const row2 = dataSourceSlice[i];
1607
- if (!row2) break;
1608
- const rKey = rowKeyGen(row2);
1609
- const isSelfRow = i === curRowIndex;
1610
- hideCells(rKey, curColIndex, colspan, isSelfRow, mergedCellKey);
1611
- }
1612
- return { colspan, rowspan };
1613
- }
1614
- function updateHoverMergedCells(rowKey) {
1615
- const set = rowKey === void 0 ? null : hoverRowMap.value[rowKey];
1616
- hoverMergedCells.value = set || /* @__PURE__ */ new Set();
1617
- }
1618
- function updateActiveMergedCells(clear) {
1619
- if (!props.rowActive) return;
1620
- if (clear) {
1621
- activeMergedCells.value.clear();
1622
- } else {
1623
- activeMergedCells.value = new Set(hoverMergedCells.value);
1624
- }
1625
- }
1626
- return {
1627
- hiddenCellMap,
1628
- mergeCellsWrapper,
1629
- hoverMergedCells,
1630
- updateHoverMergedCells,
1631
- activeMergedCells,
1632
- updateActiveMergedCells
1633
- };
1634
- }
1635
1699
  const _hoisted_1 = ["data-col-key", "draggable", "rowspan", "colspan", "title", "onClick"];
1636
1700
  const _hoisted_2 = ["onMousedown"];
1637
1701
  const _hoisted_3 = { class: "table-header-title" };
@@ -1759,6 +1823,7 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
1759
1823
  const { isSRBRActive } = useScrollRowByRow({ props, tableContainerRef });
1760
1824
  const { onThDragStart, onThDragOver, onThDrop, isHeaderDraggable } = useThDrag({ props, emits, colKeyGen });
1761
1825
  const { onTrDragStart, onTrDrop, onTrDragOver, onTrDragEnd, onTrDragEnter } = useTrDrag({ props, emits, dataSourceCopy });
1826
+ const { maxRowSpan, updateMaxRowSpan } = useMaxRowSpan({ props, tableHeaderLast, rowKeyGen, dataSourceCopy });
1762
1827
  const {
1763
1828
  virtualScroll,
1764
1829
  virtualScrollX,
@@ -1775,7 +1840,16 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
1775
1840
  updateVirtualScrollX,
1776
1841
  setAutoHeight,
1777
1842
  clearAllAutoHeight
1778
- } = useVirtualScroll({ tableContainerRef, trRef, props, dataSourceCopy, tableHeaderLast, tableHeaders, rowKeyGen });
1843
+ } = useVirtualScroll({ tableContainerRef, trRef, props, dataSourceCopy, tableHeaderLast, tableHeaders, rowKeyGen, maxRowSpan });
1844
+ const {
1845
+ hiddenCellMap,
1846
+ //
1847
+ mergeCellsWrapper,
1848
+ hoverMergedCells,
1849
+ updateHoverMergedCells,
1850
+ activeMergedCells,
1851
+ updateActiveMergedCells
1852
+ } = useMergeCells({ props, tableHeaderLast, rowKeyGen, colKeyGen, virtual_dataSourcePart });
1779
1853
  const getFixedColPosition = useGetFixedColPosition({ colKeyGen, tableHeadersForCalc });
1780
1854
  const getFixedStyle = useFixedStyle({
1781
1855
  props,
@@ -1817,17 +1891,11 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
1817
1891
  });
1818
1892
  const { toggleExpandRow, setRowExpand } = useRowExpand({ dataSourceCopy, rowKeyGen, emits });
1819
1893
  const { toggleTreeNode, setTreeExpand, flatTreeData } = useTree({ props, dataSourceCopy, rowKeyGen, emits });
1820
- const { hiddenCellMap, mergeCellsWrapper, hoverMergedCells, updateHoverMergedCells, activeMergedCells, updateActiveMergedCells } = useMergeCells({
1821
- props,
1822
- tableHeaderLast,
1823
- rowKeyGen,
1824
- colKeyGen,
1825
- virtual_dataSourcePart
1826
- });
1827
1894
  watch(
1828
1895
  () => props.columns,
1829
1896
  () => {
1830
1897
  dealColumns();
1898
+ updateMaxRowSpan();
1831
1899
  nextTick(() => {
1832
1900
  initVirtualScrollX();
1833
1901
  updateFixedShadow();
@@ -1870,6 +1938,7 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
1870
1938
  needInitVirtualScrollY = true;
1871
1939
  }
1872
1940
  initDataSource(val);
1941
+ updateMaxRowSpan();
1873
1942
  if (needInitVirtualScrollY) {
1874
1943
  nextTick(() => initVirtualScrollY());
1875
1944
  }
@@ -1887,6 +1956,7 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
1887
1956
  );
1888
1957
  dealColumns();
1889
1958
  initDataSource();
1959
+ updateMaxRowSpan();
1890
1960
  onMounted(() => {
1891
1961
  initVirtualScroll();
1892
1962
  updateFixedShadow();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "stk-table-vue",
3
- "version": "0.8.2",
3
+ "version": "0.8.4",
4
4
  "description": "Simple 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",
@@ -293,13 +293,14 @@ import { useFixedStyle } from './useFixedStyle';
293
293
  import { useGetFixedColPosition } from './useGetFixedColPosition';
294
294
  import { useHighlight } from './useHighlight';
295
295
  import { useKeyboardArrowScroll } from './useKeyboardArrowScroll';
296
+ import { useMaxRowSpan } from './useMaxRowSpan';
297
+ import { useMergeCells } from './useMergeCells';
296
298
  import { useRowExpand } from './useRowExpand';
297
299
  import { useScrollRowByRow } from './useScrollRowByRow';
298
300
  import { useThDrag } from './useThDrag';
299
301
  import { useTrDrag } from './useTrDrag';
300
302
  import { useTree } from './useTree';
301
303
  import { useVirtualScroll } from './useVirtualScroll';
302
- import { useMergeCells } from './useMergeCells';
303
304
  import { createStkTableId, getCalculatedColWidth, getColWidth } from './utils/constRefUtils';
304
305
  import { howDeepTheHeader, isEmptyValue, tableSort, transformWidthToStr } from './utils/index';
305
306
 
@@ -687,7 +688,7 @@ const sortSwitchOrder: Order[] = [null, 'desc', 'asc'];
687
688
  * @eg
688
689
  * ```js
689
690
  * [
690
- * [{dataInex:'id',...}], // 第0行列配置
691
+ * [{dataIndex:'id',...}], // 第0行列配置
691
692
  * [], // 第一行列配置
692
693
  * //...
693
694
  * ]
@@ -761,6 +762,8 @@ const { onThDragStart, onThDragOver, onThDrop, isHeaderDraggable } = useThDrag({
761
762
 
762
763
  const { onTrDragStart, onTrDrop, onTrDragOver, onTrDragEnd, onTrDragEnter } = useTrDrag({ props, emits, dataSourceCopy });
763
764
 
765
+ const { maxRowSpan, updateMaxRowSpan } = useMaxRowSpan({ props, tableHeaderLast, rowKeyGen, dataSourceCopy });
766
+
764
767
  const {
765
768
  virtualScroll,
766
769
  virtualScrollX,
@@ -777,7 +780,16 @@ const {
777
780
  updateVirtualScrollX,
778
781
  setAutoHeight,
779
782
  clearAllAutoHeight,
780
- } = useVirtualScroll({ tableContainerRef, trRef, props, dataSourceCopy, tableHeaderLast, tableHeaders, rowKeyGen });
783
+ } = useVirtualScroll({ tableContainerRef, trRef, props, dataSourceCopy, tableHeaderLast, tableHeaders, rowKeyGen, maxRowSpan });
784
+
785
+ const {
786
+ hiddenCellMap, //
787
+ mergeCellsWrapper,
788
+ hoverMergedCells,
789
+ updateHoverMergedCells,
790
+ activeMergedCells,
791
+ updateActiveMergedCells,
792
+ } = useMergeCells({ props, tableHeaderLast, rowKeyGen, colKeyGen, virtual_dataSourcePart });
781
793
 
782
794
  const getFixedColPosition = useGetFixedColPosition({ colKeyGen, tableHeadersForCalc });
783
795
 
@@ -831,19 +843,12 @@ const { toggleExpandRow, setRowExpand } = useRowExpand({ dataSourceCopy, rowKeyG
831
843
 
832
844
  const { toggleTreeNode, setTreeExpand, flatTreeData } = useTree({ props, dataSourceCopy, rowKeyGen, emits });
833
845
 
834
- const { hiddenCellMap, mergeCellsWrapper, hoverMergedCells, updateHoverMergedCells, activeMergedCells, updateActiveMergedCells } = useMergeCells({
835
- props,
836
- tableHeaderLast,
837
- rowKeyGen,
838
- colKeyGen,
839
- virtual_dataSourcePart,
840
- });
841
-
842
846
  watch(
843
847
  () => props.columns,
844
848
  () => {
845
849
  dealColumns();
846
- // initVirtualScrollX 需要获取容器滚动宽度等。必须等渲染完成后再调用。因此使用nextTick。
850
+ updateMaxRowSpan();
851
+ // nextTick: initVirtualScrollX need get container width。
847
852
  nextTick(() => {
848
853
  initVirtualScrollX();
849
854
  updateFixedShadow();
@@ -885,12 +890,13 @@ watch(
885
890
  console.warn('invalid dataSource');
886
891
  return;
887
892
  }
888
- /** 是否需要更新ScrollY,这里由于watch newValue与oldValue 的长度一样,因此需要这样使用 */
893
+
889
894
  let needInitVirtualScrollY = false;
890
895
  if (dataSourceCopy.value.length !== val.length) {
891
896
  needInitVirtualScrollY = true;
892
897
  }
893
898
  initDataSource(val);
899
+ updateMaxRowSpan();
894
900
  // if data length is not change, not init virtual scroll
895
901
  if (needInitVirtualScrollY) {
896
902
  // wait for table render,initVirtualScrollY has get `dom` operation.
@@ -913,6 +919,7 @@ watch(
913
919
 
914
920
  dealColumns();
915
921
  initDataSource();
922
+ updateMaxRowSpan();
916
923
 
917
924
  onMounted(() => {
918
925
  initVirtualScroll();
@@ -0,0 +1,56 @@
1
+ import { ShallowRef } from "vue";
2
+ import { PrivateStkTableColumn, RowKeyGen, UniqKey } from "./types";
3
+
4
+ type Options = {
5
+ props:any,
6
+ tableHeaderLast: ShallowRef<PrivateStkTableColumn<any>[]>;
7
+ rowKeyGen: RowKeyGen;
8
+ dataSourceCopy: ShallowRef<any[]>;
9
+ }
10
+
11
+ export function useMaxRowSpan({ props, tableHeaderLast, rowKeyGen, dataSourceCopy }: Options) {
12
+ /** max rowspan of each row */
13
+ const maxRowSpan = new Map<UniqKey, number>();
14
+
15
+ /**
16
+ * Use dataSourceCopy and tableHeaderLast to calculate maxRowSpan
17
+ * @link {maxRowSpan}
18
+ */
19
+ function updateMaxRowSpan() {
20
+ if(!props.virtual) {
21
+ if(maxRowSpan.size) maxRowSpan.clear();
22
+ return;
23
+ }
24
+ maxRowSpan.clear();
25
+
26
+ const data = dataSourceCopy.value;
27
+ const columns = tableHeaderLast.value;
28
+
29
+ const columnsWithMerge = columns.filter(col => col.mergeCells);
30
+ if (!columnsWithMerge.length) return;
31
+
32
+ const dataLength = data.length;
33
+ const mergeColumnsLength = columnsWithMerge.length;
34
+
35
+ for (let rowIndex = 0; rowIndex < dataLength; rowIndex++) {
36
+ const row = data[rowIndex];
37
+ const rowKey = rowKeyGen(row);
38
+ let currentMax = maxRowSpan.get(rowKey) || 0;
39
+
40
+ for (let colIndex = 0; colIndex < mergeColumnsLength; colIndex++) {
41
+ const col = columnsWithMerge[colIndex];
42
+ const { rowspan = 1 } = col.mergeCells!({ row, col, rowIndex, colIndex }) || {};
43
+
44
+ if (rowspan > 1 && rowspan > currentMax) {
45
+ currentMax = rowspan;
46
+ maxRowSpan.set(rowKey, currentMax);
47
+ }
48
+ }
49
+ }
50
+ }
51
+
52
+ return {
53
+ maxRowSpan,
54
+ updateMaxRowSpan
55
+ }
56
+ }
@@ -1,20 +1,20 @@
1
1
  import { ref, ShallowRef, watch } from 'vue';
2
2
  import { ColKeyGen, MergeCellsParam, PrivateStkTableColumn, RowKeyGen, UniqKey } from './types';
3
3
  import { pureCellKeyGen } from './utils';
4
-
4
+ type Options = {
5
+ props: any;
6
+ tableHeaderLast: ShallowRef<PrivateStkTableColumn<any>[]>;
7
+ rowKeyGen: RowKeyGen;
8
+ colKeyGen: ColKeyGen;
9
+ virtual_dataSourcePart: ShallowRef<any[]>;
10
+ }
5
11
  export function useMergeCells({
6
12
  props,
7
13
  tableHeaderLast,
8
14
  rowKeyGen,
9
15
  colKeyGen,
10
16
  virtual_dataSourcePart,
11
- }: {
12
- props: any;
13
- tableHeaderLast: ShallowRef<PrivateStkTableColumn<any>[]>;
14
- rowKeyGen: RowKeyGen;
15
- colKeyGen: ColKeyGen;
16
- virtual_dataSourcePart: ShallowRef<any[]>;
17
- }) {
17
+ }:Options ) {
18
18
  /**
19
19
  * which cell need be hidden
20
20
  * - key: rowKey
@@ -33,16 +33,19 @@ export function useMergeCells({
33
33
  /** click current row , which rowspan cells should be highlight */
34
34
  const activeMergedCells = ref(new Set<string>());
35
35
 
36
+
36
37
  watch([virtual_dataSourcePart, tableHeaderLast], () => {
37
38
  hiddenCellMap.value = {};
38
39
  hoverRowMap.value = {};
39
40
  });
40
41
 
41
- /** 抽象隐藏单元格的逻辑 */
42
+ /**
43
+ * abstract the logic of hiding cells
44
+ */
42
45
  function hideCells(rowKey: UniqKey, startIndex: number, count: number, isSelfRow = false, mergeCellKey: string) {
43
46
  for (let i = startIndex; i < startIndex + count; i++) {
44
47
  if (!isSelfRow || i !== startIndex) {
45
- // 自己不需要隐藏
48
+ // self row does not need to be hidden
46
49
  const nextCol = tableHeaderLast.value[i];
47
50
  if (!nextCol) break;
48
51
  const nextColKey = colKeyGen.value(nextCol);
@@ -81,20 +84,18 @@ export function useMergeCells({
81
84
  if (colspan === 1 && rowspan === 1) return;
82
85
 
83
86
  const rowKey = rowKeyGen(row);
87
+
84
88
  const colKey = colKeyGen.value(col);
85
- const dataSourceSlice = virtual_dataSourcePart.value.slice();
86
89
  const curColIndex = tableHeaderLast.value.findIndex(item => colKeyGen.value(item) === colKey);
87
- const curRowIndex = dataSourceSlice.findIndex(item => rowKeyGen(item) === rowKey);
90
+ const curRowIndex = virtual_dataSourcePart.value.findIndex(item => rowKeyGen(item) === rowKey);
88
91
  const mergedCellKey = pureCellKeyGen(rowKey, colKey);
89
92
 
90
93
  if (curRowIndex === -1) return;
91
-
94
+
92
95
  for (let i = curRowIndex; i < curRowIndex + rowspan; i++) {
93
- const row = dataSourceSlice[i];
96
+ const row = virtual_dataSourcePart.value[i];
94
97
  if (!row) break;
95
- const rKey = rowKeyGen(row);
96
- const isSelfRow = i === curRowIndex;
97
- hideCells(rKey, curColIndex, colspan, isSelfRow, mergedCellKey);
98
+ hideCells(rowKeyGen(row), curColIndex, colspan, i === curRowIndex, mergedCellKey);
98
99
  }
99
100
 
100
101
  return { colspan, rowspan };
@@ -9,26 +9,32 @@ type Option<DT extends Record<string, any>> = {
9
9
  };
10
10
 
11
11
  export function useRowExpand({ dataSourceCopy, rowKeyGen, emits }: Option<DT>) {
12
+ const expandedKey = '__EXPANDED__';
13
+
14
+ function isExpanded(row: DT, col?: StkTableColumn<DT> | null) {
15
+ return row?.[expandedKey] === col ? !row?.[expandedKey] : true;
16
+ }
12
17
  /** click expended icon to toggle expand row */
13
18
  function toggleExpandRow(row: DT, col: StkTableColumn<DT>) {
14
- const isExpand = row?.__EXPANDED__ === col ? !row?.__EXPANDED__ : true;
19
+ const isExpand = isExpanded(row, col);
15
20
  setRowExpand(row, isExpand, { col });
16
21
  }
17
22
 
18
23
  /**
19
24
  *
20
25
  * @param rowKeyOrRow rowKey or row
21
- * @param expand expand or collapse
26
+ * @param expand expand or collapse, if set null, toggle expand
22
27
  * @param data { col?: StkTableColumn<DT> }
23
28
  * @param data.silent if set true, not emit `toggle-row-expand`, default:false
24
29
  */
25
- function setRowExpand(rowKeyOrRow: string | undefined | DT, expand?: boolean, data?: { col?: StkTableColumn<DT>; silent?: boolean }) {
30
+ function setRowExpand(rowKeyOrRow: string | undefined | DT, expand?: boolean | null, data?: { col?: StkTableColumn<DT>; silent?: boolean }) {
26
31
  let rowKey: UniqKey;
27
32
  if (typeof rowKeyOrRow === 'string') {
28
33
  rowKey = rowKeyOrRow;
29
34
  } else {
30
35
  rowKey = rowKeyGen(rowKeyOrRow);
31
36
  }
37
+
32
38
  const tempData = dataSourceCopy.value.slice();
33
39
  const index = tempData.findIndex(it => rowKeyGen(it) === rowKey);
34
40
  if (index === -1) {
@@ -51,6 +57,10 @@ export function useRowExpand({ dataSourceCopy, rowKeyGen, emits }: Option<DT>) {
51
57
  const row = tempData[index];
52
58
  const col = data?.col || null;
53
59
 
60
+ if (expand == null) {
61
+ expand = isExpanded(row, col);
62
+ }
63
+
54
64
  if (expand) {
55
65
  // insert new expanded row
56
66
  const newExpandRow: ExpandedRow = {
@@ -11,6 +11,7 @@ type Option<DT extends Record<string, any>> = {
11
11
  tableHeaderLast: ShallowRef<PrivateStkTableColumn<DT>[]>;
12
12
  tableHeaders: ShallowRef<PrivateStkTableColumn<DT>[][]>;
13
13
  rowKeyGen: RowKeyGen;
14
+ maxRowSpan: Map<UniqKey, number>;
14
15
  };
15
16
 
16
17
  /** 暂存纵向虚拟滚动的数据 */
@@ -52,7 +53,7 @@ export type VirtualScrollXStore = {
52
53
  const VUE2_SCROLL_TIMEOUT_MS = 200;
53
54
 
54
55
  /**
55
- * 虚拟滚动
56
+ * virtual scroll
56
57
  * @param param0
57
58
  * @returns
58
59
  */
@@ -64,8 +65,8 @@ export function useVirtualScroll<DT extends Record<string, any>>({
64
65
  tableHeaderLast,
65
66
  tableHeaders,
66
67
  rowKeyGen,
68
+ maxRowSpan,
67
69
  }: Option<DT>) {
68
- /** 表头高度 */
69
70
  const tableHeaderHeight = ref(props.headerRowHeight);
70
71
 
71
72
  const virtualScroll = ref<VirtualScrollStore>({
@@ -279,7 +280,6 @@ export function useVirtualScroll<DT extends Record<string, any>>({
279
280
  // 先更新滚动条位置记录,其他地方有依赖。(stripe 时ArrowUp/Down滚动依赖)
280
281
  virtualScroll.value.scrollTop = sTop;
281
282
 
282
- // 非虚拟滚动不往下执行
283
283
  if (!virtual_on.value) {
284
284
  return;
285
285
  }
@@ -325,6 +325,41 @@ export function useVirtualScroll<DT extends Record<string, any>>({
325
325
  endIndex = startIndex + pageSize;
326
326
  }
327
327
 
328
+ if (maxRowSpan.size) {
329
+ // fix startIndex:查找是否有合并行跨越当前startIndex
330
+ let correctedStartIndex = startIndex;
331
+ let correctedEndIndex = endIndex;
332
+
333
+ for (let i = 0; i < startIndex; i++) {
334
+ const row = dataSourceCopyTemp[i];
335
+ if (!row) continue;
336
+ const spanEndIndex = i + (maxRowSpan.get(rowKeyGen(row)) || 1);
337
+ if (spanEndIndex > startIndex) {
338
+ // 找到跨越startIndex的合并行,将startIndex修正为合并行的起始索引
339
+ correctedStartIndex = i;
340
+ if (spanEndIndex > endIndex) {
341
+ // 合并行跨越了整个可视区
342
+ correctedEndIndex = spanEndIndex;
343
+ }
344
+ break;
345
+ }
346
+ }
347
+
348
+ // fix endIndex:查找是否有合并行跨越当前endIndex
349
+ for (let i = correctedStartIndex; i < endIndex; i++) {
350
+ const row = dataSourceCopyTemp[i];
351
+ if (!row) continue;
352
+ const spanEndIndex = i + (maxRowSpan.get(rowKeyGen(row)) || 1);
353
+ if (spanEndIndex > correctedEndIndex) {
354
+ // 找到跨越endIndex的合并行,将endIndex修正为合并行的结束索引
355
+ correctedEndIndex = Math.max(spanEndIndex, correctedEndIndex);
356
+ }
357
+ }
358
+
359
+ startIndex = correctedStartIndex;
360
+ endIndex = correctedEndIndex;
361
+ }
362
+
328
363
  if (stripe && startIndex > 0 && startIndex % 2) {
329
364
  // 斑马纹情况下,每滚动偶数行才加载。防止斑马纹错位。
330
365
  startIndex -= 1; // 奇数-1变成偶数
@@ -338,7 +373,7 @@ export function useVirtualScroll<DT extends Record<string, any>>({
338
373
  endIndex = Math.min(endIndex, dataLength);
339
374
 
340
375
  if (startIndex >= endIndex) {
341
- // 兜底,不一定会执行到这里
376
+ // fallback
342
377
  startIndex = endIndex - pageSize;
343
378
  }
344
379
 
@@ -351,20 +386,20 @@ export function useVirtualScroll<DT extends Record<string, any>>({
351
386
  offsetTop = autoRowHeightTop;
352
387
  } else {
353
388
  if (oldStartIndex === startIndex && oldEndIndex === endIndex) {
354
- // 没有变化,不需要更新
389
+ // Not change: not update
355
390
  return;
356
391
  }
357
392
  offsetTop = startIndex * rowHeight;
358
393
  }
359
394
 
360
395
  /**
361
- * 一次滚动大于一页时表示滚动过快,回退优化
396
+ * en: If scroll faster than one page, roll back
362
397
  */
363
398
  if (!optimizeVue2Scroll || sTop <= scrollTop || Math.abs(oldStartIndex - startIndex) >= pageSize) {
364
- // 向上滚动
399
+ // scroll up
365
400
  Object.assign(virtualScroll.value, { startIndex, endIndex, offsetTop });
366
401
  } else {
367
- // vue2向下滚动优化
402
+ // vue2 scroll down optimize
368
403
  virtualScroll.value.endIndex = endIndex;
369
404
  vue2ScrollYTimeout = window.setTimeout(() => {
370
405
  Object.assign(virtualScroll.value, { startIndex, offsetTop });
@@ -374,7 +409,9 @@ export function useVirtualScroll<DT extends Record<string, any>>({
374
409
 
375
410
  let vue2ScrollXTimeout: null | number = null;
376
411
 
377
- /** 通过横向滚动条位置,计算横向虚拟滚动的参数 */
412
+ /**
413
+ * Calculate virtual scroll parameters based on horizontal scroll bar position
414
+ */
378
415
  function updateVirtualScrollX(sLeft = 0) {
379
416
  if (!props.virtualX) return;
380
417
  const tableHeaderLastValue = tableHeaderLast.value;
@@ -384,7 +421,6 @@ export function useVirtualScroll<DT extends Record<string, any>>({
384
421
  const { scrollLeft, containerWidth } = virtualScrollX.value;
385
422
  let startIndex = 0;
386
423
  let offsetLeft = 0;
387
- /** 列宽累加 */
388
424
  let colWidthSum = 0;
389
425
  /** 固定左侧列宽 */
390
426
  let leftColWidthSum = 0;