stk-table-vue 0.7.0 → 0.7.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.
@@ -94,7 +94,7 @@ declare const __VLS_component: import('vue').DefineComponent<import('vue').Extra
94
94
  /** 当前行再次点击否可以取消 (rowActive=true)*/
95
95
  rowCurrentRevokable?: boolean;
96
96
  /** 表头行高。default = rowHeight */
97
- headerRowHeight?: number | null;
97
+ headerRowHeight?: number | string | null;
98
98
  /** 虚拟滚动 */
99
99
  virtual?: boolean;
100
100
  /** x轴虚拟滚动(必须设置列宽)*/
@@ -194,8 +194,11 @@ declare const __VLS_component: import('vue').DefineComponent<import('vue').Extra
194
194
  * - true: 不使用 onwheel 滚动。鼠标滚轮滚动时更加平滑。滚动过快时会白屏。
195
195
  */
196
196
  smoothScroll?: boolean;
197
- /** 按整数行纵向滚动 */
198
- scrollRowByRow?: boolean;
197
+ /**
198
+ * 按整数行纵向滚动
199
+ * - scrollbar:仅拖动滚动条生效
200
+ */
201
+ scrollRowByRow?: boolean | "scrollbar";
199
202
  }>, {
200
203
  width: string;
201
204
  fixedMode: boolean;
@@ -372,7 +375,7 @@ declare const __VLS_component: import('vue').DefineComponent<import('vue').Extra
372
375
  })) | (import('./types/index').UniqKey | (PrivateRowDT & {
373
376
  children?: (PrivateRowDT & /*elided*/ any)[];
374
377
  }))[], option?: {
375
- expand? /** 是否高亮选中的行 */: boolean;
378
+ expand?: boolean;
376
379
  }) => void;
377
380
  }, {}, {}, {}, import('vue').ComponentOptionsMixin, import('vue').ComponentOptionsMixin, {
378
381
  "sort-change": (col: StkTableColumn<any> | null, order: Order, data: any[], sortConfig: SortConfig<any>) => void;
@@ -456,7 +459,7 @@ declare const __VLS_component: import('vue').DefineComponent<import('vue').Extra
456
459
  /** 当前行再次点击否可以取消 (rowActive=true)*/
457
460
  rowCurrentRevokable?: boolean;
458
461
  /** 表头行高。default = rowHeight */
459
- headerRowHeight?: number | null;
462
+ headerRowHeight?: number | string | null;
460
463
  /** 虚拟滚动 */
461
464
  virtual?: boolean;
462
465
  /** x轴虚拟滚动(必须设置列宽)*/
@@ -556,8 +559,11 @@ declare const __VLS_component: import('vue').DefineComponent<import('vue').Extra
556
559
  * - true: 不使用 onwheel 滚动。鼠标滚轮滚动时更加平滑。滚动过快时会白屏。
557
560
  */
558
561
  smoothScroll?: boolean;
559
- /** 按整数行纵向滚动 */
560
- scrollRowByRow?: boolean;
562
+ /**
563
+ * 按整数行纵向滚动
564
+ * - scrollbar:仅拖动滚动条生效
565
+ */
566
+ scrollRowByRow?: boolean | "scrollbar";
561
567
  }>, {
562
568
  width: string;
563
569
  fixedMode: boolean;
@@ -670,7 +676,7 @@ declare const __VLS_component: import('vue').DefineComponent<import('vue').Extra
670
676
  stripe: boolean;
671
677
  optimizeVue2Scroll: boolean;
672
678
  rowKey: UniqKeyProp;
673
- headerRowHeight: number | null;
679
+ headerRowHeight: number | string | null;
674
680
  fixedMode: boolean;
675
681
  theme: "light" | "dark";
676
682
  rowHover: boolean;
@@ -708,7 +714,7 @@ declare const __VLS_component: import('vue').DefineComponent<import('vue').Extra
708
714
  treeConfig: TreeConfig;
709
715
  cellFixedMode: "sticky" | "relative";
710
716
  smoothScroll: boolean;
711
- scrollRowByRow: boolean;
717
+ scrollRowByRow: boolean | "scrollbar";
712
718
  }, {}, {}, {}, string, import('vue').ComponentProvideOptions, true, {}, any>;
713
719
  declare const _default: __VLS_WithTemplateSlots<typeof __VLS_component, ReturnType<typeof __VLS_template>>;
714
720
  export default _default;
@@ -38,7 +38,7 @@ export type CustomCell<T extends CustomCellProps<U> | CustomHeaderCellProps<U>,
38
38
  /** 表格列配置 */
