wave-ui 4.1.1 → 4.2.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/dist/types/types/$waveui.d.ts +2 -2
- package/dist/wave-ui.cjs.js +1 -1
- package/dist/wave-ui.esm.js +337 -309
- package/dist/wave-ui.umd.js +1 -1
- package/package.json +1 -1
- package/src/wave-ui/core.js +95 -3
- package/src/wave-ui/utils/dynamic-css.js +3 -3
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "wave-ui",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.2.0",
|
|
4
4
|
"description": "A UI framework for Vue.js 3 (and 2) with only the bright side. :sunny:",
|
|
5
5
|
"author": "Antoni Andre <antoniandre.web@gmail.com>",
|
|
6
6
|
"homepage": "https://antoniandre.github.io/wave-ui",
|
package/src/wave-ui/core.js
CHANGED
|
@@ -2,7 +2,7 @@ import { reactive, inject } from 'vue'
|
|
|
2
2
|
import { mergeConfig } from './utils/config'
|
|
3
3
|
import { consoleWarn } from './utils/console'
|
|
4
4
|
import { colorPalette, generateColorShades, flattenColors } from './utils/colors'
|
|
5
|
-
import { injectColorsCSSInDOM, injectCSSInDOM } from './utils/dynamic-css'
|
|
5
|
+
import { injectColorsCSSInDOM, injectCSSInDOM, generatePaletteVariables, generateColors } from './utils/dynamic-css'
|
|
6
6
|
import { injectNotifManagerInDOM, NotificationManager } from './utils/notification-manager'
|
|
7
7
|
import { waveRippleDirective } from './utils/wave-ripple-directive'
|
|
8
8
|
import { scheduleFocus, registerVFocus, unregisterVFocus } from './utils/focus'
|
|
@@ -88,9 +88,12 @@ export default class WaveUI {
|
|
|
88
88
|
* @param {string} theme - The theme to switch to.
|
|
89
89
|
*/
|
|
90
90
|
switchTheme (theme) {
|
|
91
|
+
// Only remove the current colors stylesheet when the theme actually changes.
|
|
92
|
+
// This prevents a blink when switchTheme is called on first mount with the same theme
|
|
93
|
+
// that was already set during SSR (via getSSRStyles + useHead) or in the constructor.
|
|
94
|
+
if (this.theme !== theme) document.head.querySelector('#wave-ui-colors')?.remove?.()
|
|
91
95
|
this.theme = theme
|
|
92
96
|
document.documentElement.setAttribute('data-theme', theme)
|
|
93
|
-
document.head.querySelector('#wave-ui-colors')?.remove?.()
|
|
94
97
|
const themeColors = this.config.colors[this.theme]
|
|
95
98
|
injectColorsCSSInDOM(themeColors, colorPalette, this.config.css.colorShadeCssVariables)
|
|
96
99
|
this.colors = flattenColors(themeColors, colorPalette)
|
|
@@ -107,9 +110,92 @@ export default class WaveUI {
|
|
|
107
110
|
wApp.className = 'w-app' // First reset the classes.
|
|
108
111
|
if (classes.length && classes[0]) wApp.classList.add(...classes)
|
|
109
112
|
}
|
|
113
|
+
},
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Returns the CSS strings for both Wave UI stylesheets so they can be injected
|
|
117
|
+
* server-side (e.g. via Nuxt's useHead) to prevent FOUC.
|
|
118
|
+
* The returned strings map to the `#wave-ui-palette` and `#wave-ui-colors` <style> tags.
|
|
119
|
+
*
|
|
120
|
+
* When called without a `theme` argument (recommended), `colors` contains both themes'
|
|
121
|
+
* custom color variables each scoped to `[data-theme="light"]` / `[data-theme="dark"]`.
|
|
122
|
+
* Combined with `WaveUI.getThemeInitScript()` injected as a blocking <script> in <head>,
|
|
123
|
+
* this guarantees the correct colors are present at first paint — no flash of wrong theme.
|
|
124
|
+
*
|
|
125
|
+
* When called with an explicit `theme`, returns only that theme's variables on `:root`
|
|
126
|
+
* (legacy / single-theme use cases).
|
|
127
|
+
*
|
|
128
|
+
* @param {string} [theme] - Explicit theme ('light'|'dark'). Omit for dual-scoped output.
|
|
129
|
+
* @returns {{ theme: string, palette: string, colors: string }}
|
|
130
|
+
*/
|
|
131
|
+
getSSRStyles (theme) {
|
|
132
|
+
const palette = generatePaletteVariables(colorPalette)
|
|
133
|
+
const { colorShadeCssVariables } = this.config.css
|
|
134
|
+
|
|
135
|
+
// No theme specified → emit both themes scoped to [data-theme="X"] so a blocking init
|
|
136
|
+
// script only needs to set the attribute — no color flash is possible.
|
|
137
|
+
if (!theme) {
|
|
138
|
+
return {
|
|
139
|
+
theme: this.theme || this.config?.theme || 'light',
|
|
140
|
+
palette,
|
|
141
|
+
colors:
|
|
142
|
+
generateColors(this.config.colors.light, colorShadeCssVariables, '[data-theme="light"]') +
|
|
143
|
+
generateColors(this.config.colors.dark, colorShadeCssVariables, '[data-theme="dark"]')
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
return {
|
|
148
|
+
theme,
|
|
149
|
+
palette,
|
|
150
|
+
colors: generateColors(this.config.colors[theme], colorShadeCssVariables)
|
|
151
|
+
}
|
|
110
152
|
}
|
|
111
153
|
}
|
|
112
154
|
|
|
155
|
+
/**
|
|
156
|
+
* Resolves the initial theme from localStorage or OS preference.
|
|
157
|
+
* Use this as the `theme` install option so Wave UI is initialized with the correct
|
|
158
|
+
* theme from the very first render — no flash, no manual localStorage reading needed.
|
|
159
|
+
*
|
|
160
|
+
* app.use(WaveUI, { theme: WaveUI.resolveInitialTheme() })
|
|
161
|
+
*
|
|
162
|
+
* On the server (SSR) where localStorage/window are unavailable, always returns 'light'
|
|
163
|
+
* as the safe fallback — pair with `getSSRStyles()` + `getThemeInitScript()` to handle
|
|
164
|
+
* the server → client handoff without FOUC.
|
|
165
|
+
*
|
|
166
|
+
* @param {string} [storageKey='waveui-theme'] - The localStorage key to read.
|
|
167
|
+
* @returns {'light'|'dark'}
|
|
168
|
+
*/
|
|
169
|
+
static resolveInitialTheme (storageKey = 'waveui-theme') {
|
|
170
|
+
if (typeof window === 'undefined') return 'light'
|
|
171
|
+
try {
|
|
172
|
+
const stored = localStorage.getItem(storageKey)
|
|
173
|
+
if (stored === 'light' || stored === 'dark') return stored
|
|
174
|
+
}
|
|
175
|
+
catch { }
|
|
176
|
+
return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light'
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* Returns a minified inline script that sets `data-theme` on `<html>` synchronously,
|
|
181
|
+
* before any CSS is parsed or rendered — eliminating theme FOUC entirely.
|
|
182
|
+
*
|
|
183
|
+
* Inject it as a blocking (no async/defer) <script> in <head> before any CSS:
|
|
184
|
+
*
|
|
185
|
+
* Nuxt — nuxt.config.ts:
|
|
186
|
+
* app: { head: { script: [{ innerHTML: WaveUI.getThemeInitScript() }] } }
|
|
187
|
+
*
|
|
188
|
+
* Plain HTML:
|
|
189
|
+
* <script><%= WaveUI.getThemeInitScript() %></script>
|
|
190
|
+
*
|
|
191
|
+
* @param {string} [storageKey='waveui-theme'] - Must match the key used when saving the theme.
|
|
192
|
+
* @returns {string} Minified inline script string (no <script> tags).
|
|
193
|
+
*/
|
|
194
|
+
static getThemeInitScript (storageKey = 'waveui-theme') {
|
|
195
|
+
const key = JSON.stringify(storageKey)
|
|
196
|
+
return `(function(){try{var t=localStorage.getItem(${key});if(t==='dark'||t==='light'){document.documentElement.setAttribute('data-theme',t);return}}catch(e){}if(window.matchMedia('(prefers-color-scheme:dark)').matches)document.documentElement.setAttribute('data-theme','dark')})()`
|
|
197
|
+
}
|
|
198
|
+
|
|
113
199
|
static install (app, options = {}) {
|
|
114
200
|
// Register directives.
|
|
115
201
|
app.directive('focus', {
|
|
@@ -159,7 +245,12 @@ export default class WaveUI {
|
|
|
159
245
|
}
|
|
160
246
|
|
|
161
247
|
if (config.theme === 'auto') detectOSDarkMode($waveui) // Also switches the theme.
|
|
162
|
-
else
|
|
248
|
+
else {
|
|
249
|
+
// Respect any data-theme already set on <html> (e.g. by getThemeInitScript) so a
|
|
250
|
+
// blocking init script is enough — no need to pass `theme` to app.use(WaveUI, ...).
|
|
251
|
+
const docTheme = document.documentElement.getAttribute('data-theme')
|
|
252
|
+
$waveui.switchTheme(docTheme === 'light' || docTheme === 'dark' ? docTheme : config.theme)
|
|
253
|
+
}
|
|
163
254
|
|
|
164
255
|
injectCSSInDOM($waveui)
|
|
165
256
|
injectNotifManagerInDOM(app)
|
|
@@ -203,6 +294,7 @@ export default class WaveUI {
|
|
|
203
294
|
app.provide('$waveui', $waveui)
|
|
204
295
|
|
|
205
296
|
if (config.theme !== 'auto') {
|
|
297
|
+
this.$waveui.theme = config.theme
|
|
206
298
|
this.$waveui.colors = flattenColors(config.colors[config.theme], colorPalette)
|
|
207
299
|
}
|
|
208
300
|
}
|
|
@@ -9,7 +9,7 @@ const cssVars = {
|
|
|
9
9
|
let breakpointsDef = { keys: [], values: [] }
|
|
10
10
|
let currentBreakpoint = null
|
|
11
11
|
|
|
12
|
-
const generatePaletteVariables = colorPalette => {
|
|
12
|
+
export const generatePaletteVariables = colorPalette => {
|
|
13
13
|
let cssVariablesString = ''
|
|
14
14
|
|
|
15
15
|
colorPalette.forEach(({ label, color, shades = [] }) => {
|
|
@@ -26,7 +26,7 @@ const generatePaletteVariables = colorPalette => {
|
|
|
26
26
|
// :root {[color1-variable], [color2-variable]}
|
|
27
27
|
// .color1--bg {background-color: [color1-variable]}
|
|
28
28
|
// .color1 {color: [color1-variable]}
|
|
29
|
-
const generateColors = (themeColors, generateShadeCssVariables) => {
|
|
29
|
+
export const generateColors = (themeColors, generateShadeCssVariables, scope = ':root') => {
|
|
30
30
|
let styles = ''
|
|
31
31
|
let cssVariablesString = ''
|
|
32
32
|
|
|
@@ -62,7 +62,7 @@ const generateColors = (themeColors, generateShadeCssVariables) => {
|
|
|
62
62
|
for (const colorName in shades) cssVariablesString += `--w-${colorName}-color: ${shades[colorName]};`
|
|
63
63
|
}
|
|
64
64
|
|
|
65
|
-
return
|
|
65
|
+
return `${scope}{${cssVariablesString}}${styles}`
|
|
66
66
|
}
|
|
67
67
|
|
|
68
68
|
// Generate the layout grid. E.g. xs1, xs2, ..., xl12.
|