stk-table-vue 0.3.0 → 0.3.2

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.
@@ -14,6 +14,7 @@
14
14
  --border-color: #e8e8f4;
15
15
  --border-width: 1px;
16
16
  --td-bgc: #fff;
17
+ --td-hover-color: #71a2fd;
17
18
  --th-bgc: #fafafc;
18
19
  --th-color: #272841;
19
20
  --tr-active-bgc: rgb(230, 247, 255);
@@ -24,7 +25,7 @@
24
25
  --bg-border-left: linear-gradient(90deg, var(--border-color) var(--border-width), transparent var(--border-width));
25
26
  --highlight-color: #71a2fd;
26
27
  --highlight-duration: 2s;
27
- --highlight-easing: linear;
28
+ --highlight-timing-function: linear;
28
29
  /* 斑马纹颜色*/
29
30
  --stripe-bgc: #fafafc;
30
31
 
@@ -48,11 +49,13 @@
48
49
  border-left: 1px solid var(--border-color);
49
50
  /* 下面border用于表格内容不满高度时,绘制表格边界线 */
50
51
  background-image: var(--bg-border-top), var(--bg-border-right), var(--bg-border-bottom);
52
+
51
53
  /**深色模式 */
52
54
  &.dark {
53
55
  --th-bgc: #202029;
54
56
  --th-color: #C0C0D1;
55
57
  --td-bgc: #1b1b24;
58
+ --td-hover-color: #70a6ff;
56
59
  --border-color: #292933;
57
60
  --tr-active-bgc: #283f63;
58
61
  --tr-hover-bgc: #1a2b46;
@@ -144,238 +147,270 @@
144
147
  }
145
148
  }
146
149
 
