vuetify-nuxt4-module 1.2.0 → 1.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +674 -155
- package/dist/module.json +1 -1
- package/dist/module.mjs +5 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -3,27 +3,50 @@
|
|
|
3
3
|
[](https://npmjs.com/package/vuetify-nuxt4-module)
|
|
4
4
|
[](https://npm.chart.dev/vuetify-nuxt4-module)
|
|
5
5
|
[](https://npmjs.com/package/vuetify-nuxt4-module)
|
|
6
|
-
[](https://github.com/KHNexTech/Vuetify-Nuxt4-Module/actions/workflows/ci.yml)
|
|
7
6
|
[](https://nuxt.com)
|
|
8
7
|
|
|
9
|
-
Zero-config Vuetify 3 module for Nuxt 4 with performance optimizations.
|
|
8
|
+
Zero-config Vuetify 3 module for Nuxt 4 with performance optimizations, SSR support, and full TypeScript integration.
|
|
10
9
|
|
|
11
10
|
- [✨ Release Notes](/CHANGELOG.md)
|
|
11
|
+
- [📖 Documentation](#documentation)
|
|
12
12
|
|
|
13
13
|
## Features
|
|
14
14
|
|
|
15
15
|
- ⚡ **Auto-import** - Automatic tree-shaking with vite-plugin-vuetify
|
|
16
|
-
- 🎨 **Theme Persistence** -
|
|
16
|
+
- 🎨 **Theme Persistence** - SSR-safe cookie/localStorage/sessionStorage support
|
|
17
|
+
- 🌍 **i18n Integration** - Seamless @nuxtjs/i18n support with vue-i18n adapter
|
|
17
18
|
- 🔧 **Date Adapters** - Support for date-fns, dayjs, luxon, moment
|
|
18
|
-
- 🎯 **Icon Sets** – MDI, FontAwesome,
|
|
19
|
-
-
|
|
19
|
+
- 🎯 **Icon Sets** – MDI, FontAwesome, Material Design (font & SVG variants)
|
|
20
|
+
- 🌐 **Locale Support** – Multiple languages with RTL support
|
|
20
21
|
- 🎭 **Blueprints** - Material Design MD1/MD2/MD3 presets
|
|
21
|
-
- 🚀 **Performance** – Optimized chunking
|
|
22
|
-
- 🔌 **Hooks** – Extend Vuetify configuration via hooks
|
|
22
|
+
- 🚀 **Performance** – Optimized chunking, tree-shaking, no duplicate CSS
|
|
23
|
+
- 🔌 **Hooks** – Extend Vuetify configuration via lifecycle hooks
|
|
24
|
+
- 📦 **SSR Ready** – No hydration mismatches
|
|
25
|
+
|
|
26
|
+
---
|
|
27
|
+
|
|
28
|
+
## Table of Contents
|
|
29
|
+
|
|
30
|
+
- [Quick Setup](#quick-setup)
|
|
31
|
+
- [Configuration](#configuration)
|
|
32
|
+
- [Usage](#usage)
|
|
33
|
+
- [i18n Integration](#i18n-integration)
|
|
34
|
+
- [Icon Configuration](#icon-configuration)
|
|
35
|
+
- [Theme Persistence](#theme-persistence)
|
|
36
|
+
- [Date Adapters](#date-adapters)
|
|
37
|
+
- [Custom SASS Variables](#custom-sass-variables)
|
|
38
|
+
- [Hooks](#hooks)
|
|
39
|
+
- [Performance Tips](#performance-tips)
|
|
40
|
+
- [TypeScript](#typescript)
|
|
41
|
+
- [Troubleshooting](#troubleshooting)
|
|
42
|
+
- [Local Development](#local-development)
|
|
43
|
+
|
|
44
|
+
---
|
|
23
45
|
|
|
24
46
|
## Quick Setup
|
|
25
47
|
|
|
26
|
-
|
|
48
|
+
### Installation
|
|
49
|
+
|
|
27
50
|
```bash
|
|
28
51
|
# npm
|
|
29
52
|
npm install vuetify-nuxt4-module vuetify vite-plugin-vuetify
|
|
@@ -35,61 +58,68 @@ pnpm add vuetify-nuxt4-module vuetify vite-plugin-vuetify
|
|
|
35
58
|
yarn add vuetify-nuxt4-module vuetify vite-plugin-vuetify
|
|
36
59
|
```
|
|
37
60
|
|
|
38
|
-
|
|
61
|
+
### Optional Dependencies
|
|
62
|
+
|
|
63
|
+
```bash
|
|
64
|
+
# Icons (choose one)
|
|
65
|
+
npm install @mdi/font # MDI font icons
|
|
66
|
+
npm install @mdi/js # MDI SVG icons (tree-shakeable)
|
|
67
|
+
|
|
68
|
+
# i18n support
|
|
69
|
+
npm install @nuxtjs/i18n vue-i18n
|
|
70
|
+
|
|
71
|
+
# Date adapters (choose one if needed)
|
|
72
|
+
npm install @date-io/date-fns date-fns
|
|
73
|
+
npm install @date-io/dayjs dayjs
|
|
74
|
+
npm install @date-io/luxon luxon
|
|
75
|
+
npm install @date-io/moment moment
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
### Minimal Configuration
|
|
79
|
+
|
|
39
80
|
```typescript
|
|
81
|
+
// nuxt.config.ts
|
|
40
82
|
export default defineNuxtConfig({
|
|
41
83
|
modules: ['vuetify-nuxt4-module'],
|
|
42
|
-
|
|
43
|
-
vuetify: {
|
|
44
|
-
moduleOptions: {
|
|
45
|
-
themePersistence: true,
|
|
46
|
-
defaultTheme: 'light',
|
|
47
|
-
},
|
|
48
|
-
|
|
49
|
-
vuetifyOptions: {
|
|
50
|
-
theme: {
|
|
51
|
-
defaultTheme: 'light',
|
|
52
|
-
themes: {
|
|
53
|
-
light: {
|
|
54
|
-
dark: false,
|
|
55
|
-
colors: {
|
|
56
|
-
primary: '#1976D2',
|
|
57
|
-
secondary: '#424242',
|
|
58
|
-
background: '#FFFFFF',
|
|
59
|
-
},
|
|
60
|
-
},
|
|
61
|
-
dark: {
|
|
62
|
-
dark: true,
|
|
63
|
-
colors: {
|
|
64
|
-
primary: '#2196F3',
|
|
65
|
-
background: '#121212',
|
|
66
|
-
},
|
|
67
|
-
},
|
|
68
|
-
},
|
|
69
|
-
},
|
|
70
|
-
},
|
|
71
|
-
},
|
|
72
84
|
})
|
|
73
|
-
|
|
74
85
|
```
|
|
75
86
|
|
|
87
|
+
That's it! Vuetify is now ready to use with sensible defaults.
|
|
88
|
+
|
|
89
|
+
---
|
|
90
|
+
|
|
76
91
|
## Configuration
|
|
77
92
|
|
|
78
|
-
### Full Example
|
|
93
|
+
### Full Configuration Example
|
|
94
|
+
|
|
79
95
|
```typescript
|
|
96
|
+
// nuxt.config.ts
|
|
80
97
|
export default defineNuxtConfig({
|
|
81
|
-
modules: [
|
|
98
|
+
modules: [
|
|
99
|
+
'@nuxtjs/i18n', // Optional: for i18n integration
|
|
100
|
+
'vuetify-nuxt4-module',
|
|
101
|
+
],
|
|
82
102
|
|
|
83
103
|
vuetify: {
|
|
104
|
+
// Vuetify instance options
|
|
84
105
|
vuetifyOptions: {
|
|
85
106
|
ssr: true,
|
|
86
|
-
blueprint: 'md3',
|
|
87
|
-
dateAdapter: 'vuetify',
|
|
107
|
+
blueprint: 'md3', // 'md1' | 'md2' | 'md3'
|
|
108
|
+
dateAdapter: 'vuetify', // 'vuetify' | 'date-fns' | 'dayjs' | 'luxon' | 'moment'
|
|
88
109
|
|
|
89
110
|
icons: {
|
|
90
|
-
defaultSet: 'mdi',
|
|
111
|
+
defaultSet: 'mdi', // 'mdi' | 'mdi-svg' | 'fa' | 'fa-svg' | 'md'
|
|
91
112
|
aliases: {
|
|
92
|
-
// Custom
|
|
113
|
+
// Custom aliases for font icons
|
|
114
|
+
},
|
|
115
|
+
svg: {
|
|
116
|
+
mdi: {
|
|
117
|
+
aliases: {
|
|
118
|
+
// Custom aliases for SVG icons (maps to @mdi/js exports)
|
|
119
|
+
account: 'mdiAccount',
|
|
120
|
+
home: 'mdiHome',
|
|
121
|
+
},
|
|
122
|
+
},
|
|
93
123
|
},
|
|
94
124
|
},
|
|
95
125
|
|
|
@@ -98,6 +128,7 @@ export default defineNuxtConfig({
|
|
|
98
128
|
fallback: 'en',
|
|
99
129
|
rtl: {
|
|
100
130
|
ar: true,
|
|
131
|
+
he: true,
|
|
101
132
|
},
|
|
102
133
|
},
|
|
103
134
|
|
|
@@ -105,15 +136,26 @@ export default defineNuxtConfig({
|
|
|
105
136
|
defaultTheme: 'light',
|
|
106
137
|
themes: {
|
|
107
138
|
light: {
|
|
139
|
+
dark: false,
|
|
108
140
|
colors: {
|
|
109
|
-
primary: '#
|
|
110
|
-
secondary: '#
|
|
141
|
+
primary: '#1976D2',
|
|
142
|
+
secondary: '#424242',
|
|
143
|
+
accent: '#82B1FF',
|
|
144
|
+
error: '#FF5252',
|
|
145
|
+
info: '#2196F3',
|
|
146
|
+
success: '#4CAF50',
|
|
147
|
+
warning: '#FFC107',
|
|
148
|
+
background: '#FFFFFF',
|
|
149
|
+
surface: '#FFFFFF',
|
|
111
150
|
},
|
|
112
151
|
},
|
|
113
152
|
dark: {
|
|
153
|
+
dark: true,
|
|
114
154
|
colors: {
|
|
115
|
-
primary: '#
|
|
116
|
-
secondary: '#
|
|
155
|
+
primary: '#2196F3',
|
|
156
|
+
secondary: '#424242',
|
|
157
|
+
background: '#121212',
|
|
158
|
+
surface: '#212121',
|
|
117
159
|
},
|
|
118
160
|
},
|
|
119
161
|
},
|
|
@@ -126,27 +168,43 @@ export default defineNuxtConfig({
|
|
|
126
168
|
},
|
|
127
169
|
VCard: {
|
|
128
170
|
elevation: 4,
|
|
171
|
+
rounded: 'lg',
|
|
172
|
+
},
|
|
173
|
+
VTextField: {
|
|
174
|
+
variant: 'outlined',
|
|
175
|
+
density: 'comfortable',
|
|
129
176
|
},
|
|
130
177
|
},
|
|
131
178
|
},
|
|
132
179
|
|
|
133
180
|
// Module options
|
|
134
|
-
importComposables: true,
|
|
135
|
-
prefixComposables: false,
|
|
136
|
-
transformAssetUrls: true,
|
|
137
|
-
autoImport: true,
|
|
138
|
-
styles: true,
|
|
139
|
-
|
|
181
|
+
importComposables: true, // Auto-import Vuetify composables
|
|
182
|
+
prefixComposables: false, // Prefix with 'V': useVTheme, useVDisplay
|
|
183
|
+
transformAssetUrls: true, // Transform asset URLs in templates
|
|
184
|
+
autoImport: true, // Auto-import components via vite-plugin-vuetify
|
|
185
|
+
styles: true, // true | 'sass' | 'none' | { configFile: string }
|
|
186
|
+
i18n: true, // Enable vue-i18n integration
|
|
187
|
+
|
|
188
|
+
// Theme persistence
|
|
140
189
|
persistence: {
|
|
141
190
|
enabled: true,
|
|
142
191
|
key: 'nuxt-vuetify-theme',
|
|
143
|
-
storage: 'cookie',
|
|
192
|
+
storage: 'cookie', // 'cookie' | 'localStorage' | 'sessionStorage'
|
|
144
193
|
cookieOptions: {
|
|
145
|
-
maxAge: 60 * 60 * 24 * 365,
|
|
194
|
+
maxAge: 60 * 60 * 24 * 365, // 1 year
|
|
146
195
|
path: '/',
|
|
147
196
|
sameSite: 'lax',
|
|
148
197
|
},
|
|
149
198
|
},
|
|
199
|
+
|
|
200
|
+
// Lazy loading (optional)
|
|
201
|
+
lazyComponents: false, // or { components: ['VDataTable'], delay: 200 }
|
|
202
|
+
|
|
203
|
+
// Preload options (optional)
|
|
204
|
+
preload: {
|
|
205
|
+
fonts: false,
|
|
206
|
+
criticalCSS: true,
|
|
207
|
+
},
|
|
150
208
|
},
|
|
151
209
|
})
|
|
152
210
|
```
|
|
@@ -157,195 +215,634 @@ export default defineNuxtConfig({
|
|
|
157
215
|
|--------|------|---------|-------------|
|
|
158
216
|
| `vuetifyOptions.ssr` | `boolean` | `true` | Enable SSR support |
|
|
159
217
|
| `vuetifyOptions.blueprint` | `'md1' \| 'md2' \| 'md3'` | `'md3'` | Material Design blueprint |
|
|
160
|
-
| `vuetifyOptions.dateAdapter` | `string` | `'vuetify'` | Date adapter
|
|
218
|
+
| `vuetifyOptions.dateAdapter` | `string` | `'vuetify'` | Date adapter for date pickers |
|
|
161
219
|
| `vuetifyOptions.icons` | `object` | `{ defaultSet: 'mdi' }` | Icon configuration |
|
|
162
|
-
| `vuetifyOptions.locale` | `object` | - | Locale configuration |
|
|
220
|
+
| `vuetifyOptions.locale` | `object` | - | Locale and RTL configuration |
|
|
163
221
|
| `vuetifyOptions.theme` | `object` | - | Theme configuration |
|
|
164
|
-
| `vuetifyOptions.defaults` | `object` | - | Component
|
|
222
|
+
| `vuetifyOptions.defaults` | `object` | - | Component default props |
|
|
165
223
|
| `importComposables` | `boolean` | `true` | Auto-import Vuetify composables |
|
|
166
224
|
| `prefixComposables` | `boolean` | `false` | Prefix composables with 'V' |
|
|
167
|
-
| `transformAssetUrls` | `boolean` | `true` | Transform asset URLs
|
|
168
|
-
| `autoImport` | `boolean \| object` | `true` | Enable
|
|
169
|
-
| `styles` | `
|
|
170
|
-
| `
|
|
225
|
+
| `transformAssetUrls` | `boolean` | `true` | Transform asset URLs |
|
|
226
|
+
| `autoImport` | `boolean \| object` | `true` | Enable vite-plugin-vuetify |
|
|
227
|
+
| `styles` | `true \| 'sass' \| 'none' \| object` | `true` | Style configuration |
|
|
228
|
+
| `i18n` | `boolean \| object` | `true` | Enable vue-i18n integration |
|
|
229
|
+
| `persistence` | `object` | `{ enabled: true, storage: 'cookie' }` | Theme persistence |
|
|
230
|
+
| `lazyComponents` | `boolean \| object` | `false` | Lazy load heavy components |
|
|
231
|
+
|
|
232
|
+
---
|
|
171
233
|
|
|
172
234
|
## Usage
|
|
173
235
|
|
|
174
|
-
### Using the Composable
|
|
236
|
+
### Using the `useVuetify` Composable
|
|
237
|
+
|
|
175
238
|
```vue
|
|
239
|
+
|
|
176
240
|
<script setup lang="ts">
|
|
177
|
-
const {
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
} = useVuetify()
|
|
241
|
+
const {
|
|
242
|
+
isDark,
|
|
243
|
+
isMobile,
|
|
244
|
+
currentTheme,
|
|
245
|
+
currentBreakpoint,
|
|
246
|
+
toggleTheme,
|
|
247
|
+
setTheme,
|
|
248
|
+
setLocale,
|
|
249
|
+
} = useVuetify()
|
|
186
250
|
</script>
|
|
187
251
|
|
|
188
252
|
<template>
|
|
189
253
|
<v-app>
|
|
190
|
-
<v-
|
|
191
|
-
|
|
192
|
-
|
|
254
|
+
<v-app-bar>
|
|
255
|
+
<v-app-bar-title>My App</v-app-bar-title>
|
|
256
|
+
<v-spacer></v-spacer>
|
|
257
|
+
<v-btn :icon="isDark ? 'mdi-white-balance-sunny' : 'mdi-moon-waning-crescent'"
|
|
258
|
+
@click="toggleTheme"></v-btn>
|
|
259
|
+
</v-app-bar>
|
|
260
|
+
|
|
261
|
+
<v-main>
|
|
262
|
+
<v-container>
|
|
263
|
+
<p>Current theme: {{ currentTheme }}</p>
|
|
264
|
+
<p>Breakpoint: {{ currentBreakpoint }}</p>
|
|
265
|
+
<p>Mobile: {{ isMobile }}</p>
|
|
266
|
+
</v-container>
|
|
267
|
+
</v-main>
|
|
193
268
|
</v-app>
|
|
194
269
|
</template>
|
|
195
270
|
```
|
|
196
271
|
|
|
197
|
-
### Using
|
|
272
|
+
### Using Vuetify Composables Directly
|
|
273
|
+
|
|
274
|
+
The module auto-imports all Vuetify composables:
|
|
275
|
+
|
|
276
|
+
```vue
|
|
277
|
+
<script setup lang="ts">
|
|
278
|
+
// These are auto-imported
|
|
279
|
+
const theme = useTheme()
|
|
280
|
+
const display = useDisplay()
|
|
281
|
+
const locale = useLocale()
|
|
282
|
+
|
|
283
|
+
// Toggle theme
|
|
284
|
+
const toggleDark = () => {
|
|
285
|
+
theme.global.name.value = theme.global.current.value.dark ? 'light' : 'dark'
|
|
286
|
+
}
|
|
287
|
+
</script>
|
|
288
|
+
```
|
|
289
|
+
|
|
290
|
+
If you have naming conflicts, enable `prefixComposables: true`:
|
|
198
291
|
|
|
199
|
-
Create a plugin to hook into Vuetify lifecycle:
|
|
200
292
|
```typescript
|
|
201
|
-
//
|
|
202
|
-
|
|
203
|
-
//
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
...vuetifyOptions.defaults,
|
|
207
|
-
VAlert: {
|
|
208
|
-
variant: 'tonal',
|
|
209
|
-
},
|
|
210
|
-
}
|
|
211
|
-
})
|
|
293
|
+
// nuxt.config.ts
|
|
294
|
+
vuetify: {
|
|
295
|
+
prefixComposables: true, // useTheme -> useVTheme
|
|
296
|
+
}
|
|
297
|
+
```
|
|
212
298
|
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
299
|
+
---
|
|
300
|
+
|
|
301
|
+
## i18n Integration
|
|
302
|
+
|
|
303
|
+
The module seamlessly integrates with `@nuxtjs/i18n` for Vuetify's locale system.
|
|
304
|
+
|
|
305
|
+
### Setup
|
|
306
|
+
|
|
307
|
+
```bash
|
|
308
|
+
npm install @nuxtjs/i18n vue-i18n
|
|
309
|
+
```
|
|
310
|
+
|
|
311
|
+
```typescript
|
|
312
|
+
// nuxt.config.ts
|
|
313
|
+
export default defineNuxtConfig({
|
|
314
|
+
modules: [
|
|
315
|
+
'@nuxtjs/i18n',
|
|
316
|
+
'vuetify-nuxt4-module',
|
|
317
|
+
],
|
|
318
|
+
|
|
319
|
+
i18n: {
|
|
320
|
+
locales: [
|
|
321
|
+
{ code: 'en', file: 'en.json', name: 'English' },
|
|
322
|
+
{ code: 'km', file: 'km.json', name: 'ខ្មែរ' },
|
|
323
|
+
{ code: 'ar', file: 'ar.json', name: 'العربية', dir: 'rtl' },
|
|
324
|
+
],
|
|
325
|
+
defaultLocale: 'en',
|
|
326
|
+
langDir: 'locales/',
|
|
327
|
+
},
|
|
328
|
+
|
|
329
|
+
vuetify: {
|
|
330
|
+
i18n: true, // Enable vue-i18n adapter
|
|
331
|
+
},
|
|
217
332
|
})
|
|
218
333
|
```
|
|
219
334
|
|
|
220
|
-
###
|
|
335
|
+
### Locale Files
|
|
336
|
+
|
|
337
|
+
Create locale files with Vuetify translations under `$vuetify` key:
|
|
338
|
+
|
|
339
|
+
```json
|
|
340
|
+
// locales/en.json
|
|
341
|
+
{
|
|
342
|
+
"welcome": "Welcome",
|
|
343
|
+
"$vuetify": {
|
|
344
|
+
"badge": "Badge",
|
|
345
|
+
"open": "Open",
|
|
346
|
+
"close": "Close",
|
|
347
|
+
"dataIterator": {
|
|
348
|
+
"noResultsText": "No matching records found",
|
|
349
|
+
"loadingText": "Loading items..."
|
|
350
|
+
},
|
|
351
|
+
"dataTable": {
|
|
352
|
+
"itemsPerPageText": "Rows per page:",
|
|
353
|
+
"ariaLabel": {
|
|
354
|
+
"sortDescending": "Sorted descending.",
|
|
355
|
+
"sortAscending": "Sorted ascending.",
|
|
356
|
+
"sortNone": "Not sorted."
|
|
357
|
+
}
|
|
358
|
+
},
|
|
359
|
+
"pagination": {
|
|
360
|
+
"ariaLabel": {
|
|
361
|
+
"root": "Pagination Navigation",
|
|
362
|
+
"next": "Next page",
|
|
363
|
+
"previous": "Previous page",
|
|
364
|
+
"page": "Go to page {0}",
|
|
365
|
+
"currentPage": "Page {0}, Current page"
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
```
|
|
221
371
|
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
372
|
+
```json
|
|
373
|
+
// locales/km.json
|
|
374
|
+
{
|
|
375
|
+
"welcome": "សូមស្វាគមន៍",
|
|
376
|
+
"$vuetify": {
|
|
377
|
+
"badge": "ផ្លាកសញ្ញា",
|
|
378
|
+
"open": "បើក",
|
|
379
|
+
"close": "បិទ",
|
|
380
|
+
"dataIterator": {
|
|
381
|
+
"noResultsText": "រកមិនឃើញទិន្នន័យ",
|
|
382
|
+
"loadingText": "កំពុងផ្ទុក..."
|
|
383
|
+
},
|
|
384
|
+
"dataTable": {
|
|
385
|
+
"itemsPerPageText": "ជួរក្នុងមួយទំព័រ:"
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
```
|
|
390
|
+
|
|
391
|
+
### Switching Locales
|
|
392
|
+
|
|
393
|
+
```vue
|
|
394
|
+
<script setup lang="ts">
|
|
395
|
+
const { locale, setLocale } = useI18n()
|
|
396
|
+
|
|
397
|
+
const switchLocale = (code: string) => {
|
|
398
|
+
setLocale(code)
|
|
399
|
+
// Vuetify locale updates automatically via the adapter
|
|
400
|
+
}
|
|
401
|
+
</script>
|
|
402
|
+
|
|
403
|
+
<template>
|
|
404
|
+
<v-btn-toggle v-model="locale">
|
|
405
|
+
<v-btn value="en">EN</v-btn>
|
|
406
|
+
<v-btn value="km">KM</v-btn>
|
|
407
|
+
<v-btn value="ar">AR</v-btn>
|
|
408
|
+
</v-btn-toggle>
|
|
409
|
+
</template>
|
|
410
|
+
```
|
|
411
|
+
|
|
412
|
+
---
|
|
413
|
+
|
|
414
|
+
## Icon Configuration
|
|
415
|
+
|
|
416
|
+
### Font Icons (MDI)
|
|
417
|
+
|
|
418
|
+
```bash
|
|
419
|
+
npm install @mdi/font
|
|
420
|
+
```
|
|
421
|
+
|
|
422
|
+
```typescript
|
|
423
|
+
// nuxt.config.ts
|
|
424
|
+
vuetify: {
|
|
425
|
+
vuetifyOptions: {
|
|
426
|
+
icons: {
|
|
427
|
+
defaultSet: 'mdi',
|
|
428
|
+
},
|
|
429
|
+
},
|
|
430
|
+
}
|
|
431
|
+
```
|
|
432
|
+
|
|
433
|
+
```vue
|
|
434
|
+
<template>
|
|
435
|
+
<v-icon icon="mdi-home" />
|
|
436
|
+
<v-icon>mdi-account</v-icon>
|
|
437
|
+
<v-btn icon="mdi-menu" />
|
|
438
|
+
</template>
|
|
439
|
+
```
|
|
440
|
+
|
|
441
|
+
### SVG Icons (MDI-SVG) - Recommended for Performance
|
|
442
|
+
|
|
443
|
+
```bash
|
|
444
|
+
npm install @mdi/js
|
|
445
|
+
```
|
|
446
|
+
|
|
447
|
+
```typescript
|
|
448
|
+
// nuxt.config.ts
|
|
449
|
+
vuetify: {
|
|
450
|
+
vuetifyOptions: {
|
|
451
|
+
icons: {
|
|
452
|
+
defaultSet: 'mdi-svg',
|
|
453
|
+
svg: {
|
|
454
|
+
mdi: {
|
|
455
|
+
aliases: {
|
|
456
|
+
// Map alias names to @mdi/js export names
|
|
457
|
+
account: 'mdiAccount',
|
|
458
|
+
home: 'mdiHome',
|
|
459
|
+
menu: 'mdiMenu',
|
|
460
|
+
settings: 'mdiCog',
|
|
461
|
+
search: 'mdiMagnify',
|
|
462
|
+
},
|
|
463
|
+
},
|
|
464
|
+
},
|
|
465
|
+
},
|
|
466
|
+
},
|
|
467
|
+
}
|
|
468
|
+
```
|
|
469
|
+
|
|
470
|
+
Usage in templates:
|
|
471
|
+
|
|
472
|
+
```vue
|
|
473
|
+
<script setup lang="ts">
|
|
474
|
+
// Direct import for icons not in aliases
|
|
475
|
+
import { mdiHeart, mdiStar } from '@mdi/js'
|
|
476
|
+
</script>
|
|
477
|
+
|
|
478
|
+
<template>
|
|
479
|
+
<!-- Using aliases (resolved at build time) -->
|
|
480
|
+
<v-icon icon="$account" />
|
|
481
|
+
<v-icon icon="$home" />
|
|
482
|
+
|
|
483
|
+
<!-- Using direct imports -->
|
|
484
|
+
<v-icon :icon="mdiHeart" />
|
|
485
|
+
<v-icon :icon="mdiStar" />
|
|
486
|
+
</template>
|
|
487
|
+
```
|
|
488
|
+
|
|
489
|
+
### FontAwesome Icons
|
|
490
|
+
|
|
491
|
+
```bash
|
|
492
|
+
npm install @fortawesome/fontawesome-free
|
|
493
|
+
```
|
|
494
|
+
|
|
495
|
+
```typescript
|
|
496
|
+
vuetify: {
|
|
497
|
+
vuetifyOptions: {
|
|
498
|
+
icons: {
|
|
499
|
+
defaultSet: 'fa', // or 'fa-svg' for SVG version
|
|
500
|
+
},
|
|
501
|
+
},
|
|
502
|
+
}
|
|
503
|
+
```
|
|
504
|
+
|
|
505
|
+
---
|
|
506
|
+
|
|
507
|
+
## Theme Persistence
|
|
508
|
+
|
|
509
|
+
The module provides SSR-safe theme persistence using cookies (recommended) or client-side storage.
|
|
510
|
+
|
|
511
|
+
### Cookie Storage (SSR-Safe) - Recommended
|
|
512
|
+
|
|
513
|
+
```typescript
|
|
514
|
+
vuetify: {
|
|
515
|
+
persistence: {
|
|
516
|
+
enabled: true,
|
|
517
|
+
storage: 'cookie',
|
|
518
|
+
key: 'nuxt-vuetify-theme',
|
|
519
|
+
cookieOptions: {
|
|
520
|
+
maxAge: 60 * 60 * 24 * 365, // 1 year
|
|
521
|
+
path: '/',
|
|
522
|
+
sameSite: 'lax',
|
|
523
|
+
},
|
|
524
|
+
},
|
|
525
|
+
}
|
|
526
|
+
```
|
|
527
|
+
|
|
528
|
+
This prevents hydration mismatches because:
|
|
529
|
+
1. Server reads theme from cookie in request headers
|
|
530
|
+
2. Client reads theme from `document.cookie`
|
|
531
|
+
3. Both render with the same theme
|
|
532
|
+
|
|
533
|
+
### LocalStorage (Client-Only)
|
|
534
|
+
|
|
535
|
+
```typescript
|
|
536
|
+
vuetify: {
|
|
537
|
+
persistence: {
|
|
538
|
+
enabled: true,
|
|
539
|
+
storage: 'localStorage',
|
|
540
|
+
key: 'my-app-theme',
|
|
541
|
+
},
|
|
542
|
+
}
|
|
543
|
+
```
|
|
544
|
+
|
|
545
|
+
> ⚠️ **Note:** LocalStorage may cause hydration mismatches on first load since the server doesn't have access to localStorage.
|
|
546
|
+
|
|
547
|
+
### Disable Persistence
|
|
548
|
+
|
|
549
|
+
```typescript
|
|
550
|
+
vuetify: {
|
|
551
|
+
persistence: {
|
|
552
|
+
enabled: false,
|
|
553
|
+
},
|
|
554
|
+
}
|
|
555
|
+
```
|
|
556
|
+
|
|
557
|
+
---
|
|
227
558
|
|
|
228
559
|
## Date Adapters
|
|
229
560
|
|
|
230
|
-
|
|
561
|
+
### Using date-fns
|
|
562
|
+
|
|
231
563
|
```bash
|
|
232
|
-
# date-fns
|
|
233
564
|
npm install @date-io/date-fns date-fns
|
|
565
|
+
```
|
|
566
|
+
|
|
567
|
+
```typescript
|
|
568
|
+
vuetify: {
|
|
569
|
+
vuetifyOptions: {
|
|
570
|
+
dateAdapter: 'date-fns',
|
|
571
|
+
},
|
|
572
|
+
}
|
|
573
|
+
```
|
|
234
574
|
|
|
235
|
-
|
|
575
|
+
### Using dayjs
|
|
576
|
+
|
|
577
|
+
```bash
|
|
236
578
|
npm install @date-io/dayjs dayjs
|
|
579
|
+
```
|
|
237
580
|
|
|
238
|
-
|
|
239
|
-
|
|
581
|
+
```typescript
|
|
582
|
+
vuetify: {
|
|
583
|
+
vuetifyOptions: {
|
|
584
|
+
dateAdapter: 'dayjs',
|
|
585
|
+
},
|
|
586
|
+
}
|
|
587
|
+
```
|
|
240
588
|
|
|
241
|
-
|
|
242
|
-
|
|
589
|
+
### Using luxon
|
|
590
|
+
|
|
591
|
+
```bash
|
|
592
|
+
npm install @date-io/luxon luxon
|
|
243
593
|
```
|
|
244
594
|
|
|
245
|
-
Then configure:
|
|
246
595
|
```typescript
|
|
247
596
|
vuetify: {
|
|
248
597
|
vuetifyOptions: {
|
|
249
|
-
dateAdapter: '
|
|
250
|
-
}
|
|
598
|
+
dateAdapter: 'luxon',
|
|
599
|
+
},
|
|
251
600
|
}
|
|
252
601
|
```
|
|
253
602
|
|
|
603
|
+
---
|
|
604
|
+
|
|
254
605
|
## Custom SASS Variables
|
|
255
606
|
|
|
256
|
-
|
|
607
|
+
### Setup
|
|
257
608
|
|
|
258
609
|
1. Create a settings file:
|
|
610
|
+
|
|
259
611
|
```scss
|
|
260
|
-
// assets/settings.scss
|
|
612
|
+
// assets/vuetify-settings.scss
|
|
261
613
|
@use 'vuetify/settings' with (
|
|
262
|
-
$body-font-family: 'Inter',
|
|
614
|
+
$body-font-family: 'Inter, sans-serif',
|
|
263
615
|
$border-radius-root: 8px,
|
|
264
616
|
$button-height: 44px,
|
|
617
|
+
$card-border-radius: 12px,
|
|
265
618
|
);
|
|
266
619
|
```
|
|
267
620
|
|
|
268
621
|
2. Configure in nuxt.config.ts:
|
|
622
|
+
|
|
269
623
|
```typescript
|
|
270
624
|
vuetify: {
|
|
271
625
|
styles: {
|
|
272
|
-
configFile: 'assets/settings.scss'
|
|
273
|
-
}
|
|
626
|
+
configFile: 'assets/vuetify-settings.scss',
|
|
627
|
+
},
|
|
274
628
|
}
|
|
275
629
|
```
|
|
276
630
|
|
|
277
|
-
|
|
278
|
-
> ```typescript
|
|
279
|
-
> experimental: {
|
|
280
|
-
> inlineSSRStyles: false
|
|
281
|
-
> }
|
|
282
|
-
> ```
|
|
631
|
+
3. **Important for SSR:** Disable inline SSR styles:
|
|
283
632
|
|
|
284
|
-
## Performance Tips
|
|
285
|
-
|
|
286
|
-
### 1. Use SVG Icons for Better Performance
|
|
287
633
|
```typescript
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
634
|
+
// nuxt.config.ts
|
|
635
|
+
export default defineNuxtConfig({
|
|
636
|
+
experimental: {
|
|
637
|
+
inlineSSRStyles: false,
|
|
638
|
+
},
|
|
639
|
+
vuetify: {
|
|
640
|
+
styles: {
|
|
641
|
+
configFile: 'assets/vuetify-settings.scss',
|
|
642
|
+
},
|
|
643
|
+
},
|
|
644
|
+
})
|
|
295
645
|
```
|
|
296
646
|
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
647
|
+
---
|
|
648
|
+
|
|
649
|
+
## Hooks
|
|
650
|
+
|
|
651
|
+
Use hooks to customize Vuetify at different lifecycle stages.
|
|
301
652
|
|
|
653
|
+
### Available Hooks
|
|
654
|
+
|
|
655
|
+
| Hook | Payload | Description |
|
|
656
|
+
|------|---------|-------------|
|
|
657
|
+
| `vuetify:before-create` | `{ vuetifyOptions }` | Modify options before Vuetify is created |
|
|
658
|
+
| `vuetify:configuration` | `{ vuetifyOptions }` | After configuration is applied |
|
|
659
|
+
| `vuetify:ready` | `vuetify` | When Vuetify instance is ready |
|
|
660
|
+
|
|
661
|
+
### Example: Custom Plugin
|
|
662
|
+
|
|
663
|
+
```typescript
|
|
664
|
+
// plugins/vuetify-custom.ts
|
|
302
665
|
export default defineNuxtPlugin((nuxtApp) => {
|
|
666
|
+
// Modify options before Vuetify is created
|
|
303
667
|
onVuetifyHook(nuxtApp, 'vuetify:before-create', ({ vuetifyOptions }) => {
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
668
|
+
// Add custom defaults
|
|
669
|
+
vuetifyOptions.defaults = {
|
|
670
|
+
...vuetifyOptions.defaults,
|
|
671
|
+
VAlert: {
|
|
672
|
+
variant: 'tonal',
|
|
673
|
+
rounded: 'lg',
|
|
310
674
|
},
|
|
675
|
+
VChip: {
|
|
676
|
+
rounded: 'pill',
|
|
677
|
+
},
|
|
678
|
+
}
|
|
679
|
+
|
|
680
|
+
// Add custom theme
|
|
681
|
+
if (vuetifyOptions.theme?.themes) {
|
|
682
|
+
vuetifyOptions.theme.themes.corporate = {
|
|
683
|
+
dark: false,
|
|
684
|
+
colors: {
|
|
685
|
+
primary: '#0066CC',
|
|
686
|
+
secondary: '#6C757D',
|
|
687
|
+
},
|
|
688
|
+
}
|
|
311
689
|
}
|
|
312
690
|
})
|
|
691
|
+
|
|
692
|
+
// Access Vuetify instance when ready
|
|
693
|
+
onVuetifyHook(nuxtApp, 'vuetify:ready', (vuetify) => {
|
|
694
|
+
console.log('Current theme:', vuetify.theme.global.name.value)
|
|
695
|
+
})
|
|
313
696
|
})
|
|
314
697
|
```
|
|
315
698
|
|
|
699
|
+
---
|
|
700
|
+
|
|
701
|
+
## Performance Tips
|
|
702
|
+
|
|
703
|
+
### 1. Use SVG Icons (Tree-Shakeable)
|
|
704
|
+
|
|
705
|
+
```typescript
|
|
706
|
+
vuetify: {
|
|
707
|
+
vuetifyOptions: {
|
|
708
|
+
icons: {
|
|
709
|
+
defaultSet: 'mdi-svg',
|
|
710
|
+
},
|
|
711
|
+
},
|
|
712
|
+
}
|
|
713
|
+
```
|
|
714
|
+
|
|
715
|
+
### 2. Enable Labs Components Only When Needed
|
|
716
|
+
|
|
717
|
+
```typescript
|
|
718
|
+
vuetify: {
|
|
719
|
+
autoImport: {
|
|
720
|
+
labs: true, // Only if using lab components
|
|
721
|
+
},
|
|
722
|
+
}
|
|
723
|
+
```
|
|
724
|
+
|
|
316
725
|
### 3. Lazy Load Heavy Components
|
|
726
|
+
|
|
317
727
|
```vue
|
|
318
728
|
<script setup>
|
|
319
|
-
// Lazy load data tables, calendars, etc.
|
|
320
729
|
const VDataTable = defineAsyncComponent(() =>
|
|
321
730
|
import('vuetify/components/VDataTable').then(m => m.VDataTable)
|
|
322
731
|
)
|
|
323
732
|
</script>
|
|
324
733
|
```
|
|
325
734
|
|
|
326
|
-
|
|
735
|
+
Or use the built-in lazy loading:
|
|
736
|
+
|
|
327
737
|
```typescript
|
|
328
|
-
// nuxt.config.ts
|
|
329
738
|
vuetify: {
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
739
|
+
lazyComponents: {
|
|
740
|
+
components: ['VDataTable', 'VDatePicker', 'VCalendar'],
|
|
741
|
+
delay: 200,
|
|
742
|
+
},
|
|
333
743
|
}
|
|
334
744
|
```
|
|
335
745
|
|
|
336
|
-
###
|
|
746
|
+
### 4. Enable Compression
|
|
747
|
+
|
|
337
748
|
```typescript
|
|
338
749
|
// nuxt.config.ts
|
|
339
750
|
nitro: {
|
|
340
|
-
compressPublicAssets: true
|
|
751
|
+
compressPublicAssets: true,
|
|
341
752
|
}
|
|
342
753
|
```
|
|
343
754
|
|
|
755
|
+
### 5. Analyze Bundle Size
|
|
756
|
+
|
|
757
|
+
```bash
|
|
758
|
+
npm run analyze
|
|
759
|
+
```
|
|
760
|
+
|
|
761
|
+
---
|
|
762
|
+
|
|
344
763
|
## TypeScript
|
|
345
764
|
|
|
346
|
-
The module provides full TypeScript support
|
|
765
|
+
The module provides full TypeScript support with auto-generated types.
|
|
766
|
+
|
|
767
|
+
### Type Augmentation
|
|
768
|
+
|
|
769
|
+
```typescript
|
|
770
|
+
// types/vuetify.d.ts
|
|
771
|
+
declare module 'vuetify' {
|
|
772
|
+
interface ThemeDefinition {
|
|
773
|
+
// Add custom theme properties
|
|
774
|
+
}
|
|
775
|
+
}
|
|
776
|
+
```
|
|
777
|
+
|
|
778
|
+
### Typed Composable
|
|
779
|
+
|
|
780
|
+
```typescript
|
|
781
|
+
const { isDark, toggleTheme } = useVuetify()
|
|
782
|
+
// isDark: ComputedRef<boolean>
|
|
783
|
+
// toggleTheme: () => void
|
|
784
|
+
```
|
|
785
|
+
|
|
786
|
+
---
|
|
787
|
+
|
|
788
|
+
## Troubleshooting
|
|
789
|
+
|
|
790
|
+
### Hydration Mismatch with Theme
|
|
791
|
+
|
|
792
|
+
**Problem:** Server renders light theme, client shows dark theme briefly.
|
|
793
|
+
|
|
794
|
+
**Solution:** Use cookie storage (default):
|
|
795
|
+
|
|
796
|
+
```typescript
|
|
797
|
+
vuetify: {
|
|
798
|
+
persistence: {
|
|
799
|
+
storage: 'cookie', // Not 'localStorage'
|
|
800
|
+
},
|
|
801
|
+
}
|
|
802
|
+
```
|
|
803
|
+
|
|
804
|
+
### Duplicate CSS Loading
|
|
805
|
+
|
|
806
|
+
**Problem:** Vuetify CSS is loaded twice.
|
|
807
|
+
|
|
808
|
+
**Solution:** The module handles this automatically. Make sure you don't manually import Vuetify styles in your nuxt.config.ts:
|
|
809
|
+
|
|
810
|
+
```typescript
|
|
811
|
+
// ❌ Don't do this
|
|
812
|
+
css: ['vuetify/styles'],
|
|
813
|
+
|
|
814
|
+
// ✅ Let the module handle it
|
|
815
|
+
vuetify: {
|
|
816
|
+
styles: true,
|
|
817
|
+
}
|
|
818
|
+
```
|
|
819
|
+
|
|
820
|
+
### i18n Not Working
|
|
821
|
+
|
|
822
|
+
**Problem:** Vuetify doesn't use vue-i18n translations.
|
|
823
|
+
|
|
824
|
+
**Solution:**
|
|
825
|
+
1. Ensure `@nuxtjs/i18n` is listed **before** `vuetify-nuxt4-module` in modules
|
|
826
|
+
2. Add translations under `$vuetify` key in locale files
|
|
827
|
+
|
|
828
|
+
### Icons Not Showing
|
|
829
|
+
|
|
830
|
+
**Problem:** Icons appear as empty boxes.
|
|
831
|
+
|
|
832
|
+
**Solution:** Install the icon package:
|
|
833
|
+
|
|
834
|
+
```bash
|
|
835
|
+
# For font icons
|
|
836
|
+
npm install @mdi/font
|
|
837
|
+
|
|
838
|
+
# For SVG icons
|
|
839
|
+
npm install @mdi/js
|
|
840
|
+
```
|
|
841
|
+
|
|
842
|
+
---
|
|
347
843
|
|
|
348
844
|
## Local Development
|
|
845
|
+
|
|
349
846
|
```bash
|
|
350
847
|
# Install dependencies
|
|
351
848
|
npm install
|
|
@@ -353,23 +850,45 @@ npm install
|
|
|
353
850
|
# Generate type stubs
|
|
354
851
|
npm run dev:prepare
|
|
355
852
|
|
|
356
|
-
# Develop with
|
|
853
|
+
# Develop with playground
|
|
357
854
|
npm run dev
|
|
358
855
|
|
|
359
|
-
# Build
|
|
856
|
+
# Build playground
|
|
360
857
|
npm run dev:build
|
|
361
858
|
|
|
362
|
-
# Run
|
|
859
|
+
# Run linter
|
|
363
860
|
npm run lint
|
|
364
861
|
|
|
365
|
-
# Run
|
|
862
|
+
# Run tests
|
|
366
863
|
npm run test
|
|
367
864
|
npm run test:watch
|
|
368
865
|
|
|
866
|
+
# Type check
|
|
867
|
+
npm run test:types
|
|
868
|
+
|
|
869
|
+
# Analyze bundle
|
|
870
|
+
npm run analyze
|
|
871
|
+
|
|
369
872
|
# Release new version
|
|
370
873
|
npm run release
|
|
371
874
|
```
|
|
372
875
|
|
|
876
|
+
---
|
|
877
|
+
|
|
878
|
+
## Contributing
|
|
879
|
+
|
|
880
|
+
Contributions are welcome! Please read our [Contributing Guide](CONTRIBUTING.md) before submitting a Pull Request.
|
|
881
|
+
|
|
882
|
+
---
|
|
883
|
+
|
|
373
884
|
## License
|
|
374
885
|
|
|
375
886
|
[MIT License](LICENSE)
|
|
887
|
+
|
|
888
|
+
---
|
|
889
|
+
|
|
890
|
+
## Credits
|
|
891
|
+
|
|
892
|
+
- [Vuetify](https://vuetifyjs.com/) - The Vue UI Library
|
|
893
|
+
- [Nuxt](https://nuxt.com/) - The Intuitive Vue Framework
|
|
894
|
+
- [@nuxtjs/i18n](https://i18n.nuxtjs.org/) - Internationalization for Nuxt
|
package/dist/module.json
CHANGED
package/dist/module.mjs
CHANGED
|
@@ -90,16 +90,19 @@ const module$1 = defineNuxtModule({
|
|
|
90
90
|
}
|
|
91
91
|
]);
|
|
92
92
|
const useVitePlugin = options.autoImport !== false;
|
|
93
|
-
let vitePluginStyles = "none";
|
|
94
93
|
if (options.autoImport && useVitePlugin) {
|
|
94
|
+
let vitePluginStyles = "none";
|
|
95
95
|
if (typeof options.styles === "object" && options.styles.configFile) {
|
|
96
96
|
vitePluginStyles = { configFile: options.styles.configFile };
|
|
97
97
|
} else if (options.styles === "sass") {
|
|
98
98
|
vitePluginStyles = "sass";
|
|
99
99
|
} else if (options.styles === true || options.styles === void 0) {
|
|
100
|
-
vitePluginStyles =
|
|
100
|
+
vitePluginStyles = "none";
|
|
101
101
|
}
|
|
102
102
|
await setupAutoImport(options.autoImport, vitePluginStyles, logger);
|
|
103
|
+
if (options.styles !== "none") {
|
|
104
|
+
addStyles(nuxt, options.styles);
|
|
105
|
+
}
|
|
103
106
|
} else {
|
|
104
107
|
addStyles(nuxt, options.styles);
|
|
105
108
|
}
|