@weni/unnnic-system 3.13.0 → 3.14.0-alpha-teleports.0
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/CHANGELOG.md +7 -0
- package/README.md +9 -1
- package/dist/{es-41fceca9.mjs → es-0d53b5b2.mjs} +1 -1
- package/dist/{index-cca96b43.mjs → index-d7070de8.mjs} +29952 -27094
- package/dist/index.d.ts +5111 -1770
- package/dist/{pt-br-a3088529.mjs → pt-br-bf4e1f97.mjs} +1 -1
- package/dist/style.css +1 -1
- package/dist/unnnic.mjs +232 -204
- package/dist/unnnic.umd.js +48 -44
- package/package.json +3 -2
- package/src/assets/scss/tailwind.scss +8 -0
- package/src/components/Alert/__tests__/__snapshots__/Alert.spec.js.snap +1 -1
- package/src/components/ChartFunnel/DefaultFunnel/ChartDefaultFunnelBase.vue +2 -1
- package/src/components/ChartFunnel/SvgFunnel/ChartFunnelTwoRows.vue +61 -60
- package/src/components/Checkbox/Checkbox.vue +1 -1
- package/src/components/CheckboxGroup/CheckboxGroup.vue +5 -7
- package/src/components/Chip/Chip.vue +1 -1
- package/src/components/DatePicker/DatePicker.vue +10 -1
- package/src/components/Drawer/Drawer.vue +180 -270
- package/src/components/Drawer/__tests__/Drawer.spec.js +32 -43
- package/src/components/Drawer/__tests__/__snapshots__/Drawer.spec.js.snap +18 -19
- package/src/components/FormElement/FormElement.vue +87 -96
- package/src/components/InputDatePicker/InputDatePicker.vue +68 -73
- package/src/components/InputDatePicker/__test__/InputDatePicker.spec.js +31 -24
- package/src/components/ModalDialog/ModalDialog.vue +63 -154
- package/src/components/ModalDialog/__tests__/ModalDialog.spec.js +30 -210
- package/src/components/ModalDialog/__tests__/__snapshots__/ModalDialog.spec.js.snap +1 -22
- package/src/components/MultiSelect/MultSelectOption.vue +49 -0
- package/src/components/MultiSelect/__tests__/MultiSelect.spec.js +557 -0
- package/src/components/MultiSelect/__tests__/MultiSelectOption.spec.js +229 -0
- package/src/components/MultiSelect/__tests__/__snapshots__/MultiSelect.spec.js.snap +87 -0
- package/src/components/MultiSelect/__tests__/__snapshots__/MultiSelectOption.spec.js.snap +51 -0
- package/src/components/MultiSelect/index.vue +265 -0
- package/src/components/Radio/Radio.vue +6 -12
- package/src/components/Radio/__test__/Radio.spec.js +1 -3
- package/src/components/RadioGroup/RadioGroup.vue +10 -18
- package/src/components/Select/__tests__/SelectItem.spec.js +15 -35
- package/src/components/Select/index.vue +3 -3
- package/src/components/Switch/Switch.vue +3 -10
- package/src/components/Tab/__test__/__snapshots__/Tab.spec.js.snap +3 -1
- package/src/components/TemplatePreview/TemplatePreview.vue +25 -28
- package/src/components/TemplatePreview/TemplatePreviewModal.vue +10 -10
- package/src/components/TemplatePreview/types.d.ts +3 -3
- package/src/components/Toast/Toast.vue +4 -1
- package/src/components/Toast/ToastManager.ts +4 -1
- package/src/components/Toast/__tests__/ToastManager.spec.js +10 -6
- package/src/components/ToolTip/ToolTip.vue +25 -177
- package/src/components/ToolTip/__tests__/ToolTip.spec.js +339 -61
- package/src/components/index.ts +58 -2
- package/src/components/ui/dialog/Dialog.vue +19 -0
- package/src/components/ui/dialog/DialogClose.vue +29 -0
- package/src/components/ui/dialog/DialogContent.vue +140 -0
- package/src/components/ui/dialog/DialogFooter.vue +50 -0
- package/src/components/ui/dialog/DialogHeader.vue +83 -0
- package/src/components/ui/dialog/DialogTitle.vue +38 -0
- package/src/components/ui/dialog/DialogTrigger.vue +16 -0
- package/src/components/ui/dialog/index.ts +7 -0
- package/src/components/ui/drawer/Drawer.vue +27 -0
- package/src/components/ui/drawer/DrawerClose.vue +31 -0
- package/src/components/ui/drawer/DrawerContent.vue +113 -0
- package/src/components/ui/drawer/DrawerDescription.vue +40 -0
- package/src/components/ui/drawer/DrawerFooter.vue +38 -0
- package/src/components/ui/drawer/DrawerHeader.vue +57 -0
- package/src/components/ui/drawer/DrawerOverlay.vue +33 -0
- package/src/components/ui/drawer/DrawerTitle.vue +37 -0
- package/src/components/ui/drawer/DrawerTrigger.vue +31 -0
- package/src/components/ui/drawer/index.ts +10 -0
- package/src/components/ui/popover/PopoverContent.vue +7 -4
- package/src/components/ui/popover/PopoverTrigger.vue +5 -1
- package/src/components/ui/tooltip/Tooltip.vue +21 -0
- package/src/components/ui/tooltip/TooltipContent.vue +77 -0
- package/src/components/ui/tooltip/TooltipTrigger.vue +24 -0
- package/src/components/ui/tooltip/index.ts +3 -0
- package/src/index.ts +9 -2
- package/src/lib/__tests__/teleport-target.spec.ts +73 -0
- package/src/lib/layer-manager.ts +64 -0
- package/src/lib/teleport-target.ts +46 -0
- package/src/stories/Dialog.stories.js +832 -0
- package/src/stories/Drawer.stories.js +1 -1
- package/src/stories/DrawerNext.stories.js +611 -0
- package/src/stories/LayerManager.docs.mdx +40 -0
- package/src/stories/LayerManager.stories.js +407 -0
- package/src/stories/ModalDialog.mdx +3 -0
- package/src/stories/ModalDialog.stories.js +96 -1
- package/src/stories/MultiSelect.stories.js +143 -45
- package/src/stories/TemplatePreview.stories.js +27 -27
- package/src/stories/TemplatePreviewModal.stories.js +31 -31
- package/src/components/MultiSelect/MultiSelect.vue +0 -297
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
<script lang="ts" setup>
|
|
2
|
+
import { type HTMLAttributes } from 'vue';
|
|
3
|
+
import { cn } from '@/lib/utils';
|
|
4
|
+
|
|
5
|
+
defineOptions({
|
|
6
|
+
name: 'UnnnicDrawerFooter',
|
|
7
|
+
});
|
|
8
|
+
|
|
9
|
+
const props = defineProps<{
|
|
10
|
+
class?: HTMLAttributes['class'];
|
|
11
|
+
}>();
|
|
12
|
+
</script>
|
|
13
|
+
|
|
14
|
+
<template>
|
|
15
|
+
<footer :class="cn('unnnic-drawer__footer', props.class)">
|
|
16
|
+
<slot />
|
|
17
|
+
</footer>
|
|
18
|
+
</template>
|
|
19
|
+
|
|
20
|
+
<style lang="scss" scoped>
|
|
21
|
+
@use '@/assets/scss/unnnic' as *;
|
|
22
|
+
|
|
23
|
+
.unnnic-drawer__footer {
|
|
24
|
+
border-top: 1px solid $unnnic-color-border-soft;
|
|
25
|
+
|
|
26
|
+
margin-top: auto;
|
|
27
|
+
padding: $unnnic-space-6;
|
|
28
|
+
|
|
29
|
+
display: flex;
|
|
30
|
+
justify-content: center;
|
|
31
|
+
align-items: center;
|
|
32
|
+
gap: $unnnic-space-2;
|
|
33
|
+
|
|
34
|
+
> * {
|
|
35
|
+
width: 100%;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
</style>
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
<script lang="ts" setup>
|
|
2
|
+
import { type HTMLAttributes } from 'vue';
|
|
3
|
+
import { cn } from '@/lib/utils';
|
|
4
|
+
import UnnnicButton from '@/components/Button/Button.vue';
|
|
5
|
+
import DrawerClose from './DrawerClose.vue';
|
|
6
|
+
|
|
7
|
+
defineOptions({
|
|
8
|
+
name: 'UnnnicDrawerHeader',
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
const props = defineProps<{
|
|
12
|
+
class?: HTMLAttributes['class'];
|
|
13
|
+
}>();
|
|
14
|
+
</script>
|
|
15
|
+
|
|
16
|
+
<template>
|
|
17
|
+
<header :class="cn('unnnic-drawer__header', props.class)">
|
|
18
|
+
<slot />
|
|
19
|
+
|
|
20
|
+
<template v-if="$slots.close">
|
|
21
|
+
<slot
|
|
22
|
+
name="close"
|
|
23
|
+
class="unnnic-drawer__close-button"
|
|
24
|
+
/>
|
|
25
|
+
</template>
|
|
26
|
+
<template v-else>
|
|
27
|
+
<DrawerClose>
|
|
28
|
+
<UnnnicButton
|
|
29
|
+
iconCenter="close"
|
|
30
|
+
size="small"
|
|
31
|
+
type="tertiary"
|
|
32
|
+
class="unnnic-drawer__close-button"
|
|
33
|
+
/>
|
|
34
|
+
</DrawerClose>
|
|
35
|
+
</template>
|
|
36
|
+
</header>
|
|
37
|
+
</template>
|
|
38
|
+
|
|
39
|
+
<style lang="scss" scoped>
|
|
40
|
+
@use '@/assets/scss/unnnic' as *;
|
|
41
|
+
|
|
42
|
+
.unnnic-drawer__header {
|
|
43
|
+
display: grid;
|
|
44
|
+
align-items: center;
|
|
45
|
+
grid-template-columns: 1fr auto;
|
|
46
|
+
padding: $unnnic-space-6;
|
|
47
|
+
|
|
48
|
+
gap: $unnnic-space-1;
|
|
49
|
+
|
|
50
|
+
border-bottom: 1px solid $unnnic-color-border-soft;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
.unnnic-drawer__close-button {
|
|
54
|
+
grid-column: 2 / 3;
|
|
55
|
+
grid-row: 1 / 2;
|
|
56
|
+
}
|
|
57
|
+
</style>
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
<script lang="ts" setup>
|
|
2
|
+
import type { DialogOverlayProps } from 'reka-ui';
|
|
3
|
+
import { type HTMLAttributes } from 'vue';
|
|
4
|
+
import { reactiveOmit } from '@vueuse/core';
|
|
5
|
+
import { DrawerOverlay } from 'vaul-vue';
|
|
6
|
+
import { cn } from '@/lib/utils';
|
|
7
|
+
|
|
8
|
+
defineOptions({
|
|
9
|
+
name: 'UnnnicDrawerOverlay',
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
const props = defineProps<
|
|
13
|
+
DialogOverlayProps & { class?: HTMLAttributes['class'] }
|
|
14
|
+
>();
|
|
15
|
+
|
|
16
|
+
const delegatedProps = reactiveOmit(props, 'class');
|
|
17
|
+
</script>
|
|
18
|
+
|
|
19
|
+
<template>
|
|
20
|
+
<DrawerOverlay
|
|
21
|
+
v-bind="delegatedProps"
|
|
22
|
+
:class="cn('unnnic-drawer__overlay', props.class)"
|
|
23
|
+
/>
|
|
24
|
+
</template>
|
|
25
|
+
|
|
26
|
+
<style lang="scss" scoped>
|
|
27
|
+
.unnnic-drawer__overlay {
|
|
28
|
+
position: fixed;
|
|
29
|
+
inset: 0;
|
|
30
|
+
|
|
31
|
+
background: rgba(53, 57, 69, 0.65);
|
|
32
|
+
}
|
|
33
|
+
</style>
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
<script lang="ts" setup>
|
|
2
|
+
import type { DrawerTitleProps } from 'vaul-vue';
|
|
3
|
+
import { type HTMLAttributes } from 'vue';
|
|
4
|
+
import { reactiveOmit } from '@vueuse/core';
|
|
5
|
+
import { DrawerTitle } from 'vaul-vue';
|
|
6
|
+
import { cn } from '@/lib/utils';
|
|
7
|
+
|
|
8
|
+
defineOptions({
|
|
9
|
+
name: 'UnnnicDrawerTitle',
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
const props = defineProps<
|
|
13
|
+
DrawerTitleProps & { class?: HTMLAttributes['class'] }
|
|
14
|
+
>();
|
|
15
|
+
|
|
16
|
+
const delegatedProps = reactiveOmit(props, 'class');
|
|
17
|
+
</script>
|
|
18
|
+
|
|
19
|
+
<template>
|
|
20
|
+
<DrawerTitle
|
|
21
|
+
v-bind="delegatedProps"
|
|
22
|
+
:class="cn('unnnic-drawer__title', props.class)"
|
|
23
|
+
>
|
|
24
|
+
<slot />
|
|
25
|
+
</DrawerTitle>
|
|
26
|
+
</template>
|
|
27
|
+
|
|
28
|
+
<style lang="scss" scoped>
|
|
29
|
+
@use '@/assets/scss/unnnic' as *;
|
|
30
|
+
|
|
31
|
+
.unnnic-drawer__title {
|
|
32
|
+
margin: 0;
|
|
33
|
+
|
|
34
|
+
color: $unnnic-color-fg-emphasized;
|
|
35
|
+
font: $unnnic-font-display-2;
|
|
36
|
+
}
|
|
37
|
+
</style>
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import type { DrawerTriggerProps } from 'vaul-vue';
|
|
3
|
+
import { DrawerTrigger } from 'vaul-vue';
|
|
4
|
+
|
|
5
|
+
defineOptions({
|
|
6
|
+
name: 'UnnnicDrawerTrigger',
|
|
7
|
+
});
|
|
8
|
+
|
|
9
|
+
const props = defineProps<DrawerTriggerProps>();
|
|
10
|
+
</script>
|
|
11
|
+
|
|
12
|
+
<template>
|
|
13
|
+
<DrawerTrigger
|
|
14
|
+
v-bind="props"
|
|
15
|
+
class="unnnic-drawer__trigger"
|
|
16
|
+
>
|
|
17
|
+
<slot />
|
|
18
|
+
</DrawerTrigger>
|
|
19
|
+
</template>
|
|
20
|
+
|
|
21
|
+
<style lang="scss" scoped>
|
|
22
|
+
@use '@/assets/scss/unnnic' as *;
|
|
23
|
+
|
|
24
|
+
.unnnic-drawer__trigger {
|
|
25
|
+
border: none;
|
|
26
|
+
background: none;
|
|
27
|
+
padding: 0;
|
|
28
|
+
margin: 0;
|
|
29
|
+
cursor: pointer;
|
|
30
|
+
}
|
|
31
|
+
</style>
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export { default as Drawer } from './Drawer.vue';
|
|
2
|
+
export { default as DrawerContent } from './DrawerContent.vue';
|
|
3
|
+
export { default as DrawerDescription } from './DrawerDescription.vue';
|
|
4
|
+
export { default as DrawerFooter } from './DrawerFooter.vue';
|
|
5
|
+
export { default as DrawerHeader } from './DrawerHeader.vue';
|
|
6
|
+
export { default as DrawerOverlay } from './DrawerOverlay.vue';
|
|
7
|
+
export { default as DrawerTitle } from './DrawerTitle.vue';
|
|
8
|
+
export { default as DrawerTrigger } from './DrawerTrigger.vue';
|
|
9
|
+
export { default as DrawerClose } from './DrawerClose.vue';
|
|
10
|
+
export { DrawerPortal } from 'vaul-vue';
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<PopoverPortal>
|
|
2
|
+
<PopoverPortal :to="portalTarget">
|
|
3
3
|
<PopoverContent
|
|
4
4
|
v-bind="{ ...forwarded, ...$attrs }"
|
|
5
|
-
:style="{ width: contentWidth }"
|
|
5
|
+
:style="{ width: contentWidth, zIndex: popoverZIndex }"
|
|
6
6
|
:class="
|
|
7
7
|
cn(
|
|
8
8
|
'unnnic-popover',
|
|
@@ -34,6 +34,8 @@ import { computed, useSlots } from 'vue';
|
|
|
34
34
|
import { reactiveOmit } from '@vueuse/core';
|
|
35
35
|
import { PopoverContent, PopoverPortal, useForwardPropsEmits } from 'reka-ui';
|
|
36
36
|
import { cn } from '@/lib/utils';
|
|
37
|
+
import { useLayerZIndex } from '@/lib/layer-manager';
|
|
38
|
+
import { useTeleportTarget } from '@/lib/teleport-target';
|
|
37
39
|
|
|
38
40
|
defineOptions({
|
|
39
41
|
inheritAttrs: false,
|
|
@@ -63,6 +65,9 @@ const forwarded = useForwardPropsEmits(delegatedProps, emits);
|
|
|
63
65
|
|
|
64
66
|
const slots = useSlots() as Slots;
|
|
65
67
|
|
|
68
|
+
const popoverZIndex = useLayerZIndex();
|
|
69
|
+
const portalTarget = useTeleportTarget();
|
|
70
|
+
|
|
66
71
|
const getComponentName = (vnode: VNode): string | undefined => {
|
|
67
72
|
const componentType = vnode.type as { name?: string; __name?: string };
|
|
68
73
|
return componentType?.name || componentType?.__name;
|
|
@@ -101,8 +106,6 @@ const contentWidth = computed(() => {
|
|
|
101
106
|
$popover-space: $unnnic-space-4;
|
|
102
107
|
|
|
103
108
|
.unnnic-popover {
|
|
104
|
-
z-index: 10000;
|
|
105
|
-
|
|
106
109
|
border-radius: $unnnic-radius-2;
|
|
107
110
|
box-shadow: $unnnic-shadow-1;
|
|
108
111
|
border: 1px solid $unnnic-color-border-soft;
|
|
@@ -14,10 +14,14 @@ const props = defineProps<PopoverTriggerProps>();
|
|
|
14
14
|
</PopoverTrigger>
|
|
15
15
|
</template>
|
|
16
16
|
|
|
17
|
-
<style scoped>
|
|
17
|
+
<style lang="scss" scoped>
|
|
18
18
|
.unnnic-popover-trigger {
|
|
19
19
|
border: none;
|
|
20
20
|
background: transparent;
|
|
21
21
|
padding: 0;
|
|
22
|
+
|
|
23
|
+
& > * {
|
|
24
|
+
width: 100%;
|
|
25
|
+
}
|
|
22
26
|
}
|
|
23
27
|
</style>
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import type { TooltipRootEmits, TooltipRootProps } from 'reka-ui';
|
|
3
|
+
import { TooltipRoot, useForwardPropsEmits } from 'reka-ui';
|
|
4
|
+
import { TooltipProvider } from 'reka-ui';
|
|
5
|
+
|
|
6
|
+
const props = defineProps<TooltipRootProps>();
|
|
7
|
+
const emits = defineEmits<TooltipRootEmits>();
|
|
8
|
+
|
|
9
|
+
const forwarded = useForwardPropsEmits(props, emits);
|
|
10
|
+
</script>
|
|
11
|
+
|
|
12
|
+
<template>
|
|
13
|
+
<TooltipProvider
|
|
14
|
+
:delayDuration="100"
|
|
15
|
+
disableClosingTrigger
|
|
16
|
+
>
|
|
17
|
+
<TooltipRoot v-bind="forwarded">
|
|
18
|
+
<slot />
|
|
19
|
+
</TooltipRoot>
|
|
20
|
+
</TooltipProvider>
|
|
21
|
+
</template>
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import type { TooltipContentEmits, TooltipContentProps } from 'reka-ui';
|
|
3
|
+
import type { HTMLAttributes } from 'vue';
|
|
4
|
+
import { reactiveOmit } from '@vueuse/core';
|
|
5
|
+
import {
|
|
6
|
+
TooltipArrow,
|
|
7
|
+
TooltipContent,
|
|
8
|
+
TooltipPortal,
|
|
9
|
+
useForwardPropsEmits,
|
|
10
|
+
} from 'reka-ui';
|
|
11
|
+
import { cn } from '@/lib/utils';
|
|
12
|
+
import { useLayerZIndex } from '@/lib/layer-manager';
|
|
13
|
+
import { useTeleportTarget } from '@/lib/teleport-target';
|
|
14
|
+
|
|
15
|
+
defineOptions({
|
|
16
|
+
inheritAttrs: false,
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
const props = withDefaults(
|
|
20
|
+
defineProps<TooltipContentProps & { class?: HTMLAttributes['class'] }>(),
|
|
21
|
+
{
|
|
22
|
+
sideOffset: 0,
|
|
23
|
+
class: undefined,
|
|
24
|
+
},
|
|
25
|
+
);
|
|
26
|
+
|
|
27
|
+
const emits = defineEmits<TooltipContentEmits>();
|
|
28
|
+
|
|
29
|
+
const delegatedProps = reactiveOmit(props, 'class');
|
|
30
|
+
|
|
31
|
+
const forwarded = useForwardPropsEmits(delegatedProps, emits);
|
|
32
|
+
const tooltipZIndex = useLayerZIndex();
|
|
33
|
+
const portalTarget = useTeleportTarget();
|
|
34
|
+
</script>
|
|
35
|
+
|
|
36
|
+
<template>
|
|
37
|
+
<TooltipPortal :to="portalTarget">
|
|
38
|
+
<TooltipContent
|
|
39
|
+
v-bind="{ ...forwarded, ...$attrs }"
|
|
40
|
+
:class="
|
|
41
|
+
cn(
|
|
42
|
+
'tooltip__content',
|
|
43
|
+
'animate-in fade-in-0 zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2',
|
|
44
|
+
props.class,
|
|
45
|
+
)
|
|
46
|
+
"
|
|
47
|
+
:style="{ zIndex: tooltipZIndex }"
|
|
48
|
+
>
|
|
49
|
+
<slot />
|
|
50
|
+
|
|
51
|
+
<TooltipArrow class="tooltip__arrow" />
|
|
52
|
+
</TooltipContent>
|
|
53
|
+
</TooltipPortal>
|
|
54
|
+
</template>
|
|
55
|
+
|
|
56
|
+
<style lang="scss">
|
|
57
|
+
@use '@/assets/scss/unnnic' as *;
|
|
58
|
+
|
|
59
|
+
.tooltip__content {
|
|
60
|
+
background-color: $unnnic-color-gray-900;
|
|
61
|
+
color: $unnnic-color-white;
|
|
62
|
+
border-radius: $unnnic-radius-1;
|
|
63
|
+
padding: $unnnic-space-2;
|
|
64
|
+
box-shadow: $unnnic-shadow-1;
|
|
65
|
+
font: $unnnic-font-caption-2;
|
|
66
|
+
|
|
67
|
+
overflow-wrap: break-word;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
.tooltip__arrow {
|
|
71
|
+
width: 10px;
|
|
72
|
+
height: 10px;
|
|
73
|
+
background-color: $unnnic-color-gray-900;
|
|
74
|
+
fill: $unnnic-color-gray-900;
|
|
75
|
+
transform: rotate(45deg) translate(-50%, -50%);
|
|
76
|
+
}
|
|
77
|
+
</style>
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import type { TooltipTriggerProps } from 'reka-ui';
|
|
3
|
+
import { TooltipTrigger } from 'reka-ui';
|
|
4
|
+
|
|
5
|
+
const props = defineProps<TooltipTriggerProps>();
|
|
6
|
+
</script>
|
|
7
|
+
|
|
8
|
+
<template>
|
|
9
|
+
<TooltipTrigger
|
|
10
|
+
v-bind="props"
|
|
11
|
+
as="div"
|
|
12
|
+
class="unnnic-tooltip-trigger"
|
|
13
|
+
>
|
|
14
|
+
<slot />
|
|
15
|
+
</TooltipTrigger>
|
|
16
|
+
</template>
|
|
17
|
+
|
|
18
|
+
<style lang="scss" scoped>
|
|
19
|
+
.unnnic-tooltip-trigger {
|
|
20
|
+
position: relative;
|
|
21
|
+
display: inline-block;
|
|
22
|
+
overflow-wrap: break-word;
|
|
23
|
+
}
|
|
24
|
+
</style>
|
package/src/index.ts
CHANGED
|
@@ -1,14 +1,21 @@
|
|
|
1
1
|
import type { App } from 'vue';
|
|
2
2
|
import { components } from '@/components';
|
|
3
3
|
import './assets/scss/tailwind.scss';
|
|
4
|
+
import { setTeleportTarget, type TeleportTarget } from '@/lib/teleport-target';
|
|
4
5
|
|
|
5
6
|
interface UnnnicPlugin {
|
|
6
|
-
install(app: App): void;
|
|
7
|
+
install(app: App, options?: UnnnicPluginOptions): void;
|
|
7
8
|
[key: string]: any;
|
|
8
9
|
}
|
|
9
10
|
|
|
11
|
+
export interface UnnnicPluginOptions {
|
|
12
|
+
teleportTarget?: TeleportTarget | null;
|
|
13
|
+
}
|
|
14
|
+
|
|
10
15
|
const Unnnic: UnnnicPlugin = {
|
|
11
|
-
install(app: App) {
|
|
16
|
+
install(app: App, options?: UnnnicPluginOptions) {
|
|
17
|
+
setTeleportTarget(options?.teleportTarget ?? null);
|
|
18
|
+
|
|
12
19
|
Object.keys(components).forEach((name) => {
|
|
13
20
|
app.component(name, components[name] as any);
|
|
14
21
|
});
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { describe, it, expect, beforeEach } from 'vitest';
|
|
2
|
+
import { ref } from 'vue';
|
|
3
|
+
import {
|
|
4
|
+
getTeleportContainer,
|
|
5
|
+
getTeleportTarget,
|
|
6
|
+
setTeleportTarget,
|
|
7
|
+
useTeleportTarget,
|
|
8
|
+
} from '@/lib/teleport-target';
|
|
9
|
+
|
|
10
|
+
describe('teleport-target helpers', () => {
|
|
11
|
+
beforeEach(() => {
|
|
12
|
+
setTeleportTarget(null);
|
|
13
|
+
document.body.innerHTML = '';
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
it('stores and returns the raw teleport target', () => {
|
|
17
|
+
expect(getTeleportTarget()).toBeUndefined();
|
|
18
|
+
|
|
19
|
+
setTeleportTarget('#micro');
|
|
20
|
+
expect(getTeleportTarget()).toBe('#micro');
|
|
21
|
+
|
|
22
|
+
const element = document.createElement('section');
|
|
23
|
+
setTeleportTarget(element);
|
|
24
|
+
expect(getTeleportTarget()).toBe(element);
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
it('keeps the composable value in sync with global updates', () => {
|
|
28
|
+
const targetRef = useTeleportTarget();
|
|
29
|
+
expect(targetRef.value).toBeUndefined();
|
|
30
|
+
|
|
31
|
+
setTeleportTarget('#first');
|
|
32
|
+
expect(targetRef.value).toBe('#first');
|
|
33
|
+
|
|
34
|
+
setTeleportTarget('#second');
|
|
35
|
+
expect(targetRef.value).toBe('#second');
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
it('prefers local overrides over the shared teleport target', () => {
|
|
39
|
+
setTeleportTarget('#global');
|
|
40
|
+
|
|
41
|
+
const override = ref('#local-1');
|
|
42
|
+
const targetRef = useTeleportTarget(override);
|
|
43
|
+
expect(targetRef.value).toBe('#local-1');
|
|
44
|
+
|
|
45
|
+
override.value = '#local-2';
|
|
46
|
+
expect(targetRef.value).toBe('#local-2');
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
it('resolves DOM elements for selector targets', () => {
|
|
50
|
+
const host = document.createElement('div');
|
|
51
|
+
host.id = 'micro-root';
|
|
52
|
+
document.body.appendChild(host);
|
|
53
|
+
|
|
54
|
+
setTeleportTarget('#micro-root');
|
|
55
|
+
expect(getTeleportContainer()).toBe(host);
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
it('falls back to document.body when selector is missing or unset', () => {
|
|
59
|
+
setTeleportTarget('#missing');
|
|
60
|
+
expect(getTeleportContainer()).toBe(document.body);
|
|
61
|
+
|
|
62
|
+
setTeleportTarget(null);
|
|
63
|
+
expect(getTeleportContainer()).toBe(document.body);
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
it('returns the element itself when the target is an HTMLElement', () => {
|
|
67
|
+
const host = document.createElement('section');
|
|
68
|
+
document.body.appendChild(host);
|
|
69
|
+
|
|
70
|
+
setTeleportTarget(host);
|
|
71
|
+
expect(getTeleportContainer()).toBe(host);
|
|
72
|
+
});
|
|
73
|
+
});
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { onBeforeUnmount, onMounted, ref, type Ref } from 'vue';
|
|
2
|
+
|
|
3
|
+
const BASE_LAYER_Z_INDEX = 1000;
|
|
4
|
+
const DEFAULT_STEP = 5;
|
|
5
|
+
|
|
6
|
+
const layerStack: number[] = [];
|
|
7
|
+
const layerHandles = new Map<symbol, number>();
|
|
8
|
+
|
|
9
|
+
interface LayerHandle {
|
|
10
|
+
id: symbol;
|
|
11
|
+
value: number;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function pushLayer(): LayerHandle {
|
|
15
|
+
const id = Symbol('layer');
|
|
16
|
+
const lastValue = layerStack.length
|
|
17
|
+
? layerStack[layerStack.length - 1]
|
|
18
|
+
: BASE_LAYER_Z_INDEX;
|
|
19
|
+
const value = lastValue + DEFAULT_STEP;
|
|
20
|
+
layerStack.push(value);
|
|
21
|
+
layerHandles.set(id, value);
|
|
22
|
+
return { id, value };
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function popLayer(id: symbol) {
|
|
26
|
+
const value = layerHandles.get(id);
|
|
27
|
+
if (value === undefined) {
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
layerHandles.delete(id);
|
|
32
|
+
const index = layerStack.indexOf(value);
|
|
33
|
+
if (index !== -1) {
|
|
34
|
+
layerStack.splice(index, 1);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export interface LayerZIndexOptions {
|
|
39
|
+
offset?: number;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export function useLayerZIndex(options?: LayerZIndexOptions): Ref<number> {
|
|
43
|
+
const offset = options?.offset ?? 0;
|
|
44
|
+
const zIndex = ref(BASE_LAYER_Z_INDEX + offset);
|
|
45
|
+
|
|
46
|
+
let allocationId: symbol | null = null;
|
|
47
|
+
|
|
48
|
+
const allocate = () => {
|
|
49
|
+
const handle = pushLayer();
|
|
50
|
+
allocationId = handle.id;
|
|
51
|
+
zIndex.value = handle.value + offset;
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
onMounted(allocate);
|
|
55
|
+
|
|
56
|
+
onBeforeUnmount(() => {
|
|
57
|
+
if (allocationId) {
|
|
58
|
+
popLayer(allocationId);
|
|
59
|
+
allocationId = null;
|
|
60
|
+
}
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
return zIndex;
|
|
64
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { computed, shallowRef, toValue, type MaybeRefOrGetter } from 'vue';
|
|
2
|
+
|
|
3
|
+
export type TeleportTarget = string | HTMLElement;
|
|
4
|
+
|
|
5
|
+
const teleportTargetRef = shallowRef<TeleportTarget | null>(null);
|
|
6
|
+
|
|
7
|
+
export function setTeleportTarget(target?: TeleportTarget | null) {
|
|
8
|
+
teleportTargetRef.value = target ?? null;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export function getTeleportTarget() {
|
|
12
|
+
return teleportTargetRef.value ?? undefined;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export function useTeleportTarget(
|
|
16
|
+
override?: MaybeRefOrGetter<TeleportTarget | null | undefined>,
|
|
17
|
+
) {
|
|
18
|
+
// Composable version, ideal for `<Teleport :to="...">` bindings because the
|
|
19
|
+
// returned ref stays reactive to both global and local overrides.
|
|
20
|
+
return computed(() => {
|
|
21
|
+
const localTarget = override ? toValue(override) : undefined;
|
|
22
|
+
return localTarget ?? teleportTargetRef.value ?? undefined;
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function resolveTargetElement(target?: TeleportTarget | null) {
|
|
27
|
+
if (typeof document === 'undefined') {
|
|
28
|
+
return null;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
if (!target) {
|
|
32
|
+
return document.body;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
if (typeof target === 'string') {
|
|
36
|
+
return document.querySelector(target) ?? document.body;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
return target;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export function getTeleportContainer() {
|
|
43
|
+
// Immediate DOM resolver for imperative consumers (e.g., Toast manager)
|
|
44
|
+
// that simply need the concrete HTMLElement to append to right away.
|
|
45
|
+
return resolveTargetElement(teleportTargetRef.value);
|
|
46
|
+
}
|