@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
|
@@ -17,6 +17,12 @@ export type MessagePlacement =
|
|
|
17
17
|
| 'bottom-left'
|
|
18
18
|
| 'bottom-right';
|
|
19
19
|
|
|
20
|
+
export type MessageCloseButtonPosition =
|
|
21
|
+
| 'top-left'
|
|
22
|
+
| 'top-right'
|
|
23
|
+
| 'bottom-left'
|
|
24
|
+
| 'bottom-right';
|
|
25
|
+
|
|
20
26
|
export type MessageRenderMessage = (props: any) => VNodeChild;
|
|
21
27
|
|
|
22
28
|
export interface MessageOptions {
|
|
@@ -24,6 +30,8 @@ export interface MessageOptions {
|
|
|
24
30
|
render?: MessageRenderMessage;
|
|
25
31
|
timeout?: number;
|
|
26
32
|
closable?: boolean;
|
|
33
|
+
closeButton?: boolean;
|
|
34
|
+
richColors?: boolean;
|
|
27
35
|
onClose?: () => void;
|
|
28
36
|
}
|
|
29
37
|
|
|
@@ -31,6 +39,8 @@ export interface MessageReactive {
|
|
|
31
39
|
content: ContentType;
|
|
32
40
|
timeout?: number;
|
|
33
41
|
closable?: boolean;
|
|
42
|
+
closeButton?: boolean;
|
|
43
|
+
richColors?: boolean;
|
|
34
44
|
type: MessageType;
|
|
35
45
|
onClose?: () => void;
|
|
36
46
|
destroy: () => void;
|
|
@@ -1,66 +1,98 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<
|
|
3
|
-
<
|
|
4
|
-
:enter-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
<div :class="
|
|
25
|
-
<
|
|
26
|
-
<
|
|
27
|
-
|
|
2
|
+
<DialogRoot :open="isOpen" modal @update:open="handleOpenChange">
|
|
3
|
+
<DialogPortal to="body">
|
|
4
|
+
<Transition appear 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" force-mount :class="style.mask" :style="{ zIndex }" />
|
|
7
|
+
</Transition>
|
|
8
|
+
<Transition appear type="animation" :enter-active-class="modalScale.enterActive" :leave-active-class="modalScale.leaveActive"
|
|
9
|
+
:enter-from-class="modalScale.enterFrom" :leave-to-class="modalScale.leaveTo">
|
|
10
|
+
<div tabIndex="-1" v-if="isOpen" ref="wrapperRef"
|
|
11
|
+
:class="computedWrapperClasses" :style="computedWrapperStyles">
|
|
12
|
+
<DialogContent
|
|
13
|
+
force-mount
|
|
14
|
+
:class="computedModalClasses"
|
|
15
|
+
v-bind="$attrs"
|
|
16
|
+
:style="contentStyleRef"
|
|
17
|
+
:disable-outside-pointer-events="true"
|
|
18
|
+
@open-auto-focus="handleOpenAutoFocus"
|
|
19
|
+
@escape-key-down="onEscapeKeyDown"
|
|
20
|
+
@pointer-down-outside="onPointerDownOutside"
|
|
21
|
+
@interact-outside="onInteractOutside"
|
|
22
|
+
>
|
|
23
|
+
<FocusScope :trapped="focusTrap" :loop="focusTrap">
|
|
24
|
+
<div :class="computedContainerClasses" ref="target">
|
|
25
|
+
<template v-if="loading">
|
|
26
|
+
<slot name="loading"> Loading...</slot>
|
|
27
|
+
</template>
|
|
28
|
+
<template v-else>
|
|
29
|
+
<div :class="style.sidebar" v-if="hasSidebar">
|
|
30
|
+
<slot name="sidebar"></slot>
|
|
31
|
+
</div>
|
|
32
|
+
<div :class="style.content">
|
|
33
|
+
<div v-if="title || showClose" :class="style.header">
|
|
34
|
+
<DialogTitle as-child v-if="title">
|
|
35
|
+
<div :class="style.title">
|
|
36
|
+
<div>{{ title }}</div>
|
|
37
|
+
</div>
|
|
38
|
+
</DialogTitle>
|
|
39
|
+
</div>
|
|
40
|
+
<DialogClose as-child v-if="showClose">
|
|
41
|
+
<button :class="style.close"></button>
|
|
42
|
+
</DialogClose>
|
|
43
|
+
<div :class="style.body" ref="bodyRef">
|
|
44
|
+
<slot name="body"></slot>
|
|
45
|
+
<slot></slot>
|
|
46
|
+
</div>
|
|
47
|
+
<div :class="style.footer" v-if="hasFooter">
|
|
48
|
+
<slot name="footer"></slot>
|
|
49
|
+
</div>
|
|
28
50
|
</div>
|
|
29
|
-
</
|
|
30
|
-
<button v-if="showClose" :class="style.close" @click="close"></button>
|
|
31
|
-
<div :class="style.body">
|
|
32
|
-
<slot name="body"></slot>
|
|
33
|
-
<slot></slot>
|
|
34
|
-
</div>
|
|
35
|
-
<div :class="style.footer" v-if="hasFooter">
|
|
36
|
-
<slot name="footer"></slot>
|
|
37
|
-
</div>
|
|
51
|
+
</template>
|
|
38
52
|
</div>
|
|
39
|
-
</
|
|
40
|
-
</
|
|
53
|
+
</FocusScope>
|
|
54
|
+
</DialogContent>
|
|
41
55
|
</div>
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
</Teleport>
|
|
56
|
+
</Transition>
|
|
57
|
+
</DialogPortal>
|
|
58
|
+
</DialogRoot>
|
|
46
59
|
</template>
|
|
47
60
|
|
|
48
61
|
<script lang="ts">
|
|
49
62
|
import type { CSSProperties, PropType } from 'vue';
|
|
63
|
+
import {
|
|
64
|
+
DialogClose,
|
|
65
|
+
DialogContent,
|
|
66
|
+
DialogOverlay,
|
|
67
|
+
DialogPortal,
|
|
68
|
+
DialogRoot,
|
|
69
|
+
DialogTitle,
|
|
70
|
+
FocusScope,
|
|
71
|
+
} from 'reka-ui';
|
|
50
72
|
import {
|
|
51
73
|
computed,
|
|
52
74
|
defineComponent,
|
|
53
75
|
nextTick,
|
|
76
|
+
provide,
|
|
54
77
|
ref,
|
|
55
78
|
shallowRef,
|
|
56
79
|
watch
|
|
57
80
|
} from 'vue';
|
|
58
81
|
import * as style from './style';
|
|
59
82
|
import { fade, modalScale } from '../../styles/transitions.css';
|
|
60
|
-
import { useZIndex } from '../../composables/useZIndex';
|
|
83
|
+
import { useZIndex, ZINDEX } from '../../composables/useZIndex';
|
|
61
84
|
|
|
62
85
|
export default defineComponent({
|
|
63
86
|
name: 'MModal',
|
|
87
|
+
components: {
|
|
88
|
+
DialogClose,
|
|
89
|
+
DialogContent,
|
|
90
|
+
DialogOverlay,
|
|
91
|
+
DialogPortal,
|
|
92
|
+
DialogRoot,
|
|
93
|
+
DialogTitle,
|
|
94
|
+
FocusScope,
|
|
95
|
+
},
|
|
64
96
|
props: {
|
|
65
97
|
open: {
|
|
66
98
|
type: Boolean,
|
|
@@ -106,15 +138,21 @@ export default defineComponent({
|
|
|
106
138
|
centered: {
|
|
107
139
|
type: Boolean,
|
|
108
140
|
default: true
|
|
141
|
+
},
|
|
142
|
+
focusTrap: {
|
|
143
|
+
type: Boolean,
|
|
144
|
+
default: true
|
|
109
145
|
}
|
|
110
146
|
},
|
|
111
|
-
emits: ['open', 'update:open'],
|
|
147
|
+
emits: ['open', 'close', 'update:open'],
|
|
112
148
|
setup(props, { emit, slots }) {
|
|
113
149
|
const { nextZIndex } = useZIndex();
|
|
114
150
|
|
|
115
151
|
const target = ref();
|
|
152
|
+
const bodyRef = ref<HTMLElement | null>(null);
|
|
116
153
|
const wrapperRef = shallowRef<HTMLDivElement>();
|
|
117
154
|
const zIndex = ref(props.zIndex ?? nextZIndex());
|
|
155
|
+
provide(ZINDEX, zIndex);
|
|
118
156
|
const hasSidebar = computed(() => !!slots.sidebar);
|
|
119
157
|
const hasFooter = computed(() => !!slots.footer);
|
|
120
158
|
const transformOrigin = ref<string>();
|
|
@@ -194,43 +232,78 @@ export default defineComponent({
|
|
|
194
232
|
};
|
|
195
233
|
});
|
|
196
234
|
|
|
197
|
-
const
|
|
198
|
-
|
|
235
|
+
const canCloseOutside = computed(() => props.canClickOutside || props.outsideClose);
|
|
236
|
+
|
|
237
|
+
const focusableBodySelector = [
|
|
238
|
+
'[autofocus]',
|
|
239
|
+
'input:not([type="hidden"])',
|
|
240
|
+
'textarea',
|
|
241
|
+
'select',
|
|
242
|
+
'[contenteditable="true"]',
|
|
243
|
+
'button',
|
|
244
|
+
'a[href]',
|
|
245
|
+
'[tabindex]:not([tabindex="-1"])'
|
|
246
|
+
].join(', ');
|
|
247
|
+
|
|
248
|
+
function getFirstFocusableBodyElement() {
|
|
249
|
+
if (!bodyRef.value) {
|
|
250
|
+
return null;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
return Array.from(
|
|
254
|
+
bodyRef.value.querySelectorAll<HTMLElement>(focusableBodySelector)
|
|
255
|
+
).find(element => {
|
|
256
|
+
return !element.matches(':disabled')
|
|
257
|
+
&& element.getAttribute('aria-disabled') !== 'true'
|
|
258
|
+
&& element.getAttribute('aria-hidden') !== 'true'
|
|
259
|
+
&& element.getClientRects().length > 0;
|
|
260
|
+
}) ?? null;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
const handleOpenAutoFocus = (event: Event) => {
|
|
264
|
+
const firstFocusableBodyElement = getFirstFocusableBodyElement();
|
|
265
|
+
|
|
266
|
+
if (!firstFocusableBodyElement) {
|
|
267
|
+
return;
|
|
268
|
+
}
|
|
199
269
|
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
contentClickRef.value = true;
|
|
270
|
+
event.preventDefault();
|
|
271
|
+
firstFocusableBodyElement.focus();
|
|
203
272
|
};
|
|
204
273
|
|
|
205
|
-
const
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
}, 0);
|
|
274
|
+
const onEscapeKeyDown = (e: KeyboardEvent) => {
|
|
275
|
+
e.stopPropagation();
|
|
276
|
+
close(e);
|
|
209
277
|
};
|
|
210
278
|
|
|
211
|
-
const
|
|
212
|
-
if (!
|
|
213
|
-
|
|
279
|
+
const onPointerDownOutside = (e: Event) => {
|
|
280
|
+
if (!canCloseOutside.value) {
|
|
281
|
+
e.preventDefault();
|
|
214
282
|
}
|
|
283
|
+
};
|
|
215
284
|
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
285
|
+
const onInteractOutside = (e: Event) => {
|
|
286
|
+
if (!canCloseOutside.value) {
|
|
287
|
+
e.preventDefault();
|
|
288
|
+
return;
|
|
220
289
|
}
|
|
290
|
+
|
|
291
|
+
close(e as MouseEvent | KeyboardEvent);
|
|
221
292
|
};
|
|
222
293
|
|
|
223
|
-
const
|
|
224
|
-
if (
|
|
225
|
-
|
|
226
|
-
close(e);
|
|
294
|
+
const handleOpenChange = (value: boolean) => {
|
|
295
|
+
if (!value && isOpen.value) {
|
|
296
|
+
close(undefined);
|
|
227
297
|
return;
|
|
228
298
|
}
|
|
299
|
+
|
|
300
|
+
isOpen.value = value;
|
|
229
301
|
};
|
|
230
302
|
|
|
231
303
|
const close = (e: MouseEvent | KeyboardEvent | undefined) => {
|
|
232
304
|
props.onClose?.(e);
|
|
233
305
|
ownerScrollbarClose();
|
|
306
|
+
emit('close', e);
|
|
234
307
|
isOpen.value = false;
|
|
235
308
|
};
|
|
236
309
|
|
|
@@ -283,12 +356,14 @@ export default defineComponent({
|
|
|
283
356
|
|
|
284
357
|
return {
|
|
285
358
|
close,
|
|
286
|
-
onOutsideClick,
|
|
287
359
|
open,
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
360
|
+
onEscapeKeyDown,
|
|
361
|
+
onPointerDownOutside,
|
|
362
|
+
onInteractOutside,
|
|
363
|
+
handleOpenChange,
|
|
364
|
+
handleOpenAutoFocus,
|
|
291
365
|
target,
|
|
366
|
+
bodyRef,
|
|
292
367
|
style,
|
|
293
368
|
isOpen,
|
|
294
369
|
computedWrapperStyles,
|
|
@@ -79,7 +79,16 @@ export const modal = style({
|
|
|
79
79
|
top: 100,
|
|
80
80
|
paddingBottom: '24px',
|
|
81
81
|
maxWidth: `calc(100vw - 32px)`,
|
|
82
|
-
pointerEvents: 'none'
|
|
82
|
+
pointerEvents: 'none',
|
|
83
|
+
selectors: {
|
|
84
|
+
'&:focus': {
|
|
85
|
+
outline: 'none'
|
|
86
|
+
},
|
|
87
|
+
'&:focus-visible': {
|
|
88
|
+
outline: 'none',
|
|
89
|
+
boxShadow: 'none'
|
|
90
|
+
}
|
|
91
|
+
}
|
|
83
92
|
});
|
|
84
93
|
|
|
85
94
|
export const centeredWrapper = style({
|
|
@@ -113,7 +122,17 @@ export const container = style({
|
|
|
113
122
|
flexDirection: 'column',
|
|
114
123
|
position: 'relative',
|
|
115
124
|
gap: themeVars.space[2],
|
|
116
|
-
pointerEvents: 'auto'
|
|
125
|
+
pointerEvents: 'auto',
|
|
126
|
+
selectors: {
|
|
127
|
+
'&:focus': {
|
|
128
|
+
outline: 'none'
|
|
129
|
+
},
|
|
130
|
+
'&:focus-visible': {
|
|
131
|
+
outline: 'none',
|
|
132
|
+
boxShadow:
|
|
133
|
+
'0 1px 2px -2px rgba(0, 0, 0, 0.08), 0 3px 6px 0 rgba(0, 0, 0, 0.06), 0 5px 12px 4px rgba(0, 0, 0, 0.04)'
|
|
134
|
+
}
|
|
135
|
+
}
|
|
117
136
|
});
|
|
118
137
|
|
|
119
138
|
export const hasSidebar = style({
|
|
@@ -159,6 +178,11 @@ export const close = style([
|
|
|
159
178
|
':focus': {
|
|
160
179
|
backgroundColor: themeVars.color.gray[150]
|
|
161
180
|
},
|
|
181
|
+
':focus-visible': {
|
|
182
|
+
outline: 'none',
|
|
183
|
+
boxShadow: 'none',
|
|
184
|
+
backgroundColor: themeVars.color.gray[150]
|
|
185
|
+
},
|
|
162
186
|
'::before': {
|
|
163
187
|
content: '""',
|
|
164
188
|
position: 'absolute',
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<PopoverRoot v-model:open="isOpen" :modal="modal">
|
|
3
|
+
<PopoverTrigger as-child>
|
|
4
|
+
<slot />
|
|
5
|
+
</PopoverTrigger>
|
|
6
|
+
<PopoverPortal :disabled="!transfer">
|
|
7
|
+
<PopoverContent
|
|
8
|
+
:class="contentClasses"
|
|
9
|
+
:side="resolvedSide"
|
|
10
|
+
:align="resolvedAlign"
|
|
11
|
+
:side-offset="sideOffset"
|
|
12
|
+
:collision-padding="collisionPadding"
|
|
13
|
+
:style="{ zIndex: zIndex }"
|
|
14
|
+
>
|
|
15
|
+
<slot name="content" />
|
|
16
|
+
<PopoverArrow v-if="showArrow" :class="style.arrow" :width="12" :height="6" />
|
|
17
|
+
</PopoverContent>
|
|
18
|
+
</PopoverPortal>
|
|
19
|
+
</PopoverRoot>
|
|
20
|
+
</template>
|
|
21
|
+
|
|
22
|
+
<script lang="ts">
|
|
23
|
+
import {
|
|
24
|
+
PopoverArrow,
|
|
25
|
+
PopoverContent,
|
|
26
|
+
PopoverPortal,
|
|
27
|
+
PopoverRoot,
|
|
28
|
+
PopoverTrigger,
|
|
29
|
+
type PopoverContentProps
|
|
30
|
+
} from 'reka-ui';
|
|
31
|
+
import {
|
|
32
|
+
computed,
|
|
33
|
+
defineComponent,
|
|
34
|
+
ref,
|
|
35
|
+
useAttrs,
|
|
36
|
+
watch,
|
|
37
|
+
type PropType
|
|
38
|
+
} from 'vue';
|
|
39
|
+
import {
|
|
40
|
+
type Placement,
|
|
41
|
+
placementWhileList
|
|
42
|
+
} from '../../composables/usePopper';
|
|
43
|
+
import { useZIndex } from '../../composables/useZIndex';
|
|
44
|
+
import * as style from './style';
|
|
45
|
+
|
|
46
|
+
export default defineComponent({
|
|
47
|
+
name: 'MPopover',
|
|
48
|
+
inheritAttrs: false,
|
|
49
|
+
components: {
|
|
50
|
+
PopoverArrow,
|
|
51
|
+
PopoverContent,
|
|
52
|
+
PopoverPortal,
|
|
53
|
+
PopoverRoot,
|
|
54
|
+
PopoverTrigger
|
|
55
|
+
},
|
|
56
|
+
props: {
|
|
57
|
+
open: {
|
|
58
|
+
type: Boolean,
|
|
59
|
+
default: false
|
|
60
|
+
},
|
|
61
|
+
modal: {
|
|
62
|
+
type: Boolean,
|
|
63
|
+
default: false
|
|
64
|
+
},
|
|
65
|
+
placement: {
|
|
66
|
+
type: String as PropType<Placement>,
|
|
67
|
+
default: 'bottom',
|
|
68
|
+
validator: (value: Placement) => placementWhileList.includes(value)
|
|
69
|
+
},
|
|
70
|
+
transfer: {
|
|
71
|
+
type: Boolean,
|
|
72
|
+
default: true
|
|
73
|
+
},
|
|
74
|
+
sideOffset: {
|
|
75
|
+
type: Number,
|
|
76
|
+
default: 5
|
|
77
|
+
},
|
|
78
|
+
collisionPadding: {
|
|
79
|
+
type: [Number, Object] as PropType<number | { top?: number; right?: number; bottom?: number; left?: number }>,
|
|
80
|
+
default: 10
|
|
81
|
+
},
|
|
82
|
+
arrow: {
|
|
83
|
+
type: Boolean,
|
|
84
|
+
default: false
|
|
85
|
+
},
|
|
86
|
+
variant: {
|
|
87
|
+
type: String as PropType<'light' | 'dark'>,
|
|
88
|
+
default: 'light'
|
|
89
|
+
},
|
|
90
|
+
zIndex: {
|
|
91
|
+
type: Number
|
|
92
|
+
}
|
|
93
|
+
},
|
|
94
|
+
emits: ['update:open'],
|
|
95
|
+
setup(props, { emit }) {
|
|
96
|
+
const attrs = useAttrs();
|
|
97
|
+
const { nextZIndex } = useZIndex();
|
|
98
|
+
const isOpen = ref(props.open);
|
|
99
|
+
const zIndex = ref(props.zIndex ?? nextZIndex());
|
|
100
|
+
|
|
101
|
+
const resolvedPlacement = computed(() => {
|
|
102
|
+
const [side, align] = props.placement.split('-');
|
|
103
|
+
|
|
104
|
+
return {
|
|
105
|
+
side: side === 'top' || side === 'right' || side === 'bottom' || side === 'left'
|
|
106
|
+
? side
|
|
107
|
+
: undefined,
|
|
108
|
+
align: align === 'start' || align === 'center' || align === 'end'
|
|
109
|
+
? align
|
|
110
|
+
: undefined
|
|
111
|
+
} as {
|
|
112
|
+
side?: PopoverContentProps['side'];
|
|
113
|
+
align?: PopoverContentProps['align'];
|
|
114
|
+
};
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
const showArrow = computed(() => {
|
|
118
|
+
const { align } = resolvedPlacement.value;
|
|
119
|
+
return props.arrow && (!align || align === 'center');
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
const contentClasses = computed(() => {
|
|
123
|
+
return [
|
|
124
|
+
'm-popover-content',
|
|
125
|
+
style.content,
|
|
126
|
+
{
|
|
127
|
+
[style.dark]: props.variant === 'dark'
|
|
128
|
+
},
|
|
129
|
+
attrs.class
|
|
130
|
+
];
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
watch(
|
|
134
|
+
() => props.open,
|
|
135
|
+
value => {
|
|
136
|
+
isOpen.value = value;
|
|
137
|
+
}
|
|
138
|
+
);
|
|
139
|
+
|
|
140
|
+
watch(isOpen, value => {
|
|
141
|
+
emit('update:open', value);
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
return {
|
|
145
|
+
isOpen,
|
|
146
|
+
zIndex,
|
|
147
|
+
showArrow,
|
|
148
|
+
contentClasses,
|
|
149
|
+
resolvedSide: computed(() => resolvedPlacement.value.side),
|
|
150
|
+
resolvedAlign: computed(() => resolvedPlacement.value.align),
|
|
151
|
+
style
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
});
|
|
155
|
+
</script>
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div style="padding: 100px; display: flex; gap: 16px; flex-wrap: wrap;">
|
|
3
|
+
<MPopover arrow placement="bottom">
|
|
4
|
+
<MButton>Bottom (Arrow)</MButton>
|
|
5
|
+
<template #content>
|
|
6
|
+
<div style="padding: 8px; min-width: 180px;">
|
|
7
|
+
<p style="margin: 0; font-size: 14px;">
|
|
8
|
+
Arrow points to the trigger.
|
|
9
|
+
</p>
|
|
10
|
+
</div>
|
|
11
|
+
</template>
|
|
12
|
+
</MPopover>
|
|
13
|
+
|
|
14
|
+
<MPopover arrow placement="top">
|
|
15
|
+
<MButton>Top (Arrow)</MButton>
|
|
16
|
+
<template #content>
|
|
17
|
+
<div style="padding: 8px; min-width: 180px;">
|
|
18
|
+
<p style="margin: 0; font-size: 14px;">
|
|
19
|
+
Arrow points to the trigger.
|
|
20
|
+
</p>
|
|
21
|
+
</div>
|
|
22
|
+
</template>
|
|
23
|
+
</MPopover>
|
|
24
|
+
|
|
25
|
+
<MPopover arrow placement="left">
|
|
26
|
+
<MButton>Left (Arrow)</MButton>
|
|
27
|
+
<template #content>
|
|
28
|
+
<div style="padding: 8px; min-width: 180px;">
|
|
29
|
+
<p style="margin: 0; font-size: 14px;">
|
|
30
|
+
Arrow points to the trigger.
|
|
31
|
+
</p>
|
|
32
|
+
</div>
|
|
33
|
+
</template>
|
|
34
|
+
</MPopover>
|
|
35
|
+
|
|
36
|
+
<MPopover arrow placement="right">
|
|
37
|
+
<MButton>Right (Arrow)</MButton>
|
|
38
|
+
<template #content>
|
|
39
|
+
<div style="padding: 8px; min-width: 180px;">
|
|
40
|
+
<p style="margin: 0; font-size: 14px;">
|
|
41
|
+
Arrow points to the trigger.
|
|
42
|
+
</p>
|
|
43
|
+
</div>
|
|
44
|
+
</template>
|
|
45
|
+
</MPopover>
|
|
46
|
+
</div>
|
|
47
|
+
</template>
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div style="padding: 100px;">
|
|
3
|
+
<MPopover>
|
|
4
|
+
<MButton>Open Popover</MButton>
|
|
5
|
+
<template #content>
|
|
6
|
+
<div style="padding: 8px; min-width: 200px;">
|
|
7
|
+
<h4 style="margin: 0 0 8px; font-weight: 600;">Popover Title</h4>
|
|
8
|
+
<p style="margin: 0; color: #666; font-size: 14px;">
|
|
9
|
+
This is the popover content. You can put anything here.
|
|
10
|
+
</p>
|
|
11
|
+
</div>
|
|
12
|
+
</template>
|
|
13
|
+
</MPopover>
|
|
14
|
+
</div>
|
|
15
|
+
</template>
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import MPopover from '../Popover.vue';
|
|
2
|
+
import MButton from '../../button/Button.vue';
|
|
3
|
+
|
|
4
|
+
import { MPopoverBasic, MPopoverPlacement, MPopoverArrow, MPopoverVariant } from './index';
|
|
5
|
+
import { createStory, popperPositionArgType } from '../../../__storybook__';
|
|
6
|
+
|
|
7
|
+
export default {
|
|
8
|
+
component: MPopover,
|
|
9
|
+
subcomponents: { MButton },
|
|
10
|
+
title: 'Components/Popover',
|
|
11
|
+
argTypes: {
|
|
12
|
+
...popperPositionArgType()
|
|
13
|
+
}
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
export const Basic = createStory(MPopoverBasic);
|
|
17
|
+
export const Placement = createStory(MPopoverPlacement);
|
|
18
|
+
export const Arrow = createStory(MPopoverArrow);
|
|
19
|
+
export const Variant = createStory(MPopoverVariant);
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div style="padding: 150px; display: flex; gap: 16px; flex-wrap: wrap;">
|
|
3
|
+
<MPopover placement="top">
|
|
4
|
+
<MButton>Top</MButton>
|
|
5
|
+
<template #content>
|
|
6
|
+
<div style="padding: 8px;">Top</div>
|
|
7
|
+
</template>
|
|
8
|
+
</MPopover>
|
|
9
|
+
|
|
10
|
+
<MPopover placement="top-start">
|
|
11
|
+
<MButton>Top Start</MButton>
|
|
12
|
+
<template #content>
|
|
13
|
+
<div style="padding: 8px;">Top Start</div>
|
|
14
|
+
</template>
|
|
15
|
+
</MPopover>
|
|
16
|
+
|
|
17
|
+
<MPopover placement="top-end">
|
|
18
|
+
<MButton>Top End</MButton>
|
|
19
|
+
<template #content>
|
|
20
|
+
<div style="padding: 8px;">Top End</div>
|
|
21
|
+
</template>
|
|
22
|
+
</MPopover>
|
|
23
|
+
|
|
24
|
+
<MPopover placement="bottom">
|
|
25
|
+
<MButton>Bottom</MButton>
|
|
26
|
+
<template #content>
|
|
27
|
+
<div style="padding: 8px;">Bottom</div>
|
|
28
|
+
</template>
|
|
29
|
+
</MPopover>
|
|
30
|
+
|
|
31
|
+
<MPopover placement="bottom-start">
|
|
32
|
+
<MButton>Bottom Start</MButton>
|
|
33
|
+
<template #content>
|
|
34
|
+
<div style="padding: 8px;">Bottom Start</div>
|
|
35
|
+
</template>
|
|
36
|
+
</MPopover>
|
|
37
|
+
|
|
38
|
+
<MPopover placement="bottom-end">
|
|
39
|
+
<MButton>Bottom End</MButton>
|
|
40
|
+
<template #content>
|
|
41
|
+
<div style="padding: 8px;">Bottom End</div>
|
|
42
|
+
</template>
|
|
43
|
+
</MPopover>
|
|
44
|
+
|
|
45
|
+
<MPopover placement="left">
|
|
46
|
+
<MButton>Left</MButton>
|
|
47
|
+
<template #content>
|
|
48
|
+
<div style="padding: 8px;">Left</div>
|
|
49
|
+
</template>
|
|
50
|
+
</MPopover>
|
|
51
|
+
|
|
52
|
+
<MPopover placement="right">
|
|
53
|
+
<MButton>Right</MButton>
|
|
54
|
+
<template #content>
|
|
55
|
+
<div style="padding: 8px;">Right</div>
|
|
56
|
+
</template>
|
|
57
|
+
</MPopover>
|
|
58
|
+
</div>
|
|
59
|
+
</template>
|