Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 42 additions & 6 deletions packages/ui/src/components/icon.tsx
Original file line number Diff line number Diff line change
@@ -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"/>`,
Expand Down Expand Up @@ -105,15 +105,50 @@ 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"
}

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)

Comment on lines 149 to +151
return (
<div data-component="icon" data-size={local.size || "normal"}>
<svg
Expand All @@ -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>
Comment on lines 149 to +166
</div>
)
}
Loading