@livestorm/ui-vue 2.0.0 → 2.0.2
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/README.md +16 -14
- package/dist/{UiTimelineItem.vue_vue_type_style_index_0_lang-DOLFRKcb.mjs → UiTimelineItem.vue_vue_type_style_index_0_lang-B0UKmlqE.mjs} +1158 -1270
- package/dist/a11y--y7uaY4j.mjs +217 -0
- package/dist/components.mjs +73 -72
- package/dist/index.mjs +28 -27
- package/dist/layouts.mjs +27 -26
- package/dist/responsive.mjs +33 -19
- package/dist/types/src/components/UiButton/UiButton.vue.d.ts +31 -0
- package/dist/types/src/components/UiDropdown/UiDropdown.vue.d.ts +2 -2
- package/dist/types/src/components/UiFieldWrapper/UiFieldWrapper.vue.d.ts +4 -4
- package/dist/types/src/components/UiTooltip/UiTooltip.vue.d.ts +28 -5
- package/dist/types/src/utils/responsive.d.ts +9 -1
- package/dist/ui-vue.css +1 -1
- package/dist/utils.mjs +117 -82
- package/package.json +3 -5
- package/src/components/Toast/LayoutToasts.vue +4 -1
- package/src/components/UiButton/UiButton.vue +49 -46
- package/src/components/UiFieldWrapper/UiFieldWrapper.vue +7 -5
- package/src/components/UiInputCheckbox/UiInputCheckbox.vue +14 -10
- package/src/components/UiLabel/UiLabel.vue +11 -9
- package/src/components/UiPage/UiPage.vue +4 -1
- package/src/components/UiTooltip/UiTooltip.vue +56 -4
- package/src/utils/clipboard.js +80 -13
- package/src/utils/responsive.js +70 -16
- package/dist/a11y-DIdrv5Mn.mjs +0 -66
package/dist/utils.mjs
CHANGED
|
@@ -1,111 +1,146 @@
|
|
|
1
|
-
import { BREAKPOINT_2XL as
|
|
1
|
+
import { BREAKPOINT_2XL as F, BREAKPOINT_LG as N, BREAKPOINT_MD as X, BREAKPOINT_SM as D, BREAKPOINT_XL as Q, BREAKPOINT_XL_PLUS as U } from "./breakpoints.mjs";
|
|
2
2
|
import "./bus.mjs";
|
|
3
|
-
import { computed as
|
|
4
|
-
import { useClipboard as
|
|
3
|
+
import { computed as h, createApp as _, h as p } from "vue";
|
|
4
|
+
import { useClipboard as g, usePermission as x } from "@vueuse/core";
|
|
5
5
|
import R from "i18next";
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
6
|
+
import { b as A, _ as C, c as P, a as T } from "./a11y--y7uaY4j.mjs";
|
|
7
|
+
import { f as j, h as G, d as z, u as H } from "./a11y--y7uaY4j.mjs";
|
|
8
|
+
import { useResponsive as Y } from "./responsive.mjs";
|
|
9
|
+
import { useRoute as $, useRouter as S } from "vue-router";
|
|
10
|
+
import { RenderSlot as ee, getSlotAsComponent as te, unwrapFragments as oe, useSlotsAsComponents as ne } from "./slot.mjs";
|
|
11
|
+
let d = null;
|
|
12
|
+
function k(s, u) {
|
|
13
|
+
d && d();
|
|
14
|
+
const t = s.getBoundingClientRect(), r = document.createElement("div");
|
|
15
|
+
document.body.appendChild(r);
|
|
16
|
+
const c = _({
|
|
17
|
+
render: () => p(A, null, () => p(C, { open: !0, delayDuration: 0 }, () => [
|
|
18
|
+
p(P, { asChild: !0 }, () => p("span", {
|
|
19
|
+
"aria-hidden": "true",
|
|
20
|
+
style: {
|
|
21
|
+
position: "fixed",
|
|
22
|
+
left: `${t.left}px`,
|
|
23
|
+
top: `${t.top}px`,
|
|
24
|
+
width: `${t.width}px`,
|
|
25
|
+
height: `${t.height}px`,
|
|
26
|
+
pointerEvents: "none"
|
|
27
|
+
}
|
|
28
|
+
})),
|
|
29
|
+
p(T, { side: "top" }, () => u),
|
|
30
|
+
// The bubble lives in reka's portal where attrs like role are not
|
|
31
|
+
// forwardable; announce the feedback from a visually hidden live
|
|
32
|
+
// region instead so screen readers hear the copy confirmation.
|
|
33
|
+
p("span", {
|
|
34
|
+
role: "status",
|
|
35
|
+
style: {
|
|
36
|
+
position: "absolute",
|
|
37
|
+
width: "1px",
|
|
38
|
+
height: "1px",
|
|
39
|
+
overflow: "hidden",
|
|
40
|
+
clipPath: "inset(50%)"
|
|
41
|
+
}
|
|
42
|
+
}, u)
|
|
43
|
+
]))
|
|
44
|
+
});
|
|
45
|
+
c.mount(r);
|
|
46
|
+
let a = !1;
|
|
47
|
+
const o = () => {
|
|
48
|
+
a || (a = !0, d === o && (d = null), c.unmount(), r.remove());
|
|
49
|
+
};
|
|
50
|
+
return d = o, o;
|
|
51
|
+
}
|
|
52
|
+
function O() {
|
|
53
|
+
const { isSupported: s, copy: u } = g(), t = x("clipboard-write"), r = h(() => s.value && t.value);
|
|
54
|
+
async function c(a, o, f = R.t("general_misc_copied-text")) {
|
|
55
|
+
if (await u(a), o) {
|
|
15
56
|
o.classList.add("copied");
|
|
16
|
-
const
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
delay: 0
|
|
20
|
-
});
|
|
21
|
-
a.show(), setTimeout(() => {
|
|
22
|
-
o.classList.remove("copied"), a.hide(), setTimeout(() => {
|
|
23
|
-
T(o);
|
|
24
|
-
}, 400);
|
|
57
|
+
const m = k(o, f);
|
|
58
|
+
setTimeout(() => {
|
|
59
|
+
o.classList.remove("copied"), m();
|
|
25
60
|
}, 600);
|
|
26
61
|
}
|
|
27
62
|
}
|
|
28
63
|
return {
|
|
29
|
-
canCopy:
|
|
30
|
-
copy:
|
|
64
|
+
canCopy: r,
|
|
65
|
+
copy: c
|
|
31
66
|
};
|
|
32
67
|
}
|
|
33
|
-
function
|
|
34
|
-
const
|
|
35
|
-
function
|
|
36
|
-
const
|
|
37
|
-
for (const
|
|
38
|
-
|
|
39
|
-
return
|
|
68
|
+
function q(s, u = !1) {
|
|
69
|
+
const t = $(), r = S(), c = `${s}-`;
|
|
70
|
+
function a() {
|
|
71
|
+
const e = [];
|
|
72
|
+
for (const n in t.query)
|
|
73
|
+
n.startsWith(c) && e.push(n);
|
|
74
|
+
return e;
|
|
40
75
|
}
|
|
41
|
-
function o(
|
|
42
|
-
return
|
|
76
|
+
function o(e) {
|
|
77
|
+
return e === "true" ? !0 : e === "false" ? !1 : e ?? !1;
|
|
43
78
|
}
|
|
44
|
-
const
|
|
79
|
+
const f = h(() => {
|
|
45
80
|
if (u) {
|
|
46
|
-
const
|
|
47
|
-
if (
|
|
81
|
+
const e = a();
|
|
82
|
+
if (e.length === 0)
|
|
48
83
|
return !1;
|
|
49
|
-
const
|
|
50
|
-
for (const
|
|
51
|
-
|
|
52
|
-
return
|
|
84
|
+
const n = {};
|
|
85
|
+
for (const i of e)
|
|
86
|
+
n[i.substring(c.length)] = o(t.query[i]);
|
|
87
|
+
return n;
|
|
53
88
|
} else
|
|
54
|
-
return o(
|
|
55
|
-
}),
|
|
56
|
-
return
|
|
57
|
-
get: () =>
|
|
58
|
-
set: (
|
|
59
|
-
if (
|
|
60
|
-
const
|
|
61
|
-
if (
|
|
62
|
-
|
|
89
|
+
return o(t.query[s]);
|
|
90
|
+
}), m = !!f.value;
|
|
91
|
+
return h({
|
|
92
|
+
get: () => f.value,
|
|
93
|
+
set: (e) => {
|
|
94
|
+
if (e === f.value) return;
|
|
95
|
+
const n = e == null || e === !1;
|
|
96
|
+
if (n && !m)
|
|
97
|
+
r.back();
|
|
63
98
|
else {
|
|
64
|
-
let
|
|
99
|
+
let i;
|
|
65
100
|
if (u)
|
|
66
|
-
if (
|
|
67
|
-
const
|
|
68
|
-
for (const
|
|
69
|
-
|
|
101
|
+
if (i = {}, n) {
|
|
102
|
+
const l = a();
|
|
103
|
+
for (const y of l)
|
|
104
|
+
i[y] = void 0;
|
|
70
105
|
} else
|
|
71
|
-
for (const
|
|
72
|
-
|
|
106
|
+
for (const l in e)
|
|
107
|
+
i[`${s}-${l}`] = e[l];
|
|
73
108
|
else
|
|
74
|
-
|
|
75
|
-
[
|
|
109
|
+
i = {
|
|
110
|
+
[s]: n ? void 0 : e
|
|
76
111
|
};
|
|
77
|
-
|
|
78
|
-
...
|
|
112
|
+
r[n ? "replace" : "push"]({
|
|
113
|
+
...t,
|
|
79
114
|
query: {
|
|
80
|
-
...
|
|
81
|
-
...
|
|
115
|
+
...t.query,
|
|
116
|
+
...i
|
|
82
117
|
}
|
|
83
118
|
});
|
|
84
119
|
}
|
|
85
120
|
}
|
|
86
121
|
});
|
|
87
122
|
}
|
|
88
|
-
let
|
|
89
|
-
function
|
|
90
|
-
return ++
|
|
123
|
+
let E = 0;
|
|
124
|
+
function M() {
|
|
125
|
+
return ++E;
|
|
91
126
|
}
|
|
92
127
|
export {
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
128
|
+
F as BREAKPOINT_2XL,
|
|
129
|
+
N as BREAKPOINT_LG,
|
|
130
|
+
X as BREAKPOINT_MD,
|
|
131
|
+
D as BREAKPOINT_SM,
|
|
132
|
+
Q as BREAKPOINT_XL,
|
|
133
|
+
U as BREAKPOINT_XL_PLUS,
|
|
134
|
+
ee as RenderSlot,
|
|
135
|
+
j as focusFirstMenuItem,
|
|
136
|
+
te as getSlotAsComponent,
|
|
137
|
+
M as getUid,
|
|
138
|
+
G as hasStackedComponents,
|
|
139
|
+
oe as unwrapFragments,
|
|
140
|
+
O as useCopyToClipboard,
|
|
141
|
+
Y as useResponsive,
|
|
142
|
+
q as useRouteQueryModel,
|
|
143
|
+
ne as useSlotsAsComponents,
|
|
144
|
+
z as useStackAutofocus,
|
|
145
|
+
H as useStackedComponent
|
|
111
146
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@livestorm/ui-vue",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.2",
|
|
4
4
|
"description": "Livestorm UI components for Vue 3",
|
|
5
5
|
"license": "UNLICENSED",
|
|
6
6
|
"repository": {
|
|
@@ -35,14 +35,12 @@
|
|
|
35
35
|
"embla-carousel": "^8.6.0",
|
|
36
36
|
"embla-carousel-autoplay": "^8.6.0",
|
|
37
37
|
"embla-carousel-vue": "^8.6.0",
|
|
38
|
-
"floating-vue": "^5.2.2",
|
|
39
38
|
"i18next": "^26.3.4",
|
|
40
39
|
"mitt": "^3.0.1",
|
|
41
40
|
"reka-ui": "2.9.10",
|
|
42
41
|
"tailwindcss": "^4.3.2",
|
|
43
|
-
"
|
|
44
|
-
"@livestorm/ui-icons": "^2.0.
|
|
45
|
-
"@livestorm/ui-tokens": "^2.0.0"
|
|
42
|
+
"@livestorm/ui-tokens": "^2.0.2",
|
|
43
|
+
"@livestorm/ui-icons": "^2.0.2"
|
|
46
44
|
},
|
|
47
45
|
"devDependencies": {
|
|
48
46
|
"@chromatic-com/storybook": "^5.2.1",
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
2
|
import { computed, getCurrentInstance } from 'vue'
|
|
3
3
|
import type { ResponsiveGlobalComponentProperty } from '../../utils/responsive.types'
|
|
4
|
+
import { useResponsive } from '../../utils/responsive'
|
|
4
5
|
import { useToasts, type ToastOptions } from './toastDSV2'
|
|
5
6
|
import ToastMessage from './ToastMessage.vue'
|
|
6
7
|
|
|
@@ -8,7 +9,9 @@ const app = getCurrentInstance()
|
|
|
8
9
|
if (!app) {
|
|
9
10
|
throw new Error('No current instance found')
|
|
10
11
|
}
|
|
11
|
-
|
|
12
|
+
// Prefer the app-installed $responsive (it may carry custom flags); fall
|
|
13
|
+
// back to the shared singleton so no plugin registration is required.
|
|
14
|
+
const responsive = computed(() => (app.appContext.config.globalProperties.$responsive ?? useResponsive()) as ResponsiveGlobalComponentProperty)
|
|
12
15
|
|
|
13
16
|
const MAX_ITEMS_DISPLAYED = 3
|
|
14
17
|
|
|
@@ -24,59 +24,60 @@
|
|
|
24
24
|
class="button-overlay absolute h-full top-0 left-0 z-10 rounded-md w-full"
|
|
25
25
|
:class="overlayClasses"
|
|
26
26
|
/>
|
|
27
|
-
<
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
>
|
|
31
|
-
<!-- Loader -->
|
|
32
|
-
<UiLoader
|
|
33
|
-
v-if="loading"
|
|
34
|
-
:size="size"
|
|
35
|
-
:class="{ 'mr-2': hasLabel }"
|
|
36
|
-
/>
|
|
37
|
-
|
|
38
|
-
<!-- Content -->
|
|
39
|
-
<template
|
|
40
|
-
v-else-if="showIconLeft"
|
|
27
|
+
<UiTooltip :content="typeof tooltip === 'string' ? tooltip : tooltip?.content">
|
|
28
|
+
<div
|
|
29
|
+
class="button-wrapper flex items-center whitespace-nowrap h-full relative z-20"
|
|
41
30
|
>
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
31
|
+
<!-- Loader -->
|
|
32
|
+
<UiLoader
|
|
33
|
+
v-if="loading"
|
|
34
|
+
:size="size"
|
|
35
|
+
:class="{ 'mr-2': hasLabel }"
|
|
36
|
+
/>
|
|
37
|
+
|
|
38
|
+
<!-- Content -->
|
|
39
|
+
<template
|
|
40
|
+
v-else-if="showIconLeft"
|
|
46
41
|
>
|
|
47
|
-
<
|
|
48
|
-
|
|
42
|
+
<div
|
|
43
|
+
v-if="iconSlot"
|
|
44
|
+
:class="[iconSizeClasses, { 'mr-2': hasLabel }]"
|
|
45
|
+
class="shrink-0"
|
|
46
|
+
>
|
|
47
|
+
<RenderSlot
|
|
48
|
+
:vnodes="iconSlot"
|
|
49
|
+
/>
|
|
50
|
+
</div>
|
|
51
|
+
<div
|
|
52
|
+
v-else-if="imageSource"
|
|
53
|
+
:class="[iconSizeClasses, { 'mr-2': hasLabel }]"
|
|
54
|
+
class="shrink-0"
|
|
55
|
+
>
|
|
56
|
+
<img
|
|
57
|
+
:src="imageSource"
|
|
58
|
+
:alt="`${labelFinal} button image`"
|
|
59
|
+
>
|
|
60
|
+
</div>
|
|
61
|
+
<div
|
|
62
|
+
v-else-if="icon"
|
|
63
|
+
:class="[icon, iconSizeClasses, { 'mr-2': hasLabel }]"
|
|
64
|
+
class="shrink-0"
|
|
49
65
|
/>
|
|
50
|
-
</
|
|
51
|
-
<
|
|
52
|
-
|
|
53
|
-
:class="[iconSizeClasses, { 'mr-2': hasLabel }]"
|
|
54
|
-
class="shrink-0"
|
|
66
|
+
</template>
|
|
67
|
+
<span
|
|
68
|
+
class="content w-full"
|
|
55
69
|
>
|
|
56
|
-
<
|
|
57
|
-
:
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
</div>
|
|
70
|
+
<RenderSlot
|
|
71
|
+
:vnodes="defaultSlot"
|
|
72
|
+
/>
|
|
73
|
+
</span>
|
|
61
74
|
<div
|
|
62
|
-
v-
|
|
63
|
-
:class="[
|
|
75
|
+
v-if="iconRight"
|
|
76
|
+
:class="[iconRight, iconSizeClasses, { 'ml-2': hasLabel }]"
|
|
64
77
|
class="shrink-0"
|
|
65
78
|
/>
|
|
66
|
-
</
|
|
67
|
-
|
|
68
|
-
class="content w-full"
|
|
69
|
-
>
|
|
70
|
-
<RenderSlot
|
|
71
|
-
:vnodes="defaultSlot"
|
|
72
|
-
/>
|
|
73
|
-
</span>
|
|
74
|
-
<div
|
|
75
|
-
v-if="iconRight"
|
|
76
|
-
:class="[iconRight, iconSizeClasses, { 'ml-2': hasLabel }]"
|
|
77
|
-
class="shrink-0"
|
|
78
|
-
/>
|
|
79
|
-
</div>
|
|
79
|
+
</div>
|
|
80
|
+
</UiTooltip>
|
|
80
81
|
</component>
|
|
81
82
|
</template>
|
|
82
83
|
|
|
@@ -86,6 +87,7 @@ import { useSlotsAsComponents, RenderSlot } from '../../utils/slot'
|
|
|
86
87
|
import { SIZES, type Size } from '../../constants'
|
|
87
88
|
import { useButtonStyles, BUTTON_COLORS, type ButtonVariant, type ButtonColor } from '../../composables/useButtonStyles'
|
|
88
89
|
import UiLoader from '../UiLoader/UiLoader.vue'
|
|
90
|
+
import UiTooltip from '../UiTooltip/UiTooltip.vue'
|
|
89
91
|
import type { RouteLocationRaw } from 'vue-router'
|
|
90
92
|
|
|
91
93
|
export type Variant = ButtonVariant
|
|
@@ -96,6 +98,7 @@ export default {
|
|
|
96
98
|
components: {
|
|
97
99
|
RenderSlot,
|
|
98
100
|
UiLoader,
|
|
101
|
+
UiTooltip,
|
|
99
102
|
},
|
|
100
103
|
|
|
101
104
|
props: {
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
2
|
import { computed } from 'vue'
|
|
3
3
|
import { Label, useId } from 'reka-ui'
|
|
4
|
+
import UiTooltip from '../UiTooltip/UiTooltip.vue'
|
|
4
5
|
|
|
5
6
|
interface Props {
|
|
6
7
|
id?: string
|
|
@@ -48,13 +49,14 @@ defineExpose({ fieldId })
|
|
|
48
49
|
>
|
|
49
50
|
{{ label }}{{ required ? '*' : '' }}
|
|
50
51
|
</Label>
|
|
51
|
-
<
|
|
52
|
+
<UiTooltip
|
|
52
53
|
v-if="labelInfo"
|
|
53
|
-
|
|
54
|
-
class="flex items-center justify-center size-4 text-text-neutral-secondary cursor-help"
|
|
54
|
+
:content="labelInfo"
|
|
55
55
|
>
|
|
56
|
-
<
|
|
57
|
-
|
|
56
|
+
<div class="flex items-center justify-center size-4 text-text-neutral-secondary cursor-help">
|
|
57
|
+
<span class="i-ls-info size-4" />
|
|
58
|
+
</div>
|
|
59
|
+
</UiTooltip>
|
|
58
60
|
</div>
|
|
59
61
|
|
|
60
62
|
<!-- Input -->
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
import { computed, ref, watch, nextTick } from 'vue'
|
|
3
3
|
import { Label, useId } from 'reka-ui'
|
|
4
4
|
import type { CheckboxSize } from './types'
|
|
5
|
+
import UiTooltip from '../UiTooltip/UiTooltip.vue'
|
|
5
6
|
|
|
6
7
|
interface UiInputCheckboxProps {
|
|
7
8
|
/**
|
|
@@ -390,18 +391,21 @@ defineExpose({
|
|
|
390
391
|
</Label>
|
|
391
392
|
|
|
392
393
|
<!-- icon -->
|
|
393
|
-
<
|
|
394
|
+
<UiTooltip
|
|
394
395
|
v-if="labelInfo"
|
|
395
|
-
|
|
396
|
-
class="flex items-center justify-center size-4 text-text-neutral-primary cursor-help shrink-0"
|
|
397
|
-
:class="{
|
|
398
|
-
'text-text-neutral-base': !error && !isChecked,
|
|
399
|
-
'text-text-danger-base': error,
|
|
400
|
-
'text-text-primary-base': isChecked && !error,
|
|
401
|
-
}"
|
|
396
|
+
:content="labelInfo"
|
|
402
397
|
>
|
|
403
|
-
<
|
|
404
|
-
|
|
398
|
+
<div
|
|
399
|
+
class="flex items-center justify-center size-4 text-text-neutral-primary cursor-help shrink-0"
|
|
400
|
+
:class="{
|
|
401
|
+
'text-text-neutral-base': !error && !isChecked,
|
|
402
|
+
'text-text-danger-base': error,
|
|
403
|
+
'text-text-primary-base': isChecked && !error,
|
|
404
|
+
}"
|
|
405
|
+
>
|
|
406
|
+
<span class="i-ls-info-fill size-4" />
|
|
407
|
+
</div>
|
|
408
|
+
</UiTooltip>
|
|
405
409
|
</div>
|
|
406
410
|
|
|
407
411
|
<span
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
2
|
import type { HTMLAttributes } from 'vue'
|
|
3
|
+
import UiTooltip from '../UiTooltip/UiTooltip.vue'
|
|
3
4
|
|
|
4
5
|
interface Props {
|
|
5
6
|
label?: string | null
|
|
@@ -21,14 +22,15 @@ withDefaults(defineProps<Props>(), {
|
|
|
21
22
|
|
|
22
23
|
<template>
|
|
23
24
|
<div class="mb-1 flex items-center text-content-legends-bold-md text-text-neutral-tertiary">
|
|
24
|
-
<
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
25
|
+
<UiTooltip :content="tooltip">
|
|
26
|
+
<label
|
|
27
|
+
:for="forId"
|
|
28
|
+
:class="labelClass"
|
|
29
|
+
:style="{
|
|
30
|
+
color: labelColor,
|
|
31
|
+
}"
|
|
32
|
+
v-html="required ? `${label}*` : label"
|
|
33
|
+
/>
|
|
34
|
+
</UiTooltip>
|
|
33
35
|
</div>
|
|
34
36
|
</template>
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
2
|
import { computed, onBeforeUnmount, onMounted, ref, watch, getCurrentInstance, type Slot } from 'vue'
|
|
3
3
|
import { BREAKPOINT_LG } from '../../utils/breakpoints'
|
|
4
|
+
import { useResponsive } from '../../utils/responsive'
|
|
4
5
|
import useThemeAndModes from '../../composables/useThemeAndModes'
|
|
5
6
|
import { useMobileMenu } from '../../composables/useMobileMenu'
|
|
6
7
|
import UiLoader from '../UiLoader/UiLoader.vue'
|
|
@@ -83,7 +84,9 @@ const app = getCurrentInstance()
|
|
|
83
84
|
if (!app) {
|
|
84
85
|
throw new Error('No current instance found')
|
|
85
86
|
}
|
|
86
|
-
|
|
87
|
+
// Prefer the app-installed $responsive (it may carry custom flags); fall
|
|
88
|
+
// back to the shared singleton so no plugin registration is required.
|
|
89
|
+
const responsive = computed(() => app.appContext.config.globalProperties.$responsive ?? useResponsive())
|
|
87
90
|
const displayLeftSlot = computed(() => responsive.value.width >= BREAKPOINT_LG)
|
|
88
91
|
|
|
89
92
|
const UiPage = ref<HTMLElement | undefined>()
|
|
@@ -1,16 +1,68 @@
|
|
|
1
1
|
<!-- UiTooltip.vue -->
|
|
2
2
|
<script setup lang="ts">
|
|
3
|
-
import
|
|
3
|
+
import { computed, getCurrentInstance } from 'vue'
|
|
4
|
+
import type { TooltipContentProps, TooltipRootEmits, TooltipRootProps } from 'reka-ui'
|
|
4
5
|
import { TooltipRoot, useForwardPropsEmits } from 'reka-ui'
|
|
6
|
+
import { reactiveOmit } from '@vueuse/core'
|
|
7
|
+
import UiTooltipProvider from './UiTooltipProvider.vue'
|
|
8
|
+
import UiTooltipTrigger from './UiTooltipTrigger.vue'
|
|
9
|
+
import UiTooltipContent from './UiTooltipContent.vue'
|
|
5
10
|
|
|
6
|
-
|
|
11
|
+
export interface UiTooltipProps extends TooltipRootProps {
|
|
12
|
+
/**
|
|
13
|
+
* Simple mode: the tooltip text. When `:content` is bound, UiTooltip is
|
|
14
|
+
* fully self-contained — the default slot becomes the trigger (merged
|
|
15
|
+
* via as-child) and no surrounding UiTooltipProvider is required:
|
|
16
|
+
*
|
|
17
|
+
* <UiTooltip content="Delete this event"><UiButton … /></UiTooltip>
|
|
18
|
+
*
|
|
19
|
+
* A bound-but-empty content renders the slot untouched (no tooltip, no
|
|
20
|
+
* overhead) — so wrappers can always bind an optional string.
|
|
21
|
+
*
|
|
22
|
+
* When `:content` is NOT bound, UiTooltip is the composable root: use it
|
|
23
|
+
* inside a UiTooltipProvider with UiTooltipTrigger/UiTooltipContent
|
|
24
|
+
* children.
|
|
25
|
+
*/
|
|
26
|
+
content?: string | null
|
|
27
|
+
/** Simple mode only: which side the tooltip appears on. */
|
|
28
|
+
side?: TooltipContentProps['side']
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
defineOptions({ inheritAttrs: false })
|
|
32
|
+
|
|
33
|
+
const props = defineProps<UiTooltipProps>()
|
|
7
34
|
const emits = defineEmits<TooltipRootEmits>()
|
|
8
35
|
|
|
9
|
-
|
|
36
|
+
// Simple vs composed mode is decided by the BINDING, not the value:
|
|
37
|
+
// reka's TooltipRoot requires a provider, so the composed branch must only
|
|
38
|
+
// render when the caller actually composes (no `content` bound).
|
|
39
|
+
const hasContentBinding = computed(() => {
|
|
40
|
+
const vnodeProps = getCurrentInstance()?.vnode.props
|
|
41
|
+
return !!vnodeProps && 'content' in vnodeProps
|
|
42
|
+
})
|
|
43
|
+
|
|
44
|
+
// content/side are ours — only forward the reka root props
|
|
45
|
+
const forwarded = useForwardPropsEmits(reactiveOmit(props, 'content', 'side'), emits)
|
|
10
46
|
</script>
|
|
11
47
|
|
|
12
48
|
<template>
|
|
13
|
-
<
|
|
49
|
+
<template v-if="hasContentBinding">
|
|
50
|
+
<UiTooltipProvider v-if="content">
|
|
51
|
+
<TooltipRoot v-bind="{ ...forwarded, ...$attrs }">
|
|
52
|
+
<UiTooltipTrigger as-child>
|
|
53
|
+
<slot />
|
|
54
|
+
</UiTooltipTrigger>
|
|
55
|
+
<UiTooltipContent :side="side">
|
|
56
|
+
{{ content }}
|
|
57
|
+
</UiTooltipContent>
|
|
58
|
+
</TooltipRoot>
|
|
59
|
+
</UiTooltipProvider>
|
|
60
|
+
<slot v-else />
|
|
61
|
+
</template>
|
|
62
|
+
<TooltipRoot
|
|
63
|
+
v-else
|
|
64
|
+
v-bind="{ ...forwarded, ...$attrs }"
|
|
65
|
+
>
|
|
14
66
|
<slot />
|
|
15
67
|
</TooltipRoot>
|
|
16
68
|
</template>
|