stk-table-vue 0.0.1-beta.6 → 0.0.1-beta.7

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
@@ -11,7 +11,8 @@ js体积(未压缩41kb)
11
11
  * [x] 表头列宽拖动。
12
12
  * [x] 多级表头。(不支持横向虚拟滚动)
13
13
  * [x] 支持table-layout: fixed 配置。
14
- * [] 鼠标点击后,键盘上下滚动表格
14
+ * [x] 鼠标悬浮在表格上,键盘上下左右滚动虚拟表格
15
+ * [] 列固定阴影
15
16
 
16
17
  ## Usage
17
18
  > npm install stk-table-vue
@@ -36,80 +37,57 @@ const stkTable = ref<InstanceType<typeof StkTable>>();
36
37
  ```ts
37
38
  export type StkProps = Partial<{
38
39
  width: string;
39
-
40
40
  /** 最小表格宽度 */
41
41
  minWidth: string;
42
-
43
42
  /** 表格最大宽度*/
44
43
  maxWidth: string;
45
-
46
44
  /** 是否使用 table-layout:fixed */
47
45
  fixedMode: boolean;
48
-
49
46
  /** 是否隐藏表头 */
50
47
  headless: boolean;
51
-
52
48
  /** 主题,亮、暗 */
53
49
  theme: 'light' | 'dark';
54
-
55
50
  /** 虚拟滚动 */
56
51
  virtual: boolean;
57
-
58
52
  /** x轴虚拟滚动 */
59
53
  virtualX: boolean;
60
-
61
54
  /** 表格列配置 */
62
55
  columns: StkTableColumn<any>[];
63
-
64
56
  /** 表格数据源 */
65
57
  dataSource: any[];
66
-
67
58
  /** 行唯一键 */
68
59
  rowKey: UniqKey;
69
-
70
60
  /** 列唯一键 */
71
61
  colKey: UniqKey;
72
-
73
62
  /** 空值展示文字 */
74
63
  emptyCellText: string;
75
-
76
64
  /** 暂无数据兜底高度是否撑满 */
77
65
  noDataFull: boolean;
78
-
79
66
  /** 是否展示暂无数据 */
80
67
  showNoData: boolean;
81
-
82
68
  /** 是否服务端排序,true则不排序数据 */
83
69
  sortRemote: boolean;
84
-
85
70
  /** 表头是否溢出展示... */
86
71
  showHeaderOverflow: boolean;
87
-
88
72
  /** 表体溢出是否展示... */
89
73
  showOverflow: boolean;
90
-
91
74
  /** 是否增加行hover class */
92
75
  showTrHoverClass: boolean;
93
-
94
76
  /** 表头是否可拖动 */
95
77
  headerDrag: boolean;
96
-
97
78
  /**
98
79
  * 给行附加className<br>
99
80
  * FIXME: 是否需要优化,因为不传此prop会使表格行一直执行空函数,是否有影响
100
81
  */
101
82
  rowClassName: (row: any, i: number) => string;
102
-
103
83
  /**
104
84
  * 列宽是否可拖动<br>
105
85
  * **不要设置**列minWidth,**必须**设置width<br>
106
86
  * 列宽拖动时,每一列都必须要有width,且minWidth/maxWidth不生效。table width会变为"fit-content"。
107
87
  */
108
88
  colResizable: boolean;
109
-
110
89
  /** 可拖动至最小的列宽 */
111
90
  colMinWidth: number;
112
-
113
91
  /**
114
92
  * 单元格分割线。
115
93
  * 默认横竖都有
@@ -118,7 +96,6 @@ export type StkProps = Partial<{
118
96
  * "body-v" - 仅表体展示竖线
119
97
  */
120
98
  bordered: boolean | 'h' | 'v' | 'body-v';
121
-
122
99
  /** 自动重新计算虚拟滚动高度宽度。默认true */
123
100
  autoResize: boolean;
124
101
  }>;
@@ -126,7 +103,7 @@ export type StkProps = Partial<{
126
103
 
127
104
  ### StkTableColumn
128
105
  ``` ts
129
- type Sorter = boolean | Function;
106
+ type Sorter<T> = boolean | ((data: T[], option: { order: Order; column: any }) => T[]);
130
107
  /** 表格列配置 */
