refactor: customize theme (#368)

This commit is contained in:
Libin YANG 2024-08-29 19:43:06 +08:00 committed by GitHub
parent a744ecba68
commit fed75b307e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 52 additions and 104 deletions

View File

@ -1,37 +1,29 @@
interface Theme { interface Theme {
BASE: Record<string, string | number> base: Record<string, string | number>
block: Record<string, Record<string, string | number>> block: Record<string, Record<string, string | number>>
inline: Record<string, Record<string, string | number>> inline: Record<string, Record<string, string | number>>
} }
const baseColor = `#3f3f3f` const baseColor = `#3f3f3f`
function mergeTheme(defaultTheme: Theme, newTheme: Theme) { function mergeTheme(defaultTheme: Theme, newTheme: Theme): Theme {
const res: Theme = { const merge = (defaultObj: Record<string, any>, newObj: Record<string, any>) => {
BASE: { const result: Record<string, any> = {}
...defaultTheme.BASE, for (const key in defaultObj) {
...newTheme.BASE, result[key] = { ...defaultObj[key], ...newObj?.[key] }
},
block: {},
inline: {},
} }
for (const el in defaultTheme.block) { return result
res.block[el] = {
...defaultTheme.block[el],
...newTheme.block[el],
} }
return {
base: { ...defaultTheme.base, ...newTheme.base },
block: merge(defaultTheme.block, newTheme.block),
inline: merge(defaultTheme.inline, newTheme.inline),
} }
for (const el in defaultTheme.inline) {
res.inline[el] = {
...defaultTheme.inline[el],
...newTheme.inline[el],
}
}
return res
} }
const defaultTheme = { const defaultTheme = {
BASE: { base: {
'--md-primary-color': `#000000`, '--md-primary-color': `#000000`,
'text-align': `left`, 'text-align': `left`,
'line-height': `1.75`, 'line-height': `1.75`,
@ -225,7 +217,7 @@ const defaultTheme = {
} }
const graceTheme = mergeTheme(defaultTheme, { const graceTheme = mergeTheme(defaultTheme, {
BASE: { base: {
}, },
block: { block: {
h1: { h1: {

View File

@ -6,10 +6,10 @@ import { useDark, useStorage, useToggle } from '@vueuse/core'
import { ElMessage, ElMessageBox } from 'element-plus' import { ElMessage, ElMessageBox } from 'element-plus'
import { altKey, codeBlockThemeOptions, colorOptions, fontFamilyOptions, fontSizeOptions, legendOptions, shiftKey, themeMap, themeOptions } from '@/config' import { altKey, codeBlockThemeOptions, colorOptions, fontFamilyOptions, fontSizeOptions, legendOptions, shiftKey, themeMap, themeOptions } from '@/config'
import WxRenderer from '@/utils/wx-renderer' import WxRenderer from '@/utils/renderer'
import DEFAULT_CONTENT from '@/assets/example/markdown.md?raw' import DEFAULT_CONTENT from '@/assets/example/markdown.md?raw'
import DEFAULT_CSS_CONTENT from '@/assets/example/theme-css.txt?raw' import DEFAULT_CSS_CONTENT from '@/assets/example/theme-css.txt?raw'
import { addPrefix, css2json, customCssWithTemplate, downloadMD, exportHTML, formatCss, formatDoc, setColorWithCustomTemplate, setFontSizeWithTemplate, setTheme } from '@/utils' import { addPrefix, css2json, customCssWithTemplate, customizeTheme, downloadMD, exportHTML, formatCss, formatDoc } from '@/utils'
export const useStore = defineStore(`store`, () => { export const useStore = defineStore(`store`, () => {
// 是否开启深色模式 // 是否开启深色模式
@ -46,7 +46,7 @@ export const useStore = defineStore(`store`, () => {
const fontSizeNumber = fontSize.value.replace(`px`, ``) const fontSizeNumber = fontSize.value.replace(`px`, ``)
const wxRenderer = new WxRenderer({ const wxRenderer = new WxRenderer({
theme: setTheme(themeMap[theme.value], fontSizeNumber, fontColor.value, theme.value === `default`), theme: customizeTheme(themeMap[theme.value], { fontSize: fontSizeNumber, color: fontColor.value }),
fonts: fontFamily.value, fonts: fontFamily.value,
size: fontSize.value, size: fontSize.value,
}) })
@ -187,11 +187,9 @@ export const useStore = defineStore(`store`, () => {
// 更新 CSS // 更新 CSS
const updateCss = () => { const updateCss = () => {
const json = css2json(cssEditor.value.getValue()) const json = css2json(cssEditor.value.getValue())
let t = setTheme(themeMap[theme.value], fontSizeNumber, fontColor.value, theme.value === `default`) const newTheme = customCssWithTemplate(json, fontColor.value, customizeTheme(themeMap[theme.value], { fontSize: fontSizeNumber, color: fontColor.value }))
t = customCssWithTemplate(json, fontColor.value, t)
wxRenderer.setOptions({ wxRenderer.setOptions({
theme: t, theme: newTheme,
}) })
editorRefresh() editorRefresh()
} }
@ -270,15 +268,15 @@ export const useStore = defineStore(`store`, () => {
} }
const getTheme = (size, color) => { const getTheme = (size, color) => {
const t = setFontSizeWithTemplate(themeMap[theme.value])(size.replace(`px`, ``), theme.value === `default`) const newTheme = themeMap[theme.value]
return setColorWithCustomTemplate(t, color, theme.value === `default`) const fontSize = size.replace(`px`, ``)
return customizeTheme(newTheme, { fontSize, color })
} }
const themeChanged = withAfterRefresh((newTheme) => { const themeChanged = withAfterRefresh((newTheme) => {
wxRenderer.setOptions({ wxRenderer.setOptions({
theme: setTheme(themeMap[newTheme], fontSizeNumber, fontColor.value, newTheme === `default`), theme: customizeTheme(themeMap[newTheme], { fontSize: fontSizeNumber, color: fontColor.value }),
}) })
theme.value = newTheme theme.value = newTheme
}) })

View File

@ -9,86 +9,44 @@ export function addPrefix(str) {
return `${prefix}__${str}` return `${prefix}__${str}`
} }
// 设置自定义颜色 export function customizeTheme(theme, options) {
function createCustomTheme(theme, color) { const newTheme = JSON.parse(JSON.stringify(theme))
const customTheme = JSON.parse(JSON.stringify(theme)) const { fontSize, color } = options
customTheme.BASE[`--md-primary-color`] = color if (fontSize) {
return customTheme
}
export function setColorWithCustomTemplate(theme, color, isDefault = true) {
return createCustomTheme(theme, color, isDefault)
}
// 设置自定义字体大小
export function setFontSizeWithTemplate(template) {
return function (fontSize) {
const customTheme = JSON.parse(JSON.stringify(template))
for (let i = 1; i <= 4; i++) { for (let i = 1; i <= 4; i++) {
const v = customTheme.block[`h${i}`][`font-size`] const v = newTheme.block[`h${i}`][`font-size`]
customTheme.block[`h${i}`][`font-size`] = `${fontSize * Number.parseFloat(v)}px` newTheme.block[`h${i}`][`font-size`] = `${fontSize * Number.parseFloat(v)}px`
} }
return customTheme
} }
} if (color) {
newTheme.base[`--md-primary-color`] = color
export function setTheme(theme, fontSize, color, isDefault) { }
return setColorWithCustomTemplate(setFontSizeWithTemplate(theme)(fontSize, isDefault), color, isDefault) return newTheme
} }
export function customCssWithTemplate(jsonString, color, theme) { export function customCssWithTemplate(jsonString, color, theme) {
// block const newTheme = customizeTheme(theme, { color });
const customTheme = createCustomTheme(theme, color)
customTheme.block.h1 = Object.assign(customTheme.block.h1, jsonString.h1) const mergeProperties = (target, source, keys) => {
customTheme.block.h2 = Object.assign(customTheme.block.h2, jsonString.h2) keys.forEach(key => {
customTheme.block.h3 = Object.assign(customTheme.block.h3, jsonString.h3) if (source[key]) {
customTheme.block.h4 = Object.assign(customTheme.block.h4, jsonString.h4) target[key] = Object.assign(target[key] || {}, source[key]);
customTheme.block.code = Object.assign( }
customTheme.block.code, });
jsonString.code, };
)
customTheme.block.p = Object.assign(customTheme.block.p, jsonString.p)
customTheme.block.hr = Object.assign(customTheme.block.hr, jsonString.hr)
customTheme.block.blockquote = Object.assign(
customTheme.block.blockquote,
jsonString.blockquote,
)
customTheme.block.blockquote_p = Object.assign(
customTheme.block.blockquote_p,
jsonString.blockquote_p,
)
customTheme.block.image = Object.assign(
customTheme.block.image,
jsonString.image,
)
// inline const blockKeys = [
customTheme.inline.strong = Object.assign( 'h1', 'h2', 'h3', 'h4', 'code', 'p', 'hr', 'blockquote',
customTheme.inline.strong, 'blockquote_p', 'image', 'ul', 'ol'
jsonString.strong, ];
) const inlineKeys = ['strong', 'codespan', 'link', 'wx_link', 'listitem'];
customTheme.inline.codespan = Object.assign(
customTheme.inline.codespan, mergeProperties(newTheme.block, jsonString, blockKeys);
jsonString.codespan, mergeProperties(newTheme.inline, jsonString, inlineKeys);
) return newTheme;
customTheme.inline.link = Object.assign(
customTheme.inline.link,
jsonString.link,
)
customTheme.inline.wx_link = Object.assign(
customTheme.inline.wx_link,
jsonString.wx_link,
)
customTheme.block.ul = Object.assign(customTheme.block.ul, jsonString.ul)
customTheme.block.ol = Object.assign(customTheme.block.ol, jsonString.ol)
customTheme.inline.listitem = Object.assign(
customTheme.inline.listitem,
jsonString.li,
)
return customTheme
} }
/** /**
* CSS 字符串转换为 JSON 对象 * CSS 字符串转换为 JSON 对象
* *

View File

@ -28,7 +28,7 @@ class WxRenderer extends Renderer {
merge = (base, extend) => ({ ...base, ...extend }) merge = (base, extend) => ({ ...base, ...extend })
buildTheme = (themeTpl) => { buildTheme = (themeTpl) => {
const base = this.merge(themeTpl.BASE, { const base = this.merge(themeTpl.base, {
'font-family': this.opts.fonts, 'font-family': this.opts.fonts,
'font-size': this.opts.size, 'font-size': this.opts.size,
}) })