@@ -23,16 +117,15 @@ const demoCharts = Object.entries(chartsData as Record
-
+
-
-
-
-
-
-
-
+
+
+
+
+
+
@@ -56,28 +149,6 @@ const demoCharts = Object.entries(chartsData as Record
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
@@ -92,15 +163,35 @@ const demoCharts = Object.entries(chartsData as Record
{
- demoCharts.map(([chartId, chart]) => (
-
-
- {chart.name && }
-
-
-
-
-
+ sections.map((section) => (
+
+
+
{section.title}
+
{section.description}
+
+ {section.charts.map(([chartId, col, subtitle]) => (
+
+
+
+
+ {charts[chartId]?.name ?? capitalize(chartId.replace(/-/g, ' '))}
+ {subtitle}
+
+
+
+ {topLegend(chartId) && (
+
+ {topLegend(chartId)?.map((item) => (
+
+ ))}
+
+ )}
+
+
+
+
+ ))}
+
))
}
diff --git a/preview/pages/legend.astro b/preview/pages/legend.astro
index f1403c801..f5cc4fd8e 100644
--- a/preview/pages/legend.astro
+++ b/preview/pages/legend.astro
@@ -32,6 +32,13 @@ const requests = [
{ name: 'Rate limited', color: 'teal', value: '-' },
]
+const latency = [
+ { name: 'P99', color: 'primary', value: 124 },
+ { name: 'P95', color: 'azure', value: 76 },
+ { name: 'P75', color: 'green', value: 32 },
+ { name: 'P50', color: 'yellow', value: 10 },
+]
+
const storage = [
{ name: 'Documents', color: 'primary', value: '3.5 GB', width: 44 },
{ name: 'Media', color: 'info', value: '1.5 GB', width: 19 },
@@ -119,13 +126,128 @@ const storage = [
+
+
+
+ Large
+ Set size="lg" to put a big value under the label. Add unit for the unit and divided on the list for the lines.
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Under a stat
+ A legend row explains the split behind a single number.
+ Requests
+ 341.0k
+
+ {requests.map((item) => )}
+
+
+
+
+
+
+
+
+ Above a chart
+ Large items show the latest value of each series, so the chart needs no legend of its own.
+
+ {latency.map((item) => )}
+
+
+
+
+
+
+
+
+
+ Large vertical
+ Combine size="lg" with a vertical list for a side panel.
+
+ {storage.map((item) => )}
+
+
+
+
+
+
+
+
+ Large links
+ Large items work as links and buttons too. Mark the current one with active and a hidden series with off.
+
+
+
+
+
+
+
+
+
+
+
+
+
+ With units
+ Add unit to a regular item to show the unit after the value.
+
+ {latency.slice(0, 3).map((item) => )}
+
+
+
+
+
+
+
+
+ Without lines
+ Leave out divided when the items sit in a tight space or wrap to more rows.
+
+ {traffic.map((item) => )}
+
+
+
+
+
+
+
+
+ Loading
+ Use placeholder blocks for the label and the value while the data loads.
+
+ {
+ [1, 2].map(() => (
+
+
+
+
+
+
+
+ ))
+ }
+
+
+
+
+
-
-
-
+
+
+
@@ -170,20 +292,6 @@ const storage = [
-
-
-
- Under a stat
- A legend row explains the split behind a single number.
- Requests
- 341.0k
-
- {requests.map((item) => )}
-
-
-
-
-
diff --git a/screenshots/pages/legend-large-list.astro b/screenshots/pages/legend-large-list.astro
new file mode 100644
index 000000000..9415dcfa2
--- /dev/null
+++ b/screenshots/pages/legend-large-list.astro
@@ -0,0 +1,22 @@
+---
+// Large legend on its own, no card and no chart: a `divided` LegendList of
+// `size="lg"` items — colored dot and name, a big value with a small unit
+// below — with a line between them. The last item shows the `off` state of a
+// hidden series.
+import ScreenshotLayout from '../layouts/ScreenshotLayout.astro'
+import Legend from '@ui/Legend.astro'
+import LegendList from '@ui/LegendList.astro'
+
+const items = [
+ { name: 'Requests', color: 'primary', value: '1,234', unit: 'req/s' },
+ { name: 'Bandwidth', color: 'azure', value: '86.2', unit: 'MB/s' },
+ { name: 'Storage', color: 'purple', value: '56', unit: 'GB' },
+ { name: 'Warnings', color: 'orange', value: '128', off: true },
+]
+---
+
+
+
+ {items.map((item) => )}
+
+
diff --git a/screenshots/pages/legend-large-vertical.astro b/screenshots/pages/legend-large-vertical.astro
new file mode 100644
index 000000000..6b9519d5c
--- /dev/null
+++ b/screenshots/pages/legend-large-vertical.astro
@@ -0,0 +1,38 @@
+---
+// Large legend next to a donut: a traffic-sources card with the chart on the
+// left and a `vertical` list of `size="lg"` items on the right, each showing a
+// source and its share. `legend-donut` is the traffic donut from charts.json
+// with the ApexCharts legend left off, since the list replaces it.
+import ScreenshotLayout from '../layouts/ScreenshotLayout.astro'
+import Card from '@ui/Card.astro'
+import CardHeader from '@ui/CardHeader.astro'
+import CardBody from '@ui/CardBody.astro'
+import Legend from '@ui/Legend.astro'
+import LegendList from '@ui/LegendList.astro'
+import Chart from '@components/demo/Chart.astro'
+
+// Order and colors follow the `traffic-sources-donut` entry in charts.json.
+const sources = [
+ { name: 'Organic search', color: 'azure', value: 38 },
+ { name: 'Direct', color: 'primary', value: 27 },
+ { name: 'Social media', color: 'purple', value: 18 },
+ { name: 'Referral', color: 'orange', value: 12 },
+ { name: 'Email', color: 'green', value: 5 },
+]
+---
+
+
+
+
+
+
+
+
+
+
+ {sources.map((item) => )}
+
+
+
+
+
diff --git a/screenshots/pages/legend-large.astro b/screenshots/pages/legend-large.astro
new file mode 100644
index 000000000..6cf7ad3d3
--- /dev/null
+++ b/screenshots/pages/legend-large.astro
@@ -0,0 +1,42 @@
+---
+// Large legend above a chart: a read-latency card where each percentile is a
+// `size="lg"` button item — colored dot and name on the first line, the latest
+// value with its unit below — in a `divided` list. The fake cursor rests on the
+// active item so the tinted hover background is part of the picture. Every
+// series stays on, because the chart draws all four. `legend-latency` is a four-series line chart in
+// charts.json with the ApexCharts legend left off, since the items replace it.
+import ScreenshotLayout from '../layouts/ScreenshotLayout.astro'
+import Card from '@ui/Card.astro'
+import CardHeader from '@ui/CardHeader.astro'
+import CardBody from '@ui/CardBody.astro'
+import Legend from '@ui/Legend.astro'
+import LegendList from '@ui/LegendList.astro'
+import Chart from '@components/demo/Chart.astro'
+
+// Series order and colors follow the `legend-latency` entry in charts.json.
+const series = [
+ { name: 'P99', color: 'primary', value: 124 },
+ { name: 'P95', color: 'azure', value: 76, active: true },
+ { name: 'P75', color: 'green', value: 32 },
+ { name: 'P50', color: 'yellow', value: 10 },
+]
+---
+
+
+
+
+
+
+ {
+ series.map((item) => (
+
+ ))
+ }
+
+
+
+
+
diff --git a/shared/components/demo/Chart.astro b/shared/components/demo/Chart.astro
index 6a096d11d..ca35756cb 100644
--- a/shared/components/demo/Chart.astro
+++ b/shared/components/demo/Chart.astro
@@ -10,9 +10,11 @@ interface Props {
class?: string;
height?: number;
label: string;
+ /** overrides the `legend` flag from charts.json, e.g. `false` when the page renders its own legend */
+ legend?: boolean | undefined;
}
-const { chartId, id = chartId, size, class: classProp, height: heightProp, label } = Astro.props;
+const { chartId, id = chartId, size, class: classProp, height: heightProp, label, legend } = Astro.props;
const chartsData = charts as Record;
let data = chartsData[chartId];
@@ -23,6 +25,10 @@ if (data?.extend) {
data = { ...chartsData[data.extend], ...chartsData[chartId] };
}
+if (data && legend !== undefined) {
+ data = { ...data, legend };
+}
+
let height = heightProp ?? data?.height ?? 10;
if (size === 'sm') {
@@ -36,7 +42,7 @@ if (size === 'sm') {
// The :root custom-property block stays a plain string rendered via set:html — it's
// inert CSS text (no injection surface), not executable code.
const style = data ? chartStyle({ id, data }) : '';
-const { config, xFormatterExpr } = data ? chartConfig({ id, data, height }) : { config: undefined, xFormatterExpr: undefined };
+const { config, xFormatterExpr, yFormatterExpr } = data ? chartConfig({ id, data, height }) : { config: undefined, xFormatterExpr: undefined, yFormatterExpr: undefined };
const chartKey = `chart-${id}`;
const elementId = `chart-${id}`;
---
@@ -48,7 +54,7 @@ const elementId = `chart-${id}`;
-