stk-table-vue 1.2.0-beta.1 → 1.2.0-beta.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.
package/README.md CHANGED
@@ -25,7 +25,7 @@ Smooth performance with tens of thousands of rows. Designed for real-time data d
25
25
  - Cell highlighting with Animation API
26
26
  - Built-in custom cells: Filter, Editable, Checkbox, Number, Change
27
27
  - Column resize, header drag, row drag
28
- - Merge cells (virtual mode supported)
28
+ - Merge cells (virtual mode, huge rowspan / colspan supported)
29
29
  - Custom scrollbar
30
30
  - Sortable & filterable
31
31
  - Theme support (dark / light)
@@ -182,7 +182,12 @@ const dataSource = [
182
182
  ```
183
183
 
184
184
  ## Compare
185
- Compare performance with other vue table [vue-table-compare](https://github.com/ja-plus/vue-table-compare)
185
+ Compare performance with other vue table [vue-virtual-table-benchmark](https://github.com/ja-plus/vue-virtual-table-benchmark)
186
+
187
+ [Benchmark Result Page](https://ja-plus.github.io/vue-virtual-table-benchmark/perf-report.html)
188
+
189
+ ## Changelog
190
+ [CHANGELOG](CHANGELOG.md)
186
191
 
187
192
 
188
193
  ## License
@@ -0,0 +1,166 @@
1
+ /**
2
+ * name: stk-table-vue
3
+ * version: v1.2.0-beta.2
4
+ * description: High performance realtime virtual table for vue3 and vue2.7
5
+ * author: japlus
6
+ * homepage: https://ja-plus.github.io/stk-table-vue/
7
+ * license: MIT
8
+ */
9
+ import { defineComponent, ref, reactive, h, onMounted, onUnmounted, createElementBlock, openBlock, withModifiers, normalizeStyle, normalizeClass, createVNode, createElementVNode, nextTick } from "vue";
10
+ import { StkTable as _sfc_main$1 } from "./stk-table-vue.js";
11
+ const DROPDOWN_DEFAULT_WIDTH = 300;
12
+ const DROPDOWN_DEFAULT_HEIGHT = 400;
13
+ const PADDING = 6;
14
+ const _sfc_main = /* @__PURE__ */ defineComponent({
15
+ __name: "index",
16
+ setup(__props, { expose: __expose }) {
17
+ const theme = ref("light");
18
+ const checkedTempValueSet = reactive(/* @__PURE__ */ new Set());
19
+ const columns = ref([
20
+ {
21
+ title: "",
22
+ dataIndex: "value",
23
+ width: 30,
24
+ className: "stk-filter-dropdown-checkbox",
25
+ customCell: ({ row }) => h("input", {
26
+ type: "checkbox",
27
+ checked: checkedTempValueSet.has(row.value)
28
+ })
29
+ },
30
+ { title: "", dataIndex: "label" }
31
+ ]);
32
+ const visible = ref(false);
33
+ const position = ref({ x: 0, y: 0 });
34
+ const options = ref([]);
35
+ const dropdownEl = ref();
36
+ onMounted(() => {
37
+ document.addEventListener("click", handleClickOutside);
38
+ });
39
+ onUnmounted(() => {
40
+ document.removeEventListener("click", handleClickOutside);
41
+ });
42
+ let onConfirmFn;
43
+ function getDropdownSize() {
44
+ if (!dropdownEl.value) {
45
+ return [DROPDOWN_DEFAULT_WIDTH, DROPDOWN_DEFAULT_HEIGHT];
46
+ }
47
+ const rect = dropdownEl.value.getBoundingClientRect();
48
+ return [rect.width || DROPDOWN_DEFAULT_WIDTH, rect.height || DROPDOWN_DEFAULT_HEIGHT];
49
+ }
50
+ function calculatePosition(docPos) {
51
+ const scrollTop = window.pageYOffset || document.documentElement.scrollTop;
52
+ const scrollLeft = window.pageXOffset || document.documentElement.scrollLeft;
53
+ const viewportWidth = document.documentElement.clientWidth;
54
+ const viewportHeight = document.documentElement.clientHeight;
55
+ const [dropdownWidth, dropdownHeight] = getDropdownSize();
56
+ let finalX = docPos.x;
57
+ let finalY = docPos.y;
58
+ const relativeX = docPos.x - scrollLeft;
59
+ if (relativeX + dropdownWidth > viewportWidth - PADDING) {
60
+ finalX = viewportWidth - dropdownWidth - PADDING + scrollLeft;
61
+ }
62
+ const relativeY = docPos.y - scrollTop;
63
+ if (relativeY + dropdownHeight > viewportHeight - PADDING) {
64
+ const triggerHeight = docPos.height || 30;
65
+ if (relativeY - triggerHeight >= dropdownHeight + PADDING) {
66
+ finalY = docPos.y - triggerHeight - dropdownHeight - PADDING;
67
+ } else {
68
+ finalY = PADDING + scrollTop;
69
+ }
70
+ }
71
+ finalX = Math.max(PADDING + scrollLeft, finalX);
72
+ finalY = Math.max(PADDING + scrollTop, finalY);
73
+ return { x: finalX, y: finalY };
74
+ }
75
+ async function show(pos, opt, onConfirm) {
76
+ if (dropdownEl.value) {
77
+ dropdownEl.value.style.visibility = "hidden";
78
+ }
79
+ visible.value = true;
80
+ options.value = opt || [];
81
+ initChecked();
82
+ onConfirmFn = onConfirm;
83
+ await nextTick();
84
+ position.value = calculatePosition(pos);
85
+ if (dropdownEl.value) {
86
+ dropdownEl.value.style.visibility = "visible";
87
+ }
88
+ }
89
+ async function handleClickOutside(e) {
90
+ var _a;
91
+ if (!visible.value || ((_a = dropdownEl.value) == null ? void 0 : _a.contains(e.target))) return;
92
+ hide();
93
+ }
94
+ function initChecked() {
95
+ options.value.forEach((opt) => {
96
+ if (opt.selected) {
97
+ checkedTempValueSet.add(opt.value);
98
+ }
99
+ });
100
+ }
101
+ function updateChecked(checked, row) {
102
+ if (checked) {
103
+ checkedTempValueSet.add(row.value);
104
+ } else {
105
+ checkedTempValueSet.delete(row.value);
106
+ }
107
+ }
108
+ function confirm() {
109
+ options.value.forEach((opt) => opt.selected = checkedTempValueSet.has(opt.value));
110
+ onConfirmFn(Array.from(checkedTempValueSet));
111
+ hide();
112
+ }
113
+ function hide() {
114
+ visible.value = false;
115
+ options.value = [];
116
+ checkedTempValueSet.clear();
117
+ }
118
+ function handleRowClick(e, row) {
119
+ const selected = checkedTempValueSet.has(row.value);
120
+ updateChecked(!selected, row);
121
+ }
122
+ function setTheme(t) {
123
+ theme.value = t;
124
+ }
125
+ function handleClear() {
126
+ checkedTempValueSet.clear();
127
+ confirm();
128
+ }
129
+ __expose({ visible, show, hide, setTheme });
130
+ return (_ctx, _cache) => {
131
+ return openBlock(), createElementBlock("div", {
132
+ ref_key: "dropdownEl",
133
+ ref: dropdownEl,
134
+ class: normalizeClass(["stk-filter-dropdown", [`stk-filter-dropdown--${theme.value}`]]),
135
+ style: normalizeStyle({
136
+ top: position.value.y + "px",
137
+ left: position.value.x + "px",
138
+ display: visible.value ? void 0 : "none"
139
+ }),
140
+ onClick: _cache[0] || (_cache[0] = withModifiers(() => {
141
+ }, ["stop"]))
142
+ }, [
143
+ createVNode(_sfc_main$1, {
144
+ "row-key": "id",
145
+ headless: "",
146
+ virtual: "",
147
+ "no-data-full": "",
148
+ theme: theme.value,
149
+ "row-active": false,
150
+ "row-height": 20,
151
+ bordered: false,
152
+ columns: columns.value,
153
+ "data-source": options.value,
154
+ onRowClick: handleRowClick
155
+ }, null, 8, ["theme", "columns", "data-source"]),
156
+ createElementVNode("footer", null, [
157
+ createElementVNode("button", { onClick: handleClear }, "↺"),
158
+ createElementVNode("button", { onClick: confirm }, "✓")
159
+ ])
160
+ ], 6);
161
+ };
162
+ }
163
+ });
164
+ export {
165
+ _sfc_main as default
166
+ };
@@ -242,6 +242,13 @@ declare const _default: __VLS_WithTemplateSlots<import('vue').DefineComponent<im
242
242
  * @see {@link initVirtualScrollY}
243
243
  */
244
244
  initVirtualScrollY: (height?: number) => void;
245
+ /**
246
+ * 清空 mergeCells 结果缓存并强制重算合并结果
247
+ *
248
+ * en: Clear mergeCells result cache and force recomputation
249
+ * @see {@link clearMergeCellsCache}
250
+ */
251
+ clearMergeCellsCache: () => void;
245
252
  /**
246
253
  * 选中一行
247
254
  *
@@ -757,13 +764,13 @@ declare const _default: __VLS_WithTemplateSlots<import('vue').DefineComponent<im
757
764
  maxWidth: string;
758
765
  sortConfig: SortConfig<any>;
759
766
  colKey: UniqKeyProp;
760
- rowHeight: number;
761
767
  headless: boolean;
762
768
  autoRowHeight: boolean | AutoRowHeightConfig<any>;
763
769
  stripe: boolean;
764
770
  optimizeVue2Scroll: boolean;
765
771
  rowKey: UniqKeyProp;
766
772
  headerRowHeight: number | string;
773
+ rowHeight: number;
767
774
  scrollbar: boolean | ScrollbarOptions;
768
775
  fixedMode: boolean;
769
776
  theme: "light" | "dark";
@@ -15,6 +15,9 @@ export type MergeCellsResult = {
15
15
  * 稳态滚动时仅新进入窗口的行会触发回调,只需在数据/列真正变化时 clear;
16
16
  * - 条目额外保存行引用并在命中时校验同一性:即使数据被重排/替换(如排序后同一批行对象
17
17
  * 顺序变化),也不会取到错行的旧结果,缓存清理时机不再影响正确性。
18
+ * - 注意:引用同一性校验无法感知行字段被原地修改(行引用不变)。
19
+ * 业务原地改行且 mergeCells 结果依赖该字段时,需调用 clear() 显式失效
20
+ * (组件已暴露 clearMergeCellsCache 实例方法)。
18
21
  */
19
22
  export declare function createMergeCellsCache(): {
20
23
  getMergeCellsResult: (row: MergeCellsParam<any>["row"], col: MergeCellsParam<any>["col"], rowIndex: number, colIndex: number) => MergeCellsResult;
@@ -50,45 +50,7 @@ export declare function useVirtualScroll(props: any, tableContainerRef: Ref<HTML
50
50
  /** 全局最大 rowspan(限定跨界修正扫描范围用) */
51
51
  getMaxRowSpanValue: () => number, scrollbarOptions: Ref<Required<ScrollbarOptions>>, isExperimentalScrollY: Ref<boolean | undefined>,
52
52
  /** mergeCells 结果共享缓存(与 useMergeCells 共用,避免重复调用用户回调) */
53
- mergeCellsCache: MergeCellsCache): readonly [Ref<{
54
- containerHeight: number;
55
- pageSize: number;
56
- startIndex: number;
57
- endIndex: number;
58
- rowHeight: number;
59
- offsetTop: number;
60
- scrollTop: number;
61
- scrollHeight: number;
62
- translateY: number;
63
- viewportStartIndex: number;
64
- viewportEndIndex: number;
65
- }, VirtualScrollStore | {
66
- containerHeight: number;
67
- pageSize: number;
68
- startIndex: number;
69
- endIndex: number;
70
- rowHeight: number;
71
- offsetTop: number;
72
- scrollTop: number;
73
- scrollHeight: number;
74
- translateY: number;
75
- viewportStartIndex: number;
76
- viewportEndIndex: number;
77
- }>, Ref<{
78
- containerWidth: number;
79
- scrollWidth: number;
80
- startIndex: number;
81
- endIndex: number;
82
- offsetLeft: number;
83
- scrollLeft: number;
84
- }, VirtualScrollXStore | {
85
- containerWidth: number;
86
- scrollWidth: number;
87
- startIndex: number;
88
- endIndex: number;
89
- offsetLeft: number;
90
- scrollLeft: number;
91
- }>, import('vue').ComputedRef<any>, import('vue').ComputedRef<PrivateRowDT[]>, import('vue').ComputedRef<number>, import('vue').ComputedRef<any>, import('vue').ComputedRef<number>, import('vue').ComputedRef<number>, (height?: number) => void, (height?: number) => void, () => void, (sTop?: number) => void, (sLeft?: number) => void, (rowKey: UniqKey, height?: number | null) => void, () => void, () => void, import('vue').ComputedRef<PrivateStkTableColumn<PrivateRowDT>[][]>, import('vue').ComputedRef<number>, import('vue').ComputedRef<{
53
+ mergeCellsCache: MergeCellsCache): readonly [ShallowRef<VirtualScrollStore, VirtualScrollStore>, ShallowRef<VirtualScrollXStore, VirtualScrollXStore>, import('vue').ComputedRef<any>, import('vue').ComputedRef<PrivateRowDT[]>, import('vue').ComputedRef<number>, import('vue').ComputedRef<any>, import('vue').ComputedRef<number>, import('vue').ComputedRef<number>, (height?: number) => void, (height?: number) => void, () => void, (sTop?: number) => void, (sLeft?: number) => void, (rowKey: UniqKey, height?: number | null) => void, () => void, () => void, import('vue').ComputedRef<PrivateStkTableColumn<PrivateRowDT>[][]>, import('vue').ComputedRef<number>, import('vue').ComputedRef<{
92
54
  startIndex: number;
93
55
  endIndex: number;
94
56
  offsetLeft: number;