import type { ShallowRef, InjectionKey, ExtractPropTypes, ComputedRef } from 'vue'; import { defineComponent, inject, provide, shallowRef, unref, triggerRef, watch, computed, } from 'vue'; import type { Theme } from '../_util/cssinjs'; import { createTheme } from '../_util/cssinjs'; import { objectType, someType } from '../_util/type'; import type { AliasToken, MapToken, OverrideToken, SeedToken } from './interface'; import defaultDerivative from './themes/default'; import defaultSeedToken from './themes/seed'; export const defaultTheme = createTheme(defaultDerivative); // ================================ Context ================================= // To ensure snapshot stable. We disable hashed in test env. const DesignTokenContextKey: InjectionKey>> = Symbol('DesignTokenContextKey'); export const globalDesignTokenApi = shallowRef(); export const defaultConfig = { token: defaultSeedToken, override: { override: defaultSeedToken }, hashed: true, }; export type ComponentsToken = { [key in keyof OverrideToken]?: OverrideToken[key] & { theme?: Theme; }; }; export const styleProviderProps = () => ({ token: objectType(), theme: objectType>(), components: objectType(), /** Just merge `token` & `override` at top to save perf */ override: objectType<{ override: Partial } & ComponentsToken>(), hashed: someType(), cssVar: someType<{ prefix?: string; key?: string; }>(), }); export type StyleProviderProps = Partial>>; export interface DesignTokenProviderProps { token: Partial; theme?: Theme; components?: ComponentsToken; /** Just merge `token` & `override` at top to save perf */ override?: { override: Partial } & ComponentsToken; hashed?: string | boolean; cssVar?: { prefix?: string; key?: string; }; } export const useDesignTokenProvider = (value: ComputedRef) => { provide(DesignTokenContextKey, value); watch( value, () => { globalDesignTokenApi.value = unref(value); triggerRef(globalDesignTokenApi); }, { immediate: true, deep: true }, ); }; export const useDesignTokenInject = () => { return inject( DesignTokenContextKey, computed(() => globalDesignTokenApi.value || defaultConfig), ); }; export const DesignTokenProvider = defineComponent({ props: { value: objectType(), }, setup(props, { slots }) { useDesignTokenProvider(computed(() => props.value)); return () => { return slots.default?.(); }; }, }); export default { useDesignTokenInject, useDesignTokenProvider, DesignTokenProvider };