95 lines
2.5 KiB
TypeScript
95 lines
2.5 KiB
TypeScript
import {
|
|
Fragment,
|
|
Static,
|
|
type VNode,
|
|
getCurrentInstance,
|
|
onMounted,
|
|
onUnmounted,
|
|
warn,
|
|
watchPostEffect,
|
|
} from '@vue/runtime-core'
|
|
import { ShapeFlags } from '@vue/shared'
|
|
|
|
export const CSS_VAR_TEXT = Symbol(__DEV__ ? 'CSS_VAR_TEXT' : '')
|
|
/**
|
|
* Runtime helper for SFC's CSS variable injection feature.
|
|
* @private
|
|
*/
|
|
export function useCssVars(getter: (ctx: any) => Record<string, string>) {
|
|
if (!__BROWSER__ && !__TEST__) return
|
|
|
|
const instance = getCurrentInstance()
|
|
/* istanbul ignore next */
|
|
if (!instance) {
|
|
__DEV__ &&
|
|
warn(`useCssVars is called without current active component instance.`)
|
|
return
|
|
}
|
|
|
|
const updateTeleports = (instance.ut = (vars = getter(instance.proxy)) => {
|
|
Array.from(
|
|
document.querySelectorAll(`[data-v-owner="${instance.uid}"]`),
|
|
).forEach(node => setVarsOnNode(node, vars))
|
|
})
|
|
|
|
if (__DEV__) {
|
|
instance.getCssVars = () => getter(instance.proxy)
|
|
}
|
|
|
|
const setVars = () => {
|
|
const vars = getter(instance.proxy)
|
|
setVarsOnVNode(instance.subTree, vars)
|
|
updateTeleports(vars)
|
|
}
|
|
|
|
watchPostEffect(setVars)
|
|
|
|
onMounted(() => {
|
|
const ob = new MutationObserver(setVars)
|
|
ob.observe(instance.subTree.el!.parentNode, { childList: true })
|
|
onUnmounted(() => ob.disconnect())
|
|
})
|
|
}
|
|
|
|
function setVarsOnVNode(vnode: VNode, vars: Record<string, string>) {
|
|
if (__FEATURE_SUSPENSE__ && vnode.shapeFlag & ShapeFlags.SUSPENSE) {
|
|
const suspense = vnode.suspense!
|
|
vnode = suspense.activeBranch!
|
|
if (suspense.pendingBranch && !suspense.isHydrating) {
|
|
suspense.effects.push(() => {
|
|
setVarsOnVNode(suspense.activeBranch!, vars)
|
|
})
|
|
}
|
|
}
|
|
|
|
// drill down HOCs until it's a non-component vnode
|
|
while (vnode.component) {
|
|
vnode = vnode.component.subTree
|
|
}
|
|
|
|
if (vnode.shapeFlag & ShapeFlags.ELEMENT && vnode.el) {
|
|
setVarsOnNode(vnode.el as Node, vars)
|
|
} else if (vnode.type === Fragment) {
|
|
;(vnode.children as VNode[]).forEach(c => setVarsOnVNode(c, vars))
|
|
} else if (vnode.type === Static) {
|
|
let { el, anchor } = vnode
|
|
while (el) {
|
|
setVarsOnNode(el as Node, vars)
|
|
if (el === anchor) break
|
|
el = el.nextSibling
|
|
}
|
|
}
|
|
}
|
|
|
|
function setVarsOnNode(el: Node, vars: Record<string, string>) {
|
|
if (el.nodeType === 1) {
|
|
const style = (el as HTMLElement).style
|
|
let cssText = ''
|
|
for (const key in vars) {
|
|
style.setProperty(`--${key}`, vars[key])
|
|
cssText += `--${key}: ${vars[key]};`
|
|
}
|
|
;(style as any)[CSS_VAR_TEXT] = cssText
|
|
}
|
|
}
|