@livestorm/ui-vue 2.0.0 → 2.0.1
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 +2 -12
- package/dist/{UiTimelineItem.vue_vue_type_style_index_0_lang-DOLFRKcb.mjs → UiTimelineItem.vue_vue_type_style_index_0_lang-BN5208td.mjs} +1226 -1339
- package/dist/a11y--y7uaY4j.mjs +217 -0
- package/dist/components.mjs +73 -72
- package/dist/index.mjs +28 -27
- 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/ui-vue.css +1 -1
- package/dist/utils.mjs +117 -82
- package/package.json +3 -4
- 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/UiTooltip/UiTooltip.vue +56 -4
- package/src/utils/clipboard.js +80 -13
- package/dist/a11y-DIdrv5Mn.mjs +0 -66
|
@@ -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,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>
|
package/src/utils/clipboard.js
CHANGED
|
@@ -1,7 +1,83 @@
|
|
|
1
|
-
import { computed } from 'vue'
|
|
1
|
+
import { computed, createApp, h } from 'vue'
|
|
2
2
|
import { useClipboard, usePermission } from '@vueuse/core'
|
|
3
3
|
import i18n from 'i18next'
|
|
4
|
-
|
|
4
|
+
|
|
5
|
+
import UiTooltipProvider from '../components/UiTooltip/UiTooltipProvider.vue'
|
|
6
|
+
import UiTooltip from '../components/UiTooltip/UiTooltip.vue'
|
|
7
|
+
import UiTooltipTrigger from '../components/UiTooltip/UiTooltipTrigger.vue'
|
|
8
|
+
import UiTooltipContent from '../components/UiTooltip/UiTooltipContent.vue'
|
|
9
|
+
|
|
10
|
+
let hideActiveFlash = null
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Transient "Copied!" flash above an element, rendered with the design
|
|
14
|
+
* system's own UiTooltip family so the bubble, arrow and positioning stay
|
|
15
|
+
* identical to every other tooltip. The copy() API is imperative and
|
|
16
|
+
* receives a raw DOM element, so the tooltip cannot wrap the caller's
|
|
17
|
+
* markup: instead a throwaway Vue app drives a controlled-open tooltip
|
|
18
|
+
* whose invisible trigger is overlaid on the element's current rect.
|
|
19
|
+
*/
|
|
20
|
+
function flashTooltip (el, text) {
|
|
21
|
+
// A copy triggered before the previous flash ended replaces it instead of
|
|
22
|
+
// stacking a second bubble on top.
|
|
23
|
+
if (hideActiveFlash) {
|
|
24
|
+
hideActiveFlash()
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const rect = el.getBoundingClientRect()
|
|
28
|
+
const host = document.createElement('div')
|
|
29
|
+
document.body.appendChild(host)
|
|
30
|
+
|
|
31
|
+
const app = createApp({
|
|
32
|
+
render: () => h(UiTooltipProvider, null, () =>
|
|
33
|
+
h(UiTooltip, { open: true, delayDuration: 0 }, () => [
|
|
34
|
+
h(UiTooltipTrigger, { asChild: true }, () =>
|
|
35
|
+
h('span', {
|
|
36
|
+
'aria-hidden': 'true',
|
|
37
|
+
style: {
|
|
38
|
+
position: 'fixed',
|
|
39
|
+
left: `${rect.left}px`,
|
|
40
|
+
top: `${rect.top}px`,
|
|
41
|
+
width: `${rect.width}px`,
|
|
42
|
+
height: `${rect.height}px`,
|
|
43
|
+
pointerEvents: 'none',
|
|
44
|
+
},
|
|
45
|
+
})),
|
|
46
|
+
h(UiTooltipContent, { side: 'top' }, () => text),
|
|
47
|
+
// The bubble lives in reka's portal where attrs like role are not
|
|
48
|
+
// forwardable; announce the feedback from a visually hidden live
|
|
49
|
+
// region instead so screen readers hear the copy confirmation.
|
|
50
|
+
h('span', {
|
|
51
|
+
role: 'status',
|
|
52
|
+
style: {
|
|
53
|
+
position: 'absolute',
|
|
54
|
+
width: '1px',
|
|
55
|
+
height: '1px',
|
|
56
|
+
overflow: 'hidden',
|
|
57
|
+
clipPath: 'inset(50%)',
|
|
58
|
+
},
|
|
59
|
+
}, text),
|
|
60
|
+
])),
|
|
61
|
+
})
|
|
62
|
+
app.mount(host)
|
|
63
|
+
|
|
64
|
+
// Idempotent: the replace-guard above may spend a flash's hide before its
|
|
65
|
+
// own copy() timer fires it a second time.
|
|
66
|
+
let hidden = false
|
|
67
|
+
const hide = () => {
|
|
68
|
+
if (hidden) {
|
|
69
|
+
return
|
|
70
|
+
}
|
|
71
|
+
hidden = true
|
|
72
|
+
if (hideActiveFlash === hide) {
|
|
73
|
+
hideActiveFlash = null
|
|
74
|
+
}
|
|
75
|
+
app.unmount()
|
|
76
|
+
host.remove()
|
|
77
|
+
}
|
|
78
|
+
hideActiveFlash = hide
|
|
79
|
+
return hide
|
|
80
|
+
}
|
|
5
81
|
|
|
6
82
|
export function useCopyToClipboard () {
|
|
7
83
|
const { isSupported, copy: _copy } = useClipboard()
|
|
@@ -14,19 +90,10 @@ export function useCopyToClipboard () {
|
|
|
14
90
|
|
|
15
91
|
if (el) {
|
|
16
92
|
el.classList.add('copied')
|
|
17
|
-
const
|
|
18
|
-
triggers: [],
|
|
19
|
-
content: tooltipText,
|
|
20
|
-
delay: 0,
|
|
21
|
-
})
|
|
22
|
-
tooltip.show()
|
|
93
|
+
const hideTooltip = flashTooltip(el, tooltipText)
|
|
23
94
|
setTimeout(() => {
|
|
24
95
|
el.classList.remove('copied')
|
|
25
|
-
|
|
26
|
-
// Transition
|
|
27
|
-
setTimeout(() => {
|
|
28
|
-
destroyTooltip(el)
|
|
29
|
-
}, 400)
|
|
96
|
+
hideTooltip()
|
|
30
97
|
}, 600)
|
|
31
98
|
}
|
|
32
99
|
}
|
package/dist/a11y-DIdrv5Mn.mjs
DELETED
|
@@ -1,66 +0,0 @@
|
|
|
1
|
-
import { ref as k, computed as u, watch as r, onUnmounted as S, getCurrentInstance as y, onMounted as g, nextTick as x } from "vue";
|
|
2
|
-
import { onKeyDown as w } from "@vueuse/core";
|
|
3
|
-
let T = 0;
|
|
4
|
-
const a = k([]), b = u(() => a.value.length > 0);
|
|
5
|
-
function A(o, s) {
|
|
6
|
-
const n = T++, t = u(() => a.value.findIndex((e) => e.id === n));
|
|
7
|
-
function f() {
|
|
8
|
-
a.value.some((e) => e.id === n) || a.value.push({
|
|
9
|
-
id: n,
|
|
10
|
-
type: s
|
|
11
|
-
});
|
|
12
|
-
}
|
|
13
|
-
function i() {
|
|
14
|
-
t.value !== -1 && a.value.splice(t.value, 1);
|
|
15
|
-
}
|
|
16
|
-
r(o, (e) => {
|
|
17
|
-
e ? f() : i();
|
|
18
|
-
}, {
|
|
19
|
-
immediate: !0
|
|
20
|
-
}), S(() => {
|
|
21
|
-
i();
|
|
22
|
-
});
|
|
23
|
-
const d = u(() => n === a.value[a.value.length - 1]?.id), m = u(() => t.value > 0), c = u(() => a.value.filter((e) => e.type === s)), p = u(() => c.value.findIndex((e) => e.id === n) < c.value.length - 1), h = u(() => c.value.findIndex((e) => e.id === n) > 0), l = [];
|
|
24
|
-
function v(e) {
|
|
25
|
-
l.push(e);
|
|
26
|
-
}
|
|
27
|
-
return w("Escape", () => {
|
|
28
|
-
d.value && l.forEach((e) => e());
|
|
29
|
-
}), {
|
|
30
|
-
isActiveStacked: d,
|
|
31
|
-
hasStackedBehind: m,
|
|
32
|
-
isBehindSameType: p,
|
|
33
|
-
hasStackedSameTypeBehind: h,
|
|
34
|
-
stackIndex: t,
|
|
35
|
-
onClose: v
|
|
36
|
-
};
|
|
37
|
-
}
|
|
38
|
-
typeof document < "u" && r(b, (o) => {
|
|
39
|
-
o ? document.documentElement.classList.add("overflow-hidden") : document.documentElement.classList.remove("overflow-hidden");
|
|
40
|
-
}, {
|
|
41
|
-
immediate: !0
|
|
42
|
-
});
|
|
43
|
-
function C() {
|
|
44
|
-
const o = y();
|
|
45
|
-
let s = null;
|
|
46
|
-
g(async () => {
|
|
47
|
-
await x();
|
|
48
|
-
const n = o.proxy.$el;
|
|
49
|
-
if (!n?.querySelector) return;
|
|
50
|
-
let t = n.querySelector("[data-stackview-autofocus]");
|
|
51
|
-
t ? ["INPUT", "A", "BUTTON", "TEXTAREA"].includes(t.tagName) || (t = t.querySelector("input, a, button, textarea") || t) : (t = n.querySelector('button[type="submit"]'), console.warn(
|
|
52
|
-
"%cDEV",
|
|
53
|
-
"background: red; color: white; padding: 2px 4px; border-radius: 3px; font-weight: bold;",
|
|
54
|
-
"A element should have the data-stackview-autofocus attribute in this component. Fallback to submit button."
|
|
55
|
-
)), clearTimeout(s), s = setTimeout(() => t?.focus(), 150);
|
|
56
|
-
});
|
|
57
|
-
}
|
|
58
|
-
function q(o) {
|
|
59
|
-
o.preventDefault(), (o.target.closest('[role="menu"]') || o.target).querySelector("a")?.focus();
|
|
60
|
-
}
|
|
61
|
-
export {
|
|
62
|
-
C as a,
|
|
63
|
-
q as f,
|
|
64
|
-
b as h,
|
|
65
|
-
A as u
|
|
66
|
-
};
|