import { lowerFirst, upperFirst } from '@antv/util'; import type { ReplacePrefix } from '../types'; /** * 是否以某个前缀开头 * * Whether starts with prefix * @param str - 字符串 | string * @param prefix - 前缀 | prefix * @returns 是否以某个前缀开头 | whether starts with prefix */ export function startsWith(str: string, prefix: string) { if (!str.startsWith(prefix)) return false; const nextChart = str[prefix.length]; return nextChart >= 'A' && nextChart <= 'Z'; } /** * 添加前缀 * * Add prefix * @param str - 字符串 | string * @param prefix - 前缀 | prefix * @returns 添加前缀后的字符串 | string with prefix */ export function addPrefix(str: string, prefix: string): string { return `${prefix}${upperFirst(str)}`; } /** * 移除前缀 * * Remove prefix * @param string - 字符串 | string * @param prefix - 前缀 | prefix * @param lowercaseFirstLetter - 是否小写首字母 | whether lowercase first letter * @returns 移除前缀后的字符串 | string without prefix */ export function removePrefix(string: string, prefix?: string, lowercaseFirstLetter: boolean = true) { if (!prefix) return string; if (!startsWith(string, prefix)) return string; const str = string.slice(prefix.length); return lowercaseFirstLetter ? lowerFirst(str) : str; } /** * 从样式中提取子样式 * * Extract sub style from style * @param style - 样式 | style * @param prefix - 子样式前缀 | sub style prefix * @returns 子样式 | sub style */ export function subStyleProps>(style: object, prefix: string) { const subStyle = Object.entries(style).reduce((acc, [key, value]) => { if (key === 'className' || key === 'class') return acc; if (startsWith(key, prefix)) { Object.assign(acc, { [removePrefix(key, prefix)]: value }); } return acc; }, {} as T); // 向下传递透明度,但避免覆盖子样式中的透明度属性 // Pass down opacity, but avoid overwriting the opacity property in the sub-style if ('opacity' in style) { const subOpacityKey = addPrefix('opacity', prefix) as keyof typeof style; const opacity = style.opacity as number; if (subOpacityKey in style) { const subOpacity = style[subOpacityKey] as number; Object.assign(subStyle, { opacity: opacity * subOpacity }); } else Object.assign(subStyle, { opacity }); } return subStyle; } /** * 从对象中提取指定前缀的属性,并移除前缀 * * Extract properties with the specified prefix from the object and remove the prefix * @param obj - 对象 | object * @param prefix - 前缀 | prefix * @returns 新对象 | new object */ export function subObject(obj: Record, prefix: string): Record { const prefixLength = prefix.length; return Object.keys(obj).reduce( (acc, key) => { if (key.startsWith(prefix)) { const newKey = key.slice(prefixLength); acc[newKey] = obj[key]; } return acc; }, {} as Record, ); } /** * 从样式中排除子样式 * * Omit sub style from style * @param style - 样式 | style * @param prefix - 子样式前缀 | sub style prefix * @returns 排除子样式后的样式 | style without sub style */ export function omitStyleProps>(style: Record, prefix: string | string[]) { const prefixArray = typeof prefix === 'string' ? [prefix] : prefix; const omitStyle: Record = {}; Object.keys(style).forEach((key) => { if (!prefixArray.find((p) => key.startsWith(p))) { omitStyle[key] = style[key]; } }); return omitStyle as T; } /** * 替换前缀 * * Replace prefix * @param style - 样式 | style * @param oldPrefix - 旧前缀 | old prefix * @param newPrefix - 新前缀 | new prefix * @returns 替换前缀后的样式 | style with replaced prefix */ export function replacePrefix(style: T, oldPrefix: string, newPrefix: string) { return Object.entries(style).reduce( (acc, [key, value]) => { if (startsWith(key, oldPrefix)) { acc[addPrefix(removePrefix(key, oldPrefix, false), newPrefix) as keyof typeof acc] = value; } else { acc[key as keyof typeof acc] = value; } return acc; }, {} as ReplacePrefix, ); }