Skip to content

Commit 6d93d55

Browse files
committed
fix
1 parent 30668e0 commit 6d93d55

File tree

1 file changed

+40
-2
lines changed

1 file changed

+40
-2
lines changed

web_src/js/svg.js

Lines changed: 40 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,6 @@ export function svg(name, size = 16, className = '') {
107107
const svgNode = document.firstChild;
108108
if (size !== 16) svgNode.setAttribute('width', String(size));
109109
if (size !== 16) svgNode.setAttribute('height', String(size));
110-
// filter array to remove empty string
111110
if (className) svgNode.classList.add(...className.split(/\s+/).filter(Boolean));
112111
return serializer.serializeToString(svgNode);
113112
}
@@ -120,6 +119,45 @@ export const SvgIcon = {
120119
className: {type: String, default: ''},
121120
},
122121
render() {
123-
return h('span', {innerHTML: svg(this.name, this.size, this.className)});
122+
const svgStr = svgs[this.name];
123+
if (!svgStr) throw new Error(`Unknown SVG icon: ${this.name}`);
124+
125+
// parse the SVG string to 2 parts
126+
// * svgInnerHtml: the inner part of the SVG, will be used as the content of the <svg> VNode
127+
// * svgOuter: the outer part of the SVG, including attributes
128+
// the builtin SVG contents are clean, so it's safe to use `indexOf` to split the content:
129+
// eg: <svg outer-attributes>${svgInnerHtml}</svg>
130+
const p1 = svgStr.indexOf('>'), p2 = svgStr.lastIndexOf('<');
131+
if (p1 === -1 || p2 === -1) throw new Error(`Invalid SVG icon: ${this.name}`);
132+
const svgInnerHtml = svgStr.slice(p1 + 1, p2);
133+
const svgOuterHtml = svgStr.slice(0, p1 + 1) + svgStr.slice(p2);
134+
const svgDoc = parser.parseFromString(svgOuterHtml, 'image/svg+xml');
135+
const svgOuter = svgDoc.firstChild;
136+
137+
// https://vuejs.org/guide/extras/render-function.html#creating-vnodes
138+
// the `^` is used for attr, set SVG attributes like 'width', `aria-hidden`, `viewBox`, etc
139+
const attrs = {};
140+
for (const attr of svgOuter.attributes) {
141+
if (attr.name === 'class') continue;
142+
attrs[`^${attr.name}`] = attr.value;
143+
}
144+
attrs[`^width`] = this.size;
145+
attrs[`^height`] = this.size;
146+
147+
// make the <SvgIcon class="foo" class-name="bar"> classes work together
148+
const classes = [];
149+
for (const cls of svgOuter.classList) {
150+
classes.push(cls);
151+
}
152+
if (this.className) {
153+
classes.push(...this.className.split(/\s+/).filter(Boolean));
154+
}
155+
156+
// create VNode
157+
return h('svg', {
158+
...attrs,
159+
class: classes,
160+
innerHTML: svgInnerHtml,
161+
});
124162
},
125163
};

0 commit comments

Comments
 (0)