From a036181677a1e6d3611f6d10323e9c4c57d2bb9b Mon Sep 17 00:00:00 2001 From: Taku Amano Date: Tue, 21 May 2024 06:07:13 +0900 Subject: [PATCH] feat(jsx/dom): support SVG kebab-case attributes --- src/jsx/dom/css.test.tsx | 1 + src/jsx/dom/index.test.tsx | 1 + src/jsx/dom/render.ts | 20 +++++++++++++++----- src/jsx/hooks/dom.test.tsx | 1 + 4 files changed, 18 insertions(+), 5 deletions(-) diff --git a/src/jsx/dom/css.test.tsx b/src/jsx/dom/css.test.tsx index 97ba4ae0e..82430174c 100644 --- a/src/jsx/dom/css.test.tsx +++ b/src/jsx/dom/css.test.tsx @@ -22,6 +22,7 @@ describe('Style and css for jsx/dom', () => { }) global.document = dom.window.document global.HTMLElement = dom.window.HTMLElement + global.SVGElement = dom.window.SVGElement global.Text = dom.window.Text root = document.getElementById('root') as HTMLElement }) diff --git a/src/jsx/dom/index.test.tsx b/src/jsx/dom/index.test.tsx index f300b6f16..ea86ca979 100644 --- a/src/jsx/dom/index.test.tsx +++ b/src/jsx/dom/index.test.tsx @@ -89,6 +89,7 @@ describe('DOM', () => { }) global.document = dom.window.document global.HTMLElement = dom.window.HTMLElement + global.SVGElement = dom.window.SVGElement global.Text = dom.window.Text root = document.getElementById('root') as HTMLElement }) diff --git a/src/jsx/dom/render.ts b/src/jsx/dom/render.ts index 8e025b04a..083f755d7 100644 --- a/src/jsx/dom/render.ts +++ b/src/jsx/dom/render.ts @@ -106,6 +106,14 @@ const getEventSpec = (key: string): [string, boolean] | undefined => { return undefined } +const toAttributeName = (element: SupportedElement, key: string): string => + element instanceof SVGElement && + /[A-Z]/.test(key) && + (key in element.style || // Presentation attributes are findable in style object. "clip-path", "font-size", "stroke-width", etc. + key.match(/^(?:o|pai|str|u|ve)/)) // Other un-deprecated kebab-case attributes. "overline-position", "paint-order", "strikethrough-position", etc. + ? key.replace(/([A-Z])/g, '-$1').toLowerCase() + : key + const applyProps = (container: SupportedElement, attributes: Props, oldAttributes?: Props) => { attributes ||= {} for (const [key, value] of Object.entries(attributes)) { @@ -164,14 +172,16 @@ const applyProps = (container: SupportedElement, attributes: Props, oldAttribute ;(container as any)[key] = value } + const k = toAttributeName(container, key) + if (value === null || value === undefined || value === false) { - container.removeAttribute(key) + container.removeAttribute(k) } else if (value === true) { - container.setAttribute(key, '') + container.setAttribute(k, '') } else if (typeof value === 'string' || typeof value === 'number') { - container.setAttribute(key, value as string) + container.setAttribute(k, value as string) } else { - container.setAttribute(key, value.toString()) + container.setAttribute(k, value.toString()) } } } @@ -189,7 +199,7 @@ const applyProps = (container: SupportedElement, attributes: Props, oldAttribute value.current = null } } else { - container.removeAttribute(key) + container.removeAttribute(toAttributeName(container, key)) } } } diff --git a/src/jsx/hooks/dom.test.tsx b/src/jsx/hooks/dom.test.tsx index 7ed3a57c8..f4a470a34 100644 --- a/src/jsx/hooks/dom.test.tsx +++ b/src/jsx/hooks/dom.test.tsx @@ -35,6 +35,7 @@ describe('Hooks', () => { }) global.document = dom.window.document global.HTMLElement = dom.window.HTMLElement + global.SVGElement = dom.window.SVGElement global.Text = dom.window.Text root = document.getElementById('root') as HTMLElement })