import type { SemanticNodeKind } from "../types"; import { SEMANTIC_NODE_KINDS } from "./semantic"; export type PropertyDefinition = { category: | "flexbox" | "layout" | "dimension" | "color" | "text" | "image" | "spacing" | "border" | "transform" | "structural"; inheritable: boolean; appliesTo: readonly SemanticNodeKind[]; values: readonly string[]; units: readonly string[]; }; export type PropertyRegistry = Readonly>; export const SEMANTIC_CSS_CSS_WIDE_KEYWORDS_V1 = ["inherit", "initial", "revert", "unset"] as const; export const SEMANTIC_CSS_LENGTH_UNITS_V1 = ["pt", "px", "in", "mm", "cm", "%", "vw", "vh", "em", "rem"] as const; export const SEMANTIC_CSS_BORDER_STYLE_VALUES_V1 = ["dotted", "dashed", "solid"] as const; export const SEMANTIC_CSS_LENGTH_VALUE_KEYWORDS_V1 = [ "auto", "none", "normal", "max-content", "min-content", "fit-content", "thin", "medium", "thick", ] as const; export const SEMANTIC_CSS_LENGTH_PROPERTIES_V1 = [ "bottom", "border-bottom-left-radius", "border-bottom-right-radius", "border-bottom-width", "border-left-width", "border-radius", "border-right-width", "border-top-left-radius", "border-top-right-radius", "border-top-width", "border-width", "column-gap", "flex-basis", "font-size", "gap", "height", "left", "letter-spacing", "margin-bottom", "margin-left", "margin-right", "margin-top", "max-height", "max-width", "min-height", "min-width", "padding-bottom", "padding-left", "padding-right", "padding-top", "right", "row-gap", "text-indent", "top", "width", "-resume-min-presence-ahead", "-resume-shadow-width", ] as const; const containerNodes = [ "page", "region", "header", "contact-list", "contact-item", "section", "section-heading", "section-items", "item", "item-header", "rich-text", "list", "list-item", "horizontal-rule", "template-part", ] as const satisfies readonly SemanticNodeKind[]; const textNodes = [ "name", "headline", "section-heading", "combined-text", "field", "rich-heading", "blockquote", "paragraph", "list-item-content", "list-marker", "strong", "emphasis", "underline", "strike", "code", "text-span", "mark", "hard-break", ] as const satisfies readonly SemanticNodeKind[]; const linkContainerNodes = [...containerNodes, "link"] as SemanticNodeKind[]; const textAndLinkNodes = [...textNodes, "link"] as SemanticNodeKind[]; const colorNodes = [...containerNodes, ...textNodes, "link", "icon", "level"] as SemanticNodeKind[]; const spacingNodes = [...containerNodes, ...textNodes, "link", "picture"] as SemanticNodeKind[]; const structuralNodes = [...SEMANTIC_NODE_KINDS.filter((kind) => kind !== "resume")] as SemanticNodeKind[]; const lengthProperties = new Set(SEMANTIC_CSS_LENGTH_PROPERTIES_V1); const numericLengthUnits = SEMANTIC_CSS_LENGTH_UNITS_V1.filter((unit) => unit !== "%"); const borderStyleProperties = /^(?:border-style|border-(?:top|right|bottom|left)-style)$/; const borderShorthandProperties = /^(?:border|border-(?:top|right|bottom|left))$/; const propertyValueHints = { "align-content": ["flex-start", "flex-end", "center", "stretch", "space-between", "space-around", "space-evenly"], "align-items": ["flex-start", "flex-end", "center", "stretch", "baseline"], "align-self": ["auto", "flex-start", "flex-end", "center", "stretch", "baseline"], flex: ["none", "auto"], "flex-direction": ["row", "row-reverse", "column", "column-reverse"], "flex-wrap": ["nowrap", "wrap", "wrap-reverse"], "flex-flow": ["row", "row-reverse", "column", "column-reverse", "nowrap", "wrap", "wrap-reverse"], "justify-content": ["flex-start", "flex-end", "center", "space-between", "space-around", "space-evenly"], display: ["flex", "none"], position: ["absolute", "relative", "static"], overflow: ["hidden"], direction: ["ltr", "rtl"], "font-style": ["normal", "italic"], "font-weight": [ "thin", "hairline", "ultralight", "extralight", "light", "normal", "medium", "semibold", "demibold", "bold", "ultrabold", "extrabold", "heavy", "black", ], "line-height": ["normal"], "text-align": ["left", "right", "center", "justify"], "text-decoration": ["none", "underline", "line-through", "line-through underline", "underline line-through"], "text-overflow": ["ellipsis"], "text-transform": ["none", "capitalize", "lowercase", "uppercase", "upperfirst"], "vertical-align": ["sub", "super"], "object-fit": ["contain", "cover", "fill", "none", "scale-down"], "object-position": ["left", "right", "top", "bottom", "center"], "transform-origin": ["left", "right", "top", "bottom", "center"], "break-before": ["auto", "page"], "break-inside": ["auto", "avoid"], size: ["A4", "letter"], "-resume-fixed": ["true", "false", "0", "1"], } as const satisfies Readonly>; function propertyValues(name: string): readonly string[] { const values = borderStyleProperties.test(name) ? SEMANTIC_CSS_BORDER_STYLE_VALUES_V1 : borderShorthandProperties.test(name) ? SEMANTIC_CSS_BORDER_STYLE_VALUES_V1.map((style) => `1pt ${style}`) : (propertyValueHints[name as keyof typeof propertyValueHints] ?? []); return [...SEMANTIC_CSS_CSS_WIDE_KEYWORDS_V1, ...values]; } function propertyUnits(name: string): readonly string[] { if (name === "size" || name === "-resume-min-presence-ahead") return numericLengthUnits; return lengthProperties.has(name) || name === "line-height" ? SEMANTIC_CSS_LENGTH_UNITS_V1 : []; } function entries( names: readonly string[], definition: Omit, ): Readonly> { return Object.fromEntries( names.map((name) => [ name, { ...definition, appliesTo: [...new Set(definition.appliesTo)], values: propertyValues(name), units: propertyUnits(name), }, ]), ); } const properties = { ...entries( [ "align-content", "align-items", "align-self", "flex", "flex-direction", "flex-wrap", "flex-flow", "flex-grow", "flex-shrink", "flex-basis", "justify-content", "gap", "row-gap", "column-gap", ], { category: "flexbox", inheritable: false, appliesTo: linkContainerNodes }, ), ...entries(["aspect-ratio", "bottom"], { category: "layout", inheritable: false, appliesTo: linkContainerNodes, }), ...entries(["display"], { category: "layout", inheritable: false, appliesTo: [...linkContainerNodes, "list-item-content", "list-marker"], }), ...entries(["left", "position", "right", "top", "overflow", "z-index"], { category: "layout", inheritable: false, appliesTo: linkContainerNodes, }), ...entries(["width", "height", "min-width", "min-height", "max-width", "max-height"], { category: "dimension", inheritable: false, appliesTo: [...linkContainerNodes, "picture"], }), ...entries(["color"], { category: "color", inheritable: true, appliesTo: colorNodes }), ...entries(["background-color"], { category: "color", inheritable: false, appliesTo: linkContainerNodes }), ...entries(["opacity"], { category: "color", inheritable: false, appliesTo: colorNodes }), ...entries(["direction"], { category: "text", inheritable: true, appliesTo: textAndLinkNodes }), ...entries(["font-size"], { category: "text", inheritable: true, appliesTo: [...textAndLinkNodes, "icon", "level"], }), ...entries(["font-style", "font-weight", "letter-spacing", "line-height"], { category: "text", inheritable: true, appliesTo: textAndLinkNodes, }), ...entries(["max-lines"], { category: "text", inheritable: false, appliesTo: textAndLinkNodes }), ...entries(["text-align"], { category: "text", inheritable: true, appliesTo: textAndLinkNodes }), ...entries(["text-decoration", "text-decoration-color", "text-decoration-style"], { category: "text", inheritable: false, appliesTo: textAndLinkNodes, }), ...entries(["text-indent"], { category: "text", inheritable: true, appliesTo: textAndLinkNodes }), ...entries(["text-overflow"], { category: "text", inheritable: false, appliesTo: textAndLinkNodes }), ...entries(["text-transform"], { category: "text", inheritable: true, appliesTo: textAndLinkNodes }), ...entries(["vertical-align"], { category: "text", inheritable: false, appliesTo: textAndLinkNodes }), ...entries(["object-fit", "object-position"], { category: "image", inheritable: false, appliesTo: ["picture"] }), ...entries(["-resume-shadow-color", "-resume-shadow-width"], { category: "image", inheritable: false, appliesTo: ["picture"], }), ...entries( [ "margin", "margin-top", "margin-right", "margin-bottom", "margin-left", "margin-horizontal", "margin-vertical", "padding", "padding-top", "padding-right", "padding-bottom", "padding-left", "padding-horizontal", "padding-vertical", ], { category: "spacing", inheritable: false, appliesTo: spacingNodes }, ), ...entries( [ "border", "border-color", "border-style", "border-width", "border-top", "border-right", "border-bottom", "border-left", "border-top-color", "border-top-style", "border-top-width", "border-right-color", "border-right-style", "border-right-width", "border-bottom-color", "border-bottom-style", "border-bottom-width", "border-left-color", "border-left-style", "border-left-width", "border-radius", "border-top-left-radius", "border-top-right-radius", "border-bottom-right-radius", "border-bottom-left-radius", ], { category: "border", inheritable: false, appliesTo: [...linkContainerNodes, "picture"] }, ), ...entries(["transform", "transform-origin"], { category: "transform", inheritable: false, appliesTo: [...linkContainerNodes, "picture"], }), ...entries(["order", "break-before", "break-inside"], { category: "structural", inheritable: false, appliesTo: structuralNodes, }), ...entries(["orphans", "widows"], { category: "structural", inheritable: false, appliesTo: textNodes }), ...entries(["size"], { category: "structural", inheritable: false, appliesTo: ["page"] }), ...entries(["-resume-fixed"], { category: "structural", inheritable: false, appliesTo: structuralNodes }), ...entries(["-resume-min-presence-ahead"], { category: "structural", inheritable: false, appliesTo: structuralNodes, }), } satisfies PropertyRegistry; export const PROPERTY_REGISTRY_V1: PropertyRegistry = properties;