protected _renderSlots(): void {
const outlets = this._getSlots()
const scopeId = this._instance!.type.__scopeId
+ const slotReplacements: Map<Node, Node[]> = new Map()
+
for (let i = 0; i < outlets.length; i++) {
const o = outlets[i] as HTMLSlotElement
const slotName = o.getAttribute('name') || 'default'
const content = this._slots![slotName]
const parent = o.parentNode!
+ const replacementNodes: Node[] = []
+
if (content) {
for (const n of content) {
// for :slotted css
}
}
parent.insertBefore(n, o)
+ replacementNodes.push(n)
+ }
+ } else {
+ while (o.firstChild) {
+ const child = o.firstChild
+ parent.insertBefore(child, o)
+ replacementNodes.push(child)
+ }
+ }
+
+ slotReplacements.set(o, replacementNodes)
+ }
+
+ // For Vapor: update fragment nodes before removing slots from DOM
+ if (slotReplacements.size > 0 && this._instance && this._instance.vapor) {
+ // @ts-expect-error TODO refactor
+ this._replaceNodesInFragments(this._instance.block, slotReplacements)
+ }
+
+ // Now safe to remove slots from DOM
+ slotReplacements.forEach((_, o) => o.parentNode!.removeChild(o))
+ }
+
+ /**
+ * Replace slot nodes with their content in fragment nodes arrays
+ * @internal
+ */
+ private _replaceNodesInFragments(
+ block: any,
+ replacements: Map<Node, Node[]>,
+ ): void {
+ if (!block) return
+
+ if (Array.isArray(block)) {
+ for (let i = 0; i < block.length; i++) {
+ this._replaceNodesInFragments(block[i], replacements)
+ }
+ } else if (block.nodes !== undefined) {
+ // This is a fragment with nodes property
+ if (Array.isArray(block.nodes)) {
+ // Replace slot nodes with their content
+ const newNodes: any[] = []
+ for (const node of block.nodes) {
+ if (node instanceof Node && replacements.has(node)) {
+ // Replace with the content nodes
+ newNodes.push(...replacements.get(node)!)
+ } else {
+ newNodes.push(node)
+ // Recursively process nested fragments
+ this._replaceNodesInFragments(node, replacements)
+ }
}
+ block.nodes = newNodes
+ } else if (block.nodes instanceof Node && replacements.has(block.nodes)) {
+ // Replace single node with its content
+ const replacement = replacements.get(block.nodes)!
+ block.nodes = replacement.length === 1 ? replacement[0] : replacement
} else {
- while (o.firstChild) parent.insertBefore(o.firstChild, o)
+ this._replaceNodesInFragments(block.nodes, replacements)
}
- parent.removeChild(o)
}
}
)
customElements.define('my-el-shadowroot-false-slots', ES)
- test.todo('should render slots', async () => {
+ test('should render slots', async () => {
container.innerHTML =
`<my-el-shadowroot-false-slots>` +
`<span>default</span>text` +
toggle.value = false
await nextTick()
expect(e.innerHTML).toBe(
- `<span>default</span>text` + `<!--if-->` + `<div>fallback</div>`,
+ `<span>default</span>text<!--slot-->` +
+ `<!--if-->` +
+ `<div>fallback</div><!--slot-->`,
)
})