begin new custom chart implementation for js ui
This commit is contained in:
@@ -9,13 +9,54 @@
|
||||
What is left is only what is genuinely this app's: the brand.
|
||||
|
||||
The values below match the Go/WASM section's css/app.css on purpose — same
|
||||
Lora, same sky accent — so that crossing between /wasm and /js reads as two
|
||||
parts of ONE site rather than two demos that happen to share a domain. The
|
||||
Open Sans, same navy accent — so that crossing between /wasm and /js reads as
|
||||
two parts of ONE site rather than two demos that happen to share a domain. The
|
||||
two sections are built by completely different pipelines; they should not look
|
||||
like it.
|
||||
--------------------------------------------------------------------------- */
|
||||
|
||||
/* Open Sans — self-hosted (files in wwwroot/fonts). One variable file per subset
|
||||
carries weights 400–700 upright and italic. unicode-range keeps the browser to the
|
||||
one subset a glyph needs; font-display: swap paints the fallback first. The same four
|
||||
blocks live in the /wasm side's css/app.css so both front-ends share one typeface. */
|
||||
@font-face {
|
||||
font-family: "Open Sans";
|
||||
font-style: normal;
|
||||
font-weight: 400 700;
|
||||
font-display: swap;
|
||||
src: url("/fonts/opensans-latin-normal.woff2") format("woff2");
|
||||
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
|
||||
}
|
||||
@font-face {
|
||||
font-family: "Open Sans";
|
||||
font-style: normal;
|
||||
font-weight: 400 700;
|
||||
font-display: swap;
|
||||
src: url("/fonts/opensans-latin-ext-normal.woff2") format("woff2");
|
||||
unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
|
||||
}
|
||||
@font-face {
|
||||
font-family: "Open Sans";
|
||||
font-style: italic;
|
||||
font-weight: 400 700;
|
||||
font-display: swap;
|
||||
src: url("/fonts/opensans-latin-italic.woff2") format("woff2");
|
||||
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
|
||||
}
|
||||
@font-face {
|
||||
font-family: "Open Sans";
|
||||
font-style: italic;
|
||||
font-weight: 400 700;
|
||||
font-display: swap;
|
||||
src: url("/fonts/opensans-latin-ext-italic.woff2") format("woff2");
|
||||
unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
|
||||
}
|
||||
|
||||
@theme {
|
||||
/* The UI typeface — see the @font-face blocks above. This one line points the kit's
|
||||
font-sans utilities and the html rule below onto Open Sans. */
|
||||
--font-sans: "Open Sans", ui-sans-serif, system-ui, sans-serif;
|
||||
|
||||
/* The brand: navy throughout, a muted flag-navy. These are the SAME values the Go/WASM
|
||||
section's css/app.css sets, and under the same names — so the Layers menu, the
|
||||
sidebar highlight and the callouts are the same navy on both sides of the site rather
|
||||
|
||||
@@ -83,7 +83,7 @@ import { CrmTabGroup, CrmSubTabGroup } from "@ui/CrmTabs";
|
||||
import { Accordion, SingleAccordion } from "@ui/Accordion";
|
||||
import { SidebarNav } from "@ui/Sidebar";
|
||||
import { FuzzyMatch } from "@ui/FuzzyMatch";
|
||||
import ReactiveChart from "@ui/Chart";
|
||||
import { Chart, ChartSeries } from "@ui/Chart";
|
||||
import { ThemeToggle, useTheme } from "@ui/Theme";
|
||||
|
||||
import { Demo } from "../layout/Demo.tsx";
|
||||
@@ -1328,7 +1328,7 @@ const KIT_NAMES = [
|
||||
"AutoTable", "Accordion", "Alert", "Badge", "ButtonUI", "Calendar", "CellGrid", "FormCombobox",
|
||||
"DatePicker", "FormInput", "Menu", "Modal", "FormMultiSelect", "Popover", "PrettyTable",
|
||||
"SegmentedButtons", "FormSignaturePad", "TabGroup", "ThemeToggle", "ToastProvider", "ToggleSwitch",
|
||||
"Tooltip", "TutorialProvider", "SidebarNav", "ReactiveChart", "FuzzyMatch",
|
||||
"Tooltip", "TutorialProvider", "SidebarNav", "Chart", "FuzzyMatch",
|
||||
];
|
||||
|
||||
function Search() {
|
||||
@@ -1364,59 +1364,48 @@ function Search() {
|
||||
|
||||
// ---- charts --------------------------------------------------------------
|
||||
|
||||
const CHART_DAYS = ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"];
|
||||
|
||||
function Charts() {
|
||||
const [seed, setSeed] = createSignal(0);
|
||||
|
||||
const data = () => {
|
||||
const base = [42, 17, 63, 28, 55, 9, 71];
|
||||
const values = base.map((v) => (seed() === 0 ? v : Math.max(4, (v * 7 + seed() * 13) % 80)));
|
||||
return {
|
||||
labels: ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
|
||||
datasets: [
|
||||
{
|
||||
label: "Requests",
|
||||
data: values,
|
||||
// A literal, not var(--color-primary): chart.js paints a canvas, and a
|
||||
// canvas cannot read a CSS variable. So this is the one place in the
|
||||
// site that names the navy instead of asking for it — and it names the
|
||||
// DARK theme's lighter navy, which is the only one of the two that
|
||||
// reads on both a white page and a near-black one.
|
||||
backgroundColor: "#2b4f80",
|
||||
borderColor: "#2b4f80",
|
||||
},
|
||||
],
|
||||
};
|
||||
};
|
||||
// Two series over the same week. seed() reshuffles them so you can watch the SVG
|
||||
// move — no teardown, no new instance, just the marks that changed.
|
||||
const shuffle = (base: number[]) =>
|
||||
base.map((v) => (seed() === 0 ? v : Math.max(4, (v * 7 + seed() * 13) % 80)));
|
||||
const requests = (): ChartSeries => ({ name: "Requests", data: shuffle([42, 17, 63, 28, 55, 9, 71]) });
|
||||
const errors = (): ChartSeries => ({ name: "Errors", data: shuffle([8, 3, 12, 6, 9, 2, 14]) });
|
||||
const pieData = (): ChartSeries => ({ name: "Traffic", data: shuffle([40, 25, 20, 15, 8]) });
|
||||
const pieLabels = ["Direct", "Search", "Social", "Email", "Referral"];
|
||||
|
||||
return (
|
||||
<Section id="charts" title="Charts">
|
||||
<Prose>
|
||||
<code class="font-mono">@ui/Chart</code> wraps chart.js: it creates a chart against a canvas
|
||||
on mount and pushes new data through an effect, so changing the data updates the chart rather
|
||||
than rebuilding the page.
|
||||
<code class="font-mono">@ui/Chart</code> draws SVG — no charting library, nothing vendored, no{" "}
|
||||
<code class="font-mono"><canvas></code>. Because the picture is JSX it is already reactive:
|
||||
change <code class="font-mono">series</code> and Solid re-renders the marks that moved. There is
|
||||
no <code class="font-mono">update()</code> to call.
|
||||
</Prose>
|
||||
<Prose>
|
||||
This is the one component in the kit that is genuinely different on the other side. The Go
|
||||
port keeps the props shape for parity and renders an empty canvas — canvas drawing, a
|
||||
JavaScript charting library and mount lifecycles do not exist in the neutral Go runtime. Kjol
|
||||
Wasm Web draws SVG with go-chart instead, and draws it in the browser, in WebAssembly, exactly
|
||||
as this one draws to a canvas here. Same place, different library. Neither side pretends
|
||||
otherwise.
|
||||
Being SVG buys two things a canvas cannot. The marks name the theme tokens
|
||||
(<code class="font-mono">var(--color-chart-1)</code> …), so the palette inverts for dark mode with
|
||||
the rest of the site — a canvas paints pixels and cannot read a CSS variable. And every mark is a
|
||||
real element, so hovering shows a crosshair and one tooltip listing every series at that point.
|
||||
</Prose>
|
||||
|
||||
<Panel title="Bar and doughnut, from the same data">
|
||||
{/* The wrapper is h-full with maintainAspectRatio:false, so the PARENT must
|
||||
have an explicit height or the canvas collapses to nothing at all. */}
|
||||
<div class="grid gap-4 lg:grid-cols-12">
|
||||
<div class="h-72 lg:col-span-7">
|
||||
<ReactiveChart
|
||||
type="bar"
|
||||
data={data()}
|
||||
options={{ plugins: { legend: { display: false } } }}
|
||||
/>
|
||||
<Panel title="Grouped bars, a smooth area, a two-line series, and a donut — from one data set">
|
||||
<div class="grid gap-6 lg:grid-cols-12">
|
||||
<div class="lg:col-span-7">
|
||||
<Chart kind="bar" labels={CHART_DAYS} series={[requests(), errors()]} height={260} />
|
||||
</div>
|
||||
<div class="h-72 lg:col-span-5">
|
||||
<ReactiveChart type="doughnut" data={data()} />
|
||||
<div class="lg:col-span-5">
|
||||
<Chart kind="donut" labels={pieLabels} series={[pieData()]} height={260} />
|
||||
</div>
|
||||
<div class="lg:col-span-7">
|
||||
<Chart kind="area" curve="smooth" labels={CHART_DAYS} series={[requests()]} height={220} />
|
||||
</div>
|
||||
<div class="lg:col-span-5">
|
||||
<Chart kind="line" labels={CHART_DAYS} series={[requests(), errors()]} height={220} />
|
||||
</div>
|
||||
</div>
|
||||
<div class="mt-4 flex items-center gap-3">
|
||||
@@ -1424,17 +1413,18 @@ function Charts() {
|
||||
New data
|
||||
</ButtonUI>
|
||||
<span class="text-xs text-ink-muted">
|
||||
The chart updates in place — it is not torn down and rebuilt.
|
||||
Every chart updates in place — hover any of them for the tooltip.
|
||||
</span>
|
||||
</div>
|
||||
</Panel>
|
||||
|
||||
<AlertYellow header="It has to be vendored" class="mt-6">
|
||||
<code class="font-mono">Chart.tsx</code> imports chart.js at the top level and registers
|
||||
every controller on module load. An app that uses it must vendor chart.js and{" "}
|
||||
<code class="font-mono">@kurkle/color</code>, or the bundle does not degrade — it fails to
|
||||
evaluate, and you get an empty page and one line in the console.
|
||||
</AlertYellow>
|
||||
<Prose>
|
||||
One <code class="font-mono">kind</code> prop picks the form —{" "}
|
||||
<code class="font-mono">"line" | "area" | "bar" | "pie" | "donut"</code> — and{" "}
|
||||
<code class="font-mono">stacked</code>, <code class="font-mono">curve</code>,{" "}
|
||||
<code class="font-mono">palette</code> and <code class="font-mono">valueFormat</code> refine it.
|
||||
The width is measured from the container, so a chart fills whatever column you give it.
|
||||
</Prose>
|
||||
</Section>
|
||||
);
|
||||
}
|
||||
|
||||
6
go/cmd/kjol-web/frontend/vendor/vendor.json
vendored
6
go/cmd/kjol-web/frontend/vendor/vendor.json
vendored
@@ -5,12 +5,8 @@
|
||||
|
||||
"//3": "Only the files the bundler actually pins are vendored, not the whole npm packages — 3.5 MB rather than 62 MB of type definitions, CJS builds and documentation. If a subpath import is ever added that reaches outside dist/ or build/, this is the first place it will fail.",
|
||||
|
||||
"//4": "chart.js (and its @kurkle/color dependency) are here for the same reason: @ui/Chart imports chart.js at the top level and registers every controller on module load. Its wrapper is h-full with maintainAspectRatio:false, so the PARENT must have an explicit height or the canvas collapses to nothing.",
|
||||
|
||||
"entrypoints": {
|
||||
"pdf-lib": "pdf-lib/dist/pdf-lib.esm.js",
|
||||
"pdfjs-dist": "pdfjs-dist/build/pdf.mjs",
|
||||
"chart.js": "chart.js/dist/chart.js",
|
||||
"@kurkle/color": "@kurkle/color/dist/color.esm.js"
|
||||
"pdfjs-dist": "pdfjs-dist/build/pdf.mjs"
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user