Преглед на файлове

perf(ui): render icons through an svg sprite (#26950)

Luke Parker преди 3 месеца
родител
ревизия
3a810fcb9a
променени са 1 файла, в които са добавени 42 реда и са изтрити 6 реда
  1. 42 6
      packages/ui/src/components/icon.tsx

+ 42 - 6
packages/ui/src/components/icon.tsx

@@ -1,4 +1,4 @@
-import { splitProps, type ComponentProps } from "solid-js"
+import { onMount, splitProps, type ComponentProps } from "solid-js"
 
 const icons = {
   "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,6 +105,41 @@ const icons = {
   "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"/>`,
 }
 
+const spriteID = "opencode-icon-sprite"
+const symbol = (name: keyof typeof icons) => `opencode-icon-${name}`
+let spriteInserted = false
+
+function viewBox(name: keyof typeof icons) {
+  return name === "magnifying-glass" || name === "arrow-undo-down" ? "0 0 16 16" : "0 0 20 20"
+}
+
+function ensureSprite() {
+  if (spriteInserted) return
+  if (typeof document === "undefined") return
+  if (document.getElementById(spriteID)) {
+    spriteInserted = true
+    return
+  }
+  const body = document.body as HTMLElement | null
+  if (!body) return
+
+  const svg = document.createElementNS("http://www.w3.org/2000/svg", "svg")
+  svg.id = spriteID
+  svg.setAttribute("aria-hidden", "true")
+  svg.setAttribute("width", "0")
+  svg.setAttribute("height", "0")
+  svg.style.position = "absolute"
+  svg.style.overflow = "hidden"
+  svg.innerHTML = Object.entries(icons)
+    .map(([name, path]) => {
+      const key = name as keyof typeof icons
+      return `<symbol id="${symbol(key)}" viewBox="${viewBox(key)}">${path}</symbol>`
+    })
+    .join("")
+  body.insertBefore(svg, body.firstChild)
+  spriteInserted = true
+}
+
 export interface IconProps extends ComponentProps<"svg"> {
   name: keyof typeof icons
   size?: "small" | "normal" | "medium" | "large"
@@ -112,8 +147,8 @@ export interface IconProps extends ComponentProps<"svg"> {
 
 export function Icon(props: IconProps) {
   const [local, others] = splitProps(props, ["name", "size", "class", "classList"])
-  const viewBox = () =>
-    local.name === "magnifying-glass" || local.name === "arrow-undo-down" ? "0 0 16 16" : "0 0 20 20"
+  onMount(ensureSprite)
+
   return (
     <div data-component="icon" data-size={local.size || "normal"}>
       <svg
@@ -123,11 +158,12 @@ export function Icon(props: IconProps) {
           [local.class ?? ""]: !!local.class,
         }}
         fill="none"
-        viewBox={viewBox()}
-        innerHTML={icons[local.name as keyof typeof icons]}
+        viewBox={viewBox(local.name)}
         aria-hidden="true"
         {...others}
-      />
+      >
+        <use href={`#${symbol(local.name)}`} />
+      </svg>
     </div>
   )
 }