daisy-ui-kit 5.0.0-pre.6 → 5.0.0-pre.8

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.
@@ -1,5 +1,5 @@
1
1
  <script setup lang="ts">
2
- import { computed } from 'vue';
2
+ import { computed } from 'vue'
3
3
 
4
4
  const props = defineProps<{
5
5
  is?: string
@@ -42,25 +42,30 @@ const props = defineProps<{
42
42
  type?: 'button' | 'submit' | 'reset'
43
43
  }>()
44
44
 
45
- // Accessibility-friendly attributes for disabling the button
46
- const disabledAttrs = computed(() => {
45
+ // Accessibility: Determine if rendering a native button
46
+ const isButton = computed(() => (props.is || 'button') === 'button')
47
+
48
+ // Accessibility: Keyboard event handler for custom elements
49
+ function onKeydown(e: KeyboardEvent) {
47
50
  if (props.disabled) {
48
- return {
49
- 'tabindex': -1,
50
- 'role': 'button',
51
- 'aria-disabled': true,
52
- }
51
+ return
53
52
  }
54
- else {
55
- return {}
53
+ if (e.code === 'Space' || e.code === 'Enter' || e.key === ' ' || e.key === 'Enter') {
54
+ e.preventDefault()
55
+ // @ts-expect-error: $el may not exist yet
56
+ e.currentTarget?.click?.()
56
57
  }
57
- })
58
+ }
58
59
  </script>
59
60
 
60
61
  <template>
61
62
  <component
62
63
  :is="is || 'button'"
63
64
  :type="type"
65
+ :disabled="isButton && disabled ? true : undefined"
66
+ :aria-disabled="!isButton && disabled ? true : undefined"
67
+ :tabindex="!isButton ? (disabled ? -1 : 0) : undefined"
68
+ :role="!isButton ? 'button' : undefined"
64
69
  class="btn"
65
70
  :class="{
66
71
  'join-item': join,
@@ -106,7 +111,7 @@ const disabledAttrs = computed(() => {
106
111
  'no-animation': noAnimation,
107
112
  'btn-active': !disabled && active,
108
113
  }"
109
- v-bind="{ ...disabledAttrs, ...$attrs }"
114
+ @keydown="!isButton ? onKeydown : undefined"
110
115
  >
111
116
  <slot />
112
117
  </component>
@@ -1,7 +1,7 @@
1
1
  <script setup lang="ts">
2
2
  import type { PikadayOptions } from 'pikaday'
3
3
  import { onMounted, ref } from 'vue'
4
- import { usePikaday } from '~/composables/usePikaday'
4
+ import { usePikaday } from '~/composables/use-pikaday'
5
5
  import CalendarSkeleton from './CalendarSkeleton.vue'
6
6
 
7
7
  const props = defineProps<{
@@ -3,7 +3,7 @@ import type { PikadayOptions } from 'pikaday'
3
3
  import type { ComponentPublicInstance } from 'vue'
4
4
  import { onMounted, ref, watch } from 'vue'
5
5
 
6
- import { usePikaday } from '~/composables/usePikaday'
6
+ import { usePikaday } from '~/composables/use-pikaday'
7
7
 
8
8
  const props = defineProps<{
9
9
  /** Bound value: Date object or ISO string or null */
@@ -3,6 +3,7 @@ import { computed } from 'vue'
3
3
 
4
4
  const props = defineProps<{
5
5
  modelValue?: any
6
+ themeController?: boolean
6
7
 
7
8
  color?: string
8
9
  primary?: boolean
@@ -44,6 +45,7 @@ const currentValue = computed({
44
45
  'checkbox-sm': sm || size === 'sm',
45
46
  'checkbox-md': md || size === 'md',
46
47
  'checkbox-lg': lg || size === 'lg',
48
+ 'theme-controller': themeController,
47
49
  }"
48
50
  >
49
51
  </template>
@@ -1,8 +1,8 @@
1
1
  <script setup lang="ts">
2
- import { autoUpdate, useFloating } from '@floating-ui/vue';
3
- import { onClickOutside, syncRefs, useElementHover } from '@vueuse/core';
4
- import { onMounted, provide, reactive, ref } from 'vue';
5
- import { randomString } from '../utils/random-string';
2
+ import { autoUpdate, useFloating } from '@floating-ui/vue'
3
+ import { onClickOutside, syncRefs, useElementHover } from '@vueuse/core'
4
+ import { onMounted, provide, reactive, ref } from 'vue'
5
+ import { randomString } from '../utils/random-string'
6
6
 
7
7
  const props = withDefaults(defineProps<{
8
8
  // allows passing a custom id to the dropdown. Required for SSR hydration.
@@ -1,5 +1,6 @@
1
1
  <script setup lang="ts">
2
- import { computed, inject } from 'vue'
2
+ import { syncRef } from '@vueuse/core'
3
+ import { computed, inject, ref } from 'vue'
3
4
 
4
5
  // Map input type to value type
5
6
  type InputType =
@@ -37,7 +38,29 @@ function setValue(val: string | number | null) {
37
38
  emit('update:modelValue', val)
38
39
  }
39
40
 
40
- const labelCtx = inject('labelCtx', false)
41
+ const labelCtx = inject('labelCtx')
42
+
43
+ function handleInput(event: Event) {
44
+ let val: string | number | null
45
+ if (props.type === 'number') {
46
+ const inputVal = (event.target as HTMLInputElement).value
47
+ val = inputVal === '' ? null : Number(inputVal)
48
+ }
49
+ else {
50
+ val = (event.target as HTMLInputElement).value
51
+ }
52
+ emit('update:modelValue', val)
53
+
54
+ // Sync checked only on input if it's a checkbox
55
+ if (
56
+ props.type === 'checkbox'
57
+ && labelCtx
58
+ && Object.prototype.hasOwnProperty.call(labelCtx, 'checked')
59
+ && typeof labelCtx.checked.value === 'boolean'
60
+ ) {
61
+ labelCtx.checked.value = Boolean(event.target.checked)
62
+ }
63
+ }
41
64
 
42
65
  const inputAttrs = computed(() => {
43
66
  if (labelCtx) {
@@ -91,12 +114,6 @@ const inputModel = computed({
91
114
  v-else
92
115
  v-bind="inputAttrs"
93
116
  :value="inputModel"
94
- @input="
95
- $emit('update:modelValue',
96
- props.type === 'number'
97
- ? ($event.target as HTMLInputElement).value === '' ? null : Number(($event.target as HTMLInputElement).value)
98
- : ($event.target as HTMLInputElement).value,
99
- )
100
- "
117
+ @input="handleInput"
101
118
  >
102
119
  </template>
@@ -1,14 +1,18 @@
1
1
  <script setup lang="ts">
2
- import { provide } from 'vue'
2
+ import { provide, ref } from 'vue'
3
3
  import Text from './Text.vue'
4
4
 
5
5
  withDefaults(defineProps<{
6
6
  is?: string
7
7
  floating?: boolean
8
+
8
9
  input?: boolean
10
+ select?: boolean
11
+ toggle?: boolean
9
12
 
10
13
  join?: boolean
11
14
  color?: string
15
+ neutral?: boolean
12
16
  primary?: boolean
13
17
  secondary?: boolean
14
18
  accent?: boolean
@@ -28,7 +32,8 @@ withDefaults(defineProps<{
28
32
  })
29
33
 
30
34
  // Provide label context for child inputs
31
- provide('labelCtx', true)
35
+ const checked = ref(false)
36
+ provide('labelCtx', { checked })
32
37
  </script>
33
38
 
34
39
  <template>
@@ -37,7 +42,10 @@ provide('labelCtx', true)
37
42
  :class="[
38
43
  {
39
44
  'input': input,
40
- 'join-item': input && join,
45
+ 'select': select,
46
+ 'toggle': toggle,
47
+ 'join-item': (input || select || toggle) && join,
48
+ 'input-neutral': input && (neutral || color === 'neutral'),
41
49
  'input-primary': input && (primary || color === 'primary'),
42
50
  'input-secondary': input && (secondary || color === 'secondary'),
43
51
  'input-accent': input && (accent || color === 'accent'),
@@ -51,8 +59,36 @@ provide('labelCtx', true)
51
59
  'input-md': input && (size === 'md' || md),
52
60
  'input-sm': input && (size === 'sm' || sm),
53
61
  'input-xs': input && (size === 'xs' || xs),
62
+ 'select-neutral': select && (neutral || color === 'neutral'),
63
+ 'select-primary': select && (primary || color === 'primary'),
64
+ 'select-secondary': select && (secondary || color === 'secondary'),
65
+ 'select-accent': select && (accent || color === 'accent'),
66
+ 'select-info': select && (info || color === 'info'),
67
+ 'select-success': select && (success || color === 'success'),
68
+ 'select-warning': select && (warning || color === 'warning'),
69
+ 'select-error': select && (error || color === 'error'),
70
+ 'select-ghost': select && ghost,
71
+ 'select-xl': select && (size === 'xl' || xl),
72
+ 'select-lg': select && (size === 'lg' || lg),
73
+ 'select-md': select && (size === 'md' || md),
74
+ 'select-sm': select && (size === 'sm' || sm),
75
+ 'select-xs': select && (size === 'xs' || xs),
76
+ 'text-neutral': toggle && checked && (neutral || color === 'neutral'),
77
+ 'text-primary': toggle && checked && (primary || color === 'primary'),
78
+ 'text-secondary': toggle && checked && (secondary || color === 'secondary'),
79
+ 'text-accent': toggle && checked && (accent || color === 'accent'),
80
+ 'text-info': toggle && checked && (info || color === 'info'),
81
+ 'text-success': toggle && checked && (success || color === 'success'),
82
+ 'text-warning': toggle && checked && (warning || color === 'warning'),
83
+ 'text-error': toggle && checked && (error || color === 'error'),
84
+ 'text-ghost': toggle && ghost,
85
+ 'toggle-xl': toggle && (size === 'xl' || xl),
86
+ 'toggle-lg': toggle && (size === 'lg' || lg),
87
+ 'toggle-md': toggle && (size === 'md' || md),
88
+ 'toggle-sm': toggle && (size === 'sm' || sm),
89
+ 'toggle-xs': toggle && (size === 'xs' || xs),
54
90
  },
55
- { label: !floating && !input },
91
+ { label: !floating && !(input || toggle) },
56
92
  { 'floating-label': floating },
57
93
  ]"
58
94
  >
@@ -1,7 +1,13 @@
1
+ <script lang="ts" setup>
2
+ defineProps<{
3
+ displayClasses?: string
4
+ }>()
5
+ </script>
6
+
1
7
  <template>
2
8
  <div class="mockup-phone">
3
- <div class="camera" />
4
- <div class="display">
9
+ <div class="mockup-phone-camera" />
10
+ <div class="mockup-phone-display" :class="displayClasses">
5
11
  <slot />
6
12
  </div>
7
13
  </div>
@@ -1,10 +1,11 @@
1
1
  <script setup lang="ts">
2
- import { computed, inject } from 'vue';
2
+ import { computed, inject } from 'vue'
3
3
 
4
4
  const props = defineProps<{
5
5
  modelValue?: any
6
6
  value: any
7
7
  disabled?: boolean
8
+ themeController?: boolean
8
9
 
9
10
  color?: string
10
11
  neutral?: boolean
@@ -60,6 +61,7 @@ const currentValue = computed({
60
61
  'radio-md': props.md || props.size === 'md',
61
62
  'radio-lg': props.lg || props.size === 'lg',
62
63
  'radio-xl': props.xl || props.size === 'xl',
64
+ 'theme-controller': props.themeController,
63
65
  }"
64
66
  :disabled="props.disabled"
65
67
  :value="props.value"
@@ -1,5 +1,5 @@
1
1
  <script setup lang="ts">
2
- import { computed } from 'vue';
2
+ import { computed } from 'vue'
3
3
 
4
4
  type Color = | 'primary' | 'secondary' | 'accent' | 'info' | 'success' | 'warning' | 'error'
5
5
 
@@ -0,0 +1,46 @@
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 = props.themes || props.defaultTheme
22
+ ? { themes: props.themes || [], defaultTheme: props.defaultTheme }
23
+ : undefined
24
+
25
+ const { theme, effectiveTheme, themes, setTheme, cycleTheme, registerTheme, removeTheme } = useDaisyTheme(
26
+ props.storage,
27
+ themeOptions,
28
+ )
29
+
30
+ const availableThemes = computed(() => themes.value.map(t => typeof t === 'string' ? t : t.theme))
31
+ </script>
32
+
33
+ <template>
34
+ <div class="theme-controller">
35
+ <slot
36
+ :theme="theme"
37
+ :effective-theme="effectiveTheme"
38
+ :themes="themes"
39
+ :available-themes="availableThemes"
40
+ :set-theme="setTheme"
41
+ :cycle-theme="cycleTheme"
42
+ :register-theme="registerTheme"
43
+ :remove-theme="removeTheme"
44
+ />
45
+ </div>
46
+ </template>
@@ -0,0 +1,287 @@
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): { style: Record<string, string>, dataAttrs: Record<string, string>, dataTheme: string | undefined, allAttrs: Record<string, string> } {
23
+ if (!themeString) {
24
+ return { style: {}, dataAttrs: {}, dataTheme: undefined, allAttrs: {} }
25
+ }
26
+ const varNamePattern = /^--[\w-]+$/
27
+ const attrNamePattern = /^[a-z_][\w-]*$/i
28
+ const forbiddenChars = /[<>{};]/g
29
+
30
+ const style: Record<string, string> = {}
31
+ const dataAttrs: Record<string, string> = {}
32
+ let dataTheme: string | undefined
33
+ const allAttrs: Record<string, string> = {}
34
+
35
+ // Remove @plugin ... { and closing } if present
36
+ const str = themeString.trim().replace(/^@plugin[^{}]*\{/, '').replace(/\}$/, '').trim()
37
+
38
+ // works with or without newline characters
39
+ str.split(/[\n;]/).forEach((line) => {
40
+ const trimmed = line.trim()
41
+ if (!trimmed || trimmed.startsWith('//')) {
42
+ return
43
+ }
44
+ const colonIdx = trimmed.indexOf(':')
45
+ if (colonIdx === -1) {
46
+ return
47
+ }
48
+ const key = trimmed.slice(0, colonIdx).trim()
49
+ let value = trimmed.slice(colonIdx + 1).trim()
50
+ if (value.endsWith(';')) {
51
+ value = value.slice(0, -1).trim()
52
+ }
53
+ value = value.replace(forbiddenChars, '')
54
+ if (!value) {
55
+ return
56
+ }
57
+ if (varNamePattern.test(key)) {
58
+ style[key] = value
59
+ allAttrs[cssVarToCamel(key)] = value
60
+ }
61
+ else if (key === 'name') {
62
+ dataTheme = value.replace(/"/g, '')
63
+ allAttrs.dataTheme = dataTheme
64
+ }
65
+ else if (attrNamePattern.test(key)) {
66
+ const attrKey = `data-${key.replace(/[^\w-]/g, '')}`
67
+ const attrVal = value.replace(/"/g, '')
68
+ dataAttrs[attrKey] = attrVal
69
+ if (attrKey.startsWith('data-')) {
70
+ allAttrs[dataAttrToCamel(attrKey)] = attrVal
71
+ }
72
+ }
73
+ })
74
+ return { style, dataAttrs, dataTheme, allAttrs }
75
+ }
76
+
77
+ const parsed = computed(() => parseThemeString(props.dataTheme ?? props.cssVars))
78
+
79
+ const themeVars = ref<Record<string, string>>({})
80
+ let observers: MutationObserver[] = []
81
+ let themeControllerInputs: Array<{ el: HTMLInputElement, listener: () => void }> = []
82
+ let themeControllerDomObserver: MutationObserver | null = null
83
+
84
+ // Converts a themeAttrs object to a DaisyUI theme string
85
+ function toThemeString(attrs: Record<string, string>, opts?: { asPlugin?: boolean }) {
86
+ const lines: string[] = []
87
+ // Map camelCase back to DaisyUI keys
88
+ for (const [key, value] of Object.entries(attrs)) {
89
+ if (key === 'dataTheme') {
90
+ lines.push(`name: "${value}";`)
91
+ }
92
+ else if (key.startsWith('data')) {
93
+ // Convert dataFooBar -> foo-bar
94
+ const attr = key.slice(4).replace(/([A-Z])/g, '-$1').toLowerCase()
95
+ if (attr !== 'theme') {
96
+ lines.push(`${attr}: ${value};`)
97
+ }
98
+ }
99
+ else {
100
+ // Convert camelCase to --kebab-case
101
+ const cssVar = `--${key.replace(/([A-Z])/g, '-$1').toLowerCase()}`
102
+ lines.push(`${cssVar}: ${value};`)
103
+ }
104
+ }
105
+ const inner = lines.join('\n')
106
+ if (opts?.asPlugin) {
107
+ return `@plugin \"daisyui/theme\" {\n${inner}\n}`
108
+ }
109
+ return inner
110
+ }
111
+
112
+ // Returns the slot data object depending on snoop mode
113
+ const slotData: ComputedThemeVarsSlot = computed(() => {
114
+ // Access both dependencies so Vue always tracks them for reactivity
115
+ void themeVars.value
116
+ void parsed.value.allAttrs
117
+ if (props.snoop) {
118
+ return {
119
+ vars: themeVars.value,
120
+ toThemeString: (attrs = themeVars.value, opts) => toThemeString(attrs, opts),
121
+ }
122
+ }
123
+ return {
124
+ vars: parsed.value.allAttrs,
125
+ toThemeString: (attrs = parsed.value.allAttrs, opts) => toThemeString(attrs, opts),
126
+ }
127
+ })
128
+
129
+ // Provide the slotData as 'daisyUiTheme' for child consumers
130
+ provide(daisyUiThemeKey, slotData)
131
+
132
+ // DaisyUI variable names
133
+ const daisyVars = [
134
+ '--color-primary',
135
+ '--color-primary-content',
136
+ '--color-secondary',
137
+ '--color-secondary-content',
138
+ '--color-accent',
139
+ '--color-accent-content',
140
+ '--color-neutral',
141
+ '--color-neutral-content',
142
+ '--color-base-100',
143
+ '--color-base-200',
144
+ '--color-base-300',
145
+ '--color-base-content',
146
+ '--color-info',
147
+ '--color-info-content',
148
+ '--color-success',
149
+ '--color-success-content',
150
+ '--color-warning',
151
+ '--color-warning-content',
152
+ '--color-error',
153
+ '--color-error-content',
154
+ '--radius-selector',
155
+ '--radius-field',
156
+ '--radius-box',
157
+ '--size-selector',
158
+ '--size-field',
159
+ '--border',
160
+ '--depth',
161
+ '--noise',
162
+ ]
163
+
164
+ function getDaisyVarsFromEl(el: HTMLElement): Record<string, string> {
165
+ const vars: Record<string, string> = {}
166
+ const style = getComputedStyle(el)
167
+ for (const varName of daisyVars) {
168
+ const val = style.getPropertyValue(varName)
169
+ if (val) {
170
+ vars[cssVarToCamel(varName)] = val.trim()
171
+ }
172
+ }
173
+ return vars
174
+ }
175
+
176
+ let prefersColorSchemeMql: MediaQueryList | null = null
177
+ let prefersColorSchemeCleanup: (() => void) | null = null
178
+
179
+ function setupSnoop(rootEl: HTMLElement): void {
180
+ function updateVars(): void {
181
+ themeVars.value = getDaisyVarsFromEl(rootEl)
182
+ }
183
+ // Observe the current element for data-theme changes
184
+ const obs = new MutationObserver(() => updateVars())
185
+ obs.observe(rootEl, { attributes: true, attributeFilter: ['data-theme'] })
186
+
187
+ // Also observe the <html> element for data-theme changes
188
+ const htmlEl = document.documentElement
189
+ const htmlObs = new MutationObserver(() => updateVars())
190
+ htmlObs.observe(htmlEl, { attributes: true, attributeFilter: ['data-theme'] })
191
+
192
+ observers = [obs, htmlObs]
193
+
194
+ // Listen for prefers-color-scheme changes
195
+ prefersColorSchemeMql = window.matchMedia('(prefers-color-scheme: dark)')
196
+ const prefersColorSchemeListener = () => updateVars()
197
+ prefersColorSchemeMql.addEventListener('change', prefersColorSchemeListener)
198
+ prefersColorSchemeCleanup = () => {
199
+ prefersColorSchemeMql?.removeEventListener('change', prefersColorSchemeListener)
200
+ }
201
+
202
+ // --- Observe theme-controller checkboxes/radios, including dynamic changes ---
203
+ function bindThemeControllerInputs() {
204
+ // Remove any previous listeners
205
+ themeControllerInputs.forEach(({ el, listener }) => {
206
+ el.removeEventListener('change', listener)
207
+ })
208
+ themeControllerInputs = []
209
+ // Find all matching inputs in the document
210
+ const inputs = Array.from(document.querySelectorAll('input[type="checkbox"].theme-controller, input[type="radio"].theme-controller')) as HTMLInputElement[]
211
+ inputs.forEach((el) => {
212
+ const listener = () => updateVars()
213
+ el.addEventListener('change', listener)
214
+ themeControllerInputs.push({ el, listener })
215
+ })
216
+ }
217
+ bindThemeControllerInputs()
218
+ // Observe DOM for dynamic addition/removal of theme-controller inputs
219
+ if (themeControllerDomObserver) {
220
+ themeControllerDomObserver.disconnect()
221
+ themeControllerDomObserver = null
222
+ }
223
+ themeControllerDomObserver = new MutationObserver(() => {
224
+ bindThemeControllerInputs()
225
+ })
226
+ themeControllerDomObserver.observe(document.body, { childList: true, subtree: true })
227
+
228
+ // Watch for applied style changes (parsed.style)
229
+ watch(() => parsed.value.style, () => {
230
+ nextTick(() => updateVars())
231
+ }, { immediate: false, deep: true })
232
+ updateVars()
233
+ }
234
+
235
+ const rootEl = ref<HTMLElement | null>(null)
236
+
237
+ const isClient = typeof window !== 'undefined' && typeof document !== 'undefined'
238
+
239
+ watch([
240
+ () => props.snoop,
241
+ () => rootEl.value,
242
+ ], ([snoop, el]) => {
243
+ if (isClient) {
244
+ // Clean up previous observers if any
245
+ observers.forEach((o: MutationObserver) => o.disconnect())
246
+ observers = []
247
+ if (snoop && el) {
248
+ nextTick(() => {
249
+ setupSnoop(el)
250
+ })
251
+ }
252
+ }
253
+ }, { immediate: true })
254
+
255
+ onUnmounted(() => {
256
+ observers.forEach((o: MutationObserver) => o.disconnect())
257
+ observers = []
258
+ // Remove prefers-color-scheme listener if present
259
+ if (prefersColorSchemeCleanup) {
260
+ prefersColorSchemeCleanup()
261
+ prefersColorSchemeCleanup = null
262
+ prefersColorSchemeMql = null
263
+ }
264
+ // Remove theme-controller listeners
265
+ themeControllerInputs.forEach(({ el, listener }) => {
266
+ el.removeEventListener('change', listener)
267
+ })
268
+ themeControllerInputs = []
269
+ // Disconnect theme-controller DOM observer
270
+ if (themeControllerDomObserver) {
271
+ themeControllerDomObserver.disconnect()
272
+ themeControllerDomObserver = null
273
+ }
274
+ })
275
+ </script>
276
+
277
+ <template>
278
+ <div
279
+ ref="rootEl"
280
+ v-bind="parsed.dataAttrs"
281
+ :data-theme="dataTheme"
282
+ :style="parsed.style"
283
+ class="[background-color:unset] theme-provider"
284
+ >
285
+ <slot v-bind="slotData" />
286
+ </div>
287
+ </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>
@@ -1,10 +1,11 @@
1
1
  <script setup lang="ts">
2
- import { computed } from 'vue';
2
+ import { computed } from 'vue'
3
3
 
4
4
  const props = defineProps<{
5
5
  modelValue?: any
6
6
  disabled?: boolean
7
7
  validator?: boolean
8
+ themeController?: boolean
8
9
 
9
10
  color?: string
10
11
  neutral?: boolean
@@ -53,6 +54,7 @@ const currentValue = computed({
53
54
  'toggle-md': md || size === 'md',
54
55
  'toggle-lg': lg || size === 'lg',
55
56
  'toggle-xl': xl || size === 'xl',
57
+ 'theme-controller': themeController,
56
58
  }"
57
59
  >
58
60
  </template>
package/nuxt.js CHANGED
@@ -14,9 +14,15 @@ export default defineNuxtModule({
14
14
 
15
15
  nuxt.hook('components:dirs', (dirs) => {
16
16
  dirs.push({
17
- path: fileURLToPath(new URL('./components', import.meta.url)),
17
+ path: fileURLToPath(new URL('./app/components', import.meta.url)),
18
18
  prefix,
19
19
  })
20
20
  })
21
+
22
+ nuxt.hook('composables:dirs', (dirs) => {
23
+ dirs.push({
24
+ path: fileURLToPath(new URL('./app/composables-docs', import.meta.url)),
25
+ })
26
+ })
21
27
  },
22
28
  })
package/package.json CHANGED
@@ -1,14 +1,15 @@
1
1
  {
2
2
  "name": "daisy-ui-kit",
3
3
  "type": "module",
4
- "version": "5.0.0-pre.6",
5
- "packageManager": "pnpm@10.9.0",
4
+ "version": "5.0.0-pre.8",
5
+ "packageManager": "pnpm@10.10.0",
6
6
  "files": [
7
7
  "app/components/*.vue",
8
8
  "app/utils/*",
9
9
  "nuxt.js"
10
10
  ],
11
11
  "scripts": {
12
+ "import-d1-dumps": "./import-d1-dumps.sh",
12
13
  "build": "nuxt build",
13
14
  "dev": "nuxt dev",
14
15
  "lint": "eslint",
@@ -20,7 +21,8 @@
20
21
  "release:pre": "npm version prerelease && npm publish --tag pre",
21
22
  "release:patch": "npm version patch && npm publish",
22
23
  "release:minor": "npm version minor && npm publish",
23
- "release:major": "npm version major && npm publish"
24
+ "release:major": "npm version major && npm publish",
25
+ "deploy": "pnpm run build && npx wrangler pages deploy dist"
24
26
  },
25
27
  "peerDependencies": {
26
28
  "@vueuse/core": "^13.1.0",
@@ -28,34 +30,34 @@
28
30
  },
29
31
  "dependencies": {
30
32
  "@floating-ui/vue": "^1.1.6",
31
- "@nuxt/content": "3.4.0",
33
+ "@nuxt/content": "3.5.1",
32
34
  "@nuxt/eslint": "1.3.0",
33
- "@nuxt/fonts": "0.11.1",
35
+ "@nuxt/fonts": "0.11.2",
34
36
  "@nuxt/icon": "1.12.0",
35
37
  "@nuxt/image": "1.10.0",
36
- "@nuxt/kit": "^3.17.1",
38
+ "@nuxt/kit": "^3.17.2",
37
39
  "@nuxt/scripts": "0.11.6",
38
- "@nuxt/test-utils": "3.17.2",
40
+ "@nuxt/test-utils": "3.18.0",
39
41
  "@unhead/vue": "^2.0.8",
40
42
  "@vueuse/core": "^13.1.0",
41
43
  "@vueuse/integrations": "^13.1.0",
42
44
  "focus-trap": "^7.6.4",
43
- "nuxt": "^3.16.2",
45
+ "nuxt": "^3.17.2",
44
46
  "pikaday": "^1.8.2",
45
47
  "shiki": "^3.3.0",
46
48
  "typescript": "^5.8.3",
47
49
  "vue": "^3.5.13",
48
- "vue-router": "^4.5.0",
49
- "zod": "^3.24.3"
50
+ "vue-router": "^4.5.1",
51
+ "zod": "^3.24.4"
50
52
  },
51
53
  "devDependencies": {
52
54
  "@antfu/eslint-config": "^4.12.0",
53
55
  "@stylistic/eslint-plugin": "^4.2.0",
54
56
  "@tailwindcss/typography": "^0.5.16",
55
- "@tailwindcss/vite": "^4.1.4",
57
+ "@tailwindcss/vite": "^4.1.5",
56
58
  "@types/pikaday": "^1.7.9",
57
- "daisyui": "^5.0.27",
58
- "eslint": "^9.25.1",
59
- "tailwindcss": "^4.1.4"
59
+ "daisyui": "^5.0.35",
60
+ "eslint": "^9.26.0",
61
+ "tailwindcss": "^4.1.5"
60
62
  }
61
63
  }