diff --git a/src/app/toFragment.ts b/src/app/toFragment.ts index b2cf72b..7c386f5 100644 --- a/src/app/toFragment.ts +++ b/src/app/toFragment.ts @@ -1,24 +1,9 @@ import { type JSONTemplate } from '../api/types' import { RegorConfig } from '../app/RegorConfig' import { isTemplate, normalizeAttributeName } from '../common/common' +import { createElement } from '../common/createElement' import { isArray } from '../common/is-what' -const svgTags = - 'svg,animate,animateMotion,animateTransform,circle,clipPath,color-profile,' + - 'defs,desc,discard,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,hatch,hatchpath,image,line,linearGradient,marker,mask,' + - 'mesh,meshgradient,meshpatch,meshrow,metadata,mpath,path,pattern,' + - 'polygon,polyline,radialGradient,rect,set,solidcolor,stop,switch,symbol,' + - 'text,textPath,title,tspan,unknown,use,view' - -const svgSet = new Set(svgTags.toUpperCase().split(',')) - -const svgNamespace = 'http://www.w3.org/2000/svg' - const appendChild = (parent: Node, el: Node): void => { if (isTemplate(parent)) parent.content.appendChild(el) else parent.appendChild(el) @@ -32,10 +17,7 @@ const render = ( ): void => { const tag = json.t if (tag) { - const el = - isSVG && svgSet.has(tag.toUpperCase()) - ? document.createElementNS(svgNamespace, tag.toLowerCase()) - : document.createElement(tag) + const el = createElement(tag, isSVG) const attributes = json.a if (attributes) { for (const attr of Object.entries(attributes)) { diff --git a/src/bind/DynamicBinder.ts b/src/bind/DynamicBinder.ts index dae57f9..d315ff6 100644 --- a/src/bind/DynamicBinder.ts +++ b/src/bind/DynamicBinder.ts @@ -2,13 +2,14 @@ import { type StopObserving } from '../api/types' import { addUnbinder } from '../cleanup/addUnbinder' import { removeNode } from '../cleanup/removeNode' import { getNodes, isTemplate, unmount } from '../common/common' +import { createElement, svgNamespace } from '../common/createElement' import { isNullOrWhitespace, isObject, isString } from '../common/is-what' import { type Binder } from './Binder' import { setSwitchOwner } from './switch' const noopStopObserving: StopObserving = () => {} -const mount = (nodes: ChildNode[], parent: HTMLElement): void => { +const mount = (nodes: ChildNode[], parent: Element): void => { for (const x of nodes) { const node = x.cloneNode(true) parent.appendChild(node) @@ -59,11 +60,14 @@ export class DynamicBinder { if (!expression) return if (!expression.startsWith('regor:')) { if (!expression.startsWith('r-')) return - const staticName = expression.slice(2).trim().toLowerCase() + const staticName = expression.slice(2).trim() if (!staticName) return const parent = el.parentNode if (!parent) return - const nativeElement = document.createElement(staticName) + const nativeElement = createElement( + staticName, + el.namespaceURI === svgNamespace, + ) for (const attr of el.getAttributeNames()) { if (attr === 'is') continue nativeElement.setAttribute(attr, el.getAttribute(attr) as string) @@ -169,7 +173,10 @@ export class DynamicBinder { const currentParent = (commentEnd.parentNode ?? commentBegin.parentNode) as HTMLElement | null if (!currentParent) return - const componentElement = document.createElement(name) + const componentElement = createElement( + name, + el.namespaceURI === svgNamespace, + ) for (const attr of el.getAttributeNames()) { if (attr === this.__is) continue componentElement.setAttribute(attr, el.getAttribute(attr) as string) diff --git a/src/common/createElement.ts b/src/common/createElement.ts new file mode 100644 index 0000000..c12d21f --- /dev/null +++ b/src/common/createElement.ts @@ -0,0 +1,24 @@ +const svgTags = + 'svg,animate,animateMotion,animateTransform,circle,clipPath,color-profile,' + + 'defs,desc,discard,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,hatch,hatchpath,image,line,linearGradient,marker,mask,' + + 'mesh,meshgradient,meshpatch,meshrow,metadata,mpath,path,pattern,' + + 'polygon,polyline,radialGradient,rect,set,solidcolor,stop,switch,symbol,' + + 'text,textPath,title,tspan,unknown,use,view' + +const svgNames = new Map( + svgTags.split(',').map((tag) => [tag.toUpperCase(), tag]), +) + +export const svgNamespace = 'http://www.w3.org/2000/svg' + +export const createElement = (tag: string, isSVG = false): Element => { + const svgName = isSVG ? svgNames.get(tag.toUpperCase()) : undefined + return svgName + ? document.createElementNS(svgNamespace, svgName) + : document.createElement(tag) +}