diff --git a/README.md b/README.md index 9d5d883..6ba9c91 100644 --- a/README.md +++ b/README.md @@ -67,3 +67,79 @@ pnpm up pnpm lint pnpm lint:fix ``` + +## 主题 + +### 主题配置 + +```ts +const themeSetting = { + themeMode: 'auto', + themeColor: { + primaryColor: '#18A058', + infoColor: '#2080F0', + successColor: '#18A058', + warningColor: '#F0A020', + errorColor: '#D03050', + }, + darkThemeColor: { + primaryColor: '#63e2b7', + infoColor: '#70c0e8', + successColor: '#63e2b7', + warningColor: '#f2c97d', + errorColor: '#e88080', + }, + layoutMode: 'vertical', + header: { + show: true, + height: 64, + inverted: false, + }, + sider: { + show: true, + width: 240, + collapsedWidth: 64, + inverted: false, + }, + footer: { + show: true, + height: 64, + inverted: false, + }, + page: { + animate: true, + animateMode: 'fade-slide', + loadingBar: true, + }, +} + +const { setting } = useThemeSetting({ initialValue: themeSetting }) +``` + +### 主题模式 + +- 明亮 +- 暗黑 +- 系统 + +```ts +const mode = useColorMode() +console.log(mode.value) // 'light' | 'dark' | 'auto' +``` + +### 全局化配置 + +```ts +const configProviderProps = useNaiveConfigProvider() +``` + +```html + ... +``` + +### 脱离上下文的 API + +```ts +const { dialog } = useNaiveApi() +dialog.warning('warning!') +``` diff --git a/src/App.vue b/src/App.vue index 3d7e4ba..a8002a2 100644 --- a/src/App.vue +++ b/src/App.vue @@ -3,9 +3,21 @@ import { cloneDeep } from 'lodash-es' import { themeSetting } from '~/settings/theme' const { setting } = useThemeSetting({ initialValue: cloneDeep(themeSetting) }) -const { theme, themeOverrides } = useNaiveTheme({ - themeOverrides: computed(() => ({ - common: setting.value?.themeColor, + +const configProviderProps = useNaiveConfigProvider({ + themeOverrides: { + common: { + borderRadius: '8px', + }, + Card: { + borderRadius: '12px', + }, + Drawer: { + borderRadius: '12px', + }, + }, + lightThemeOverrides: computed(() => ({ + common: setting.value?.lightThemeColor, })), darkThemeOverrides: computed(() => ({ common: setting.value?.darkThemeColor, @@ -14,10 +26,7 @@ const { theme, themeOverrides } = useNaiveTheme({ diff --git a/src/composables/useNaiveConfigProvider.ts b/src/composables/useNaiveConfigProvider.ts new file mode 100644 index 0000000..db45da5 --- /dev/null +++ b/src/composables/useNaiveConfigProvider.ts @@ -0,0 +1,156 @@ +import type { UseColorModeOptions } from '@vueuse/core' +import { merge } from 'lodash-es' +import type { ConfigProviderProps, GlobalThemeOverrides } from 'naive-ui' +import { commonDark, darkTheme, dateZhCN, zhCN } from 'naive-ui' +import type { MaybeRefOrGetter } from 'vue' + +export type ColorType = 'primary' | 'info' | 'success' | 'warning' | 'error' +export type ColorScene = '' | 'Suppl' | 'Hover' | 'Pressed' +export interface ColorAction { + scene: ColorScene + handler: (color: string) => string +} + +export interface UseNaiveConfigProviderOptions { + /** + * 是否不存在 DOM 包裹 + */ + abstract?: ConfigProviderProps['abstract'] + /** + * 屏幕响应式断点,对 n-grid 生效。这个属性不是响应式的,你需要在组件第一次挂载时就设定好 + */ + breakpoints?: ConfigProviderProps['breakpoints'] + /** + * 内部所有组件的类的前缀,仅首次设定会生效 + */ + clsPrefix?: ConfigProviderProps['clsPrefix'] + /** + * 对后代组件生效的日期语言对象,为 null 时会使用默认 dateEnUS,为 undefined 时会继承上级 n-config-provider + */ + dateLocale?: ConfigProviderProps['dateLocale'] + /** + * 是否禁用 inline css 主题变量,如果你不会频繁调整主题变量,并且需要 SSR 或者想让 devtools 看起来更干净,可以打开这个选项。注意,这个属性不是响应式的 + */ + inlineThemeDisabled?: ConfigProviderProps['inlineThemeDisabled'] + /** + * 公式组件需要的 katex 对象 + */ + katex?: ConfigProviderProps['katex'] + /** + * 对后代组件生效的语言对象,为 null 时会使用默认 enUS,为 undefined 时会继承上级 n-config-provider + */ + locale?: ConfigProviderProps['locale'] + /** + * n-config-provider 内部组件被卸载于其他位置的 DOM 的类名 + */ + namespace?: ConfigProviderProps['namespace'] + /** + * 是否禁用默认样式,如果你禁用了它,便可以完全控制全局样式。你也可以使用 n-global-style 去挂载全局样式(推荐,样式是响应式的) + */ + preflightStyleDisabled?: ConfigProviderProps['preflightStyleDisabled'] + /** + * n-config-provider 被渲染成的元素 + */ + tag?: ConfigProviderProps['tag'] + /** + * 主题模式 + */ + themeMode?: UseColorModeOptions['initialValue'] + /** + * Naive UI 主题覆盖 + */ + themeOverrides?: MaybeRefOrGetter + /** + * Naive UI 明亮主题覆盖 + */ + lightThemeOverrides?: MaybeRefOrGetter + /** + * Naive UI 暗黑主题覆盖 + */ + darkThemeOverrides?: MaybeRefOrGetter +} + +export interface UseNaiveConfigProviderReturn extends ConfigProviderProps {} + +export function useNaiveConfigProvider(options: UseNaiveConfigProviderOptions = {}): UseNaiveConfigProviderReturn { + const { + dateLocale = dateZhCN, + locale = zhCN, + themeMode, + themeOverrides: initialThemeOverrides = {}, + lightThemeOverrides: initialLightThemeOverrides = {}, + darkThemeOverrides: initialDarkThemeOverrides = {}, + ...opts + } = options + + const mode = useColorMode({ initialValue: themeMode }) + const isDark = computed(() => mode.value === 'dark') + const theme = computed(() => isDark.value ? darkTheme : null) + + const currentThemeOverrides = computed(() => { + const themeOverrides = isDark.value ? toValue(initialDarkThemeOverrides) : toValue(initialLightThemeOverrides) + return merge(themeOverrides, toValue(initialThemeOverrides)) + }) + const themeOverrides = computed(() => { + if (!currentThemeOverrides.value) + return {} + + const commonColor = getCommonColor(currentThemeOverrides.value.common, isDark.value) + return { + ...currentThemeOverrides.value, + common: { + ...currentThemeOverrides.value.common, + ...commonColor, + }, + } + }) + + function getGenerateColors(color: string, isDark: boolean): string[] { + return isDark + ? generateColorPalettes(color, true, commonDark.bodyColor) + : generateColorPalettes(color) + } + + function getCommonColor(commonColor: GlobalThemeOverrides['common'], isDark: boolean) { + const result: any = {} + const keys = Object.keys(commonColor!) as ColorType[] + + const colorActions: ColorAction[] = [ + { + scene: '', + handler: color => color, + }, + { + scene: 'Hover', + handler: color => getGenerateColors(color, isDark)[4], + }, + { + scene: 'Suppl', + handler: color => getGenerateColors(color, isDark)[4], + }, + { + scene: 'Pressed', + handler: color => getGenerateColors(color, isDark)[6], + }, + ] + keys.forEach((key) => { + if (key.includes('Color')) { + colorActions.forEach((action) => { + const color = action.handler((commonColor as any)[key]) + const colorKey = key + action.scene + result[colorKey] = color + }) + } + }) + + return result + } + + return reactive({ + dateLocale, + locale, + theme, + themeOverrides, + ...opts, + }) +} diff --git a/src/composables/useNaiveApi.ts b/src/composables/useNaiveDiscreteApi.ts similarity index 58% rename from src/composables/useNaiveApi.ts rename to src/composables/useNaiveDiscreteApi.ts index 431f8e5..9787f89 100644 --- a/src/composables/useNaiveApi.ts +++ b/src/composables/useNaiveDiscreteApi.ts @@ -1,7 +1,9 @@ -import type { DialogApi, LoadingBarApi, MessageApi, NotificationApi } from 'naive-ui' +import type { ConfigProviderProps, DialogApi, LoadingBarApi, MessageApi, NotificationApi } from 'naive-ui' import { createDiscreteApi } from 'naive-ui' -export interface UseNaiveApiReturn { +export interface UseNaiveDiscreteApiOptions extends ConfigProviderProps {} + +export interface UseNaiveDiscreteApiReturn { /** * Naive UI 对话框 API */ @@ -23,25 +25,23 @@ export interface UseNaiveApiReturn { /** * Naive UI 脱离上下文的 API */ -export function useNaiveApi(): UseNaiveApiReturn { +export function useNaiveDiscreteApi(options: UseNaiveDiscreteApiOptions = {}): UseNaiveDiscreteApiReturn { const { setting } = useThemeSetting() - const { theme, themeOverrides } = useNaiveTheme({ - themeOverrides: computed(() => ({ - common: setting.value?.themeColor, + const configProviderProps = useNaiveConfigProvider({ + lightThemeOverrides: computed(() => ({ + common: setting.value?.lightThemeColor, })), darkThemeOverrides: computed(() => ({ common: setting.value?.darkThemeColor, })), + ...options, }) const { dialog, loadingBar, message, notification } = createDiscreteApi( ['message', 'dialog', 'notification', 'loadingBar'], { - configProviderProps: { - theme: theme.value, - themeOverrides: themeOverrides.value, - }, + configProviderProps, }, ) diff --git a/src/settings/theme.ts b/src/settings/theme.ts index a5d3054..72da0fd 100644 --- a/src/settings/theme.ts +++ b/src/settings/theme.ts @@ -1,6 +1,6 @@ export const themeSetting = { themeMode: 'auto', - themeColor: { + lightThemeColor: { primaryColor: '#18A058', infoColor: '#2080F0', successColor: '#18A058', @@ -24,6 +24,7 @@ export const themeSetting = { show: true, width: 240, collapsedWidth: 64, + collapsed: false, inverted: false, }, footer: {