enarratio-astro 0.4.1 → 0.4.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -7,6 +7,30 @@ every such change is listed here.
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.4.3] - 2026-11-07
11
+
12
+ ### Changed
13
+
14
+ - The `astro` peer dependency is `>=6` (was exactly `7.4.4`), as the README says. A site on any supported Astro version now installs
15
+ without a peer conflict; the version tested here stays pinned in the repository's dev dependencies.
16
+
17
+ ### Removed
18
+
19
+ - `parseChartBlock` is no longer exported from `enarratio-astro/markdown`. It was undocumented; use the
20
+ `remarkCharts` or `satteriCharts` plugin instead.
21
+
22
+ ## [0.4.2] - 2026-11-03
23
+
24
+ ### Changed
25
+
26
+ - Draws with Enarratio 0.7.3: charts no longer move the page when the enhancement layer loads. The redraw keeps the
27
+ height the server drawing showed, a legend entry has its button's box before it is a button, and an interactive
28
+ chart's figure holds the controls the layer adds (Show table, Play as sound, the downloads), inert until it
29
+ arrives.
30
+ - A chart that is not interactive leaves those controls out (Enarratio's `controls: false`), since no layer ever
31
+ makes them work. `renderChart` takes a second argument, `{ interactive }`, for this; it defaults to true, as
32
+ Enarratio does.
33
+
10
34
  ## [0.4.1] - 2026-11-03
11
35
 
12
36
  ### Changed
package/README.md CHANGED
@@ -36,6 +36,7 @@ const downloads = [
36
36
  { year: 2026, package: "Plugin", downloads: 2800 },
37
37
  ];
38
38
  ---
39
+
39
40
  <Chart
40
41
  type="line"
41
42
  data={downloads}
@@ -57,7 +58,7 @@ columns. The chart CSS is written once per page, however many charts it has.
57
58
  A code block whose language is `chart` becomes a chart: options, a line of three dashes, then the
58
59
  data.
59
60
 
60
- ~~~md
61
+ ````md
61
62
  ```chart
62
63
  type: bar
63
64
  x: team
@@ -72,7 +73,7 @@ Support,Closed,22
72
73
  Platform,Open,5
73
74
  Platform,Closed,31
74
75
  ```
75
- ~~~
76
+ ````
76
77
 
77
78
  Register the plugin for Astro's Markdown processor. Astro 7's default, Sätteri:
78
79
 
@@ -102,28 +103,28 @@ interactive chart on the page and works with view transitions.
102
103
 
103
104
  The component's props and a Markdown block's keys are the same:
104
105
 
105
- | Option | Used by | Notes |
106
- |---|---|---|
107
- | `type` | all | `bar` (default), `line`, `area` (stacked), `scatter` or `heatmap`. |
108
- | `data` | all | The data (component only; in Markdown it follows the dashes). Up to 5000 rows and 50 columns. |
109
- | `alt` | all | **Required.** What the chart shows and finds, in a sentence. It names the chart for screen readers. |
110
- | `title`, `subtitle` | all | Optional. Shown above the chart. |
111
- | `caption`, `notes` | all | Optional. The caption, under the chart, is for method or caveats; notes are a smaller line for definitions. |
112
- | `source`, `sourceHref` | all | Optional. Where the data came from, linked when `sourceHref` is set, beside a CSV download of the data. |
113
- | `byline` | all | Optional. Who made the chart. |
114
- | `x` | all | The column along the x axis. Column names match without regard to case. ISO dates make a time axis in line and area charts. |
115
- | `y` | all | The value column. Bar charts count rows when it is missing. For a heatmap, the row column. |
116
- | `series` | bar, line, area, scatter | Splits rows into series, told apart by color and by legend, labels or symbols. |
117
- | `value` | heatmap | The cell value column. |
118
- | `xLabel`, `yLabel` | all | Axis labels. |
119
- | `xUnit`, `yUnit` | all | Optional. The unit, shown after the axis label (`Sales (units)`) and in the table and summary. |
120
- | `layout` | bar | `stacked` (default) or `grouped`. |
121
- | `orientation` | bar | `vertical` (default) or `horizontal`. |
122
- | `scale` | line, scatter | `linear` (default) or `log`. |
123
- | `regression` | scatter | A least-squares line with its 95% band. |
124
- | `dataTable` | all | `details` (default; behind a "Data table" toggle) or `visually-hidden` (screen readers only). |
125
- | `id` | all | The figure's id. |
126
- | `interactive` | all | Adds the enhancement script. Off by default, so pages stay script-free. |
106
+ | Option | Used by | Notes |
107
+ | ---------------------- | ------------------------ | --------------------------------------------------------------------------------------------------------------------------- |
108
+ | `type` | all | `bar` (default), `line`, `area` (stacked), `scatter` or `heatmap`. |
109
+ | `data` | all | The data (component only; in Markdown it follows the dashes). Up to 5000 rows and 50 columns. |
110
+ | `alt` | all | **Required.** What the chart shows and finds, in a sentence. It names the chart for screen readers. |
111
+ | `title`, `subtitle` | all | Optional. Shown above the chart. |
112
+ | `caption`, `notes` | all | Optional. The caption, under the chart, is for method or caveats; notes are a smaller line for definitions. |
113
+ | `source`, `sourceHref` | all | Optional. Where the data came from, linked when `sourceHref` is set, beside a CSV download of the data. |
114
+ | `byline` | all | Optional. Who made the chart. |
115
+ | `x` | all | The column along the x axis. Column names match without regard to case. ISO dates make a time axis in line and area charts. |
116
+ | `y` | all | The value column. Bar charts count rows when it is missing. For a heatmap, the row column. |
117
+ | `series` | bar, line, area, scatter | Splits rows into series, told apart by color and by legend, labels or symbols. |
118
+ | `value` | heatmap | The cell value column. |
119
+ | `xLabel`, `yLabel` | all | Axis labels. |
120
+ | `xUnit`, `yUnit` | all | Optional. The unit, shown after the axis label (`Sales (units)`) and in the table and summary. |
121
+ | `layout` | bar | `stacked` (default) or `grouped`. |
122
+ | `orientation` | bar | `vertical` (default) or `horizontal`. |
123
+ | `scale` | line, scatter | `linear` (default) or `log`. |
124
+ | `regression` | scatter | A least-squares line with its 95% band. |
125
+ | `dataTable` | all | `details` (default; behind a "Data table" toggle) or `visually-hidden` (screen readers only). |
126
+ | `id` | all | The figure's id. |
127
+ | `interactive` | all | Adds the enhancement script. Off by default, so pages stay script-free. |
127
128
 
128
129
  A chart that cannot be drawn (a column that is not in the data, text in a number column, a missing
129
130
  `alt`) is replaced by a short note saying why, and the reason is logged, so one mistake never
package/dist/chart.d.ts CHANGED
@@ -63,10 +63,19 @@ export type ChartResult = {
63
63
  readonly ok: false;
64
64
  readonly error: string;
65
65
  };
66
+ /** How a chart is drawn, beside what it shows. */
67
+ export interface RenderOptions {
68
+ /**
69
+ * Whether the page enhances the chart (`<Chart interactive>`). An enhanced chart's figure holds
70
+ * the controls the enhancement layer adds, inert until it arrives, so the page does not move;
71
+ * a chart that is never enhanced leaves them out. Defaults to true, as Enarratio does.
72
+ */
73
+ readonly interactive?: boolean | undefined;
74
+ }
66
75
  /**
67
76
  * Draws a chart. Bar, line and area charts come from `enarratio/lite`, which builds the SVG as a
68
77
  * string with no DOM, the cheapest render on a Worker; scatter plots and heatmaps load
69
78
  * `enarratio/plot` (Observable Plot and linkedom) only when a page has one. Never throws: a chart
70
79
  * that cannot be drawn returns the reason instead, so one mistake cannot break a page.
71
80
  */
72
- export declare function renderChart(spec: ChartSpec): Promise<ChartResult>;
81
+ export declare function renderChart(spec: ChartSpec, options?: RenderOptions): Promise<ChartResult>;
package/dist/chart.js CHANGED
@@ -22,8 +22,8 @@ function numeric(table, name) {
22
22
  if (bad)
23
23
  throw new Error(`The column "${name}" should hold numbers, but has "${bad[name]}".`);
24
24
  }
25
- /** The options every chart type shares: text, the data table and the id. */
26
- function figure(spec) {
25
+ /** The options every chart type shares: text, the data table, the id and the pending controls. */
26
+ function figure(spec, options) {
27
27
  const alt = text(spec.alt);
28
28
  if (!alt)
29
29
  throw new Error("Add a description (alt): one sentence on what the chart shows.");
@@ -43,14 +43,15 @@ function figure(spec) {
43
43
  ...(source ? { source: sourceHref ? { name: source, href: sourceHref } : source } : {}),
44
44
  ...(id ? { id } : {}),
45
45
  dataTable: spec.dataTable === "visually-hidden" ? "visually-hidden" : "details",
46
+ ...(options.interactive === false ? { controls: false } : {}),
46
47
  };
47
48
  }
48
- async function draw(spec) {
49
+ async function draw(spec, options) {
49
50
  const type = (text(spec.type) ?? "bar").toLowerCase();
50
51
  if (!CHART_TYPES.includes(type)) {
51
52
  throw new Error(`There is no chart type "${spec.type}". Use one of: ${CHART_TYPES.join(", ")}.`);
52
53
  }
53
- const common = figure(spec);
54
+ const common = figure(spec, options);
54
55
  const table = typeof spec.data === "string" ? parseTable(spec.data) : tableFromRows(spec.data);
55
56
  const x = column(table, spec.x, "x axis");
56
57
  const seriesName = text(spec.series) ? column(table, spec.series, "series") : undefined;
@@ -109,9 +110,9 @@ async function draw(spec) {
109
110
  * `enarratio/plot` (Observable Plot and linkedom) only when a page has one. Never throws: a chart
110
111
  * that cannot be drawn returns the reason instead, so one mistake cannot break a page.
111
112
  */
112
- export async function renderChart(spec) {
113
+ export async function renderChart(spec, options = {}) {
113
114
  try {
114
- return { ok: true, html: await draw(spec) };
115
+ return { ok: true, html: await draw(spec, options) };
115
116
  }
116
117
  catch (error) {
117
118
  return { ok: false, error: error instanceof Error ? error.message : String(error) };
package/dist/data.d.ts CHANGED
@@ -5,9 +5,6 @@ export interface Table {
5
5
  readonly columns: readonly string[];
6
6
  readonly rows: ReadonlyArray<Readonly<Record<string, Cell>>>;
7
7
  }
8
- /** Limits that keep one chart's render bounded, whatever an editor pastes. */
9
- export declare const MAX_ROWS = 5000;
10
- export declare const MAX_COLUMNS = 50;
11
8
  /**
12
9
  * Parses the data an editor pasted or typed: CSV, semicolon-separated or tab-separated text
13
10
  * whose first row names the columns. Throws an Error whose message an editor can act on.
package/dist/data.js CHANGED
@@ -1,6 +1,6 @@
1
1
  /** Limits that keep one chart's render bounded, whatever an editor pastes. */
2
- export const MAX_ROWS = 5000;
3
- export const MAX_COLUMNS = 50;
2
+ const MAX_ROWS = 5000;
3
+ const MAX_COLUMNS = 50;
4
4
  const NUMBER = /^[+-]?(?:\d+\.?\d*|\.\d+)(?:e[+-]?\d+)?$/i;
5
5
  const GROUPED_NUMBER = /^[+-]?\d{1,3}(?:,\d{3})+(?:\.\d+)?$/;
6
6
  /** Reads a cell: numbers (including "1,234") become numbers, an empty cell `null`, the rest text. */
package/dist/index.d.ts CHANGED
@@ -5,7 +5,7 @@
5
5
  *
6
6
  * @packageDocumentation
7
7
  */
8
- export { CHART_TYPES, type ChartResult, type ChartSpec, type ChartType, renderChart, } from "./chart.js";
8
+ export { CHART_TYPES, type ChartResult, type ChartSpec, type ChartType, type RenderOptions, renderChart, } from "./chart.js";
9
9
  export { type Cell, parseTable, type Table, tableFromRows } from "./data.js";
10
10
  export { chartMarkup } from "./figure.js";
11
11
  export { chartStyles, type StyleOptions, type Styles } from "./styles.js";
@@ -1,23 +1,6 @@
1
- import { type ChartSpec } from "./chart.js";
2
1
  import { type StyleOptions } from "./styles.js";
3
2
  /** Options for the Markdown plugins: how charts are styled. */
4
3
  export type MarkdownChartOptions = StyleOptions;
5
- /**
6
- * Reads a `chart` code block: `key: value` lines, a line of three dashes, then the data.
7
- *
8
- * ```chart
9
- * type: line
10
- * x: month
11
- * y: visitors
12
- * alt: Monthly visitors rise from 820 to 1,190.
13
- * ---
14
- * month,visitors
15
- * 2026-01-01,820
16
- * ```
17
- */
18
- export declare function parseChartBlock(source: string): ChartSpec & {
19
- readonly interactive: boolean;
20
- };
21
4
  /** The parts of a Markdown code node the plugins read. */
22
5
  interface CodeNode {
23
6
  readonly type: "code";
package/dist/markdown.js CHANGED
@@ -41,7 +41,7 @@ const KEYS = new Set([
41
41
  * 2026-01-01,820
42
42
  * ```
43
43
  */
44
- export function parseChartBlock(source) {
44
+ function parseChartBlock(source) {
45
45
  const lines = source.split(/\r?\n/);
46
46
  const rule = lines.findIndex((line) => line.trim() === "---");
47
47
  if (rule === -1)
@@ -69,7 +69,7 @@ async function chartHtml(source, first, options) {
69
69
  let markup;
70
70
  try {
71
71
  const { interactive, ...spec } = parseChartBlock(source);
72
- const result = await renderChart(spec);
72
+ const result = await renderChart(spec, { interactive });
73
73
  if (!result.ok)
74
74
  console.warn(`[enarratio-astro] Markdown chart not drawn: ${result.error}`);
75
75
  markup = chartMarkup(result, interactive);
package/package.json CHANGED
@@ -1,27 +1,33 @@
1
1
  {
2
2
  "name": "enarratio-astro",
3
- "version": "0.4.1",
3
+ "version": "0.4.3",
4
4
  "description": "Accessible charts for Astro: a Chart component and charts in Markdown, drawn on the server by Enarratio",
5
5
  "keywords": [
6
+ "accessibility",
6
7
  "astro",
7
8
  "astro-component",
8
- "withastro",
9
9
  "charts",
10
- "accessibility",
10
+ "enarratio",
11
11
  "markdown",
12
- "enarratio"
12
+ "withastro"
13
13
  ],
14
14
  "homepage": "https://github.com/DrDustinEdwards/astro/tree/main/packages/enarratio-astro#readme",
15
15
  "bugs": {
16
16
  "url": "https://github.com/DrDustinEdwards/astro/issues"
17
17
  },
18
+ "license": "MIT",
19
+ "author": "Dustin Edwards (https://dustinedwards.info)",
18
20
  "repository": {
19
21
  "type": "git",
20
22
  "url": "git+https://github.com/DrDustinEdwards/astro.git",
21
23
  "directory": "packages/enarratio-astro"
22
24
  },
23
- "license": "MIT",
24
- "author": "Dustin Edwards (https://dustinedwards.info)",
25
+ "files": [
26
+ "dist",
27
+ "src",
28
+ "CHANGELOG.md",
29
+ "README.md"
30
+ ],
25
31
  "type": "module",
26
32
  "sideEffects": false,
27
33
  "exports": {
@@ -37,30 +43,24 @@
37
43
  "./Enhance.astro": "./src/Enhance.astro",
38
44
  "./package.json": "./package.json"
39
45
  },
40
- "files": [
41
- "dist",
42
- "src",
43
- "CHANGELOG.md",
44
- "README.md"
45
- ],
46
- "engines": {
47
- "node": ">=22.22"
46
+ "publishConfig": {
47
+ "access": "public",
48
+ "tag": "latest"
48
49
  },
49
50
  "scripts": {
50
51
  "build": "tsc -p tsconfig.build.json",
51
52
  "test": "vitest run",
52
53
  "prepare": "npm run build"
53
54
  },
54
- "peerDependencies": {
55
- "astro": "7.4.4"
56
- },
57
55
  "dependencies": {
58
56
  "@observablehq/plot": "0.6.17",
59
- "enarratio": "0.7.2",
57
+ "enarratio": "0.7.3",
60
58
  "linkedom": "0.18.14"
61
59
  },
62
- "publishConfig": {
63
- "access": "public",
64
- "tag": "latest"
60
+ "peerDependencies": {
61
+ "astro": ">=6"
62
+ },
63
+ "engines": {
64
+ "node": ">=22.22"
65
65
  }
66
66
  }
package/src/Chart.astro CHANGED
@@ -21,7 +21,7 @@ interface Props extends ChartSpec, StyleOptions {
21
21
  }
22
22
 
23
23
  const { interactive = false, styles, theme, stylesheetOptions, ...spec } = Astro.props;
24
- const result = await renderChart(spec);
24
+ const result = await renderChart(spec, { interactive });
25
25
  const css = firstOnPage(Astro.request) ? chartStyles({ styles, theme, stylesheetOptions }) : "";
26
26
  if (!result.ok) console.warn(`[enarratio-astro] chart ${spec.id ?? ""} not drawn: ${result.error}`);
27
27
  ---
package/src/Enhance.astro CHANGED
@@ -11,7 +11,9 @@
11
11
  import { enhance } from "enarratio/enhance";
12
12
 
13
13
  function run() {
14
- for (const chart of document.querySelectorAll<HTMLElement>("[data-enarratio-interactive-chart]")) {
14
+ for (const chart of document.querySelectorAll<HTMLElement>(
15
+ "[data-enarratio-interactive-chart]",
16
+ )) {
15
17
  enhance(chart);
16
18
  }
17
19
  }
package/src/chart.ts CHANGED
@@ -88,8 +88,18 @@ function numeric(table: Table, name: string): void {
88
88
  if (bad) throw new Error(`The column "${name}" should hold numbers, but has "${bad[name]}".`);
89
89
  }
90
90
 
91
- /** The options every chart type shares: text, the data table and the id. */
92
- function figure(spec: ChartSpec) {
91
+ /** How a chart is drawn, beside what it shows. */
92
+ export interface RenderOptions {
93
+ /**
94
+ * Whether the page enhances the chart (`<Chart interactive>`). An enhanced chart's figure holds
95
+ * the controls the enhancement layer adds, inert until it arrives, so the page does not move;
96
+ * a chart that is never enhanced leaves them out. Defaults to true, as Enarratio does.
97
+ */
98
+ readonly interactive?: boolean | undefined;
99
+ }
100
+
101
+ /** The options every chart type shares: text, the data table, the id and the pending controls. */
102
+ function figure(spec: ChartSpec, options: RenderOptions) {
93
103
  const alt = text(spec.alt);
94
104
  if (!alt) throw new Error("Add a description (alt): one sentence on what the chart shows.");
95
105
  const title = text(spec.title);
@@ -111,17 +121,18 @@ function figure(spec: ChartSpec) {
111
121
  ...(id ? { id } : {}),
112
122
  dataTable:
113
123
  spec.dataTable === "visually-hidden" ? ("visually-hidden" as const) : ("details" as const),
124
+ ...(options.interactive === false ? { controls: false } : {}),
114
125
  };
115
126
  }
116
127
 
117
- async function draw(spec: ChartSpec): Promise<string> {
128
+ async function draw(spec: ChartSpec, options: RenderOptions): Promise<string> {
118
129
  const type = (text(spec.type) ?? "bar").toLowerCase();
119
130
  if (!CHART_TYPES.includes(type as ChartType)) {
120
131
  throw new Error(
121
132
  `There is no chart type "${spec.type}". Use one of: ${CHART_TYPES.join(", ")}.`,
122
133
  );
123
134
  }
124
- const common = figure(spec);
135
+ const common = figure(spec, options);
125
136
  const table = typeof spec.data === "string" ? parseTable(spec.data) : tableFromRows(spec.data);
126
137
  const x = column(table, spec.x, "x axis");
127
138
  const seriesName = text(spec.series) ? column(table, spec.series, "series") : undefined;
@@ -130,9 +141,8 @@ async function draw(spec: ChartSpec): Promise<string> {
130
141
  const log = spec.scale === "log" ? ({ yType: "log" } as const) : {};
131
142
 
132
143
  // Enarratio types its field options by the row type; parsed rows are only known at run time,
133
- // so the options are checked here and passed on as plain objects.
134
- // biome-ignore lint/suspicious/noExplicitAny: see above
135
- type Loose = any;
144
+ // so every row is a record of cells and the columns are checked here.
145
+ type Loose = Record<string, Cell>;
136
146
 
137
147
  switch (type as ChartType) {
138
148
  case "bar": {
@@ -186,9 +196,12 @@ async function draw(spec: ChartSpec): Promise<string> {
186
196
  * `enarratio/plot` (Observable Plot and linkedom) only when a page has one. Never throws: a chart
187
197
  * that cannot be drawn returns the reason instead, so one mistake cannot break a page.
188
198
  */
189
- export async function renderChart(spec: ChartSpec): Promise<ChartResult> {
199
+ export async function renderChart(
200
+ spec: ChartSpec,
201
+ options: RenderOptions = {},
202
+ ): Promise<ChartResult> {
190
203
  try {
191
- return { ok: true, html: await draw(spec) };
204
+ return { ok: true, html: await draw(spec, options) };
192
205
  } catch (error) {
193
206
  return { ok: false, error: error instanceof Error ? error.message : String(error) };
194
207
  }
package/src/data.ts CHANGED
@@ -8,8 +8,8 @@ export interface Table {
8
8
  }
9
9
 
10
10
  /** Limits that keep one chart's render bounded, whatever an editor pastes. */
11
- export const MAX_ROWS = 5000;
12
- export const MAX_COLUMNS = 50;
11
+ const MAX_ROWS = 5000;
12
+ const MAX_COLUMNS = 50;
13
13
 
14
14
  const NUMBER = /^[+-]?(?:\d+\.?\d*|\.\d+)(?:e[+-]?\d+)?$/i;
15
15
  const GROUPED_NUMBER = /^[+-]?\d{1,3}(?:,\d{3})+(?:\.\d+)?$/;
package/src/index.ts CHANGED
@@ -10,6 +10,7 @@ export {
10
10
  type ChartResult,
11
11
  type ChartSpec,
12
12
  type ChartType,
13
+ type RenderOptions,
13
14
  renderChart,
14
15
  } from "./chart.js";
15
16
  export { type Cell, parseTable, type Table, tableFromRows } from "./data.js";
package/src/markdown.ts CHANGED
@@ -46,7 +46,7 @@ const KEYS = new Set([
46
46
  * 2026-01-01,820
47
47
  * ```
48
48
  */
49
- export function parseChartBlock(source: string): ChartSpec & { readonly interactive: boolean } {
49
+ function parseChartBlock(source: string): ChartSpec & { readonly interactive: boolean } {
50
50
  const lines = source.split(/\r?\n/);
51
51
  const rule = lines.findIndex((line) => line.trim() === "---");
52
52
  if (rule === -1)
@@ -78,7 +78,7 @@ async function chartHtml(
78
78
  let markup: string;
79
79
  try {
80
80
  const { interactive, ...spec } = parseChartBlock(source);
81
- const result = await renderChart(spec);
81
+ const result = await renderChart(spec, { interactive });
82
82
  if (!result.ok) console.warn(`[enarratio-astro] Markdown chart not drawn: ${result.error}`);
83
83
  markup = chartMarkup(result, interactive);
84
84
  } catch (error) {