deno.land / x / netzo@0.5.16 / components / blocks / plot.tsx
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180import { IS_BROWSER } from "$fresh/runtime.ts";import { createElement as h } from "preact";import { useEffect, useRef } from "preact/compat";import * as Plot from "../../deps/@observablehq/plot.ts";
export * from "../../deps/@observablehq/plot.ts";
export function usePlot<T = Plot.Data>(options: Plot.PlotOptions) { if (!IS_BROWSER) return;
const containerRef = useRef();
useEffect(() => { const plot = Plot.plot(options); // .replaceWith() to replace the server-side rendered plot with // the client-side (hydrated) plot entirely (avoids flickering) containerRef.current.replaceWith(plot); }, []);
return containerRef;}
/** * Isomorphic Plot component for rendering charts on the server and * hydrating them on the client for client-side interactivity. * * @example import * as Plot from "netzo/components/blocks/plot/plot.tsx"; * export default () => <Plot.Figure options={options} />; * * @param {Plot.PlotOptions} options - the plot options * @returns {JSX.Element} the plot figure */export function Figure({ options }: { options: Plot.PlotOptions }) { // // server-side render: uses a virtual Document implementation to generate HTML // if (!IS_BROWSER) return <PlotSSR options={options} />;
// // client-side render: uses a real DOM container and the usePlot() hook for mounting // const containerRef = usePlot(options); // return <figure ref={containerRef} />;
// client-side render: uses a real DOM container and the usePlot() hook for mounting const containerRef = usePlot(options); return ( <figure ref={containerRef}> <PlotSSR options={options} /> </figure> );}
function PlotSSR({ options }: { options: Plot.PlotOptions }) { return Plot.plot({ ...options, document: new Document() }).toHyperScript();}
// (virtual) Document implementation for SSR:
class Document { constructor() { this.documentElement = new Element(this, "html"); } createElementNS(namespace, tagName) { return new Element(this, tagName); } createElement(tagName) { return new Element(this, tagName); } createTextNode(value) { return new TextNode(this, value); } querySelector() { return null; } querySelectorAll() { return []; }}
class Style { static empty = new Style(); setProperty() {} removeProperty() {}}
class Element { constructor(ownerDocument, tagName) { this.ownerDocument = ownerDocument; this.tagName = tagName; this.attributes = {}; this.children = []; this.parentNode = null; } setAttribute(name, value) { this.attributes[name] = String(value); } setAttributeNS(namespace, name, value) { this.setAttribute(name, value); } getAttribute(name) { return this.attributes[name]; } getAttributeNS(name) { return this.getAttribute(name); } hasAttribute(name) { return name in this.attributes; } hasAttributeNS(name) { return this.hasAttribute(name); } removeAttribute(name) { delete this.attributes[name]; } removeAttributeNS(namespace, name) { this.removeAttribute(name); } addEventListener() { // ignored; interaction needs real DOM } removeEventListener() { // ignored; interaction needs real DOM } dispatchEvent() { // ignored; interaction needs real DOM } append(...children) { for (const child of children) { this.appendChild( child?.ownerDocument ? child : this.ownerDocument.createTextNode(child), ); } } appendChild(child) { this.children.push(child); child.parentNode = this; return child; } insertBefore(child, after) { if (after == null) { this.children.push(child); } else { const i = this.children.indexOf(after); if (i < 0) throw new Error("insertBefore reference node not found"); this.children.splice(i, 0, child); } child.parentNode = this; return child; } querySelector() { return null; } querySelectorAll() { return []; } set textContent(value) { this.children = [this.ownerDocument.createTextNode(value)]; } set style(value) { this.attributes.style = value; } get style() { return Style.empty; } toHyperScript() { return h( this.tagName, this.attributes, this.children.map((c) => c.toHyperScript()), ); }}
class TextNode { constructor(ownerDocument, nodeValue) { this.ownerDocument = ownerDocument; this.nodeValue = String(nodeValue); } toHyperScript() { return this.nodeValue; }}
Version Info