daisy-ui-kit 5.0.0-pre.9 → 5.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 +277 -48
- package/app/components/Accordion.vue +8 -5
- package/app/components/Alert.vue +2 -1
- package/app/components/Avatar.vue +10 -7
- package/app/components/AvatarGroup.vue +6 -2
- package/app/components/Badge.vue +19 -1
- package/app/components/Button.vue +66 -46
- package/app/components/Calendar.vue +151 -42
- package/app/components/CalendarInput.vue +229 -130
- package/app/components/CalendarSkeleton.vue +51 -10
- package/app/components/Card.vue +20 -2
- package/app/components/CardActions.vue +1 -1
- package/app/components/CardBody.vue +1 -1
- package/app/components/CardTitle.vue +1 -1
- package/app/components/Carousel.vue +2 -1
- package/app/components/Chat.vue +6 -1
- package/app/components/Checkbox.vue +1 -1
- package/app/components/Collapse.vue +38 -5
- package/app/components/CollapseTitle.vue +11 -1
- package/app/components/Countdown.vue +3 -3
- package/app/components/CountdownTimers.vue +4 -7
- package/app/components/Counter.vue +14 -3
- package/app/components/DaisyLink.vue +33 -15
- package/app/components/Dock.vue +5 -6
- package/app/components/DockItem.vue +5 -3
- package/app/components/Drawer.vue +15 -12
- package/app/components/DrawerContent.vue +9 -6
- package/app/components/DrawerSide.vue +9 -6
- package/app/components/Dropdown.vue +61 -50
- package/app/components/DropdownButton.vue +11 -4
- package/app/components/DropdownContent.vue +90 -20
- package/app/components/DropdownTarget.vue +10 -3
- package/app/components/Fab.vue +16 -0
- package/app/components/FabClose.vue +18 -0
- package/app/components/FabMainAction.vue +5 -0
- package/app/components/FabTrigger.vue +117 -0
- package/app/components/Fieldset.vue +5 -4
- package/app/components/FileInput.vue +1 -1
- package/app/components/Filter.vue +45 -38
- package/app/components/Flex.vue +8 -1
- package/app/components/FlexItem.vue +30 -27
- package/app/components/Footer.vue +16 -12
- package/app/components/FooterTitle.vue +8 -5
- package/app/components/Hero.vue +9 -6
- package/app/components/HeroContent.vue +9 -6
- package/app/components/Hover3D.vue +22 -0
- package/app/components/HoverGallery.vue +11 -0
- package/app/components/Indicator.vue +12 -5
- package/app/components/IndicatorItem.vue +21 -14
- package/app/components/Input.vue +44 -47
- package/app/components/Kbd.vue +2 -1
- package/app/components/Label.vue +32 -29
- package/app/components/MenuExpand.vue +5 -13
- package/app/components/MenuExpandToggle.vue +7 -1
- package/app/components/MenuItem.vue +6 -4
- package/app/components/Modal.vue +23 -17
- package/app/components/Progress.vue +13 -1
- package/app/components/Prose.vue +7 -2
- package/app/components/RadialProgress.vue +8 -8
- package/app/components/Radio.vue +1 -1
- package/app/components/RadioGroup.vue +2 -2
- package/app/components/Range.vue +186 -46
- package/app/components/RangeMeasure.vue +33 -30
- package/app/components/RangeMeasureTick.vue +4 -5
- package/app/components/Rating.vue +70 -53
- package/app/components/Select.vue +44 -47
- package/app/components/SkeletonText.vue +11 -0
- package/app/components/Stack.vue +5 -0
- package/app/components/Steps.vue +7 -2
- package/app/components/Swap.vue +4 -10
- package/app/components/Tab.vue +23 -5
- package/app/components/Text.vue +47 -23
- package/app/components/TextArea.vue +75 -30
- package/app/components/TextRotate.vue +24 -0
- package/app/components/ThemeController.vue +3 -4
- package/app/components/ThemeProvider.vue +47 -32
- package/app/components/TimelineLine.vue +1 -1
- package/app/components/TimelineStart.vue +2 -1
- package/app/components/Toast.vue +3 -8
- package/app/components/Toggle.vue +2 -2
- package/app/components/Tooltip.vue +111 -21
- package/app/components/TooltipContent.vue +279 -1
- package/app/components/TooltipTarget.vue +20 -0
- package/app/composables/__tests__/use-calendar.test.ts +239 -0
- package/app/composables/use-calendar.ts +288 -0
- package/app/composables/use-daisy-theme.ts +140 -0
- package/app/composables/use-toast.ts +345 -0
- package/app/composables/useSearch.ts +22 -0
- package/app/utils/drawer-utils.ts +15 -13
- package/app/utils/position-area.ts +40 -0
- package/nuxt.d.ts +13 -0
- package/nuxt.js +12 -9
- package/package.json +34 -17
- package/app/utils/random-string.ts +0 -19
|
@@ -19,7 +19,12 @@ function cssVarToCamel(str: string): string {
|
|
|
19
19
|
function dataAttrToCamel(str: string): string {
|
|
20
20
|
return str.replace(/-([a-z0-9])/g, (_, c: string) => c.toUpperCase())
|
|
21
21
|
}
|
|
22
|
-
function parseThemeString(themeString?: string): {
|
|
22
|
+
function parseThemeString(themeString?: string): {
|
|
23
|
+
style: Record<string, string>
|
|
24
|
+
dataAttrs: Record<string, string>
|
|
25
|
+
dataTheme: string | undefined
|
|
26
|
+
allAttrs: Record<string, string>
|
|
27
|
+
} {
|
|
23
28
|
if (!themeString) {
|
|
24
29
|
return { style: {}, dataAttrs: {}, dataTheme: undefined, allAttrs: {} }
|
|
25
30
|
}
|
|
@@ -33,10 +38,14 @@ function parseThemeString(themeString?: string): { style: Record<string, string>
|
|
|
33
38
|
const allAttrs: Record<string, string> = {}
|
|
34
39
|
|
|
35
40
|
// Remove @plugin ... { and closing } if present
|
|
36
|
-
const str = themeString
|
|
41
|
+
const str = themeString
|
|
42
|
+
.trim()
|
|
43
|
+
.replace(/^@plugin[^{}]*\{/, '')
|
|
44
|
+
.replace(/\}$/, '')
|
|
45
|
+
.trim()
|
|
37
46
|
|
|
38
47
|
// works with or without newline characters
|
|
39
|
-
str.split(/[\n;]/).forEach(
|
|
48
|
+
str.split(/[\n;]/).forEach(line => {
|
|
40
49
|
const trimmed = line.trim()
|
|
41
50
|
if (!trimmed || trimmed.startsWith('//')) {
|
|
42
51
|
return
|
|
@@ -57,12 +66,10 @@ function parseThemeString(themeString?: string): { style: Record<string, string>
|
|
|
57
66
|
if (varNamePattern.test(key)) {
|
|
58
67
|
style[key] = value
|
|
59
68
|
allAttrs[cssVarToCamel(key)] = value
|
|
60
|
-
}
|
|
61
|
-
else if (key === 'name') {
|
|
69
|
+
} else if (key === 'name') {
|
|
62
70
|
dataTheme = value.replace(/"/g, '')
|
|
63
71
|
allAttrs.dataTheme = dataTheme
|
|
64
|
-
}
|
|
65
|
-
else if (attrNamePattern.test(key)) {
|
|
72
|
+
} else if (attrNamePattern.test(key)) {
|
|
66
73
|
const attrKey = `data-${key.replace(/[^\w-]/g, '')}`
|
|
67
74
|
const attrVal = value.replace(/"/g, '')
|
|
68
75
|
dataAttrs[attrKey] = attrVal
|
|
@@ -78,7 +85,7 @@ const parsed = computed(() => parseThemeString(props.dataTheme ?? props.cssVars)
|
|
|
78
85
|
|
|
79
86
|
const themeVars = ref<Record<string, string>>({})
|
|
80
87
|
let observers: MutationObserver[] = []
|
|
81
|
-
let themeControllerInputs: Array<{ el: HTMLInputElement
|
|
88
|
+
let themeControllerInputs: Array<{ el: HTMLInputElement; listener: () => void }> = []
|
|
82
89
|
let themeControllerDomObserver: MutationObserver | null = null
|
|
83
90
|
|
|
84
91
|
// Converts a themeAttrs object to a DaisyUI theme string
|
|
@@ -88,15 +95,16 @@ function toThemeString(attrs: Record<string, string>, opts?: { asPlugin?: boolea
|
|
|
88
95
|
for (const [key, value] of Object.entries(attrs)) {
|
|
89
96
|
if (key === 'dataTheme') {
|
|
90
97
|
lines.push(`name: "${value}";`)
|
|
91
|
-
}
|
|
92
|
-
else if (key.startsWith('data')) {
|
|
98
|
+
} else if (key.startsWith('data')) {
|
|
93
99
|
// Convert dataFooBar -> foo-bar
|
|
94
|
-
const attr = key
|
|
100
|
+
const attr = key
|
|
101
|
+
.slice(4)
|
|
102
|
+
.replace(/([A-Z])/g, '-$1')
|
|
103
|
+
.toLowerCase()
|
|
95
104
|
if (attr !== 'theme') {
|
|
96
105
|
lines.push(`${attr}: ${value};`)
|
|
97
106
|
}
|
|
98
|
-
}
|
|
99
|
-
else {
|
|
107
|
+
} else {
|
|
100
108
|
// Convert camelCase to --kebab-case
|
|
101
109
|
const cssVar = `--${key.replace(/([A-Z])/g, '-$1').toLowerCase()}`
|
|
102
110
|
lines.push(`${cssVar}: ${value};`)
|
|
@@ -207,8 +215,10 @@ function setupSnoop(rootEl: HTMLElement): void {
|
|
|
207
215
|
})
|
|
208
216
|
themeControllerInputs = []
|
|
209
217
|
// Find all matching inputs in the document
|
|
210
|
-
const inputs = Array.from(
|
|
211
|
-
|
|
218
|
+
const inputs = Array.from(
|
|
219
|
+
document.querySelectorAll('input[type="checkbox"].theme-controller, input[type="radio"].theme-controller'),
|
|
220
|
+
) as HTMLInputElement[]
|
|
221
|
+
inputs.forEach(el => {
|
|
212
222
|
const listener = () => updateVars()
|
|
213
223
|
el.addEventListener('change', listener)
|
|
214
224
|
themeControllerInputs.push({ el, listener })
|
|
@@ -226,9 +236,13 @@ function setupSnoop(rootEl: HTMLElement): void {
|
|
|
226
236
|
themeControllerDomObserver.observe(document.body, { childList: true, subtree: true })
|
|
227
237
|
|
|
228
238
|
// Watch for applied style changes (parsed.style)
|
|
229
|
-
watch(
|
|
230
|
-
|
|
231
|
-
|
|
239
|
+
watch(
|
|
240
|
+
() => parsed.value.style,
|
|
241
|
+
() => {
|
|
242
|
+
nextTick(() => updateVars())
|
|
243
|
+
},
|
|
244
|
+
{ immediate: false, deep: true },
|
|
245
|
+
)
|
|
232
246
|
updateVars()
|
|
233
247
|
}
|
|
234
248
|
|
|
@@ -236,21 +250,22 @@ const rootEl = ref<HTMLElement | null>(null)
|
|
|
236
250
|
|
|
237
251
|
const isClient = typeof window !== 'undefined' && typeof document !== 'undefined'
|
|
238
252
|
|
|
239
|
-
watch(
|
|
240
|
-
() => props.snoop,
|
|
241
|
-
() =>
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
}
|
|
253
|
+
watch(
|
|
254
|
+
[() => props.snoop, () => rootEl.value],
|
|
255
|
+
([snoop, el]) => {
|
|
256
|
+
if (isClient) {
|
|
257
|
+
// Clean up previous observers if any
|
|
258
|
+
observers.forEach((o: MutationObserver) => o.disconnect())
|
|
259
|
+
observers = []
|
|
260
|
+
if (snoop && el) {
|
|
261
|
+
nextTick(() => {
|
|
262
|
+
setupSnoop(el)
|
|
263
|
+
})
|
|
264
|
+
}
|
|
251
265
|
}
|
|
252
|
-
}
|
|
253
|
-
|
|
266
|
+
},
|
|
267
|
+
{ immediate: true },
|
|
268
|
+
)
|
|
254
269
|
|
|
255
270
|
onUnmounted(() => {
|
|
256
271
|
observers.forEach((o: MutationObserver) => o.disconnect())
|
package/app/components/Toast.vue
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
|
-
import type { Toast } from '
|
|
2
|
+
import type { Toast } from '../composables/use-toast'
|
|
3
3
|
import { computed } from 'vue'
|
|
4
|
-
import { useToast } from '
|
|
4
|
+
import { useToast } from '../composables/use-toast'
|
|
5
5
|
|
|
6
6
|
// Explicit slot typing (Vue 3.4+ / Volar)
|
|
7
7
|
interface ToastSlotProps {
|
|
@@ -61,12 +61,7 @@ const visibleToasts = computed(() => toasts.value ?? [])
|
|
|
61
61
|
'toast-bottom': props.bottom || props.vAlign === 'bottom',
|
|
62
62
|
}"
|
|
63
63
|
>
|
|
64
|
-
<slot
|
|
65
|
-
v-for="toast in visibleToasts"
|
|
66
|
-
:key="toast.id"
|
|
67
|
-
:toast="toast"
|
|
68
|
-
:remove-toast="removeToast"
|
|
69
|
-
/>
|
|
64
|
+
<slot v-for="toast in visibleToasts" :key="toast.id" :toast="toast" :remove-toast="removeToast" />
|
|
70
65
|
<slot />
|
|
71
66
|
</div>
|
|
72
67
|
</template>
|
|
@@ -40,7 +40,7 @@ const currentValue = computed({
|
|
|
40
40
|
class="toggle"
|
|
41
41
|
:disabled="disabled"
|
|
42
42
|
:class="{
|
|
43
|
-
|
|
43
|
+
validator,
|
|
44
44
|
'toggle-neutral': neutral || color === 'neutral',
|
|
45
45
|
'toggle-primary': primary || color === 'primary',
|
|
46
46
|
'toggle-secondary': secondary || color === 'secondary',
|
|
@@ -56,5 +56,5 @@ const currentValue = computed({
|
|
|
56
56
|
'toggle-xl': xl || size === 'xl',
|
|
57
57
|
'theme-controller': themeController,
|
|
58
58
|
}"
|
|
59
|
-
|
|
59
|
+
/>
|
|
60
60
|
</template>
|
|
@@ -1,30 +1,115 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
2
|
+
import { useId } from 'vue'
|
|
3
|
+
import { useElementHover } from '@vueuse/core'
|
|
4
|
+
import { computed, onMounted, provide, ref, watch } from 'vue'
|
|
5
|
+
|
|
6
|
+
const props = withDefaults(
|
|
7
|
+
defineProps<{
|
|
8
|
+
tip?: string | number
|
|
9
|
+
open?: boolean
|
|
10
|
+
|
|
11
|
+
color?: string
|
|
12
|
+
neutral?: boolean
|
|
13
|
+
primary?: boolean
|
|
14
|
+
secondary?: boolean
|
|
15
|
+
accent?: boolean
|
|
16
|
+
info?: boolean
|
|
17
|
+
success?: boolean
|
|
18
|
+
warning?: boolean
|
|
19
|
+
error?: boolean
|
|
20
|
+
|
|
21
|
+
position?: 'top' | 'right' | 'bottom' | 'left'
|
|
22
|
+
top?: boolean
|
|
23
|
+
right?: boolean
|
|
24
|
+
bottom?: boolean
|
|
25
|
+
left?: boolean
|
|
26
|
+
|
|
27
|
+
// Popover mode props
|
|
28
|
+
delayEnter?: number
|
|
29
|
+
delayLeave?: number
|
|
30
|
+
}>(),
|
|
31
|
+
{
|
|
32
|
+
delayEnter: 200,
|
|
33
|
+
delayLeave: 100,
|
|
34
|
+
},
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
// Detect if using popover mode (when TooltipTarget/TooltipContent are used)
|
|
38
|
+
const isPopoverMode = computed(() => {
|
|
39
|
+
return !props.tip
|
|
40
|
+
})
|
|
41
|
+
|
|
42
|
+
// Popover mode setup
|
|
43
|
+
const isOpen = defineModel('open', { default: false })
|
|
44
|
+
provide('isTooltipOpen', isOpen)
|
|
45
|
+
|
|
46
|
+
const uniqueId = useId()
|
|
47
|
+
const id = `tooltip-${uniqueId}`
|
|
48
|
+
provide('tooltipId', id)
|
|
49
|
+
|
|
50
|
+
// Compute placement for CSS anchor positioning
|
|
51
|
+
const placement = computed(() => {
|
|
52
|
+
if (props.top || props.position === 'top') return 'top'
|
|
53
|
+
if (props.right || props.position === 'right') return 'right'
|
|
54
|
+
if (props.left || props.position === 'left') return 'left'
|
|
55
|
+
return 'bottom' // default
|
|
56
|
+
})
|
|
57
|
+
provide('tooltipPlacement', placement)
|
|
58
|
+
|
|
59
|
+
// Provide color for TooltipContent
|
|
60
|
+
const color = computed(() => {
|
|
61
|
+
if (props.color) return props.color
|
|
62
|
+
if (props.neutral) return 'neutral'
|
|
63
|
+
if (props.primary) return 'primary'
|
|
64
|
+
if (props.secondary) return 'secondary'
|
|
65
|
+
if (props.accent) return 'accent'
|
|
66
|
+
if (props.info) return 'info'
|
|
67
|
+
if (props.success) return 'success'
|
|
68
|
+
if (props.warning) return 'warning'
|
|
69
|
+
if (props.error) return 'error'
|
|
70
|
+
return null
|
|
71
|
+
})
|
|
72
|
+
provide('tooltipColor', color)
|
|
73
|
+
|
|
74
|
+
// References
|
|
75
|
+
const targetEl = ref(null)
|
|
76
|
+
const contentEl = ref(null)
|
|
77
|
+
provide('targetEl', targetEl)
|
|
78
|
+
provide('contentEl', contentEl)
|
|
79
|
+
|
|
80
|
+
// Visibility utils
|
|
81
|
+
function open() {
|
|
82
|
+
isOpen.value = true
|
|
83
|
+
}
|
|
84
|
+
function close() {
|
|
85
|
+
isOpen.value = false
|
|
86
|
+
}
|
|
87
|
+
provide('openTooltip', open)
|
|
88
|
+
provide('closeTooltip', close)
|
|
89
|
+
|
|
90
|
+
const tooltipWrapper = ref(null)
|
|
91
|
+
|
|
92
|
+
onMounted(() => {
|
|
93
|
+
if (isPopoverMode.value) {
|
|
94
|
+
const hover = useElementHover(tooltipWrapper, {
|
|
95
|
+
delayLeave: props.delayLeave,
|
|
96
|
+
delayEnter: props.delayEnter,
|
|
97
|
+
})
|
|
98
|
+
|
|
99
|
+
watch(hover, newValue => {
|
|
100
|
+
isOpen.value = newValue
|
|
101
|
+
})
|
|
102
|
+
}
|
|
103
|
+
})
|
|
22
104
|
</script>
|
|
23
105
|
|
|
24
106
|
<template>
|
|
107
|
+
<!-- CSS-only mode (when tip prop is provided) -->
|
|
25
108
|
<div
|
|
109
|
+
v-if="!isPopoverMode"
|
|
26
110
|
:data-tip="tip"
|
|
27
|
-
class="tooltip"
|
|
111
|
+
class="tooltip"
|
|
112
|
+
:class="{
|
|
28
113
|
'tooltip-open': props.open,
|
|
29
114
|
|
|
30
115
|
'tooltip-top': props.top || props.position === 'top',
|
|
@@ -44,4 +129,9 @@ const props = defineProps<{
|
|
|
44
129
|
>
|
|
45
130
|
<slot />
|
|
46
131
|
</div>
|
|
132
|
+
|
|
133
|
+
<!-- Popover API mode (when using TooltipTarget/TooltipContent) -->
|
|
134
|
+
<div v-else ref="tooltipWrapper" class="tooltip-wrapper inline-block">
|
|
135
|
+
<slot />
|
|
136
|
+
</div>
|
|
47
137
|
</template>
|
|
@@ -1,5 +1,283 @@
|
|
|
1
|
+
<script setup>
|
|
2
|
+
import { computed, inject, ref, watch } from 'vue'
|
|
3
|
+
import { getPositionArea, getPositionFallbacks } from '../utils/position-area'
|
|
4
|
+
|
|
5
|
+
const id = inject('tooltipId', null)
|
|
6
|
+
const isOpen = inject('isTooltipOpen', ref(false))
|
|
7
|
+
const contentEl = inject('contentEl', ref(null))
|
|
8
|
+
const placement = inject('tooltipPlacement', ref('bottom'))
|
|
9
|
+
const color = inject('tooltipColor', ref(null))
|
|
10
|
+
|
|
11
|
+
// Check if we're in popover mode (inside a Tooltip wrapper with context)
|
|
12
|
+
const isPopoverMode = computed(() => id !== null)
|
|
13
|
+
|
|
14
|
+
// Compute CSS position-area value based on placement
|
|
15
|
+
const positionArea = computed(() => getPositionArea(placement.value))
|
|
16
|
+
const positionFallbacks = computed(() => getPositionFallbacks(placement.value))
|
|
17
|
+
|
|
18
|
+
// Color classes for the tooltip
|
|
19
|
+
const colorClass = computed(() => {
|
|
20
|
+
if (!color.value) return ''
|
|
21
|
+
const colorMap = {
|
|
22
|
+
neutral: 'tooltip-neutral',
|
|
23
|
+
primary: 'tooltip-primary',
|
|
24
|
+
secondary: 'tooltip-secondary',
|
|
25
|
+
accent: 'tooltip-accent',
|
|
26
|
+
info: 'tooltip-info',
|
|
27
|
+
success: 'tooltip-success',
|
|
28
|
+
warning: 'tooltip-warning',
|
|
29
|
+
error: 'tooltip-error',
|
|
30
|
+
}
|
|
31
|
+
return colorMap[color.value] || ''
|
|
32
|
+
})
|
|
33
|
+
|
|
34
|
+
// Sync popover state with isOpen model
|
|
35
|
+
watch(
|
|
36
|
+
isOpen,
|
|
37
|
+
newValue => {
|
|
38
|
+
if (!isPopoverMode.value || !contentEl.value) return
|
|
39
|
+
|
|
40
|
+
try {
|
|
41
|
+
const isPopoverOpen = contentEl.value.matches(':popover-open')
|
|
42
|
+
|
|
43
|
+
if (newValue && !isPopoverOpen) {
|
|
44
|
+
contentEl.value.showPopover()
|
|
45
|
+
} else if (!newValue && isPopoverOpen) {
|
|
46
|
+
contentEl.value.hidePopover()
|
|
47
|
+
}
|
|
48
|
+
} catch (e) {
|
|
49
|
+
console.warn('Popover API not supported:', e)
|
|
50
|
+
}
|
|
51
|
+
},
|
|
52
|
+
{ flush: 'post' },
|
|
53
|
+
)
|
|
54
|
+
|
|
55
|
+
// Listen to popover toggle events to sync back to isOpen model
|
|
56
|
+
function handleToggle(event) {
|
|
57
|
+
const newState = event.newState === 'open'
|
|
58
|
+
if (isOpen.value !== newState) {
|
|
59
|
+
isOpen.value = newState
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
</script>
|
|
63
|
+
|
|
1
64
|
<template>
|
|
2
|
-
|
|
65
|
+
<!-- Popover mode -->
|
|
66
|
+
<div
|
|
67
|
+
v-if="isPopoverMode"
|
|
68
|
+
:id="`${id}-content`"
|
|
69
|
+
ref="contentEl"
|
|
70
|
+
:anchor="id"
|
|
71
|
+
:data-placement="placement"
|
|
72
|
+
role="tooltip"
|
|
73
|
+
popover="manual"
|
|
74
|
+
class="tooltip-popover"
|
|
75
|
+
:class="colorClass"
|
|
76
|
+
:style="{
|
|
77
|
+
'position-anchor': `--${id}`,
|
|
78
|
+
'position-area': positionArea,
|
|
79
|
+
'position-try-fallbacks': positionFallbacks,
|
|
80
|
+
}"
|
|
81
|
+
@toggle="handleToggle"
|
|
82
|
+
>
|
|
83
|
+
<slot />
|
|
84
|
+
</div>
|
|
85
|
+
|
|
86
|
+
<!-- Simple mode (no Tooltip wrapper context) -->
|
|
87
|
+
<div v-else class="tooltip-content">
|
|
3
88
|
<slot />
|
|
4
89
|
</div>
|
|
5
90
|
</template>
|
|
91
|
+
|
|
92
|
+
<style>
|
|
93
|
+
@layer components {
|
|
94
|
+
.tooltip-popover[popover] {
|
|
95
|
+
border: none;
|
|
96
|
+
overflow: visible;
|
|
97
|
+
inset: auto;
|
|
98
|
+
margin: 0;
|
|
99
|
+
padding: 0.25rem 0.5rem;
|
|
100
|
+
font-size: 0.875rem;
|
|
101
|
+
line-height: 1.25rem;
|
|
102
|
+
border-radius: var(--radius-selector, 0.25rem);
|
|
103
|
+
background-color: var(--color-neutral);
|
|
104
|
+
color: var(--color-neutral-content);
|
|
105
|
+
max-width: 20rem;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
.tooltip-popover[popover]:popover-open {
|
|
109
|
+
position: fixed;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/* Arrow base styles */
|
|
113
|
+
.tooltip-popover[popover]::before {
|
|
114
|
+
content: '';
|
|
115
|
+
position: absolute;
|
|
116
|
+
border: 6px solid transparent;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/* Bottom placement - arrow points up */
|
|
120
|
+
.tooltip-popover[data-placement='bottom'] {
|
|
121
|
+
margin-top: 6px;
|
|
122
|
+
}
|
|
123
|
+
.tooltip-popover[data-placement='bottom']::before {
|
|
124
|
+
bottom: 100%;
|
|
125
|
+
left: 50%;
|
|
126
|
+
transform: translateX(-50%);
|
|
127
|
+
border-bottom-color: var(--color-neutral);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/* Top placement - arrow points down */
|
|
131
|
+
.tooltip-popover[data-placement='top'] {
|
|
132
|
+
margin-bottom: 6px;
|
|
133
|
+
}
|
|
134
|
+
.tooltip-popover[data-placement='top']::before {
|
|
135
|
+
top: 100%;
|
|
136
|
+
left: 50%;
|
|
137
|
+
transform: translateX(-50%);
|
|
138
|
+
border-top-color: var(--color-neutral);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/* Right placement - arrow points left */
|
|
142
|
+
.tooltip-popover[data-placement='right'] {
|
|
143
|
+
margin-left: 6px;
|
|
144
|
+
}
|
|
145
|
+
.tooltip-popover[data-placement='right']::before {
|
|
146
|
+
right: 100%;
|
|
147
|
+
top: 50%;
|
|
148
|
+
transform: translateY(-50%);
|
|
149
|
+
border-right-color: var(--color-neutral);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/* Left placement - arrow points right */
|
|
153
|
+
.tooltip-popover[data-placement='left'] {
|
|
154
|
+
margin-right: 6px;
|
|
155
|
+
}
|
|
156
|
+
.tooltip-popover[data-placement='left']::before {
|
|
157
|
+
left: 100%;
|
|
158
|
+
top: 50%;
|
|
159
|
+
transform: translateY(-50%);
|
|
160
|
+
border-left-color: var(--color-neutral);
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/* Color variants */
|
|
164
|
+
.tooltip-popover.tooltip-primary {
|
|
165
|
+
background-color: var(--color-primary);
|
|
166
|
+
color: var(--color-primary-content);
|
|
167
|
+
}
|
|
168
|
+
.tooltip-popover.tooltip-primary[data-placement='bottom']::before {
|
|
169
|
+
border-bottom-color: var(--color-primary);
|
|
170
|
+
}
|
|
171
|
+
.tooltip-popover.tooltip-primary[data-placement='top']::before {
|
|
172
|
+
border-top-color: var(--color-primary);
|
|
173
|
+
}
|
|
174
|
+
.tooltip-popover.tooltip-primary[data-placement='right']::before {
|
|
175
|
+
border-right-color: var(--color-primary);
|
|
176
|
+
}
|
|
177
|
+
.tooltip-popover.tooltip-primary[data-placement='left']::before {
|
|
178
|
+
border-left-color: var(--color-primary);
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
.tooltip-popover.tooltip-secondary {
|
|
182
|
+
background-color: var(--color-secondary);
|
|
183
|
+
color: var(--color-secondary-content);
|
|
184
|
+
}
|
|
185
|
+
.tooltip-popover.tooltip-secondary[data-placement='bottom']::before {
|
|
186
|
+
border-bottom-color: var(--color-secondary);
|
|
187
|
+
}
|
|
188
|
+
.tooltip-popover.tooltip-secondary[data-placement='top']::before {
|
|
189
|
+
border-top-color: var(--color-secondary);
|
|
190
|
+
}
|
|
191
|
+
.tooltip-popover.tooltip-secondary[data-placement='right']::before {
|
|
192
|
+
border-right-color: var(--color-secondary);
|
|
193
|
+
}
|
|
194
|
+
.tooltip-popover.tooltip-secondary[data-placement='left']::before {
|
|
195
|
+
border-left-color: var(--color-secondary);
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
.tooltip-popover.tooltip-accent {
|
|
199
|
+
background-color: var(--color-accent);
|
|
200
|
+
color: var(--color-accent-content);
|
|
201
|
+
}
|
|
202
|
+
.tooltip-popover.tooltip-accent[data-placement='bottom']::before {
|
|
203
|
+
border-bottom-color: var(--color-accent);
|
|
204
|
+
}
|
|
205
|
+
.tooltip-popover.tooltip-accent[data-placement='top']::before {
|
|
206
|
+
border-top-color: var(--color-accent);
|
|
207
|
+
}
|
|
208
|
+
.tooltip-popover.tooltip-accent[data-placement='right']::before {
|
|
209
|
+
border-right-color: var(--color-accent);
|
|
210
|
+
}
|
|
211
|
+
.tooltip-popover.tooltip-accent[data-placement='left']::before {
|
|
212
|
+
border-left-color: var(--color-accent);
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
.tooltip-popover.tooltip-info {
|
|
216
|
+
background-color: var(--color-info);
|
|
217
|
+
color: var(--color-info-content);
|
|
218
|
+
}
|
|
219
|
+
.tooltip-popover.tooltip-info[data-placement='bottom']::before {
|
|
220
|
+
border-bottom-color: var(--color-info);
|
|
221
|
+
}
|
|
222
|
+
.tooltip-popover.tooltip-info[data-placement='top']::before {
|
|
223
|
+
border-top-color: var(--color-info);
|
|
224
|
+
}
|
|
225
|
+
.tooltip-popover.tooltip-info[data-placement='right']::before {
|
|
226
|
+
border-right-color: var(--color-info);
|
|
227
|
+
}
|
|
228
|
+
.tooltip-popover.tooltip-info[data-placement='left']::before {
|
|
229
|
+
border-left-color: var(--color-info);
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
.tooltip-popover.tooltip-success {
|
|
233
|
+
background-color: var(--color-success);
|
|
234
|
+
color: var(--color-success-content);
|
|
235
|
+
}
|
|
236
|
+
.tooltip-popover.tooltip-success[data-placement='bottom']::before {
|
|
237
|
+
border-bottom-color: var(--color-success);
|
|
238
|
+
}
|
|
239
|
+
.tooltip-popover.tooltip-success[data-placement='top']::before {
|
|
240
|
+
border-top-color: var(--color-success);
|
|
241
|
+
}
|
|
242
|
+
.tooltip-popover.tooltip-success[data-placement='right']::before {
|
|
243
|
+
border-right-color: var(--color-success);
|
|
244
|
+
}
|
|
245
|
+
.tooltip-popover.tooltip-success[data-placement='left']::before {
|
|
246
|
+
border-left-color: var(--color-success);
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
.tooltip-popover.tooltip-warning {
|
|
250
|
+
background-color: var(--color-warning);
|
|
251
|
+
color: var(--color-warning-content);
|
|
252
|
+
}
|
|
253
|
+
.tooltip-popover.tooltip-warning[data-placement='bottom']::before {
|
|
254
|
+
border-bottom-color: var(--color-warning);
|
|
255
|
+
}
|
|
256
|
+
.tooltip-popover.tooltip-warning[data-placement='top']::before {
|
|
257
|
+
border-top-color: var(--color-warning);
|
|
258
|
+
}
|
|
259
|
+
.tooltip-popover.tooltip-warning[data-placement='right']::before {
|
|
260
|
+
border-right-color: var(--color-warning);
|
|
261
|
+
}
|
|
262
|
+
.tooltip-popover.tooltip-warning[data-placement='left']::before {
|
|
263
|
+
border-left-color: var(--color-warning);
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
.tooltip-popover.tooltip-error {
|
|
267
|
+
background-color: var(--color-error);
|
|
268
|
+
color: var(--color-error-content);
|
|
269
|
+
}
|
|
270
|
+
.tooltip-popover.tooltip-error[data-placement='bottom']::before {
|
|
271
|
+
border-bottom-color: var(--color-error);
|
|
272
|
+
}
|
|
273
|
+
.tooltip-popover.tooltip-error[data-placement='top']::before {
|
|
274
|
+
border-top-color: var(--color-error);
|
|
275
|
+
}
|
|
276
|
+
.tooltip-popover.tooltip-error[data-placement='right']::before {
|
|
277
|
+
border-right-color: var(--color-error);
|
|
278
|
+
}
|
|
279
|
+
.tooltip-popover.tooltip-error[data-placement='left']::before {
|
|
280
|
+
border-left-color: var(--color-error);
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
</style>
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
<script setup>
|
|
2
|
+
import { inject } from 'vue'
|
|
3
|
+
|
|
4
|
+
const id = inject('tooltipId')
|
|
5
|
+
const targetEl = inject('targetEl')
|
|
6
|
+
</script>
|
|
7
|
+
|
|
8
|
+
<template>
|
|
9
|
+
<div
|
|
10
|
+
:id="id"
|
|
11
|
+
ref="targetEl"
|
|
12
|
+
:aria-describedby="`${id}-content`"
|
|
13
|
+
:popovertarget="`${id}-content`"
|
|
14
|
+
popovertargetaction="toggle"
|
|
15
|
+
:style="{ 'anchor-name': `--${id}` }"
|
|
16
|
+
class="tooltip-target inline-block"
|
|
17
|
+
>
|
|
18
|
+
<slot />
|
|
19
|
+
</div>
|
|
20
|
+
</template>
|