staff_data/packages/mindmap/src/utils/Theme.ts

128 lines
3.6 KiB
TypeScript
Raw Normal View History

2025-01-13 07:52:51 +08:00
import MersenneTwister from "./mersenneTwister"
import { ThemeConfig } from "../theme"
import { richTextSupportStyleList } from "../constants/constant"
import merge from 'deepmerge'
// 判断一个颜色是否是白色
export const isWhite = (color: string): boolean => {
color = String(color).replaceAll(/\s+/g, '')
return (
['#fff', '#ffffff', '#FFF', '#FFFFFF', 'rgb(255,255,255)'].includes(
color
) || /rgba\(255,255,255,[^)]+\)/.test(color)
)
}
// 判断一个颜色是否是透明
export const isTransparent = (color: string): boolean => {
color = String(color).replaceAll(/\s+/g, '')
return (
['', 'transparent'].includes(color) || /rgba\(\d+,\d+,\d+,0\)/.test(color)
)
}
// 从当前主题里获取一个非透明非白色的颜色
export const getVisibleColorFromTheme = (themeConfig: ThemeConfig): string | undefined => {
const { lineColor, root, second, node } = themeConfig
const list = [
lineColor,
root.fillColor,
root.color,
second.fillColor,
second.color,
node.fillColor,
node.color,
root.borderColor,
second.borderColor,
node.borderColor
]
for (let i = 0; i < list.length; i++) {
const color = list[i]
if (!isTransparent(color) && !isWhite(color)) {
return color
}
}
return undefined
}
// 根据内容生成颜色
export const generateColorByContent = (str: string): string => {
let hash = 0
for (let i = 0; i < str.length; i++) {
hash = str.charCodeAt(i) + ((hash << 5) - hash)
}
// 这里使用伪随机数的原因是因为
// 1. 如果字符串的内容差不多根据hash生产的颜色就比较相近不好区分比如v1.1 v1.2,所以需要加入随机数来使得颜色能够区分开
// 2. 普通的随机数每次数值不一样,就会导致每次新增标签原来的标签颜色就会发生改变,所以加入了这个方法,使得内容不变随机数也不变
const rng = new MersenneTwister(hash)
const h = rng.genrand_int32() % 360
return 'hsla(' + h + ', 50%, 50%, 1)'
}
interface IconItem {
type: string
list: Array<{
name: string
icon: string
}>
}
export const mergerIconList = (list: IconItem[]): IconItem[] => {
return list.reduce((result: IconItem[], item) => {
const existingItem = result.find(x => x.type === item.type)
if (existingItem) {
item.list.forEach(newObj => {
const existingObj = existingItem.list.find(x => x.name === newObj.name)
if (existingObj) {
existingObj.icon = newObj.icon
} else {
existingItem.list.push(newObj)
}
})
} else {
result.push({ ...item })
}
return result
}, [])
}
// 合并主题配置
export const mergeTheme = <T extends object>(dest: T, source: T): T => {
return merge(dest, source, {
arrayMerge: (_destinationArray: any[], sourceArray: any[]) => sourceArray
})
}
interface NodeStyle {
merge: (prop: string) => string | number
}
interface Node {
style: NodeStyle
}
// 获取节点实例的文本样式数据
export const getNodeRichTextStyles = (node: Node): Record<string, string> => {
const res: Record<string, string> = {}
richTextSupportStyleList.forEach(prop => {
let value = node.style.merge(prop)
if (prop === 'fontSize') {
value = value + 'px'
}
res[prop] = String(value)
})
return res
}
export interface FontOptions {
italic?: boolean;
bold?: boolean;
fontSize: number;
fontFamily: string;
}
// 拼接font字符串
export const joinFontStr = ({ italic, bold, fontSize, fontFamily }: FontOptions): string => {
return `${italic ? 'italic ' : ''} ${bold ? 'bold ' : ''
} ${fontSize}px ${fontFamily} `
}