@@ -107,7 +107,6 @@ export function svg(name, size = 16, className = '') {
107
107
const svgNode = document . firstChild ;
108
108
if ( size !== 16 ) svgNode . setAttribute ( 'width' , String ( size ) ) ;
109
109
if ( size !== 16 ) svgNode . setAttribute ( 'height' , String ( size ) ) ;
110
- // filter array to remove empty string
111
110
if ( className ) svgNode . classList . add ( ...className . split ( / \s + / ) . filter ( Boolean ) ) ;
112
111
return serializer . serializeToString ( svgNode ) ;
113
112
}
@@ -120,6 +119,45 @@ export const SvgIcon = {
120
119
className : { type : String , default : '' } ,
121
120
} ,
122
121
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
+ } ) ;
124
162
} ,
125
163
} ;
0 commit comments