daisy-ui-kit 5.0.0-pre.9 → 5.0.0
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 +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 +40 -29
- package/app/utils/random-string.ts +0 -19
|
@@ -0,0 +1,345 @@
|
|
|
1
|
+
import type { Ref } from 'vue'
|
|
2
|
+
import { computed, reactive, ref, toRef } from 'vue'
|
|
3
|
+
|
|
4
|
+
export type ToastType = 'success' | 'error' | 'info' | 'warning' | string
|
|
5
|
+
|
|
6
|
+
export type ToastPosition =
|
|
7
|
+
| 'top-start'
|
|
8
|
+
| 'top-center'
|
|
9
|
+
| 'top-end'
|
|
10
|
+
| 'middle-start'
|
|
11
|
+
| 'middle-center'
|
|
12
|
+
| 'middle-end'
|
|
13
|
+
| 'bottom-start'
|
|
14
|
+
| 'bottom-center'
|
|
15
|
+
| 'bottom-end'
|
|
16
|
+
|
|
17
|
+
export type ToastStatus = 'pending' | 'success' | 'error' | 'info' | 'warning' | 'default'
|
|
18
|
+
|
|
19
|
+
export interface Toast {
|
|
20
|
+
id: number
|
|
21
|
+
message: string
|
|
22
|
+
name?: string // toast channel name, optional but always set by logic
|
|
23
|
+
type?: ToastType
|
|
24
|
+
duration?: number
|
|
25
|
+
position: ToastPosition
|
|
26
|
+
countdown?: number
|
|
27
|
+
status?: ToastStatus
|
|
28
|
+
progress?: number // 0-1 for progress bar
|
|
29
|
+
promiseId?: string // for async/promise support
|
|
30
|
+
ariaLive?: 'polite' | 'assertive' // accessibility
|
|
31
|
+
[key: string]: any
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* State for a single toast channel (internal use)
|
|
36
|
+
*/
|
|
37
|
+
export interface ToastChannelState {
|
|
38
|
+
toasts: Ref<Toast[]>
|
|
39
|
+
toastQueue: Toast[]
|
|
40
|
+
toastLimit: number | null
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
let globalToastChannels: Ref<Record<string, ToastChannelState & { defaults?: Partial<Toast> }>> | null = null
|
|
44
|
+
let globalNextToastId: Ref<number> | null = null
|
|
45
|
+
|
|
46
|
+
function getGlobalToastChannels() {
|
|
47
|
+
if (!globalToastChannels) {
|
|
48
|
+
globalToastChannels = ref({})
|
|
49
|
+
}
|
|
50
|
+
return globalToastChannels
|
|
51
|
+
}
|
|
52
|
+
function getGlobalNextToastId() {
|
|
53
|
+
if (!globalNextToastId) {
|
|
54
|
+
globalNextToastId = ref(1)
|
|
55
|
+
}
|
|
56
|
+
return globalNextToastId
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function getOrCreateChannel(name: string, defaults?: Partial<Toast>, limit?: number | null) {
|
|
60
|
+
const channels = getGlobalToastChannels().value
|
|
61
|
+
if (!channels[name]) {
|
|
62
|
+
channels[name] = {
|
|
63
|
+
toasts: ref<Toast[]>([]),
|
|
64
|
+
toastQueue: [],
|
|
65
|
+
toastLimit: typeof limit === 'number' ? limit : null,
|
|
66
|
+
defaults,
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
return toRef(channels[name])
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Clear all toast queues for all channels (useful for logout or channel switch)
|
|
74
|
+
*/
|
|
75
|
+
export function clearAllToastQueues() {
|
|
76
|
+
const channels = getGlobalToastChannels().value
|
|
77
|
+
Object.values(channels).forEach(channel => {
|
|
78
|
+
channel.toastQueue.length = 0
|
|
79
|
+
})
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Global toast notification composable supporting named channels.
|
|
84
|
+
*
|
|
85
|
+
* - Each toast has an optional `name` property (defaults to 'default').
|
|
86
|
+
* - All toast state (toasts, queue, limit) is isolated per channel.
|
|
87
|
+
* - UI and logic can independently manage/display different channels.
|
|
88
|
+
* - Defensive dev warnings if name is missing/invalid.
|
|
89
|
+
*
|
|
90
|
+
* Example usage:
|
|
91
|
+
* const { toasts, addToast } = useToast({ name: 'admin' })
|
|
92
|
+
* addToast({ message: 'Hi', name: 'admin' })
|
|
93
|
+
* // In UI: <Toast name="admin" />
|
|
94
|
+
*/
|
|
95
|
+
/**
|
|
96
|
+
* Options for useToast composable.
|
|
97
|
+
* - name: channel name (default: 'default')
|
|
98
|
+
* - defaults: default toast settings for this channel (merged into each toast)
|
|
99
|
+
*/
|
|
100
|
+
export interface UseToastOptions {
|
|
101
|
+
name?: string
|
|
102
|
+
defaults?: Partial<Toast>
|
|
103
|
+
limit?: number // per-channel toast limit
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Global toast notification composable supporting named channels and customizable defaults.
|
|
108
|
+
*
|
|
109
|
+
* - Each toast has an optional `name` property (defaults to 'default').
|
|
110
|
+
* - All toast state (toasts, queue, limit) is isolated per channel.
|
|
111
|
+
* - UI and logic can independently manage/display different channels.
|
|
112
|
+
* - Defensive dev warnings if name is missing/invalid.
|
|
113
|
+
* - You can provide `defaults` to set default toast settings for all toasts in this channel.
|
|
114
|
+
*
|
|
115
|
+
* Example usage:
|
|
116
|
+
* const { toasts, addToast } = useToast({ name: 'admin', defaults: { duration: 6000, type: 'info' } })
|
|
117
|
+
* addToast({ message: 'Hi' }) // will use defaults
|
|
118
|
+
* // In UI: <Toast name="admin" />
|
|
119
|
+
*/
|
|
120
|
+
function normalizeToast(toast: any): Toast & { countdown: number; originalDuration: number; intervalId?: number } {
|
|
121
|
+
if (toast.originalDuration == null) {
|
|
122
|
+
toast.originalDuration = toast.duration ?? 0
|
|
123
|
+
}
|
|
124
|
+
if (toast.countdown == null) {
|
|
125
|
+
toast.countdown = toast.originalDuration
|
|
126
|
+
}
|
|
127
|
+
if (typeof toast.intervalId === 'undefined') {
|
|
128
|
+
toast.intervalId = undefined
|
|
129
|
+
}
|
|
130
|
+
return toast as Toast & { countdown: number; originalDuration: number; intervalId?: number }
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
export function useToast(options?: UseToastOptions) {
|
|
134
|
+
const name = options?.name?.trim() || 'default'
|
|
135
|
+
const defaults = options?.defaults || {}
|
|
136
|
+
const limit = typeof options?.limit === 'number' ? options.limit : null
|
|
137
|
+
|
|
138
|
+
// Always get or create the channel (init only once)
|
|
139
|
+
const channel = getOrCreateChannel(name, defaults, limit)
|
|
140
|
+
|
|
141
|
+
function addToast(
|
|
142
|
+
toast: { message: string; position?: ToastPosition; name?: string } & Partial<Omit<Toast, 'id' | 'name'>>,
|
|
143
|
+
) {
|
|
144
|
+
const toastName = toast.name?.trim() || name
|
|
145
|
+
const nextToastIdRef = getGlobalNextToastId()
|
|
146
|
+
// Always get or create the target channel for this toast, using the correct limit if provided
|
|
147
|
+
const channelLimit = typeof options?.limit === 'number' ? options.limit : null
|
|
148
|
+
const channel = getOrCreateChannel(toastName, defaults, channelLimit)
|
|
149
|
+
const merged = { ...channel.value.defaults, ...defaults, ...toast }
|
|
150
|
+
const position = merged.position ?? 'bottom-center'
|
|
151
|
+
const duration = merged.duration ?? 0
|
|
152
|
+
const status = merged.status ?? 'default'
|
|
153
|
+
if (import.meta.env.NODE_ENV !== 'production' && !toastName) {
|
|
154
|
+
console.warn('[addToast] Toast channel name is empty or invalid. Falling back to "default".')
|
|
155
|
+
}
|
|
156
|
+
const id = nextToastIdRef.value++
|
|
157
|
+
const newToast = normalizeToast(
|
|
158
|
+
reactive({
|
|
159
|
+
id,
|
|
160
|
+
...merged,
|
|
161
|
+
name: toastName,
|
|
162
|
+
position,
|
|
163
|
+
countdown: duration,
|
|
164
|
+
originalDuration: duration,
|
|
165
|
+
status,
|
|
166
|
+
progress: typeof merged.progress === 'number' ? merged.progress : undefined,
|
|
167
|
+
ariaLive: merged.ariaLive ?? 'polite',
|
|
168
|
+
intervalId: undefined as number | undefined,
|
|
169
|
+
}),
|
|
170
|
+
)
|
|
171
|
+
|
|
172
|
+
// If limit is set and reached, queue the toast
|
|
173
|
+
if (channel.value.toastLimit && channel.value.toasts.length >= channel.value.toastLimit) {
|
|
174
|
+
;(channel.value.toastQueue ??= []).push(newToast)
|
|
175
|
+
return id
|
|
176
|
+
}
|
|
177
|
+
channel.value.toasts.push(newToast)
|
|
178
|
+
// Ensure timer always starts for visible toasts
|
|
179
|
+
if (channel.value.toasts.includes(newToast)) {
|
|
180
|
+
startToastTimer(newToast)
|
|
181
|
+
}
|
|
182
|
+
return id
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
function removeToast(id: number) {
|
|
186
|
+
const idx = channel.value.toasts.findIndex(t => t.id === id)
|
|
187
|
+
if (idx !== -1) {
|
|
188
|
+
// Defensive: clear countdown timer if present
|
|
189
|
+
const toast = channel.value.toasts[idx] as Toast & { intervalId?: number }
|
|
190
|
+
if (toast.intervalId) {
|
|
191
|
+
clearInterval(toast.intervalId)
|
|
192
|
+
}
|
|
193
|
+
channel.value.toasts.splice(idx, 1)
|
|
194
|
+
// If queue exists, pop next toast
|
|
195
|
+
if (channel.value.toastLimit && (channel.value.toastQueue?.length ?? 0) > 0) {
|
|
196
|
+
const next = channel.value.toastQueue!.shift()
|
|
197
|
+
if (next) {
|
|
198
|
+
const norm = normalizeToast(next)
|
|
199
|
+
channel.value.toasts.push(norm)
|
|
200
|
+
// Ensure timer always starts for visible toasts
|
|
201
|
+
if (channel.value.toasts.includes(norm)) {
|
|
202
|
+
startToastTimer(norm)
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
function clearToasts() {
|
|
210
|
+
channel.value.toasts = []
|
|
211
|
+
if (channel.value.toastQueue) {
|
|
212
|
+
channel.value.toastQueue.length = 0
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
/**
|
|
217
|
+
* Update or replace a toast by id
|
|
218
|
+
*/
|
|
219
|
+
function updateToast(id: number, updates: Partial<Toast>) {
|
|
220
|
+
const toast = channel.value.toasts.find(t => t.id === id)
|
|
221
|
+
if (toast) {
|
|
222
|
+
Object.assign(toast, updates)
|
|
223
|
+
normalizeToast(toast)
|
|
224
|
+
// If updating countdown/duration, recalculate progress
|
|
225
|
+
if (typeof toast.countdown === 'number' && typeof toast.duration === 'number' && toast.duration > 0) {
|
|
226
|
+
toast.progress = Math.max(0, toast.countdown / toast.duration)
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
/**
|
|
232
|
+
* Show a toast for the duration of a promise
|
|
233
|
+
* Sets status to 'pending', then 'success' or 'error' on resolve/reject
|
|
234
|
+
*/
|
|
235
|
+
function toastPromise<T>(
|
|
236
|
+
promise: Promise<T>,
|
|
237
|
+
options: {
|
|
238
|
+
pending: Omit<Toast, 'id'>
|
|
239
|
+
success: Omit<Toast, 'id'>
|
|
240
|
+
error: Omit<Toast, 'id'>
|
|
241
|
+
name?: string
|
|
242
|
+
},
|
|
243
|
+
): Promise<T> {
|
|
244
|
+
const toastName = options.name?.trim() || name
|
|
245
|
+
if (import.meta.env.NODE_ENV !== 'production' && !toastName) {
|
|
246
|
+
console.warn('[toastPromise] Toast channel name is empty or invalid. Falling back to "default".')
|
|
247
|
+
}
|
|
248
|
+
const promiseId = `promise-${Date.now()}-${Math.random()}`
|
|
249
|
+
const pendingId = addToast({
|
|
250
|
+
...options.pending,
|
|
251
|
+
status: 'pending',
|
|
252
|
+
promiseId,
|
|
253
|
+
message: options.pending.message,
|
|
254
|
+
name: toastName,
|
|
255
|
+
})
|
|
256
|
+
return promise.then(
|
|
257
|
+
result => {
|
|
258
|
+
updateToast(pendingId, {
|
|
259
|
+
...options.success,
|
|
260
|
+
status: 'success',
|
|
261
|
+
promiseId,
|
|
262
|
+
message: options.success.message,
|
|
263
|
+
name: toastName,
|
|
264
|
+
})
|
|
265
|
+
return result
|
|
266
|
+
},
|
|
267
|
+
err => {
|
|
268
|
+
updateToast(pendingId, {
|
|
269
|
+
...options.error,
|
|
270
|
+
status: 'error',
|
|
271
|
+
promiseId,
|
|
272
|
+
message: options.error.message,
|
|
273
|
+
name: toastName,
|
|
274
|
+
})
|
|
275
|
+
throw err
|
|
276
|
+
},
|
|
277
|
+
)
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
/**
|
|
281
|
+
* Set a maximum number of visible toasts (queue extras)
|
|
282
|
+
*/
|
|
283
|
+
function setToastLimit(limit: number) {
|
|
284
|
+
channel.value.toastLimit = limit
|
|
285
|
+
if (channel.value.toastLimit) {
|
|
286
|
+
// If over limit, move extras to queue
|
|
287
|
+
while (channel.value.toasts.length > channel.value.toastLimit) {
|
|
288
|
+
const removed = channel.value.toasts.pop()
|
|
289
|
+
if (removed) {
|
|
290
|
+
// Stop timer for toast leaving visible list
|
|
291
|
+
if (removed.intervalId) {
|
|
292
|
+
clearInterval(removed.intervalId)
|
|
293
|
+
removed.intervalId = undefined
|
|
294
|
+
}
|
|
295
|
+
;(channel.value.toastQueue ??= []).unshift(removed)
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
// If under limit and queue has items, fill up
|
|
299
|
+
while (channel.value.toasts.length < channel.value.toastLimit && (channel.value.toastQueue?.length ?? 0) > 0) {
|
|
300
|
+
const next = channel.value.toastQueue!.shift()
|
|
301
|
+
if (next) {
|
|
302
|
+
const norm = normalizeToast(next)
|
|
303
|
+
channel.value.toasts.push(norm)
|
|
304
|
+
// Ensure timer always starts for visible toasts
|
|
305
|
+
if (channel.value.toasts.includes(norm)) {
|
|
306
|
+
startToastTimer(norm)
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
function startToastTimer(
|
|
314
|
+
toast: Toast & { countdown: number; originalDuration: number; intervalId?: number; startTime?: number },
|
|
315
|
+
) {
|
|
316
|
+
if (toast.originalDuration > 0) {
|
|
317
|
+
if (toast.intervalId) {
|
|
318
|
+
clearInterval(toast.intervalId)
|
|
319
|
+
}
|
|
320
|
+
const start = Date.now()
|
|
321
|
+
toast.startTime = start
|
|
322
|
+
|
|
323
|
+
toast.intervalId = setInterval(() => {
|
|
324
|
+
const elapsed = Date.now() - (toast.startTime ?? start)
|
|
325
|
+
toast.countdown = Math.max(0, toast.originalDuration - elapsed)
|
|
326
|
+
toast.progress = Math.max(0, toast.countdown / toast.originalDuration)
|
|
327
|
+
if (toast.countdown <= 0) {
|
|
328
|
+
clearInterval(toast.intervalId)
|
|
329
|
+
removeToast(toast.id)
|
|
330
|
+
}
|
|
331
|
+
}, 16) as unknown as number // 60fps for smooth animation
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
return {
|
|
336
|
+
toasts: computed(() => channel.value.toasts),
|
|
337
|
+
addToast,
|
|
338
|
+
removeToast,
|
|
339
|
+
clearToasts,
|
|
340
|
+
updateToast,
|
|
341
|
+
toastPromise,
|
|
342
|
+
setToastLimit,
|
|
343
|
+
name,
|
|
344
|
+
}
|
|
345
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
const isSearchOpen = ref(false)
|
|
2
|
+
|
|
3
|
+
export function useSearch() {
|
|
4
|
+
function openSearch() {
|
|
5
|
+
isSearchOpen.value = true
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
function closeSearch() {
|
|
9
|
+
isSearchOpen.value = false
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
function toggleSearch() {
|
|
13
|
+
isSearchOpen.value = !isSearchOpen.value
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
return {
|
|
17
|
+
isSearchOpen,
|
|
18
|
+
openSearch,
|
|
19
|
+
closeSearch,
|
|
20
|
+
toggleSearch,
|
|
21
|
+
}
|
|
22
|
+
}
|
|
@@ -14,19 +14,21 @@ export interface Drawers {
|
|
|
14
14
|
export const drawers = reactive<Drawers>({})
|
|
15
15
|
|
|
16
16
|
export function createDrawerState(name: string) {
|
|
17
|
-
const drawerState =
|
|
18
|
-
name
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
17
|
+
const drawerState =
|
|
18
|
+
drawers[name] ||
|
|
19
|
+
reactive<DrawerState>({
|
|
20
|
+
name,
|
|
21
|
+
isDrawerOpen: false,
|
|
22
|
+
openDrawer() {
|
|
23
|
+
drawerState.isDrawerOpen = true
|
|
24
|
+
},
|
|
25
|
+
closeDrawer() {
|
|
26
|
+
drawerState.isDrawerOpen = false
|
|
27
|
+
},
|
|
28
|
+
toggleDrawer() {
|
|
29
|
+
drawerState.isDrawerOpen = !drawerState.isDrawerOpen
|
|
30
|
+
},
|
|
31
|
+
})
|
|
30
32
|
drawers[name] = drawerState
|
|
31
33
|
return drawerState
|
|
32
34
|
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Maps placement values to CSS position-area values for anchor positioning
|
|
3
|
+
*
|
|
4
|
+
* Using span to align edges:
|
|
5
|
+
* - "bottom span-right" = below anchor, aligned to left edge (spans from left to center)
|
|
6
|
+
* - "bottom span-left" = below anchor, aligned to right edge (spans from center to right)
|
|
7
|
+
*/
|
|
8
|
+
export function getPositionArea(placement: string): string {
|
|
9
|
+
const positionMap: Record<string, string> = {
|
|
10
|
+
// Top positions
|
|
11
|
+
top: 'center top', // centered above
|
|
12
|
+
'top-start': 'top span-right', // above, left edge aligned
|
|
13
|
+
'top-end': 'top span-left', // above, right edge aligned
|
|
14
|
+
|
|
15
|
+
// Right positions
|
|
16
|
+
right: 'center right', // centered to right
|
|
17
|
+
'right-start': 'right span-bottom', // right, top edge aligned
|
|
18
|
+
'right-end': 'right span-top', // right, bottom edge aligned
|
|
19
|
+
|
|
20
|
+
// Bottom positions (most common for dropdowns)
|
|
21
|
+
bottom: 'center bottom', // centered below
|
|
22
|
+
'bottom-start': 'bottom span-right', // below, left edge aligned
|
|
23
|
+
'bottom-end': 'bottom span-left', // below, right edge aligned
|
|
24
|
+
|
|
25
|
+
// Left positions
|
|
26
|
+
left: 'center left', // centered to left
|
|
27
|
+
'left-start': 'left span-bottom', // left, top edge aligned
|
|
28
|
+
'left-end': 'left span-top', // left, bottom edge aligned
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
return positionMap[placement] || 'bottom span-right'
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Gets position-try-fallbacks for automatic repositioning
|
|
36
|
+
*/
|
|
37
|
+
export function getPositionFallbacks(_placement: string): string {
|
|
38
|
+
// Simple flip fallbacks - flip to opposite side on same axis
|
|
39
|
+
return 'flip-block, flip-inline'
|
|
40
|
+
}
|
package/nuxt.d.ts
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { NuxtModule } from '@nuxt/schema'
|
|
2
|
+
|
|
3
|
+
export interface ModuleOptions {
|
|
4
|
+
/**
|
|
5
|
+
* Prefix for all DaisyUI Kit components
|
|
6
|
+
* @default ''
|
|
7
|
+
*/
|
|
8
|
+
prefix?: string
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
declare const module: NuxtModule<ModuleOptions>
|
|
12
|
+
|
|
13
|
+
export default module
|
package/nuxt.js
CHANGED
|
@@ -1,28 +1,31 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { defineNuxtModule } from '@nuxt/kit'
|
|
1
|
+
import { addImports, createResolver, defineNuxtModule } from '@nuxt/kit'
|
|
3
2
|
|
|
4
3
|
export default defineNuxtModule({
|
|
5
4
|
meta: {
|
|
6
5
|
name: 'daisy-ui-kit',
|
|
7
6
|
configKey: 'daisy',
|
|
7
|
+
compatibility: {
|
|
8
|
+
nuxt: '>=3.0.0',
|
|
9
|
+
},
|
|
8
10
|
},
|
|
9
11
|
defaults: {
|
|
10
12
|
prefix: '',
|
|
11
13
|
},
|
|
12
14
|
setup(moduleOptions, nuxt) {
|
|
13
15
|
const { prefix } = moduleOptions
|
|
16
|
+
const { resolve } = createResolver(import.meta.url)
|
|
14
17
|
|
|
15
|
-
nuxt.hook('components:dirs',
|
|
18
|
+
nuxt.hook('components:dirs', dirs => {
|
|
16
19
|
dirs.push({
|
|
17
|
-
path:
|
|
20
|
+
path: resolve('./app/components'),
|
|
18
21
|
prefix,
|
|
19
22
|
})
|
|
20
23
|
})
|
|
21
24
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
}
|
|
26
|
-
|
|
25
|
+
addImports([
|
|
26
|
+
{ name: 'useCalendar', from: resolve('./app/composables/use-calendar') },
|
|
27
|
+
{ name: 'useDaisyTheme', from: resolve('./app/composables/use-daisy-theme') },
|
|
28
|
+
{ name: 'useToast', from: resolve('./app/composables/use-toast') },
|
|
29
|
+
])
|
|
27
30
|
},
|
|
28
31
|
})
|
package/package.json
CHANGED
|
@@ -1,63 +1,52 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "daisy-ui-kit",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "5.0.0
|
|
5
|
-
"
|
|
6
|
-
"main": "./nuxt.js",
|
|
7
|
-
"module": "./nuxt.js",
|
|
4
|
+
"version": "5.0.0",
|
|
5
|
+
"author": "feathers.dev",
|
|
8
6
|
"exports": {
|
|
9
7
|
".": {
|
|
8
|
+
"types": "./nuxt.d.ts",
|
|
10
9
|
"import": "./nuxt.js",
|
|
11
10
|
"require": "./nuxt.js"
|
|
12
11
|
},
|
|
13
12
|
"./nuxt": {
|
|
13
|
+
"types": "./nuxt.d.ts",
|
|
14
14
|
"import": "./nuxt.js",
|
|
15
15
|
"require": "./nuxt.js"
|
|
16
16
|
},
|
|
17
17
|
"./components/*": "./app/components/*",
|
|
18
|
+
"./composables/*": "./app/composables/*",
|
|
18
19
|
"./utils/*": "./app/utils/*"
|
|
19
20
|
},
|
|
21
|
+
"main": "./nuxt.js",
|
|
22
|
+
"module": "./nuxt.js",
|
|
23
|
+
"types": "./nuxt.d.ts",
|
|
20
24
|
"files": [
|
|
21
25
|
"app/components/*.vue",
|
|
26
|
+
"app/composables/*",
|
|
22
27
|
"app/utils/*",
|
|
23
|
-
"nuxt.js"
|
|
28
|
+
"nuxt.js",
|
|
29
|
+
"nuxt.d.ts"
|
|
24
30
|
],
|
|
25
|
-
"scripts": {
|
|
26
|
-
"import-d1-dumps": "./import-d1-dumps.sh",
|
|
27
|
-
"build": "nuxt build",
|
|
28
|
-
"dev": "nuxt dev",
|
|
29
|
-
"lint": "eslint",
|
|
30
|
-
"lint:fix": "eslint --fix",
|
|
31
|
-
"generate": "nuxt generate",
|
|
32
|
-
"preview": "nuxt preview",
|
|
33
|
-
"postinstall": "nuxt prepare",
|
|
34
|
-
"publish": "git push origin --tags && git push origin",
|
|
35
|
-
"release:pre": "npm version prerelease && npm publish --tag pre",
|
|
36
|
-
"release:patch": "npm version patch && npm publish",
|
|
37
|
-
"release:minor": "npm version minor && npm publish",
|
|
38
|
-
"release:major": "npm version major && npm publish",
|
|
39
|
-
"deploy": "pnpm run build && npx wrangler pages deploy dist"
|
|
40
|
-
},
|
|
41
31
|
"peerDependencies": {
|
|
42
32
|
"@vueuse/core": "^13.1.0",
|
|
43
33
|
"daisyui": "^5"
|
|
44
34
|
},
|
|
45
35
|
"dependencies": {
|
|
46
|
-
"@floating-ui/vue": "^1.1.6",
|
|
47
36
|
"@nuxt/content": "3.5.1",
|
|
48
37
|
"@nuxt/eslint": "1.3.0",
|
|
49
38
|
"@nuxt/fonts": "0.11.2",
|
|
50
39
|
"@nuxt/icon": "1.12.0",
|
|
51
40
|
"@nuxt/image": "1.10.0",
|
|
52
|
-
"@nuxt/kit": "^
|
|
41
|
+
"@nuxt/kit": "^4.2.1",
|
|
53
42
|
"@nuxt/scripts": "0.11.6",
|
|
54
43
|
"@nuxt/test-utils": "3.18.0",
|
|
44
|
+
"@oddbird/css-anchor-positioning": "^0.7.0",
|
|
55
45
|
"@unhead/vue": "^2.0.8",
|
|
56
46
|
"@vueuse/core": "^13.1.0",
|
|
57
47
|
"@vueuse/integrations": "^13.1.0",
|
|
58
48
|
"focus-trap": "^7.6.4",
|
|
59
|
-
"nuxt": "^
|
|
60
|
-
"pikaday": "^1.8.2",
|
|
49
|
+
"nuxt": "^4.2.1",
|
|
61
50
|
"shiki": "^3.3.0",
|
|
62
51
|
"typescript": "^5.8.3",
|
|
63
52
|
"vue": "^3.5.13",
|
|
@@ -69,9 +58,31 @@
|
|
|
69
58
|
"@stylistic/eslint-plugin": "^4.2.0",
|
|
70
59
|
"@tailwindcss/typography": "^0.5.16",
|
|
71
60
|
"@tailwindcss/vite": "^4.1.5",
|
|
72
|
-
"@
|
|
73
|
-
"
|
|
61
|
+
"@vitejs/plugin-vue": "^6.0.3",
|
|
62
|
+
"@vue/test-utils": "^2.4.6",
|
|
63
|
+
"daisyui": "^5.5.5",
|
|
74
64
|
"eslint": "^9.26.0",
|
|
75
|
-
"
|
|
65
|
+
"eslint-config-prettier": "^10.1.8",
|
|
66
|
+
"eslint-plugin-vue": "^10.5.1",
|
|
67
|
+
"happy-dom": "^20.0.11",
|
|
68
|
+
"tailwindcss": "^4.1.5",
|
|
69
|
+
"vitest": "^4.0.16"
|
|
70
|
+
},
|
|
71
|
+
"scripts": {
|
|
72
|
+
"import-d1-dumps": "./import-d1-dumps.sh",
|
|
73
|
+
"prebuild": "cp CHANGELOG.md content/docs/changelog.md",
|
|
74
|
+
"build": "NODE_OPTIONS='--max-old-space-size=7168' nuxt build",
|
|
75
|
+
"predev": "cp CHANGELOG.md content/docs/changelog.md",
|
|
76
|
+
"dev": "nuxt dev",
|
|
77
|
+
"lint": "eslint",
|
|
78
|
+
"lint:fix": "eslint --fix",
|
|
79
|
+
"generate": "nuxt generate",
|
|
80
|
+
"preview": "nuxt preview",
|
|
81
|
+
"postinstall": "nuxt prepare",
|
|
82
|
+
"release:pre": "node scripts/release.js prerelease",
|
|
83
|
+
"release:patch": "node scripts/release.js patch",
|
|
84
|
+
"release:minor": "node scripts/release.js minor",
|
|
85
|
+
"release:major": "node scripts/release.js major",
|
|
86
|
+
"deploy": "pnpm run build && npx wrangler deploy"
|
|
76
87
|
}
|
|
77
|
-
}
|
|
88
|
+
}
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Generate a random string of `length` characters
|
|
3
|
-
*
|
|
4
|
-
* Usage:
|
|
5
|
-
* const myString = randomString(15); // This will give you a random string of 15 characters
|
|
6
|
-
* @param length The length of the string to generate
|
|
7
|
-
* @returns A random string of `length` characters
|
|
8
|
-
*/
|
|
9
|
-
export function randomString(length: number = 10): string {
|
|
10
|
-
const characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'
|
|
11
|
-
let result = ''
|
|
12
|
-
|
|
13
|
-
for (let i = 0; i < length; i++) {
|
|
14
|
-
const randomIndex = Math.floor(Math.random() * characters.length)
|
|
15
|
-
result += characters[randomIndex]
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
return result
|
|
19
|
-
}
|