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,44 @@ 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+
111+ function viewBox ( name : keyof typeof icons ) {
112+ return name === "magnifying-glass" || name === "arrow-undo-down" ? "0 0 16 16" : "0 0 20 20"
113+ }
114+
115+ function ensureSprite ( ) {
116+ if ( typeof document === "undefined" ) return
117+ if ( document . getElementById ( spriteID ) ) return
118+ const body = document . body as HTMLElement | null
119+ if ( ! body ) return
120+
121+ const svg = document . createElementNS ( "http://www.w3.org/2000/svg" , "svg" )
122+ svg . id = spriteID
123+ svg . setAttribute ( "aria-hidden" , "true" )
124+ svg . setAttribute ( "width" , "0" )
125+ svg . setAttribute ( "height" , "0" )
126+ svg . style . position = "absolute"
127+ svg . style . overflow = "hidden"
128+ svg . innerHTML = Object . entries ( icons )
129+ . map ( ( [ name , path ] ) => {
130+ const key = name as keyof typeof icons
131+ return `<symbol id="${ symbol ( key ) } " viewBox="${ viewBox ( key ) } ">${ path } </symbol>`
132+ } )
133+ . join ( "" )
134+ body . insertBefore ( svg , body . firstChild )
135+ }
136+
108137export interface IconProps extends ComponentProps < "svg" > {
109138 name : keyof typeof icons
110139 size ?: "small" | "normal" | "medium" | "large"
111140}
112141
113142export function Icon ( props : IconProps ) {
114143 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"
144+ onMount ( ensureSprite )
145+
117146 return (
118147 < div data-component = "icon" data-size = { local . size || "normal" } >
119148 < svg
@@ -123,11 +152,12 @@ export function Icon(props: IconProps) {
123152 [ local . class ?? "" ] : ! ! local . class ,
124153 } }
125154 fill = "none"
126- viewBox = { viewBox ( ) }
127- innerHTML = { icons [ local . name as keyof typeof icons ] }
155+ viewBox = { viewBox ( local . name ) }
128156 aria-hidden = "true"
129157 { ...others }
130- />
158+ >
159+ < use href = { `#${ symbol ( local . name ) } ` } />
160+ </ svg >
131161 </ div >
132162 )
133163}
0 commit comments