daisy-ui-kit 5.0.0-pre.3 → 5.0.0-pre.30
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/app/components/Accordion.vue +29 -0
- package/app/components/Alert.vue +36 -0
- package/app/components/Avatar.vue +131 -0
- package/app/components/AvatarGroup.vue +22 -0
- package/app/components/Badge.vue +72 -0
- package/app/components/Breadcrumbs.vue +7 -0
- package/app/components/Button.vue +140 -0
- package/app/components/Calendar.vue +175 -0
- package/app/components/CalendarInput.vue +275 -0
- package/app/components/CalendarSkeleton.vue +87 -0
- package/app/components/Card.vue +51 -0
- package/app/components/CardActions.vue +13 -0
- package/app/components/CardBody.vue +13 -0
- package/app/components/CardTitle.vue +11 -0
- package/app/components/Carousel.vue +24 -0
- package/app/components/CarouselItem.vue +5 -0
- package/app/components/Chat.vue +26 -0
- package/app/components/ChatBubble.vue +31 -0
- package/app/components/ChatFooter.vue +5 -0
- package/app/components/ChatHeader.vue +5 -0
- package/app/components/ChatImage.vue +5 -0
- package/app/components/Checkbox.vue +51 -0
- package/app/components/Collapse.vue +75 -0
- package/app/components/CollapseContent.vue +5 -0
- package/app/components/CollapseTitle.vue +15 -0
- package/app/components/Countdown.vue +15 -0
- package/app/components/CountdownTimers.vue +69 -0
- package/app/components/Counter.vue +21 -0
- package/app/components/Crumb.vue +5 -0
- package/app/components/DaisyLink.vue +56 -0
- package/app/components/Diff.vue +11 -0
- package/app/components/Divider.vue +43 -0
- package/app/components/Dock.vue +57 -0
- package/app/components/DockItem.vue +27 -0
- package/app/components/DockLabel.vue +5 -0
- package/app/components/Drawer.vue +50 -0
- package/app/components/DrawerContent.vue +20 -0
- package/app/components/DrawerSide.vue +21 -0
- package/app/components/Dropdown.vue +106 -0
- package/app/components/DropdownButton.vue +23 -0
- package/app/components/DropdownContent.vue +127 -0
- package/app/components/DropdownTarget.vue +21 -0
- 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 +20 -0
- package/app/components/FileInput.vue +53 -0
- package/app/components/Filter.vue +129 -0
- package/app/components/Flex.vue +89 -0
- package/app/components/FlexItem.vue +62 -0
- package/app/components/Footer.vue +31 -0
- package/app/components/FooterTitle.vue +18 -0
- package/app/components/FormControl.vue +5 -0
- package/app/components/Hero.vue +18 -0
- package/app/components/HeroContent.vue +18 -0
- package/app/components/HeroOverlay.vue +5 -0
- package/app/components/Hover3D.vue +22 -0
- package/app/components/HoverGallery.vue +11 -0
- package/app/components/Indicator.vue +20 -0
- package/app/components/IndicatorItem.vue +43 -0
- package/app/components/Input.vue +116 -0
- package/app/components/Join.vue +5 -0
- package/app/components/Kbd.vue +25 -0
- package/app/components/Label.vue +100 -0
- package/app/components/List.vue +5 -0
- package/app/components/ListColGrow.vue +5 -0
- package/app/components/ListColWrap.vue +5 -0
- package/app/components/ListRow.vue +5 -0
- package/app/components/LoadingBall.vue +42 -0
- package/app/components/LoadingBars.vue +42 -0
- package/app/components/LoadingDots.vue +42 -0
- package/app/components/LoadingInfinity.vue +42 -0
- package/app/components/LoadingRing.vue +42 -0
- package/app/components/LoadingSpinner.vue +42 -0
- package/app/components/Mask.vue +49 -0
- package/app/components/Menu.vue +30 -0
- package/app/components/MenuExpand.vue +92 -0
- package/app/components/MenuExpandToggle.vue +20 -0
- package/app/components/MenuItem.vue +39 -0
- package/app/components/MenuTitle.vue +5 -0
- package/app/components/MockupBrowser.vue +5 -0
- package/app/components/MockupBrowserToolbar.vue +5 -0
- package/app/components/MockupCode.vue +4 -0
- package/app/components/MockupPhone.vue +14 -0
- package/app/components/MockupWindow.vue +5 -0
- package/app/components/Modal.vue +63 -0
- package/app/components/ModalAction.vue +5 -0
- package/app/components/ModalBox.vue +5 -0
- package/app/components/NavButton.vue +12 -0
- package/app/components/Navbar.vue +12 -0
- package/app/components/NavbarCenter.vue +11 -0
- package/app/components/NavbarEnd.vue +11 -0
- package/app/components/NavbarStart.vue +11 -0
- package/app/components/Progress.vue +46 -0
- package/app/components/Prose.vue +37 -0
- package/app/components/RadialProgress.vue +36 -0
- package/app/components/Radio.vue +69 -0
- package/app/components/RadioGroup.vue +47 -0
- package/app/components/Range.vue +201 -0
- package/app/components/RangeMeasure.vue +87 -0
- package/app/components/RangeMeasureTick.vue +69 -0
- package/app/components/Rating.vue +197 -0
- package/app/components/Select.vue +101 -0
- package/app/components/Skeleton.vue +5 -0
- package/app/components/SkeletonText.vue +11 -0
- package/app/components/Stack.vue +30 -0
- package/app/components/Stat.vue +19 -0
- package/app/components/StatActions.vue +5 -0
- package/app/components/StatDesc.vue +5 -0
- package/app/components/StatFigure.vue +5 -0
- package/app/components/StatTitle.vue +5 -0
- package/app/components/StatValue.vue +5 -0
- package/app/components/Stats.vue +5 -0
- package/app/components/Status.vue +43 -0
- package/app/components/Step.vue +34 -0
- package/app/components/StepIcon.vue +5 -0
- package/app/components/Steps.vue +23 -0
- package/app/components/Swap.vue +56 -0
- package/app/components/Tab.vue +56 -0
- package/app/components/TabContent.vue +29 -0
- package/app/components/Table.vue +32 -0
- package/app/components/Tabs.vue +53 -0
- package/app/components/Text.vue +166 -0
- package/app/components/TextArea.vue +106 -0
- package/app/components/TextRotate.vue +24 -0
- package/app/components/ThemeController.vue +45 -0
- package/app/components/ThemeProvider.vue +302 -0
- package/app/components/ThemeTile.vue +50 -0
- package/app/components/Timeline.vue +22 -0
- package/app/components/TimelineEnd.vue +14 -0
- package/app/components/TimelineItem.vue +5 -0
- package/app/components/TimelineLine.vue +29 -0
- package/app/components/TimelineMiddle.vue +5 -0
- package/app/components/TimelineStart.vue +14 -0
- package/app/components/Toast.vue +67 -0
- package/app/components/Toggle.vue +60 -0
- package/app/components/Tooltip.vue +137 -0
- package/app/components/TooltipContent.vue +283 -0
- package/app/components/TooltipTarget.vue +20 -0
- package/app/components/ValidatorHint.vue +5 -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 +131 -0
- package/app/composables/use-toast.ts +345 -0
- package/app/composables/useSearch.ts +22 -0
- package/app/utils/drawer-utils.ts +34 -0
- package/app/utils/position-area.ts +40 -0
- package/nuxt.d.ts +13 -0
- package/nuxt.js +31 -0
- package/package.json +50 -22
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { computed } from 'vue'
|
|
3
|
+
|
|
4
|
+
const props = defineProps<{
|
|
5
|
+
is?: any
|
|
6
|
+
center?: boolean
|
|
7
|
+
duration?: number
|
|
8
|
+
}>()
|
|
9
|
+
|
|
10
|
+
const style = computed(() => {
|
|
11
|
+
if (props.duration) {
|
|
12
|
+
return { animationDuration: `${props.duration}ms` }
|
|
13
|
+
}
|
|
14
|
+
return undefined
|
|
15
|
+
})
|
|
16
|
+
</script>
|
|
17
|
+
|
|
18
|
+
<template>
|
|
19
|
+
<component :is="props.is || 'span'" class="text-rotate" :style="style">
|
|
20
|
+
<span :class="{ 'justify-items-center': props.center }">
|
|
21
|
+
<slot />
|
|
22
|
+
</span>
|
|
23
|
+
</component>
|
|
24
|
+
</template>
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import type { DaisyThemeInput } from '@/composables/use-daisy-theme'
|
|
3
|
+
import { useDaisyTheme } from '@/composables/use-daisy-theme'
|
|
4
|
+
import { computed } from 'vue'
|
|
5
|
+
|
|
6
|
+
const props = defineProps<{
|
|
7
|
+
/**
|
|
8
|
+
* Optional: List of themes to provide (string or DaisyThemeMeta)
|
|
9
|
+
*/
|
|
10
|
+
themes?: DaisyThemeInput[]
|
|
11
|
+
/**
|
|
12
|
+
* Optional: Default theme name (string or 'default')
|
|
13
|
+
*/
|
|
14
|
+
defaultTheme?: string
|
|
15
|
+
/**
|
|
16
|
+
* Optional: Storage function for persistence (e.g., useCookie, useLocalStorage, or ref)
|
|
17
|
+
*/
|
|
18
|
+
storage?: <T>(key: string, initial: T) => import('vue').Ref<T>
|
|
19
|
+
}>()
|
|
20
|
+
|
|
21
|
+
const themeOptions =
|
|
22
|
+
props.themes || props.defaultTheme ? { themes: props.themes || [], defaultTheme: props.defaultTheme } : undefined
|
|
23
|
+
|
|
24
|
+
const { theme, effectiveTheme, themes, setTheme, cycleTheme, registerTheme, removeTheme } = useDaisyTheme(
|
|
25
|
+
props.storage,
|
|
26
|
+
themeOptions,
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
const availableThemes = computed(() => themes.value.map(t => (typeof t === 'string' ? t : t.theme)))
|
|
30
|
+
</script>
|
|
31
|
+
|
|
32
|
+
<template>
|
|
33
|
+
<div class="theme-controller">
|
|
34
|
+
<slot
|
|
35
|
+
:theme="theme"
|
|
36
|
+
:effective-theme="effectiveTheme"
|
|
37
|
+
:themes="themes"
|
|
38
|
+
:available-themes="availableThemes"
|
|
39
|
+
:set-theme="setTheme"
|
|
40
|
+
:cycle-theme="cycleTheme"
|
|
41
|
+
:register-theme="registerTheme"
|
|
42
|
+
:remove-theme="removeTheme"
|
|
43
|
+
/>
|
|
44
|
+
</div>
|
|
45
|
+
</template>
|
|
@@ -0,0 +1,302 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import type { ComputedThemeVarsSlot } from './ThemeProvider.inject.js'
|
|
3
|
+
import { computed, nextTick, onUnmounted, provide, ref, watch } from 'vue'
|
|
4
|
+
import { daisyUiThemeKey } from './ThemeProvider.inject.js'
|
|
5
|
+
|
|
6
|
+
const props = defineProps<{
|
|
7
|
+
/**
|
|
8
|
+
* DaisyUI theme string (recommended prop name)
|
|
9
|
+
*/
|
|
10
|
+
dataTheme?: string
|
|
11
|
+
|
|
12
|
+
cssVars?: string
|
|
13
|
+
snoop?: boolean
|
|
14
|
+
}>()
|
|
15
|
+
|
|
16
|
+
function cssVarToCamel(str: string): string {
|
|
17
|
+
return str.replace(/^--/, '').replace(/-([a-z0-9])/g, (_, c: string) => c.toUpperCase())
|
|
18
|
+
}
|
|
19
|
+
function dataAttrToCamel(str: string): string {
|
|
20
|
+
return str.replace(/-([a-z0-9])/g, (_, c: string) => c.toUpperCase())
|
|
21
|
+
}
|
|
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
|
+
} {
|
|
28
|
+
if (!themeString) {
|
|
29
|
+
return { style: {}, dataAttrs: {}, dataTheme: undefined, allAttrs: {} }
|
|
30
|
+
}
|
|
31
|
+
const varNamePattern = /^--[\w-]+$/
|
|
32
|
+
const attrNamePattern = /^[a-z_][\w-]*$/i
|
|
33
|
+
const forbiddenChars = /[<>{};]/g
|
|
34
|
+
|
|
35
|
+
const style: Record<string, string> = {}
|
|
36
|
+
const dataAttrs: Record<string, string> = {}
|
|
37
|
+
let dataTheme: string | undefined
|
|
38
|
+
const allAttrs: Record<string, string> = {}
|
|
39
|
+
|
|
40
|
+
// Remove @plugin ... { and closing } if present
|
|
41
|
+
const str = themeString
|
|
42
|
+
.trim()
|
|
43
|
+
.replace(/^@plugin[^{}]*\{/, '')
|
|
44
|
+
.replace(/\}$/, '')
|
|
45
|
+
.trim()
|
|
46
|
+
|
|
47
|
+
// works with or without newline characters
|
|
48
|
+
str.split(/[\n;]/).forEach(line => {
|
|
49
|
+
const trimmed = line.trim()
|
|
50
|
+
if (!trimmed || trimmed.startsWith('//')) {
|
|
51
|
+
return
|
|
52
|
+
}
|
|
53
|
+
const colonIdx = trimmed.indexOf(':')
|
|
54
|
+
if (colonIdx === -1) {
|
|
55
|
+
return
|
|
56
|
+
}
|
|
57
|
+
const key = trimmed.slice(0, colonIdx).trim()
|
|
58
|
+
let value = trimmed.slice(colonIdx + 1).trim()
|
|
59
|
+
if (value.endsWith(';')) {
|
|
60
|
+
value = value.slice(0, -1).trim()
|
|
61
|
+
}
|
|
62
|
+
value = value.replace(forbiddenChars, '')
|
|
63
|
+
if (!value) {
|
|
64
|
+
return
|
|
65
|
+
}
|
|
66
|
+
if (varNamePattern.test(key)) {
|
|
67
|
+
style[key] = value
|
|
68
|
+
allAttrs[cssVarToCamel(key)] = value
|
|
69
|
+
} else if (key === 'name') {
|
|
70
|
+
dataTheme = value.replace(/"/g, '')
|
|
71
|
+
allAttrs.dataTheme = dataTheme
|
|
72
|
+
} else if (attrNamePattern.test(key)) {
|
|
73
|
+
const attrKey = `data-${key.replace(/[^\w-]/g, '')}`
|
|
74
|
+
const attrVal = value.replace(/"/g, '')
|
|
75
|
+
dataAttrs[attrKey] = attrVal
|
|
76
|
+
if (attrKey.startsWith('data-')) {
|
|
77
|
+
allAttrs[dataAttrToCamel(attrKey)] = attrVal
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
})
|
|
81
|
+
return { style, dataAttrs, dataTheme, allAttrs }
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
const parsed = computed(() => parseThemeString(props.dataTheme ?? props.cssVars))
|
|
85
|
+
|
|
86
|
+
const themeVars = ref<Record<string, string>>({})
|
|
87
|
+
let observers: MutationObserver[] = []
|
|
88
|
+
let themeControllerInputs: Array<{ el: HTMLInputElement; listener: () => void }> = []
|
|
89
|
+
let themeControllerDomObserver: MutationObserver | null = null
|
|
90
|
+
|
|
91
|
+
// Converts a themeAttrs object to a DaisyUI theme string
|
|
92
|
+
function toThemeString(attrs: Record<string, string>, opts?: { asPlugin?: boolean }) {
|
|
93
|
+
const lines: string[] = []
|
|
94
|
+
// Map camelCase back to DaisyUI keys
|
|
95
|
+
for (const [key, value] of Object.entries(attrs)) {
|
|
96
|
+
if (key === 'dataTheme') {
|
|
97
|
+
lines.push(`name: "${value}";`)
|
|
98
|
+
} else if (key.startsWith('data')) {
|
|
99
|
+
// Convert dataFooBar -> foo-bar
|
|
100
|
+
const attr = key
|
|
101
|
+
.slice(4)
|
|
102
|
+
.replace(/([A-Z])/g, '-$1')
|
|
103
|
+
.toLowerCase()
|
|
104
|
+
if (attr !== 'theme') {
|
|
105
|
+
lines.push(`${attr}: ${value};`)
|
|
106
|
+
}
|
|
107
|
+
} else {
|
|
108
|
+
// Convert camelCase to --kebab-case
|
|
109
|
+
const cssVar = `--${key.replace(/([A-Z])/g, '-$1').toLowerCase()}`
|
|
110
|
+
lines.push(`${cssVar}: ${value};`)
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
const inner = lines.join('\n')
|
|
114
|
+
if (opts?.asPlugin) {
|
|
115
|
+
return `@plugin \"daisyui/theme\" {\n${inner}\n}`
|
|
116
|
+
}
|
|
117
|
+
return inner
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// Returns the slot data object depending on snoop mode
|
|
121
|
+
const slotData: ComputedThemeVarsSlot = computed(() => {
|
|
122
|
+
// Access both dependencies so Vue always tracks them for reactivity
|
|
123
|
+
void themeVars.value
|
|
124
|
+
void parsed.value.allAttrs
|
|
125
|
+
if (props.snoop) {
|
|
126
|
+
return {
|
|
127
|
+
vars: themeVars.value,
|
|
128
|
+
toThemeString: (attrs = themeVars.value, opts) => toThemeString(attrs, opts),
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
return {
|
|
132
|
+
vars: parsed.value.allAttrs,
|
|
133
|
+
toThemeString: (attrs = parsed.value.allAttrs, opts) => toThemeString(attrs, opts),
|
|
134
|
+
}
|
|
135
|
+
})
|
|
136
|
+
|
|
137
|
+
// Provide the slotData as 'daisyUiTheme' for child consumers
|
|
138
|
+
provide(daisyUiThemeKey, slotData)
|
|
139
|
+
|
|
140
|
+
// DaisyUI variable names
|
|
141
|
+
const daisyVars = [
|
|
142
|
+
'--color-primary',
|
|
143
|
+
'--color-primary-content',
|
|
144
|
+
'--color-secondary',
|
|
145
|
+
'--color-secondary-content',
|
|
146
|
+
'--color-accent',
|
|
147
|
+
'--color-accent-content',
|
|
148
|
+
'--color-neutral',
|
|
149
|
+
'--color-neutral-content',
|
|
150
|
+
'--color-base-100',
|
|
151
|
+
'--color-base-200',
|
|
152
|
+
'--color-base-300',
|
|
153
|
+
'--color-base-content',
|
|
154
|
+
'--color-info',
|
|
155
|
+
'--color-info-content',
|
|
156
|
+
'--color-success',
|
|
157
|
+
'--color-success-content',
|
|
158
|
+
'--color-warning',
|
|
159
|
+
'--color-warning-content',
|
|
160
|
+
'--color-error',
|
|
161
|
+
'--color-error-content',
|
|
162
|
+
'--radius-selector',
|
|
163
|
+
'--radius-field',
|
|
164
|
+
'--radius-box',
|
|
165
|
+
'--size-selector',
|
|
166
|
+
'--size-field',
|
|
167
|
+
'--border',
|
|
168
|
+
'--depth',
|
|
169
|
+
'--noise',
|
|
170
|
+
]
|
|
171
|
+
|
|
172
|
+
function getDaisyVarsFromEl(el: HTMLElement): Record<string, string> {
|
|
173
|
+
const vars: Record<string, string> = {}
|
|
174
|
+
const style = getComputedStyle(el)
|
|
175
|
+
for (const varName of daisyVars) {
|
|
176
|
+
const val = style.getPropertyValue(varName)
|
|
177
|
+
if (val) {
|
|
178
|
+
vars[cssVarToCamel(varName)] = val.trim()
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
return vars
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
let prefersColorSchemeMql: MediaQueryList | null = null
|
|
185
|
+
let prefersColorSchemeCleanup: (() => void) | null = null
|
|
186
|
+
|
|
187
|
+
function setupSnoop(rootEl: HTMLElement): void {
|
|
188
|
+
function updateVars(): void {
|
|
189
|
+
themeVars.value = getDaisyVarsFromEl(rootEl)
|
|
190
|
+
}
|
|
191
|
+
// Observe the current element for data-theme changes
|
|
192
|
+
const obs = new MutationObserver(() => updateVars())
|
|
193
|
+
obs.observe(rootEl, { attributes: true, attributeFilter: ['data-theme'] })
|
|
194
|
+
|
|
195
|
+
// Also observe the <html> element for data-theme changes
|
|
196
|
+
const htmlEl = document.documentElement
|
|
197
|
+
const htmlObs = new MutationObserver(() => updateVars())
|
|
198
|
+
htmlObs.observe(htmlEl, { attributes: true, attributeFilter: ['data-theme'] })
|
|
199
|
+
|
|
200
|
+
observers = [obs, htmlObs]
|
|
201
|
+
|
|
202
|
+
// Listen for prefers-color-scheme changes
|
|
203
|
+
prefersColorSchemeMql = window.matchMedia('(prefers-color-scheme: dark)')
|
|
204
|
+
const prefersColorSchemeListener = () => updateVars()
|
|
205
|
+
prefersColorSchemeMql.addEventListener('change', prefersColorSchemeListener)
|
|
206
|
+
prefersColorSchemeCleanup = () => {
|
|
207
|
+
prefersColorSchemeMql?.removeEventListener('change', prefersColorSchemeListener)
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
// --- Observe theme-controller checkboxes/radios, including dynamic changes ---
|
|
211
|
+
function bindThemeControllerInputs() {
|
|
212
|
+
// Remove any previous listeners
|
|
213
|
+
themeControllerInputs.forEach(({ el, listener }) => {
|
|
214
|
+
el.removeEventListener('change', listener)
|
|
215
|
+
})
|
|
216
|
+
themeControllerInputs = []
|
|
217
|
+
// Find all matching inputs in the document
|
|
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 => {
|
|
222
|
+
const listener = () => updateVars()
|
|
223
|
+
el.addEventListener('change', listener)
|
|
224
|
+
themeControllerInputs.push({ el, listener })
|
|
225
|
+
})
|
|
226
|
+
}
|
|
227
|
+
bindThemeControllerInputs()
|
|
228
|
+
// Observe DOM for dynamic addition/removal of theme-controller inputs
|
|
229
|
+
if (themeControllerDomObserver) {
|
|
230
|
+
themeControllerDomObserver.disconnect()
|
|
231
|
+
themeControllerDomObserver = null
|
|
232
|
+
}
|
|
233
|
+
themeControllerDomObserver = new MutationObserver(() => {
|
|
234
|
+
bindThemeControllerInputs()
|
|
235
|
+
})
|
|
236
|
+
themeControllerDomObserver.observe(document.body, { childList: true, subtree: true })
|
|
237
|
+
|
|
238
|
+
// Watch for applied style changes (parsed.style)
|
|
239
|
+
watch(
|
|
240
|
+
() => parsed.value.style,
|
|
241
|
+
() => {
|
|
242
|
+
nextTick(() => updateVars())
|
|
243
|
+
},
|
|
244
|
+
{ immediate: false, deep: true },
|
|
245
|
+
)
|
|
246
|
+
updateVars()
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
const rootEl = ref<HTMLElement | null>(null)
|
|
250
|
+
|
|
251
|
+
const isClient = typeof window !== 'undefined' && typeof document !== 'undefined'
|
|
252
|
+
|
|
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
|
+
}
|
|
265
|
+
}
|
|
266
|
+
},
|
|
267
|
+
{ immediate: true },
|
|
268
|
+
)
|
|
269
|
+
|
|
270
|
+
onUnmounted(() => {
|
|
271
|
+
observers.forEach((o: MutationObserver) => o.disconnect())
|
|
272
|
+
observers = []
|
|
273
|
+
// Remove prefers-color-scheme listener if present
|
|
274
|
+
if (prefersColorSchemeCleanup) {
|
|
275
|
+
prefersColorSchemeCleanup()
|
|
276
|
+
prefersColorSchemeCleanup = null
|
|
277
|
+
prefersColorSchemeMql = null
|
|
278
|
+
}
|
|
279
|
+
// Remove theme-controller listeners
|
|
280
|
+
themeControllerInputs.forEach(({ el, listener }) => {
|
|
281
|
+
el.removeEventListener('change', listener)
|
|
282
|
+
})
|
|
283
|
+
themeControllerInputs = []
|
|
284
|
+
// Disconnect theme-controller DOM observer
|
|
285
|
+
if (themeControllerDomObserver) {
|
|
286
|
+
themeControllerDomObserver.disconnect()
|
|
287
|
+
themeControllerDomObserver = null
|
|
288
|
+
}
|
|
289
|
+
})
|
|
290
|
+
</script>
|
|
291
|
+
|
|
292
|
+
<template>
|
|
293
|
+
<div
|
|
294
|
+
ref="rootEl"
|
|
295
|
+
v-bind="parsed.dataAttrs"
|
|
296
|
+
:data-theme="dataTheme"
|
|
297
|
+
:style="parsed.style"
|
|
298
|
+
class="[background-color:unset] theme-provider"
|
|
299
|
+
>
|
|
300
|
+
<slot v-bind="slotData" />
|
|
301
|
+
</div>
|
|
302
|
+
</template>
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { computed } from 'vue'
|
|
3
|
+
|
|
4
|
+
const props = defineProps<{
|
|
5
|
+
theme?: string
|
|
6
|
+
size?: 'xs' | 'sm' | 'md' | 'lg' | 'xl'
|
|
7
|
+
xs?: boolean
|
|
8
|
+
sm?: boolean
|
|
9
|
+
md?: boolean
|
|
10
|
+
lg?: boolean
|
|
11
|
+
xl?: boolean
|
|
12
|
+
}>()
|
|
13
|
+
const size = computed(() => props.size ?? 'md')
|
|
14
|
+
|
|
15
|
+
const squareClass = computed(() => {
|
|
16
|
+
switch (size.value) {
|
|
17
|
+
case 'xs':
|
|
18
|
+
return 'w-full h-full rounded-[2px]'
|
|
19
|
+
case 'sm':
|
|
20
|
+
return 'w-full h-full rounded-[2px]'
|
|
21
|
+
case 'md':
|
|
22
|
+
return 'w-full h-full rounded-xs'
|
|
23
|
+
case 'lg':
|
|
24
|
+
return 'w-full h-full rounded-sm'
|
|
25
|
+
case 'xl':
|
|
26
|
+
return 'w-full h-full rounded'
|
|
27
|
+
default:
|
|
28
|
+
return 'w-full h-full rounded-xs'
|
|
29
|
+
}
|
|
30
|
+
})
|
|
31
|
+
</script>
|
|
32
|
+
|
|
33
|
+
<template>
|
|
34
|
+
<div
|
|
35
|
+
:data-theme="theme"
|
|
36
|
+
class="grid grid-cols-2 bg-white rounded-sm"
|
|
37
|
+
:class="{
|
|
38
|
+
'!size-4 p-0.5 gap-px': xs || size === 'xs',
|
|
39
|
+
'!size-5 p-0.5 gap-px': sm || size === 'sm',
|
|
40
|
+
'size-6 p-0.5 gap-0.5': md || size === 'md' || !size,
|
|
41
|
+
'size-8 p-0.75 gap-0.5': lg || size === 'lg',
|
|
42
|
+
'size-10 h-10 p-1 gap-0.75': xl || size === 'xl',
|
|
43
|
+
}"
|
|
44
|
+
>
|
|
45
|
+
<div class="bg-neutral" :class="squareClass" />
|
|
46
|
+
<div class="bg-primary" :class="squareClass" />
|
|
47
|
+
<div class="bg-secondary" :class="squareClass" />
|
|
48
|
+
<div class="bg-accent" :class="squareClass" />
|
|
49
|
+
</div>
|
|
50
|
+
</template>
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
defineProps<{
|
|
3
|
+
vertical?: boolean
|
|
4
|
+
horizontal?: boolean
|
|
5
|
+
compact?: boolean
|
|
6
|
+
snapIcon?: boolean
|
|
7
|
+
}>()
|
|
8
|
+
</script>
|
|
9
|
+
|
|
10
|
+
<template>
|
|
11
|
+
<ul
|
|
12
|
+
class="timeline"
|
|
13
|
+
:class="{
|
|
14
|
+
'timeline-vertical': vertical,
|
|
15
|
+
'timeline-horizontal': horizontal,
|
|
16
|
+
'timeline-compact': compact,
|
|
17
|
+
'timeline-snap-icon': snapIcon,
|
|
18
|
+
}"
|
|
19
|
+
>
|
|
20
|
+
<slot />
|
|
21
|
+
</ul>
|
|
22
|
+
</template>
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
defineProps<{
|
|
3
|
+
color?: 'neutral' | 'primary' | 'secondary' | 'accent' | 'info' | 'success' | 'warning' | 'error'
|
|
4
|
+
neutral?: boolean
|
|
5
|
+
primary?: boolean
|
|
6
|
+
secondary?: boolean
|
|
7
|
+
accent?: boolean
|
|
8
|
+
info?: boolean
|
|
9
|
+
success?: boolean
|
|
10
|
+
warning?: boolean
|
|
11
|
+
error?: boolean
|
|
12
|
+
}>()
|
|
13
|
+
</script>
|
|
14
|
+
|
|
15
|
+
<template>
|
|
16
|
+
<hr
|
|
17
|
+
class="timeline-line"
|
|
18
|
+
:class="{
|
|
19
|
+
'bg-neutral': neutral || color === 'neutral',
|
|
20
|
+
'bg-primary': primary || color === 'primary',
|
|
21
|
+
'bg-secondary': secondary || color === 'secondary',
|
|
22
|
+
'bg-accent': accent || color === 'accent',
|
|
23
|
+
'bg-info': info || color === 'info',
|
|
24
|
+
'bg-success': success || color === 'success',
|
|
25
|
+
'bg-warning': warning || color === 'warning',
|
|
26
|
+
'bg-error': error || color === 'error',
|
|
27
|
+
}"
|
|
28
|
+
/>
|
|
29
|
+
</template>
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import type { Toast } from '../composables/use-toast'
|
|
3
|
+
import { computed } from 'vue'
|
|
4
|
+
import { useToast } from '../composables/use-toast'
|
|
5
|
+
|
|
6
|
+
// Explicit slot typing (Vue 3.4+ / Volar)
|
|
7
|
+
interface ToastSlotProps {
|
|
8
|
+
toast?: Toast
|
|
9
|
+
removeToast?: (id: number) => void
|
|
10
|
+
[key: string]: unknown
|
|
11
|
+
}
|
|
12
|
+
const props = defineProps<{
|
|
13
|
+
/**
|
|
14
|
+
* Horizontal alignment (start, center, end)
|
|
15
|
+
*/
|
|
16
|
+
hAlign?: 'start' | 'center' | 'end'
|
|
17
|
+
start?: boolean
|
|
18
|
+
center?: boolean
|
|
19
|
+
end?: boolean
|
|
20
|
+
/**
|
|
21
|
+
* Vertical alignment (top, middle, bottom)
|
|
22
|
+
*/
|
|
23
|
+
vAlign?: 'top' | 'middle' | 'bottom'
|
|
24
|
+
top?: boolean
|
|
25
|
+
middle?: boolean
|
|
26
|
+
bottom?: boolean
|
|
27
|
+
/**
|
|
28
|
+
* Toast channel name (for named channels)
|
|
29
|
+
*/
|
|
30
|
+
name?: string
|
|
31
|
+
/**
|
|
32
|
+
* Default toast settings for this channel (merged into every toast)
|
|
33
|
+
* Example: { duration: 6000, type: 'info', position: 'top-center' }
|
|
34
|
+
*/
|
|
35
|
+
defaults?: Partial<Toast>
|
|
36
|
+
}>()
|
|
37
|
+
|
|
38
|
+
defineSlots<{
|
|
39
|
+
default: (props: ToastSlotProps) => any
|
|
40
|
+
}>()
|
|
41
|
+
|
|
42
|
+
// Extract useToast options from props (name, defaults, future-proof)
|
|
43
|
+
const toastOptions = computed(() => {
|
|
44
|
+
const { name, defaults } = props
|
|
45
|
+
return { name, defaults }
|
|
46
|
+
})
|
|
47
|
+
|
|
48
|
+
const { toasts, removeToast } = useToast(toastOptions.value)
|
|
49
|
+
const visibleToasts = computed(() => toasts.value ?? [])
|
|
50
|
+
</script>
|
|
51
|
+
|
|
52
|
+
<template>
|
|
53
|
+
<div
|
|
54
|
+
class="toast"
|
|
55
|
+
:class="{
|
|
56
|
+
'toast-start': props.start || props.hAlign === 'start',
|
|
57
|
+
'toast-center': props.center || props.hAlign === 'center',
|
|
58
|
+
'toast-end': props.end || props.hAlign === 'end',
|
|
59
|
+
'toast-top': props.top || props.vAlign === 'top',
|
|
60
|
+
'toast-middle': props.middle || props.vAlign === 'middle',
|
|
61
|
+
'toast-bottom': props.bottom || props.vAlign === 'bottom',
|
|
62
|
+
}"
|
|
63
|
+
>
|
|
64
|
+
<slot v-for="toast in visibleToasts" :key="toast.id" :toast="toast" :remove-toast="removeToast" />
|
|
65
|
+
<slot />
|
|
66
|
+
</div>
|
|
67
|
+
</template>
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { computed } from 'vue'
|
|
3
|
+
|
|
4
|
+
const props = defineProps<{
|
|
5
|
+
modelValue?: any
|
|
6
|
+
disabled?: boolean
|
|
7
|
+
validator?: boolean
|
|
8
|
+
themeController?: boolean
|
|
9
|
+
|
|
10
|
+
color?: string
|
|
11
|
+
neutral?: boolean
|
|
12
|
+
primary?: boolean
|
|
13
|
+
secondary?: boolean
|
|
14
|
+
accent?: boolean
|
|
15
|
+
success?: boolean
|
|
16
|
+
info?: boolean
|
|
17
|
+
warning?: boolean
|
|
18
|
+
error?: boolean
|
|
19
|
+
|
|
20
|
+
size?: string
|
|
21
|
+
xs?: boolean
|
|
22
|
+
sm?: boolean
|
|
23
|
+
md?: boolean
|
|
24
|
+
lg?: boolean
|
|
25
|
+
xl?: boolean
|
|
26
|
+
}>()
|
|
27
|
+
const emit = defineEmits(['update:modelValue'])
|
|
28
|
+
|
|
29
|
+
const currentValue = computed({
|
|
30
|
+
get: () => props.modelValue,
|
|
31
|
+
set: (val: string) => emit('update:modelValue', val),
|
|
32
|
+
})
|
|
33
|
+
</script>
|
|
34
|
+
|
|
35
|
+
<template>
|
|
36
|
+
<input
|
|
37
|
+
v-model="currentValue"
|
|
38
|
+
type="checkbox"
|
|
39
|
+
v-bind="$attrs"
|
|
40
|
+
class="toggle"
|
|
41
|
+
:disabled="disabled"
|
|
42
|
+
:class="{
|
|
43
|
+
validator,
|
|
44
|
+
'toggle-neutral': neutral || color === 'neutral',
|
|
45
|
+
'toggle-primary': primary || color === 'primary',
|
|
46
|
+
'toggle-secondary': secondary || color === 'secondary',
|
|
47
|
+
'toggle-accent': accent || color === 'accent',
|
|
48
|
+
'toggle-success': success || color === 'success',
|
|
49
|
+
'toggle-info': info || color === 'info',
|
|
50
|
+
'toggle-warning': warning || color === 'warning',
|
|
51
|
+
'toggle-error': error || color === 'error',
|
|
52
|
+
'toggle-xs': xs || size === 'xs',
|
|
53
|
+
'toggle-sm': sm || size === 'sm',
|
|
54
|
+
'toggle-md': md || size === 'md',
|
|
55
|
+
'toggle-lg': lg || size === 'lg',
|
|
56
|
+
'toggle-xl': xl || size === 'xl',
|
|
57
|
+
'theme-controller': themeController,
|
|
58
|
+
}"
|
|
59
|
+
/>
|
|
60
|
+
</template>
|