import type { CSSProperties } from 'react'; import type { ColorValue } from 'react-native'; import type { HtmlStyle, MentionStyleProperties } from '../../types'; import { DEFAULT_HTML_STYLE } from '../../utils/defaultHtmlStyle'; import { expandMentionStylesForIndicators } from '../../utils/expandMentionStylesForIndicators'; import { HEADING_TAGS } from '../formats/EnrichedHeading'; import { indicatorToMentionCssKey, MENTION_STYLE_DEFAULT_KEY, } from './mentionIndicatorCssKey'; import { toColor } from './toColor'; import { isMentionStyleRecord } from '../../utils/isMentionStyleRecord'; export function mergeWithDefaultHtmlStyle( htmlStyle?: HtmlStyle ): Required { const style = htmlStyle ?? {}; const mentionMap = expandMentionStylesForIndicatorsIncludeDefault(style); const converted: HtmlStyle = { ...style, mention: mentionMap, }; const merged: Record = { ...DEFAULT_HTML_STYLE }; for (const key in converted) { if (key === 'mention') { merged[key] = { ...(converted.mention as object) }; continue; } merged[key] = { ...DEFAULT_HTML_STYLE[key as keyof HtmlStyle], ...(converted[key as keyof HtmlStyle] as object), }; } return merged as Required; } const ETI_CSS_VARS = { codeColor: '--eti-code-color', codeBgColor: '--eti-code-bg-color', blockquoteBorderColor: '--eti-blockquote-border-color', blockquoteBorderWidth: '--eti-blockquote-border-width', blockquoteGapWidth: '--eti-blockquote-gap-width', blockquoteColor: '--eti-blockquote-color', codeblockBgColor: '--eti-codeblock-bg-color', codeblockColor: '--eti-codeblock-color', codeblockBorderRadius: '--eti-codeblock-border-radius', linkColor: '--eti-link-color', linkTextDecorationLine: '--eti-link-text-decoration-line', ulBulletColor: '--eti-ul-bullet-color', ulBulletSize: '--eti-ul-bullet-size', ulMarginLeft: '--eti-ul-margin-left', ulGapWidth: '--eti-ul-gap-width', olMarginLeft: '--eti-ol-margin-left', olGapWidth: '--eti-ol-gap-width', olMarkerColor: '--eti-ol-marker-color', olMarkerFontWeight: '--eti-ol-marker-font-weight', checkboxBoxSize: '--eti-checkbox-box-size', checkboxGapWidth: '--eti-checkbox-gap-width', checkboxMarginLeft: '--eti-checkbox-margin-left', checkboxBoxColor: '--eti-checkbox-box-color', } as const; export const ETI_MENTION_CSS_VARS = { color: (indicator: string) => `--eti-mention-${indicatorToMentionCssKey(indicator)}-color`, backgroundColor: (indicator: string) => `--eti-mention-${indicatorToMentionCssKey(indicator)}-background-color`, textDecorationLine: (indicator: string) => `--eti-mention-${indicatorToMentionCssKey(indicator)}-text-decoration-line`, } as const; function setColorVar( vars: Record, name: string, value?: ColorValue ): void { const c = toColor(value); if (c) vars[name] = c; } function setPxVar( vars: Record, name: string, n?: number | null ): void { if (n != null) vars[name] = `${n}px`; } function applyCodeVars( vars: Record, code?: HtmlStyle['code'] ): void { setColorVar(vars, ETI_CSS_VARS.codeColor, code?.color); setColorVar(vars, ETI_CSS_VARS.codeBgColor, code?.backgroundColor); } function applyHeadingVars( vars: Record, htmlStyle?: HtmlStyle ): void { for (const level of HEADING_TAGS) { const h = htmlStyle?.[level]; if (h?.fontSize != null) vars[`--eti-${level}-font-size`] = `${h.fontSize}px`; if (h?.bold != null) vars[`--eti-${level}-font-weight`] = h.bold ? 'bold' : 'normal'; } } function applyBlockquoteVars( vars: Record, bq?: HtmlStyle['blockquote'] ): void { setColorVar(vars, ETI_CSS_VARS.blockquoteBorderColor, bq?.borderColor); setPxVar(vars, ETI_CSS_VARS.blockquoteBorderWidth, bq?.borderWidth); setPxVar(vars, ETI_CSS_VARS.blockquoteGapWidth, bq?.gapWidth); setColorVar(vars, ETI_CSS_VARS.blockquoteColor, bq?.color); } function applyCodeblockVars( vars: Record, cb?: HtmlStyle['codeblock'] ): void { setColorVar(vars, ETI_CSS_VARS.codeblockBgColor, cb?.backgroundColor); setColorVar(vars, ETI_CSS_VARS.codeblockColor, cb?.color); setPxVar(vars, ETI_CSS_VARS.codeblockBorderRadius, cb?.borderRadius); } function applyLinkVars( vars: Record, anchor?: HtmlStyle['a'] ): void { setColorVar(vars, ETI_CSS_VARS.linkColor, anchor?.color); if (anchor?.textDecorationLine != null) { vars[ETI_CSS_VARS.linkTextDecorationLine] = anchor.textDecorationLine; } } function applyUnorderedListVars( vars: Record, ul?: HtmlStyle['ul'] ): void { setColorVar(vars, ETI_CSS_VARS.ulBulletColor, ul?.bulletColor); setPxVar(vars, ETI_CSS_VARS.ulBulletSize, ul?.bulletSize); setPxVar(vars, ETI_CSS_VARS.ulMarginLeft, ul?.marginLeft); setPxVar(vars, ETI_CSS_VARS.ulGapWidth, ul?.gapWidth); } function applyOrderedListVars( vars: Record, ol?: HtmlStyle['ol'] ): void { setPxVar(vars, ETI_CSS_VARS.olMarginLeft, ol?.marginLeft); setPxVar(vars, ETI_CSS_VARS.olGapWidth, ol?.gapWidth); setColorVar(vars, ETI_CSS_VARS.olMarkerColor, ol?.markerColor); if (ol?.markerFontWeight != null) { vars[ETI_CSS_VARS.olMarkerFontWeight] = String(ol.markerFontWeight); } } function applyCheckboxListVars( vars: Record, ulCheckbox?: HtmlStyle['ulCheckbox'] ): void { setPxVar(vars, ETI_CSS_VARS.checkboxBoxSize, ulCheckbox?.boxSize); setPxVar(vars, ETI_CSS_VARS.checkboxGapWidth, ulCheckbox?.gapWidth); setPxVar(vars, ETI_CSS_VARS.checkboxMarginLeft, ulCheckbox?.marginLeft); setColorVar(vars, ETI_CSS_VARS.checkboxBoxColor, ulCheckbox?.boxColor); } function applyMentionVars( vars: Record, mention: Record ): void { for (const [indicator, mentionStyle] of Object.entries(mention)) { setColorVar( vars, ETI_MENTION_CSS_VARS.color(indicator), mentionStyle.color ); setColorVar( vars, ETI_MENTION_CSS_VARS.backgroundColor(indicator), mentionStyle.backgroundColor ); if (mentionStyle.textDecorationLine != null) { vars[ETI_MENTION_CSS_VARS.textDecorationLine(indicator)] = mentionStyle.textDecorationLine; } } } function expandMentionStylesForIndicatorsIncludeDefault(htmlStyle?: HtmlStyle) { const mentionIndicators = isMentionStyleRecord(htmlStyle?.mention) ? Object.keys(htmlStyle?.mention) : []; if (!mentionIndicators.includes(MENTION_STYLE_DEFAULT_KEY)) mentionIndicators.push(MENTION_STYLE_DEFAULT_KEY); return expandMentionStylesForIndicators( htmlStyle?.mention, mentionIndicators ); } export function htmlStyleToCSSVariables(htmlStyle?: HtmlStyle): CSSProperties { const vars: Record = {}; applyCodeVars(vars, htmlStyle?.code); applyHeadingVars(vars, htmlStyle); applyBlockquoteVars(vars, htmlStyle?.blockquote); applyCodeblockVars(vars, htmlStyle?.codeblock); applyLinkVars(vars, htmlStyle?.a); applyUnorderedListVars(vars, htmlStyle?.ul); applyOrderedListVars(vars, htmlStyle?.ol); applyCheckboxListVars(vars, htmlStyle?.ulCheckbox); applyMentionVars( vars, expandMentionStylesForIndicatorsIncludeDefault(htmlStyle) ); return vars as CSSProperties; }