@v1nt1248/3nclient-lib 0.3.27 → 0.3.29
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/dist/chunks/ui3n-notification-Dr_3Td9N.js +279 -0
- package/dist/components/index.d.ts +12 -12
- package/dist/components/ui3n-autocomplete/types.d.ts +8 -0
- package/dist/components/ui3n-autocomplete/ui3n-autocomplete.d.ts +2 -2
- package/dist/components/ui3n-badge/types.d.ts +12 -0
- package/dist/components/ui3n-badge/ui3n-badge-simple.d.ts +1 -1
- package/dist/components/ui3n-badge/ui3n-badge.d.ts +1 -1
- package/dist/components/ui3n-breadcrumbs/types.d.ts +1 -1
- package/dist/components/ui3n-breadcrumbs/ui3n-breadcrumb.d.ts +2 -2
- package/dist/components/ui3n-button/types.d.ts +4 -2
- package/dist/components/ui3n-checkbox/types.d.ts +11 -0
- package/dist/components/ui3n-checkbox/ui3n-checkbox.d.ts +4 -1
- package/dist/components/ui3n-chip/types.d.ts +6 -1
- package/dist/components/ui3n-chip/ui3n-chip.d.ts +2 -0
- package/dist/components/ui3n-drop-files/types.d.ts +7 -0
- package/dist/components/ui3n-drop-files/ui3n-drop-files.d.ts +7 -1
- package/dist/components/ui3n-editable/types.d.ts +11 -0
- package/dist/components/ui3n-editable/ui3n-editable.d.ts +4 -2
- package/dist/components/ui3n-icon/types.d.ts +5 -1
- package/dist/components/ui3n-icon/ui3n-icon.d.ts +2 -2
- package/dist/components/ui3n-input/types.d.ts +4 -0
- package/dist/components/ui3n-input/ui3n-input.d.ts +4 -1
- package/dist/components/ui3n-input-file/types.d.ts +11 -0
- package/dist/components/ui3n-input-file/ui3n-input-file.d.ts +4 -1
- package/dist/components/ui3n-list/types.d.ts +4 -0
- package/dist/components/ui3n-menu/types.d.ts +4 -0
- package/dist/components/ui3n-menu/ui3n-menu.d.ts +1 -1
- package/dist/components/ui3n-notification/ui3n-notification.d.ts +2 -2
- package/dist/components/ui3n-progress/ui3n-progress-circular.d.ts +1 -1
- package/dist/components/ui3n-progress/ui3n-progress-linear.d.ts +1 -1
- package/dist/components/ui3n-radio-group/types.d.ts +18 -0
- package/dist/components/ui3n-radio-group/ui3n-radio-group.d.ts +3 -1
- package/dist/components/ui3n-radio-group/ui3n-radio.d.ts +6 -7
- package/dist/components/ui3n-scrollbar-horizontal/types.d.ts +1 -0
- package/dist/components/ui3n-scrollbar-horizontal/ui3n-scroolbar-horizontal.d.ts +1 -0
- package/dist/components/ui3n-scrollbar-vertical/types.d.ts +3 -0
- package/dist/components/ui3n-scrollbar-vertical/ui3n-scrollbar-vertical.d.ts +3 -0
- package/dist/components/ui3n-selector/types.d.ts +11 -0
- package/dist/components/ui3n-selector/ui3n-selector.d.ts +2 -2
- package/dist/components/ui3n-slider/types.d.ts +11 -0
- package/dist/components/ui3n-slider/ui3n-slider.d.ts +4 -1
- package/dist/components/ui3n-step-line-bar/types.d.ts +4 -0
- package/dist/components/ui3n-switch/types.d.ts +11 -0
- package/dist/components/ui3n-switch/ui3n-switch.d.ts +4 -1
- package/dist/components/ui3n-table/composables/useTable.d.ts +1 -0
- package/dist/components/ui3n-table/types.d.ts +4 -0
- package/dist/components/ui3n-table/ui3n-table-sort-icon.d.ts +4 -1
- package/dist/components/ui3n-tabs/types.d.ts +4 -0
- package/dist/components/ui3n-text/types.d.ts +11 -0
- package/dist/components/ui3n-text/ui3n-text.d.ts +4 -1
- package/dist/components/ui3n-tooltip/types.d.ts +4 -0
- package/dist/components/ui3n-tooltip/ui3n-tooltip.d.ts +4 -2
- package/dist/style.css +1 -1
- package/dist/ui3n-components.js +2083 -1697
- package/dist/ui3n-plugins.js +1 -1
- package/package.json +1 -1
- package/src/components/index.ts +29 -7
- package/src/components/ui3n-autocomplete/types.ts +9 -1
- package/src/components/ui3n-autocomplete/ui3n-autocomplete.vue +182 -51
- package/src/components/ui3n-badge/types.ts +10 -0
- package/src/components/ui3n-badge/ui3n-badge-simple.vue +54 -55
- package/src/components/ui3n-badge/ui3n-badge.vue +4 -0
- package/src/components/ui3n-breadcrumbs/types.ts +1 -1
- package/src/components/ui3n-breadcrumbs/ui3n-breadcrumb.vue +7 -5
- package/src/components/ui3n-button/types.ts +4 -2
- package/src/components/ui3n-button/ui3n-button.vue +24 -19
- package/src/components/ui3n-checkbox/types.ts +12 -0
- package/src/components/ui3n-checkbox/ui3n-checkbox.vue +79 -29
- package/src/components/ui3n-chip/types.ts +6 -1
- package/src/components/ui3n-chip/ui3n-chip.vue +64 -61
- package/src/components/ui3n-dialog/ui3n-dialog.vue +3 -3
- package/src/components/ui3n-drop-files/types.ts +8 -0
- package/src/components/ui3n-drop-files/ui3n-drop-files.vue +73 -29
- package/src/components/ui3n-editable/types.ts +12 -0
- package/src/components/ui3n-editable/ui3n-editable.vue +56 -24
- package/src/components/ui3n-icon/types.ts +5 -1
- package/src/components/ui3n-icon/ui3n-icon.vue +45 -50
- package/src/components/ui3n-input/types.ts +4 -0
- package/src/components/ui3n-input/ui3n-input.vue +14 -1
- package/src/components/ui3n-input-file/types.ts +12 -0
- package/src/components/ui3n-input-file/ui3n-input-file.vue +33 -16
- package/src/components/ui3n-list/types.ts +4 -0
- package/src/components/ui3n-list/ui3n-list.vue +3 -1
- package/src/components/ui3n-menu/types.ts +4 -0
- package/src/components/ui3n-menu/ui3n-menu.vue +43 -32
- package/src/components/ui3n-notification/ui3n-notification.vue +190 -175
- package/src/components/ui3n-progress/ui3n-progress-circular.vue +40 -22
- package/src/components/ui3n-progress/ui3n-progress-linear.vue +14 -7
- package/src/components/ui3n-radio-group/types.ts +20 -0
- package/src/components/ui3n-radio-group/ui3n-radio-group.vue +61 -48
- package/src/components/ui3n-radio-group/ui3n-radio.vue +60 -65
- package/src/components/ui3n-scrollbar-horizontal/types.ts +1 -0
- package/src/components/ui3n-scrollbar-horizontal/ui3n-scroolbar-horizontal.vue +8 -1
- package/src/components/ui3n-scrollbar-vertical/types.ts +3 -0
- package/src/components/ui3n-scrollbar-vertical/ui3n-scrollbar-vertical.vue +12 -5
- package/src/components/ui3n-selector/types.ts +12 -0
- package/src/components/ui3n-selector/ui3n-selector.vue +63 -40
- package/src/components/ui3n-slider/types.ts +12 -0
- package/src/components/ui3n-slider/ui3n-slider.vue +88 -39
- package/src/components/ui3n-step-line-bar/types.ts +4 -0
- package/src/components/ui3n-step-line-bar/ui3n-step-line-bar.vue +8 -10
- package/src/components/ui3n-switch/types.ts +12 -0
- package/src/components/ui3n-switch/ui3n-switch.vue +57 -25
- package/src/components/ui3n-table/composables/useTable.ts +17 -0
- package/src/components/ui3n-table/types.ts +4 -0
- package/src/components/ui3n-table/ui3n-table-sort-icon.vue +9 -8
- package/src/components/ui3n-table/ui3n-table.vue +48 -17
- package/src/components/ui3n-tabs/types.ts +4 -0
- package/src/components/ui3n-tabs/ui3n-tabs.vue +146 -108
- package/src/components/ui3n-text/types.ts +12 -0
- package/src/components/ui3n-text/ui3n-text.vue +56 -15
- package/src/components/ui3n-tooltip/types.ts +4 -0
- package/src/components/ui3n-tooltip/ui3n-tooltip.vue +61 -43
- package/src/components/ui3n-virtual-scroll/ui3n-virtual-scroll.vue +201 -17
- package/dist/chunks/ui3n-notification-DlDM-u-R.js +0 -276
|
@@ -1,9 +1,11 @@
|
|
|
1
|
+
<!-- @strictTemplates false -->
|
|
1
2
|
<script lang="ts" setup>
|
|
2
3
|
import { onMounted, ref, watch } from 'vue';
|
|
3
4
|
import { patchTextareaMaxRowsSupport } from '@/utils';
|
|
4
|
-
import type { Ui3nTextEmits, Ui3nTextProps } from './types';
|
|
5
|
+
import type { Ui3nTextEmits, Ui3nTextProps, Ui3nTextExpose } from './types';
|
|
5
6
|
|
|
6
7
|
const props = withDefaults(defineProps<Ui3nTextProps>(), {
|
|
8
|
+
name: undefined,
|
|
7
9
|
rows: 6,
|
|
8
10
|
maxRows: 6,
|
|
9
11
|
label: '',
|
|
@@ -42,9 +44,10 @@
|
|
|
42
44
|
}
|
|
43
45
|
|
|
44
46
|
function onBlur(event: Event) {
|
|
47
|
+
const value = (event.target as HTMLInputElement).value;
|
|
48
|
+
|
|
45
49
|
setTimeout(() => {
|
|
46
50
|
isFocused.value = false;
|
|
47
|
-
const value = (event.target as HTMLInputElement).value;
|
|
48
51
|
validate(value);
|
|
49
52
|
emits('blur', event);
|
|
50
53
|
emits('change', value);
|
|
@@ -82,6 +85,25 @@
|
|
|
82
85
|
emits('keydown', event);
|
|
83
86
|
}
|
|
84
87
|
|
|
88
|
+
function clear() {
|
|
89
|
+
text.value = '';
|
|
90
|
+
errorMessage.value = '';
|
|
91
|
+
isDirty.value = false;
|
|
92
|
+
|
|
93
|
+
if (textareaElement.value) {
|
|
94
|
+
textareaElement.value.value = '';
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
emits('update:modelValue', '');
|
|
98
|
+
emits('input', '');
|
|
99
|
+
emits('change', '');
|
|
100
|
+
emits('update:valid', true);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
defineExpose<Ui3nTextExpose>({
|
|
104
|
+
clear,
|
|
105
|
+
});
|
|
106
|
+
|
|
85
107
|
onMounted(() => {
|
|
86
108
|
if (textareaElement.value) {
|
|
87
109
|
patchTextareaMaxRowsSupport(textareaElement.value!);
|
|
@@ -93,6 +115,15 @@
|
|
|
93
115
|
}
|
|
94
116
|
});
|
|
95
117
|
|
|
118
|
+
watch(
|
|
119
|
+
() => props.name,
|
|
120
|
+
newName => {
|
|
121
|
+
if (!newName) {
|
|
122
|
+
clear();
|
|
123
|
+
}
|
|
124
|
+
},
|
|
125
|
+
);
|
|
126
|
+
|
|
96
127
|
watch(
|
|
97
128
|
() => props.modelValue,
|
|
98
129
|
(val, oldVal) => {
|
|
@@ -106,7 +137,10 @@
|
|
|
106
137
|
</script>
|
|
107
138
|
|
|
108
139
|
<template>
|
|
109
|
-
<div
|
|
140
|
+
<div
|
|
141
|
+
:id="id"
|
|
142
|
+
:class="[$style.ui3nText, disabled && $style.disabled, isFocused && $style.focused]"
|
|
143
|
+
>
|
|
110
144
|
<label
|
|
111
145
|
v-if="label"
|
|
112
146
|
:class="$style.label"
|
|
@@ -119,6 +153,7 @@
|
|
|
119
153
|
ref="textareaElement"
|
|
120
154
|
:class="$style.content"
|
|
121
155
|
:value="text"
|
|
156
|
+
:name="name"
|
|
122
157
|
:placeholder="placeholder"
|
|
123
158
|
:readonly="disabled"
|
|
124
159
|
:rows="rows"
|
|
@@ -138,28 +173,34 @@
|
|
|
138
173
|
|
|
139
174
|
<style lang="scss" module>
|
|
140
175
|
.ui3nText {
|
|
176
|
+
--ui3n-text-border-radius: 4px;
|
|
177
|
+
--ui3n-text-label-font-size: 12px;
|
|
178
|
+
--ui3n-text-font-size: 13px;
|
|
179
|
+
--ui3n-text-padding-inline: 8px;
|
|
180
|
+
--ui3n-text-padding-block: 8px;
|
|
181
|
+
|
|
141
182
|
position: relative;
|
|
142
183
|
width: 100%;
|
|
143
|
-
border-radius: var(--
|
|
184
|
+
border-radius: var(--ui3n-text-border-radius);
|
|
144
185
|
padding: 1px;
|
|
145
186
|
}
|
|
146
187
|
|
|
147
188
|
.label {
|
|
148
189
|
display: block;
|
|
149
190
|
width: 100%;
|
|
150
|
-
font-size: var(--font-
|
|
151
|
-
line-height:
|
|
191
|
+
font-size: var(--ui3n-text-label-font-size);
|
|
192
|
+
line-height: 1.33;
|
|
152
193
|
font-weight: 600;
|
|
153
194
|
color: var(--color-text-control-primary-default);
|
|
154
|
-
margin-bottom:
|
|
195
|
+
margin-bottom: 4px;
|
|
155
196
|
}
|
|
156
197
|
|
|
157
198
|
.body {
|
|
158
199
|
position: relative;
|
|
159
200
|
width: 100%;
|
|
160
|
-
padding: var(--
|
|
201
|
+
padding: var(--ui3n-text-padding-block) 0;
|
|
161
202
|
background-color: var(--color-bg-control-secondary-default);
|
|
162
|
-
border-radius: var(--
|
|
203
|
+
border-radius: var(--ui3n-text-border-radius);
|
|
163
204
|
transition: all 0.2s ease-in-out;
|
|
164
205
|
|
|
165
206
|
&:hover {
|
|
@@ -184,12 +225,12 @@
|
|
|
184
225
|
box-sizing: border-box;
|
|
185
226
|
position: relative;
|
|
186
227
|
width: 100%;
|
|
187
|
-
border-radius: var(--
|
|
228
|
+
border-radius: var(--ui3n-text-border-radius);
|
|
188
229
|
background-color: var(--color-bg-control-secondary-default);
|
|
189
|
-
padding: 0 var(--
|
|
230
|
+
padding: 0 var(--ui3n-text-padding-inline);
|
|
190
231
|
font-family: Inter, system-ui, sans-serif;
|
|
191
|
-
font-size: var(--font-
|
|
192
|
-
line-height:
|
|
232
|
+
font-size: var(--ui3n-text-font-size);
|
|
233
|
+
line-height: 1.23;
|
|
193
234
|
font-weight: 400;
|
|
194
235
|
color: var(--color-text-control-primary-default);
|
|
195
236
|
transition: all 0.2s ease-in-out;
|
|
@@ -197,8 +238,8 @@
|
|
|
197
238
|
&::placeholder {
|
|
198
239
|
color: var(--color-text-control-secondary-default);
|
|
199
240
|
font-style: italic;
|
|
200
|
-
font-size: var(--font-
|
|
201
|
-
line-height:
|
|
241
|
+
font-size: var(--ui3n-text-font-size);
|
|
242
|
+
line-height: 1.23;
|
|
202
243
|
font-weight: 400;
|
|
203
244
|
}
|
|
204
245
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
<script lang="ts" setup>
|
|
2
|
-
import { computed,
|
|
2
|
+
import { computed, onBeforeUnmount, onMounted, ref, watch } from 'vue';
|
|
3
3
|
import type { Nullable } from '@/types';
|
|
4
4
|
import type { Ui3nTooltipProps, Ui3nTooltipEmits, Ui3nTooltipSlots } from './types';
|
|
5
5
|
import { arrow, autoUpdate, offset, useFloating } from '@floating-ui/vue';
|
|
@@ -20,23 +20,17 @@
|
|
|
20
20
|
defineSlots<Ui3nTooltipSlots>();
|
|
21
21
|
|
|
22
22
|
const showTooltip = ref(false);
|
|
23
|
-
const
|
|
23
|
+
const referenceContainer = ref<Nullable<HTMLElement>>(null);
|
|
24
24
|
const floatingEl = ref<Nullable<HTMLElement>>(null);
|
|
25
25
|
const floatingArrowEl = ref<Nullable<HTMLElement>>(null);
|
|
26
26
|
|
|
27
|
-
const maxContentWidthCssValue = computed(() => `${props.maxContentWidth}px`);
|
|
28
|
-
const baseOffsetCssValue = computed(() => `${-baseOffset}px`);
|
|
29
|
-
const arrowSizeCssValue = computed(() => `${baseOffset}px`);
|
|
30
27
|
const mainPlacement = computed(() => {
|
|
31
|
-
|
|
32
|
-
const [part1, _] = props.placement.split('-');
|
|
28
|
+
const [part1] = props.placement.split('-');
|
|
33
29
|
return part1;
|
|
34
30
|
});
|
|
31
|
+
|
|
35
32
|
const offsetOptions = computed(() => {
|
|
36
|
-
const options = {
|
|
37
|
-
mainAxis: 0,
|
|
38
|
-
crossAxis: 0,
|
|
39
|
-
};
|
|
33
|
+
const options = { mainAxis: 0, crossAxis: 0 };
|
|
40
34
|
switch (mainPlacement.value) {
|
|
41
35
|
case 'top':
|
|
42
36
|
options.mainAxis = -1 * Number(props.offsetY) + baseOffset;
|
|
@@ -58,16 +52,32 @@
|
|
|
58
52
|
return options;
|
|
59
53
|
});
|
|
60
54
|
|
|
61
|
-
const
|
|
55
|
+
const middlewareComputed = computed(() => [
|
|
56
|
+
offset(offsetOptions.value),
|
|
57
|
+
arrow({ element: floatingArrowEl, padding: 8 }),
|
|
58
|
+
]);
|
|
59
|
+
|
|
60
|
+
const { floatingStyles, isPositioned, middlewareData } = useFloating(referenceContainer, floatingEl, {
|
|
62
61
|
open: showTooltip,
|
|
63
62
|
placement: props.placement,
|
|
64
63
|
strategy: props.positionStrategy,
|
|
65
|
-
middleware:
|
|
64
|
+
middleware: middlewareComputed,
|
|
66
65
|
whileElementsMounted: props.positionStrategy === 'fixed' ? autoUpdate : undefined,
|
|
67
66
|
});
|
|
68
67
|
|
|
68
|
+
const tooltipStylesComputed = computed(() => ({
|
|
69
|
+
...floatingStyles.value,
|
|
70
|
+
'--ui3n-tooltip-bg-color': props.color,
|
|
71
|
+
'--ui3n-tooltip-text-color': props.textColor,
|
|
72
|
+
'--ui3n-tooltip-max-width': `${props.maxContentWidth}px`,
|
|
73
|
+
'--ui3n-tooltip-arrow-size': `${baseOffset}px`,
|
|
74
|
+
'--ui3n-tooltip-base-offset': `${-baseOffset}px`,
|
|
75
|
+
}));
|
|
76
|
+
|
|
69
77
|
function onMouseenter() {
|
|
70
|
-
if (props.disabled)
|
|
78
|
+
if (props.disabled) {
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
71
81
|
|
|
72
82
|
showTooltip.value = true;
|
|
73
83
|
emits('open');
|
|
@@ -75,7 +85,9 @@
|
|
|
75
85
|
}
|
|
76
86
|
|
|
77
87
|
function onMouseleave() {
|
|
78
|
-
if (props.disabled && !showTooltip.value)
|
|
88
|
+
if (props.disabled && !showTooltip.value) {
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
79
91
|
|
|
80
92
|
showTooltip.value = false;
|
|
81
93
|
emits('close');
|
|
@@ -83,7 +95,9 @@
|
|
|
83
95
|
}
|
|
84
96
|
|
|
85
97
|
function onClick() {
|
|
86
|
-
if (props.disabled && !showTooltip.value)
|
|
98
|
+
if (props.disabled && !showTooltip.value) {
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
87
101
|
|
|
88
102
|
if (showTooltip.value) {
|
|
89
103
|
onMouseleave();
|
|
@@ -93,32 +107,28 @@
|
|
|
93
107
|
}
|
|
94
108
|
|
|
95
109
|
onMounted(() => {
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
if (!el) {
|
|
99
|
-
throw new Error('[ui3n-tooltip] A reference element is absent.');
|
|
110
|
+
if (!referenceContainer.value) {
|
|
111
|
+
return;
|
|
100
112
|
}
|
|
101
113
|
|
|
102
|
-
referenceEl.value = el.nodeName === '#text' ? el.nextElementSibling : el;
|
|
103
|
-
|
|
104
|
-
if (!referenceEl.value) return;
|
|
105
|
-
|
|
106
114
|
if (props.trigger === 'hover') {
|
|
107
|
-
|
|
108
|
-
|
|
115
|
+
referenceContainer.value.addEventListener('mouseenter', onMouseenter);
|
|
116
|
+
referenceContainer.value.addEventListener('mouseleave', onMouseleave);
|
|
109
117
|
} else if (props.trigger === 'click') {
|
|
110
|
-
|
|
118
|
+
referenceContainer.value.addEventListener('click', onClick);
|
|
111
119
|
}
|
|
112
120
|
});
|
|
113
121
|
|
|
114
122
|
onBeforeUnmount(() => {
|
|
115
|
-
if (!
|
|
123
|
+
if (!referenceContainer.value) {
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
116
126
|
|
|
117
127
|
if (props.trigger === 'hover') {
|
|
118
|
-
|
|
119
|
-
|
|
128
|
+
referenceContainer.value.removeEventListener('mouseenter', onMouseenter);
|
|
129
|
+
referenceContainer.value.removeEventListener('mouseleave', onMouseleave);
|
|
120
130
|
} else if (props.trigger === 'click') {
|
|
121
|
-
|
|
131
|
+
referenceContainer.value.removeEventListener('click', onClick);
|
|
122
132
|
}
|
|
123
133
|
});
|
|
124
134
|
|
|
@@ -134,13 +144,18 @@
|
|
|
134
144
|
</script>
|
|
135
145
|
|
|
136
146
|
<template>
|
|
137
|
-
<
|
|
147
|
+
<div
|
|
148
|
+
ref="referenceContainer"
|
|
149
|
+
:class="$style.container"
|
|
150
|
+
>
|
|
151
|
+
<slot />
|
|
152
|
+
</div>
|
|
138
153
|
|
|
139
154
|
<div
|
|
140
155
|
v-if="showTooltip"
|
|
141
156
|
ref="floatingEl"
|
|
142
157
|
:class="$style.floating"
|
|
143
|
-
:style="
|
|
158
|
+
:style="tooltipStylesComputed"
|
|
144
159
|
>
|
|
145
160
|
<slot name="content">
|
|
146
161
|
<div :class="$style.content">
|
|
@@ -159,10 +174,13 @@
|
|
|
159
174
|
</template>
|
|
160
175
|
|
|
161
176
|
<style lang="scss" module>
|
|
162
|
-
.
|
|
163
|
-
|
|
164
|
-
|
|
177
|
+
.container {
|
|
178
|
+
display: inline-block;
|
|
179
|
+
width: max-content;
|
|
180
|
+
max-width: 100%;
|
|
181
|
+
}
|
|
165
182
|
|
|
183
|
+
.floating {
|
|
166
184
|
width: max-content;
|
|
167
185
|
z-index: 5;
|
|
168
186
|
}
|
|
@@ -175,41 +193,41 @@
|
|
|
175
193
|
&-top,
|
|
176
194
|
&-bottom {
|
|
177
195
|
border-style: solid;
|
|
178
|
-
border-width: 0
|
|
196
|
+
border-width: 0 var(--ui3n-tooltip-arrow-size) var(--ui3n-tooltip-arrow-size) var(--ui3n-tooltip-arrow-size);
|
|
179
197
|
border-color: transparent transparent var(--ui3n-tooltip-bg-color) transparent;
|
|
180
198
|
}
|
|
181
199
|
|
|
182
200
|
&-top {
|
|
183
|
-
bottom:
|
|
201
|
+
bottom: var(--ui3n-tooltip-base-offset);
|
|
184
202
|
transform: rotate(180deg);
|
|
185
203
|
}
|
|
186
204
|
|
|
187
205
|
&-bottom {
|
|
188
|
-
top:
|
|
206
|
+
top: var(--ui3n-tooltip-base-offset);
|
|
189
207
|
transform: rotate(0deg);
|
|
190
208
|
}
|
|
191
209
|
|
|
192
210
|
&-left,
|
|
193
211
|
&-right {
|
|
194
212
|
border-style: solid;
|
|
195
|
-
border-width:
|
|
213
|
+
border-width: var(--ui3n-tooltip-arrow-size) 0 var(--ui3n-tooltip-arrow-size) var(--ui3n-tooltip-arrow-size);
|
|
196
214
|
border-color: transparent transparent transparent var(--ui3n-tooltip-bg-color);
|
|
197
215
|
}
|
|
198
216
|
|
|
199
217
|
&-left {
|
|
200
|
-
right:
|
|
218
|
+
right: var(--ui3n-tooltip-base-offset);
|
|
201
219
|
transform: rotate(0deg);
|
|
202
220
|
}
|
|
203
221
|
|
|
204
222
|
&-right {
|
|
205
|
-
left:
|
|
223
|
+
left: var(--ui3n-tooltip-base-offset);
|
|
206
224
|
transform: rotate(180deg);
|
|
207
225
|
}
|
|
208
226
|
}
|
|
209
227
|
|
|
210
228
|
.content {
|
|
211
229
|
position: relative;
|
|
212
|
-
max-width:
|
|
230
|
+
max-width: var(--ui3n-tooltip-max-width);
|
|
213
231
|
padding: 6px var(--spacing-s);
|
|
214
232
|
border-radius: 6px;
|
|
215
233
|
font-size: var(--font-11);
|
|
@@ -1,47 +1,228 @@
|
|
|
1
1
|
<script lang="ts" setup generic="T extends { id?: string }">
|
|
2
|
-
|
|
3
|
-
import
|
|
2
|
+
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
3
|
+
import { computed, ref, shallowRef, onMounted, onBeforeUnmount, watch } from 'vue';
|
|
4
|
+
import Ui3nScrollbarVertical from '../ui3n-scrollbar-vertical/ui3n-scrollbar-vertical.vue';
|
|
4
5
|
import type { Ui3nVirtualScrollProps, Ui3nVirtualScrollSlots } from './types';
|
|
5
6
|
|
|
7
|
+
interface MeasuredSize {
|
|
8
|
+
height: number;
|
|
9
|
+
top: number;
|
|
10
|
+
}
|
|
11
|
+
|
|
6
12
|
const props = withDefaults(defineProps<Ui3nVirtualScrollProps<T>>(), {
|
|
7
13
|
renderAhead: 20,
|
|
14
|
+
minChildHeight: 16,
|
|
8
15
|
});
|
|
9
16
|
defineSlots<Ui3nVirtualScrollSlots<T>>();
|
|
10
17
|
|
|
11
18
|
const scrollTop = ref(0);
|
|
19
|
+
const viewportHeight = ref<number | null>(null);
|
|
12
20
|
const scrollbarComponentRef = ref<InstanceType<typeof Ui3nScrollbarVertical> | null>(null);
|
|
13
21
|
|
|
14
|
-
const
|
|
15
|
-
const
|
|
22
|
+
const sizeCache = shallowRef<MeasuredSize[]>([]);
|
|
23
|
+
const cacheVersion = ref(0);
|
|
24
|
+
|
|
25
|
+
let isAdjustingScroll = false;
|
|
26
|
+
let listResizeObserver: ResizeObserver | null = null;
|
|
27
|
+
let containerResizeObserver: ResizeObserver | null = null;
|
|
28
|
+
|
|
29
|
+
const itemRefs = new Map<string | number, HTMLElement>();
|
|
30
|
+
|
|
31
|
+
const totalHeight = computed(() => {
|
|
32
|
+
cacheVersion.value;
|
|
33
|
+
if (!sizeCache.value.length) {
|
|
34
|
+
return 0;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const lastItem = sizeCache.value[sizeCache.value.length - 1];
|
|
38
|
+
return lastItem.top + lastItem.height;
|
|
39
|
+
});
|
|
16
40
|
|
|
17
41
|
const wrapElementHeight = computed(() => {
|
|
42
|
+
if (viewportHeight.value !== null) {
|
|
43
|
+
return viewportHeight.value;
|
|
44
|
+
}
|
|
45
|
+
|
|
18
46
|
const el = scrollbarComponentRef.value?.getContainer();
|
|
19
47
|
return el ? el.clientHeight : 0;
|
|
20
48
|
});
|
|
21
49
|
|
|
22
50
|
const startNode = computed(() => {
|
|
23
|
-
|
|
24
|
-
|
|
51
|
+
cacheVersion.value;
|
|
52
|
+
const targetTop = scrollTop.value;
|
|
53
|
+
|
|
54
|
+
if (!sizeCache.value.length) {
|
|
55
|
+
return 0;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
let low = 0;
|
|
59
|
+
let high = sizeCache.value.length - 1;
|
|
60
|
+
let foundIndex = -1;
|
|
61
|
+
|
|
62
|
+
while (low <= high) {
|
|
63
|
+
const mid = Math.floor((low + high) / 2);
|
|
64
|
+
const item = sizeCache.value[mid];
|
|
65
|
+
|
|
66
|
+
if (item.top <= targetTop && item.top + item.height > targetTop) {
|
|
67
|
+
foundIndex = mid;
|
|
68
|
+
break;
|
|
69
|
+
} else if (item.top > targetTop) {
|
|
70
|
+
high = mid - 1;
|
|
71
|
+
} else {
|
|
72
|
+
low = mid + 1;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
if (foundIndex === -1) {
|
|
77
|
+
foundIndex = Math.max(0, high);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
return Math.max(0, foundIndex - props.renderAhead);
|
|
25
81
|
});
|
|
26
82
|
|
|
27
83
|
const visibleNodeCount = computed(() => {
|
|
28
|
-
|
|
84
|
+
const height = wrapElementHeight.value;
|
|
85
|
+
if (!height) {
|
|
86
|
+
return props.renderAhead * 2;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
const estimatedVisible = Math.ceil(height / props.minChildHeight);
|
|
90
|
+
return estimatedVisible + 2 * props.renderAhead;
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
const offsetY = computed(() => {
|
|
94
|
+
cacheVersion.value;
|
|
95
|
+
if (!sizeCache.value.length) {
|
|
96
|
+
return 0;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
return sizeCache.value[startNode.value]?.top || 0;
|
|
29
100
|
});
|
|
30
101
|
|
|
31
|
-
const
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
102
|
+
const visibleChildren = computed(() => {
|
|
103
|
+
const start = startNode.value;
|
|
104
|
+
const count = visibleNodeCount.value;
|
|
105
|
+
|
|
106
|
+
if (start + count >= props.items.length - props.renderAhead) {
|
|
107
|
+
return props.items.slice(start);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
return props.items.slice(start, start + count);
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
function onScroll(event: Event) {
|
|
114
|
+
if (isAdjustingScroll) {
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
35
117
|
|
|
36
|
-
const onScroll = (event: Event) => {
|
|
37
118
|
scrollTop.value = (event.target as HTMLDivElement).scrollTop;
|
|
38
|
-
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
function setItemRef(el: any, id: string | number) {
|
|
122
|
+
if (el) {
|
|
123
|
+
const element = el.$el || el;
|
|
124
|
+
itemRefs.set(id, element);
|
|
125
|
+
listResizeObserver?.observe(element);
|
|
126
|
+
} else {
|
|
127
|
+
itemRefs.delete(id);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
function initCache() {
|
|
132
|
+
let currentTop = 0;
|
|
133
|
+
sizeCache.value = props.items.map(() => {
|
|
134
|
+
const size = { height: props.minChildHeight, top: currentTop };
|
|
135
|
+
currentTop += props.minChildHeight;
|
|
136
|
+
return size;
|
|
137
|
+
});
|
|
138
|
+
cacheVersion.value++;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
onMounted(() => {
|
|
142
|
+
const containerEl = scrollbarComponentRef.value?.getContainer();
|
|
143
|
+
|
|
144
|
+
if (containerEl) {
|
|
145
|
+
viewportHeight.value = containerEl.clientHeight;
|
|
146
|
+
containerResizeObserver = new ResizeObserver(entries => {
|
|
147
|
+
for (const entry of entries) {
|
|
148
|
+
viewportHeight.value = entry.contentRect.height || entry.target.clientHeight;
|
|
149
|
+
}
|
|
150
|
+
});
|
|
151
|
+
containerResizeObserver.observe(containerEl);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
listResizeObserver = new ResizeObserver(entries => {
|
|
155
|
+
requestAnimationFrame(() => {
|
|
156
|
+
let lowestChangedIndex = Infinity;
|
|
157
|
+
let totalDeltaAboveKey = 0;
|
|
158
|
+
|
|
159
|
+
const updatedCache = [...sizeCache.value];
|
|
160
|
+
const currentScrollTop = scrollTop.value;
|
|
161
|
+
|
|
162
|
+
for (const entry of entries) {
|
|
163
|
+
const sid = entry.target.getAttribute('data-sid');
|
|
164
|
+
if (!sid) {
|
|
165
|
+
continue;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
const index = props.items.findIndex((item, idx) => (item.id ?? idx).toString() === sid);
|
|
169
|
+
if (index === -1) {
|
|
170
|
+
continue;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
const realHeight = entry.contentRect.height || entry.target.getBoundingClientRect().height;
|
|
174
|
+
|
|
175
|
+
if (updatedCache[index] && updatedCache[index].height !== realHeight) {
|
|
176
|
+
const oldHeight = updatedCache[index].height;
|
|
177
|
+
updatedCache[index] = { ...updatedCache[index], height: realHeight };
|
|
178
|
+
|
|
179
|
+
if (updatedCache[index].top < currentScrollTop) {
|
|
180
|
+
totalDeltaAboveKey += realHeight - oldHeight;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
if (index < lowestChangedIndex) {
|
|
184
|
+
lowestChangedIndex = index;
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
if (lowestChangedIndex !== Infinity) {
|
|
190
|
+
let currentTop = updatedCache[lowestChangedIndex].top;
|
|
191
|
+
|
|
192
|
+
for (let i = lowestChangedIndex; i < updatedCache.length; i++) {
|
|
193
|
+
updatedCache[i].top = currentTop;
|
|
194
|
+
currentTop += updatedCache[i].height;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
sizeCache.value = updatedCache;
|
|
198
|
+
cacheVersion.value++;
|
|
199
|
+
|
|
200
|
+
if (containerEl && totalDeltaAboveKey !== 0) {
|
|
201
|
+
isAdjustingScroll = true;
|
|
202
|
+
containerEl.scrollTop = currentScrollTop + totalDeltaAboveKey;
|
|
203
|
+
scrollTop.value = containerEl.scrollTop;
|
|
204
|
+
isAdjustingScroll = false;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
scrollbarComponentRef.value?.updateMetrics();
|
|
208
|
+
}
|
|
209
|
+
});
|
|
210
|
+
});
|
|
211
|
+
});
|
|
212
|
+
|
|
213
|
+
onBeforeUnmount(() => {
|
|
214
|
+
listResizeObserver?.disconnect();
|
|
215
|
+
containerResizeObserver?.disconnect();
|
|
216
|
+
});
|
|
217
|
+
|
|
218
|
+
watch(() => props.items, initCache, { immediate: true });
|
|
39
219
|
</script>
|
|
40
220
|
|
|
41
221
|
<template>
|
|
42
222
|
<ui3n-scrollbar-vertical
|
|
43
223
|
ref="scrollbarComponentRef"
|
|
44
224
|
:class="$style.ui3nVirtualScroll"
|
|
225
|
+
:auto-update="false"
|
|
45
226
|
@scroll="onScroll"
|
|
46
227
|
>
|
|
47
228
|
<div
|
|
@@ -55,7 +236,8 @@
|
|
|
55
236
|
<div
|
|
56
237
|
v-for="(item, index) in visibleChildren"
|
|
57
238
|
:key="item.id"
|
|
58
|
-
:
|
|
239
|
+
:ref="el => setItemRef(el, item.id ?? startNode + index)"
|
|
240
|
+
v-bind="{ 'data-sid': item.id ?? startNode + index }"
|
|
59
241
|
:class="$style.item"
|
|
60
242
|
>
|
|
61
243
|
<slot
|
|
@@ -71,8 +253,6 @@
|
|
|
71
253
|
|
|
72
254
|
<style lang="scss" module>
|
|
73
255
|
.ui3nVirtualScroll {
|
|
74
|
-
--ui3n-virtual-scroll-item-min-height: 16px;
|
|
75
|
-
|
|
76
256
|
background-color: var(--color-bg-block-primary-default);
|
|
77
257
|
}
|
|
78
258
|
|
|
@@ -80,6 +260,9 @@
|
|
|
80
260
|
position: relative;
|
|
81
261
|
overflow: hidden;
|
|
82
262
|
will-change: transform;
|
|
263
|
+
box-sizing: border-box;
|
|
264
|
+
margin: 0 !important;
|
|
265
|
+
padding: 0 !important;
|
|
83
266
|
}
|
|
84
267
|
|
|
85
268
|
.content {
|
|
@@ -88,6 +271,7 @@
|
|
|
88
271
|
|
|
89
272
|
.item {
|
|
90
273
|
position: relative;
|
|
91
|
-
min-height:
|
|
274
|
+
min-height: v-bind('props.minChildHeight + "px"');
|
|
275
|
+
box-sizing: border-box;
|
|
92
276
|
}
|
|
93
277
|
</style>
|