@v1nt1248/3nclient-lib 0.3.28 → 0.3.30
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 +13 -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 +9 -0
- package/dist/components/ui3n-menu/ui3n-menu.d.ts +2 -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-vertical/types.d.ts +2 -0
- package/dist/components/ui3n-scrollbar-vertical/ui3n-scrollbar-vertical.d.ts +2 -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 +2121 -1696
- 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 +14 -1
- package/src/components/ui3n-autocomplete/ui3n-autocomplete.vue +189 -55
- 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 +9 -0
- package/src/components/ui3n-menu/ui3n-menu.vue +169 -39
- 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-vertical/types.ts +2 -0
- package/src/components/ui3n-scrollbar-vertical/ui3n-scrollbar-vertical.vue +4 -4
- 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,67 +1,62 @@
|
|
|
1
1
|
<script lang="ts" setup>
|
|
2
|
-
import { computed } from 'vue';
|
|
3
|
-
import type { Ui3nIconEmits, Ui3nIconProps } from './types';
|
|
2
|
+
import { computed } from 'vue';
|
|
3
|
+
import type { Ui3nIconEmits, Ui3nIconProps } from './types';
|
|
4
4
|
|
|
5
|
-
const props = withDefaults(
|
|
6
|
-
defineProps<Ui3nIconProps>(),
|
|
7
|
-
{
|
|
5
|
+
const props = withDefaults(defineProps<Ui3nIconProps>(), {
|
|
8
6
|
title: '',
|
|
9
7
|
rotate: 0,
|
|
10
8
|
color: 'var(--color-icon-control-primary-default)',
|
|
11
|
-
}
|
|
12
|
-
);
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
const
|
|
16
|
-
|
|
17
|
-
const
|
|
18
|
-
const
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
function onClick(ev: Event) {
|
|
34
|
-
emits('click', ev);
|
|
35
|
-
}
|
|
9
|
+
});
|
|
10
|
+
const emits = defineEmits<Ui3nIconEmits>();
|
|
11
|
+
|
|
12
|
+
const widthVal = computed(() => props.size || props.width || 16);
|
|
13
|
+
const heightVal = computed(() => props.size || props.height || 16);
|
|
14
|
+
|
|
15
|
+
const iconStyle = computed(() => {
|
|
16
|
+
const value: Record<string, string> = {
|
|
17
|
+
'--ui3n-icon-width': `${widthVal.value}px`,
|
|
18
|
+
'--ui3n-icon-height': `${heightVal.value}px`,
|
|
19
|
+
'--ui3n-icon-color': props.color,
|
|
20
|
+
};
|
|
21
|
+
const transformData = [];
|
|
22
|
+
props.horizontalFlip && transformData.push('rotateY(180deg)');
|
|
23
|
+
props.verticalFlip && transformData.push('rotateX(180deg)');
|
|
24
|
+
props.rotate && transformData.push(`rotate(${props.rotate}deg)`);
|
|
25
|
+
if (transformData.length) {
|
|
26
|
+
value.transform = transformData.join(' ');
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
return value;
|
|
30
|
+
});
|
|
36
31
|
</script>
|
|
37
32
|
|
|
38
33
|
<template>
|
|
39
34
|
<div
|
|
35
|
+
:id="id"
|
|
40
36
|
:class="$style.ui3nIcon"
|
|
41
|
-
:data-icon="icon"
|
|
42
37
|
:title="title"
|
|
43
38
|
:style="iconStyle"
|
|
44
|
-
|
|
39
|
+
v-bind="{ 'data-icon': icon }"
|
|
40
|
+
@click="emits('click', $event)"
|
|
45
41
|
/>
|
|
46
42
|
</template>
|
|
47
43
|
|
|
48
44
|
<style lang="scss" module>
|
|
49
|
-
.ui3nIcon {
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
}
|
|
45
|
+
.ui3nIcon {
|
|
46
|
+
display: block;
|
|
47
|
+
min-width: var(--ui3n-icon-width);
|
|
48
|
+
width: var(--ui3n-icon-width);
|
|
49
|
+
|
|
50
|
+
min-height: var(--ui3n-icon-height);
|
|
51
|
+
height: var(--ui3n-icon-height);
|
|
52
|
+
max-height: var(--ui3n-icon-height);
|
|
53
|
+
|
|
54
|
+
flex-grow: 0;
|
|
55
|
+
flex-shrink: 0;
|
|
56
|
+
background-color: currentcolor;
|
|
57
|
+
color: var(--ui3n-icon-color);
|
|
58
|
+
mask-image: var(--svg);
|
|
59
|
+
mask-repeat: no-repeat;
|
|
60
|
+
mask-size: 100% 100%;
|
|
61
|
+
}
|
|
67
62
|
</style>
|
|
@@ -6,6 +6,9 @@
|
|
|
6
6
|
|
|
7
7
|
const props = withDefaults(defineProps<Ui3nInputProps>(), {
|
|
8
8
|
modelValue: '',
|
|
9
|
+
name: undefined,
|
|
10
|
+
maxlength: undefined,
|
|
11
|
+
minlength: undefined,
|
|
9
12
|
type: 'text',
|
|
10
13
|
size: 'regular',
|
|
11
14
|
label: '',
|
|
@@ -177,10 +180,20 @@
|
|
|
177
180
|
}
|
|
178
181
|
},
|
|
179
182
|
);
|
|
183
|
+
|
|
184
|
+
watch(
|
|
185
|
+
() => props.name,
|
|
186
|
+
newName => {
|
|
187
|
+
if (!newName) {
|
|
188
|
+
clearValue();
|
|
189
|
+
}
|
|
190
|
+
},
|
|
191
|
+
);
|
|
180
192
|
</script>
|
|
181
193
|
|
|
182
194
|
<template>
|
|
183
195
|
<div
|
|
196
|
+
:id="id"
|
|
184
197
|
:class="[
|
|
185
198
|
$style.ui3nInput,
|
|
186
199
|
size === 'large' && $style.large,
|
|
@@ -359,7 +372,7 @@
|
|
|
359
372
|
padding: 0 var(--ui3n-input-padding-x);
|
|
360
373
|
background-color: transparent;
|
|
361
374
|
font-size: var(--ui3n-input-font-size);
|
|
362
|
-
line-height:
|
|
375
|
+
line-height: 1.23;
|
|
363
376
|
font-weight: 400;
|
|
364
377
|
color: var(--color-text-control-primary-default);
|
|
365
378
|
transition: color 0.2s ease-in-out;
|
|
@@ -1,6 +1,14 @@
|
|
|
1
1
|
import { VNode } from 'vue';
|
|
2
2
|
|
|
3
3
|
export interface Ui3nInputFileProps {
|
|
4
|
+
/**
|
|
5
|
+
* Root element id
|
|
6
|
+
*/
|
|
7
|
+
id?: string;
|
|
8
|
+
/**
|
|
9
|
+
* Input name attribute (for use in native forms)
|
|
10
|
+
*/
|
|
11
|
+
name?: string;
|
|
4
12
|
/**
|
|
5
13
|
* Selected files
|
|
6
14
|
*/
|
|
@@ -50,3 +58,7 @@ export interface Ui3nInputFileSlots {
|
|
|
50
58
|
*/
|
|
51
59
|
default: () => VNode;
|
|
52
60
|
}
|
|
61
|
+
|
|
62
|
+
export interface Ui3nInputFileExpose {
|
|
63
|
+
clear: () => void;
|
|
64
|
+
}
|
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
<script lang="ts" setup>
|
|
2
|
-
import { ref } from 'vue';
|
|
2
|
+
import { ref, watch } from 'vue';
|
|
3
3
|
import size from 'lodash/size';
|
|
4
4
|
import last from 'lodash/last';
|
|
5
5
|
import trim from 'lodash/trim';
|
|
6
6
|
import { getRandomId, formatFileSize } from '@/utils';
|
|
7
|
-
import type { Ui3nInputFileProps, Ui3nInputFileEmits, Ui3nInputFileSlots } from './types';
|
|
8
7
|
import type { Nullable } from '@/types';
|
|
8
|
+
import type { Ui3nInputFileProps, Ui3nInputFileEmits, Ui3nInputFileSlots, Ui3nInputFileExpose } from './types';
|
|
9
9
|
|
|
10
10
|
const props = withDefaults(defineProps<Ui3nInputFileProps>(), {
|
|
11
|
+
name: undefined,
|
|
11
12
|
modelValue: () => [],
|
|
12
13
|
buttonText: 'Select file',
|
|
13
14
|
allowedFileTypes: '*',
|
|
@@ -15,7 +16,7 @@
|
|
|
15
16
|
const emits = defineEmits<Ui3nInputFileEmits>();
|
|
16
17
|
defineSlots<Ui3nInputFileSlots>();
|
|
17
18
|
|
|
18
|
-
const id = getRandomId(4);
|
|
19
|
+
const id = props.id || getRandomId(4);
|
|
19
20
|
const fileInput = ref<Nullable<HTMLInputElement>>(null);
|
|
20
21
|
|
|
21
22
|
function selectFiles(ev: MouseEvent) {
|
|
@@ -80,15 +81,8 @@
|
|
|
80
81
|
return true;
|
|
81
82
|
}
|
|
82
83
|
|
|
83
|
-
const
|
|
84
|
-
|
|
85
|
-
if (size) {
|
|
86
|
-
acc += size;
|
|
87
|
-
}
|
|
88
|
-
return acc;
|
|
89
|
-
}, 0);
|
|
90
|
-
|
|
91
|
-
const isValid = total <= Number(props.maxNumberOfFiles);
|
|
84
|
+
const totalCount = size(props.modelValue) + size(files);
|
|
85
|
+
const isValid = totalCount <= Number(props.maxNumberOfFiles);
|
|
92
86
|
if (!isValid) {
|
|
93
87
|
emits('error', `You cannot select more than ${props.maxNumberOfFiles} files.`);
|
|
94
88
|
}
|
|
@@ -116,6 +110,26 @@
|
|
|
116
110
|
fileInput.value && fileInput.value.files && processFiles([...fileInput.value.files]);
|
|
117
111
|
fileInput.value && (fileInput.value!.value = '');
|
|
118
112
|
}
|
|
113
|
+
|
|
114
|
+
function clear() {
|
|
115
|
+
if (fileInput.value) {
|
|
116
|
+
fileInput.value.value = '';
|
|
117
|
+
}
|
|
118
|
+
emits('update:modelValue', []);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
defineExpose<Ui3nInputFileExpose>({
|
|
122
|
+
clear,
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
watch(
|
|
126
|
+
() => props.name,
|
|
127
|
+
newName => {
|
|
128
|
+
if (!newName) {
|
|
129
|
+
clear();
|
|
130
|
+
}
|
|
131
|
+
},
|
|
132
|
+
);
|
|
119
133
|
</script>
|
|
120
134
|
|
|
121
135
|
<template>
|
|
@@ -132,7 +146,7 @@
|
|
|
132
146
|
:id="id"
|
|
133
147
|
ref="fileInput"
|
|
134
148
|
type="file"
|
|
135
|
-
name="file"
|
|
149
|
+
:name="name || 'file'"
|
|
136
150
|
hidden
|
|
137
151
|
:multiple="multiple"
|
|
138
152
|
:accept="allowedFileTypes"
|
|
@@ -144,16 +158,19 @@
|
|
|
144
158
|
|
|
145
159
|
<style lang="scss" module>
|
|
146
160
|
.ui3nInputFile {
|
|
161
|
+
--ui3n-input-file-font-size: 12px;
|
|
162
|
+
--ui3n-input-file-padding-inline: 4px;
|
|
163
|
+
|
|
147
164
|
position: relative;
|
|
148
165
|
width: max-content;
|
|
149
166
|
}
|
|
150
167
|
|
|
151
168
|
.label {
|
|
152
|
-
font-size: var(--font-
|
|
169
|
+
font-size: var(--ui3n-input-file-font-size);
|
|
153
170
|
font-weight: 500;
|
|
154
|
-
line-height:
|
|
171
|
+
line-height: 1.33;
|
|
155
172
|
color: var(--color-text-button-secondary-default);
|
|
156
|
-
padding: 0 var(--
|
|
173
|
+
padding: 0 var(--ui3n-input-file-padding-inline);
|
|
157
174
|
cursor: pointer;
|
|
158
175
|
|
|
159
176
|
&:hover {
|
|
@@ -15,6 +15,7 @@
|
|
|
15
15
|
<template>
|
|
16
16
|
<div
|
|
17
17
|
ref="listElement"
|
|
18
|
+
:id="id"
|
|
18
19
|
:class="$style.ui3nList"
|
|
19
20
|
>
|
|
20
21
|
<div :class="[$style.ui3nListTitle, sticky && $style.ui3nListTitleSticky]">
|
|
@@ -55,6 +56,7 @@
|
|
|
55
56
|
.ui3nList {
|
|
56
57
|
--ui3n-list-item-height: 28px;
|
|
57
58
|
--ui3n-list-bg-color: transparent;
|
|
59
|
+
--ui3n-list-padding-block: 4px;
|
|
58
60
|
|
|
59
61
|
position: relative;
|
|
60
62
|
width: 100%;
|
|
@@ -81,7 +83,7 @@
|
|
|
81
83
|
position: relative;
|
|
82
84
|
width: 100%;
|
|
83
85
|
min-height: var(--ui3n-list-item-height);
|
|
84
|
-
padding:
|
|
86
|
+
padding: var(--ui3n-list-padding-block) 0;
|
|
85
87
|
display: flex;
|
|
86
88
|
justify-content: flex-start;
|
|
87
89
|
align-items: center;
|
|
@@ -4,6 +4,10 @@ import type { Ref, VNode } from 'vue';
|
|
|
4
4
|
* Menu component properties
|
|
5
5
|
*/
|
|
6
6
|
export interface Ui3nMenuProps {
|
|
7
|
+
/**
|
|
8
|
+
* Root element id
|
|
9
|
+
*/
|
|
10
|
+
id?: string;
|
|
7
11
|
/**
|
|
8
12
|
* Menu open state
|
|
9
13
|
*/
|
|
@@ -27,6 +31,11 @@ export interface Ui3nMenuProps {
|
|
|
27
31
|
* @default true
|
|
28
32
|
*/
|
|
29
33
|
allowFlip?: boolean;
|
|
34
|
+
/**
|
|
35
|
+
* Whether to lock scroll when menu is open
|
|
36
|
+
* @default false
|
|
37
|
+
*/
|
|
38
|
+
lockScroll?: boolean;
|
|
30
39
|
/**
|
|
31
40
|
* Offset X
|
|
32
41
|
* @default 0
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
<script lang="ts" setup>
|
|
2
|
-
import { computed, ref, watch, useTemplateRef } from 'vue';
|
|
2
|
+
import { computed, nextTick, onUnmounted, ref, watch, useTemplateRef } from 'vue';
|
|
3
3
|
import { autoUpdate, flip, useFloating, offset, shift } from '@floating-ui/vue';
|
|
4
4
|
import { default as vClickOutside } from '../../directives/ui3n-click-outside';
|
|
5
|
-
import type { Ui3nMenuEmits, Ui3nMenuProps, Ui3nMenuSlots, Ui3nMenuExpose } from './types';
|
|
6
5
|
import { Nullable } from '@/types';
|
|
6
|
+
import type { Ui3nMenuEmits, Ui3nMenuProps, Ui3nMenuSlots, Ui3nMenuExpose } from './types';
|
|
7
7
|
|
|
8
8
|
const props = withDefaults(defineProps<Ui3nMenuProps>(), {
|
|
9
|
+
lockScroll: false,
|
|
9
10
|
positionStrategy: 'absolute',
|
|
10
11
|
offsetX: 0,
|
|
11
12
|
offsetY: 0,
|
|
@@ -23,37 +24,32 @@
|
|
|
23
24
|
const menuContentElement = useTemplateRef<HTMLDivElement>('menu-content-element');
|
|
24
25
|
const isShow = ref(false);
|
|
25
26
|
|
|
27
|
+
let targetScrollElement: HTMLElement | null = null;
|
|
28
|
+
|
|
26
29
|
const outerTriggerElement = ref<Nullable<HTMLElement>>(props.triggerElement || null);
|
|
27
30
|
|
|
28
31
|
const usedTriggerElement = computed(() => outerTriggerElement.value || menuTriggerElement.value);
|
|
29
32
|
|
|
30
|
-
const
|
|
31
|
-
|
|
32
|
-
|
|
33
|
+
const middleware = computed(() => {
|
|
34
|
+
const list = [
|
|
35
|
+
offset({
|
|
36
|
+
mainAxis: props.offsetY,
|
|
37
|
+
crossAxis: props.offsetX + 2,
|
|
38
|
+
}),
|
|
39
|
+
];
|
|
40
|
+
|
|
41
|
+
if (props.allowFlip) {
|
|
42
|
+
list.push(
|
|
43
|
+
flip({
|
|
44
|
+
fallbackAxisSideDirection: 'end',
|
|
45
|
+
fallbackPlacements: ['top-end', 'bottom-end'],
|
|
46
|
+
}),
|
|
47
|
+
);
|
|
33
48
|
}
|
|
34
49
|
|
|
35
|
-
|
|
36
|
-
return
|
|
50
|
+
list.push(shift());
|
|
51
|
+
return list;
|
|
37
52
|
});
|
|
38
|
-
const zIndexCss = computed(() => props.zIndex);
|
|
39
|
-
|
|
40
|
-
const middleware = [
|
|
41
|
-
offset({
|
|
42
|
-
mainAxis: props.offsetY,
|
|
43
|
-
crossAxis: props.offsetX + 2,
|
|
44
|
-
}),
|
|
45
|
-
];
|
|
46
|
-
|
|
47
|
-
if (props.allowFlip) {
|
|
48
|
-
middleware.push(
|
|
49
|
-
flip({
|
|
50
|
-
fallbackAxisSideDirection: 'end',
|
|
51
|
-
fallbackPlacements: ['top-end', 'bottom-end'],
|
|
52
|
-
}),
|
|
53
|
-
);
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
middleware.push(shift());
|
|
57
53
|
|
|
58
54
|
const { floatingStyles, isPositioned, update } = useFloating(usedTriggerElement, menuContentElement, {
|
|
59
55
|
placement: 'bottom-start',
|
|
@@ -62,6 +58,126 @@
|
|
|
62
58
|
whileElementsMounted: props.positionAutoupdate || props.positionStrategy === 'fixed' ? autoUpdate : undefined,
|
|
63
59
|
});
|
|
64
60
|
|
|
61
|
+
const contentStylesComputed = computed(() => {
|
|
62
|
+
let borderRadiusStr = '';
|
|
63
|
+
if (typeof props.contentBorderRadius === 'number') {
|
|
64
|
+
borderRadiusStr = `${props.contentBorderRadius}px`;
|
|
65
|
+
} else if (Array.isArray(props.contentBorderRadius)) {
|
|
66
|
+
const [tl, tr, br, bl] = props.contentBorderRadius;
|
|
67
|
+
borderRadiusStr = `${tl}px ${tr}px ${br}px ${bl}px`;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
return {
|
|
71
|
+
...floatingStyles.value,
|
|
72
|
+
'--ui3n-menu-zIndex': String(props.zIndex),
|
|
73
|
+
...(borderRadiusStr && { '--ui3n-menu-border-radius': borderRadiusStr }),
|
|
74
|
+
...(props.contentStyles || {}),
|
|
75
|
+
};
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
function preventScrollEvent(e: Event) {
|
|
79
|
+
e.preventDefault();
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function getVerticalScrollParent(element: HTMLElement | null): HTMLElement {
|
|
83
|
+
if (!element) {
|
|
84
|
+
return document.body;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
let parent: HTMLElement | null = element.parentElement;
|
|
88
|
+
|
|
89
|
+
while (parent && parent !== document.body && parent !== document.documentElement) {
|
|
90
|
+
const style = window.getComputedStyle(parent);
|
|
91
|
+
|
|
92
|
+
if (/(auto|scroll)/.test(style.overflowY || style.overflow)) {
|
|
93
|
+
if (parent.scrollHeight > parent.clientHeight) {
|
|
94
|
+
return parent;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
parent = parent.parentElement;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
return document.body;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
function lockScroll() {
|
|
104
|
+
if (!props.lockScroll) {
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
const trigger = usedTriggerElement.value;
|
|
109
|
+
targetScrollElement = getVerticalScrollParent(trigger);
|
|
110
|
+
|
|
111
|
+
if (targetScrollElement) {
|
|
112
|
+
const el = targetScrollElement;
|
|
113
|
+
|
|
114
|
+
const currentLocks = Number(el.dataset.scrollLocksCount || '0');
|
|
115
|
+
|
|
116
|
+
// If this is the FIRST menu opened for this container
|
|
117
|
+
if (currentLocks === 0) {
|
|
118
|
+
el.dataset.scrollLocksCount = '1';
|
|
119
|
+
|
|
120
|
+
// We remember the original overscroll-behavior-y in data-original-overscroll
|
|
121
|
+
el.dataset.originalOverscroll = el.style.overscrollBehaviorY || '';
|
|
122
|
+
|
|
123
|
+
// Isolating scrolling
|
|
124
|
+
el.style.overscrollBehaviorY = 'contain';
|
|
125
|
+
|
|
126
|
+
if (targetScrollElement === document.body) {
|
|
127
|
+
document.addEventListener('wheel', preventScrollEvent, { passive: false });
|
|
128
|
+
document.addEventListener('touchmove', preventScrollEvent, { passive: false });
|
|
129
|
+
} else {
|
|
130
|
+
targetScrollElement.addEventListener('wheel', preventScrollEvent, { passive: false });
|
|
131
|
+
targetScrollElement.addEventListener('touchmove', preventScrollEvent, { passive: false });
|
|
132
|
+
}
|
|
133
|
+
} else {
|
|
134
|
+
// If the container is already blocked by another menu, simply increment the counter
|
|
135
|
+
el.dataset.scrollLocksCount = String(currentLocks + 1);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
function unlockScroll() {
|
|
141
|
+
if (!targetScrollElement) {
|
|
142
|
+
return;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
const el = targetScrollElement;
|
|
146
|
+
const currentLocks = Number(el.dataset.scrollLocksCount || '0');
|
|
147
|
+
|
|
148
|
+
if (currentLocks > 0) {
|
|
149
|
+
const newLocks = currentLocks - 1;
|
|
150
|
+
|
|
151
|
+
// Release the lock only when the LAST menu holding this container has closed
|
|
152
|
+
if (newLocks === 0) {
|
|
153
|
+
const originalOverscroll = el.dataset.originalOverscroll;
|
|
154
|
+
|
|
155
|
+
if (originalOverscroll) {
|
|
156
|
+
el.style.overscrollBehaviorY = originalOverscroll;
|
|
157
|
+
} else {
|
|
158
|
+
el.style.removeProperty('overscroll-behavior-y');
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
if (targetScrollElement === document.body) {
|
|
162
|
+
document.removeEventListener('wheel', preventScrollEvent);
|
|
163
|
+
document.removeEventListener('touchmove', preventScrollEvent);
|
|
164
|
+
} else {
|
|
165
|
+
targetScrollElement.removeEventListener('wheel', preventScrollEvent);
|
|
166
|
+
targetScrollElement.removeEventListener('touchmove', preventScrollEvent);
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
// We completely remove data attributes from the DOM tree to avoid leaving garbage.
|
|
170
|
+
delete el.dataset.scrollLocksCount;
|
|
171
|
+
delete el.dataset.originalOverscroll;
|
|
172
|
+
} else {
|
|
173
|
+
// If there are still open menus, simply keep the reduced counter
|
|
174
|
+
el.dataset.scrollLocksCount = String(newLocks);
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
targetScrollElement = null;
|
|
179
|
+
}
|
|
180
|
+
|
|
65
181
|
function toggleMenu() {
|
|
66
182
|
if (props.disabled) {
|
|
67
183
|
return;
|
|
@@ -78,7 +194,11 @@
|
|
|
78
194
|
emits('update:modelValue', isShow.value);
|
|
79
195
|
}
|
|
80
196
|
|
|
81
|
-
function onClickOutside() {
|
|
197
|
+
function onClickOutside(event: MouseEvent) {
|
|
198
|
+
if (usedTriggerElement.value?.contains(event.target as Node)) {
|
|
199
|
+
return;
|
|
200
|
+
}
|
|
201
|
+
|
|
82
202
|
emits('click-outside');
|
|
83
203
|
if (props.closeOnClickOutside) {
|
|
84
204
|
isShow.value = false;
|
|
@@ -87,13 +207,13 @@
|
|
|
87
207
|
}
|
|
88
208
|
}
|
|
89
209
|
|
|
90
|
-
watch(isPositioned, val => {
|
|
91
|
-
val ? emits('opened') : emits('closed');
|
|
92
|
-
});
|
|
93
|
-
|
|
94
210
|
watch(
|
|
95
211
|
() => props.modelValue,
|
|
96
|
-
val => {
|
|
212
|
+
async val => {
|
|
213
|
+
if (!val) {
|
|
214
|
+
unlockScroll();
|
|
215
|
+
}
|
|
216
|
+
|
|
97
217
|
if (isShow.value !== val) {
|
|
98
218
|
isShow.value = val;
|
|
99
219
|
}
|
|
@@ -101,6 +221,15 @@
|
|
|
101
221
|
{ immediate: true },
|
|
102
222
|
);
|
|
103
223
|
|
|
224
|
+
watch(isPositioned, val => {
|
|
225
|
+
if (val) {
|
|
226
|
+
lockScroll();
|
|
227
|
+
emits('opened');
|
|
228
|
+
} else {
|
|
229
|
+
emits('closed');
|
|
230
|
+
}
|
|
231
|
+
});
|
|
232
|
+
|
|
104
233
|
watch(
|
|
105
234
|
() => props.triggerElement,
|
|
106
235
|
val => {
|
|
@@ -117,6 +246,10 @@
|
|
|
117
246
|
}
|
|
118
247
|
});
|
|
119
248
|
|
|
249
|
+
onUnmounted(() => {
|
|
250
|
+
unlockScroll();
|
|
251
|
+
});
|
|
252
|
+
|
|
120
253
|
defineExpose<Ui3nMenuExpose>({
|
|
121
254
|
isPositioned,
|
|
122
255
|
update,
|
|
@@ -126,6 +259,7 @@
|
|
|
126
259
|
<template>
|
|
127
260
|
<div
|
|
128
261
|
ref="menu-element"
|
|
262
|
+
:id="id"
|
|
129
263
|
:class="$style.ui3nMenu"
|
|
130
264
|
>
|
|
131
265
|
<div
|
|
@@ -140,10 +274,7 @@
|
|
|
140
274
|
v-if="isShow"
|
|
141
275
|
ref="menu-content-element"
|
|
142
276
|
v-click-outside="onClickOutside"
|
|
143
|
-
:style="
|
|
144
|
-
...floatingStyles,
|
|
145
|
-
...(contentStyles && { ...contentStyles }),
|
|
146
|
-
}"
|
|
277
|
+
:style="contentStylesComputed"
|
|
147
278
|
:class="$style.ui3nMenuContent"
|
|
148
279
|
v-on="closeOnClick ? { click: onContentClick } : {}"
|
|
149
280
|
>
|
|
@@ -157,7 +288,6 @@
|
|
|
157
288
|
|
|
158
289
|
.ui3nMenu {
|
|
159
290
|
--ui3n-menu-content-bg: var(--color-bg-control-secondary-default);
|
|
160
|
-
--ui3n-menu-content-border-raduis: v-bind(contentBorderRadiusCss);
|
|
161
291
|
|
|
162
292
|
position: relative;
|
|
163
293
|
max-width: max-content;
|
|
@@ -176,9 +306,9 @@
|
|
|
176
306
|
|
|
177
307
|
.ui3nMenuContent {
|
|
178
308
|
position: absolute;
|
|
179
|
-
border-radius: var(--ui3n-menu-content-border-
|
|
309
|
+
border-radius: var(--ui3n-menu-content-border-radius);
|
|
180
310
|
background-color: var(--ui3n-menu-content-bg);
|
|
181
|
-
z-index:
|
|
311
|
+
z-index: var(--ui3n-menu-zIndex, 1000);
|
|
182
312
|
overflow: hidden;
|
|
183
313
|
@include mixins.dropShadow();
|
|
184
314
|
}
|