128 lines
3.6 KiB
TypeScript
128 lines
3.6 KiB
TypeScript
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} `
|
||
} |