@v1nt1248/3nclient-lib 0.3.35 → 0.3.37
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 +1 -0
- package/dist/src/components/index.d.ts +4 -2
- package/dist/src/components/ui3n-scrollbar/types.d.ts +61 -0
- package/dist/src/components/ui3n-scrollbar/ui3n-scrollbar.d.ts +53 -0
- package/dist/src/components/ui3n-scrollbar-vertical/ui3n-scrollbar-vertical.d.ts +1 -1
- package/dist/src/components/ui3n-table/composables/useTable.d.ts +6 -1
- package/dist/src/components/ui3n-table/types.d.ts +52 -0
- package/dist/src/components/ui3n-virtual-scroll/types.d.ts +38 -0
- package/dist/src/components/ui3n-virtual-scroll/ui3n-virtual-scroll.d.ts +9 -5
- package/dist/src/index.d.ts +2 -0
- package/dist/src/ui3n-components.d.ts +2 -0
- package/dist/style.css +1 -1
- package/dist/ui3n-components.js +1819 -1181
- package/package.json +2 -14
- package/src/components/index.ts +26 -1
- package/src/components/ui3n-scrollbar/types.ts +63 -0
- package/src/components/ui3n-scrollbar/ui3n-scrollbar.vue +697 -0
- package/src/components/ui3n-table/composables/useTable.ts +169 -16
- package/src/components/ui3n-table/types.ts +52 -0
- package/src/components/ui3n-table/ui3n-table.vue +376 -6
- package/src/components/ui3n-virtual-scroll/types.ts +40 -0
- package/src/components/ui3n-virtual-scroll/ui3n-virtual-scroll.vue +368 -59
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { computed, type Ref, ref, watch, UnwrapRef } from 'vue';
|
|
1
|
+
import { computed, type Ref, ref, watch, UnwrapRef, onBeforeUnmount } from 'vue';
|
|
2
2
|
import get from 'lodash/get';
|
|
3
3
|
import isEmpty from 'lodash/isEmpty';
|
|
4
4
|
import size from 'lodash/size';
|
|
5
5
|
import cloneDeep from 'lodash/cloneDeep';
|
|
6
|
-
import {
|
|
6
|
+
import type {
|
|
7
7
|
Ui3nTableBodyBaseItem,
|
|
8
8
|
Ui3nTableConfig,
|
|
9
9
|
Ui3nTableEmits,
|
|
@@ -12,7 +12,21 @@ import {
|
|
|
12
12
|
} from '@/components/ui3n-table/types';
|
|
13
13
|
import type { Ui3nCheckboxValue } from '@/components/ui3n-checkbox/types';
|
|
14
14
|
|
|
15
|
-
|
|
15
|
+
const ABSOLUTE_CSS_SIZE_RE = /^-?\d+(\.\d+)?(px|rem|em|ch|ex|cm|mm|in|pt|pc)$/i;
|
|
16
|
+
|
|
17
|
+
export function isAbsoluteCssSize(value: string): boolean {
|
|
18
|
+
if (!value || typeof value !== 'string') {
|
|
19
|
+
return false;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
return ABSOLUTE_CSS_SIZE_RE.test(value.trim());
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export function useTable<T extends Ui3nTableBodyBaseItem>(
|
|
26
|
+
props: Ui3nTableProps<T>,
|
|
27
|
+
emits: Ui3nTableEmits<T>,
|
|
28
|
+
scrollbarContainerRef?: Ref<HTMLDivElement | null>,
|
|
29
|
+
) {
|
|
16
30
|
const tableEl = ref<HTMLDivElement | null>(null);
|
|
17
31
|
const currentConfig = ref<Pick<Ui3nTableConfig<T>, 'sortOrder' | 'fieldAsRowKey'>>({
|
|
18
32
|
sortOrder: setInitialSortOrder(),
|
|
@@ -22,6 +36,9 @@ export function useTable<T extends Ui3nTableBodyBaseItem>(props: Ui3nTableProps<
|
|
|
22
36
|
? ref([])
|
|
23
37
|
: (ref(new Set()) as Ref<Set<T>>);
|
|
24
38
|
const hasGroupActionsRow = ref(false);
|
|
39
|
+
const scrollportWidth = ref<number | null>(null);
|
|
40
|
+
|
|
41
|
+
let resizeObserver: ResizeObserver | null = null;
|
|
25
42
|
|
|
26
43
|
const isRowKeyUsed = computed(() => !!currentConfig.value.fieldAsRowKey);
|
|
27
44
|
const visibleColumns = computed(() => props.head.filter(h => !h.hidden));
|
|
@@ -37,6 +54,77 @@ export function useTable<T extends Ui3nTableBodyBaseItem>(props: Ui3nTableProps<
|
|
|
37
54
|
);
|
|
38
55
|
const showGroupActionsRow = computed(() => props.config?.selectable === 'multiple' && hasGroupActionsRow.value);
|
|
39
56
|
|
|
57
|
+
function getInvalidStickyColumnWidths(): Array<{ key: string; width: string }> {
|
|
58
|
+
const { columnStyle } = props.config ?? {};
|
|
59
|
+
const invalid: Array<{ key: string; width: string }> = [];
|
|
60
|
+
|
|
61
|
+
for (const h of visibleColumns.value) {
|
|
62
|
+
const width = get(columnStyle, [h.key, 'width'], '') as string;
|
|
63
|
+
if (!isAbsoluteCssSize(width)) {
|
|
64
|
+
invalid.push({ key: String(h.key), width: width || '(missing)' });
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
return invalid;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
const stickyColumnsRequested = computed(() => {
|
|
72
|
+
const n = Number(props.config?.stickyColumns);
|
|
73
|
+
if (!Number.isFinite(n) || n < 1) {
|
|
74
|
+
return 0;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
return Math.floor(n);
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
const stickyColumnsCount = computed(() => {
|
|
81
|
+
if (!stickyColumnsRequested.value) {
|
|
82
|
+
return 0;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
const max = Math.max(0, visibleColumns.value.length - 1);
|
|
86
|
+
if (!max) {
|
|
87
|
+
return 0;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
if (getInvalidStickyColumnWidths().length) {
|
|
91
|
+
return 0;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
return Math.min(stickyColumnsRequested.value, max);
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
const stickyColumnsActive = computed(() => stickyColumnsCount.value > 0);
|
|
98
|
+
|
|
99
|
+
const stickyColumnLefts = computed(() => {
|
|
100
|
+
const count = stickyColumnsCount.value;
|
|
101
|
+
if (!count) {
|
|
102
|
+
return [] as string[];
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
const { columnStyle } = props.config ?? {};
|
|
106
|
+
const widths = visibleColumns.value.slice(0, count).map(h => {
|
|
107
|
+
return (get(columnStyle, [h.key, 'width'], '0px') as string).trim();
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
const lefts: string[] = [];
|
|
111
|
+
for (let i = 0; i < count; i++) {
|
|
112
|
+
if (i === 0) {
|
|
113
|
+
lefts.push('0px');
|
|
114
|
+
continue;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
if (i === 1) {
|
|
118
|
+
lefts.push(widths[0]);
|
|
119
|
+
continue;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
lefts.push(`calc(${widths.slice(0, i).join(' + ')})`);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
return lefts;
|
|
126
|
+
});
|
|
127
|
+
|
|
40
128
|
const tableColumnWidth = computed(() => {
|
|
41
129
|
if (!tableEl.value) {
|
|
42
130
|
return '100%';
|
|
@@ -45,8 +133,12 @@ export function useTable<T extends Ui3nTableBodyBaseItem>(props: Ui3nTableProps<
|
|
|
45
133
|
const { config = {}, head } = props;
|
|
46
134
|
const { columnStyle } = config;
|
|
47
135
|
const keys = head.filter(h => !h.hidden).map(h => h.key);
|
|
136
|
+
const useAbsolute = stickyColumnsActive.value;
|
|
137
|
+
|
|
48
138
|
return keys.reduce((res, key, index) => {
|
|
49
|
-
const width =
|
|
139
|
+
const width = useAbsolute
|
|
140
|
+
? (get(columnStyle, [key, 'width']) as string)
|
|
141
|
+
: (get(columnStyle, [key, 'width'], '1fr') as string);
|
|
50
142
|
res = index === 0 ? `${width}` : `${res} ${width}`;
|
|
51
143
|
return res;
|
|
52
144
|
}, '');
|
|
@@ -220,6 +312,29 @@ export function useTable<T extends Ui3nTableBodyBaseItem>(props: Ui3nTableProps<
|
|
|
220
312
|
emits('select:row', []);
|
|
221
313
|
}
|
|
222
314
|
|
|
315
|
+
function disconnectScrollportObserver() {
|
|
316
|
+
resizeObserver?.disconnect();
|
|
317
|
+
resizeObserver = null;
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
function syncScrollportObserver() {
|
|
321
|
+
disconnectScrollportObserver();
|
|
322
|
+
|
|
323
|
+
const el = scrollbarContainerRef?.value ?? tableEl.value;
|
|
324
|
+
if (!el || !stickyColumnsActive.value) {
|
|
325
|
+
scrollportWidth.value = null;
|
|
326
|
+
return;
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
const updateWidth = () => {
|
|
330
|
+
scrollportWidth.value = el.clientWidth;
|
|
331
|
+
};
|
|
332
|
+
|
|
333
|
+
updateWidth();
|
|
334
|
+
resizeObserver = new ResizeObserver(updateWidth);
|
|
335
|
+
resizeObserver.observe(el);
|
|
336
|
+
}
|
|
337
|
+
|
|
223
338
|
watch(
|
|
224
339
|
() => props.config?.tableName,
|
|
225
340
|
newTableName => {
|
|
@@ -229,18 +344,6 @@ export function useTable<T extends Ui3nTableBodyBaseItem>(props: Ui3nTableProps<
|
|
|
229
344
|
},
|
|
230
345
|
);
|
|
231
346
|
|
|
232
|
-
// watch(
|
|
233
|
-
// tableColumnWidth,
|
|
234
|
-
// (val, oVal) => {
|
|
235
|
-
// if (val && val !== oVal) {
|
|
236
|
-
// tableEl.value && tableEl.value.style.setProperty('--ui3n-table-columns-width', val);
|
|
237
|
-
// }
|
|
238
|
-
// },
|
|
239
|
-
// {
|
|
240
|
-
// immediate: true,
|
|
241
|
-
// },
|
|
242
|
-
// );
|
|
243
|
-
|
|
244
347
|
watch(
|
|
245
348
|
() => selectedRowsSize.value,
|
|
246
349
|
(val, oldVal) => {
|
|
@@ -250,6 +353,52 @@ export function useTable<T extends Ui3nTableBodyBaseItem>(props: Ui3nTableProps<
|
|
|
250
353
|
},
|
|
251
354
|
);
|
|
252
355
|
|
|
356
|
+
watch(
|
|
357
|
+
() => ({
|
|
358
|
+
requested: stickyColumnsRequested.value,
|
|
359
|
+
tableName: props.config?.tableName,
|
|
360
|
+
visibleCount: visibleColumns.value.length,
|
|
361
|
+
columnKeys: visibleColumns.value.map(h => String(h.key)).join('\0'),
|
|
362
|
+
widths: visibleColumns.value
|
|
363
|
+
.map(h => `${String(h.key)}=${get(props.config?.columnStyle, [h.key, 'width'], '')}`)
|
|
364
|
+
.join('\0'),
|
|
365
|
+
}),
|
|
366
|
+
({ requested, tableName, visibleCount }) => {
|
|
367
|
+
if (!requested) {
|
|
368
|
+
return;
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
const tableLabel = tableName ? ` tableName="${tableName}"` : '';
|
|
372
|
+
const max = Math.max(0, visibleCount - 1);
|
|
373
|
+
|
|
374
|
+
if (requested > max) {
|
|
375
|
+
console.warn(
|
|
376
|
+
`[Ui3nTable] stickyColumns clamped:${tableLabel} requested=${requested}, max=${max} (visibleColumns.length - 1).`,
|
|
377
|
+
);
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
const invalid = getInvalidStickyColumnWidths();
|
|
381
|
+
if (!invalid.length) {
|
|
382
|
+
return;
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
const details = invalid.map(({ key, width }) => `key="${key}" width="${width}"`).join(', ');
|
|
386
|
+
console.warn(
|
|
387
|
+
`[Ui3nTable] stickyColumns disabled:${tableLabel} column width must be an absolute CSS length.\nInvalid: ${details}`,
|
|
388
|
+
);
|
|
389
|
+
},
|
|
390
|
+
{ immediate: true },
|
|
391
|
+
);
|
|
392
|
+
|
|
393
|
+
watch([tableEl, stickyColumnsActive, scrollbarContainerRef], syncScrollportObserver, {
|
|
394
|
+
immediate: true,
|
|
395
|
+
flush: 'post',
|
|
396
|
+
});
|
|
397
|
+
|
|
398
|
+
onBeforeUnmount(() => {
|
|
399
|
+
disconnectScrollportObserver();
|
|
400
|
+
});
|
|
401
|
+
|
|
253
402
|
return {
|
|
254
403
|
tableEl,
|
|
255
404
|
tableColumnWidth,
|
|
@@ -261,6 +410,10 @@ export function useTable<T extends Ui3nTableBodyBaseItem>(props: Ui3nTableProps<
|
|
|
261
410
|
selectedRows,
|
|
262
411
|
selectedRowsArray,
|
|
263
412
|
selectedRowsSize,
|
|
413
|
+
stickyColumnsActive,
|
|
414
|
+
stickyColumnsCount,
|
|
415
|
+
stickyColumnLefts,
|
|
416
|
+
scrollportWidth,
|
|
264
417
|
closeGroupActionsRow,
|
|
265
418
|
getRowKey,
|
|
266
419
|
isRowSelected,
|
|
@@ -59,6 +59,48 @@ export type Ui3nTableConfig<T extends Ui3nTableBodyBaseItem, K extends keyof T =
|
|
|
59
59
|
* Minimum height for unused place
|
|
60
60
|
*/
|
|
61
61
|
minHeightUnusedPlace?: number | string;
|
|
62
|
+
/**
|
|
63
|
+
* Number of leading visible columns to pin on horizontal scroll.
|
|
64
|
+
* `0` / omitted — off. Clamped to at most `visibleColumns.length - 1`.
|
|
65
|
+
* Requires absolute widths (e.g. px/rem) on all visible columns when > 0.
|
|
66
|
+
* @default 0
|
|
67
|
+
*/
|
|
68
|
+
stickyColumns?: number;
|
|
69
|
+
/**
|
|
70
|
+
* Custom scrollbar settings.
|
|
71
|
+
* When omitted, native browser overflow is used.
|
|
72
|
+
*/
|
|
73
|
+
scrollbar?: {
|
|
74
|
+
/**
|
|
75
|
+
* Which overflow axes to enable.
|
|
76
|
+
* @default 'both'
|
|
77
|
+
*/
|
|
78
|
+
axes?: 'vertical' | 'horizontal' | 'both';
|
|
79
|
+
vertical?: {
|
|
80
|
+
thumbMinHeight?: number | string;
|
|
81
|
+
thumbHeight?: number | string | 'auto';
|
|
82
|
+
thumbRadius?: number | string;
|
|
83
|
+
thumbColor?: string;
|
|
84
|
+
thumbHoverColor?: string;
|
|
85
|
+
thumbActiveColor?: string;
|
|
86
|
+
trackWidth?: number;
|
|
87
|
+
trackRadius?: number | string;
|
|
88
|
+
trackColor?: string;
|
|
89
|
+
/** Offset of vertical track from top (usually set by table for sticky header). */
|
|
90
|
+
trackOffsetTop?: number | string;
|
|
91
|
+
};
|
|
92
|
+
horizontal?: {
|
|
93
|
+
thumbMinWidth?: number | string;
|
|
94
|
+
thumbWidth?: number | string | 'auto';
|
|
95
|
+
thumbRadius?: number | string;
|
|
96
|
+
thumbColor?: string;
|
|
97
|
+
thumbHoverColor?: string;
|
|
98
|
+
thumbActiveColor?: string;
|
|
99
|
+
trackHeight?: number | string;
|
|
100
|
+
trackRadius?: number | string;
|
|
101
|
+
trackColor?: string;
|
|
102
|
+
};
|
|
103
|
+
};
|
|
62
104
|
};
|
|
63
105
|
|
|
64
106
|
/**
|
|
@@ -201,6 +243,16 @@ export type Ui3nTableBodyRowSlotScope<T extends Ui3nTableBodyBaseItem, K extends
|
|
|
201
243
|
* Column styles
|
|
202
244
|
*/
|
|
203
245
|
columnStyle?: { [P in Omit<K, 'id'> as string | number]: Record<string, string> };
|
|
246
|
+
/**
|
|
247
|
+
* Effective sticky column count after validation/clamp (`0` = off).
|
|
248
|
+
* Custom `#row`: first `stickyColumnsCount` direct children should be sticky cells
|
|
249
|
+
* with `left: stickyColumnLefts[i]`.
|
|
250
|
+
*/
|
|
251
|
+
stickyColumnsCount?: number;
|
|
252
|
+
/**
|
|
253
|
+
* CSS `left` values for sticky columns `[0 .. stickyColumnsCount - 1]`.
|
|
254
|
+
*/
|
|
255
|
+
stickyColumnLefts?: string[];
|
|
204
256
|
/**
|
|
205
257
|
* Events
|
|
206
258
|
*/
|