cynx-lib 1.3.63 → 1.3.65
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/package.json +1 -1
- package/vue/components/Table.vue +78 -6
package/package.json
CHANGED
package/vue/components/Table.vue
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
|
-
import { computed, ref } from "vue";
|
|
2
|
+
import { computed, ref, onMounted, onUnmounted, watch, nextTick } from "vue";
|
|
3
3
|
import {
|
|
4
4
|
ChevronUp,
|
|
5
5
|
ChevronDown,
|
|
@@ -62,7 +62,53 @@ const emit = defineEmits<{
|
|
|
62
62
|
}>();
|
|
63
63
|
|
|
64
64
|
const rootRef = ref<HTMLElement | null>(null);
|
|
65
|
+
const headerScrollRef = ref<HTMLElement | null>(null);
|
|
65
66
|
const bodyScrollRef = ref<HTMLElement | null>(null);
|
|
67
|
+
const gutterWidth = ref(0);
|
|
68
|
+
|
|
69
|
+
const updateGutter = () => {
|
|
70
|
+
if (bodyScrollRef.value) {
|
|
71
|
+
const hasVScroll = bodyScrollRef.value.scrollHeight > bodyScrollRef.value.clientHeight;
|
|
72
|
+
if (hasVScroll) {
|
|
73
|
+
const scrollWidth = bodyScrollRef.value.offsetWidth - bodyScrollRef.value.clientWidth;
|
|
74
|
+
gutterWidth.value = scrollWidth > 0 ? scrollWidth : 6;
|
|
75
|
+
} else {
|
|
76
|
+
gutterWidth.value = 0;
|
|
77
|
+
}
|
|
78
|
+
} else {
|
|
79
|
+
gutterWidth.value = 0;
|
|
80
|
+
}
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
let resizeObserver: ResizeObserver | null = null;
|
|
84
|
+
|
|
85
|
+
onMounted(() => {
|
|
86
|
+
if (bodyScrollRef.value && typeof ResizeObserver !== "undefined") {
|
|
87
|
+
resizeObserver = new ResizeObserver(() => {
|
|
88
|
+
updateGutter();
|
|
89
|
+
});
|
|
90
|
+
resizeObserver.observe(bodyScrollRef.value);
|
|
91
|
+
}
|
|
92
|
+
nextTick(updateGutter);
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
onUnmounted(() => {
|
|
96
|
+
resizeObserver?.disconnect();
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
watch(
|
|
100
|
+
() => [props.data, props.columns, props.loading],
|
|
101
|
+
() => {
|
|
102
|
+
nextTick(updateGutter);
|
|
103
|
+
},
|
|
104
|
+
{ deep: true }
|
|
105
|
+
);
|
|
106
|
+
|
|
107
|
+
const handleBodyScroll = () => {
|
|
108
|
+
if (headerScrollRef.value && bodyScrollRef.value) {
|
|
109
|
+
headerScrollRef.value.scrollLeft = bodyScrollRef.value.scrollLeft;
|
|
110
|
+
}
|
|
111
|
+
};
|
|
66
112
|
|
|
67
113
|
const { rows: autoRows } = useAutoSkeletonRows(rootRef, {
|
|
68
114
|
rowHeight: props.rowHeight,
|
|
@@ -126,23 +172,24 @@ const getSkeletonWidth = (rIdx: number, cIdx: number, key: string) => {
|
|
|
126
172
|
]"
|
|
127
173
|
:style="height ? { height: formatWidth(height) } : undefined"
|
|
128
174
|
>
|
|
129
|
-
<!--
|
|
175
|
+
<!-- Dedicated Fixed Header Container (Never moves on row scroll) -->
|
|
130
176
|
<div
|
|
131
|
-
ref="
|
|
132
|
-
class="w-full overflow-
|
|
177
|
+
ref="headerScrollRef"
|
|
178
|
+
class="w-full overflow-hidden flex-shrink-0 border-b border-slate-200 dark:border-slate-800 bg-[#f8fafc] dark:bg-[#16192e]"
|
|
133
179
|
>
|
|
134
180
|
<table class="w-full border-collapse text-left text-xs table-fixed">
|
|
135
181
|
<colgroup>
|
|
136
182
|
<col
|
|
137
183
|
v-for="col in columns"
|
|
138
|
-
:key="`col-${col.key}`"
|
|
184
|
+
:key="`h-col-${col.key}`"
|
|
139
185
|
:style="{
|
|
140
186
|
width: formatWidth(col.width),
|
|
141
187
|
minWidth: formatWidth(col.minWidth || col.width || 100),
|
|
142
188
|
}"
|
|
143
189
|
/>
|
|
190
|
+
<col v-if="gutterWidth > 0" :style="{ width: `${gutterWidth}px` }" />
|
|
144
191
|
</colgroup>
|
|
145
|
-
<thead
|
|
192
|
+
<thead>
|
|
146
193
|
<tr>
|
|
147
194
|
<th
|
|
148
195
|
v-for="col in columns"
|
|
@@ -191,8 +238,33 @@ const getSkeletonWidth = (rIdx: number, cIdx: number, key: string) => {
|
|
|
191
238
|
</span>
|
|
192
239
|
</div>
|
|
193
240
|
</th>
|
|
241
|
+
<th
|
|
242
|
+
v-if="gutterWidth > 0"
|
|
243
|
+
class="bg-[#f8fafc] dark:bg-[#16192e] p-0 border-0 pointer-events-none"
|
|
244
|
+
:style="{ width: `${gutterWidth}px`, minWidth: `${gutterWidth}px` }"
|
|
245
|
+
/>
|
|
194
246
|
</tr>
|
|
195
247
|
</thead>
|
|
248
|
+
</table>
|
|
249
|
+
</div>
|
|
250
|
+
|
|
251
|
+
<!-- Dedicated Scrollable Body Container -->
|
|
252
|
+
<div
|
|
253
|
+
ref="bodyScrollRef"
|
|
254
|
+
class="w-full overflow-x-auto overflow-y-auto custom-scrollbar flex-1 relative"
|
|
255
|
+
@scroll="handleBodyScroll"
|
|
256
|
+
>
|
|
257
|
+
<table class="w-full border-collapse text-left text-xs table-fixed">
|
|
258
|
+
<colgroup>
|
|
259
|
+
<col
|
|
260
|
+
v-for="col in columns"
|
|
261
|
+
:key="`b-col-${col.key}`"
|
|
262
|
+
:style="{
|
|
263
|
+
width: formatWidth(col.width),
|
|
264
|
+
minWidth: formatWidth(col.minWidth || col.width || 100),
|
|
265
|
+
}"
|
|
266
|
+
/>
|
|
267
|
+
</colgroup>
|
|
196
268
|
<tbody class="divide-y divide-slate-100 dark:divide-slate-800/80">
|
|
197
269
|
<!-- Loading State (Real matching heights and paddings) -->
|
|
198
270
|
<template v-if="loading">
|