create-nuxt-base 0.3.16 → 0.3.17
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/CHANGELOG.md +2 -0
- package/nuxt-base-template/.env.example +1 -1
- package/nuxt-base-template/CLAUDE.md +361 -0
- package/nuxt-base-template/README.md +127 -13
- package/nuxt-base-template/app/app.config.ts +67 -0
- package/nuxt-base-template/app/app.vue +10 -2
- package/nuxt-base-template/app/assets/css/tailwind.css +124 -84
- package/nuxt-base-template/app/components/Modal/ModalBase.vue +65 -0
- package/nuxt-base-template/app/components/Transition/TransitionSlide.vue +0 -2
- package/nuxt-base-template/app/components/Transition/TransitionSlideBottom.vue +0 -2
- package/nuxt-base-template/app/components/Transition/TransitionSlideRevert.vue +0 -2
- package/nuxt-base-template/app/composables/use-file.ts +19 -3
- package/nuxt-base-template/app/composables/use-share.ts +26 -10
- package/nuxt-base-template/app/error.vue +7 -43
- package/nuxt-base-template/app/layouts/default.vue +76 -4
- package/nuxt-base-template/app/layouts/slim.vue +5 -0
- package/nuxt-base-template/app/pages/auth/forgot-password.vue +64 -0
- package/nuxt-base-template/app/pages/auth/login.vue +71 -0
- package/nuxt-base-template/app/pages/auth/reset-password/[token].vue +110 -0
- package/nuxt-base-template/app/pages/index.vue +139 -2
- package/nuxt-base-template/app/public/favicon.ico +0 -0
- package/nuxt-base-template/docs/nuxt.config.ts +4 -0
- package/nuxt-base-template/docs/pages/docs.vue +663 -0
- package/nuxt-base-template/eslint.config.mjs +2 -1
- package/nuxt-base-template/nuxt.config.ts +72 -30
- package/nuxt-base-template/openapi-ts.config.ts +18 -0
- package/nuxt-base-template/package-lock.json +9781 -15157
- package/nuxt-base-template/package.json +30 -35
- package/nuxt-base-template/tsconfig.json +1 -1
- package/package.json +3 -3
- package/nuxt-base-template/app/composables/use-context-menu.ts +0 -19
- package/nuxt-base-template/app/composables/use-form-helper.ts +0 -41
- package/nuxt-base-template/app/composables/use-modal.ts +0 -84
- package/nuxt-base-template/app/composables/use-notification.ts +0 -29
- package/nuxt-base-template/app/middleware/admin.global.ts +0 -9
- package/nuxt-base-template/app/middleware/auth.global.ts +0 -9
- package/nuxt-base-template/app/middleware/logged-in.global.ts +0 -9
- package/nuxt-base-template/app/plugins/auth.server.ts +0 -72
- package/nuxt-base-template/app/plugins/form.plugin.ts +0 -21
- package/nuxt-base-template/app/plugins/pwa.plugin.ts +0 -114
- package/nuxt-base-template/tailwind.config.js +0 -21
|
@@ -0,0 +1,663 @@
|
|
|
1
|
+
<script lang="ts" setup>
|
|
2
|
+
import type { RadioGroupItem } from '#ui/components/RadioGroup.vue';
|
|
3
|
+
import type { SelectItem } from '#ui/components/Select.vue';
|
|
4
|
+
import type { SelectMenuItem } from '#ui/components/SelectMenu.vue';
|
|
5
|
+
// ============================================================================
|
|
6
|
+
// Imports
|
|
7
|
+
// ============================================================================
|
|
8
|
+
import type { InferOutput } from 'valibot';
|
|
9
|
+
|
|
10
|
+
import { ModalBase } from '#components';
|
|
11
|
+
import * as v from 'valibot';
|
|
12
|
+
|
|
13
|
+
// ============================================================================
|
|
14
|
+
// Composables
|
|
15
|
+
// ============================================================================
|
|
16
|
+
const toast = useToast();
|
|
17
|
+
const colorMode = useColorMode();
|
|
18
|
+
const overlay = useOverlay();
|
|
19
|
+
|
|
20
|
+
// ============================================================================
|
|
21
|
+
// Page Meta
|
|
22
|
+
// ============================================================================
|
|
23
|
+
definePageMeta({
|
|
24
|
+
middleware: () => {
|
|
25
|
+
const config = useRuntimeConfig();
|
|
26
|
+
if (config.public.appEnv === 'production') {
|
|
27
|
+
return navigateTo('/');
|
|
28
|
+
}
|
|
29
|
+
},
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
// ============================================================================
|
|
33
|
+
// Variables
|
|
34
|
+
// ============================================================================
|
|
35
|
+
// Button variants for showcase
|
|
36
|
+
const buttonVariants: Array<'ghost' | 'link' | 'outline' | 'soft' | 'solid'> = ['solid', 'outline', 'soft', 'ghost', 'link'];
|
|
37
|
+
const buttonColors: Array<'error' | 'info' | 'neutral' | 'primary' | 'secondary' | 'success' | 'warning'> = [
|
|
38
|
+
'primary',
|
|
39
|
+
'secondary',
|
|
40
|
+
'success',
|
|
41
|
+
'error',
|
|
42
|
+
'warning',
|
|
43
|
+
'info',
|
|
44
|
+
'neutral',
|
|
45
|
+
];
|
|
46
|
+
|
|
47
|
+
// Form Schema and State
|
|
48
|
+
// Valibot validation schema - demonstrates various validation patterns
|
|
49
|
+
const formSchema = v.object({
|
|
50
|
+
// Boolean with custom validation - checkbox must be checked (true)
|
|
51
|
+
acceptTerms: v.pipe(
|
|
52
|
+
v.boolean(),
|
|
53
|
+
v.custom((value) => value === true, 'You must accept the terms and conditions'),
|
|
54
|
+
),
|
|
55
|
+
// Number with range and type validation
|
|
56
|
+
age: v.pipe(
|
|
57
|
+
v.number('Age must be a number'),
|
|
58
|
+
v.minValue(18, 'You must be at least 18 years old'),
|
|
59
|
+
v.maxValue(120, 'Please enter a valid age'),
|
|
60
|
+
v.integer('Age must be a whole number'),
|
|
61
|
+
),
|
|
62
|
+
// Optional field with max length
|
|
63
|
+
bio: v.optional(v.pipe(v.string(), v.maxLength(500, 'Bio must not exceed 500 characters'))),
|
|
64
|
+
// Required string (select value)
|
|
65
|
+
country: v.pipe(v.string(), v.minLength(1, 'Please select a country')),
|
|
66
|
+
// Email validation
|
|
67
|
+
email: v.pipe(v.string(), v.email('Please enter a valid email address')),
|
|
68
|
+
// Optional file upload with custom validation (type and size)
|
|
69
|
+
image: v.optional(
|
|
70
|
+
v.custom<File>(
|
|
71
|
+
(value) => value instanceof File && ['image/gif', 'image/jpeg', 'image/png'].includes(value.type) && value.size <= 2 * 1024 * 1024,
|
|
72
|
+
'Please upload an image file (JPG, PNG, GIF) with max. 2MB',
|
|
73
|
+
),
|
|
74
|
+
),
|
|
75
|
+
// Array validation - at least one item required
|
|
76
|
+
interests: v.pipe(v.array(v.string()), v.minLength(1, 'Please select at least one interest')),
|
|
77
|
+
// String with min and max length
|
|
78
|
+
name: v.pipe(v.string(), v.minLength(2, 'Name must be at least 2 characters'), v.maxLength(50, 'Name must not exceed 50 characters')),
|
|
79
|
+
// Simple boolean without custom validation
|
|
80
|
+
newsletter: v.boolean(),
|
|
81
|
+
// Password with minimum length
|
|
82
|
+
password: v.pipe(v.string(), v.minLength(8, 'Password must be at least 8 characters')),
|
|
83
|
+
// Required radio group selection
|
|
84
|
+
preferredContact: v.pipe(v.string(), v.minLength(1, 'Please select a preferred contact method')),
|
|
85
|
+
// Number (slider) with minimum value
|
|
86
|
+
salary: v.pipe(v.number(), v.minValue(0, 'Salary must be positive'), v.integer()),
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
type FormSchema = InferOutput<typeof formSchema>;
|
|
90
|
+
|
|
91
|
+
const formState = reactive<FormSchema>({
|
|
92
|
+
acceptTerms: false,
|
|
93
|
+
age: 25,
|
|
94
|
+
bio: '',
|
|
95
|
+
country: 'de',
|
|
96
|
+
email: '',
|
|
97
|
+
image: undefined,
|
|
98
|
+
interests: [],
|
|
99
|
+
name: '',
|
|
100
|
+
newsletter: false,
|
|
101
|
+
password: '',
|
|
102
|
+
preferredContact: '',
|
|
103
|
+
salary: 50000,
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
const countryOptions = ref<SelectItem[]>([
|
|
107
|
+
{
|
|
108
|
+
label: 'Germany',
|
|
109
|
+
value: 'de',
|
|
110
|
+
},
|
|
111
|
+
{
|
|
112
|
+
label: 'United Kingdom',
|
|
113
|
+
value: 'en',
|
|
114
|
+
},
|
|
115
|
+
{
|
|
116
|
+
label: 'Spain',
|
|
117
|
+
value: 'es',
|
|
118
|
+
},
|
|
119
|
+
{
|
|
120
|
+
label: 'Italy',
|
|
121
|
+
value: 'it',
|
|
122
|
+
},
|
|
123
|
+
]);
|
|
124
|
+
|
|
125
|
+
const interestOptions = ref<SelectMenuItem[]>([
|
|
126
|
+
{
|
|
127
|
+
label: 'Frontend Development',
|
|
128
|
+
value: 'frontend',
|
|
129
|
+
},
|
|
130
|
+
{
|
|
131
|
+
label: 'Backend Development',
|
|
132
|
+
value: 'backend',
|
|
133
|
+
},
|
|
134
|
+
{
|
|
135
|
+
label: 'DevOps',
|
|
136
|
+
value: 'devops',
|
|
137
|
+
},
|
|
138
|
+
{
|
|
139
|
+
label: 'UI/UX Design',
|
|
140
|
+
value: 'design',
|
|
141
|
+
},
|
|
142
|
+
{
|
|
143
|
+
label: 'Mobile Development',
|
|
144
|
+
value: 'mobile',
|
|
145
|
+
},
|
|
146
|
+
]);
|
|
147
|
+
|
|
148
|
+
const contactMethodOptions = ref<RadioGroupItem[]>([
|
|
149
|
+
{
|
|
150
|
+
description: 'Email as option.',
|
|
151
|
+
label: 'Email',
|
|
152
|
+
value: 'email',
|
|
153
|
+
},
|
|
154
|
+
{
|
|
155
|
+
description: 'Phone as option.',
|
|
156
|
+
label: 'Phone',
|
|
157
|
+
value: 'phone',
|
|
158
|
+
},
|
|
159
|
+
{
|
|
160
|
+
description: 'SMS as option.',
|
|
161
|
+
label: 'SMS',
|
|
162
|
+
value: 'sms',
|
|
163
|
+
},
|
|
164
|
+
{
|
|
165
|
+
description: 'Pigeon as option.',
|
|
166
|
+
label: 'Pigeon',
|
|
167
|
+
value: 'pigeon',
|
|
168
|
+
},
|
|
169
|
+
]);
|
|
170
|
+
|
|
171
|
+
// ============================================================================
|
|
172
|
+
// Functions
|
|
173
|
+
// ============================================================================
|
|
174
|
+
async function handleFormSubmit(): Promise<void> {
|
|
175
|
+
toast.add({
|
|
176
|
+
color: 'success',
|
|
177
|
+
description: 'All form fields are valid! Check the browser console for the submitted data.',
|
|
178
|
+
title: 'Form Submitted',
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
console.info('Form Data:', formState);
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
async function handleShare(): Promise<void> {
|
|
185
|
+
const { share } = useShare();
|
|
186
|
+
await share('Nuxt Base Starter', 'Check out this Nuxt Base Starter Template!', window.location.href);
|
|
187
|
+
|
|
188
|
+
toast.add({
|
|
189
|
+
color: 'success',
|
|
190
|
+
description: 'Content shared successfully or copied to clipboard!',
|
|
191
|
+
title: 'Shared',
|
|
192
|
+
});
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
async function openModal(): Promise<void> {
|
|
196
|
+
const modal = overlay.create(ModalBase);
|
|
197
|
+
const instance = modal.open({
|
|
198
|
+
description: 'This demonstrates the useOverlay composable for programmatic modal control.',
|
|
199
|
+
title: 'Programmatic Modal',
|
|
200
|
+
});
|
|
201
|
+
|
|
202
|
+
const result = await instance.result;
|
|
203
|
+
|
|
204
|
+
if (result) {
|
|
205
|
+
toast.add({
|
|
206
|
+
color: 'success',
|
|
207
|
+
description: 'You confirmed the modal action.',
|
|
208
|
+
title: 'Confirmed',
|
|
209
|
+
});
|
|
210
|
+
} else {
|
|
211
|
+
toast.add({
|
|
212
|
+
color: 'neutral',
|
|
213
|
+
description: 'Modal was dismissed.',
|
|
214
|
+
title: 'Dismissed',
|
|
215
|
+
});
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
function showToast(type: 'error' | 'info' | 'success' | 'warning'): void {
|
|
220
|
+
const messages: Record<string, { description: string; title: string }> = {
|
|
221
|
+
error: {
|
|
222
|
+
description: 'Something went wrong.',
|
|
223
|
+
title: 'Error',
|
|
224
|
+
},
|
|
225
|
+
info: {
|
|
226
|
+
description: 'Here is some information.',
|
|
227
|
+
title: 'Info',
|
|
228
|
+
},
|
|
229
|
+
success: {
|
|
230
|
+
description: 'Your action was successful!',
|
|
231
|
+
title: 'Success',
|
|
232
|
+
},
|
|
233
|
+
warning: {
|
|
234
|
+
description: 'Please be careful.',
|
|
235
|
+
title: 'Warning',
|
|
236
|
+
},
|
|
237
|
+
};
|
|
238
|
+
|
|
239
|
+
toast.add({
|
|
240
|
+
color: type,
|
|
241
|
+
description: messages[type].description,
|
|
242
|
+
title: messages[type].title,
|
|
243
|
+
});
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
function toggleColorMode(): void {
|
|
247
|
+
colorMode.preference = colorMode.value === 'dark' ? 'light' : 'dark';
|
|
248
|
+
}
|
|
249
|
+
</script>
|
|
250
|
+
|
|
251
|
+
<template>
|
|
252
|
+
<div>
|
|
253
|
+
<div class="mx-auto max-w-7xl px-6 py-16 lg:px-8">
|
|
254
|
+
<!-- Header -->
|
|
255
|
+
<div class="mb-12">
|
|
256
|
+
<div class="flex items-center justify-between mb-4">
|
|
257
|
+
<div>
|
|
258
|
+
<UBadge color="warning" variant="subtle" size="lg" class="mb-4"> Development Only </UBadge>
|
|
259
|
+
<h1 class="text-4xl font-bold tracking-tight text-neutral-900 dark:text-white sm:text-5xl">Interactive Examples</h1>
|
|
260
|
+
<p class="mt-4 text-lg text-neutral-600 dark:text-neutral-400">
|
|
261
|
+
This page is only available in development mode and showcases some interactive examples of the template.
|
|
262
|
+
</p>
|
|
263
|
+
</div>
|
|
264
|
+
<UButton to="/" icon="i-lucide-arrow-left" variant="outline" color="neutral" size="lg"> Back to Home </UButton>
|
|
265
|
+
</div>
|
|
266
|
+
</div>
|
|
267
|
+
|
|
268
|
+
<!-- Interactive Examples -->
|
|
269
|
+
<div class="space-y-8">
|
|
270
|
+
<UAlert
|
|
271
|
+
title="Note"
|
|
272
|
+
description="These examples demonstrate the most important UI components and composables of the template. Perfect for testing and as a reference for new projects."
|
|
273
|
+
icon="i-lucide-lightbulb"
|
|
274
|
+
color="primary"
|
|
275
|
+
variant="subtle"
|
|
276
|
+
/>
|
|
277
|
+
|
|
278
|
+
<!-- Button Showcase -->
|
|
279
|
+
<UCard variant="outline">
|
|
280
|
+
<template #header>
|
|
281
|
+
<div class="flex items-center justify-between">
|
|
282
|
+
<h3 class="text-xl font-semibold text-neutral-900 dark:text-white">Buttons</h3>
|
|
283
|
+
<UButton to="https://ui.nuxt.com/components/button" target="_blank" trailing-icon="i-lucide-external-link" variant="ghost" color="neutral" size="xs"> Docs </UButton>
|
|
284
|
+
</div>
|
|
285
|
+
</template>
|
|
286
|
+
|
|
287
|
+
<div class="space-y-6">
|
|
288
|
+
<p class="text-sm text-neutral-600 dark:text-neutral-400">Interactive buttons with multiple variants, sizes, and states. Essential for all user interactions.</p>
|
|
289
|
+
|
|
290
|
+
<!-- Variants -->
|
|
291
|
+
<div>
|
|
292
|
+
<h4 class="text-sm font-medium text-neutral-900 dark:text-white mb-3">Variants</h4>
|
|
293
|
+
<div class="flex flex-wrap gap-2">
|
|
294
|
+
<UButton v-for="variant in buttonVariants" :key="variant" :variant="variant" color="primary"> {{ variant }} </UButton>
|
|
295
|
+
</div>
|
|
296
|
+
</div>
|
|
297
|
+
|
|
298
|
+
<USeparator />
|
|
299
|
+
|
|
300
|
+
<!-- Colors -->
|
|
301
|
+
<div>
|
|
302
|
+
<h4 class="text-sm font-medium text-neutral-900 dark:text-white mb-3">Colors (Solid Variant)</h4>
|
|
303
|
+
<div class="flex flex-wrap gap-2">
|
|
304
|
+
<UButton v-for="color in buttonColors" :key="color" :color="color"> {{ color }} </UButton>
|
|
305
|
+
</div>
|
|
306
|
+
</div>
|
|
307
|
+
|
|
308
|
+
<USeparator />
|
|
309
|
+
|
|
310
|
+
<!-- Sizes & Icons -->
|
|
311
|
+
<div>
|
|
312
|
+
<h4 class="text-sm font-medium text-neutral-900 dark:text-white mb-3">Sizes & Icons</h4>
|
|
313
|
+
<div class="flex flex-wrap items-center gap-2">
|
|
314
|
+
<UButton size="xs" icon="i-lucide-star"> Extra Small </UButton>
|
|
315
|
+
<UButton size="sm" icon="i-lucide-star"> Small </UButton>
|
|
316
|
+
<UButton size="md" icon="i-lucide-star"> Medium </UButton>
|
|
317
|
+
<UButton size="lg" icon="i-lucide-star"> Large </UButton>
|
|
318
|
+
<UButton size="xl" icon="i-lucide-star"> Extra Large </UButton>
|
|
319
|
+
</div>
|
|
320
|
+
</div>
|
|
321
|
+
|
|
322
|
+
<USeparator />
|
|
323
|
+
|
|
324
|
+
<!-- States -->
|
|
325
|
+
<div>
|
|
326
|
+
<h4 class="text-sm font-medium text-neutral-900 dark:text-white mb-3">States</h4>
|
|
327
|
+
<div class="flex flex-wrap gap-2">
|
|
328
|
+
<UButton color="primary"> Normal </UButton>
|
|
329
|
+
<UButton color="primary" loading> Loading </UButton>
|
|
330
|
+
<UButton color="primary" disabled> Disabled </UButton>
|
|
331
|
+
<UButton color="primary" icon="i-lucide-download" trailing-icon="i-lucide-arrow-right"> With Icons </UButton>
|
|
332
|
+
</div>
|
|
333
|
+
</div>
|
|
334
|
+
</div>
|
|
335
|
+
</UCard>
|
|
336
|
+
|
|
337
|
+
<!-- Interactive Components -->
|
|
338
|
+
<UCard variant="outline">
|
|
339
|
+
<template #header>
|
|
340
|
+
<h3 class="text-xl font-semibold text-neutral-900 dark:text-white">Interactive Components</h3>
|
|
341
|
+
</template>
|
|
342
|
+
|
|
343
|
+
<div class="space-y-6">
|
|
344
|
+
<!-- Toast Notifications -->
|
|
345
|
+
<div>
|
|
346
|
+
<div class="flex items-center justify-between mb-3">
|
|
347
|
+
<h4 class="text-sm font-medium text-neutral-900 dark:text-white">Toast Notifications</h4>
|
|
348
|
+
<UButton to="https://ui.nuxt.com/composables/use-toast" target="_blank" trailing-icon="i-lucide-external-link" variant="ghost" color="neutral" size="xs">
|
|
349
|
+
Docs
|
|
350
|
+
</UButton>
|
|
351
|
+
</div>
|
|
352
|
+
<p class="text-sm text-neutral-600 dark:text-neutral-400 mb-3">
|
|
353
|
+
Use <code class="px-1.5 py-0.5 bg-neutral-100 dark:bg-neutral-800 rounded text-xs">useToast()</code> for notifications.
|
|
354
|
+
</p>
|
|
355
|
+
<div class="flex flex-wrap gap-2 mb-3">
|
|
356
|
+
<UButton color="success" size="sm" @click="showToast('success')"> Success </UButton>
|
|
357
|
+
<UButton color="error" size="sm" @click="showToast('error')"> Error </UButton>
|
|
358
|
+
<UButton color="info" size="sm" @click="showToast('info')"> Info </UButton>
|
|
359
|
+
<UButton color="warning" size="sm" @click="showToast('warning')"> Warning </UButton>
|
|
360
|
+
</div>
|
|
361
|
+
</div>
|
|
362
|
+
|
|
363
|
+
<USeparator />
|
|
364
|
+
|
|
365
|
+
<!-- Color Mode Toggle -->
|
|
366
|
+
<div>
|
|
367
|
+
<div class="flex items-center justify-between mb-3">
|
|
368
|
+
<h4 class="text-sm font-medium text-neutral-900 dark:text-white">Color Mode</h4>
|
|
369
|
+
<UButton to="https://ui.nuxt.com/getting-started/color-mode" target="_blank" trailing-icon="i-lucide-external-link" variant="ghost" color="neutral" size="xs">
|
|
370
|
+
Docs
|
|
371
|
+
</UButton>
|
|
372
|
+
</div>
|
|
373
|
+
<p class="text-sm text-neutral-600 dark:text-neutral-400 mb-3">
|
|
374
|
+
Use <code class="px-1.5 py-0.5 bg-neutral-100 dark:bg-neutral-800 rounded text-xs">useColorMode()</code> for Dark/Light Mode.
|
|
375
|
+
</p>
|
|
376
|
+
<div class="flex items-center gap-4 mb-3">
|
|
377
|
+
<UButton :icon="colorMode.value === 'dark' ? 'i-lucide-sun' : 'i-lucide-moon'" color="neutral" variant="outline" size="sm" @click="toggleColorMode">
|
|
378
|
+
Toggle {{ colorMode.value === 'dark' ? 'Light' : 'Dark' }} Mode
|
|
379
|
+
</UButton>
|
|
380
|
+
<UBadge :color="colorMode.value === 'dark' ? 'primary' : 'warning'" size="md"> Current: {{ colorMode.value }} </UBadge>
|
|
381
|
+
</div>
|
|
382
|
+
</div>
|
|
383
|
+
|
|
384
|
+
<USeparator />
|
|
385
|
+
|
|
386
|
+
<!-- Badges -->
|
|
387
|
+
<div>
|
|
388
|
+
<div class="flex items-center justify-between mb-3">
|
|
389
|
+
<h4 class="text-sm font-medium text-neutral-900 dark:text-white">Badges & Variants</h4>
|
|
390
|
+
<UButton to="https://ui.nuxt.com/components/badge" target="_blank" trailing-icon="i-lucide-external-link" variant="ghost" color="neutral" size="xs"> Docs </UButton>
|
|
391
|
+
</div>
|
|
392
|
+
<p class="text-sm text-neutral-600 dark:text-neutral-400 mb-3">Various badge variants and colors for different purposes.</p>
|
|
393
|
+
<div class="flex flex-wrap gap-2 mb-3">
|
|
394
|
+
<UBadge color="primary" variant="solid"> Primary </UBadge>
|
|
395
|
+
<UBadge color="secondary" variant="solid"> Secondary </UBadge>
|
|
396
|
+
<UBadge color="success" variant="outline"> Success </UBadge>
|
|
397
|
+
<UBadge color="error" variant="soft"> Error </UBadge>
|
|
398
|
+
<UBadge color="info" variant="subtle" icon="i-lucide-info"> Info </UBadge>
|
|
399
|
+
</div>
|
|
400
|
+
</div>
|
|
401
|
+
|
|
402
|
+
<USeparator />
|
|
403
|
+
|
|
404
|
+
<!-- Modal -->
|
|
405
|
+
<div>
|
|
406
|
+
<div class="flex items-center justify-between mb-3">
|
|
407
|
+
<h4 class="text-sm font-medium text-neutral-900 dark:text-white">Programmatic Modal</h4>
|
|
408
|
+
<UButton
|
|
409
|
+
to="https://ui.nuxt.com/docs/components/modal#programmatic-usage"
|
|
410
|
+
target="_blank"
|
|
411
|
+
trailing-icon="i-lucide-external-link"
|
|
412
|
+
variant="ghost"
|
|
413
|
+
color="neutral"
|
|
414
|
+
size="xs"
|
|
415
|
+
>
|
|
416
|
+
Docs
|
|
417
|
+
</UButton>
|
|
418
|
+
</div>
|
|
419
|
+
<p class="text-sm text-neutral-600 dark:text-neutral-400 mb-3">
|
|
420
|
+
Use the <code class="px-1.5 py-0.5 bg-neutral-100 dark:bg-neutral-800 rounded text-xs">useOverlay()</code> composable to open modals programmatically and await the
|
|
421
|
+
result.
|
|
422
|
+
</p>
|
|
423
|
+
<UButton icon="i-lucide-square-dashed-mouse-pointer" color="primary" size="sm" variant="outline" class="mb-3" @click="openModal"> Open Modal </UButton>
|
|
424
|
+
</div>
|
|
425
|
+
</div>
|
|
426
|
+
</UCard>
|
|
427
|
+
|
|
428
|
+
<!-- Form Example with Valibot -->
|
|
429
|
+
<UCard variant="outline">
|
|
430
|
+
<template #header>
|
|
431
|
+
<div class="flex items-center justify-between">
|
|
432
|
+
<h3 class="text-xl font-semibold text-neutral-900 dark:text-white">Form Validation with Valibot</h3>
|
|
433
|
+
<UButton to="https://ui.nuxt.com/components/form" target="_blank" trailing-icon="i-lucide-external-link" variant="ghost" color="neutral" size="xs"> Docs </UButton>
|
|
434
|
+
</div>
|
|
435
|
+
</template>
|
|
436
|
+
|
|
437
|
+
<div class="space-y-4">
|
|
438
|
+
<p class="text-sm text-neutral-600 dark:text-neutral-400 mb-4">
|
|
439
|
+
This form demonstrates all important Nuxt UI input components with Valibot validation. Fill in all required fields and observe the real-time validation.
|
|
440
|
+
</p>
|
|
441
|
+
|
|
442
|
+
<UForm :state="formState" :schema="formSchema" class="space-y-6" @submit="handleFormSubmit">
|
|
443
|
+
<!-- Basic Text Inputs -->
|
|
444
|
+
<div class="space-y-4">
|
|
445
|
+
<h4 class="text-sm font-semibold text-neutral-900 dark:text-white flex items-center gap-2">
|
|
446
|
+
Text Inputs
|
|
447
|
+
<UButton to="https://ui.nuxt.com/components/input" target="_blank" trailing-icon="i-lucide-external-link" variant="ghost" color="neutral" size="xs">
|
|
448
|
+
Docs
|
|
449
|
+
</UButton>
|
|
450
|
+
</h4>
|
|
451
|
+
<div class="grid md:grid-cols-2 gap-4">
|
|
452
|
+
<UFormField label="Full Name" name="name" required>
|
|
453
|
+
<UInput v-model="formState.name" placeholder="Enter your full name" icon="i-lucide-user" />
|
|
454
|
+
</UFormField>
|
|
455
|
+
|
|
456
|
+
<UFormField label="Email Address" name="email" required>
|
|
457
|
+
<UInput v-model="formState.email" type="email" placeholder="your.email@example.com" icon="i-lucide-mail" />
|
|
458
|
+
</UFormField>
|
|
459
|
+
|
|
460
|
+
<UFormField label="Password" name="password" required help="At least 8 characters">
|
|
461
|
+
<UInput v-model="formState.password" type="password" placeholder="Enter a secure password" icon="i-lucide-lock" />
|
|
462
|
+
</UFormField>
|
|
463
|
+
|
|
464
|
+
<UFormField label="Age" name="age" required help="You must be at least 18 years old">
|
|
465
|
+
<UInput v-model.number="formState.age" type="number" placeholder="Enter your age" icon="i-lucide-calendar" />
|
|
466
|
+
</UFormField>
|
|
467
|
+
</div>
|
|
468
|
+
</div>
|
|
469
|
+
|
|
470
|
+
<USeparator />
|
|
471
|
+
|
|
472
|
+
<!-- Textarea -->
|
|
473
|
+
<div class="space-y-4">
|
|
474
|
+
<h4 class="text-sm font-semibold text-neutral-900 dark:text-white flex items-center gap-2">
|
|
475
|
+
Textarea
|
|
476
|
+
<UButton to="https://ui.nuxt.com/components/textarea" target="_blank" trailing-icon="i-lucide-external-link" variant="ghost" color="neutral" size="xs">
|
|
477
|
+
Docs
|
|
478
|
+
</UButton>
|
|
479
|
+
</h4>
|
|
480
|
+
<UFormField label="Bio" name="bio" help="Optional - maximum 500 characters">
|
|
481
|
+
<UTextarea v-model="formState.bio" placeholder="Tell us about yourself..." :rows="4" />
|
|
482
|
+
</UFormField>
|
|
483
|
+
</div>
|
|
484
|
+
|
|
485
|
+
<USeparator />
|
|
486
|
+
|
|
487
|
+
<!-- Selection Components -->
|
|
488
|
+
<div class="space-y-4">
|
|
489
|
+
<h4 class="text-sm font-semibold text-neutral-900 dark:text-white">Selection Components</h4>
|
|
490
|
+
<div class="grid md:grid-cols-2 gap-4">
|
|
491
|
+
<UFormField label="Country" name="country" required>
|
|
492
|
+
<template #help>
|
|
493
|
+
<span class="flex items-center gap-1">
|
|
494
|
+
Select
|
|
495
|
+
<UButton to="https://ui.nuxt.com/components/select" target="_blank" trailing-icon="i-lucide-external-link" variant="ghost" color="neutral" size="xs">
|
|
496
|
+
Docs
|
|
497
|
+
</UButton>
|
|
498
|
+
</span>
|
|
499
|
+
</template>
|
|
500
|
+
<USelect v-model="formState.country" :items="countryOptions" placeholder="Select your country" />
|
|
501
|
+
</UFormField>
|
|
502
|
+
|
|
503
|
+
<UFormField label="Areas of Interest" name="interests" required>
|
|
504
|
+
<template #help>
|
|
505
|
+
<span class="flex items-center gap-1">
|
|
506
|
+
Select Menu
|
|
507
|
+
<UButton to="https://ui.nuxt.com/components/select-menu" target="_blank" trailing-icon="i-lucide-external-link" variant="ghost" color="neutral" size="xs">
|
|
508
|
+
Docs
|
|
509
|
+
</UButton>
|
|
510
|
+
</span>
|
|
511
|
+
</template>
|
|
512
|
+
<USelectMenu v-model="formState.interests" :items="interestOptions" value-key="value" multiple placeholder="Select your interests" />
|
|
513
|
+
</UFormField>
|
|
514
|
+
</div>
|
|
515
|
+
</div>
|
|
516
|
+
|
|
517
|
+
<USeparator />
|
|
518
|
+
|
|
519
|
+
<!-- Slider & Toggles -->
|
|
520
|
+
<div class="space-y-4">
|
|
521
|
+
<h4 class="text-sm font-semibold text-neutral-900 dark:text-white">Slider & Toggles</h4>
|
|
522
|
+
<div class="grid md:grid-cols-2 gap-4">
|
|
523
|
+
<UFormField label="Expected Salary (USD)" name="salary" :help="`$${formState.salary.toLocaleString()} per year`">
|
|
524
|
+
<template #label>
|
|
525
|
+
<span class="flex items-center gap-1">
|
|
526
|
+
Expected Salary (USD)
|
|
527
|
+
<UButton to="https://ui.nuxt.com/components/slider" target="_blank" trailing-icon="i-lucide-external-link" variant="ghost" color="neutral" size="xs">
|
|
528
|
+
Docs
|
|
529
|
+
</UButton>
|
|
530
|
+
</span>
|
|
531
|
+
</template>
|
|
532
|
+
<USlider v-model="formState.salary" :min="0" :max="200000" :step="1000" />
|
|
533
|
+
</UFormField>
|
|
534
|
+
|
|
535
|
+
<UFormField label="Newsletter Subscription" name="newsletter" help="Receive updates about new features">
|
|
536
|
+
<template #label>
|
|
537
|
+
<span class="flex items-center gap-1">
|
|
538
|
+
Newsletter Subscription
|
|
539
|
+
<UButton to="https://ui.nuxt.com/components/switch" target="_blank" trailing-icon="i-lucide-external-link" variant="ghost" color="neutral" size="xs">
|
|
540
|
+
Docs
|
|
541
|
+
</UButton>
|
|
542
|
+
</span>
|
|
543
|
+
</template>
|
|
544
|
+
<USwitch v-model="formState.newsletter" />
|
|
545
|
+
</UFormField>
|
|
546
|
+
</div>
|
|
547
|
+
</div>
|
|
548
|
+
|
|
549
|
+
<USeparator />
|
|
550
|
+
|
|
551
|
+
<!-- Radio Group -->
|
|
552
|
+
<div class="space-y-4">
|
|
553
|
+
<h4 class="text-sm font-semibold text-neutral-900 dark:text-white flex items-center gap-2">
|
|
554
|
+
Radio Group
|
|
555
|
+
<UButton to="https://ui.nuxt.com/components/radio-group" target="_blank" trailing-icon="i-lucide-external-link" variant="ghost" color="neutral" size="xs">
|
|
556
|
+
Docs
|
|
557
|
+
</UButton>
|
|
558
|
+
</h4>
|
|
559
|
+
<UFormField label="Preferred Contact Method" name="preferredContact" required>
|
|
560
|
+
<URadioGroup v-model="formState.preferredContact" :items="contactMethodOptions" />
|
|
561
|
+
</UFormField>
|
|
562
|
+
</div>
|
|
563
|
+
|
|
564
|
+
<USeparator />
|
|
565
|
+
|
|
566
|
+
<!-- File Upload -->
|
|
567
|
+
<div class="space-y-4">
|
|
568
|
+
<h4 class="text-sm font-semibold text-neutral-900 dark:text-white flex items-center gap-2">
|
|
569
|
+
File Upload
|
|
570
|
+
<UButton to="https://ui.nuxt.com/components/file-upload" target="_blank" trailing-icon="i-lucide-external-link" variant="ghost" color="neutral" size="xs">
|
|
571
|
+
Docs
|
|
572
|
+
</UButton>
|
|
573
|
+
</h4>
|
|
574
|
+
<UFormField name="image" label="Image" description="JPG, GIF or PNG. 2MB Max.">
|
|
575
|
+
<UFileUpload v-model="formState.image" accept="image/*" class="min-h-48" />
|
|
576
|
+
</UFormField>
|
|
577
|
+
</div>
|
|
578
|
+
|
|
579
|
+
<USeparator />
|
|
580
|
+
|
|
581
|
+
<!-- Checkbox -->
|
|
582
|
+
<div class="space-y-4">
|
|
583
|
+
<h4 class="text-sm font-semibold text-neutral-900 dark:text-white flex items-center gap-2">
|
|
584
|
+
Checkbox
|
|
585
|
+
<UButton to="https://ui.nuxt.com/components/checkbox" target="_blank" trailing-icon="i-lucide-external-link" variant="ghost" color="neutral" size="xs">
|
|
586
|
+
Docs
|
|
587
|
+
</UButton>
|
|
588
|
+
</h4>
|
|
589
|
+
<UFormField name="acceptTerms" required>
|
|
590
|
+
<UCheckbox v-model="formState.acceptTerms" label="I accept the terms and conditions" />
|
|
591
|
+
</UFormField>
|
|
592
|
+
</div>
|
|
593
|
+
|
|
594
|
+
<USeparator />
|
|
595
|
+
|
|
596
|
+
<!-- Submit Button -->
|
|
597
|
+
<div class="flex items-center gap-3 pt-4">
|
|
598
|
+
<UButton type="submit" color="primary" size="lg" icon="i-lucide-send"> Submit Form </UButton>
|
|
599
|
+
<p class="text-xs text-neutral-500">All data will be logged to the console</p>
|
|
600
|
+
</div>
|
|
601
|
+
</UForm>
|
|
602
|
+
</div>
|
|
603
|
+
</UCard>
|
|
604
|
+
|
|
605
|
+
<!-- Custom Composables -->
|
|
606
|
+
<UCard variant="outline">
|
|
607
|
+
<template #header>
|
|
608
|
+
<h3 class="text-xl font-semibold text-neutral-900 dark:text-white">Template Composables</h3>
|
|
609
|
+
</template>
|
|
610
|
+
|
|
611
|
+
<div class="space-y-6">
|
|
612
|
+
<p class="text-sm text-neutral-600 dark:text-neutral-400">This template includes custom composables for common tasks. These are auto-imported and ready to use.</p>
|
|
613
|
+
|
|
614
|
+
<!-- useShare -->
|
|
615
|
+
<div>
|
|
616
|
+
<div class="flex items-center justify-between mb-3">
|
|
617
|
+
<h4 class="text-sm font-medium text-neutral-900 dark:text-white">useShare</h4>
|
|
618
|
+
<UBadge color="primary" variant="subtle"> Auto-imported </UBadge>
|
|
619
|
+
</div>
|
|
620
|
+
<p class="text-sm text-neutral-600 dark:text-neutral-400 mb-3">Share content using the Web Share API with automatic fallback.</p>
|
|
621
|
+
<UButton size="sm" icon="i-lucide-share-2" class="mb-3" @click="handleShare"> Share Content </UButton>
|
|
622
|
+
</div>
|
|
623
|
+
</div>
|
|
624
|
+
</UCard>
|
|
625
|
+
|
|
626
|
+
<!-- Documentation Links -->
|
|
627
|
+
<UCard variant="outline">
|
|
628
|
+
<template #header>
|
|
629
|
+
<h3 class="text-xl font-semibold text-neutral-900 dark:text-white">Additional Resources</h3>
|
|
630
|
+
</template>
|
|
631
|
+
|
|
632
|
+
<div class="space-y-3">
|
|
633
|
+
<div class="flex items-center justify-between p-3 bg-neutral-50 dark:bg-neutral-900 rounded-lg">
|
|
634
|
+
<div>
|
|
635
|
+
<h4 class="font-medium text-neutral-900 dark:text-white">Nuxt UI Documentation</h4>
|
|
636
|
+
<p class="text-sm text-neutral-600 dark:text-neutral-400">All available components and their props</p>
|
|
637
|
+
</div>
|
|
638
|
+
<UButton to="https://ui.nuxt.com" target="_blank" trailing-icon="i-lucide-external-link" variant="outline" color="neutral" size="sm"> Docs </UButton>
|
|
639
|
+
</div>
|
|
640
|
+
|
|
641
|
+
<div class="flex items-center justify-between p-3 bg-neutral-50 dark:bg-neutral-900 rounded-lg">
|
|
642
|
+
<div>
|
|
643
|
+
<h4 class="font-medium text-neutral-900 dark:text-white">GitHub Repository</h4>
|
|
644
|
+
<p class="text-sm text-neutral-600 dark:text-neutral-400">Source code and additional examples</p>
|
|
645
|
+
</div>
|
|
646
|
+
<UButton to="https://github.com/lenneTech/nuxt-base-starter" target="_blank" trailing-icon="i-lucide-external-link" variant="outline" color="neutral" size="sm">
|
|
647
|
+
GitHub
|
|
648
|
+
</UButton>
|
|
649
|
+
</div>
|
|
650
|
+
|
|
651
|
+
<div class="flex items-center justify-between p-3 bg-neutral-50 dark:bg-neutral-900 rounded-lg">
|
|
652
|
+
<div>
|
|
653
|
+
<h4 class="font-medium text-neutral-900 dark:text-white">CLAUDE.md</h4>
|
|
654
|
+
<p class="text-sm text-neutral-600 dark:text-neutral-400">Detailed project documentation</p>
|
|
655
|
+
</div>
|
|
656
|
+
<UBadge color="primary" variant="subtle"> Included in project </UBadge>
|
|
657
|
+
</div>
|
|
658
|
+
</div>
|
|
659
|
+
</UCard>
|
|
660
|
+
</div>
|
|
661
|
+
</div>
|
|
662
|
+
</div>
|
|
663
|
+
</template>
|