From aec6fa1573d0f64be6e2879e54a8e4d7e9e300ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=B4=96?= Date: Mon, 5 Dec 2022 15:57:04 +0800 Subject: [PATCH] fix: clone svg symbols (#344) --- src/clone-node.ts | 48 ++++++++++++++++++++++++++++ test/resources/svg-use-tag/image | 1 + test/resources/svg-use-tag/node.html | 28 ++++++++++++++++ test/resources/svg-use-tag/style.css | 27 ++++++++++++++++ test/spec/svg.spec.ts | 11 +++++++ 5 files changed, 115 insertions(+) create mode 100644 test/resources/svg-use-tag/image create mode 100644 test/resources/svg-use-tag/node.html create mode 100644 test/resources/svg-use-tag/style.css diff --git a/src/clone-node.ts b/src/clone-node.ts index 2877036e..b41d4a0f 100644 --- a/src/clone-node.ts +++ b/src/clone-node.ts @@ -128,6 +128,53 @@ function decorate(nativeNode: T, clonedNode: T): T { return clonedNode } +async function ensureSVGSymbols( + clone: T, + options: Options, +) { + const uses = clone.querySelectorAll ? clone.querySelectorAll('use') : [] + if (uses.length === 0) { + return clone + } + + const processedDefs: { [key: string]: HTMLElement } = {} + for (let i = 0; i < uses.length; i++) { + const use = uses[i] + const id = use.getAttribute('xlink:href') + if (id) { + const exist = clone.querySelector(id) + const definition = document.querySelector(id) as HTMLElement + if (!exist && definition && !processedDefs[id]) { + // eslint-disable-next-line no-await-in-loop + processedDefs[id] = (await cloneNode(definition, options, true))! + } + } + } + + const nodes = Object.values(processedDefs) + if (nodes.length) { + const ns = 'http://www.w3.org/1999/xhtml' + const svg = document.createElementNS(ns, 'svg') + svg.setAttribute('xmlns', ns) + svg.style.position = 'absolute' + svg.style.width = '0' + svg.style.height = '0' + svg.style.overflow = 'hidden' + svg.style.display = 'none' + + const defs = document.createElementNS(ns, 'defs') + svg.appendChild(defs) + + for (let i = 0; i < nodes.length; i++) { + defs.appendChild(nodes[i]) + } + + clone.appendChild(svg) + } + + return clone +} + export async function cloneNode( node: T, options: Options, @@ -141,4 +188,5 @@ export async function cloneNode( .then((clonedNode) => cloneSingleNode(clonedNode, options) as Promise) .then((clonedNode) => cloneChildren(node, clonedNode, options)) .then((clonedNode) => decorate(node, clonedNode)) + .then((clonedNode) => ensureSVGSymbols(clonedNode, options)) } diff --git a/test/resources/svg-use-tag/image b/test/resources/svg-use-tag/image new file mode 100644 index 00000000..5e5f4054 --- /dev/null +++ b/test/resources/svg-use-tag/image @@ -0,0 +1 @@ +data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAGQAAABkCAYAAABw4pVUAAAAAXNSR0IArs4c6QAAAwpJREFUeF7tmPGNzFEYRe90QAWUoAN0QAXogApQAR2gAjpAB3RABzpY+TZvZCLDnkl+bkb2vGT+mdzMfXPOvt/3ZncXyUVcZ0NgN0J2ye5sdnSNN3LpQiHn8xegkPNxcbkTIuRekudJXib5lORxkkdJPid5cWbf57/fzlVCBv6bg2/5JMntJWiEjCzXhgT+JuTtOgnv1kmY0zAn40eSG+uEKGRDGX96ZA3sj0nuJJkTMdBHxAj6muTV2oMnZGMZx4SMhPdJbq5ZMbNj3tuvL2uWjJz57XI/ybzn2ojA4SPr4ZoX35O8XidhTsvv69uS8jTJrXWKPmy0n2v/MYdC5hY1j6eBOyfjmIw9sJkjk3+w8iPQtQGBY0N9rrZ3wWc7QwCkUyNEyLM1J2aW7Af69CjkVNogT4TM4J5TM4+zuX3tl0IA4FMjCjmV2D/OHxMyv87ntV9zm5qr7TyyDof3XH3n5dqQwFX/Otmwyo8iBBRCKBUzCinCJlUKIZSKGYUUYZMqhRBKxYxCirBJlUIIpWJGIUXYpEohhFIxo5AibFKlEEKpmFFIETapUgihVMwopAibVCmEUCpmFFKETaoUQigVMwopwiZVCiGUihmFFGGTKoUQSsWMQoqwSZVCCKViRiFF2KRKIYRSMaOQImxSpRBCqZhRSBE2qVIIoVTMKKQIm1QphFAqZhRShE2qFEIoFTMKKcImVQohlIoZhRRhkyqFEErFjEKKsEmVQgilYkYhRdikSiGEUjGjkCJsUqUQQqmYUUgRNqlSCKFUzCikCJtUKYRQKmYUUoRNqhRCKBUzCinCJlUKIZSKGYUUYZMqhRBKxYxCirBJlUIIpWJGIUXYpEohhFIxo5AibFKlEEKpmFFIETapUgihVMwopAibVCmEUCpmFFKETaoUQigVMwopwiZVCiGUihmFFGGTKoUQSsWMQoqwSZVCCKViRiFF2KRKIYRSMaOQImxSpRBCqZhRSBE2qVIIoVTMKKQIm1QphFAqZhRShE2qFEIoFTMKKcImVb+EkLCZDoGfbnm/rNO8R/AAAAAASUVORK5CYII= diff --git a/test/resources/svg-use-tag/node.html b/test/resources/svg-use-tag/node.html new file mode 100644 index 00000000..06e8b610 --- /dev/null +++ b/test/resources/svg-use-tag/node.html @@ -0,0 +1,28 @@ +
+
+ + + +
+
+
+ + + + home + + + + +
diff --git a/test/resources/svg-use-tag/style.css b/test/resources/svg-use-tag/style.css new file mode 100644 index 00000000..b51cf808 --- /dev/null +++ b/test/resources/svg-use-tag/style.css @@ -0,0 +1,27 @@ +#dom-node { + width: 100px; + overflow: hidden; +} + +#root { + border: 1px solid red; + position: relative; + height: 100px; +} + +svg { + width: 100%; + height: 100%; +} + +.icon { + display: inline-block; + width: 0.9285714285714285em; + height: 1em; + stroke-width: 0; + stroke: currentColor; + fill: currentColor; + vertical-align: middle; + top: -1px; + position: relative; +} diff --git a/test/spec/svg.spec.ts b/test/spec/svg.spec.ts index 726597b0..f4595a11 100644 --- a/test/spec/svg.spec.ts +++ b/test/spec/svg.spec.ts @@ -46,4 +46,15 @@ describe('work with svg element', () => { .then(done) .catch(done) }) + + it('should render SVG use tags', function (done) { + bootstrap( + 'svg-use-tag/node.html', + 'svg-use-tag/style.css', + 'svg-use-tag/image', + ) + .then(renderAndCheck) + .then(done) + .catch(done) + }) })