39
39
  export type StkTableColumn<T extends Record<string, any>> = {
40
40
  /**
41
- * 用于区分相同dataIndex 的列。
41
+ * 列唯一键,(可选),不传则默认取dataIndex 字段作为列唯一键。
42
42
  */
43
43
  key?: any;
44
44
  /**
@@ -0,0 +1,11 @@
1
+ import { Ref } from 'vue';
2
+
3
+ type Params = {
4
+ props: any;
5
+ tableContainerRef: Ref<HTMLElement | undefined>;
6
+ };
7
+ export declare function useScrollRowByRow({ props, tableContainerRef }: Params): {
8
+ isSRBRActive: import('vue').ComputedRef<any>;
9
+ isDragScroll: Ref<boolean, boolean>;
10
+ };
11
+ export {};
@@ -14,7 +14,9 @@ export declare function isEmptyValue(val: any, isNumber?: boolean): boolean;
14
14
  * @param targetArray 表格数据
15
15
  * @return targetArray 的浅拷贝
16
16
  */
17
- export declare function insertToOrderedArray<T extends object>(sortState: SortState<T>, newItem: T, targetArray: T[], sortConfig?: SortConfig<T>): T[];
17
+ export declare function insertToOrderedArray<T extends object>(sortState: SortState<T>, newItem: T, targetArray: T[], sortConfig?: SortConfig<T> & {
18
+ customCompare?: (a: T, b: T) => number;
19
+ }): T[];
18
20
  /**
19
21
  * 二分查找
20
22
  * @param searchArray 查找数组
@@ -1,4 +1,4 @@
1
- import { createElementBlock, openBlock, createElementVNode, watch, onMounted, onBeforeUnmount, ref, computed, shallowRef, defineComponent, nextTick, toRaw, normalizeStyle, normalizeClass, unref, createCommentVNode, renderSlot, Fragment, renderList, createBlock, resolveDynamicComponent, toDisplayString, createTextVNode, withCtx, createVNode } from "vue";
1
+ import { createElementBlock, openBlock, createElementVNode, watch, onMounted, onBeforeUnmount, ref, computed, shallowRef, onUnmounted, defineComponent, nextTick, toRaw, normalizeStyle, normalizeClass, unref, createCommentVNode, renderSlot, Fragment, renderList, createBlock, resolveDynamicComponent, toDisplayString, createTextVNode, withCtx, createVNode } from "vue";
2
2
  const _export_sfc = (sfc, props) => {
3
3
  const target = sfc.__vccOpts || sfc;
4
4
  for (const [key, val] of props) {
@@ -61,7 +61,6 @@ function isEmptyValue(val, isNumber) {
61
61
  }
62
62
  function insertToOrderedArray(sortState, newItem, targetArray, sortConfig = {}) {
63
63
  const { dataIndex, sortField, order } = sortState;
64
- sortConfig = { emptyToBottom: false, ...sortConfig };
65
64
  let { sortType } = sortState;
66
65
  const field = sortField || dataIndex;
67
66
  if (!sortType) sortType = typeof newItem[field];
@@ -70,15 +69,19 @@ function insertToOrderedArray(sortState, newItem, targetArray, sortConfig = {})
70
69
  data.unshift(newItem);
71
70
  return data;
72
71
  }
72
+ const { emptyToBottom, customCompare, stringLocaleCompare } = { emptyToBottom: false, ...sortConfig };
73
73
  const targetVal = newItem[field];
74
- if (sortConfig.emptyToBottom && isEmptyValue(targetVal)) {
74
+ if (emptyToBottom && isEmptyValue(targetVal)) {
75
75
  data.push(newItem);
76
76
  } else {
77
+ const customCompareFn = customCompare || ((a, b) => {
78
+ const midVal = a[field];
79
+ const compareRes = strCompare(midVal, targetVal, isNumber, stringLocaleCompare);
80
+ return order === "asc" ? compareRes : -compareRes;
81
+ });
77
82
  const isNumber = sortType === "number";
78
83
  const sIndex = binarySearch(data, (midIndex) => {
79
- const midVal = data[midIndex][field];
80
- const compareRes = strCompare(midVal, targetVal, isNumber, sortConfig.stringLocaleCompare);
81
- return order === "asc" ? compareRes : -compareRes;
84
+ return customCompareFn(data[midIndex], newItem);
82
85
  });
83
86
  data.splice(sIndex, 0, newItem);
84
87
  }
@@ -636,7 +639,7 @@ function useHighlight({ props, stkTableId, tableContainerRef }) {
636
639
  const recursion = () => {
637
640
  window.requestAnimationFrame(
638
641
  () => {
639
- const nowTs = Date.now();
642
+ const nowTs = performance.now();
640
643
  highlightDimRowsAnimation.forEach((store, rowKeyValue) => {
641
644
  const { ts, duration } = store;
642
645
  const timeOffset = nowTs - ts;
@@ -660,13 +663,13 @@ function useHighlight({ props, stkTableId, tableContainerRef }) {
660
663
  function setHighlightDimCell(rowKeyValue, colKeyValue, option = {}) {
661
664
  var _a;
662
665
  const cellEl = (_a = tableContainerRef.value) == null ? void 0 : _a.querySelector(`[data-cell-key="${rowKeyValue}--${colKeyValue}"]`);
666
+ if (!cellEl) return;
663
667
  const { className, method, duration, keyframe } = {
664
668
  className: HIGHLIGHT_CELL_CLASS,
665
669
  method: "animation",
666
670
  ...defaultHighlightDimOption.value,
667
671
  ...option
668
672
  };
669
- if (!cellEl) return;
670
673
  if (method === "animation") {
671
674
  cellEl.animate(keyframe, duration);
672
675
  } else {
@@ -675,17 +678,16 @@ function useHighlight({ props, stkTableId, tableContainerRef }) {
675
678
  }
676
679
  function setHighlightDimRow(rowKeyValues, option = {}) {
677
680
  if (!Array.isArray(rowKeyValues)) rowKeyValues = [rowKeyValues];
681
+ if (!rowKeyValues.length) return;
678
682
  const { className, method, keyframe, duration } = {
679
683
  className: HIGHLIGHT_ROW_CLASS,
680
684
  method: "animation",
681
685
  ...defaultHighlightDimOption.value,
682
686
  ...option
683
687
  };
684
- if (method === "css") {
685
- highlightRowsInCssKeyframe(rowKeyValues, className, duration);
686
- } else if (method === "animation") {
688
+ if (method === "animation") {
687
689
  if (props.virtual) {
688
- const nowTs = Date.now();
690
+ const nowTs = performance.now();
689
691
  for (let i = 0; i < rowKeyValues.length; i++) {
690
692
  const rowKeyValue = rowKeyValues[i];
691
693
  const store = { ts: nowTs, visible: false, keyframe, duration };
@@ -700,6 +702,8 @@ function useHighlight({ props, stkTableId, tableContainerRef }) {
700
702
  rowEl.animate(keyframe, duration);
701
703
  }
702
704
  }
705
+ } else {
706
+ highlightRowsInCssKeyframe(rowKeyValues, className, duration);
703
707
  }
704
708
  }
705
709
  function highlightRowsInCssKeyframe(rowKeyValues, className, duration) {
@@ -897,6 +901,63 @@ function useRowExpand({ dataSourceCopy, rowKeyGen, emits }) {
897
901
  setRowExpand
898
902
  };
899
903
  }
904
+ function useScrollRowByRow({ props, tableContainerRef }) {
905
+ let isMouseDown = false;
906
+ let isAddListeners = false;
907
+ const isDragScroll = ref(false);
908
+ const onlyDragScroll = computed(() => props.scrollRowByRow === "scrollbar");
909
+ const isSRBRActive = computed(() => {
910
+ if (onlyDragScroll.value) {
911
+ return isDragScroll.value;
912
+ }
913
+ return props.scrollRowByRow;
914
+ });
915
+ watch(
916
+ () => onlyDragScroll.value,
917
+ (v) => {
918
+ if (v) {
919
+ addEventListener();
920
+ } else {
921
+ removeEventListener();
922
+ }
923
+ }
924
+ );
925
+ onMounted(() => {
926
+ addEventListener();
927
+ });
928
+ onUnmounted(() => {
929
+ removeEventListener();
930
+ });
931
+ function addEventListener() {
932
+ if (isAddListeners || !onlyDragScroll.value) return;
933
+ const container = tableContainerRef.value;
934
+ if (!container) return;
935
+ container.addEventListener("mousedown", handleMouseDown);
936
+ container.addEventListener("mouseup", handleMouseUp);
937
+ container.addEventListener("scroll", handleScroll);
938
+ isAddListeners = true;
939
+ }
940
+ function removeEventListener() {
941
+ const container = tableContainerRef.value;
942
+ if (!container) return;
943
+ container.removeEventListener("mousedown", handleMouseDown);
944
+ container.removeEventListener("mouseup", handleMouseUp);
945
+ container.removeEventListener("scroll", handleScroll);
946
+ isAddListeners = false;
947
+ }
948
+ function handleMouseDown() {
949
+ isMouseDown = true;
950
+ }
951
+ function handleMouseUp() {
952
+ isMouseDown = false;
953
+ isDragScroll.value = false;
954
+ }
955
+ function handleScroll() {
956
+ if (!isMouseDown) return;
957
+ isDragScroll.value = true;
958
+ }
959
+ return { isSRBRActive, isDragScroll };
960
+ }
900
961
  function useThDrag({ props, emits, colKeyGen }) {
901
962
  const findParentTH = (e) => e.target.closest("th");
902
963
  const dragConfig = computed(() => {
@@ -1061,6 +1122,125 @@ function useTrDrag({ props, emits, dataSourceCopy }) {
1061
1122
  onTrDragEnd
1062
1123
  };
1063
1124
  }
1125
+ function useTree({ props, dataSourceCopy, rowKeyGen, emits }) {
1126
+ const { defaultExpandAll, defaultExpandKeys, defaultExpandLevel } = props.treeConfig;
1127
+ function toggleTreeNode(row, col) {
1128
+ const expand = row ? !row.__T_EXPANDED__ : false;
1129
+ privateSetTreeExpand(row, { expand, col, isClick: true });
1130
+ }
1131
+ function privateSetTreeExpand(row, option) {
1132
+ const rowKeyOrRowArr = Array.isArray(row) ? row : [row];
1133
+ const tempData = dataSourceCopy.value.slice();
1134
+ for (let i = 0; i < rowKeyOrRowArr.length; i++) {
1135
+ const rowKeyOrRow = rowKeyOrRowArr[i];
1136
+ let rowKey;
1137
+ if (typeof rowKeyOrRow === "string") {
1138
+ rowKey = rowKeyOrRow;
1139
+ } else {
1140
+ rowKey = rowKeyGen(rowKeyOrRow);
1141
+ }
1142
+ const index = tempData.findIndex((it) => rowKeyGen(it) === rowKey);
1143
+ if (index === -1) {
1144
+ console.warn("treeExpandRow failed.rowKey:", rowKey);
1145
+ return;
1146
+ }
1147
+ const row2 = tempData[index];
1148
+ const level = row2.__T_LV__ || 0;
1149
+ let expanded = option == null ? void 0 : option.expand;
1150
+ if (expanded === void 0) {
1151
+ expanded = !row2.__T_EXPANDED__;
1152
+ }
1153
+ if (expanded) {
1154
+ const children = expandNode(row2, level);
1155
+ tempData.splice(index + 1, 0, ...children);
1156
+ } else {
1157
+ const deleteCount = foldNode(index, tempData, level);
1158
+ tempData.splice(index + 1, deleteCount);
1159
+ }
1160
+ setNodeExpanded(row2, expanded, level);
1161
+ if (option.isClick) {
1162
+ emits("toggle-tree-expand", { expanded: Boolean(expanded), row: row2, col: option.col });
1163
+ }
1164
+ }
1165
+ dataSourceCopy.value = tempData;
1166
+ }
1167
+ function setTreeExpand(row, option) {
1168
+ privateSetTreeExpand(row, { ...option, isClick: false });
1169
+ }
1170
+ function setNodeExpanded(row, expanded, level, parent) {
1171
+ row.__T_EXPANDED__ = expanded;
1172
+ if (level !== void 0) {
1173
+ row.__T_LV__ = level;
1174
+ }
1175
+ if (parent) {
1176
+ row.__T_PARENT_K__ = rowKeyGen(parent);
1177
+ }
1178
+ }
1179
+ function flatTreeData(data) {
1180
+ const result = [];
1181
+ (function recursion(data2, level, parent) {
1182
+ if (!data2) return;
1183
+ for (let i = 0; i < data2.length; i++) {
1184
+ const item = data2[i];
1185
+ result.push(item);
1186
+ const isExpanded = Boolean(item.__T_EXPANDED__);
1187
+ setNodeExpanded(item, isExpanded, level, parent);
1188
+ if (!isExpanded) {
1189
+ if (defaultExpandAll) {
1190
+ setNodeExpanded(item, true);
1191
+ } else {
1192
+ if (defaultExpandLevel) {
1193
+ if (level < defaultExpandLevel) {
1194
+ setNodeExpanded(item, true);
1195
+ }
1196
+ }
1197
+ if (defaultExpandKeys) {
1198
+ if (defaultExpandKeys.includes(rowKeyGen(item))) {
1199
+ setNodeExpanded(item, true);
1200
+ }
1201
+ }
1202
+ if (!item.__T_EXPANDED__) {
1203
+ continue;
1204
+ }
1205
+ }
1206
+ }
1207
+ recursion(item.children, level + 1, item);
1208
+ }
1209
+ })(data, 0);
1210
+ return result;
1211
+ }
1212
+ function expandNode(row, level) {
1213
+ let result = [];
1214
+ row.children && row.children.forEach((child) => {
1215
+ result.push(child);
1216
+ const childLv = level + 1;
1217
+ if (child.__T_EXPANDED__ && child.children) {
1218
+ const res = expandNode(child, childLv);
1219
+ result = result.concat(res);
1220
+ } else {
1221
+ setNodeExpanded(child, false, childLv, row);
1222
+ }
1223
+ });
1224
+ return result;
1225
+ }
1226
+ function foldNode(index, tempData, level) {
1227
+ let deleteCount = 0;
1228
+ for (let i = index + 1; i < tempData.length; i++) {
1229
+ const child = tempData[i];
1230
+ if (child.__T_LV__ && child.__T_LV__ > level) {
1231
+ deleteCount++;
1232
+ } else {
1233
+ break;
1234
+ }
1235
+ }
1236
+ return deleteCount;
1237
+ }
1238
+ return {
1239
+ toggleTreeNode,
1240
+ setTreeExpand,
1241
+ flatTreeData
1242
+ };
1243
+ }
1064
1244
  const VUE2_SCROLL_TIMEOUT_MS = 200;
1065
1245
  function useVirtualScroll({
1066
1246
  props,
@@ -1377,125 +1557,6 @@ function useVirtualScroll({
1377
1557
  clearAllAutoHeight
1378
1558
  };
1379
1559
  }
1380
- function useTree({ props, dataSourceCopy, rowKeyGen, emits }) {
1381
- const { defaultExpandAll, defaultExpandKeys, defaultExpandLevel } = props.treeConfig;
1382
- function toggleTreeNode(row, col) {
1383
- const expand = row ? !row.__T_EXPANDED__ : false;
1384
- privateSetTreeExpand(row, { expand, col, isClick: true });
1385
- }
1386
- function privateSetTreeExpand(row, option) {
1387
- const rowKeyOrRowArr = Array.isArray(row) ? row : [row];
1388
- const tempData = dataSourceCopy.value.slice();
1389
- for (let i = 0; i < rowKeyOrRowArr.length; i++) {
1390
- const rowKeyOrRow = rowKeyOrRowArr[i];
1391
- let rowKey;
1392
- if (typeof rowKeyOrRow === "string") {
1393
- rowKey = rowKeyOrRow;
1394
- } else {
1395
- rowKey = rowKeyGen(rowKeyOrRow);
1396
- }
1397
- const index = tempData.findIndex((it) => rowKeyGen(it) === rowKey);
1398
- if (index === -1) {
1399
- console.warn("treeExpandRow failed.rowKey:", rowKey);
1400
- return;
1401
- }
1402
- const row2 = tempData[index];
1403
- const level = row2.__T_LV__ || 0;
1404
- let expanded = option == null ? void 0 : option.expand;
1405
- if (expanded === void 0) {
1406
- expanded = !row2.__T_EXPANDED__;
1407
- }
1408
- if (expanded) {
1409
- const children = expandNode(row2, level);
1410
- tempData.splice(index + 1, 0, ...children);
1411
- } else {
1412
- const deleteCount = foldNode(index, tempData, level);
1413
- tempData.splice(index + 1, deleteCount);
1414
- }
1415
- setNodeExpanded(row2, expanded, level);
1416
- if (option.isClick) {
1417
- emits("toggle-tree-expand", { expanded: Boolean(expanded), row: row2, col: option.col });
1418
- }
1419
- }
1420
- dataSourceCopy.value = tempData;
1421
- }
1422
- function setTreeExpand(row, option) {
1423
- privateSetTreeExpand(row, { ...option, isClick: false });
1424
- }
1425
- function setNodeExpanded(row, expanded, level, parent) {
1426
- row.__T_EXPANDED__ = expanded;
1427
- if (level !== void 0) {
1428
- row.__T_LV__ = level;
1429
- }
1430
- if (parent) {
1431
- row.__T_PARENT_K__ = rowKeyGen(parent);
1432
- }
1433
- }
1434
- function flatTreeData(data) {
1435
- const result = [];
1436
- (function recursion(data2, level, parent) {
1437
- if (!data2) return;
1438
- for (let i = 0; i < data2.length; i++) {
1439
- const item = data2[i];
1440
- result.push(item);
1441
- const isExpanded = Boolean(item.__T_EXPANDED__);
1442
- setNodeExpanded(item, isExpanded, level, parent);
1443
- if (!isExpanded) {
1444
- if (defaultExpandAll) {
1445
- setNodeExpanded(item, true);
1446
- } else {
1447
- if (defaultExpandLevel) {
1448
- if (level < defaultExpandLevel) {
1449
- setNodeExpanded(item, true);
1450
- }
1451
- }
1452
- if (defaultExpandKeys) {
1453
- if (defaultExpandKeys.includes(rowKeyGen(item))) {
1454
- setNodeExpanded(item, true);
1455
- }
1456
- }
1457
- if (!item.__T_EXPANDED__) {
1458
- continue;
1459
- }
1460
- }
1461
- }
1462
- recursion(item.children, level + 1, item);
1463
- }
1464
- })(data, 0);
1465
- return result;
1466
- }
1467
- function expandNode(row, level) {
1468
- let result = [];
1469
- row.children && row.children.forEach((child) => {
1470
- result.push(child);
1471
- const childLv = level + 1;
1472
- if (child.__T_EXPANDED__ && child.children) {
1473
- const res = expandNode(child, childLv);
1474
- result = result.concat(res);
1475
- } else {
1476
- setNodeExpanded(child, false, childLv, row);
1477
- }
1478
- });
1479
- return result;
1480
- }
1481
- function foldNode(index, tempData, level) {
1482
- let deleteCount = 0;
1483
- for (let i = index + 1; i < tempData.length; i++) {
1484
- const child = tempData[i];
1485
- if (child.__T_LV__ && child.__T_LV__ > level) {
1486
- deleteCount++;
1487
- } else {
1488
- break;
1489
- }
1490
- }
1491
- return deleteCount;
1492
- }
1493
- return {
1494
- toggleTreeNode,
1495
- setTreeExpand,
1496
- flatTreeData
1497
- };
1498
- }
1499
1560
  const _hoisted_1 = ["data-col-key", "draggable", "rowspan", "colspan", "title", "onClick"];
1500
1561
  const _hoisted_2 = ["onMousedown"];
1501
1562
  const _hoisted_3 = { class: "table-header-title" };
@@ -1566,7 +1627,7 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
1566
1627
  treeConfig: { default: () => ({}) },
1567
1628
  cellFixedMode: { default: "sticky" },
1568
1629
  smoothScroll: { type: Boolean, default: DEFAULT_SMOOTH_SCROLL },
1569
- scrollRowByRow: { type: Boolean, default: false }
1630
+ scrollRowByRow: { type: [Boolean, String], default: false }
1570
1631
  },
1571
1632
  emits: ["sort-change", "row-click", "current-change", "cell-selected", "row-dblclick", "header-row-menu", "row-menu", "cell-click", "cell-mouseenter", "cell-mouseleave", "cell-mouseover", "cell-mousedown", "header-cell-click", "scroll", "scroll-x", "col-order-change", "th-drag-start", "th-drop", "row-order-change", "col-resize", "toggle-row-expand", "toggle-tree-expand", "update:columns"],
1572
1633
  setup(__props, { expose: __expose, emit: __emit }) {
@@ -1620,6 +1681,7 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
1620
1681
  }
1621
1682
  });
1622
1683
  const rowKeyGenCache = /* @__PURE__ */ new WeakMap();
1684
+ const { isSRBRActive } = useScrollRowByRow({ props, tableContainerRef });
1623
1685
  const { onThDragStart, onThDragOver, onThDrop, isHeaderDraggable } = useThDrag({ props, emits, colKeyGen });
1624
1686
  const { onTrDragStart, onTrDrop, onTrDragOver, onTrDragEnd, onTrDragEnter } = useTrDrag({ props, emits, dataSourceCopy });
1625
1687
  const {
@@ -1730,7 +1792,7 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
1730
1792
  nextTick(() => initVirtualScrollY());
1731
1793
  }
1732
1794
  const sortColValue = sortCol.value;
1733
- if (sortColValue) {
1795
+ if (!isEmptyValue(sortColValue) && !props.sortRemote) {
1734
1796
  const colKey = colKeyGen.value;
1735
1797
  const column = tableHeaderLast.value.find((it) => colKey(it) === sortColValue);
1736
1798
  onColumnSort(column, false);
@@ -2270,7 +2332,7 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
2270
2332
  "header-text-overflow": props.showHeaderOverflow,
2271
2333
  "fixed-relative-mode": isRelativeMode.value,
2272
2334
  "auto-row-height": props.autoRowHeight,
2273
- "scroll-row-by-row": props.scrollRowByRow
2335
+ "scroll-row-by-row": unref(isSRBRActive)
2274
2336
  }]),
