/** Shared DOM namespace/attribute helpers for both client rendering runtimes. */ export const SVG_NS = "http://www.w3.org/2000/svg"; // Automatic JSX evaluates child nodes before they are attached to a parent, so // jsx-dom cannot rely on parent namespace alone. Keep the common SVG vocabulary // here and also honor SVG parent context in the VDOM renderer. export const SVG_TAGS = new Set([ "svg", "altGlyph", "altGlyphDef", "altGlyphItem", "animate", "animateColor", "animateMotion", "animateTransform", "circle", "clipPath", "defs", "desc", "ellipse", "feBlend", "feColorMatrix", "feComponentTransfer", "feComposite", "feConvolveMatrix", "feDiffuseLighting", "feDisplacementMap", "feDistantLight", "feDropShadow", "feFlood", "feFuncA", "feFuncB", "feFuncG", "feFuncR", "feGaussianBlur", "feImage", "feMerge", "feMergeNode", "feMorphology", "feOffset", "fePointLight", "feSpecularLighting", "feSpotLight", "feTile", "feTurbulence", "filter", "foreignObject", "g", "glyph", "glyphRef", "image", "line", "linearGradient", "marker", "mask", "metadata", "mpath", "path", "pattern", "polygon", "polyline", "radialGradient", "rect", "set", "stop", "switch", "symbol", "text", "textPath", "tspan", "use", "view", ]); export const SVG_ATTR_MAP: Record = { accentHeight: "accent-height", alignmentBaseline: "alignment-baseline", baselineShift: "baseline-shift", clipPath: "clip-path", clipRule: "clip-rule", colorInterpolation: "color-interpolation", colorInterpolationFilters: "color-interpolation-filters", colorProfile: "color-profile", colorRendering: "color-rendering", dominantBaseline: "dominant-baseline", enableBackground: "enable-background", fillOpacity: "fill-opacity", fillRule: "fill-rule", floodColor: "flood-color", floodOpacity: "flood-opacity", fontFamily: "font-family", fontSize: "font-size", fontSizeAdjust: "font-size-adjust", fontStretch: "font-stretch", fontStyle: "font-style", fontVariant: "font-variant", fontWeight: "font-weight", glyphName: "glyph-name", glyphOrientationHorizontal: "glyph-orientation-horizontal", glyphOrientationVertical: "glyph-orientation-vertical", horizAdvX: "horiz-adv-x", horizOriginX: "horiz-origin-x", imageRendering: "image-rendering", letterSpacing: "letter-spacing", lightingColor: "lighting-color", markerEnd: "marker-end", markerMid: "marker-mid", markerStart: "marker-start", overlinePosition: "overline-position", overlineThickness: "overline-thickness", paintOrder: "paint-order", panose1: "panose-1", pointerEvents: "pointer-events", renderingIntent: "rendering-intent", shapeRendering: "shape-rendering", stopColor: "stop-color", stopOpacity: "stop-opacity", strikethroughPosition: "strikethrough-position", strikethroughThickness: "strikethrough-thickness", strokeDasharray: "stroke-dasharray", strokeDashoffset: "stroke-dashoffset", strokeLinecap: "stroke-linecap", strokeLinejoin: "stroke-linejoin", strokeMiterlimit: "stroke-miterlimit", strokeOpacity: "stroke-opacity", strokeWidth: "stroke-width", textAnchor: "text-anchor", textDecoration: "text-decoration", textRendering: "text-rendering", underlinePosition: "underline-position", underlineThickness: "underline-thickness", unicodeBidi: "unicode-bidi", unicodeRange: "unicode-range", unitsPerEm: "units-per-em", vAlphabetic: "v-alphabetic", vHanging: "v-hanging", vIdeographic: "v-ideographic", vMathematical: "v-mathematical", vectorEffect: "vector-effect", vertAdvY: "vert-adv-y", vertOriginX: "vert-origin-x", vertOriginY: "vert-origin-y", wordSpacing: "word-spacing", writingMode: "writing-mode", xHeight: "x-height", xlinkActuate: "xlink:actuate", xlinkArcrole: "xlink:arcrole", xlinkHref: "xlink:href", xlinkRole: "xlink:role", xlinkShow: "xlink:show", xlinkTitle: "xlink:title", xlinkType: "xlink:type", xmlBase: "xml:base", xmlLang: "xml:lang", xmlSpace: "xml:space", }; export function isSvgElement(el: Element): el is SVGElement { return el.namespaceURI === SVG_NS; } export function createDomElement(tag: string, parentNode?: Node): Element { const parentElement = parentNode instanceof Element ? parentNode : null; const inheritsSvgNamespace = !!parentElement && isSvgElement(parentElement) && parentElement.localName !== "foreignObject"; const svg = tag === "svg" || inheritsSvgNamespace || SVG_TAGS.has(tag); return svg ? document.createElementNS(SVG_NS, tag) : document.createElement(tag); } export function domAttributeName(el: Element, key: string): string { if (key === "htmlFor") return "for"; return isSvgElement(el) ? SVG_ATTR_MAP[key] || key : key; } export function setElementClass(el: Element, value: unknown): void { if (isSvgElement(el)) { if (value == null || value === false || value === "") el.removeAttribute("class"); else el.setAttribute("class", String(value)); return; } (el as HTMLElement).className = value == null || value === false ? "" : String(value); } export function assignElementStyle( el: Element, value: Record, reset = false, ): void { if (reset) el.removeAttribute("style"); Object.assign((el as HTMLElement | SVGElement).style, value); }