element-plus/packages/hooks/use-global-config/index.ts
2022-03-25 15:35:56 +08:00

75 lines
2.2 KiB
TypeScript

import { computed, getCurrentInstance, inject, provide, ref, unref } from 'vue'
import { configProviderContextKey } from '@element-plus/tokens'
import { debugWarn, keysOf } from '@element-plus/utils'
import type { MaybeRef } from '@vueuse/core'
import type { App, Ref } from 'vue'
import type { ConfigProviderContext } from '@element-plus/tokens'
// this is meant to fix global methods like `ElMessage(opts)`, this way we can inject current locale
// into the component as default injection value.
// refer to: https://github.com/element-plus/element-plus/issues/2610#issuecomment-887965266
const globalConfig = ref<ConfigProviderContext>()
export function useGlobalConfig<
K extends keyof ConfigProviderContext,
D extends ConfigProviderContext[K]
>(
key: K,
defaultValue?: D
): Ref<Exclude<ConfigProviderContext[K], undefined> | D>
export function useGlobalConfig(): Ref<ConfigProviderContext>
export function useGlobalConfig(
key?: keyof ConfigProviderContext,
defaultValue = undefined
) {
const config = getCurrentInstance()
? inject(configProviderContextKey, globalConfig)
: globalConfig
if (key) {
return computed(() => config.value?.[key] ?? defaultValue)
} else {
return config
}
}
export const provideGlobalConfig = (
config: MaybeRef<ConfigProviderContext>,
app?: App,
global = false
) => {
const inSetup = !!getCurrentInstance()
const oldConfig = inSetup ? useGlobalConfig() : undefined
const provideFn = app?.provide ?? (inSetup ? provide : undefined)
if (!provideFn) {
debugWarn(
'provideGlobalConfig',
'provideGlobalConfig() can only be used inside setup().'
)
return
}
const context = computed(() => {
const cfg = unref(config)
if (!oldConfig?.value) return cfg
return mergeConfig(oldConfig.value, cfg)
})
provideFn(configProviderContextKey, context)
if (global || !globalConfig.value) {
globalConfig.value = context.value
}
return context
}
const mergeConfig = (
a: ConfigProviderContext,
b: ConfigProviderContext
): ConfigProviderContext => {
const keys = [...new Set([...keysOf(a), ...keysOf(b)])]
const obj = {}
for (const key of keys) {
obj[key] = b[key] ?? a[key]
}
return obj
}