| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351 |
- import { computed, inject, provide, ref, shallowRef, type ComputedRef, type Ref } from "vue";
- import { DefaultDarkTheme, DefaultTheme } from "./Theme";
- import type { DynamicVarType } from "./ThemeTools";
- import { ObjectUtils } from "@imengyu/imengyu-utils";
- export const ThemeKey = Symbol("NanaThemeKey");
- let defaultSizeUnit = 'rpx';
- /**
- * 配置默认的尺寸单位
- * @param unit
- */
- export function configDefaultSizeUnit(unit: string) {
- defaultSizeUnit = unit;
- }
- export type ViewStyle = Record<string, any>
- export type TextStyle = Record<string, any>
- export type ThemeSizeType = number|string;
- export type ThemePaddingOrMarginType = number|Array<number>|ThemePaddingMargin|string|string[];
- export interface ThemeConfig {
- varOverrides: Record<string, any>,
- colorConfigs: Record<string, Record<string, string>>,
- textConfigs: Record<string, Record<string, string|object>>,
- }
- function popInjectTheme() : ThemeConfig {
- return inject<Ref<ThemeConfig>>(ThemeKey)?.value ?? DefaultTheme;
- }
- /**
- * 在PropDefault回调中使用主题默认值的函数
- * @param key
- * @param defaultValue
- * @returns
- */
- export function propGetThemeVar<T>(key: string, defaultValue?: T) : T {
- const theme = popInjectTheme();
- return theme?.varOverrides[key] ?? defaultValue;
- }
- /**
- * 在PropDefault回调中使用主题默认值的函数(回调)
- * @param cb 回调函数,参数为获取主题变量的函数和主题配置
- * @returns 回调函数的返回值
- */
- export function propGetThemeVar2(cb: (getVar: (key: string, defaultValue: any) => any, theme: ThemeConfig) => any) {
- const theme = popInjectTheme();
- return cb((key, defaultValue) => getVar(theme, key, defaultValue), theme);
- }
- export function provideSomeThemeColor(record: Record<string, any>) {
- const topTheme = inject<Ref<ThemeConfig>>(ThemeKey);
- const newTheme = computed(() => {
- const topThemeValue = topTheme?.value ?? DefaultTheme;
- const v = {
- ...topThemeValue,
- colorConfigs: {
- ...topThemeValue.colorConfigs,
- ...record
- }
- } as ThemeConfig;
- return v;
- });
- provide(ThemeKey, newTheme);
- }
- export function provideSomeThemeVar(record: Record<string, any>) {
- const topTheme = inject<Ref<ThemeConfig>>(ThemeKey);
- const newTheme = computed(() => {
- const topThemeValue = topTheme?.value ?? DefaultTheme;
- const v = {
- ...topThemeValue,
- varOverrides: {
- ...topThemeValue.varOverrides,
- ...record
- }
- } as ThemeConfig;
- return v;
- });
- provide(ThemeKey, newTheme);
- }
- export function getVar<T>(theme : ThemeConfig, key: string, defaultValue: T) : T {
- return theme.varOverrides[key] ?? defaultValue;
- }
- /**
- * 主题的组合代码
- */
- export function useTheme() {
- const topTheme = inject<Ref<ThemeConfig>>(ThemeKey);
- const theme = computed(() => topTheme?.value ?? DefaultTheme);
- function resolveThemeSize(inValue?: ThemeSizeType, defaultValue?: ThemeSizeType) : string|undefined {
- const preResolve = resolveSize(inValue);
- if (preResolve !== undefined)
- return preResolve;
- if (defaultValue && !isRealSize(defaultValue))
- defaultValue = resolveThemeSize(defaultValue);
- if (inValue === undefined)
- inValue = defaultValue;
- if (inValue === undefined)
- return undefined;
- if (typeof inValue === 'string') {
- const v = getSize(inValue, undefined);
- if (v === undefined)
- inValue = defaultValue;
- else
- inValue = v;
- }
- return resolveSize(inValue);
- }
- function resolveThemeColor(inValue?: string, defaultValue?: string) : string|undefined {
- if (isSpecialColor(inValue))
- return inValue;
- if (inValue === undefined)
- inValue = defaultValue;
- else
- defaultValue = resolveThemeColor(defaultValue);
- if (inValue === undefined)
- return undefined;
- if (inValue.startsWith('#') || inValue.startsWith('rgb'))
- return inValue;
- return getColor(inValue, defaultValue);
- }
- function resolveThemeSizes<T extends Record<string, string|number>>(defaults: T) {
- const result : T = {} as T;
- for (let key in defaults)
- result[key] = resolveThemeSize(key, defaults[key]) as any;
- return result;
- }
- function resolveThemeColors<T extends Record<string, string>>(defaults: T) {
- const result : T = {} as T;
- for (let key in defaults)
- result[key] = resolveThemeColor(key, defaults[key]) as any;
- return result;
- }
- function isSpecialColor(key?: string) {
- return key === 'transparent' || key === 'currentColor';
- }
- function getColor(key: string, defaultValue?: string) {
- if (key === undefined)
- return defaultValue;
- let type = '';
- let keyResolve = getVar(key, key);
- if (typeof keyResolve === 'string')
- key = keyResolve;
- if (key.includes('.'))
- [type, key] = key.split('.');
- if (isSpecialColor(keyResolve))
- return keyResolve;
- let group = theme.value.colorConfigs[type || 'default'];
- if (!group)
- group = theme.value.colorConfigs['default'];
- return group?.[key] ?? defaultValue;
- }
- function getSize(key: string, defaultValue?: string|number) {
- if (key === undefined)
- return defaultValue;
- let type = '';
- if (key.includes('.'))
- [type, key] = key.split('.');
- let v: any = undefined;
- if (type) {
- const group = theme.value.varOverrides[type];
- v = group?.[key];
- } else
- v = theme.value.varOverrides[key];
- return resolveSize(v ?? defaultValue);
- }
- function getText(key: string, defaultValue?: Record<string, string>) {
- return theme.value.textConfigs[key]?? defaultValue;
- }
- function getVar<T>(key: string, defaultValue?: T) : T {
- let rs = undefined;
- let type = '';
- if (key.includes('.'))
- [type, key] = key.split('.');
- if (type) {
- const group = theme.value.varOverrides[type];
- rs = group?.[key];
- } else
- rs = theme.value.varOverrides[key];
- return rs ?? defaultValue;
- }
- function getVars<T extends Record<string, string|number|boolean>>(defaults: T) {
- const result : T = {} as T;
- for (let key in defaults) {
- result[key] = getVar(key, defaults[key]);
- }
- return result;
- }
- function getColors<T extends Record<string, string|undefined>>(defaults: T) {
- const result : T = {} as T;
- for (let key in defaults) {
- result[key] = getColor(key, defaults[key]) as any;
- }
- return result;
- }
- function useThemeStyle(style: Record<string, any>) {
- const mapDynamicVars = new Map<string, DynamicVarType>();
- for (const key in style) {
- const v = style[key];
- if (typeof v === 'object' && typeof v['type'] === 'string')
- mapDynamicVars.set(key, v)
- }
- return computed(() => {
- for (const [key, v] of mapDynamicVars) {
- switch (v.type) {
- case 'var': style[key] = getVar(v.name, v.defaultValue); break;
- case 'color': style[key] = resolveThemeColor(v.name, v.defaultValue); break;
- case 'size': style[key] = resolveThemeSize(v.name, v.defaultValue); break;
- case 'size2': style[key] = `${resolveThemeSize(v.name, v.defaultValue)} ${resolveThemeSize(v.name2, v.defaultValue2)}`; break;
- }
- }
- return style;
- })
- }
- function useThemeStyles<T extends Record<string, Record<string, any>>>
- (style: Record<string, Record<string, any>>) : {
- [key in keyof T]: ComputedRef<Record<string, any>>
- }
- {
- const result = {} as Record<string, Record<string, any>>;
- for (const key in style)
- result[key] = useThemeStyle(style[key]);
- return result as any;
- }
- return {
- theme,
- resolveThemeSizes,
- resolveThemeSize,
- resolveThemeColors,
- resolveThemeColor,
- resolveSize,
- getSize,
- getVar,
- getVars,
- getText,
- getColor,
- getColors,
- useThemeStyle,
- useThemeStyles,
- }
- }
- export type ThemeContext = ReturnType<typeof useTheme>;
- function isNumbrSize(inValue: ThemeSizeType|undefined) {
- if (inValue == undefined)
- return false;
- return typeof inValue === 'number' || !isNaN(Number(inValue));
- }
- function isRealSize(inValue: ThemeSizeType|undefined) {
- if (inValue == undefined)
- return false;
- return typeof inValue === 'number' ||
- (typeof inValue === 'string' && (
- inValue.startsWith('calc(') || inValue.endsWith('px') || inValue.endsWith('%') ||
- inValue.endsWith('em') || inValue.endsWith('vh') || inValue.endsWith('vw')
- ));
- }
- /**
- * 数字单位的处理
- * @param inValue 输入值
- * @param forceUnit 强制单位,默认为defaultSizeUnit即系统配置单位
- * @returns
- */
- export function resolveSize(inValue: ThemeSizeType|undefined, forceUnit = defaultSizeUnit) : string|undefined {
- if (inValue == undefined)
- return undefined;
- if (isNumbrSize(inValue))
- return `${inValue}${forceUnit}`;
- if (isRealSize(inValue as string))
- return inValue as string;
- if (inValue == 'fill')
- return '100%';
- if (inValue == 'half' || inValue == '1/2')
- return '50%';
- if (inValue == '1/3')
- return '33%';
- if (inValue == '1/4')
- return '25%';
- return undefined;
- }
- export interface ThemePaddingMargin {
- l?: number|string,
- r?: number|string,
- t?: number|string,
- b?: number|string,
- }
- /**
- * 配置主题变量,建议在App.vue中调用
- * @param cb 回调函数,参数为默认主题配置,返回值为新的主题配置
- * @returns 主题配置对象,返回currentTheme为当前主题,可以修改为其他主题对象,但请注意主题对象为shallowRef,
- * 直接修改主题对象的属性不会触发主题更新,需要调用currentTheme.value = newTheme新的对象来更新主题。
- */
- export function configTheme(
- /**
- * 是否自动匹配系统深色主题
- * @default true
- */
- autoMatchSystemDark?: boolean,
- /**
- * 回调函数,用于配置修改主题,参数为默认主题配置,返回值为新的主题配置
- * @default undefined
- * @returns 可以返回新的主题对象或者是传入的对象,第一个为亮色主题,第二个为深色主题
- */
- cb?: (defaultTheme: ThemeConfig, defaultDarkTheme: ThemeConfig) => [ThemeConfig,ThemeConfig]
- ) {
- let defaultTheme = ObjectUtils.clone(DefaultTheme);
- let defaultDarkTheme = ObjectUtils.clone(DefaultDarkTheme);
-
- const [theme, darkTheme] = cb?.(defaultTheme, defaultDarkTheme) ?? [defaultTheme, defaultDarkTheme];
- const currentSystemDark = ref(autoMatchSystemDark !== false && uni.getAppBaseInfo().theme === 'dark');
- const currentTheme = shallowRef(currentSystemDark.value ? darkTheme : theme);
- provide(ThemeKey, currentTheme);
- if (autoMatchSystemDark !== false) {
- // 监听系统主题变化
- uni.onThemeChange((res) => {
- currentSystemDark.value = res.theme === 'dark';
- currentTheme.value = currentSystemDark.value ? darkTheme : theme;
- });
- }
- return {
- currentTheme,
- }
- }
- /**
- * 克隆默认主题,用于自定义主题
- * @param cb 回调函数,参数为默认主题配置,返回值为新的主题配置
- * @param dark 是否为深色主题,默认false为亮色主题
- * @returns 新的主题配置对象
- */
- export function cloneTheme(dark?: boolean, cb?: (defaultTheme: ThemeConfig) => ThemeConfig) {
- return cb?.(ObjectUtils.clone(dark ? DefaultDarkTheme : DefaultTheme)) ?? ObjectUtils.clone(dark ? DefaultDarkTheme : DefaultTheme);
- }
|