import {c, css, html, useEffect, useHost, useRef, useState} from "atomico"; declare global { namespace JSX { interface IntrinsicElements { "c-svg": any; } } var htmx: any; } function styleSvgPath(path:SVGPathElement) { const length = path.getTotalLength(); path.style.stroke = "rgb(107 114 128 )" // Red stroke for high visibility path.style.strokeWidth = '2'; path.style.fill = 'white'; // No fill initially to ensure stroke visibility // Set the stroke-dasharray and dashoffset to create the drawing effect // path.style.strokeDasharray = length; // path.style.strokeDashoffset = length; path.style.fillOpacity ="70%" // Add the animation class to animate the stroke path.classList.add('draw-path'); } async function* fetchSvgInBatches(src: string) { const response = await fetch(src); const reader = response.body?.getReader(); const decoder = new TextDecoder(); let done = false; let svgContent = ''; let yieldPaths:SVGPathElement[] = []; while (!done) { const { value, done: readerDone } = await reader?.read() || { value: undefined, done: true }; done = readerDone; svgContent += decoder.decode(value, { stream: !done }); const dom = new DOMParser().parseFromString(svgContent, 'image/svg+xml'); const paths = Array.from(dom.querySelectorAll('path')); for (const path of paths.filter(path => !yieldPaths.includes(path))) { yield {path, svgElement: dom.documentElement}; yieldPaths.push(path); await new Promise(resolve => setTimeout(resolve, 200)); } } } export const SVG = c(function ({ src }) { const [svgPaths, setSvgPaths] = useState([] as SVGPathElement[]); const svgElement = useRef(); useEffect(() => { const loadSvg = async () => { for await (const { path, svgElement: elm } of fetchSvgInBatches(src! )) { if(elm.getAttribute("viewBox") && svgElement.current.getAttribute("viewBox") !== elm.getAttribute("viewBox") ) { svgElement.current.setAttribute("viewBox", elm.getAttribute("viewBox")); } styleSvgPath(path); setSvgPaths((prevPaths:SVGPathElement[]) => [...prevPaths, path]); } }; src && loadSvg(); }, [src]); const host = useHost(); useEffect(()=> { if(host.current?.shadowRoot) { console.log("svg:host.current.shadowRoot", host.current.shadowRoot) htmx.process(host.current.shadowRoot) } }, [host.current?.shadowRoot]) return html` ${svgPaths.map((path:SVGPathElement) => html`<${path} />`) } `; }, { props: { src: { type: String, reflect: true }, alt: { type: String, reflect: false }, }, styles: css` svg { width: 100%; height: 100%; display: block; z-index: 100; }, :host { display: inline-block; width: 100%; height: 100%; background-color: blue; } /* CSS Animation for Handwriting Effect */ .draw-path { animation: draw 1s ease-in-out forwards; } @keyframes draw { from { stroke-dashoffset: 100%; /* Start with the stroke completely hidden */ } to { stroke-dashoffset: 0%; /* Draw the stroke fully */ } } ` }); customElements.define("c-svg", SVG);