refactor(dom-css): use CSS acronym in identifiers

This commit is contained in:
Danila Poyarkov 2026-06-02 15:35:32 +03:00
parent 107b52d473
commit c613df838c
10 changed files with 54 additions and 54 deletions

View file

@ -6,7 +6,7 @@ import type { DesignStyleDeclaration } from './types'
const TRANSPARENT_KEYWORDS = new Set(['transparent', 'rgba(0, 0, 0, 0)', 'rgb(0 0 0 / 0)'])
export function parseCssNumber(value: string | undefined): number | null {
export function parseCSSNumber(value: string | undefined): number | null {
if (!value) return null
const trimmed = value.trim()
if (trimmed.length === 0 || trimmed === 'auto') return null
@ -14,20 +14,20 @@ export function parseCssNumber(value: string | undefined): number | null {
return Number.isFinite(parsed) ? parsed : null
}
export function parseCssColor(value: string | undefined): Color | null {
export function parseCSSColor(value: string | undefined): Color | null {
if (!value) return null
const trimmed = value.trim()
if (trimmed.length === 0 || TRANSPARENT_KEYWORDS.has(trimmed.toLowerCase())) return null
return parseColor(trimmed)
}
export function fillToCss(fill: Fill | undefined): string | undefined {
export function fillToCSS(fill: Fill | undefined): string | undefined {
if (fill?.type !== 'SOLID' || !fill.visible) return undefined
return colorToCSS({ ...fill.color, a: fill.opacity })
}
export function cssColorToFill(value: string | undefined): Fill[] {
const color = parseCssColor(value)
export function colorToFillFromCSS(value: string | undefined): Fill[] {
const color = parseCSSColor(value)
if (!color) return []
return [{ type: 'SOLID', color, opacity: color.a, visible: true }]
}

View file

@ -2,7 +2,7 @@ import { colorToCSS } from '@open-pencil/core/color'
import { BLACK } from '@open-pencil/core/constants'
import type { SceneGraph, SceneNode } from '@open-pencil/core/scene-graph'
import { fillToCss, sceneNodeSizeStyle } from './css-values'
import { fillToCSS, sceneNodeSizeStyle } from './css-values'
import type { DesignDocument, DesignNode, DesignStyleDeclaration } from './types'
export interface SceneGraphToDesignOptions {
@ -18,7 +18,7 @@ function nodeChildren(graph: SceneGraph, node: SceneNode): SceneNode[] {
function styleFromSceneNode(node: SceneNode): DesignStyleDeclaration {
const style = sceneNodeSizeStyle(node)
const fill = fillToCss(node.fills[0])
const fill = fillToCSS(node.fills[0])
if (fill) style['background-color'] = fill
if (node.opacity < 1) style.opacity = String(node.opacity)
if (node.cornerRadius > 0) style['border-radius'] = `${node.cornerRadius}px`
@ -38,7 +38,7 @@ function styleFromSceneNode(node: SceneNode): DesignStyleDeclaration {
function styleFromTextNode(node: SceneNode): DesignStyleDeclaration {
const style = sceneNodeSizeStyle(node)
style.color = fillToCss(node.fills[0]) ?? colorToCSS(BLACK)
style.color = fillToCSS(node.fills[0]) ?? colorToCSS(BLACK)
style['font-family'] = node.fontFamily
style['font-size'] = `${node.fontSize}px`
style['font-weight'] = String(node.fontWeight)

View file

@ -2,7 +2,7 @@ import { parse, type CSSStyleDeclarationLike, type CSSStyleRuleLike } from '@ace
import type { DesignDocument, DesignElement, DesignNode, DesignStyleDeclaration } from './types'
interface CssRule {
interface HeadlessCSSRule {
selectors: string[]
style: DesignStyleDeclaration
}
@ -26,7 +26,7 @@ function isStyleRule(rule: unknown): rule is CSSStyleRuleLike {
)
}
function parseRules(cssText: string): CssRule[] {
function parseRules(cssText: string): HeadlessCSSRule[] {
const sheet = parse(cssText)
return sheet.cssRules.filter(isStyleRule).map((rule) => ({
selectors: rule.selectorText
@ -49,11 +49,11 @@ function matchesSimpleSelector(element: DesignElement, selector: string): boolea
return element.tagName.toLowerCase() === selector.toLowerCase()
}
function matchesRule(element: DesignElement, rule: CssRule): boolean {
function matchesRule(element: DesignElement, rule: HeadlessCSSRule): boolean {
return rule.selectors.some((selector) => matchesSimpleSelector(element, selector))
}
function applyComputedStyles(node: DesignNode, rules: CssRule[]): DesignNode {
function applyComputedStyles(node: DesignNode, rules: HeadlessCSSRule[]): DesignNode {
if (node.type === 'text') return node
const computedStyle: DesignStyleDeclaration = {}

View file

@ -1,12 +1,12 @@
export { serializeHTML, serializeNode } from './serialize'
export { createBrowserCssRuntime, createCssRuntime, createHeadlessCssRuntime } from './runtime'
export { createBrowserCSSRuntime, createCSSRuntime, createHeadlessCSSRuntime } from './runtime'
export { designDocumentToSceneGraph } from './to-scene-graph'
export { sceneGraphToDesignDocument } from './from-scene-graph'
export type { ToDesignDocumentOptions } from './from-scene-graph'
export type { ToSceneGraphOptions } from './to-scene-graph'
export type {
CssComputeOptions,
CssRuntime,
CSSComputeOptions,
CSSRuntime,
DesignDocument,
DesignElement,
DesignNode,

View file

@ -1,7 +1,7 @@
import { serializeHTML } from '../serialize'
import type {
CssComputeOptions,
CssRuntime,
CSSComputeOptions,
CSSRuntime,
DesignDocument,
DesignElement,
DesignNode
@ -125,7 +125,7 @@ function collectElementPairs(
function computedStyleToRecord(
style: CSSStyleDeclaration,
options: CssComputeOptions
options: CSSComputeOptions
): Record<string, string> {
const entries: Record<string, string> = {}
const properties = options.includeBrowserDefaults
@ -143,7 +143,7 @@ function computedStyleToRecord(
async function computeStyles(
designDocument: DesignDocument,
cssText = '',
options: CssComputeOptions = {}
options: CSSComputeOptions = {}
): Promise<DesignDocument> {
const browserDocument = requireBrowserDocument()
const host = browserDocument.createElement('div')
@ -191,7 +191,7 @@ async function computeStyles(
}
}
export function createBrowserCssRuntime(): CssRuntime {
export function createBrowserCSSRuntime(): CSSRuntime {
return {
kind: 'browser',
parseHTML,

View file

@ -3,7 +3,7 @@ import { parseFragment, type DefaultTreeAdapterTypes } from 'parse5'
import { computeHeadlessStyles } from '../headless-css'
import { serializeHTML } from '../serialize'
import { parseStyleAttribute } from '../style-attribute'
import type { CssRuntime, DesignDocument, DesignElement, DesignNode } from '../types'
import type { CSSRuntime, DesignDocument, DesignElement, DesignNode } from '../types'
function attrsToRecord(attrs: DefaultTreeAdapterTypes.Element['attrs']): Record<string, string> {
const result: Record<string, string> = {}
@ -50,7 +50,7 @@ function parseHTML(html: string): DesignDocument {
}
}
export function createHeadlessCssRuntime(): CssRuntime {
export function createHeadlessCSSRuntime(): CSSRuntime {
return {
kind: 'headless',
parseHTML,

View file

@ -1,10 +1,10 @@
import type { CssRuntime } from '../types'
import { createBrowserCssRuntime } from './browser'
import { createHeadlessCssRuntime } from './headless'
import type { CSSRuntime } from '../types'
import { createBrowserCSSRuntime } from './browser'
import { createHeadlessCSSRuntime } from './headless'
export { createBrowserCssRuntime } from './browser'
export { createHeadlessCssRuntime } from './headless'
export { createBrowserCSSRuntime } from './browser'
export { createHeadlessCSSRuntime } from './headless'
export function createCssRuntime(): CssRuntime {
return typeof document !== 'undefined' ? createBrowserCssRuntime() : createHeadlessCssRuntime()
export function createCSSRuntime(): CSSRuntime {
return typeof document !== 'undefined' ? createBrowserCSSRuntime() : createHeadlessCSSRuntime()
}

View file

@ -1,6 +1,6 @@
import { SceneGraph, type Fill, type SceneNode } from '@open-pencil/core/scene-graph'
import { cssColorToFill, mergedStyle, parseCssNumber, pickStyle } from './css-values'
import { colorToFillFromCSS, mergedStyle, parseCSSNumber, pickStyle } from './css-values'
import type { DesignDocument, DesignElement, DesignNode, DesignStyleDeclaration } from './types'
export interface DesignDocumentToSceneGraphOptions {
@ -30,21 +30,21 @@ function isTextLikeElement(node: DesignElement): boolean {
].includes(node.tagName.toLowerCase())
}
function firstCssNumber(style: DesignStyleDeclaration, ...properties: string[]): number | null {
function firstCSSNumber(style: DesignStyleDeclaration, ...properties: string[]): number | null {
for (const property of properties) {
const parsed = parseCssNumber(pickStyle(style, property))
const parsed = parseCSSNumber(pickStyle(style, property))
if (parsed !== null) return parsed
}
return null
}
function fillsFromStyle(style: DesignStyleDeclaration, property: string): Fill[] {
return cssColorToFill(pickStyle(style, property))
return colorToFillFromCSS(pickStyle(style, property))
}
function setNodeBox(node: SceneNode, style: DesignStyleDeclaration): void {
const width = firstCssNumber(style, 'width')
const height = firstCssNumber(style, 'height')
const width = firstCSSNumber(style, 'width')
const height = firstCSSNumber(style, 'height')
if (width !== null) node.width = width
if (height !== null) node.height = height
}
@ -55,19 +55,19 @@ function applyElementStyle(node: SceneNode, style: DesignStyleDeclaration): void
const fills = fillsFromStyle(style, 'background-color')
if (fills.length > 0) node.fills = fills
const opacity = parseCssNumber(pickStyle(style, 'opacity'))
const opacity = parseCSSNumber(pickStyle(style, 'opacity'))
if (opacity !== null) node.opacity = opacity
const cornerRadius = firstCssNumber(style, 'border-radius')
const cornerRadius = firstCSSNumber(style, 'border-radius')
if (cornerRadius !== null) node.cornerRadius = cornerRadius
if (pickStyle(style, 'display') === 'flex') {
node.layoutMode = pickStyle(style, 'flex-direction') === 'column' ? 'VERTICAL' : 'HORIZONTAL'
node.itemSpacing = firstCssNumber(style, 'gap', 'column-gap', 'row-gap') ?? 0
node.paddingTop = firstCssNumber(style, 'padding-top', 'padding') ?? 0
node.paddingRight = firstCssNumber(style, 'padding-right', 'padding') ?? 0
node.paddingBottom = firstCssNumber(style, 'padding-bottom', 'padding') ?? 0
node.paddingLeft = firstCssNumber(style, 'padding-left', 'padding') ?? 0
node.itemSpacing = firstCSSNumber(style, 'gap', 'column-gap', 'row-gap') ?? 0
node.paddingTop = firstCSSNumber(style, 'padding-top', 'padding') ?? 0
node.paddingRight = firstCSSNumber(style, 'padding-right', 'padding') ?? 0
node.paddingBottom = firstCSSNumber(style, 'padding-bottom', 'padding') ?? 0
node.paddingLeft = firstCSSNumber(style, 'padding-left', 'padding') ?? 0
}
}
@ -77,16 +77,16 @@ function applyTextStyle(node: SceneNode, style: DesignStyleDeclaration): void {
const fills = fillsFromStyle(style, 'color')
if (fills.length > 0) node.fills = fills
const fontSize = parseCssNumber(pickStyle(style, 'font-size'))
const fontSize = parseCSSNumber(pickStyle(style, 'font-size'))
if (fontSize !== null) node.fontSize = fontSize
const fontWeight = parseCssNumber(pickStyle(style, 'font-weight'))
const fontWeight = parseCSSNumber(pickStyle(style, 'font-weight'))
if (fontWeight !== null) node.fontWeight = fontWeight
const lineHeight = parseCssNumber(pickStyle(style, 'line-height'))
const lineHeight = parseCSSNumber(pickStyle(style, 'line-height'))
if (lineHeight !== null) node.lineHeight = lineHeight
const letterSpacing = parseCssNumber(pickStyle(style, 'letter-spacing'))
const letterSpacing = parseCSSNumber(pickStyle(style, 'letter-spacing'))
if (letterSpacing !== null) node.letterSpacing = letterSpacing
const fontFamily = pickStyle(style, 'font-family')

View file

@ -33,17 +33,17 @@ export interface DesignStyleSheet {
export type DesignStyleDeclaration = Record<string, string>
export interface CssComputeOptions {
export interface CSSComputeOptions {
includeBrowserDefaults?: boolean
}
export interface CssRuntime {
export interface CSSRuntime {
readonly kind: 'browser' | 'headless'
parseHTML(html: string): DesignDocument
serializeHTML(document: DesignDocument): string
computeStyles(
document: DesignDocument,
cssText?: string,
options?: CssComputeOptions
options?: CSSComputeOptions
): Promise<DesignDocument>
}

View file

@ -1,8 +1,8 @@
import { describe, expect, it } from 'bun:test'
import {
createCssRuntime,
createHeadlessCssRuntime,
createCSSRuntime,
createHeadlessCSSRuntime,
serializeHTML,
type DesignDocument
} from '@open-pencil/dom-css'
@ -27,7 +27,7 @@ describe('@open-pencil/dom-css', () => {
})
it('uses the headless runtime outside browser contexts', () => {
const runtime = createCssRuntime()
const runtime = createCSSRuntime()
expect(runtime.kind).toBe('headless')
expect(runtime.serializeHTML(documentFixture)).toBe(
@ -36,7 +36,7 @@ describe('@open-pencil/dom-css', () => {
})
it('parses HTML with the headless runtime', () => {
const runtime = createHeadlessCssRuntime()
const runtime = createHeadlessCSSRuntime()
const document = runtime.parseHTML(
'<section class="card" style="width: 320px; color: rgb(17, 24, 39)">OpenPencil</section>'
)
@ -52,7 +52,7 @@ describe('@open-pencil/dom-css', () => {
})
it('computes simple headless styles from CSSOM rules', async () => {
const runtime = createHeadlessCssRuntime()
const runtime = createHeadlessCSSRuntime()
const document = await runtime.computeStyles(
documentFixture,
'.card { width: 320px; color: rgb(17, 24, 39); } #missing { color: red; }'