import type { CSSInterpolation, Theme } from '../_util/cssinjs'; import { createTheme, useCacheToken, useStyleRegister } from '../_util/cssinjs'; import version from '../version'; import type { AliasToken, GlobalToken, MapToken, OverrideToken, PresetColorType, PresetColorKey, SeedToken, } from './interface'; import { PresetColors } from './interface'; import defaultDerivative from './themes/default'; import defaultSeedToken from './themes/seed'; import formatToken from './util/alias'; import type { FullToken } from './util/genComponentStyleHook'; import genComponentStyleHook from './util/genComponentStyleHook'; import statisticToken, { merge as mergeToken, statistic } from './util/statistic'; import type { VueNode } from '../_util/type'; import { objectType } from '../_util/type'; import type { ComputedRef, InjectionKey, Ref } from 'vue'; import { defineComponent, provide, computed, inject, watchEffect, ref } from 'vue'; import { toReactive } from '../_util/toReactive'; const defaultTheme = createTheme(defaultDerivative); export { // colors PresetColors, // Statistic statistic, statisticToken, mergeToken, // hooks useStyleRegister, genComponentStyleHook, }; export type { SeedToken, AliasToken, PresetColorType, PresetColorKey, // FIXME: Remove this type AliasToken as DerivativeToken, FullToken, }; // ================================ Context ================================= // To ensure snapshot stable. We disable hashed in test env. export const defaultConfig = { token: defaultSeedToken, hashed: true, }; export interface DesignTokenContext { token: Partial; theme?: Theme; components?: OverrideToken; hashed?: string | boolean; } //defaultConfig const DesignTokenContextKey: InjectionKey = Symbol('DesignTokenContext'); export const globalDesignTokenApi = ref(); export const useDesignTokenProvider = (value: DesignTokenContext) => { provide(DesignTokenContextKey, value); watchEffect(() => { globalDesignTokenApi.value = value; }); }; export const useDesignTokenInject = () => { return inject(DesignTokenContextKey, globalDesignTokenApi.value || defaultConfig); }; export const DesignTokenProvider = defineComponent({ props: { value: objectType(), }, setup(props, { slots }) { useDesignTokenProvider(toReactive(computed(() => props.value))); return () => { return slots.default?.(); }; }, }); // ================================== Hook ================================== export function useToken(): [ ComputedRef>, ComputedRef, ComputedRef, ] { const designTokenContext = inject( DesignTokenContextKey, globalDesignTokenApi.value || defaultConfig, ); const salt = computed(() => `${version}-${designTokenContext.hashed || ''}`); const mergedTheme = computed(() => designTokenContext.theme || defaultTheme); const cacheToken = useCacheToken( mergedTheme, computed(() => [defaultSeedToken, designTokenContext.token]), computed(() => ({ salt: salt.value, override: { override: designTokenContext.token, ...designTokenContext.components }, formatToken, })), ); return [ mergedTheme, computed(() => cacheToken.value[0]), computed(() => (designTokenContext.hashed ? cacheToken.value[1] : '')), ]; } export type UseComponentStyleResult = [(node: VueNode) => VueNode, Ref]; export type GenerateStyle< ComponentToken extends object = AliasToken, ReturnType = CSSInterpolation, > = (token: ComponentToken) => ReturnType;