From cf57c638a6fdfd02d51416b937688934cece4281 Mon Sep 17 00:00:00 2001 From: Maksim Nedoshev Date: Mon, 1 May 2023 09:05:12 +0300 Subject: [PATCH] Fix/dark theme blink (#3387) * fix: refactor nuxt module plugin ts types * feat(nuxt): save theme in cookies * chore(docs): remove useTheme --- .../components/layout/header/ThemeSwitch.vue | 6 +-- packages/docs/composables/useTheme.ts | 28 ------------- packages/docs/config/vuestic-config.ts | 5 --- packages/docs/layouts/default.vue | 3 +- packages/nuxt/nuxt.shim.d.ts | 15 +++++++ packages/nuxt/src/module.ts | 1 + packages/nuxt/src/runtime/plugin.ts | 39 ++++++++++++------- packages/nuxt/src/types.ts | 13 ++++--- 8 files changed, 52 insertions(+), 58 deletions(-) delete mode 100644 packages/docs/composables/useTheme.ts diff --git a/packages/docs/components/layout/header/ThemeSwitch.vue b/packages/docs/components/layout/header/ThemeSwitch.vue index c55991a18f..ecb2694373 100644 --- a/packages/docs/components/layout/header/ThemeSwitch.vue +++ b/packages/docs/components/layout/header/ThemeSwitch.vue @@ -25,13 +25,11 @@ import { computed } from 'vue' import { useColors } from 'vuestic-ui' -const { currentPresetName } = useColors() - -const { setTheme } = useTheme() +const { currentPresetName, applyPreset } = useColors() const isDark = computed({ get: () => currentPresetName.value === 'dark', - set: (value) => setTheme(value ? 'dark' : 'light'), + set: (value) => applyPreset(value ? 'dark' : 'light'), }) diff --git a/packages/docs/composables/useTheme.ts b/packages/docs/composables/useTheme.ts deleted file mode 100644 index 80c312491d..0000000000 --- a/packages/docs/composables/useTheme.ts +++ /dev/null @@ -1,28 +0,0 @@ -export const useTheme = () => { - const colorMode = useColorMode() - const cookie = useCookie('vuestic-theme') - - const { applyPreset } = useColors() - - const setTheme = (theme?: string) => { - if (theme) { - colorMode.preference = theme - applyPreset(theme) - cookie.value = theme - return - } - - if (cookie.value) { - applyPreset(cookie.value) - return - } - - if (!colorMode.unknown) { - applyPreset(colorMode.value) - } - } - - return { - setTheme - } -} diff --git a/packages/docs/config/vuestic-config.ts b/packages/docs/config/vuestic-config.ts index f6811ea31d..095418595a 100644 --- a/packages/docs/config/vuestic-config.ts +++ b/packages/docs/config/vuestic-config.ts @@ -11,10 +11,6 @@ const VaButtonLandingHeader = { 'hover-opacity': '1', } -// const cookie = useCookie('vuestic-theme') - -const theme = 'light' - export const VuesticConfig = defineVuesticConfig({ icons, components: { @@ -39,7 +35,6 @@ export const VuesticConfig = defineVuesticConfig({ }, }, colors: { - currentPresetName: theme, presets: { light: { secondary: '#666E75', diff --git a/packages/docs/layouts/default.vue b/packages/docs/layouts/default.vue index 451ada1d62..5c30a3e5c8 100644 --- a/packages/docs/layouts/default.vue +++ b/packages/docs/layouts/default.vue @@ -32,7 +32,6 @@ import { useColors } from 'vuestic-ui' import { useDocsScroll } from '../composables/useDocsScroll'; -const colorMode = useColorMode() const cookie = useCookie('vuestic-theme') const { applyPreset } = useColors() const breakpoints = useBreakpoint() @@ -40,7 +39,7 @@ const breakpoints = useBreakpoint() const isSidebarVisible = ref(!breakpoints.smDown) const isOptionsVisible = ref(false) -applyPreset(cookie.value || colorMode.preference) +applyPreset(cookie.value || 'light') watch(() => breakpoints.smDown, (newValue, oldValue) => { if (newValue && !oldValue) { diff --git a/packages/nuxt/nuxt.shim.d.ts b/packages/nuxt/nuxt.shim.d.ts index a546b53335..b6ad41afb5 100644 --- a/packages/nuxt/nuxt.shim.d.ts +++ b/packages/nuxt/nuxt.shim.d.ts @@ -2,7 +2,22 @@ declare module '#app' { export * from 'nuxt/app' } +declare module '#app/components/nuxt-link' { + export * from 'nuxt/dist/app/components/nuxt-link.ts' + export { default as default } from 'nuxt/dist/app/components/nuxt-link.ts' +} + // Path to vuestic-ui main, so it is not required to build vuestic before working with packages/nuxt declare module 'vuestci-ui' { export * from 'vuestic-ui/src/main' +} + +declare module '#imports' { + export * from 'nuxt/dist/head/runtime/composables.ts' +} + +declare module '#vuestic-config' { + import { GlobalConfig } from 'vuestic-ui' + var gc: GlobalConfig | undefined + export default gc } \ No newline at end of file diff --git a/packages/nuxt/src/module.ts b/packages/nuxt/src/module.ts index 05c570897d..d4435d327e 100644 --- a/packages/nuxt/src/module.ts +++ b/packages/nuxt/src/module.ts @@ -22,6 +22,7 @@ export default defineNuxtModule({ config: {}, css: ['smart-helpers', 'typography'], fonts: true, + themeCookieKey: 'vuestic-theme', }, setup (options) { diff --git a/packages/nuxt/src/runtime/plugin.ts b/packages/nuxt/src/runtime/plugin.ts index feaf2f3bd4..4ad9706841 100644 --- a/packages/nuxt/src/runtime/plugin.ts +++ b/packages/nuxt/src/runtime/plugin.ts @@ -5,34 +5,41 @@ import { VaModalPlugin, ColorsClassesPlugin, BreakpointConfigPlugin, + type GlobalConfig, + type PartialGlobalConfig } from 'vuestic-ui' -import { markRaw, computed } from 'vue' +import { markRaw, computed, watch, type Ref } from 'vue' -import type { VuesticOptions } from '../types' -// @ts-ignore: nuxt import alias -import { defineNuxtPlugin } from '#app' -// @ts-ignore: direct nuxt-link import +import { defineNuxtPlugin, useCookie } from '#app' +import { useHead } from '#imports' import NuxtLink from '#app/components/nuxt-link' -// @ts-ignore: use-config-file import alias import configFromFile from '#vuestic-config' -// @ts-ignore: use-head import alias -import { useHead } from '#imports' + +import type { VuesticOptions } from '../types' function getGlobalProperty (app, key) { return app.config.globalProperties[key] } -export default defineNuxtPlugin((nuxtApp) => { +export default defineNuxtPlugin(async (nuxtApp) => { const { vueApp: app } = nuxtApp // It's important to use `, because TS will compile qoutes to " and JSON will not be parsed... - const { config }: VuesticOptions = JSON.parse(`<%= options.value %>`) - const userConfig = configFromFile || config + const moduleOptions: VuesticOptions = JSON.parse(`<%= options.value %>`) + const themeCookie = useCookie(moduleOptions.themeCookieKey) + const userConfig = configFromFile || moduleOptions.config || {} + const configWithColors: PartialGlobalConfig = { + ...userConfig, + colors: { + currentPresetName: themeCookie.value || userConfig.colors?.currentPresetName, + ...userConfig.colors, + } + } /** Use tree-shaking by default and do not register any component. Components will be registered by nuxt in use-components. */ app.use(createVuesticEssential({ - config: { ...userConfig, routerComponent: markRaw(NuxtLink) }, - // TODO: Would be nice to tree-shake plugins, but they're small so we don't cant for now. + config: { ...configWithColors, routerComponent: markRaw(NuxtLink) }, + // TODO: Would be nice to tree-shake plugins, but they're small so we can let it be for now. // Should be synced with create-vuestic.ts plugins: { BreakpointConfigPlugin, @@ -66,4 +73,10 @@ export default defineNuxtPlugin((nuxtApp) => { } })) } + + // Watch for preset name change and update cookie + const { globalConfig } = getGlobalProperty(app, '$vaConfig') as { globalConfig: Ref } + watch(() => globalConfig.value.colors.currentPresetName, (newTheme) => { + themeCookie.value = newTheme + }, { immediate: true }) }) diff --git a/packages/nuxt/src/types.ts b/packages/nuxt/src/types.ts index 63fdfefe9e..a1c0e66fbb 100644 --- a/packages/nuxt/src/types.ts +++ b/packages/nuxt/src/types.ts @@ -32,11 +32,12 @@ export interface VuesticOptions { * @see https://vuestic.dev/en/getting-started/installation#assets-installation */ fonts: boolean -} -/** Declare Vuestic module options in NuxtConfig */ - declare module '@nuxt/schema' { - interface NuxtConfig { - vuestic?: Partial - } + + /** + * Vuestic will automatically store its theme in cookies. If you want to change the key, you can do it here. + * + * @default 'vuestic-theme' + */ + themeCookieKey: string }