@teammodlr/ui-kit 2.3.22 → 3.0.0-beta.10
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/lib/components/avatar/Avatar.vue.d.ts +2 -2
- package/lib/components/badge/style/Badge.css.d.ts +2 -2
- package/lib/components/button/Button.vue.d.ts +7 -4
- package/lib/components/button/style/Button.css.d.ts +3 -3
- package/lib/components/cardcontextmenu/CardContextMenu.vue.d.ts +5 -8
- package/lib/components/cardcontextmenu/CardContextMenuItem.vue.d.ts +11 -2
- package/lib/components/cardcontextmenu/CardContextMenuTriggerProvider.vue.d.ts +2 -0
- package/lib/components/cardcontextmenu/style/Dropdown.css.d.ts +2 -1
- package/lib/components/cardcontextmenu/symbol.d.ts +1 -0
- package/lib/components/checkbox/Checkbox.vue.d.ts +15 -4
- package/lib/components/collapse/CollapseItem.vue.d.ts +3 -2
- package/lib/components/colorpicker/ColorPicker.vue.d.ts +8 -9
- package/lib/components/confirm/Confirm.vue.d.ts +9 -0
- package/lib/components/confirm/props.d.ts +4 -0
- package/lib/components/dialog/Dialog.vue.d.ts +13 -5
- package/lib/components/dialogprovider/DialogProvider.vue.d.ts +1 -0
- package/lib/components/dialogprovider/types.d.ts +1 -0
- package/lib/components/dropdown/Dropdown.vue.d.ts +3 -1
- package/lib/components/dropdown/DropdownItem.vue.d.ts +11 -1
- package/lib/components/dropdown/DropdownSeperator.vue.d.ts +1 -0
- package/lib/components/dropdown/DropdownTriggerProvider.vue.d.ts +2 -0
- package/lib/components/dropdown/style/Dropdown.css.d.ts +2 -1
- package/lib/components/dropdown/symbol.d.ts +2 -0
- package/lib/components/image/Image.vue.d.ts +1 -1
- package/lib/components/index.d.ts +1 -0
- package/lib/components/messageprovider/Message.vue.d.ts +1 -1
- package/lib/components/messageprovider/MessageProvider.vue.d.ts +24 -24
- package/lib/components/messageprovider/style/Message.css.d.ts +1 -1
- package/lib/components/messageprovider/style/Toaster.css.d.ts +1 -0
- package/lib/components/messageprovider/types.d.ts +5 -0
- package/lib/components/modal/Modal.vue.d.ts +18 -6
- package/lib/components/popover/Popover.vue.d.ts +113 -0
- package/lib/components/popover/style/Popover.css.d.ts +4 -0
- package/lib/components/popover/style/index.d.ts +1 -0
- package/lib/components/radio/Radio.vue.d.ts +2 -0
- package/lib/components/radiobuttongroup/RadioButtonGroup.vue.d.ts +1 -0
- package/lib/components/select/Select.vue.d.ts +23 -11
- package/lib/components/select/style/Select.css.d.ts +2 -0
- package/lib/components/switch/Switch.vue.d.ts +2 -0
- package/lib/components/tab/Tab.vue.d.ts +5 -2
- package/lib/components/tab/TabGroup.vue.d.ts +8 -3
- package/lib/components/textarea/Textarea.vue.d.ts +2 -1
- package/lib/components/tooltip/Tooltip.vue.d.ts +5 -20
- package/lib/components/tooltip/TooltipTrigger.vue.d.ts +1 -10
- package/lib/components/tree/Tree.vue.d.ts +3 -3
- package/lib/components/tree/TreeNode.vue.d.ts +26 -12
- package/lib/components/tree/types.d.ts +5 -5
- package/lib/index.cjs.js +5 -14
- package/lib/index.es.js +14813 -10079
- package/lib/style.css +1 -1
- package/package.json +12 -6
- package/src/components/avatar/Avatar.vue +32 -24
- package/src/components/button/Button.vue +24 -5
- package/src/components/cardcontextmenu/CardContextMenu.vue +68 -95
- package/src/components/cardcontextmenu/CardContextMenuItem.vue +40 -50
- package/src/components/cardcontextmenu/CardContextMenuSeperator.vue +9 -7
- package/src/components/cardcontextmenu/CardContextMenuTriggerProvider.vue +15 -0
- package/src/components/cardcontextmenu/style/Dropdown.css.ts +155 -2
- package/src/components/cardcontextmenu/symbol.ts +3 -0
- package/src/components/checkbox/Checkbox.vue +37 -12
- package/src/components/checkbox/style/Checkbox.css.ts +6 -6
- package/src/components/collapse/CollapseItem.vue +46 -21
- package/src/components/collapse/style/Collapse.css.ts +5 -1
- package/src/components/colorpicker/ColorPicker.vue +76 -45
- package/src/components/confirm/Confirm.vue +15 -8
- package/src/components/confirm/props.ts +4 -0
- package/src/components/dialog/Dialog.vue +183 -37
- package/src/components/dialog/style/Dialog.css.ts +15 -1
- package/src/components/dialogprovider/types.ts +1 -0
- package/src/components/dropdown/Dropdown.vue +46 -3
- package/src/components/dropdown/DropdownItem.vue +55 -25
- package/src/components/dropdown/DropdownSeperator.vue +19 -11
- package/src/components/dropdown/DropdownTriggerProvider.vue +15 -0
- package/src/components/dropdown/examples/context.vue +2 -2
- package/src/components/dropdown/style/Dropdown.css.ts +173 -4
- package/src/components/dropdown/symbol.ts +4 -0
- package/src/components/form/examples/basic.vue +19 -8
- package/src/components/index.ts +1 -0
- package/src/components/messageprovider/MessageProvider.vue +139 -90
- package/src/components/messageprovider/examples/basic.vue +3 -1
- package/src/components/messageprovider/examples/index.stories.ts +37 -1
- package/src/components/messageprovider/style/Toaster.css.ts +24 -0
- package/src/components/messageprovider/types.ts +10 -0
- package/src/components/modal/Modal.vue +142 -67
- package/src/components/modal/style/Modal.css.ts +26 -2
- package/src/components/popover/Popover.vue +155 -0
- package/src/components/popover/examples/arrow.vue +47 -0
- package/src/components/popover/examples/basic.vue +15 -0
- package/src/components/popover/examples/index.stories.ts +19 -0
- package/src/components/popover/examples/index.ts +4 -0
- package/src/components/popover/examples/placement.vue +59 -0
- package/src/components/popover/examples/variant.vue +30 -0
- package/src/components/popover/style/Popover.css.ts +203 -0
- package/src/components/popover/style/index.ts +1 -0
- package/src/components/radio/Radio.vue +31 -17
- package/src/components/radiobutton/RadioButton.vue +15 -2
- package/src/components/radiobuttongroup/RadioButtonGroup.vue +11 -4
- package/src/components/select/Select.vue +309 -148
- package/src/components/select/examples/basic.vue +1 -1
- package/src/components/select/style/Select.css.ts +15 -0
- package/src/components/slider/Slider.vue +1 -1
- package/src/components/switch/Switch.vue +30 -10
- package/src/components/switch/style/Switch.css.ts +3 -5
- package/src/components/tab/Tab.vue +29 -20
- package/src/components/tab/TabGroup.vue +29 -38
- package/src/components/textarea/Textarea.vue +8 -4
- package/src/components/tooltip/Tooltip.vue +52 -141
- package/src/components/tooltip/TooltipTrigger.vue +14 -17
- package/src/components/tooltip/style/Tooltip.css.ts +148 -29
- package/src/components/tree/Tree.vue +138 -100
- package/src/components/tree/TreeNode.vue +105 -98
- package/src/components/tree/types.ts +4 -4
- package/src/styles/transitions.css.ts +36 -16
|
@@ -1,25 +1,41 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<
|
|
3
|
-
<
|
|
4
|
-
:enter-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
2
|
+
<DialogRoot :open="isOpen" :modal="modal" @update:open="handleOpenChange">
|
|
3
|
+
<DialogPortal to="body">
|
|
4
|
+
<Transition type="animation" :enter-active-class="fade.enterActive" :leave-active-class="fade.leaveActive"
|
|
5
|
+
:enter-from-class="fade.enterFrom" :leave-to-class="fade.leaveTo">
|
|
6
|
+
<DialogOverlay v-if="isOpen && modal" force-mount :class="backdropClasses" style="z-index: 10" />
|
|
7
|
+
</Transition>
|
|
8
|
+
<Transition type="animation" :enter-active-class="modalScale.enterActive" :leave-active-class="modalScale.leaveActive"
|
|
9
|
+
@after-leave="handleAfterLeave"
|
|
10
|
+
:enter-from-class="modalScale.enterFrom" :leave-to-class="modalScale.leaveTo">
|
|
11
|
+
|
|
12
|
+
<DialogContent
|
|
13
|
+
v-if="isOpen"
|
|
14
|
+
ref="dialog"
|
|
15
|
+
force-mount
|
|
16
|
+
:style="computedStyle"
|
|
17
|
+
:disable-outside-pointer-events="modal"
|
|
18
|
+
@open-auto-focus="handleOpenAutoFocus"
|
|
19
|
+
@escape-key-down="handleEscapeKeyDown"
|
|
20
|
+
@pointer-down-outside.prevent
|
|
21
|
+
@focus-outside="handleFocusOutside"
|
|
22
|
+
@interact-outside.prevent
|
|
23
|
+
>
|
|
24
|
+
<FocusScope :trapped="focusTrap" :loop="focusTrap">
|
|
13
25
|
<slot v-if="$slots.container" name="container" :close="close"></slot>
|
|
14
26
|
<template v-else>
|
|
15
27
|
<div :class="style.content">
|
|
16
|
-
<
|
|
28
|
+
<DialogClose as-child>
|
|
29
|
+
<button ref="closeButton" role="button" :class="style.close"></button>
|
|
30
|
+
</DialogClose>
|
|
17
31
|
<div :class="headerClasses" ref="handle">
|
|
18
|
-
<
|
|
19
|
-
<
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
32
|
+
<DialogTitle as-child>
|
|
33
|
+
<div :class="style.title">
|
|
34
|
+
<slot name="title">
|
|
35
|
+
{{ resolvedTitle }}
|
|
36
|
+
</slot>
|
|
37
|
+
</div>
|
|
38
|
+
</DialogTitle>
|
|
23
39
|
</div>
|
|
24
40
|
<div>
|
|
25
41
|
<slot></slot>
|
|
@@ -29,35 +45,53 @@
|
|
|
29
45
|
</div>
|
|
30
46
|
</div>
|
|
31
47
|
</template>
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
</div>
|
|
38
|
-
</Transition>
|
|
39
|
-
</Teleport>
|
|
48
|
+
</FocusScope>
|
|
49
|
+
</DialogContent>
|
|
50
|
+
</Transition>
|
|
51
|
+
</DialogPortal>
|
|
52
|
+
</DialogRoot>
|
|
40
53
|
</template>
|
|
41
54
|
<script lang="ts">
|
|
42
55
|
import { fade, modalScale } from '../../styles/transitions.css';
|
|
56
|
+
import {
|
|
57
|
+
DialogClose,
|
|
58
|
+
DialogContent,
|
|
59
|
+
DialogOverlay,
|
|
60
|
+
DialogPortal,
|
|
61
|
+
DialogRoot,
|
|
62
|
+
DialogTitle,
|
|
63
|
+
FocusScope,
|
|
64
|
+
} from 'reka-ui';
|
|
43
65
|
import {
|
|
44
66
|
computed,
|
|
45
67
|
defineComponent,
|
|
68
|
+
provide,
|
|
46
69
|
ref,
|
|
70
|
+
shallowRef,
|
|
47
71
|
watch,
|
|
48
72
|
nextTick,
|
|
49
73
|
isRef,
|
|
50
74
|
type CSSProperties,
|
|
75
|
+
type ComponentPublicInstance,
|
|
51
76
|
type PropType,
|
|
52
77
|
type Ref,
|
|
53
78
|
} from 'vue';
|
|
54
79
|
import type { ComputedRef } from 'vue';
|
|
55
|
-
import { useZIndex } from '../../composables/useZIndex';
|
|
80
|
+
import { useZIndex, ZINDEX } from '../../composables/useZIndex';
|
|
56
81
|
import { useDraggable, useEventListener } from '@vueuse/core';
|
|
57
82
|
import * as style from './style';
|
|
58
83
|
|
|
59
84
|
export default defineComponent({
|
|
60
85
|
name: 'Dialog',
|
|
86
|
+
components: {
|
|
87
|
+
DialogClose,
|
|
88
|
+
DialogContent,
|
|
89
|
+
DialogOverlay,
|
|
90
|
+
DialogPortal,
|
|
91
|
+
DialogRoot,
|
|
92
|
+
DialogTitle,
|
|
93
|
+
FocusScope,
|
|
94
|
+
},
|
|
61
95
|
props: {
|
|
62
96
|
title: {
|
|
63
97
|
type: [String, Object] as PropType<string | Ref<string>>,
|
|
@@ -92,17 +126,40 @@ export default defineComponent({
|
|
|
92
126
|
},
|
|
93
127
|
},
|
|
94
128
|
// Eventually remove 'open', i lazy cant be assed for now
|
|
95
|
-
emits: ['onOpen', 'onClose', 'update:open', 'open'],
|
|
129
|
+
emits: ['onOpen', 'onClose', 'update:open', 'open', 'close'],
|
|
96
130
|
setup(props, { emit, expose }) {
|
|
97
131
|
const wrapper = ref<HTMLElement | null>(null);
|
|
98
|
-
const dialog =
|
|
132
|
+
const dialog = shallowRef<HTMLElement | ComponentPublicInstance | null>(null);
|
|
99
133
|
const handle = ref<HTMLElement | null>(null);
|
|
134
|
+
const closeButton = ref<HTMLElement | null>(null);
|
|
100
135
|
const { nextZIndex, current: maxZIndex } = useZIndex();
|
|
101
136
|
const zIndex = ref(props.zIndex ?? nextZIndex());
|
|
137
|
+
provide(ZINDEX, zIndex);
|
|
138
|
+
const hasOpened = ref(false);
|
|
102
139
|
|
|
103
140
|
const isDragging = ref<boolean>(false);
|
|
104
141
|
|
|
105
|
-
|
|
142
|
+
function resolveElement(
|
|
143
|
+
value: HTMLElement | ComponentPublicInstance | null | undefined
|
|
144
|
+
): HTMLElement | null {
|
|
145
|
+
if (!value) {
|
|
146
|
+
return null;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
if (value instanceof HTMLElement) {
|
|
150
|
+
return value;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
if ('$el' in value && value.$el instanceof HTMLElement) {
|
|
154
|
+
return value.$el;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
return null;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
const dialogElement = computed(() => resolveElement(dialog.value));
|
|
161
|
+
|
|
162
|
+
const { x, y } = useDraggable(dialogElement, {
|
|
106
163
|
handle,
|
|
107
164
|
preventDefault: true,
|
|
108
165
|
containerElement: wrapper,
|
|
@@ -115,6 +172,17 @@ export default defineComponent({
|
|
|
115
172
|
isRef(props.title) ? props.title.value : props.title
|
|
116
173
|
);
|
|
117
174
|
|
|
175
|
+
const focusableSelector = [
|
|
176
|
+
'[autofocus]',
|
|
177
|
+
'input:not([type="hidden"])',
|
|
178
|
+
'textarea',
|
|
179
|
+
'select',
|
|
180
|
+
'[contenteditable="true"]',
|
|
181
|
+
'button',
|
|
182
|
+
'a[href]',
|
|
183
|
+
'[tabindex]:not([tabindex="-1"])'
|
|
184
|
+
].join(', ');
|
|
185
|
+
|
|
118
186
|
const backdropClasses = computed(() => ({
|
|
119
187
|
[style.backdrop]: true,
|
|
120
188
|
[style.backdropColor]: props.modal === true,
|
|
@@ -131,6 +199,13 @@ export default defineComponent({
|
|
|
131
199
|
[style.notModal]: !props.modal,
|
|
132
200
|
}));
|
|
133
201
|
|
|
202
|
+
const wrapperStyle: ComputedRef<CSSProperties> = computed(() => ({
|
|
203
|
+
zIndex: zIndex.value,
|
|
204
|
+
position: 'fixed',
|
|
205
|
+
inset: '0',
|
|
206
|
+
pointerEvents: props.modal ? 'all' : 'none'
|
|
207
|
+
}));
|
|
208
|
+
|
|
134
209
|
const width = computed(() =>
|
|
135
210
|
typeof props.width === 'number' ? `${props.width}px` : props.width
|
|
136
211
|
);
|
|
@@ -144,8 +219,17 @@ export default defineComponent({
|
|
|
144
219
|
}));
|
|
145
220
|
|
|
146
221
|
function close() {
|
|
147
|
-
|
|
148
|
-
|
|
222
|
+
handleOpenChange(false);
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
function handleEscapeKeyDown(event: KeyboardEvent) {
|
|
226
|
+
event.preventDefault();
|
|
227
|
+
event.stopPropagation();
|
|
228
|
+
close();
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
function handleAfterLeave() {
|
|
232
|
+
emit('close');
|
|
149
233
|
}
|
|
150
234
|
|
|
151
235
|
function onCloseEmit() {
|
|
@@ -157,9 +241,54 @@ export default defineComponent({
|
|
|
157
241
|
|
|
158
242
|
function onEnter() {
|
|
159
243
|
emit('onOpen');
|
|
244
|
+
emit('open');
|
|
160
245
|
zIndex.value = nextZIndex();
|
|
161
246
|
}
|
|
162
247
|
|
|
248
|
+
function handleFocusOutside(event: Event) {
|
|
249
|
+
if (!props.focusTrap) {
|
|
250
|
+
return;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
event.preventDefault();
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
function handleOpenAutoFocus(event: Event) {
|
|
257
|
+
const element = dialogElement.value;
|
|
258
|
+
|
|
259
|
+
if (!element) {
|
|
260
|
+
return;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
const firstFocusableElement = Array.from(
|
|
264
|
+
element.querySelectorAll<HTMLElement>(focusableSelector)
|
|
265
|
+
).find(candidate => {
|
|
266
|
+
return candidate !== closeButton.value
|
|
267
|
+
&& !candidate.matches(':disabled')
|
|
268
|
+
&& candidate.getAttribute('aria-disabled') !== 'true'
|
|
269
|
+
&& candidate.getAttribute('aria-hidden') !== 'true'
|
|
270
|
+
&& candidate.getClientRects().length > 0;
|
|
271
|
+
});
|
|
272
|
+
|
|
273
|
+
if (!firstFocusableElement) {
|
|
274
|
+
return;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
event.preventDefault();
|
|
278
|
+
firstFocusableElement.focus();
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
function handleOpenChange(value: boolean) {
|
|
282
|
+
if (!value && props.open) {
|
|
283
|
+
emit('update:open', false);
|
|
284
|
+
onCloseEmit();
|
|
285
|
+
hasOpened.value = false;
|
|
286
|
+
return;
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
emit('update:open', value);
|
|
290
|
+
}
|
|
291
|
+
|
|
163
292
|
function onDragStart() {
|
|
164
293
|
if (zIndex.value < maxZIndex.value) {
|
|
165
294
|
zIndex.value = nextZIndex();
|
|
@@ -172,16 +301,20 @@ export default defineComponent({
|
|
|
172
301
|
}
|
|
173
302
|
|
|
174
303
|
function centerDialog() {
|
|
175
|
-
|
|
176
|
-
|
|
304
|
+
const element = dialogElement.value;
|
|
305
|
+
|
|
306
|
+
if (element) {
|
|
307
|
+
const boundingRect = element.getBoundingClientRect();
|
|
177
308
|
x.value = (window.innerWidth - boundingRect.width) / 2;
|
|
178
309
|
y.value = (window.innerHeight - boundingRect.height) / 2;
|
|
179
310
|
}
|
|
180
311
|
}
|
|
181
312
|
|
|
182
313
|
function keepWithInBounds() {
|
|
183
|
-
|
|
184
|
-
|
|
314
|
+
const element = dialogElement.value;
|
|
315
|
+
|
|
316
|
+
if (element) {
|
|
317
|
+
const boundingRect = element.getBoundingClientRect();
|
|
185
318
|
const maxX = window.innerWidth - boundingRect.width;
|
|
186
319
|
const maxY = window.innerHeight - boundingRect.height;
|
|
187
320
|
|
|
@@ -206,7 +339,13 @@ export default defineComponent({
|
|
|
206
339
|
if (newVal) {
|
|
207
340
|
await nextTick();
|
|
208
341
|
centerDialog();
|
|
209
|
-
|
|
342
|
+
zIndex.value = props.zIndex ?? nextZIndex();
|
|
343
|
+
if (!hasOpened.value) {
|
|
344
|
+
onEnter();
|
|
345
|
+
hasOpened.value = true;
|
|
346
|
+
}
|
|
347
|
+
} else {
|
|
348
|
+
hasOpened.value = false;
|
|
210
349
|
}
|
|
211
350
|
},
|
|
212
351
|
{ immediate: true }
|
|
@@ -220,6 +359,7 @@ export default defineComponent({
|
|
|
220
359
|
wrapper,
|
|
221
360
|
dialog,
|
|
222
361
|
handle,
|
|
362
|
+
closeButton,
|
|
223
363
|
zIndex,
|
|
224
364
|
fade,
|
|
225
365
|
modalScale,
|
|
@@ -229,9 +369,15 @@ export default defineComponent({
|
|
|
229
369
|
backdropClasses,
|
|
230
370
|
resolvedTitle,
|
|
231
371
|
wrapperClasses,
|
|
372
|
+
wrapperStyle,
|
|
232
373
|
close,
|
|
374
|
+
handleAfterLeave,
|
|
375
|
+
handleEscapeKeyDown,
|
|
233
376
|
onEnter,
|
|
234
|
-
|
|
377
|
+
handleOpenChange,
|
|
378
|
+
handleOpenAutoFocus,
|
|
379
|
+
handleFocusOutside,
|
|
380
|
+
isOpen: computed(() => props.open)
|
|
235
381
|
};
|
|
236
382
|
},
|
|
237
383
|
});
|
|
@@ -60,7 +60,16 @@ export const content = style({
|
|
|
60
60
|
border: `1px solid ${themeVars.color.gray[300]}`,
|
|
61
61
|
borderRadius: themeVars.rounded.medium,
|
|
62
62
|
pointerEvents: 'auto',
|
|
63
|
-
boxShadow: `2px 2px 8px rgba(128, 128, 128, 0.1), 0 9px 28px 8px rgba(0,0,0,.05)
|
|
63
|
+
boxShadow: `2px 2px 8px rgba(128, 128, 128, 0.1), 0 9px 28px 8px rgba(0,0,0,.05)`,
|
|
64
|
+
selectors: {
|
|
65
|
+
'&:focus': {
|
|
66
|
+
outline: 'none'
|
|
67
|
+
},
|
|
68
|
+
'&:focus-visible': {
|
|
69
|
+
outline: 'none',
|
|
70
|
+
boxShadow: `2px 2px 8px rgba(128, 128, 128, 0.1), 0 9px 28px 8px rgba(0,0,0,.05)`
|
|
71
|
+
}
|
|
72
|
+
}
|
|
64
73
|
});
|
|
65
74
|
|
|
66
75
|
export const header = style({
|
|
@@ -119,6 +128,11 @@ export const close = style([
|
|
|
119
128
|
':focus': {
|
|
120
129
|
backgroundColor: themeVars.color.gray[150]
|
|
121
130
|
},
|
|
131
|
+
':focus-visible': {
|
|
132
|
+
outline: 'none',
|
|
133
|
+
boxShadow: 'none',
|
|
134
|
+
backgroundColor: themeVars.color.gray[150]
|
|
135
|
+
},
|
|
122
136
|
'::before': {
|
|
123
137
|
content: '""',
|
|
124
138
|
position: 'absolute',
|
|
@@ -1,5 +1,15 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<
|
|
2
|
+
<ContextMenuSub v-model:open="isOpen" v-if="isContextMenu && isSubmenu">
|
|
3
|
+
<DropdownTriggerProvider>
|
|
4
|
+
<slot />
|
|
5
|
+
</DropdownTriggerProvider>
|
|
6
|
+
<ContextMenuPortal>
|
|
7
|
+
<ContextMenuSubContent :class="menuClasses" align="start" side="right" :side-offset="2" :align-offset="-6">
|
|
8
|
+
<slot name="menu" />
|
|
9
|
+
</ContextMenuSubContent>
|
|
10
|
+
</ContextMenuPortal>
|
|
11
|
+
</ContextMenuSub>
|
|
12
|
+
<ContextMenuRoot v-else-if="isContextMenu" v-model:open="isOpen" v-bind="$attrs">
|
|
3
13
|
<ContextMenuTrigger as-child>
|
|
4
14
|
<slot />
|
|
5
15
|
</ContextMenuTrigger>
|
|
@@ -9,7 +19,17 @@
|
|
|
9
19
|
</ContextMenuContent>
|
|
10
20
|
</ContextMenuPortal>
|
|
11
21
|
</ContextMenuRoot>
|
|
12
|
-
<
|
|
22
|
+
<DropdownMenuSub v-else-if="isSubmenu" v-model:open="isOpen">
|
|
23
|
+
<DropdownTriggerProvider>
|
|
24
|
+
<slot />
|
|
25
|
+
</DropdownTriggerProvider>
|
|
26
|
+
<DropdownMenuPortal>
|
|
27
|
+
<DropdownMenuSubContent :class="menuClasses" align="start" side="right" :side-offset="2" :align-offset="-6">
|
|
28
|
+
<slot name="menu" />
|
|
29
|
+
</DropdownMenuSubContent>
|
|
30
|
+
</DropdownMenuPortal>
|
|
31
|
+
</DropdownMenuSub>
|
|
32
|
+
<DropdownMenuRoot v-else v-model:open="isOpen" :modal="modal" v-bind="$attrs">
|
|
13
33
|
<DropdownMenuTrigger as-child>
|
|
14
34
|
<slot />
|
|
15
35
|
</DropdownMenuTrigger>
|
|
@@ -53,10 +73,12 @@ import {
|
|
|
53
73
|
ContextMenuSubContent,
|
|
54
74
|
ContextMenuSubTrigger,
|
|
55
75
|
ContextMenuTrigger,
|
|
56
|
-
} from '
|
|
76
|
+
} from 'reka-ui';
|
|
57
77
|
import {
|
|
58
78
|
computed,
|
|
59
79
|
defineComponent,
|
|
80
|
+
inject,
|
|
81
|
+
provide,
|
|
60
82
|
ref,
|
|
61
83
|
watch,
|
|
62
84
|
type PropType
|
|
@@ -65,6 +87,7 @@ import { useZIndex } from '../../composables/useZIndex';
|
|
|
65
87
|
import { scale } from '../../styles/transitions.css';
|
|
66
88
|
import * as style from './style';
|
|
67
89
|
import { DROPDOWN_STATE } from './symbol';
|
|
90
|
+
import DropdownTriggerProvider from './DropdownTriggerProvider.vue';
|
|
68
91
|
|
|
69
92
|
export default defineComponent({
|
|
70
93
|
name: 'Dropdown',
|
|
@@ -99,6 +122,7 @@ export default defineComponent({
|
|
|
99
122
|
ContextMenuSubContent,
|
|
100
123
|
ContextMenuSubTrigger,
|
|
101
124
|
ContextMenuTrigger,
|
|
125
|
+
DropdownTriggerProvider,
|
|
102
126
|
},
|
|
103
127
|
props: {
|
|
104
128
|
open: {
|
|
@@ -151,6 +175,9 @@ export default defineComponent({
|
|
|
151
175
|
},
|
|
152
176
|
emits: ['update:open'],
|
|
153
177
|
setup(props, { emit }) {
|
|
178
|
+
const parentState = inject(DROPDOWN_STATE, null);
|
|
179
|
+
const isSubmenu = computed(() => !!parentState);
|
|
180
|
+
const isContextMenu = computed(() => props.contextMenu || parentState?.contextMenu || false);
|
|
154
181
|
const { nextZIndex } = useZIndex();
|
|
155
182
|
const isOpen = ref(props.open);
|
|
156
183
|
const placement = ref(props.placement);
|
|
@@ -158,6 +185,16 @@ export default defineComponent({
|
|
|
158
185
|
const referenceRef = ref<HTMLElement>();
|
|
159
186
|
const zIndex = ref(props.zIndex ?? nextZIndex());
|
|
160
187
|
|
|
188
|
+
provide(DROPDOWN_STATE, {
|
|
189
|
+
contextMenu: isContextMenu.value,
|
|
190
|
+
isOpen: isOpen.value,
|
|
191
|
+
handleSelect(labels: string[]) {
|
|
192
|
+
isOpen.value = false;
|
|
193
|
+
props.onSelect(labels);
|
|
194
|
+
parentState?.handleSelect(labels);
|
|
195
|
+
}
|
|
196
|
+
});
|
|
197
|
+
|
|
161
198
|
watch(
|
|
162
199
|
() => props.open,
|
|
163
200
|
value => {
|
|
@@ -165,6 +202,10 @@ export default defineComponent({
|
|
|
165
202
|
}
|
|
166
203
|
);
|
|
167
204
|
|
|
205
|
+
watch(isOpen, value => {
|
|
206
|
+
emit('update:open', value);
|
|
207
|
+
});
|
|
208
|
+
|
|
168
209
|
function toggleDropdown() {
|
|
169
210
|
isOpen.value = !isOpen.value;
|
|
170
211
|
}
|
|
@@ -199,6 +240,8 @@ export default defineComponent({
|
|
|
199
240
|
computedReferenceClasses,
|
|
200
241
|
transitionName,
|
|
201
242
|
toggleDropdown,
|
|
243
|
+
isContextMenu,
|
|
244
|
+
isSubmenu,
|
|
202
245
|
zIndex,
|
|
203
246
|
scale
|
|
204
247
|
};
|
|
@@ -1,54 +1,50 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<
|
|
3
|
-
|
|
4
|
-
<slot name="icon-left" />
|
|
5
|
-
</div>
|
|
6
|
-
<div :class="style.menuItemTextContainer">
|
|
7
|
-
<span :class="style.menuItemText"><slot /></span>
|
|
8
|
-
</div>
|
|
9
|
-
<slot name="keyboard" />
|
|
10
|
-
</DropdownMenuItem>
|
|
11
|
-
<!-- <component
|
|
12
|
-
:is="computedType"
|
|
13
|
-
:href="href"
|
|
2
|
+
<component
|
|
3
|
+
:is="itemComponent"
|
|
14
4
|
ref="wrapper"
|
|
15
5
|
:class="computedWrapperClasses"
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
@
|
|
19
|
-
|
|
6
|
+
:disabled="disabled"
|
|
7
|
+
v-bind="componentProps"
|
|
8
|
+
@select="!isSubmenuTrigger ? handleSelect : undefined"
|
|
9
|
+
>
|
|
20
10
|
<div v-if="hasIconLeft" :class="style.menuItemIcon">
|
|
21
11
|
<slot name="icon-left" />
|
|
22
12
|
</div>
|
|
23
13
|
<div :class="style.menuItemTextContainer">
|
|
24
14
|
<span :class="style.menuItemText"><slot /></span>
|
|
25
15
|
</div>
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
<kbd>{{ key }}</kbd>
|
|
29
|
-
</template>
|
|
30
|
-
</div> -->
|
|
31
|
-
<!-- <div v-if="hasIconRight" :class="style.menuItemIcon">
|
|
16
|
+
<slot name="keyboard" />
|
|
17
|
+
<div v-if="hasIconRight" :class="style.menuItemIconRight">
|
|
32
18
|
<slot name="icon-right" />
|
|
33
19
|
</div>
|
|
34
|
-
</component>
|
|
20
|
+
</component>
|
|
35
21
|
</template>
|
|
36
22
|
|
|
37
23
|
<script lang="ts">
|
|
38
24
|
import {
|
|
39
25
|
computed,
|
|
40
26
|
defineComponent,
|
|
27
|
+
inject,
|
|
41
28
|
type PropType,
|
|
42
29
|
ref
|
|
43
30
|
} from 'vue';
|
|
44
|
-
import {
|
|
31
|
+
import {
|
|
32
|
+
ContextMenuItem,
|
|
33
|
+
ContextMenuSubTrigger,
|
|
34
|
+
DropdownMenuItem,
|
|
35
|
+
DropdownMenuSubTrigger
|
|
36
|
+
} from 'reka-ui';
|
|
45
37
|
import * as style from './style';
|
|
46
38
|
import type { Variant } from '../../types/props';
|
|
39
|
+
import { DROPDOWN_STATE, DROPDOWN_TRIGGER_STATE } from './symbol';
|
|
47
40
|
|
|
48
41
|
export default defineComponent({
|
|
49
42
|
name: 'DropdownItem',
|
|
50
43
|
components: {
|
|
44
|
+
ContextMenuItem,
|
|
45
|
+
ContextMenuSubTrigger,
|
|
51
46
|
DropdownMenuItem
|
|
47
|
+
,DropdownMenuSubTrigger
|
|
52
48
|
},
|
|
53
49
|
props: {
|
|
54
50
|
label: {
|
|
@@ -86,15 +82,25 @@ export default defineComponent({
|
|
|
86
82
|
},
|
|
87
83
|
emits: [],
|
|
88
84
|
setup(props, { slots }) {
|
|
85
|
+
const dropdownState = inject(DROPDOWN_STATE, null);
|
|
86
|
+
const isSubmenuTrigger = inject(DROPDOWN_TRIGGER_STATE, false);
|
|
89
87
|
|
|
90
88
|
const hasIconLeft = computed(() => !!slots['icon-left']);
|
|
91
89
|
const hasIconRight = computed(() => !!slots['icon-right']);
|
|
92
90
|
|
|
93
|
-
const wrapper = ref(null);
|
|
91
|
+
const wrapper = ref<HTMLElement | null>(null);
|
|
94
92
|
const computedWrapperClasses = computed(() => {
|
|
95
93
|
return [style.menuItem, style.variant[props.variant]];
|
|
96
94
|
});
|
|
97
95
|
|
|
96
|
+
const itemComponent = computed(() => {
|
|
97
|
+
if (isSubmenuTrigger) {
|
|
98
|
+
return dropdownState?.contextMenu ? ContextMenuSubTrigger : DropdownMenuSubTrigger;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
return dropdownState?.contextMenu ? ContextMenuItem : DropdownMenuItem;
|
|
102
|
+
});
|
|
103
|
+
|
|
98
104
|
const computedType = computed(() => {
|
|
99
105
|
if (props.href) {
|
|
100
106
|
return 'a';
|
|
@@ -103,11 +109,35 @@ export default defineComponent({
|
|
|
103
109
|
return 'div';
|
|
104
110
|
});
|
|
105
111
|
|
|
112
|
+
const componentProps = computed(() => {
|
|
113
|
+
if (isSubmenuTrigger) {
|
|
114
|
+
return {};
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
return {
|
|
118
|
+
as: computedType.value,
|
|
119
|
+
href: props.href
|
|
120
|
+
};
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
function handleSelect() {
|
|
124
|
+
if (props.disabled || isSubmenuTrigger) {
|
|
125
|
+
return;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
dropdownState?.handleSelect([String(props.label ?? wrapper.value?.textContent?.trim() ?? '')]);
|
|
129
|
+
props.onSelect(props.label ?? wrapper.value?.textContent?.trim() ?? '');
|
|
130
|
+
}
|
|
131
|
+
|
|
106
132
|
return {
|
|
107
133
|
wrapper,
|
|
134
|
+
itemComponent,
|
|
135
|
+
isSubmenuTrigger,
|
|
108
136
|
hasIconLeft,
|
|
109
137
|
hasIconRight,
|
|
110
138
|
computedWrapperClasses,
|
|
139
|
+
componentProps,
|
|
140
|
+
handleSelect,
|
|
111
141
|
style,
|
|
112
142
|
computedType
|
|
113
143
|
};
|
|
@@ -1,26 +1,34 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<
|
|
2
|
+
<component :is="separatorComponent" :class="computedClasses" />
|
|
3
3
|
</template>
|
|
4
4
|
|
|
5
5
|
<script lang="ts">
|
|
6
|
-
import {computed, defineComponent} from
|
|
7
|
-
import
|
|
8
|
-
import
|
|
6
|
+
import { computed, defineComponent, inject } from 'vue';
|
|
7
|
+
import { ContextMenuSeparator, DropdownMenuSeparator } from 'reka-ui';
|
|
8
|
+
import * as style from './style';
|
|
9
|
+
import { DROPDOWN_STATE } from './symbol';
|
|
10
|
+
|
|
9
11
|
export default defineComponent({
|
|
10
|
-
name:
|
|
12
|
+
name: 'DropdownSeperator',
|
|
11
13
|
components: {
|
|
14
|
+
ContextMenuSeparator,
|
|
12
15
|
DropdownMenuSeparator
|
|
13
16
|
},
|
|
14
17
|
setup() {
|
|
18
|
+
const dropdownState = inject(DROPDOWN_STATE, null);
|
|
19
|
+
|
|
15
20
|
const computedClasses = computed(() => {
|
|
16
|
-
return [
|
|
17
|
-
|
|
18
|
-
|
|
21
|
+
return [style.menuSeperator];
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
const separatorComponent = computed(() => {
|
|
25
|
+
return dropdownState?.contextMenu ? ContextMenuSeparator : DropdownMenuSeparator;
|
|
19
26
|
});
|
|
20
27
|
|
|
21
28
|
return {
|
|
22
|
-
computedClasses
|
|
23
|
-
|
|
29
|
+
computedClasses,
|
|
30
|
+
separatorComponent
|
|
31
|
+
};
|
|
24
32
|
}
|
|
25
|
-
})
|
|
33
|
+
});
|
|
26
34
|
</script>
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<slot />
|
|
3
|
+
</template>
|
|
4
|
+
|
|
5
|
+
<script lang="ts">
|
|
6
|
+
import { defineComponent, provide } from 'vue';
|
|
7
|
+
import { DROPDOWN_TRIGGER_STATE } from './symbol';
|
|
8
|
+
|
|
9
|
+
export default defineComponent({
|
|
10
|
+
name: 'DropdownTriggerProvider',
|
|
11
|
+
setup() {
|
|
12
|
+
provide(DROPDOWN_TRIGGER_STATE, true);
|
|
13
|
+
}
|
|
14
|
+
});
|
|
15
|
+
</script>
|