Skip to content

Commit afef877

Browse files
committed
feat(useTheme): add support for unhead
1 parent 0a54e6a commit afef877

File tree

2 files changed

+44
-9
lines changed

2 files changed

+44
-9
lines changed

packages/0/src/composables/useTheme/index.ts

Lines changed: 42 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ export interface ThemePluginOptions<Z extends ThemeRecord = ThemeRecord> {
6161
default?: ID
6262
palette?: TokenCollection
6363
themes?: Record<ID, Z>
64+
target?: string | HTMLElement | null
6465
}
6566

6667
/**
@@ -246,24 +247,56 @@ export function createThemePlugin<
246247
Z extends ThemeTicket = ThemeTicket,
247248
E extends ThemeContext<Z> = ThemeContext<Z>,
248249
> (_options: ThemePluginOptions = {}) {
249-
const { adapter = new Vuetify0ThemeAdapter(), palette = {}, themes = {}, ...options } = _options
250+
const { adapter = new Vuetify0ThemeAdapter(), palette = {}, themes = {}, target, ...options } = _options
250251
const [, provideThemeContext, themeContext] = createTheme<Z, E>('v0:theme', { ...options, themes, palette })
251252

252-
function update (colors: Record<string, Colors>) {
253-
adapter.update(colors)
254-
}
255-
256253
return createPlugin({
257254
namespace: 'v0:theme',
258255
provide: (app: App) => {
259256
provideThemeContext(themeContext, app)
260257
},
261-
setup: () => {
258+
setup: (app: App) => {
262259
if (IN_BROWSER) {
263-
const stop = watch(themeContext.colors, update, { immediate: true, deep: true })
264-
onScopeDispose(stop, true)
260+
const stopWatch = watch(themeContext.colors, colors => {
261+
adapter.update(colors)
262+
}, { immediate: true })
263+
264+
onScopeDispose(stopWatch, true)
265+
266+
if (target === null) return
267+
268+
const targetEl = target instanceof HTMLElement
269+
? target
270+
: (typeof target === 'string'
271+
? document.querySelector(target)
272+
: (app._container as HTMLElement | undefined) || document.querySelector('#app') || document.body)
273+
274+
if (!targetEl) return
275+
276+
let prevClass = ''
277+
278+
const stopClass = watch(themeContext.selectedId, id => {
279+
if (!id) return
280+
281+
const themeClass = `${adapter.prefix}-theme--${id}`
282+
if (prevClass) targetEl.classList.remove(prevClass)
283+
targetEl.classList.add(themeClass)
284+
prevClass = themeClass
285+
}, { immediate: true })
286+
287+
onScopeDispose(stopClass, true)
265288
} else {
266-
update(themeContext.colors.value)
289+
const head = app._context?.provides?.usehead
290+
if (head?.push) {
291+
const id = themeContext.selectedId.value
292+
head.push({
293+
htmlAttrs: { class: id ? `${adapter.prefix}-theme--${id}` : '' },
294+
style: [{
295+
innerHTML: adapter.generate(themeContext.colors.value),
296+
id: adapter.stylesheetId,
297+
}],
298+
})
299+
}
267300
}
268301
},
269302
})

playground/src/components.d.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@ declare module 'vue' {
2525
PopoverAnchor: typeof import('./../../packages/0/src/components/Popover/PopoverAnchor.vue')['default']
2626
PopoverContent: typeof import('./../../packages/0/src/components/Popover/PopoverContent.vue')['default']
2727
PopoverRoot: typeof import('./../../packages/0/src/components/Popover/PopoverRoot.vue')['default']
28+
RouterLink: typeof import('vue-router')['RouterLink']
29+
RouterView: typeof import('vue-router')['RouterView']
2830
StepItem: typeof import('./../../packages/0/src/components/Step/StepItem.vue')['default']
2931
StepRoot: typeof import('./../../packages/0/src/components/Step/StepRoot.vue')['default']
3032
ThemeItem: typeof import('./../../packages/0/src/components/Theme/ThemeItem.vue')['default']

0 commit comments

Comments
 (0)