150
+ /* 斑马纹*/
147
151
  &.stripe {
148
152
 
149
- /* 斑马纹*/
150
- tbody {
151
- tr:nth-child(odd) {
152
- background-color: var(--stripe-bgc);
153
- }
153
+ tbody tr:nth-child(odd) {
154
+ background-color: var(--stripe-bgc);
154
155
  }
155
156
  }
156
157
 
157
- /** 列宽调整指示器 */
158
- .column-resize-indicator {
159
- width: 0;
160
- height: 100%;
161
- border-left: 2px solid var(--col-resize-indicator-color);
162
- position: absolute;
163
- z-index: 10;
164
- display: none;
165
- pointer-events: none;
158
+ /* 单元格悬浮 */
159
+ &.cell-hover tbody td:hover {
160
+ box-shadow: inset 0 0 0 2px var(--td-hover-color);
166
161
  }
167
162
 
168
- .stk-table-main {
169
- border-spacing: 0;
170
- border-collapse: separate;
171
- width: fit-content;
172
- /* 不加会导致width 超过100%时为100%,行hover高亮会断开*/
173
- min-width: 100%;
174
-
175
- &.fixed-mode {
176
- table-layout: fixed;
177
- min-width: min-content;
163
+ /* td 溢出*/
164
+ &.text-overflow {
165
+ .table-cell-wrapper {
166
+ white-space: nowrap;
167
+ overflow: hidden;
168
+ text-overflow: ellipsis;
178
169
  }
170
+ }
179
171
 
180
- th,
181
- td {
182
- z-index: 1;
183
- font-size: 14px;
184
- box-sizing: border-box;
185
- padding: 0 var(--cell-padding-x);
172
+ /* th 溢出*/
173
+ &.header-text-overflow {
174
+ .table-header-cell-wrapper {
175
+ white-space: nowrap;
176
+ overflow: hidden;
186
177
  }
187
178
 
188
- th {
189
- color: var(--th-color);
190
- background-color: var(--th-bgc);
191
-
192
- &.sortable {
193
- cursor: pointer;
194
- }
179
+ .table-header-title {
180
+ text-overflow: ellipsis;
181
+ overflow: hidden;
182
+ }
183
+ }
195
184
 
196
- &.text-overflow {
197
- .table-header-cell-wrapper {
198
- white-space: nowrap;
199
- overflow: hidden;
200
- }
185
+ /**虚拟滚动模式 */
186
+ &.virtual {
201
187
 
202
- .table-header-title {
203
- text-overflow: ellipsis;
204
- overflow: hidden;
205
- }
206
- }
188
+ /* 为不影响布局,表头行高要定死*/
189
+ .table-header-cell-wrapper {
190
+ overflow: hidden;
191
+ max-height: var(--header-row-height);
207
192
  }
208
193
 
209
- td {
210
- &.fixed-cell {
211
- background-color: inherit;
212
- /* 防止横向滚动后透明*/
213
- }
214
-
215
- &.highlight-cell {
216
- animation: stk-table-dim var(--highlight-duration) var(--highlight-easing);
217
- }
194
+ tbody {
195
+ td {
196
+ height: var(--row-height);
197
+ line-height: 1;
218
198
 
219
- &.text-overflow {
220
199
  .table-cell-wrapper {
221
- white-space: nowrap;
200
+ max-height: var(--row-height);
222
201
  overflow: hidden;
223
- text-overflow: ellipsis;
224
202
  }
225
203
  }
226
-
227
- &.seq-column{
228
- text-align: center;
229
- }
230
204
  }
231
205
 
232
- /*固定列阴影-左*/
233
- .fixed-cell--left {
234
- --shadow-rotate: 90deg;
206
+ .padding-top-tr td {
207
+ height: 0;
208
+ }
209
+ }
235
210
 
236
- &.fixed-cell--shadow::after {
237
- right: -10px;
238
- }
211
+ &.virtual-x {
212
+ .virtual-x-left {
213
+ padding: 0;
239
214
  }
215
+ }
240
216
 
241
- /*固定列阴影-右*/
242
- .fixed-cell--right {
243
- --shadow-rotate: -90deg;
217
+ th,
218
+ td {
219
+ z-index: 1;
220
+ font-size: 14px;
221
+ box-sizing: border-box;
222
+ padding: 0 var(--cell-padding-x);
223
+ }
244
224
 
245
- &.fixed-cell--shadow::after {
246
- left: -10px;
247
- }
248
- }
225
+ th {
226
+ color: var(--th-color);
227
+ background-color: inherit;
249
228
 
250
- /*固定列阴影*/
251
- .fixed-cell--shadow::after {
252
- content: '';
253
- width: 10px;
254
- height: 100%;
255
- top: 0px;
256
- position: absolute;
257
- pointer-events: none;
258
- background-image: linear-gradient(var(--shadow-rotate), var(--fixed-col-shadow-color-from), var(--fixed-col-shadow-color-to)),
229
+ &.sortable {
230
+ cursor: pointer;
259
231
  }
260
232
 
261
- thead {
262
- tr {
263
- height: var(--header-row-height);
233
+ &:not(.sorter-desc):not(.sorter-asc):hover .table-header-sorter {
234
+ .arrow-up {
235
+ fill: var(--sort-arrow-hover-color);
236
+ }
264
237
 
265
- &:first-child th {
266
- position: sticky;
267
- top: 0;
268
- }
238
+ .arrow-down {
239
+ fill: var(--sort-arrow-hover-color);
269
240
  }
270
241
  }
271
242
 
272
- th {
243
+ &.sorter-desc .table-header-sorter {
244
+ display: initial;
273
245
 
274
- &:not(.sorter-desc):not(.sorter-asc):hover .table-header-sorter {
275
- .arrow-up {
276
- fill: var(--sort-arrow-hover-color);
277
- }
246
+ .arrow-up {
247
+ fill: var(--sort-arrow-active-sub-color);
248
+ }
278
249
 
279
- .arrow-down {
280
- fill: var(--sort-arrow-hover-color);
281
- }
250
+ .arrow-down {
251
+ fill: var(--sort-arrow-active-color);
282
252
  }
253
+ }
283
254
 
284
- &.sorter-desc .table-header-sorter {
285
- display: initial;
255
+ &.sorter-asc .table-header-sorter {
256
+ display: initial;
286
257
 
287
- .arrow-up {
288
- fill: var(--sort-arrow-active-sub-color);
289
- }
258
+ .arrow-up {
259
+ fill: var(--sort-arrow-active-color);
260
+ }
290
261
 
291
- .arrow-down {
292
- fill: var(--sort-arrow-active-color);
293
- }
262
+ .arrow-down {
263
+ fill: var(--sort-arrow-active-sub-color);
294
264
  }
265
+ }
295
266
 
296
- &.sorter-asc .table-header-sorter {
297
- display: initial;
267
+ }
298
268
 
299
- .arrow-up {
300
- fill: var(--sort-arrow-active-color);
301
- }
269
+ thead {
270
+ tr {
271
+ background-color: var(--th-bgc);
272
+ height: var(--header-row-height);
302
273
 
303
- .arrow-down {
304
- fill: var(--sort-arrow-active-sub-color);
305
- }
274
+ &:first-child th {
275
+ position: sticky;
276
+ top: 0;
306
277
  }
278
+ }
279
+ }
307
280
 
281
+ /* stk-table-main 这层为了增加选择器优先级,防止被斑马纹颜色覆盖*/
282
+ .stk-table-main tbody tr {
283
+ background-color: var(--td-bgc);
284
+ height: var(--row-height);
285
+ /** 一行分层,有利于高亮行重绘
286
+ transform: translateZ(0);*/
308
287
 
288
+ &:hover {
289
+ background-color: var(--tr-hover-bgc);
309
290
  }
310
291
 
311
- .table-header-cell-wrapper {
312
- max-width: 100%;
313
- /*最大宽度不超过列宽*/
314
- display: inline-flex;
315
- align-items: center;
292
+ &.active {
293
+ background-color: var(--tr-active-bgc);
316
294
  }
295
+ }
317
296
 
318
- .table-header-title {
319
- overflow: hidden;
320
- align-self: flex-start;
321
- }
322
297
 
323
- .table-header-sorter {
324
- flex-shrink: 0;
325
- margin-left: 4px;
326
- width: 16px;
327
- height: 16px;
328
- display: none;
298
+ /** 列宽调整指示器 */
299
+ .column-resize-indicator {
300
+ width: 0;
301
+ height: 100%;
302
+ border-left: 2px solid var(--col-resize-indicator-color);
303
+ position: absolute;
304
+ z-index: 10;
305
+ display: none;
306
+ pointer-events: none;
307
+ }
329
308
 
330
- .arrow-up,
331
- .arrow-down {
332
- fill: var(--sort-arrow-color);
333
- }
309
+ .stk-table-main {
310
+ border-spacing: 0;
311
+ border-collapse: separate;
312
+ width: fit-content;
313
+ /* 不加会导致width 超过100%时为100%,行hover高亮会断开*/
314
+ min-width: 100%;
315
+
316
+ &.fixed-mode {
317
+ table-layout: fixed;
318
+ min-width: min-content;
334
319
  }
320
+ }
335
321
 
336
- .table-header-resizer {
337
- position: absolute;
338
- top: 0;
339
- bottom: 0;
340
- cursor: col-resize;
341
- width: var(--resize-handle-width);
322
+ .fixed-cell {
323
+ background-color: inherit;
324
+ /* 防止横向滚动后透明*/
325
+ }
342
326
 
343
- &.left {
344
- left: 0;
345
- }
327
+ .highlight-cell {
328
+ animation: stk-table-dim var(--highlight-duration);
329
+ animation-timing-function: var(--highlight-timing-function);
330
+ /* 必须分开些,否则var(step(x))不兼容旧浏览器*/
331
+ }
346
332
 
347
- &.right {
348
- right: 0;
349
- }
333
+ .seq-column {
334
+ text-align: center;
335
+ }
336
+
337
+ /*固定列阴影-左*/
338
+ .fixed-cell--left {
339
+ --shadow-rotate: 90deg;
340
+
341
+ &.fixed-cell--shadow::after {
342
+ right: -10px;
350
343
  }
344
+ }
351
345
 
352
- tbody {
346
+ /*固定列阴影-右*/
347
+ .fixed-cell--right {
348
+ --shadow-rotate: -90deg;
353
349
 
354
- tr {
355
- background-color: var(--td-bgc);
356
- height: var(--row-height);
357
- /** 一行分层,有利于高亮行重绘*/
358
- transform: translateZ(0);
350
+ &.fixed-cell--shadow::after {
351
+ left: -10px;
352
+ }
353
+ }
359
354
 
360
- /* td inherit tr bgc*/
361
- &.highlight-row {
362
- animation: stk-table-dim var(--highlight-duration) var(--highlight-easing);
363
- }
364
-
355
+ /*固定列阴影*/
356
+ .fixed-cell--shadow::after {
357
+ content: '';
358
+ width: 10px;
359
+ height: 100%;
360
+ top: 0px;
361
+ position: absolute;
362
+ pointer-events: none;
363
+ background-image: linear-gradient(var(--shadow-rotate), var(--fixed-col-shadow-color-from), var(--fixed-col-shadow-color-to)),
364
+ }
365
365
 
366
- &.hover,
367
- &:hover {
368
- background-color: var(--tr-hover-bgc);
369
- }
366
+ .table-header-cell-wrapper {
367
+ max-width: 100%;
368
+ /*最大宽度不超过列宽*/
369
+ display: inline-flex;
370
+ align-items: center;
371
+ }
370
372
 
371
- &.active {
372
- background-color: var(--tr-active-bgc);
373
- }
374
- }
373
+ .table-header-title {
374
+ overflow: hidden;
375
+ align-self: flex-start;
376
+ }
375
377
 
378
+ .table-header-sorter {
379
+ flex-shrink: 0;
380
+ margin-left: 4px;
381
+ width: 16px;
382
+ height: 16px;
383
+ display: none;
384
+
385
+ .arrow-up,
386
+ .arrow-down {
387
+ fill: var(--sort-arrow-color);
376
388
  }
377
389
  }
378
390
 
391
+ .table-header-resizer {
392
+ position: absolute;
393
+ top: 0;
394
+ bottom: 0;
395
+ cursor: col-resize;
396
+ width: var(--resize-handle-width);
397
+
398
+ &.left {
399
+ left: 0;
400
+ }
401
+
402
+ &.right {
403
+ right: 0;
404
+ }
405
+ }
406
+
407
+ /* td inherit tr bgc*/
408
+ .highlight-row {
409
+ animation: stk-table-dim var(--highlight-duration);
410
+ /* 必须分开写,否则var(step(x))不兼容旧浏览器*/
411
+ animation-timing-function: var(--highlight-timing-function);
412
+ }
413
+
379
414
  .stk-table-no-data {
380
415
  background-color: var(--table-bgc);
381
416
  line-height: var(--row-height);
@@ -395,40 +430,4 @@
395
430
  }
396
431
  }
397
432
 
398
- /**虚拟滚动模式 */
399
- &.virtual {
400
-
401
- /* 为不影响布局,表头行高要定死*/
402
- .table-header-cell-wrapper {
403
- overflow: hidden;
404
- max-height: var(--header-row-height);
405
- }
406
-
407
- tbody {
408
- position: relative;
409
-
410
- tr {
411
-
412
- td {
413
- height: var(--row-height);
414
- line-height: 1;
415
-
416
- .table-cell-wrapper {
417
- max-height: var(--row-height);
418
- overflow: hidden;
419
- }
420
- }
421
- }
422
- }
423
-
424
- .padding-top-tr td {
425
- height: 0;
426
- }
427
- }
428
-
429
- &.virtual-x {
430
- .virtual-x-left {
431
- padding: 0;
432
- }
433
- }
434
433
  }
@@ -88,7 +88,7 @@ export function useFixedStyle<DT extends Record<string, any>>({
88
88
  if (tagType === TagType.TH) {
89
89
  // TH
90
90
  if (IS_LEGACY_MODE) {
91
- style.top = virtualScroll.value.scrollTop + depth * props.rowHeight + 'px';
91
+ style.top = virtualScroll.value.scrollTop + 'px';
92
92
  } else {
93
93
  style.top = depth * props.rowHeight + 'px';
94
94
  }
@@ -1,6 +1,6 @@
1
1
  import { interpolateRgb } from 'd3-interpolate';
2
2
  import { Ref, computed } from 'vue';
3
- import { HIGHLIGHT_CELL_CLASS, HIGHLIGHT_COLOR, HIGHLIGHT_DURATION, HIGHLIGHT_FREQ, HIGHLIGHT_ROW_CLASS, IS_LEGACY_MODE } from './const';
3
+ import { HIGHLIGHT_CELL_CLASS, HIGHLIGHT_COLOR, HIGHLIGHT_DURATION, HIGHLIGHT_FREQ, HIGHLIGHT_ROW_CLASS } from './const';
4
4
  import { HighlightConfig, UniqKey } from './types';
5
5
 
6
6
  type Params = {
@@ -114,7 +114,7 @@ export function useHighlight({ props, stkTableId, tableContainerRef }: Params) {
114
114
  /** 经过的时间 ÷ 高亮持续时间 计算出 颜色过渡进度 (0-1) */
115
115
  const progress = (nowTs - highlightStart) / highlightDuration;
116
116
  let bgc = '';
117
- if (0 < progress && progress < 1) {
117
+ if (0 <= progress && progress <= 1) {
118
118
  bgc = highlightInter.value(progress);
119
119
  } else {
120
120
  highlightDimRowsJs.delete(rowKeyValue);
@@ -128,7 +128,7 @@ export function useHighlight({ props, stkTableId, tableContainerRef }: Params) {
128
128
  } else {
129
129
  // 没有则停止循环
130
130
  calcHighlightDimLoopJs = false;
131
- highlightDimRowsJs.clear();
131
+ highlightDimRowsJs.clear(); // TODO: 是否需要 清除
132
132
  }
133
133
  }, highlightFrequency);
134
134
  };
@@ -198,13 +198,13 @@ export function useHighlight({ props, stkTableId, tableContainerRef }: Params) {
198
198
  ...option,
199
199
  };
200
200
 
201
- const nowTs = Date.now();
202
201
  if (method === 'css' || useCss) {
203
202
  // -------- use css keyframe
204
203
  highlightRowsInCssKeyframe(rowKeyValues, className, duration);
205
204
  } else if (method === 'animation') {
206
205
  if (props.virtual) {
207
206
  // -------- 用animation 接口实现动画
207
+ const nowTs = Date.now();
208
208
  for (let i = 0; i < rowKeyValues.length; i++) {
209
209
  const rowKeyValue = rowKeyValues[i];
210
210
  const store: HighlightDimRowStore = { ts: nowTs, visible: false, keyframe, duration };
@@ -222,6 +222,7 @@ export function useHighlight({ props, stkTableId, tableContainerRef }: Params) {
222
222
  }
223
223
  } else if (method === 'js') {
224
224
  // -------- 用js计算颜色渐变的高亮方案
225
+ const nowTs = Date.now();
225
226
  for (let i = 0; i < rowKeyValues.length; i++) {
226
227
  const rowKeyValue = rowKeyValues[i];
227
228
  highlightDimRowsJs.set(rowKeyValue, nowTs);
@@ -83,7 +83,7 @@ export function useVirtualScroll<DT extends Record<string, any>>({
83
83
  const virtual_dataSourcePart = computed(() => {
84
84
  if (!virtual_on.value) return dataSourceCopy.value;
85
85
  const { startIndex, endIndex } = virtualScroll.value;
86
- return dataSourceCopy.value.slice(startIndex, endIndex);
86
+ return dataSourceCopy.value.slice(startIndex, endIndex + 1);
87
87
  });
88
88
 
89
89
  const virtual_offsetBottom = computed(() => {
@@ -184,6 +184,10 @@ export function useVirtualScroll<DT extends Record<string, any>>({
184
184
  const { rowHeight, pageSize, scrollTop, startIndex: oldStartIndex } = virtualScroll.value;
185
185
  // 先更新滚动条位置记录,其他地方可能有依赖。(stripe 时ArrowUp/Down滚动依赖)
186
186
  virtualScroll.value.scrollTop = sTop;
187
+
188
+ // 非虚拟滚动不往下执行
189
+ if (!virtual_on.value) return;
190
+
187
191
  let startIndex = Math.floor(sTop / rowHeight);
188
192
  if (props.stripe) {
189
193
  startIndex -= 1; //预渲染1行
@@ -202,13 +206,11 @@ export function useVirtualScroll<DT extends Record<string, any>>({
202
206
  }
203
207
  let endIndex = startIndex + pageSize;
204
208
  if (props.stripe) {
205
- // 由于上方预渲染一行,这里也要预渲染1+1行
206
- endIndex += 2;
209
+ // 由于stripe上方预渲染-1行,这里也要预渲染1+1行
210
+ endIndex += 1;
207
211
  }
208
212
  const offsetTop = startIndex * rowHeight; // startIndex之前的高度
209
- if (endIndex > dataSourceCopy.value.length) {
210
- endIndex = dataSourceCopy.value.length; // 溢出index修正
211
- }
213
+ endIndex = Math.min(endIndex, dataSourceCopy.value.length); // 溢出index修正
212
214
  if (vue2ScrollYTimeout) {
213
215
  window.clearTimeout(vue2ScrollYTimeout);
214
216
  }
@@ -269,13 +271,12 @@ export function useVirtualScroll<DT extends Record<string, any>>({
269
271
  colWidthSum += getCalculatedColWidth(col);
270
272
  // 列宽大于容器宽度则停止
271
273
  if (colWidthSum >= containerWidth) {
272
- endIndex = colIndex + 1; // 由于slice[start,end),要加1
274
+ endIndex = colIndex + 1; // slice endIndex + 1
273
275
  break;
274
276
  }
275
277
  }
276
- if (endIndex > headerLength) {
277
- endIndex = headerLength;
278
- }
278
+
279
+ endIndex = Math.min(endIndex, headerLength);
279
280
 
280
281
  if (vue2ScrollXTimeout) {
281
282
  window.clearTimeout(vue2ScrollXTimeout);
@@ -5,7 +5,7 @@ import { Order, SortConfig, SortOption, SortState, StkTableColumn } from './type
5
5
  function isEmptyValue(val: any, isNumber?: boolean) {
6
6
  let isEmpty = val === null || val === '';
7
7
  if (isNumber) {
8
- isEmpty ||= typeof val === 'boolean' || Number.isNaN(+val);
8
+ isEmpty = isEmpty || typeof val === 'boolean' || Number.isNaN(+val);
9
9
  }
10
10
  return isEmpty;
11
11
  }