2275
2337
  style: normalizeStyle({
2276
2338
  "--row-height": props.autoRowHeight ? void 0 : unref(virtualScroll).rowHeight + "px",
@@ -2281,7 +2343,7 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
2281
2343
  onScroll: onTableScroll,
2282
2344
  onWheel: onTableWheel
2283
2345
  }, [
2284
- props.scrollRowByRow && _ctx.virtual ? (openBlock(), createElementBlock("div", {
2346
+ unref(isSRBRActive) && _ctx.virtual ? (openBlock(), createElementBlock("div", {
2285
2347
  key: 0,
2286
2348
  class: "row-by-row-table-height",
2287
2349
  style: normalizeStyle({ height: dataSourceCopy.value.length * unref(virtualScroll).rowHeight + "px" })
@@ -2388,7 +2450,7 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
2388
2450
  onDragend: _cache[6] || (_cache[6] = //@ts-ignore
2389
2451
  (...args) => unref(onTrDragEnd) && unref(onTrDragEnd)(...args))
2390
2452
  }, [
2391
- unref(virtual_on) && !props.scrollRowByRow ? (openBlock(), createElementBlock("tr", {
2453
+ unref(virtual_on) && !unref(isSRBRActive) ? (openBlock(), createElementBlock("tr", {
2392
2454
  key: 0,
2393
2455
  style: normalizeStyle(`height:${unref(virtualScroll).offsetTop}px`),
2394
2456
  class: "padding-top-tr"
@@ -2523,7 +2585,7 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
2523
2585
  }), 128))
2524
2586
  ], 46, _hoisted_6);
2525
2587
  }), 128)),
2526
- unref(virtual_on) && !props.scrollRowByRow ? (openBlock(), createElementBlock("tr", {
2588
+ unref(virtual_on) && !unref(isSRBRActive) ? (openBlock(), createElementBlock("tr", {
2527
2589
  key: 1,
2528
2590
  style: normalizeStyle(`height: ${unref(virtual_offsetBottom)}px`)
2529
2591
  }, null, 4)) : createCommentVNode("", true)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "stk-table-vue",
3
- "version": "0.7.0",
3
+ "version": "0.7.2",
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",
@@ -40,7 +40,6 @@
40
40
  },
41
41
  "license": "MIT",
42
42
  "devDependencies": {
43
- "@types/d3-interpolate": "^3.0.4",
44
43
  "@types/mockjs": "^1.0.10",
45
44
  "@types/node": "^20.12.10",
46
45
  "@typescript-eslint/eslint-plugin": "^7.7.0",
@@ -60,7 +59,7 @@
60
59
  "postcss-discard-comments": "^6.0.2",
61
60
  "postcss-preset-env": "^9.5.11",
62
61
  "prettier": "^3.2.5",
63
- "stk-table-vue": "^0.6.17",
62
+ "stk-table-vue": "^0.7.1",
64
63
  "typescript": "^5.4.5",
65
64
  "vite": "^5.4.10",
66
65
  "vite-plugin-dts": "3.9.1",
@@ -25,7 +25,7 @@
25
25
  'header-text-overflow': props.showHeaderOverflow,
26
26
  'fixed-relative-mode': isRelativeMode,
27
27
  'auto-row-height': props.autoRowHeight,
28
- 'scroll-row-by-row': props.scrollRowByRow,
28
+ 'scroll-row-by-row': isSRBRActive,
29
29
  }"
30
30
  :style="{
31
31
  '--row-height': props.autoRowHeight ? void 0 : virtualScroll.rowHeight + 'px',
@@ -38,7 +38,7 @@
38
38
  >
39
39
  <!-- 这个元素用于整数行虚拟滚动时,撑开父容器的高度) -->
40
40
  <div
41
- v-if="props.scrollRowByRow && virtual"
41
+ v-if="isSRBRActive && virtual"
42
42
  class="row-by-row-table-height"
43
43
  :style="{ height: dataSourceCopy.length * virtualScroll.rowHeight + 'px' }"
44
44
  ></div>
@@ -114,7 +114,7 @@
114
114
  <!-- <tbody v-if="virtual_on" :style="{ height: `${virtualScroll.offsetTop}px` }"></tbody> -->
115
115
  <!-- <tbody :style="{ transform: `translateY(${virtualScroll.offsetTop}px)` }"> -->
116
116
  <tbody class="stk-tbody-main" @dragover="onTrDragOver" @dragenter="onTrDragEnter" @dragend="onTrDragEnd">
117
- <tr v-if="virtual_on && !props.scrollRowByRow" :style="`height:${virtualScroll.offsetTop}px`" class="padding-top-tr">
117
+ <tr v-if="virtual_on && !isSRBRActive" :style="`height:${virtualScroll.offsetTop}px`" class="padding-top-tr">
118
118
  <!--这个td用于配合虚拟滚动的th对应,防止列错位-->
119
119
  <td v-if="virtualX_on && fixedMode && headless" class="vt-x-left"></td>
120
120
  <template v-if="fixedMode && headless">
@@ -238,7 +238,7 @@
238
238
  </td>
239
239
  </template>
240
240
  </tr>
241
- <tr v-if="virtual_on && !props.scrollRowByRow" :style="`height: ${virtual_offsetBottom}px`"></tr>
241
+ <tr v-if="virtual_on && !isSRBRActive" :style="`height: ${virtual_offsetBottom}px`"></tr>
242
242
  </tbody>
243
243
  </table>
244
244
  <div v-if="(!dataSourceCopy || !dataSourceCopy.length) && showNoData" class="stk-table-no-data" :class="{ 'no-data-full': noDataFull }">
@@ -283,12 +283,13 @@ import { useGetFixedColPosition } from './useGetFixedColPosition';
283
283
  import { useHighlight } from './useHighlight';
284
284
  import { useKeyboardArrowScroll } from './useKeyboardArrowScroll';
285
285
  import { useRowExpand } from './useRowExpand';
286
+ import { useScrollRowByRow } from './useScrollRowByRow';
286
287
  import { useThDrag } from './useThDrag';
287
288
  import { useTrDrag } from './useTrDrag';
289
+ import { useTree } from './useTree';
288
290
  import { useVirtualScroll } from './useVirtualScroll';
289
291
  import { createStkTableId, getCalculatedColWidth, getColWidth } from './utils/constRefUtils';
290
- import { howDeepTheHeader, tableSort, transformWidthToStr } from './utils/index';
291
- import { useTree } from './useTree';
292
+ import { howDeepTheHeader, isEmptyValue, tableSort, transformWidthToStr } from './utils/index';
292
293
 
293
294
  /** Generic stands for DataType */
294
295
  type DT = any & PrivateRowDT;
@@ -331,7 +332,7 @@ const props = withDefaults(
331
332
  /** 当前行再次点击否可以取消 (rowActive=true)*/
332
333
  rowCurrentRevokable?: boolean;
333
334
  /** 表头行高。default = rowHeight */
334
- headerRowHeight?: number | null;
335
+ headerRowHeight?: number | string | null;
335
336
  /** 虚拟滚动 */
336
337
  virtual?: boolean;
337
338
  /** x轴虚拟滚动(必须设置列宽)*/
@@ -428,8 +429,11 @@ const props = withDefaults(
428
429
  * - true: 不使用 onwheel 滚动。鼠标滚轮滚动时更加平滑。滚动过快时会白屏。
429
430
  */
430
431
  smoothScroll?: boolean;
431
- /** 按整数行纵向滚动 */
432
- scrollRowByRow?: boolean;
432
+ /**
433
+ * 按整数行纵向滚动
434
+ * - scrollbar:仅拖动滚动条生效
435
+ */
436
+ scrollRowByRow?: boolean | 'scrollbar';
433
437
  }>(),
434
438
  {
435
439
  width: '',
@@ -738,6 +742,8 @@ const getEmptyCellText = computed(() => {
738
742
 
739
743
  const rowKeyGenCache = new WeakMap();
740
744
 
745
+ const { isSRBRActive } = useScrollRowByRow({ props, tableContainerRef });
746
+
741
747
  const { onThDragStart, onThDragOver, onThDrop, isHeaderDraggable } = useThDrag({ props, emits, colKeyGen });
742
748
 
743
749
  const { onTrDragStart, onTrDrop, onTrDragOver, onTrDragEnd, onTrDragEnter } = useTrDrag({ props, emits, dataSourceCopy });
@@ -870,7 +876,7 @@ watch(
870
876
  nextTick(() => initVirtualScrollY());
871
877
  }
872
878
  const sortColValue = sortCol.value;
873
- if (sortColValue) {
879
+ if (!isEmptyValue(sortColValue) && !props.sortRemote) {
874
880
  // sort
875
881
  const colKey = colKeyGen.value;
876
882
  const column = tableHeaderLast.value.find(it => colKey(it) === sortColValue);
@@ -43,7 +43,7 @@ export type CustomCell<T extends CustomCellProps<U> | CustomHeaderCellProps<U>,
43
43
  /** 表格列配置 */
44
44
  export type StkTableColumn<T extends Record<string, any>> = {
45
45
  /**
46
- * 用于区分相同dataIndex 的列。
46
+ * 列唯一键,(可选),不传则默认取dataIndex 字段作为列唯一键。
47
47
  */
48
48
  key?: any;
49
49
  /**
@@ -40,18 +40,6 @@ export function useHighlight({ props, stkTableId, tableContainerRef }: Params) {
40
40
  const highlightSteps = computed(() => (highlightFrequency.value ? Math.round(highlightDuration.value / highlightFrequency.value) : null));
41
41
  /** 高亮开始 */
42
42
  const highlightFrom = computed(() => highlightColor[props.theme as 'light' | 'dark'].from);
43
- /** 高亮结束 */
44
- // const highlightTo = computed(() => highlightColor[props.theme as 'light' | 'dark'].to);
45
- // const highlightInter = computed(() => interpolateRgb(highlightFrom.value, highlightTo.value));
46
-
47
- /**
48
- * 存放高亮行的状态-使用js计算颜色
49
- * @key 行唯一键
50
- * @value 记录高亮开始时间
51
- */
52
- // const highlightDimRowsJs = new Map<UniqKey, number>();
53
- /** 是否正在计算高亮行的循环-使用js计算颜色 */
54
- // const calcHighlightDimLoopJs = false;
55
43
 
56
44
  /**
57
45
  * 存放高亮行的状态-使用animation api实现
@@ -85,7 +73,7 @@ export function useHighlight({ props, stkTableId, tableContainerRef }: Params) {
85
73
  const recursion = () => {
86
74
  window.requestAnimationFrame(
87
75
  () => {
88
- const nowTs = Date.now();
76
+ const nowTs = performance.now();
89
77
  highlightDimRowsAnimation.forEach((store, rowKeyValue) => {
90
78
  const { ts, duration } = store;
91
79
  const timeOffset = nowTs - ts;
@@ -110,59 +98,24 @@ export function useHighlight({ props, stkTableId, tableContainerRef }: Params) {
110
98
  recursion();
111
99
  }
112
100
 
113
- /**
114
- * js计算高亮渐暗颜色的循环
115
- */
116
- // function calcRowHighlightLoopJs() {
117
- // if (calcHighlightDimLoopJs) return;
118
- // calcHighlightDimLoopJs = true;
119
- // // js计算gradient
120
- // const recursion = () => {
121
- // window.setTimeout(() => {
122
- // const nowTs = Date.now();
123
- // highlightDimRowsJs.forEach((highlightStart, rowKeyValue) => {
124
- // /** 经过的时间 ÷ 高亮持续时间 计算出 颜色过渡进度 (0-1) */
125
- // const progress = (nowTs - highlightStart) / highlightDuration;
126
- // let bgc = '';
127
- // if (0 <= progress && progress <= 1) {
128
- // bgc = highlightInter.value(progress);
129
- // } else {
130
- // highlightDimRowsJs.delete(rowKeyValue);
131
- // }
132
- // updateRowBgcJs(rowKeyValue, bgc);
133
- // });
134
-
135
- // if (highlightDimRowsJs.size > 0) {
136
- // // 还有高亮的行,则下一次循环
137
- // recursion();
138
- // } else {
139
- // // 没有则停止循环
140
- // calcHighlightDimLoopJs = false;
141
- // highlightDimRowsJs.clear(); // TODO: 是否需要 清除
142
- // }
143
- // }, highlightFrequency || HIGHLIGHT_FREQ);
144
- // };
145
- // recursion();
146
- // }
147
-
148
101
  /**
149
102
  * 高亮一个单元格。暂不支持虚拟滚动高亮状态记忆。
150
103
  * @param rowKeyValue 一行的key
151
104
  * @param colKeyValue 列key
152
105
  * @param options.method css-使用css渲染,animation-使用animation api。默认animation;
153
106
  * @param option.className 自定义css动画的class。
154
- * @param option.keyframe Keyframe https://developer.mozilla.org/zh-CN/docs/Web/API/Web_Animations_API/Keyframe_Formats
107
+ * @param option.keyframe 如果自定义keyframe,则 highlightConfig.fps 将会失效。Keyframehttps://developer.mozilla.org/zh-CN/docs/Web/API/Web_Animations_API/Keyframe_Formats
155
108
  * @param option.duration 动画时长。method='css'状态下,用于移除class,如果传入了className则需要与自定义的动画时间一致。
156
109
  */
157
110
  function setHighlightDimCell(rowKeyValue: UniqKey, colKeyValue: string, option: HighlightDimCellOption = {}) {
158
111
  const cellEl = tableContainerRef.value?.querySelector<HTMLElement>(`[data-cell-key="${rowKeyValue}--${colKeyValue}"]`);
112
+ if (!cellEl) return;
159
113
  const { className, method, duration, keyframe } = {
160
114
  className: HIGHLIGHT_CELL_CLASS,
161
115
  method: 'animation',
162
116
  ...defaultHighlightDimOption.value,
163
117
  ...option,
164
118
  };
165
- if (!cellEl) return;
166
119
  if (method === 'animation') {
167
120
  cellEl.animate(keyframe, duration);
168
121
  } else {
@@ -175,11 +128,12 @@ export function useHighlight({ props, stkTableId, tableContainerRef }: Params) {
175
128
  * @param rowKeyValues 行唯一键的数组
176
129
  * @param option.method css-使用css渲染,animation-使用animation api,js-使用js计算颜色。默认animation
177
130
  * @param option.className 自定义css动画的class。
178
- * @param option.keyframe Keyframehttps://developer.mozilla.org/zh-CN/docs/Web/API/Web_Animations_API/Keyframe_Formats
131
+ * @param option.keyframe 如果自定义keyframe,则 highlightConfig.fps 将会失效。Keyframehttps://developer.mozilla.org/zh-CN/docs/Web/API/Web_Animations_API/Keyframe_Formats
179
132
  * @param option.duration 动画时长。method='css'状态下,用于移除class,如果传入了className则需要与自定义的动画时间一致。。
180
133
  */
181
134
  function setHighlightDimRow(rowKeyValues: UniqKey[], option: HighlightDimRowOption = {}) {
182
135
  if (!Array.isArray(rowKeyValues)) rowKeyValues = [rowKeyValues];
136
+ if (!rowKeyValues.length) return;
183
137
  const { className, method, keyframe, duration } = {
184
138
  className: HIGHLIGHT_ROW_CLASS,
185
139
  method: 'animation',
@@ -187,13 +141,10 @@ export function useHighlight({ props, stkTableId, tableContainerRef }: Params) {
187
141
  ...option,
188
142
  };
189
143
 
190
- if (method === 'css') {
191
- // -------- use css keyframe
192
- highlightRowsInCssKeyframe(rowKeyValues, className, duration);
193
- } else if (method === 'animation') {
144
+ if (method === 'animation') {
194
145
  if (props.virtual) {
195
146
  // -------- 用animation 接口实现动画
196
- const nowTs = Date.now();
147
+ const nowTs = performance.now();
197
148
  for (let i = 0; i < rowKeyValues.length; i++) {
198
149
  const rowKeyValue = rowKeyValues[i];
199
150
  const store: HighlightDimRowStore = { ts: nowTs, visible: false, keyframe, duration };
@@ -209,17 +160,10 @@ export function useHighlight({ props, stkTableId, tableContainerRef }: Params) {
209
160
  rowEl.animate(keyframe, duration);
210
161
  }
211
162
  }
163
+ } else {
164
+ // -------- use css keyframe
165
+ highlightRowsInCssKeyframe(rowKeyValues, className, duration);
212
166
  }
213
- // else if (method === 'js') {
214
- // // -------- 用js计算颜色渐变的高亮方案
215
- // const nowTs = Date.now();
216
- // for (let i = 0; i < rowKeyValues.length; i++) {
217
- // const rowKeyValue = rowKeyValues[i];
218
- // highlightDimRowsJs.set(rowKeyValue, nowTs);
219
- // updateRowBgcJs(rowKeyValue, highlightFrom.value);
220
- // }
221
- // calcRowHighlightLoopJs();
222
- // }
223
167
  }
224
168
 
225
169
  /**
@@ -305,13 +249,6 @@ export function useHighlight({ props, stkTableId, tableContainerRef }: Params) {
305
249
  }
306
250
  }
307
251
 
308
- /** 更新行状态 */
309
- // function updateRowBgcJs(rowKeyValue: UniqKey, color: string) {
310
- // const rowEl = document.getElementById(stkTableId + '-' + String(rowKeyValue));
311
- // if (!rowEl) return;
312
- // rowEl.style.backgroundColor = color;
313
- // }
314
-
315
252
  return {
316
253
  highlightSteps,
317
254
  setHighlightDimRow,
@@ -0,0 +1,76 @@
1
+ import { computed, Ref, ref, onMounted, onUnmounted, watch } from 'vue';
2
+
3
+ type Params = {
4
+ props: any;
5
+ tableContainerRef: Ref<HTMLElement | undefined>;
6
+ };
7
+
8
+ export function useScrollRowByRow({ props, tableContainerRef }: Params) {
9
+ let isMouseDown = false;
10
+ let isAddListeners = false;
11
+
12
+ /**记录滚动条是否正在被拖动 */
13
+ const isDragScroll = ref(false);
14
+ const onlyDragScroll = computed(() => props.scrollRowByRow === 'scrollbar');
15
+
16
+ /** is ScrollRowByRow active */
17
+ const isSRBRActive = computed(() => {
18
+ if (onlyDragScroll.value) {
19
+ return isDragScroll.value;
20
+ }
21
+ return props.scrollRowByRow;
22
+ });
23
+
24
+ watch(
25
+ () => onlyDragScroll.value,
26
+ v => {
27
+ if (v) {
28
+ addEventListener();
29
+ } else {
30
+ removeEventListener();
31
+ }
32
+ },
33
+ );
34
+
35
+ onMounted(() => {
36
+ addEventListener();
37
+ });
38
+
39
+ onUnmounted(() => {
40
+ removeEventListener();
41
+ });
42
+
43
+ function addEventListener() {
44
+ if (isAddListeners || !onlyDragScroll.value) return;
45
+ const container = tableContainerRef.value;
46
+ if (!container) return;
47
+ container.addEventListener('mousedown', handleMouseDown);
48
+ container.addEventListener('mouseup', handleMouseUp);
49
+ container.addEventListener('scroll', handleScroll);
50
+ isAddListeners = true;
51
+ }
52
+
53
+ function removeEventListener() {
54
+ const container = tableContainerRef.value;
55
+ if (!container) return;
56
+ container.removeEventListener('mousedown', handleMouseDown);
57
+ container.removeEventListener('mouseup', handleMouseUp);
58
+ container.removeEventListener('scroll', handleScroll);
59
+ isAddListeners = false;
60
+ }
61
+
62
+ function handleMouseDown() {
63
+ isMouseDown = true;
64
+ }
65
+
66
+ function handleMouseUp() {
67
+ isMouseDown = false;
68
+ isDragScroll.value = false;
69
+ }
70
+
71
+ function handleScroll() {
72
+ if (!isMouseDown) return;
73
+ isDragScroll.value = true;
74
+ }
75
+ return { isSRBRActive, isDragScroll };
76
+ }
@@ -21,9 +21,13 @@ export function isEmptyValue(val: any, isNumber?: boolean) {
21
21
  * @param targetArray 表格数据
22
22
  * @return targetArray 的浅拷贝
23
23
  */
24
- export function insertToOrderedArray<T extends object>(sortState: SortState<T>, newItem: T, targetArray: T[], sortConfig: SortConfig<T> = {}) {
24
+ export function insertToOrderedArray<T extends object>(
25
+ sortState: SortState<T>,
26
+ newItem: T,
27
+ targetArray: T[],
28
+ sortConfig: SortConfig<T> & { customCompare?: (a: T, b: T) => number } = {},
29
+ ) {
25
30
  const { dataIndex, sortField, order } = sortState;
26
- sortConfig = { emptyToBottom: false, ...sortConfig };
27
31
  let { sortType } = sortState;
28
32
  const field = sortField || dataIndex;
29
33
  if (!sortType) sortType = typeof newItem[field] as 'number' | 'string';
@@ -35,17 +39,24 @@ export function insertToOrderedArray<T extends object>(sortState: SortState<T>,
35
39
  return data;
36
40
  }
37
41
 
42
+ const { emptyToBottom, customCompare, stringLocaleCompare } = { emptyToBottom: false, ...sortConfig };
43
+
38
44
  const targetVal: any = newItem[field];
39
- if (sortConfig.emptyToBottom && isEmptyValue(targetVal)) {
45
+ if (emptyToBottom && isEmptyValue(targetVal)) {
40
46
  // 空值排在最下方
41
47
  data.push(newItem);
42
48
  } else {
49
+ const customCompareFn =
50
+ customCompare ||
51
+ ((a, b) => {
52
+ const midVal: any = a[field];
53
+ const compareRes = strCompare(midVal, targetVal, isNumber, stringLocaleCompare);
54
+ return order === 'asc' ? compareRes : -compareRes;
55
+ });
43
56
  const isNumber = sortType === 'number';
44
57
  // 二分插入
45
58
  const sIndex = binarySearch(data, midIndex => {
46
- const midVal: any = data[midIndex][field];
47
- const compareRes = strCompare(midVal, targetVal, isNumber, sortConfig.stringLocaleCompare);
48
- return order === 'asc' ? compareRes : -compareRes;
59
+ return customCompareFn(data[midIndex], newItem);
49
60
  });
50
61
  data.splice(sIndex, 0, newItem);
51
62
  }