Skip to content

Commit 4814af5

Browse files
committed
Add namespace support to patchElements
1 parent 91f752a commit 4814af5

File tree

2 files changed

+36
-7
lines changed

2 files changed

+36
-7
lines changed

library/src/plugins/watchers/patchElements.ts

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -23,13 +23,14 @@ type PatchElementsArgs = {
2323
mode: PatchElementsMode
2424
selector: string
2525
useViewTransition: boolean
26+
wrap?: string
2627
}
2728

2829
watcher({
2930
name: 'datastar-patch-elements',
3031
apply(
3132
ctx,
32-
{ elements = '', selector = '', mode = 'outer', useViewTransition },
33+
{ elements = '', selector = '', mode = 'outer', useViewTransition, wrap },
3334
) {
3435
switch (mode) {
3536
case 'remove':
@@ -54,6 +55,7 @@ watcher({
5455
selector,
5556
elements,
5657
useViewTransition: useViewTransition?.trim() === 'true',
58+
wrap,
5759
}
5860

5961
if (supportsViewTransitions && useViewTransition) {
@@ -66,7 +68,7 @@ watcher({
6668

6769
const onPatchElements = (
6870
{ error }: WatcherContext,
69-
{ elements, selector, mode }: PatchElementsArgs,
71+
{ elements, selector, mode, wrap }: PatchElementsArgs,
7072
) => {
7173
const elementsWithSvgsRemoved = elements.replace(
7274
/<svg(\s[^>]*>|>)([\s\S]*?)<\/svg>/gim,
@@ -79,7 +81,9 @@ const onPatchElements = (
7981
const newDocument = new DOMParser().parseFromString(
8082
hasHtml || hasHead || hasBody
8183
? elements
82-
: `<body><template>${elements}</template></body>`,
84+
: wrap
85+
? `<body><template><${wrap}>${elements}</${wrap}></template></body>`
86+
: `<body><template>${elements}</template></body>`,
8387
'text/html',
8488
)
8589

@@ -93,6 +97,11 @@ const onPatchElements = (
9397
newContent.appendChild(newDocument.head)
9498
} else if (hasBody) {
9599
newContent.appendChild(newDocument.body)
100+
} else if (wrap) {
101+
const wrapEl = newDocument.querySelector('template')!.content.querySelector(wrap)!
102+
for (const child of wrapEl.childNodes) {
103+
newContent.appendChild(child)
104+
}
96105
} else {
97106
newContent = newDocument.querySelector('template')!.content
98107
}
@@ -345,9 +354,12 @@ const morphChildren = (
345354
// elements with persistent IDs and possible state info we can still preserve by moving in and then morphing
346355
if (ctxIdMap.has(newChild)) {
347356
// node has children with IDs with possible state so create a dummy elt of same type and apply full morph algorithm
348-
const newEmptyChild = document.createElement(
349-
(newChild as Element).tagName,
350-
)
357+
const ns = (newChild as Element).namespaceURI
358+
const tagName = (newChild as Element).tagName
359+
const newEmptyChild =
360+
ns && ns !== 'http://www.w3.org/1999/xhtml'
361+
? document.createElementNS(ns, tagName)
362+
: document.createElement(tagName)
351363
oldParent.insertBefore(newEmptyChild, insertionPoint)
352364
morphNode(newEmptyChild, newChild)
353365
insertionPoint = newEmptyChild.nextSibling

sdk/ADR.md

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,7 @@ ServerSentEventGenerator.PatchElements(
109109
selector?: string,
110110
mode?: ElementPatchMode,
111111
useViewTransition?: boolean,
112+
wrap: string,
112113
eventId?: string,
113114
retryDuration?: durationInMilliseconds
114115
}
@@ -137,6 +138,7 @@ ServerSentEventGenerator.PatchElements(
137138
data: mode inner
138139
data: selector #feed
139140
data: useViewTransition true
141+
data: wrap div
140142
data: elements <div id="feed">
141143
data: elements <span>1</span>
142144
data: elements </div>
@@ -175,6 +177,20 @@ ServerSentEventGenerator.PatchElements(
175177
```
176178
</details>
177179

180+
<details>
181+
<summary>Patch SVG elements</summary>
182+
183+
```
184+
event: datastar-patch-elements
185+
data: mode append
186+
data: selector #vis
187+
data: wrap svg
188+
data: elements <circle id="c1" cx="10" r="5" fill="red"/>
189+
data: elements <circle id="c2" cx="20" r="5" fill="green"/>
190+
data: elements <circle id="c3" cx="30" r="5" fill="blue"/>
191+
```
192+
</details>
193+
178194
`PatchElements` sends HTML elements to the browser for DOM manipulation.
179195

180196
> [!TIP]
@@ -218,6 +234,7 @@ String enum defining how elements are patched into the DOM.
218234
| `selector` | string | Element ID | CSS selector for target element. If a selector is not specified, each element must have an ID specified. |
219235
| `mode` | ElementPatchMode | `outer` | How to patch the element |
220236
| `useViewTransition` | boolean | `false` | Enable view transitions API |
237+
| `wrap` | string | Tag name | Tag name used to control the [namespace](https://developer.mozilla.org/en-US/docs/Web/API/Element/namespaceURI) of each element. If a tag name is not specified, elements will be created in the HTML namespace. |
221238

222239
### Implementation
223240

@@ -227,6 +244,7 @@ String enum defining how elements are patched into the DOM.
227244
- `selector SELECTOR\n` (if provided)
228245
- `mode PATCH_MODE\n` (if not `outer`)
229246
- `useViewTransition true\n` (if `true`)
247+
- `wrap TAG_NAME\n` (if provided)
230248
- `elements HTML_LINE\n` (for each line of HTML)
231249

232250
---
@@ -414,4 +432,3 @@ The function ***must*** parse the incoming HTTP request based on the method:
414432
| Others | Request body | JSON | Parse request body directly |
415433

416434
**Error Handling**: ***Must*** return error for invalid JSON.
417-

0 commit comments

Comments
 (0)