131
108
  export type StkTableColumn<T extends Record<string, any>> = {
132
109
  /** 取值id */
@@ -138,7 +115,7 @@ export type StkTableColumn<T extends Record<string, any>> = {
138
115
  /** 表头内容对齐方式 */
139
116
  headerAlign?: 'right' | 'left' | 'center';
140
117
  /** 筛选 */
141
- sorter?: Sorter;
118
+ sorter?: Sorter<T>;
142
119
  /** 列宽。横向虚拟滚动时必须设置。 */
143
120
  width?: string;
144
121
  /** 最小列宽。非x虚拟滚动生效。 */
@@ -1,9 +1,4 @@
1
1
  import { SortOption, StkTableColumn } from './types/index';
2
- /**
3
- * 初始化虚拟滚动参数
4
- * @param {number} [height] 虚拟滚动的高度
5
- */
6
- declare function initVirtualScroll(height?: number): void;
7
2
  /**
8
3
  * 选中一行,
9
4
  * @param {string} rowKey
@@ -44,6 +39,7 @@ declare const _default: __VLS_WithTemplateSlots<import("vue").DefineComponent<__
44
39
  theme: "light" | "dark";
45
40
  virtual: boolean;
46
41
  virtualX: boolean;
42
+ /** 排序切换顺序 */
47
43
  columns: StkTableColumn<any>[];
48
44
  dataSource: any[];
49
45
  rowKey: import("./types/index").UniqKey;
@@ -88,7 +84,7 @@ declare const _default: __VLS_WithTemplateSlots<import("vue").DefineComponent<__
88
84
  bordered: boolean;
89
85
  autoResize: boolean;
90
86
  }>, {
91
- initVirtualScroll: typeof initVirtualScroll;
87
+ initVirtualScroll: (height?: number | undefined) => void;
92
88
  initVirtualScrollX: () => void;
93
89
  initVirtualScrollY: (height?: number | undefined) => void;
94
90
  setCurrentRow: typeof setCurrentRow;
@@ -122,6 +118,7 @@ declare const _default: __VLS_WithTemplateSlots<import("vue").DefineComponent<__
122
118
  theme: "light" | "dark";
123
119
  virtual: boolean;
124
120
  virtualX: boolean;
121
+ /** 排序切换顺序 */
125
122
  columns: StkTableColumn<any>[];
126
123
  dataSource: any[];
127
124
  rowKey: import("./types/index").UniqKey;
@@ -1,10 +1,10 @@
1
1
  import { Component, VNode } from 'vue';
2
2
  /** 排序方式,asc-正序,desc-倒序,null-默认顺序 */
3
3
  export type Order = null | 'asc' | 'desc';
4
- type Sorter = boolean | ((data: any[], option: {
4
+ type Sorter<T> = boolean | ((data: T[], option: {
5
5
  order: Order;
6
6
  column: any;
7
- }) => any[]);
7
+ }) => T[]);
8
8
  export type CustomCellFunc<T extends Record<string, any>> = (props: {
9
9
  row: T;
10
10
  col: StkTableColumn<T>;
@@ -24,7 +24,7 @@ export type StkTableColumn<T extends Record<string, any>> = {
24
24
  /** 表头内容对齐方式 */
25
25
  headerAlign?: 'right' | 'left' | 'center';
26
26
  /** 筛选 */
27
- sorter?: Sorter;
27
+ sorter?: Sorter<T>;
28
28
  /** 列宽。横向虚拟滚动时必须设置。 */
29
29
  width?: string;
30
30
  /** 最小列宽。非x虚拟滚动生效。 */
@@ -0,0 +1,14 @@
1
+ import { Ref } from 'vue';
2
+ import { VirtualScrollStore, VirtualScrollXStore } from './useVirtualScroll';
3
+ type Options = {
4
+ scrollTo: (x: number | null, y: number | null) => void;
5
+ virtualScroll: Ref<VirtualScrollStore>;
6
+ virtualScrollX: Ref<VirtualScrollXStore>;
7
+ };
8
+ /**
9
+ * 按下键盘箭头滚动。只有悬浮在表体上才能生效键盘。
10
+ *
11
+ * 在低版本浏览器中,虚拟滚动时,使用键盘滚动,等选中的行消失在视口外时,滚动会失效。
12
+ */
13
+ export declare function useKeyboardArrowScroll(targetElement: Ref<HTMLElement | undefined>, { scrollTo, virtualScroll, virtualScrollX }: Options): void;
14
+ export {};
@@ -6,6 +6,37 @@ type Option = {
6
6
  dataSourceCopy: ShallowRef<any[]>;
7
7
  tableHeaderLast: Ref<StkTableColumn<any>[]>;
8
8
  };
9
+ /** 暂存纵向虚拟滚动的数据 */
10
+ export type VirtualScrollStore = {
11
+ /** 容器高度 */
12
+ containerHeight: number;
13
+ /** 数组开始位置 */
14
+ startIndex: number;
15
+ /** 行高 */
16
+ rowHeight: number;
17
+ /** 表格定位上边距 */
18
+ offsetTop: number;
19
+ /** 纵向滚动条位置,用于判断是横向滚动还是纵向 */
20
+ scrollTop: number;
21
+ };
22
+ /** 暂存横向虚拟滚动的数据 */
23
+ export type VirtualScrollXStore = {
24
+ /** 容器宽度 */
25
+ containerWidth: number;
26
+ /** 开始位置 */
27
+ startIndex: number;
28
+ /** 结束始位置 */
29
+ endIndex: number;
30
+ /** 表格定位左边距 */
31
+ offsetLeft: number;
32
+ /** 横向滚动位置,用于判断是横向滚动还是纵向 */
33
+ scrollLeft: number;
34
+ };
35
+ /**
36
+ * 虚拟滚动
37
+ * @param param0
38
+ * @returns
39
+ */
9
40
  export declare function useVirtualScroll({ tableContainer, props, dataSourceCopy, tableHeaderLast }: Option): {
10
41
  virtualScroll: Ref<{
11
42
  containerHeight: number;
@@ -27,6 +58,7 @@ export declare function useVirtualScroll({ tableContainer, props, dataSourceCopy
27
58
  virtualX_on: import("vue").ComputedRef<any>;
28
59
  virtualX_columnPart: import("vue").ComputedRef<StkTableColumn<any>[]>;
29
60
  virtualX_offsetRight: import("vue").ComputedRef<number>;
61
+ initVirtualScroll: (height?: number) => void;
30
62
  initVirtualScrollY: (height?: number) => void;
31
63
  initVirtualScrollX: () => void;
32
64
  updateVirtualScrollY: (sTop?: number) => void;
@@ -280,6 +280,52 @@ function useHighlight({ props, tableContainer, rowKeyGen }) {
280
280
  setHighlightDimCell
281
281
  };
282
282
  }
283
+ const ARROW_CODES = ["ArrowUp", "ArrowRight", "ArrowDown", "ArrowLeft"];
284
+ function useKeyboardArrowScroll(targetElement, { scrollTo, virtualScroll, virtualScrollX }) {
285
+ let isMouseOver = false;
286
+ onMounted(() => {
287
+ var _a, _b, _c;
288
+ window.addEventListener("keydown", handleKeydown);
289
+ (_a = targetElement.value) == null ? void 0 : _a.addEventListener("mouseenter", handleMouseEnter);
290
+ (_b = targetElement.value) == null ? void 0 : _b.addEventListener("mouseleave", handleMouseLeave);
291
+ (_c = targetElement.value) == null ? void 0 : _c.addEventListener("mousedown", handleMouseDown);
292
+ });
293
+ onBeforeUnmount(() => {
294
+ var _a, _b, _c;
295
+ window.removeEventListener("keydown", handleKeydown);
296
+ (_a = targetElement.value) == null ? void 0 : _a.removeEventListener("mouseenter", handleMouseEnter);
297
+ (_b = targetElement.value) == null ? void 0 : _b.removeEventListener("mouseleave", handleMouseLeave);
298
+ (_c = targetElement.value) == null ? void 0 : _c.removeEventListener("mousedown", handleMouseDown);
299
+ });
300
+ function handleKeydown(e) {
301
+ if (!ARROW_CODES.includes(e.code))
302
+ return;
303
+ if (!isMouseOver)
304
+ return;
305
+ e.preventDefault();
306
+ const { scrollTop, rowHeight } = virtualScroll.value;
307
+ const { scrollLeft } = virtualScrollX.value;
308
+ if (e.code === ARROW_CODES[0]) {
309
+ scrollTo(scrollTop - rowHeight, null);
310
+ } else if (e.code === ARROW_CODES[1]) {
311
+ scrollTo(null, scrollLeft + rowHeight);
312
+ } else if (e.code === ARROW_CODES[2]) {
313
+ scrollTo(scrollTop + rowHeight, null);
314
+ } else if (e.code === ARROW_CODES[3]) {
315
+ scrollTo(null, scrollLeft - rowHeight);
316
+ }
317
+ }
318
+ function handleMouseEnter() {
319
+ isMouseOver = true;
320
+ }
321
+ function handleMouseLeave() {
322
+ isMouseOver = false;
323
+ }
324
+ function handleMouseDown() {
325
+ if (!isMouseOver)
326
+ isMouseOver = true;
327
+ }
328
+ }
283
329
  function useThDrag({ emit }) {
284
330
  let dragStartKey = void 0;
285
331
  function onThDragStart(e) {
@@ -311,12 +357,9 @@ function useVirtualScroll({ tableContainer, props, dataSourceCopy, tableHeaderLa
311
357
  const virtualScroll = ref({
312
358
  containerHeight: 0,
313
359
  startIndex: 0,
314
- // 数组开始位置
315
360
  rowHeight: 28,
316
361
  offsetTop: 0,
317
- // 表格定位上边距
318
362
  scrollTop: 0
319
- // 纵向滚动条位置,用于判断是横向滚动还是纵向
320
363
  });
321
364
  const virtualScrollX = ref({
322
365
  containerWidth: 0,
@@ -324,7 +367,6 @@ function useVirtualScroll({ tableContainer, props, dataSourceCopy, tableHeaderLa
324
367
  endIndex: 0,
325
368
  offsetLeft: 0,
326
369
  scrollLeft: 0
327
- // 横向滚动位置,用于判断是横向滚动还是纵向
328
370
  });
329
371
  const virtual_on = computed(() => {
330
372
  return props.virtual && dataSourceCopy.value.length > virtual_pageSize.value * 2;
@@ -375,11 +417,15 @@ function useVirtualScroll({ tableContainer, props, dataSourceCopy, tableHeaderLa
375
417
  return width;
376
418
  });
377
419
  function initVirtualScrollY(height) {
378
- var _a, _b;
379
420
  if (!virtual_on.value)
380
421
  return;
381
- virtualScroll.value.containerHeight = typeof height === "number" ? height : ((_a = tableContainer.value) == null ? void 0 : _a.offsetHeight) || Default_Table_Height;
382
- updateVirtualScrollY((_b = tableContainer.value) == null ? void 0 : _b.scrollTop);
422
+ const { offsetHeight, scrollTop } = tableContainer.value || {};
423
+ if (typeof height === "number") {
424
+ virtualScroll.value.containerHeight = height;
425
+ } else {
426
+ virtualScroll.value.containerHeight = offsetHeight || Default_Table_Height;
427
+ }
428
+ updateVirtualScrollY(scrollTop);
383
429
  }
384
430
  function initVirtualScrollX() {
385
431
  if (!props.virtualX)
@@ -388,6 +434,10 @@ function useVirtualScroll({ tableContainer, props, dataSourceCopy, tableHeaderLa
388
434
  virtualScrollX.value.containerWidth = offsetWidth || Default_Table_Width;
389
435
  updateVirtualScrollX(scrollLeft);
390
436
  }
437
+ function initVirtualScroll(height) {
438
+ initVirtualScrollY(height);
439
+ initVirtualScrollX();
440
+ }
391
441
  function updateVirtualScrollY(sTop = 0) {
392
442
  const { rowHeight } = virtualScroll.value;
393
443
  const startIndex = Math.floor(sTop / rowHeight);
@@ -438,6 +488,7 @@ function useVirtualScroll({ tableContainer, props, dataSourceCopy, tableHeaderLa
438
488
  virtualX_on,
439
489
  virtualX_columnPart,
440
490
  virtualX_offsetRight,
491
+ initVirtualScroll,
441
492
  initVirtualScrollY,
442
493
  initVirtualScrollX,
443
494
  updateVirtualScrollY,
@@ -697,6 +748,7 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
697
748
  virtualX_on,
698
749
  virtualX_columnPart,
699
750
  virtualX_offsetRight,
751
+ initVirtualScroll,
700
752
  initVirtualScrollY,
701
753
  initVirtualScrollX,
702
754
  updateVirtualScrollY,
@@ -706,6 +758,11 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
706
758
  if (props.autoResize) {
707
759
  useAutoResize({ tableContainer, initVirtualScroll, scrollTo, props, debounceMs: 500 });
708
760
  }
761
+ useKeyboardArrowScroll(tableContainer, {
762
+ scrollTo,
763
+ virtualScroll,
764
+ virtualScrollX
765
+ });
709
766
  watch(
710
767
  () => props.columns,
711
768
  () => {
@@ -740,10 +797,6 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
740
797
  onMounted(() => {
741
798
  initVirtualScroll();
742
799
  });
743
- function initVirtualScroll(height) {
744
- initVirtualScrollY(height);
745
- initVirtualScrollX();
746
- }
747
800
  function getFixedStyle(tagType, col) {
748
801
  const style = {};
749
802
  if (Is_Legacy_Mode) {
package/lib/style.css CHANGED
@@ -1,255 +1,257 @@
1
- .stk-table {
2
- --row-height: 28px;
3
- --cell-padding-x: 8px;
4
- --resize-handle-width: 4px;
5
- --border-color: #e8eaec;
6
- --border-width: 1px;
7
- --td-bgc: #fff;
8
- --th-bgc: #f8f8f9;
9
- --tr-active-bgc: #e6f7ff;
10
- --tr-hover-bgc: rgba(230, 247, 255, 0.7);
11
- --bg-border-top: linear-gradient(180deg, var(--border-color) var(--border-width), transparent var(--border-width));
12
- --bg-border-right: linear-gradient(270deg, var(--border-color) var(--border-width), transparent var(--border-width));
13
- --bg-border-bottom: linear-gradient(0deg, var(--border-color) var(--border-width), transparent var(--border-width));
14
- --bg-border-left: linear-gradient(90deg, var(--border-color) var(--border-width), transparent var(--border-width));
15
- --highlight-color: #71a2fd;
16
- --sort-arrow-color: #5d5f69;
17
- --sort-arrow-hover-color: #8f90b5;
18
- --sort-arrow-active-color: #1b63d9;
19
- --sort-arrow-active-sub-color: #cbcbe1;
20
- /** 列宽拖动指示器颜色 */
21
- --col-resize-indicator-color: #cbcbe1;
22
- position: relative;
23
- overflow: auto;
24
- display: flex;
25
- flex-direction: column;
26
- /**深色模式 */
27
- /** 调整列宽的时候不要触发th事件。否则会导致触发表头点击排序 */
28
- /** 列宽调整指示器 */
29
- /**虚拟滚动模式 */
30
- }
31
- .stk-table.dark {
32
- --th-bgc: #181c21;
33
- --td-bgc: #181c21;
34
- --border-color: #26292e;
35
- --tr-active-bgc: #283f63;
36
- --tr-hover-bgc: #1a2b46;
37
- --table-bgc: #181c21;
38
- --highlight-color: #1e4c99;
39
- --sort-arrow-color: #5d6064;
40
- --sort-arrow-hover-color: #727782;
41
- --sort-arrow-active-color: #d0d1d2;
42
- --sort-arrow-active-sub-color: #5d6064;
43
- --col-resize-indicator-color: #5d6064;
44
- color: #d0d1d2;
45
- }
46
- .stk-table.headless {
47
- border-top: 1px solid var(--border-color);
48
- }
49
- .stk-table.is-col-resizing th {
50
- pointer-events: none;
51
- }
52
- .stk-table.border-h {
53
- --bg-border-right: linear-gradient(transparent, transparent);
54
- --bg-border-left: linear-gradient(transparent, transparent);
55
- }
56
- .stk-table.border-v {
57
- --bg-border-bottom: linear-gradient(transparent, transparent);
58
- }
59
- .stk-table.border .stk-table-main th,
60
- .stk-table.border .stk-table-main td {
61
- background-image: var(--bg-border-right), var(--bg-border-bottom);
62
- }
63
- .stk-table.border .stk-table-main thead tr:first-child th {
64
- background-image: var(--bg-border-top), var(--bg-border-right), var(--bg-border-bottom);
65
- }
66
- .stk-table.border .stk-table-main thead tr:first-child th:first-child {
67
- background-image: var(--bg-border-top), var(--bg-border-right), var(--bg-border-bottom), var(--bg-border-left);
68
- }
69
- .stk-table.border .stk-table-main thead tr th:first-child {
70
- background-image: var(--bg-border-right), var(--bg-border-bottom), var(--bg-border-left);
71
- }
72
- .stk-table.border .stk-table-main tbody td:first-child {
73
- background-image: var(--bg-border-right), var(--bg-border-bottom), var(--bg-border-left);
74
- }
75
- .stk-table.border.virtual-x .stk-table-main thead tr:first-child .virtual-x-left + th {
76
- background-image: var(--bg-border-top), var(--bg-border-right), var(--bg-border-bottom), var(--bg-border-left);
77
- }
78
- .stk-table.border.virtual-x .stk-table-main tr .virtual-x-left + th {
79
- background-image: var(--bg-border-right), var(--bg-border-bottom), var(--bg-border-left);
80
- }
81
- .stk-table.border-body-v .stk-table-main tbody {
82
- --bg-border-bottom: linear-gradient(transparent, transparent);
83
- }
84
- .stk-table .column-resize-indicator {
85
- width: 0;
86
- height: 100%;
87
- border-left: 1px dashed var(--col-resize-indicator-color);
88
- position: absolute;
89
- z-index: 10;
90
- display: none;
91
- pointer-events: none;
92
- }
93
- .stk-table .stk-table-main {
94
- border-spacing: 0;
95
- border-collapse: separate;
96
- width: fit-content;
97
- min-width: 100%;
98
- }
99
- .stk-table .stk-table-main.fixed-mode {
100
- table-layout: fixed;
101
- }
102
- .stk-table .stk-table-main th,
103
- .stk-table .stk-table-main td {
104
- z-index: 1;
105
- height: var(--row-height);
106
- font-size: 14px;
107
- box-sizing: border-box;
108
- padding: 0 var(--cell-padding-x);
109
- }
110
- .stk-table .stk-table-main thead tr:first-child th {
111
- position: sticky;
112
- top: 0;
113
- }
114
- .stk-table .stk-table-main thead tr th {
115
- background-color: var(--th-bgc);
116
- }
117
- .stk-table .stk-table-main thead tr th.sortable {
118
- cursor: pointer;
119
- }
120
- .stk-table .stk-table-main thead tr th.text-overflow .table-header-cell-wrapper {
121
- white-space: nowrap;
122
- overflow: hidden;
123
- }
124
- .stk-table .stk-table-main thead tr th.text-overflow .table-header-cell-wrapper .table-header-title {
125
- text-overflow: ellipsis;
126
- overflow: hidden;
127
- }
128
- .stk-table .stk-table-main thead tr th:not(.sorter-desc):not(.sorter-asc):hover .table-header-cell-wrapper .table-header-sorter #arrow-up {
129
- fill: var(--sort-arrow-hover-color);
130
- }
131
- .stk-table .stk-table-main thead tr th:not(.sorter-desc):not(.sorter-asc):hover .table-header-cell-wrapper .table-header-sorter #arrow-down {
132
- fill: var(--sort-arrow-hover-color);
133
- }
134
- .stk-table .stk-table-main thead tr th.sorter-desc .table-header-cell-wrapper .table-header-sorter {
135
- display: initial;
136
- }
137
- .stk-table .stk-table-main thead tr th.sorter-desc .table-header-cell-wrapper .table-header-sorter #arrow-up {
138
- fill: var(--sort-arrow-active-sub-color);
1
+
2
+ .stk-table{
3
+ --row-height:28px;
4
+ --cell-padding-x:8px;
5
+ --resize-handle-width:4px;
6
+ --border-color:#e8eaec;
7
+ --border-width:1px;
8
+ --td-bgc:#fff;
9
+ --th-bgc:#f8f8f9;
10
+ --tr-active-bgc:rgb(230, 247, 255);
11
+ --tr-hover-bgc:rgba(230, 247, 255, 0.7);
12
+ --bg-border-top:linear-gradient(180deg, var(--border-color) var(--border-width), transparent var(--border-width));
13
+ --bg-border-right:linear-gradient(270deg, var(--border-color) var(--border-width), transparent var(--border-width));
14
+ --bg-border-bottom:linear-gradient(0deg, var(--border-color) var(--border-width), transparent var(--border-width));
15
+ --bg-border-left:linear-gradient(90deg, var(--border-color) var(--border-width), transparent var(--border-width));
16
+ --highlight-color:#71a2fd;
17
+
18
+ --sort-arrow-color:#5d5f69;
19
+ --sort-arrow-hover-color:#8f90b5;
20
+ --sort-arrow-active-color:#1b63d9;
21
+ --sort-arrow-active-sub-color:#cbcbe1;
22
+ --col-resize-indicator-color:#cbcbe1;
23
+
24
+ position:relative;
25
+ overflow:auto;
26
+ display:flex;
27
+ flex-direction:column;
28
+ }
29
+ .stk-table.dark{
30
+ --th-bgc:#181c21;
31
+ --td-bgc:#181c21;
32
+ --border-color:#26292e;
33
+ --tr-active-bgc:#283f63;
34
+ --tr-hover-bgc:#1a2b46;
35
+ --table-bgc:#181c21;
36
+ --highlight-color:#1e4c99;
37
+
38
+ --sort-arrow-color:#5d6064;
39
+ --sort-arrow-hover-color:#727782;
40
+ --sort-arrow-active-color:#d0d1d2;
41
+ --sort-arrow-active-sub-color:#5d6064;
42
+
43
+ --col-resize-indicator-color:#5d6064;
44
+ color:#d0d1d2;
45
+ }
46
+ .stk-table.headless{
47
+ border-top:1px solid var(--border-color);
48
+ }
49
+ .stk-table.is-col-resizing th{
50
+ pointer-events:none;
51
+ }
52
+ .stk-table.border-h{
53
+ --bg-border-right:linear-gradient(transparent, transparent);
54
+ --bg-border-left:linear-gradient(transparent, transparent);
55
+ }
56
+ .stk-table.border-v{
57
+ --bg-border-bottom:linear-gradient(transparent, transparent);
58
+ }
59
+ .stk-table.border .stk-table-main th,
60
+ .stk-table.border .stk-table-main td{
61
+ background-image:var(--bg-border-right), var(--bg-border-bottom);
62
+ }
63
+ .stk-table.border .stk-table-main thead tr:first-child th{
64
+ background-image:var(--bg-border-top), var(--bg-border-right), var(--bg-border-bottom);
65
+ }
66
+ .stk-table.border .stk-table-main thead tr:first-child th:first-child{
67
+ background-image:var(--bg-border-top), var(--bg-border-right), var(--bg-border-bottom), var(--bg-border-left);
68
+ }
69
+ .stk-table.border .stk-table-main thead tr th:first-child{
70
+ background-image:var(--bg-border-right), var(--bg-border-bottom), var(--bg-border-left);
71
+ }
72
+ .stk-table.border .stk-table-main tbody td:first-child{
73
+ background-image:var(--bg-border-right), var(--bg-border-bottom), var(--bg-border-left);
74
+ }
75
+ .stk-table.border.virtual-x .stk-table-main thead tr:first-child .virtual-x-left + th{
76
+ background-image:var(--bg-border-top), var(--bg-border-right), var(--bg-border-bottom), var(--bg-border-left);
77
+ }
78
+ .stk-table.border.virtual-x .stk-table-main tr .virtual-x-left + th{
79
+ background-image:var(--bg-border-right), var(--bg-border-bottom), var(--bg-border-left);
80
+ }
81
+ .stk-table.border-body-v .stk-table-main tbody{
82
+ --bg-border-bottom:linear-gradient(transparent, transparent);
83
+ }
84
+ .stk-table .column-resize-indicator{
85
+ width:0;
86
+ height:100%;
87
+ border-left:1px dashed var(--col-resize-indicator-color);
88
+ position:absolute;
89
+ z-index:10;
90
+ display:none;
91
+ pointer-events:none;
92
+ }
93
+ .stk-table .stk-table-main{
94
+ border-spacing:0;
95
+ border-collapse:separate;
96
+ width:-moz-fit-content;
97
+ width:fit-content;
98
+ min-width:100%;
99
+ }
100
+ .stk-table .stk-table-main.fixed-mode{
101
+ table-layout:fixed;
102
+ }
103
+ .stk-table .stk-table-main th,
104
+ .stk-table .stk-table-main td{
105
+ z-index:1;
106
+ height:var(--row-height);
107
+ font-size:14px;
108
+ box-sizing:border-box;
109
+ padding:0 var(--cell-padding-x);
110
+ }
111
+ .stk-table .stk-table-main thead tr:first-child th{
112
+ position:sticky;
113
+ top:0;
114
+ }
115
+ .stk-table .stk-table-main thead tr th{
116
+ background-color:var(--th-bgc);
117
+ }
118
+ .stk-table .stk-table-main thead tr th.sortable{
119
+ cursor:pointer;
120
+ }
121
+ .stk-table .stk-table-main thead tr th.text-overflow .table-header-cell-wrapper{
122
+ white-space:nowrap;
123
+ overflow:hidden;
124
+ }
125
+ .stk-table .stk-table-main thead tr th.text-overflow .table-header-cell-wrapper .table-header-title{
126
+ text-overflow:ellipsis;
127
+ overflow:hidden;
128
+ }
129
+ .stk-table .stk-table-main thead tr th:not(.sorter-desc):not(.sorter-asc):hover .table-header-cell-wrapper .table-header-sorter #arrow-up{
130
+ fill:var(--sort-arrow-hover-color);
131
+ }
132
+ .stk-table .stk-table-main thead tr th:not(.sorter-desc):not(.sorter-asc):hover .table-header-cell-wrapper .table-header-sorter #arrow-down{
133
+ fill:var(--sort-arrow-hover-color);
134
+ }
135
+ .stk-table .stk-table-main thead tr th.sorter-desc .table-header-cell-wrapper .table-header-sorter{
136
+ display:inline;
137
+ display:initial;
138
+ }
139
+ .stk-table .stk-table-main thead tr th.sorter-desc .table-header-cell-wrapper .table-header-sorter #arrow-up{
140
+ fill:var(--sort-arrow-active-sub-color);
139
141
  }
140
- .stk-table .stk-table-main thead tr th.sorter-desc .table-header-cell-wrapper .table-header-sorter #arrow-down {
141
- fill: var(--sort-arrow-active-color);
142
+ .stk-table .stk-table-main thead tr th.sorter-desc .table-header-cell-wrapper .table-header-sorter #arrow-down{
143
+ fill:var(--sort-arrow-active-color);
142
144
  }
143
- .stk-table .stk-table-main thead tr th.sorter-asc .table-header-cell-wrapper .table-header-sorter {
144
- display: initial;
145
+ .stk-table .stk-table-main thead tr th.sorter-asc .table-header-cell-wrapper .table-header-sorter{
146
+ display:inline;
147
+ display:initial;
145
148
  }
146
- .stk-table .stk-table-main thead tr th.sorter-asc .table-header-cell-wrapper .table-header-sorter #arrow-up {
147
- fill: var(--sort-arrow-active-color);
149
+ .stk-table .stk-table-main thead tr th.sorter-asc .table-header-cell-wrapper .table-header-sorter #arrow-up{
150
+ fill:var(--sort-arrow-active-color);
148
151
  }
149
- .stk-table .stk-table-main thead tr th.sorter-asc .table-header-cell-wrapper .table-header-sorter #arrow-down {
150
- fill: var(--sort-arrow-active-sub-color);
152
+ .stk-table .stk-table-main thead tr th.sorter-asc .table-header-cell-wrapper .table-header-sorter #arrow-down{
153
+ fill:var(--sort-arrow-active-sub-color);
151
154
  }
152
- .stk-table .stk-table-main thead tr th .table-header-cell-wrapper {
153
- max-width: 100%;
154
- display: inline-flex;
155
- align-items: center;
155
+ .stk-table .stk-table-main thead tr th .table-header-cell-wrapper{
156
+ max-width:100%;
157
+ display:inline-flex;
158
+ align-items:center;
156
159
  }
157
- .stk-table .stk-table-main thead tr th .table-header-cell-wrapper .table-header-title {
158
- overflow: hidden;
159
- align-self: flex-start;
160
+ .stk-table .stk-table-main thead tr th .table-header-cell-wrapper .table-header-title{
161
+ overflow:hidden;
162
+ align-self:flex-start;
160
163
  }
161
- .stk-table .stk-table-main thead tr th .table-header-cell-wrapper .table-header-sorter {
162
- flex-shrink: 0;
163
- margin-left: 4px;
164
- width: 16px;
165
- height: 16px;
166
- display: none;
164
+ .stk-table .stk-table-main thead tr th .table-header-cell-wrapper .table-header-sorter{
165
+ flex-shrink:0;
166
+ margin-left:4px;
167
+ width:16px;
168
+ height:16px;
169
+ display:none;
170
+ }
171
+ .stk-table .stk-table-main thead tr th .table-header-cell-wrapper .table-header-sorter #arrow-up,
172
+ .stk-table .stk-table-main thead tr th .table-header-cell-wrapper .table-header-sorter #arrow-down{
173
+ fill:var(--sort-arrow-color);
174
+ }
175
+ .stk-table .stk-table-main thead tr th .table-header-cell-wrapper .table-header-resizer{
176
+ position:absolute;
177
+ top:0;
178
+ bottom:0;
179
+ cursor:col-resize;
180
+ width:var(--resize-handle-width);
181
+ }
182
+ .stk-table .stk-table-main thead tr th .table-header-cell-wrapper .table-header-resizer.left{
183
+ left:0;
184
+ }
185
+ .stk-table .stk-table-main thead tr th .table-header-cell-wrapper .table-header-resizer.right{
186
+ right:0;
187
+ }
188
+ .stk-table .stk-table-main tbody{
189
+ @keyframes dim{
190
+ from{
191
+ background-color:var(--highlight-color);
167
192
  }
168
- .stk-table .stk-table-main thead tr th .table-header-cell-wrapper .table-header-sorter #arrow-up,
169
- .stk-table .stk-table-main thead tr th .table-header-cell-wrapper .table-header-sorter #arrow-down {
170
- fill: var(--sort-arrow-color);
171
193
  }
172
- .stk-table .stk-table-main thead tr th .table-header-cell-wrapper .table-header-resizer {
173
- position: absolute;
174
- top: 0;
175
- bottom: 0;
176
- cursor: col-resize;
177
- width: var(--resize-handle-width);
178
194
  }
179
- .stk-table .stk-table-main thead tr th .table-header-cell-wrapper .table-header-resizer.left {
180
- left: 0;
195
+ .stk-table .stk-table-main tbody tr{
196
+ background-color:var(--td-bgc);
197
+ }
198
+ .stk-table .stk-table-main tbody tr.highlight-row{
199
+ animation:dim 2s linear;
200
+ }
201
+ .stk-table .stk-table-main tbody tr.hover,
202
+ .stk-table .stk-table-main tbody tr:hover{
203
+ background-color:var(--tr-hover-bgc);
181
204
  }
182
- .stk-table .stk-table-main thead tr th .table-header-cell-wrapper .table-header-resizer.right {
183
- right: 0;
184
- }
185
- .stk-table .stk-table-main tbody {
186
- /**高亮渐暗 */
205
+ .stk-table .stk-table-main tbody tr.active{
206
+ background-color:var(--tr-active-bgc);
187
207
  }
188
- @keyframes dim {
189
- from {
190
- background-color: var(--highlight-color);
208
+ .stk-table .stk-table-main tbody tr td.fixed-cell{
209
+ background-color:inherit;
191
210
  }
211
+ .stk-table .stk-table-main tbody tr td.highlight-cell{
212
+ animation:dim 2s linear;
192
213
  }
193
- .stk-table .stk-table-main tbody tr {
194
- background-color: var(--td-bgc);
214
+ .stk-table .stk-table-main tbody tr td.text-overflow .table-cell-wrapper{
215
+ white-space:nowrap;
216
+ overflow:hidden;
217
+ text-overflow:ellipsis;
195
218
  }
196
- .stk-table .stk-table-main tbody tr.highlight-row {
197
- animation: dim 2s linear;
219
+ .stk-table .stk-table-no-data{
220
+ background-color:var(--table-bgc);
221
+ line-height:var(--row-height);
222
+ text-align:center;
223
+ font-size:14px;
224
+ position:sticky;
225
+ left:0px;
226
+ border-left:var(--border-width) solid var(--border-color);
227
+ border-right:var(--border-width) solid var(--border-color);
228
+ border-bottom:var(--border-width) solid var(--border-color);
229
+ display:flex;
230
+ flex-direction:column;
231
+ align-items:center;
232
+ justify-content:center;
198
233
  }
199
- .stk-table .stk-table-main tbody tr.hover,
200
- .stk-table .stk-table-main tbody tr:hover {
201
- background-color: var(--tr-hover-bgc);
234
+ .stk-table .stk-table-no-data.no-data-full{
235
+ flex:1;
202
236
  }
203
- .stk-table .stk-table-main tbody tr.active {
204
- background-color: var(--tr-active-bgc);
237
+ .stk-table.virtual .stk-table-main thead tr th .table-header-cell-wrapper{
238
+ overflow:hidden;
239
+ max-height:var(--row-height);
205
240
  }
206
- .stk-table .stk-table-main tbody tr td.fixed-cell {
207
- background-color: inherit;
208
- }
209
- .stk-table .stk-table-main tbody tr td.highlight-cell {
210
- animation: dim 2s linear;
211
- }
212
- .stk-table .stk-table-main tbody tr td.text-overflow .table-cell-wrapper {
213
- white-space: nowrap;
214
- overflow: hidden;
215
- text-overflow: ellipsis;
216
- }
217
- .stk-table .stk-table-no-data {
218
- background-color: var(--table-bgc);
219
- line-height: var(--row-height);
220
- text-align: center;
221
- font-size: 14px;
222
- position: sticky;
223
- left: 0px;
224
- border-left: var(--border-width) solid var(--border-color);
225
- border-right: var(--border-width) solid var(--border-color);
226
- border-bottom: var(--border-width) solid var(--border-color);
227
- display: flex;
228
- flex-direction: column;
229
- align-items: center;
230
- justify-content: center;
231
- }
232
- .stk-table .stk-table-no-data.no-data-full {
233
- flex: 1;
234
- }
235
- .stk-table.virtual .stk-table-main thead tr th .table-header-cell-wrapper {
236
- overflow: hidden;
237
- max-height: var(--row-height);
238
- }
239
- .stk-table.virtual .stk-table-main tbody {
240
- position: relative;
241
- }
242
- .stk-table.virtual .stk-table-main tbody tr.padding-top-tr td {
243
- height: 0;
244
- }
245
- .stk-table.virtual .stk-table-main tbody tr td {
246
- height: var(--row-height);
247
- line-height: 1;
248
- }
249
- .stk-table.virtual .stk-table-main tbody tr td .table-cell-wrapper {
250
- max-height: var(--row-height);
251
- overflow: hidden;
252
- }
253
- .stk-table.virtual-x .stk-table-main .virtual-x-left {
254
- padding: 0;
241
+ .stk-table.virtual .stk-table-main tbody{
242
+ position:relative;
255
243
  }
244
+ .stk-table.virtual .stk-table-main tbody tr.padding-top-tr td{
245
+ height:0;
246
+ }
247
+ .stk-table.virtual .stk-table-main tbody tr td{
248
+ height:var(--row-height);
249
+ line-height:1;
250
+ }
251
+ .stk-table.virtual .stk-table-main tbody tr td .table-cell-wrapper{
252
+ max-height:var(--row-height);
253
+ overflow:hidden;
254
+ }
255
+ .stk-table.virtual-x .stk-table-main .virtual-x-left{
256
+ padding:0;
257
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "stk-table-vue",
3
- "version": "0.0.1-beta.6",
3
+ "version": "0.0.1-beta.7",
4
4
  "description": "simple realtime virtual table for vue3",
5
5
  "main": "./lib/stk-table-vue.js",
6
6
  "types": "./lib/StkTable/index.d.ts",
@@ -43,7 +43,8 @@
43
43
  "eslint-plugin-prettier": "^5.0.1",
44
44
  "eslint-plugin-vue": "^9.19.2",
45
45
  "happy-dom": "^12.10.3",
46
- "less": "^4.2.0",
46
+ "postcss-discard-comments": "^6.0.1",
47
+ "postcss-preset-env": "^9.3.0",
47
48
  "prettier": "^3.1.1",
48
49
  "pug": "^3.0.2",
49
50
  "typescript": "^5.3.3",
@@ -51,8 +52,7 @@
51
52
  "vite-plugin-dts": "^3.6.4",
52
53
  "vitest": "^1.1.0",
53
54
  "vue": "^3.3.9",
54
- "vue-eslint-parser": "^9.3.2",
55
- "vue-loader": "^17.2.2"
55
+ "vue-eslint-parser": "^9.3.2"
56
56
  },
57
57
  "dependencies": {
58
58
  "d3-interpolate": "^3.0.1"
@@ -201,6 +201,7 @@ import { Order, SortOption, StkProps, StkTableColumn } from './types/index';
201
201
  import { useAutoResize } from './useAutoResize';
202
202
  import { useColResize } from './useColResize';
203
203
  import { useHighlight } from './useHighlight';
204
+ import { useKeyboardArrowScroll } from './useKeyboardArrowScroll';
204
205
  import { useThDrag } from './useThDrag';
205
206
  import { useVirtualScroll } from './useVirtualScroll';
206
207
  import { howDeepTheColumn, tableSort } from './utils';
@@ -335,6 +336,7 @@ const {
335
336
  virtualX_on,
336
337
  virtualX_columnPart,
337
338
  virtualX_offsetRight,
339
+ initVirtualScroll,
338
340
  initVirtualScrollY,
339
341
  initVirtualScrollX,
340
342
  updateVirtualScrollY,
@@ -350,6 +352,13 @@ if (props.autoResize) {
350
352
  useAutoResize({ tableContainer, initVirtualScroll, scrollTo, props, debounceMs: 500 });
351
353
  }
352
354
 
355
+ /** 键盘箭头滚动 */
356
+ useKeyboardArrowScroll(tableContainer, {
357
+ scrollTo,
358
+ virtualScroll,
359
+ virtualScrollX,
360
+ });
361
+
353
362
  watch(
354
363
  () => props.columns,
355
364
  () => {
@@ -390,15 +399,6 @@ onMounted(() => {
390
399
  initVirtualScroll();
391
400
  });
392
401
 
393
- /**
394
- * 初始化虚拟滚动参数
395
- * @param {number} [height] 虚拟滚动的高度
396
- */
397
- function initVirtualScroll(height?: number) {
398
- initVirtualScrollY(height);
399
- initVirtualScrollX();
400
- }
401
-
402
402
  /**
403
403
  * 固定列的style
404
404
  * @param {1|2} tagType 1-th 2-td
@@ -729,9 +729,9 @@ defineExpose({
729
729
  });
730
730
  </script>
731
731
 
732
- <style lang="less">
732
+ <style type="css">
733
733
  .stk-table {
734
- // contain: strict;
734
+ /* contain: strict;*/
735
735
  --row-height: 28px;
736
736
  --cell-padding-x: 8px;
737
737
  --resize-handle-width: 4px;
@@ -767,7 +767,7 @@ defineExpose({
767
767
  --tr-active-bgc: #283f63;
768
768
  --tr-hover-bgc: #1a2b46;
769
769
  --table-bgc: #181c21;
770
- --highlight-color: #1e4c99; // 不能用rgba,因为固定列时,会变成半透明
770
+ --highlight-color: #1e4c99; /* 不能用rgba,因为固定列时,会变成半透明*/
771
771
 
772
772
  --sort-arrow-color: #5d6064;
773
773
  --sort-arrow-hover-color: #727782;
@@ -776,19 +776,19 @@ defineExpose({
776
776
 
777
777
  --col-resize-indicator-color: #5d6064;
778
778
 
779
- // background-color: var(--table-bgc); // ⭐这里加background-color会导致表格出滚动白屏
779
+ /* background-color: var(--table-bgc); */ /* ⭐这里加background-color会导致表格出滚动白屏*/
780
780
  color: #d0d1d2;
781
781
  }
782
782
 
783
- // .stk-table-fixed-left-col-box-shadow {
784
- // position: sticky;
785
- // left: 0;
786
- // top: 0;
787
- // height: 100%;
788
- // box-shadow: 0 0 10px;
789
- // z-index: 1;
790
- // pointer-events: none;
791
- // }
783
+ /*.stk-table-fixed-left-col-box-shadow {
784
+ position: sticky;
785
+ left: 0;
786
+ top: 0;
787
+ height: 100%;
788
+ box-shadow: 0 0 10px;
789
+ z-index: 1;
790
+ pointer-events: none;
791
+ }*/
792
792
  &.headless {
793
793
  border-top: 1px solid var(--border-color);
794
794
  }
@@ -839,7 +839,7 @@ defineExpose({
839
839
  &.virtual-x {
840
840
  .stk-table-main {
841
841
  thead tr:first-child .virtual-x-left + th {
842
- // 横向虚拟滚动时,左侧第一个单元格加上border-left
842
+ /* 横向虚拟滚动时,左侧第一个单元格加上border-left*/
843
843
  background-image: var(--bg-border-top), var(--bg-border-right), var(--bg-border-bottom), var(--bg-border-left);
844
844
  }
845
845
 
@@ -871,8 +871,8 @@ defineExpose({
871
871
  .stk-table-main {
872
872
  border-spacing: 0;
873
873
  border-collapse: separate;
874
- width: fit-content; // 用于兼容低版本,低版本width超过100%的时候还是100%,导致sticky错误。
875
- min-width: 100%; // 用于兼容低版本chrome。低版本min-width 是min-content;
874
+ width: fit-content; /* 用于兼容低版本,低版本width超过100%的时候还是100%,导致sticky错误。*/
875
+ min-width: 100%; /* 用于兼容低版本chrome。低版本min-width 是min-content;*/
876
876
  &.fixed-mode {
877
877
  table-layout: fixed;
878
878
  }
@@ -945,7 +945,7 @@ defineExpose({
945
945
  }
946
946
 
947
947
  .table-header-cell-wrapper {
948
- max-width: 100%; //最大宽度不超过列宽
948
+ max-width: 100%; /*最大宽度不超过列宽*/
949
949
  display: inline-flex;
950
950
  align-items: center;
951
951
 
@@ -995,15 +995,15 @@ defineExpose({
995
995
  }
996
996
 
997
997
  tr {
998
- background-color: var(--td-bgc); // td inherit tr bgc
998
+ background-color: var(--td-bgc); /* td inherit tr bgc*/
999
999
 
1000
1000
  &.highlight-row {
1001
1001
  animation: dim 2s linear;
1002
1002
  }
1003
1003
 
1004
- // &.highlight-row-transition {
1005
- // transition: background-color v-bind(highlightStepDuration) linear;
1006
- // }
1004
+ /* &.highlight-row-transition {
1005
+ transition: background-color v-bind(highlightStepDuration) linear;
1006
+ }*/
1007
1007
 
1008
1008
  &.hover,
1009
1009
  &:hover {
@@ -1016,7 +1016,7 @@ defineExpose({
1016
1016
 
1017
1017
  td {
1018
1018
  &.fixed-cell {
1019
- background-color: inherit; // 防止横向滚动后透明
1019
+ background-color: inherit; /* 防止横向滚动后透明*/
1020
1020
  }
1021
1021
 
1022
1022
  &.highlight-cell {
@@ -1031,40 +1031,40 @@ defineExpose({
1031
1031
  }
1032
1032
  }
1033
1033
 
1034
- // &.perch-td {
1035
- // padding: 0;
1036
- // height: 0;
1037
- // &.top {
1038
- // background-image: repeating-linear-gradient(
1039
- // 180deg,
1040
- // transparent 0,
1041
- // transparent var(--row-height),
1042
- // var(--border-color) var(--row-height),
1043
- // var(--border-color) calc(var(--row-height) + 1px)
1044
- // ),
1045
- // var(--bg-border-right);
1046
- // }
1047
- // &.bottom {
1048
- // background-image: repeating-linear-gradient(
1049
- // 0deg,
1050
- // transparent 0,
1051
- // transparent var(--row-height),
1052
- // var(--border-color) var(--row-height),
1053
- // var(--border-color) calc(var(--row-height) + 1px)
1054
- // ),
1055
- // var(--bg-border-right);
1056
- // }
1057
- // }
1034
+ /* &.perch-td {
1035
+ padding: 0;
1036
+ height: 0;
1037
+ &.top {
1038
+ background-image: repeating-linear-gradient(
1039
+ 180deg,
1040
+ transparent 0,
1041
+ transparent var(--row-height),
1042
+ var(--border-color) var(--row-height),
1043
+ var(--border-color) calc(var(--row-height) + 1px)
1044
+ ),
1045
+ var(--bg-border-right);
1046
+ }
1047
+ &.bottom {
1048
+ background-image: repeating-linear-gradient(
1049
+ 0deg,
1050
+ transparent 0,
1051
+ transparent var(--row-height),
1052
+ var(--border-color) var(--row-height),
1053
+ var(--border-color) calc(var(--row-height) + 1px)
1054
+ ),
1055
+ var(--bg-border-right);
1056
+ }
1057
+ }*/
1058
1058
  }
1059
1059
  }
1060
1060
 
1061
- // 斑马纹
1062
- // tr:nth-child(2n) td {
1063
- // background-color: #fafafc;
1064
- // }
1065
- // tr:hover {
1066
- // background-color: #ebf3ff;
1067
- // }
1061
+ /* 斑马纹
1062
+ tr:nth-child(2n) td {
1063
+ background-color: #fafafc;
1064
+ }
1065
+ tr:hover {
1066
+ background-color: #ebf3ff;
1067
+ }*/
1068
1068
  }
1069
1069
  }
1070
1070
 
@@ -1094,7 +1094,7 @@ defineExpose({
1094
1094
  thead {
1095
1095
  tr {
1096
1096
  th {
1097
- // 为不影响布局,表头行高要定死
1097
+ /* 为不影响布局,表头行高要定死*/
1098
1098
  .table-header-cell-wrapper {
1099
1099
  overflow: hidden;
1100
1100
  max-height: var(--row-height);
@@ -3,7 +3,7 @@ import { Component, VNode } from 'vue';
3
3
  /** 排序方式,asc-正序,desc-倒序,null-默认顺序 */
4
4
  export type Order = null | 'asc' | 'desc';
5
5
 
6
- type Sorter = boolean | ((data: any[], option: { order: Order; column: any }) => any[]);
6
+ type Sorter<T> = boolean | ((data: T[], option: { order: Order; column: any }) => T[]);
7
7
 
8
8
  export type CustomCellFunc<T extends Record<string, any>> = (props: { row: T; col: StkTableColumn<T>; cellValue: any }) => VNode;
9
9
  export type CustomHeaderCellFunc<T extends Record<string, any>> = (props: { col: StkTableColumn<T> }) => VNode;
@@ -19,7 +19,7 @@ export type StkTableColumn<T extends Record<string, any>> = {
19
19
  /** 表头内容对齐方式 */
20
20
  headerAlign?: 'right' | 'left' | 'center';
21
21
  /** 筛选 */
22
- sorter?: Sorter;
22
+ sorter?: Sorter<T>;
23
23
  /** 列宽。横向虚拟滚动时必须设置。 */
24
24
  width?: string;
25
25
  /** 最小列宽。非x虚拟滚动生效。 */
@@ -0,0 +1,66 @@
1
+ import { Ref, onBeforeUnmount, onMounted } from 'vue';
2
+ import { VirtualScrollStore, VirtualScrollXStore } from './useVirtualScroll';
3
+
4
+ const ARROW_CODES = ['ArrowUp', 'ArrowRight', 'ArrowDown', 'ArrowLeft'];
5
+
6
+ type Options = {
7
+ scrollTo: (x: number | null, y: number | null) => void;
8
+ virtualScroll: Ref<VirtualScrollStore>;
9
+ virtualScrollX: Ref<VirtualScrollXStore>;
10
+ };
11
+ /**
12
+ * 按下键盘箭头滚动。只有悬浮在表体上才能生效键盘。
13
+ *
14
+ * 在低版本浏览器中,虚拟滚动时,使用键盘滚动,等选中的行消失在视口外时,滚动会失效。
15
+ */
16
+ export function useKeyboardArrowScroll(targetElement: Ref<HTMLElement | undefined>, { scrollTo, virtualScroll, virtualScrollX }: Options) {
17
+ /** 检测鼠标是否悬浮在表格体上 */
18
+ let isMouseOver = false;
19
+
20
+ onMounted(() => {
21
+ window.addEventListener('keydown', handleKeydown);
22
+ targetElement.value?.addEventListener('mouseenter', handleMouseEnter);
23
+ targetElement.value?.addEventListener('mouseleave', handleMouseLeave);
24
+ targetElement.value?.addEventListener('mousedown', handleMouseDown);
25
+ });
26
+
27
+ onBeforeUnmount(() => {
28
+ window.removeEventListener('keydown', handleKeydown);
29
+ targetElement.value?.removeEventListener('mouseenter', handleMouseEnter);
30
+ targetElement.value?.removeEventListener('mouseleave', handleMouseLeave);
31
+ targetElement.value?.removeEventListener('mousedown', handleMouseDown);
32
+ });
33
+
34
+ /** 键盘按下事件 */
35
+ function handleKeydown(e: KeyboardEvent) {
36
+ if (!ARROW_CODES.includes(e.code)) return;
37
+ if (!isMouseOver) return; // 不悬浮还是要触发键盘事件的
38
+ e.preventDefault(); // 不触发键盘默认的箭头事件
39
+
40
+ const { scrollTop, rowHeight } = virtualScroll.value;
41
+ const { scrollLeft } = virtualScrollX.value;
42
+ if (e.code === ARROW_CODES[0]) {
43
+ scrollTo(scrollTop - rowHeight, null);
44
+ } else if (e.code === ARROW_CODES[1]) {
45
+ scrollTo(null, scrollLeft + rowHeight);
46
+ } else if (e.code === ARROW_CODES[2]) {
47
+ scrollTo(scrollTop + rowHeight, null);
48
+ } else if (e.code === ARROW_CODES[3]) {
49
+ scrollTo(null, scrollLeft - rowHeight);
50
+ }
51
+ }
52
+
53
+ function handleMouseEnter() {
54
+ isMouseOver = true;
55
+ }
56
+ function handleMouseLeave() {
57
+ isMouseOver = false;
58
+ }
59
+ /**
60
+ * 兜底。
61
+ * 是否存在不触发mouseEnter的时候?
62
+ */
63
+ function handleMouseDown() {
64
+ if (!isMouseOver) isMouseOver = true;
65
+ }
66
+ }
@@ -1,6 +1,6 @@
1
1
  import { Ref, ShallowRef, computed, ref } from 'vue';
2
- import { StkTableColumn } from './types';
3
2
  import { Default_Col_Width, Default_Table_Height, Default_Table_Width } from './const';
3
+ import { StkTableColumn } from './types';
4
4
 
5
5
  type Option = {
6
6
  tableContainer: Ref<HTMLElement | undefined>;
@@ -9,21 +9,53 @@ type Option = {
9
9
  tableHeaderLast: Ref<StkTableColumn<any>[]>;
10
10
  };
11
11
 
12
+ /** 暂存纵向虚拟滚动的数据 */
13
+ export type VirtualScrollStore = {
14
+ /** 容器高度 */
15
+ containerHeight: number;
16
+ /** 数组开始位置 */
17
+ startIndex: number;
18
+ /** 行高 */
19
+ rowHeight: number;
20
+ /** 表格定位上边距 */
21
+ offsetTop: number;
22
+ /** 纵向滚动条位置,用于判断是横向滚动还是纵向 */
23
+ scrollTop: number;
24
+ };
25
+ /** 暂存横向虚拟滚动的数据 */
26
+ export type VirtualScrollXStore = {
27
+ /** 容器宽度 */
28
+ containerWidth: number;
29
+ /** 开始位置 */
30
+ startIndex: number;
31
+ /** 结束始位置 */
32
+ endIndex: number;
33
+ /** 表格定位左边距 */
34
+ offsetLeft: number;
35
+ /** 横向滚动位置,用于判断是横向滚动还是纵向 */
36
+ scrollLeft: number;
37
+ };
38
+
39
+ /**
40
+ * 虚拟滚动
41
+ * @param param0
42
+ * @returns
43
+ */
12
44
  export function useVirtualScroll({ tableContainer, props, dataSourceCopy, tableHeaderLast }: Option) {
13
- const virtualScroll = ref({
45
+ const virtualScroll = ref<VirtualScrollStore>({
14
46
  containerHeight: 0,
15
- startIndex: 0, // 数组开始位置
47
+ startIndex: 0,
16
48
  rowHeight: 28,
17
- offsetTop: 0, // 表格定位上边距
18
- scrollTop: 0, // 纵向滚动条位置,用于判断是横向滚动还是纵向
49
+ offsetTop: 0,
50
+ scrollTop: 0,
19
51
  });
20
52
 
21
- const virtualScrollX = ref({
53
+ const virtualScrollX = ref<VirtualScrollXStore>({
22
54
  containerWidth: 0,
23
55
  startIndex: 0,
24
56
  endIndex: 0,
25
57
  offsetLeft: 0,
26
- scrollLeft: 0, // 横向滚动位置,用于判断是横向滚动还是纵向
58
+ scrollLeft: 0,
27
59
  });
28
60
 
29
61
  const virtual_on = computed(() => {
@@ -92,9 +124,14 @@ export function useVirtualScroll({ tableContainer, props, dataSourceCopy, tableH
92
124
  */
93
125
  function initVirtualScrollY(height?: number) {
94
126
  if (!virtual_on.value) return;
127
+ const { offsetHeight, scrollTop } = tableContainer.value || {};
95
128
  // FIXME: 可能多次获取offsetHeight 会导致浏览器频繁重排
96
- virtualScroll.value.containerHeight = typeof height === 'number' ? height : tableContainer.value?.offsetHeight || Default_Table_Height;
97
- updateVirtualScrollY(tableContainer.value?.scrollTop);
129
+ if (typeof height === 'number') {
130
+ virtualScroll.value.containerHeight = height;
131
+ } else {
132
+ virtualScroll.value.containerHeight = offsetHeight || Default_Table_Height;
133
+ }
134
+ updateVirtualScrollY(scrollTop);
98
135
  }
99
136
 
100
137
  function initVirtualScrollX() {
@@ -104,6 +141,14 @@ export function useVirtualScroll({ tableContainer, props, dataSourceCopy, tableH
104
141
  virtualScrollX.value.containerWidth = offsetWidth || Default_Table_Width;
105
142
  updateVirtualScrollX(scrollLeft);
106
143
  }
144
+ /**
145
+ * 初始化虚拟滚动参数
146
+ * @param {number} [height] 虚拟滚动的高度
147
+ */
148
+ function initVirtualScroll(height?: number) {
149
+ initVirtualScrollY(height);
150
+ initVirtualScrollX();
151
+ }
107
152
 
108
153
  /** 通过滚动条位置,计算虚拟滚动的参数 */
109
154
  function updateVirtualScrollY(sTop = 0) {
@@ -160,6 +205,7 @@ export function useVirtualScroll({ tableContainer, props, dataSourceCopy, tableH
160
205
  virtualX_on,
161
206
  virtualX_columnPart,
162
207
  virtualX_offsetRight,
208
+ initVirtualScroll,
163
209
  initVirtualScrollY,
164
210
  initVirtualScrollX,
165
211
  updateVirtualScrollY,