1- import { splitProps , type ComponentProps } from "solid-js"
1+ import { onMount , splitProps , type ComponentProps } from "solid-js"
22
33const icons = {
44 "align-right" : `<path d="M12.292 6.04167L16.2503 9.99998L12.292 13.9583M2.91699 9.99998H15.6253M17.0837 3.75V16.25" stroke="currentColor" stroke-linecap="square"/>` ,
@@ -105,15 +105,50 @@ const icons = {
105105 "arrow-undo-down" : `<path d="M4.08333 11.0859L1.75 8.7526L4.08333 6.41927M2.33333 8.7526L12.5417 8.7526L12.5417 3.21094L7 3.21094" stroke="currentColor" stroke-width="1" stroke-linecap="square"/>` ,
106106}
107107
108+ const spriteID = "opencode-icon-sprite"
109+ const symbol = ( name : keyof typeof icons ) => `opencode-icon-${ name } `
110+ let spriteInserted = false
111+
112+ function viewBox ( name : keyof typeof icons ) {
113+ return name === "magnifying-glass" || name === "arrow-undo-down" ? "0 0 16 16" : "0 0 20 20"
114+ }
115+
116+ function ensureSprite ( ) {
117+ if ( spriteInserted ) return
118+ if ( typeof document === "undefined" ) return
119+ if ( document . getElementById ( spriteID ) ) {
120+ spriteInserted = true
121+ return
122+ }
123+ const body = document . body as HTMLElement | null
124+ if ( ! body ) return
125+
126+ const svg = document . createElementNS ( "http://www.w3.org/2000/svg" , "svg" )
127+ svg . id = spriteID
128+ svg . setAttribute ( "aria-hidden" , "true" )
129+ svg . setAttribute ( "width" , "0" )
130+ svg . setAttribute ( "height" , "0" )
131+ svg . style . position = "absolute"
132+ svg . style . overflow = "hidden"
133+ svg . innerHTML = Object . entries ( icons )
134+ . map ( ( [ name , path ] ) => {
135+ const key = name as keyof typeof icons
136+ return `<symbol id="${ symbol ( key ) } " viewBox="${ viewBox ( key ) } ">${ path } </symbol>`
137+ } )
138+ . join ( "" )
139+ body . insertBefore ( svg , body . firstChild )
140+ spriteInserted = true
141+ }
142+
108143export interface IconProps extends ComponentProps < "svg" > {
109144 name : keyof typeof icons
110145 size ?: "small" | "normal" | "medium" | "large"
111146}
112147
113148export function Icon ( props : IconProps ) {
114149 const [ local , others ] = splitProps ( props , [ "name" , "size" , "class" , "classList" ] )
115- const viewBox = ( ) =>
116- local . name === "magnifying-glass" || local . name === "arrow-undo-down" ? "0 0 16 16" : "0 0 20 20"
150+ onMount ( ensureSprite )
151+
117152 return (
118153 < div data-component = "icon" data-size = { local . size || "normal" } >
119154 < svg
@@ -123,11 +158,12 @@ export function Icon(props: IconProps) {
123158 [ local . class ?? "" ] : ! ! local . class ,
124159 } }
125160 fill = "none"
126- viewBox = { viewBox ( ) }
127- innerHTML = { icons [ local . name as keyof typeof icons ] }
161+ viewBox = { viewBox ( local . name ) }
128162 aria-hidden = "true"
129163 { ...others }
130- />
164+ >
165+ < use href = { `#${ symbol ( local . name ) } ` } />
166+ </ svg >
131167 </ div >
132168 )
133169}
0 commit comments