import { useLayoutEffect, useRef, type ReactNode } from "react" import "../../theme/sw-viewer.css" const SHAPES = "path, line, polyline, polygon, circle, ellipse, rect" export interface SWViewerIconProps { /** The program's SVG icon (any size; stroke and / or fill, currentColor recommended). */ children: ReactNode className?: string } /** * Icon with the SWHeader black outline drawn as SVG stroke (SWHeader icon assets: 1px black OUTSIDE stroke). * A copy of the icon sits behind it: every shape of the copy is stroked black with its own stroke width plus * 2 x `--hm-sw-viewer-icon-outline-width` (1px each side, in screen pixels at the rendered size), and filled * shapes are filled black. The icon on top is untouched, so its color, shape, size and line width stay the same. */ export function SWViewerIcon({ children, className }: SWViewerIconProps) { const outlineRef = useRef(null) useLayoutEffect(() => { const root = outlineRef.current if (!root) return const outline = parseFloat(getComputedStyle(root).getPropertyValue("--hm-sw-viewer-icon-outline-width")) || 1 for (const svg of Array.from(root.querySelectorAll("svg"))) { const view = svg.viewBox?.baseVal const rendered = svg.getBoundingClientRect().width // User units per screen pixel, so the outline is 1px on screen whatever the viewBox (e.g. 24 drawn at 16px). const unit = view && view.width && rendered ? view.width / rendered : 1 for (const shape of Array.from(svg.querySelectorAll(SHAPES))) { shape.style.removeProperty("stroke-width") const style = getComputedStyle(shape) const strokeWidth = style.stroke !== "none" ? parseFloat(style.strokeWidth) || 0 : 0 const filled = style.fill !== "none" && Number(style.fillOpacity) !== 0 if (!strokeWidth && !filled) { shape.style.visibility = "hidden"; continue } shape.style.stroke = "var(--hm-sw-viewer-icon-outline-color)" shape.style.strokeWidth = `${strokeWidth + 2 * outline * unit}` shape.style.strokeLinejoin = "round" if (!strokeWidth) shape.style.strokeLinecap = "round" if (filled) shape.style.fill = "var(--hm-sw-viewer-icon-outline-color)" } } }) return ( ) }