@martinomagnifico/tablechart 1.1.2 → 1.2.0

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
@@ -1,5 +1,57 @@
1
1
  # Changelog
2
2
 
3
+ ## [1.2.0] - 2026-10-28
4
+
5
+ ### Added
6
+
7
+ - Summaries! They need a separate import from `@martinomagnifico/tablechart/summary`. Summaries are important for screen readers and print.
8
+ - A line over columns, with `data-chart="line"` on a column header.
9
+ - `data-chart-values` and the `values` option: which values a line shows. A long line shows only its key values.
10
+ - Notes: a column with `data-note`, shown as a line at its row and in the tooltip.
11
+ - A `quadrant` annotation for scatter charts, and one step for a scatter with `data-fragment`.
12
+ - `--tablechart-bubble-min`, so the smallest bubble stays visible.
13
+
14
+ ### Changed
15
+
16
+ - Values on a line keep clear of the line, the points and each other, if possible.
17
+ - Line and area charts have a tick above each name. `data-chart-ticks="false"` turns them off.
18
+ - Scatter markers have an edge in the background colour, so they stand apart from lines and each other.
19
+ - Bars put their numbers after the bar by default, as columns put them above. `data-chart-labels="inside"` puts them inside, as on columns.
20
+ - Badges are rounded boxes instead of ellipses. They take less room.
21
+ - A few columns or bars start one after the other again, and their numbers come when the column is nearly grown. Many columns still grow almost together.
22
+
23
+ ### Fixed
24
+
25
+ - Holes in a line come with the line, not after the build.
26
+ - A legend box is no longer shown empty before its names.
27
+ - Triangle markers are centred on their point.
28
+ - Names of a long line no longer wait seconds after the build.
29
+ - Grouped columns and bars no longer grow after the first resize.
30
+
31
+
32
+
33
+ ## [1.1.3] - 2026-10-26
34
+
35
+ ### Changed
36
+
37
+ - Fixes charts that should be shown directly. Labels and annotations now also show immediately.
38
+ - Trend and bracket lines stop at their badge from the start. Before, they ran through it until the chart was built.
39
+ - Gridlines leave a gap for a badge, as they do for text. The gap appears with the badge.
40
+ - Badge backgrounds no longer fade in. A fading background showed as a grey box over the marks.
41
+ - The mask under a badge goes on when the marks under it are done. Before, it waited for the whole chart.
42
+
43
+ ### Added
44
+
45
+ - `atOnce` in `setShown(state, shown, atOnce)` from `@martinomagnifico/tablechart/core`. It shows or hides a chart without transitions, with `AT_ONCE_CLASS`.
46
+ - `numbers: false` for a chart type without numbers on the chart, such as a scatter. Its numbers then stay out of the layout.
47
+
48
+ ### Fixed
49
+
50
+ - Legend keys no longer fade in when printing or when a chart is shown at once.
51
+ - Removed a layout for hidden scatter plot labels to increase performance.
52
+
53
+
54
+
3
55
  ## [1.1.2] - 2026-10-24
4
56
 
5
57
  ### Changed
package/README.md CHANGED
@@ -12,14 +12,15 @@
12
12
  Tablechart makes simple charts from a table: an HTML table, a Markdown table or a JSON file. You write the table, Tablechart shows it as a chart.
13
13
 
14
14
  * **Chart types:** column, bar, line, area, waterfall and donut, and scatter as an additional type
15
- * **The table stays:** it is hidden from sight, but screen readers, search engines and translation scripts still read it. Without JavaScript, the table is all there is.
16
- * **Real text:** names and numbers are HTML, so they use the font of the page and do not scale with the drawing.
17
- * **CSS for the looks:** colours, sizes and animation are CSS variables. Light and dark mode follow the page.
15
+ * **The table:** the table is hidden from sight, but screen readers, search engines and translation scripts still read it.
16
+ * **Real text:** names and numbers are HTML, so they use the font of the page. *No more Times New Roman labels.* Text does not scale, but just uses the font size of the page or a size you define.
17
+ * **CSS styling:** colours, sizes and animation are CSS variables. Light and dark mode follow the page.
18
18
  * **Annotations:** badges, brackets, trend arrows, reference lines and a break in the scale
19
19
  * **Tooltips:** when a chart leaves numbers out, point at a row, or tap it, to see all its values
20
- * **Print and PDF:** every chart prints whole, and the table can be shown under a chart that leaves numbers out
20
+ * **Summary:** sometimes a table is just too large, for example when a screen reader talks through it, You try to remember the number that was said 4 sentences ago. In print too. A summary deals with that. See [Summary](https://martinomagnifico.github.io/tablechart/features/summary.html).
21
+ * **Print and PDF:** tablechart in a PDF flow builds the charts immediately
21
22
  * **Step by step:** show the rows one at a time, with Previous and Next buttons
22
- * **Animation:** a chart builds when it scrolls into view
23
+ * **Animation:** a chart can build when it scrolls into view
23
24
 
24
25
  ----
25
26
 
@@ -139,4 +140,4 @@ Tablechart is free and open source. If it saves you time, consider [sponsoring m
139
140
 
140
141
  ## License
141
142
 
142
- MIT © [Martinomagnifico](https://github.com/martinomagnifico)
143
+ MIT © [Martinomagnifico](https://github.com/martinomagnifico)**
@@ -1 +1 @@
1
- !function(t){var e=["circle","triangle","square","diamond"],o=["right","left","above","below","right","left","right","left"],n=t=>{const e=t.flatMap((t,e)=>t.group?[]:[e]);return{x:e[0],y:e[1],size:e[2],group:t.findIndex(t=>t.group)}},r=(t,e)=>e<0?[]:[...new Set(t.map(t=>t.cells[e]?.raw??""))],a=(t,e,o)=>t.cells[e]?.missing||t.cells[o]?.missing?null:`${t.cells[e]?.value} ${t.cells[o]?.value}`,s={aspect:.62,categories:"beside",columns:!0,axes:!0,text({figure:o,plot:s,rows:c,series:i,config:l}){const{x:u,y:f,size:h,group:d}=n(i),g=s.querySelector(":scope > .tablechart-categories")?.children??[],p=new Map;c.forEach((t,e)=>{const o=a(t,u,f);o&&(p.get(o)?.push(e)??p.set(o,[e]))});const M=t=>void 0===h?0:Math.abs(c[t].cells[h]?.value??0);for(const t of p.values()){if(t.length<2)continue;const e=t.reduce((t,e)=>M(e)>M(t)?e:t);for(const o of t){const n=g[o];o===e?n?.setAttribute("data-more",String(t.length-1)):n?.setAttribute("data-same","")}}const b=r(c,d);if(b.length<2&&void 0===h)return;const m=document.createElement("div");m.className=t.LEGEND_CLASS,m.setAttribute("aria-hidden","true"),m.inert=!0;const v=Array.from(o.querySelectorAll(`tbody tr > td:nth-of-type(${d+1})`)),x=(e,o)=>{const n=(0,t.htmlEl)(t.NAME_CLASS,m);return n.dataset.place="legend",n.dataset.line="none",n.textContent=e,o&&l.langattribute&&n.setAttribute(l.langattribute,o),n};b.length>1&&b.forEach((t,o)=>{const n=l.langattribute?v.find(e=>e.textContent?.trim()===t)?.getAttribute(l.langattribute):null,r=x(t,n);r.dataset.marker=e[o%e.length],r.style.setProperty("--n",String(o)),r.style.setProperty("--palette",`var(--tablechart-color-${o+1})`)}),void 0!==h&&(x(i[h].name,i[h].key).dataset.marker="size"),o.appendChild(m)},draw(a){const{rows:s,series:c,x:i,y:l,frame:u,unit:f,svg:h,categories:d,seriesValues:g,height:p,config:M,plot:b}=a;if(!i||!u)return;const m=n(c),v=r(s,m.group),x=v.length>1;for(const t of g.flat())t.dataset.place="none";const w=(a.length("--tablechart-marker-size","10px")??10*f)/2,y=(a.length("--tablechart-bubble-max","24px")??24*f)/2,$=t=>void 0===m.size?void 0:t.cells[m.size],S=(0,t.largest)(s.map(t=>Math.abs($(t)?.value??0))),z=s.flatMap((t,e)=>{const o=t.cells[m.x],n=t.cells[m.y];if(!o||!n||o.missing||n.missing)return[];const r=$(t),a=r&&!r.missing&&S>0?Math.max(.6*w,Math.sqrt(Math.abs(r.value)/S)*y):w,s=x?v.indexOf(t.cells[m.group]?.raw??""):-1;return[{i:e,px:i(o.value),py:l(n.value),r:a,g:s}]}),k=Math.min(z.length-1,6);b.style.setProperty("--count",String(k+1));const q=new Map([...z].sort((t,e)=>t.px-e.px||t.i-e.i).map((e,o)=>[e.i,k?(0,t.short)(o*k/(z.length-1)):0])),A=[...z].sort((t,e)=>e.r-t.r||t.i-e.i);for(const o of A){const n=x?e[o.g%e.length]:"circle";h.appendChild((0,t.markerEl)(n,o.px,o.py,o.r,{class:t.POINT_CLASS,"data-row":o.i+1,"data-marker":n,style:`--i:${q.get(o.i)}; ${x?`--n:${o.g}; --palette:var(--tablechart-color-${o.g+1})`:""}`}))}const T=b.parentElement?.getAttribute("data-chart-names"),V=null==T?M.names:"false"!==T,j=4*f,B=[u[0]+1.5*j,u[1],u[2]-1.5*j,u[3]-1.5*j],[C,D,E,F]=B,G=A.map(({i:e,px:o,py:n,r:r})=>{if(d[e].hasAttribute("data-same"))return[];const{width:a,height:s}=(0,t.sizeOf)(d[e]),c=a*f,i=s*f,l=.1*i,u=.7*r+j/2,h=(t,e)=>[Math.max(C,Math.min(t,C+E-c)),Math.max(D,Math.min(e,D+F-i))+l,c,i-2*l];return[h(o+r+j,n-i/2),h(o-r-j-c,n-i/2),h(o-c/2,n-r-j-i),h(o-c/2,n+r+j),h(o+u,n-u-i),h(o-u-c,n-u-i),h(o+u,n+u),h(o-u-c,n+u)]}),H=V?(0,t.avoidCollisions)(G,z.map(({px:t,py:e,r:o})=>[t-o,e-o,2*o,2*o]),B):A.map(()=>null);for(const t of d)t.hidden=!0,t.dataset.missing="";A.forEach((e,n)=>{const r=d[e.i],a=H[n];if(r.hidden=!1,delete r.dataset.missing,r.style.setProperty("--i",String(q.get(e.i))),r.toggleAttribute("data-left-out",null===a),null===a)return;r.dataset.side=o[a];const[s,c,,i]=G[n][a];(0,t.placeAt)(r,s,c-((0,t.sizeOf)(r).height*f-i)/2,t.VIEWBOX_WIDTH,p)}),a.rowAt=(t,e)=>{let o=null,n=6*f;for(const r of z){const a=Math.max(Math.hypot(r.px-t*f,r.py-e*f)-r.r,0);(a<n||a===n&&o&&r.r<o.r)&&(n=a,o=r)}return o?o.i:null}},tooltip(t,e){const{x:o,y:r}=n(t.series),s=a(t.rows[e],o,r),c=t.rows.flatMap((t,n)=>n===e||null!==s&&a(t,o,r)===s?[n]:[]),i=c.filter(e=>t.categories[e]?.hasAttribute("data-more"));return[...i,...c.filter(t=>!i.includes(t))].map(e=>({name:t.categories[e]?.textContent??"",value:""}))}};window.Tablechart.addType("scatter",s)}(Tablechart.kit);
1
+ !function(t){var o=`${t.QUADRANT_CLASS}-name`,n=["top left","top right","bottom left","bottom right"],r=(t,o)=>{if(!t)return null;if("mean"===t)return o.length?o.reduce((t,o)=>t+o,0)/o.length:null;if("median"===t){const t=[...o].sort((t,o)=>t-o),n=t.length/2;return t.length?t.length%2?t[Math.floor(n)]:(t[n-1]+t[n])/2:null}const n=Number.parseFloat(t);return Number.isFinite(n)?n:null},e=["circle","triangle","square","diamond"],a=["right","left","above","below","right","left","right","left"],s=t=>{const o=t.flatMap((t,o)=>t.group?[]:[o]);return{x:o[0],y:o[1],size:o[2],group:t.findIndex(t=>t.group)}},c=(t,o)=>o<0?[]:[...new Set(t.map(t=>t.cells[o]?.raw??""))],i=(t,o,n)=>t.cells[o]?.missing||t.cells[n]?.missing?null:`${t.cells[o]?.value} ${t.cells[n]?.value}`,l={aspect:.62,categories:"beside",columns:!0,axes:!0,numbers:!1,annotations:{quadrant:{create(r,e){for(const a of r.querySelectorAll("[data-corner]")){const r=(a.getAttribute("data-corner")??"").toLowerCase().split(/\s+/),s=`${r.find(t=>"top"===t||"bottom"===t)} ${r.find(t=>"left"===t||"right"===t)}`;if(!n.includes(s)){console.warn(`[${t.LIBRARY_NAME}] A quadrant has a corner "${a.getAttribute("data-corner")}". It takes ${n.join(", ")}.`);continue}const c=document.createElement("span");if(c.className=o,c.dataset.corner=s,c.inert=!0,a.childElementCount)for(const t of Array.from(a.children))c.append(t.cloneNode(!0));else c.textContent=(a.textContent??"").trim();for(const{name:t,value:o}of Array.from(a.attributes))"data-corner"!==t&&c.setAttribute(t,o);e.append(c)}return null},draw(n,e,a){const{frame:s,x:c,y:i,rows:l,series:u,height:f,unit:d,svg:h,plot:p,categories:g}=a;if(!s||!c)return void console.warn(`[${t.LIBRARY_NAME}] A quadrant works on a scatter chart, which has an axis across and an axis up.`);const[m,b,M,$]=s,x=u.flatMap((t,o)=>t.group?[]:[o]),v=t=>l.flatMap(o=>o.cells[x[t]]&&!o.cells[x[t]].missing?[o.cells[x[t]].value]:[]),w=r(n.getAttribute("data-x"),v(0)),y=r(n.getAttribute("data-y"),v(1)),A=null===w?m+M/2:Math.min(Math.max(c(w),m),m+M),S=null===y?b+$/2:Math.min(Math.max(i(y),b),b+$),k=h.querySelector(`:scope > .${t.GRID_CLASS}`);if(k){const t=(t,o)=>Math.abs(t-o)<d/2,o=(k.getAttribute("d")??"").match(/M[^M]+/g)??[];k.setAttribute("d",o.filter(o=>{const[,n,r,e]=o.match(/M(-?[\d.]+),(-?[\d.]+)([HV])/)??[];return!("V"===e&&t(Number(n),A)||"H"===e&&t(Number(r),S))}).join(""))}h.insertBefore((0,t.svgEl)("path",{class:t.QUADRANT_CLASS,d:`M${(0,t.short)(A)},${(0,t.short)(b)}V${(0,t.short)(b+$)}M${(0,t.short)(m)},${(0,t.short)(S)}H${(0,t.short)(m+M)}`}),h.querySelector(`:scope > .${t.POINT_CLASS}`));const q=6*d,z=[];for(const r of p.querySelectorAll(`:scope > .${o}`)){const o=r.dataset.corner??"",n=o.includes("right"),e=o.includes("bottom"),a=n?m+M-q:m+q,s=e?b+$-q:b+q;(0,t.placeAt)(r,a,s,t.VIEWBOX_WIDTH,f);const c=(0,t.sizeOf)(r).width*d,i=(0,t.sizeOf)(r).height*d;z.push([n?a-c:a,e?s-i:s,c,i])}a.taken.push(...z);for(const o of g){if(o.hidden||o.hasAttribute("data-left-out")||!o.style.left)continue;const n=(0,t.boxOf)(o,d,t.VIEWBOX_WIDTH,f,[0,0]);z.some(o=>(0,t.overlaps)(o,n))&&o.setAttribute("data-left-out","")}}}},text({figure:o,plot:n,rows:r,series:a,config:l}){const{x:u,y:f,size:d,group:h}=s(a),p=n.querySelector(":scope > .tablechart-categories")?.children??[],g=new Map;r.forEach((t,o)=>{const n=i(t,u,f);n&&(g.get(n)?.push(o)??g.set(n,[o]))});const m=t=>void 0===d?0:Math.abs(r[t].cells[d]?.value??0);for(const t of g.values()){if(t.length<2)continue;const o=t.reduce((t,o)=>m(o)>m(t)?o:t);for(const n of t){const r=p[n];n===o?r?.setAttribute("data-more",String(t.length-1)):r?.setAttribute("data-same","")}}const b=c(r,h);if(b.length<2&&void 0===d)return;const M=document.createElement("div");M.className=t.LEGEND_CLASS,M.setAttribute("aria-hidden","true"),M.inert=!0;const $=Array.from(o.querySelectorAll(`tbody tr > td:nth-of-type(${h+1})`)),x=(o,n)=>{const r=(0,t.htmlEl)(t.NAME_CLASS,M);return r.dataset.place="legend",r.dataset.line="none",r.textContent=o,n&&l.langattribute&&r.setAttribute(l.langattribute,n),r};b.length>1&&b.forEach((t,o)=>{const n=l.langattribute?$.find(o=>o.textContent?.trim()===t)?.getAttribute(l.langattribute):null,r=x(t,n);r.dataset.marker=e[o%e.length],r.style.setProperty("--n",String(o)),r.style.setProperty("--palette",`var(--tablechart-color-${o+1})`)}),void 0!==d&&(x(a[d].name,a[d].key).dataset.marker="size"),n.after(M)},draw(o){const{rows:n,series:r,x:i,y:l,frame:u,unit:f,svg:d,categories:h,height:p,config:g,plot:m}=o;if(!i||!u)return;const b=s(r),M=c(n,b.group),$=M.length>1,x=(o.length("--tablechart-marker-size","10px")??10*f)/2,v=(o.length("--tablechart-bubble-max","24px")??24*f)/2,w=(o.length("--tablechart-bubble-min","")??1.2*x)/2,y=t=>void 0===b.size?void 0:t.cells[b.size],A=(0,t.largest)(n.map(t=>Math.abs(y(t)?.value??0))),S=n.flatMap((t,o)=>{const n=t.cells[b.x],r=t.cells[b.y];if(!n||!r||n.missing||r.missing)return[];const e=y(t),a=e&&!e.missing&&A>0?Math.max(w,Math.sqrt(Math.abs(e.value)/A)*v):x,s=$?M.indexOf(t.cells[b.group]?.raw??""):-1;return[{i:o,px:i(n.value),py:l(r.value),r:a,g:s}]}),k=Math.min(S.length-1,6);m.style.setProperty("--count",String(k+1));const q=new Map([...S].sort((t,o)=>t.px-o.px||t.i-o.i).map((o,n)=>[o.i,k?(0,t.short)(n*k/(S.length-1)):0])),z=[...S].sort((t,o)=>o.r-t.r||t.i-o.i);for(const a of z){const o=$?e[a.g%e.length]:"circle";d.appendChild((0,t.markerEl)(o,a.px,a.py,a.r,{class:t.POINT_CLASS,"data-row":a.i+1,"data-marker":o,style:`--i:${q.get(a.i)}; ${$?`--n:${a.g}; --palette:var(--tablechart-color-${a.g+1})`:""}`}))}const N=m.parentElement?.getAttribute("data-chart-names"),H=null==N?g.names:"false"!==N,V=4*f,I=[u[0]+1.5*V,u[1],u[2]-1.5*V,u[3]-1.5*V],[T,j,B,C]=I,D=z.map(({i:o,px:n,py:r,r:e})=>{if(h[o].hasAttribute("data-same"))return[];const{width:a,height:s}=(0,t.sizeOf)(h[o]),c=a*f,i=s*f,l=.1*i,u=.7*e+V/2,d=(t,o)=>[Math.max(T,Math.min(t,T+B-c)),Math.max(j,Math.min(o,j+C-i))+l,c,i-2*l];return[d(n+e+V,r-i/2),d(n-e-V-c,r-i/2),d(n-c/2,r-e-V-i),d(n-c/2,r+e+V),d(n+u,r-u-i),d(n-u-c,r-u-i),d(n+u,r+u),d(n-u-c,r+u)]}),E=H?(0,t.avoidCollisions)(D,S.map(({px:t,py:o,r:n})=>[t-n,o-n,2*n,2*n]),I):z.map(()=>null);for(const t of h)t.hidden=!0,t.dataset.missing="";z.forEach((o,n)=>{const r=h[o.i],e=E[n];if(r.hidden=!1,delete r.dataset.missing,r.style.setProperty("--i",String(q.get(o.i))),r.toggleAttribute("data-left-out",null===e),null===e)return;r.dataset.side=a[e];const[s,c,,i]=D[n][e];(0,t.placeAt)(r,s,c-((0,t.sizeOf)(r).height*f-i)/2,t.VIEWBOX_WIDTH,p)}),o.rowAt=(t,o)=>{let n=null,r=6*f;for(const e of S){const a=Math.max(Math.hypot(e.px-t*f,e.py-o*f)-e.r,0);(a<r||a===r&&n&&e.r<n.r)&&(r=a,n=e)}return n?n.i:null}},tooltip(t,o){const{x:n,y:r}=s(t.series),e=i(t.rows[o],n,r),a=t.rows.flatMap((t,a)=>a===o||null!==e&&i(t,n,r)===e?[a]:[]),c=a.filter(o=>t.categories[o]?.hasAttribute("data-more"));return[...c,...a.filter(t=>!c.includes(t))].map(o=>({name:t.categories[o]?.textContent??"",value:""}))}};window.Tablechart.addType("scatter",l)}(Tablechart.kit);
@@ -1,11 +1,78 @@
1
- import { C as e, D as t, S as n, _ as r, b as i, g as a, j as o, v as s, w as c, x as l, y as u } from "./tablechart-shared.mjs";
2
- //#region src/js/charts/scatter.ts
3
- var d = [
1
+ import { A as e, C as t, E as n, F as r, I as i, N as a, O as o, P as s, S as c, V as l, W as u, b as d, j as f, k as p, w as m, x as h, z as g } from "./tablechart-shared.mjs";
2
+ // src/js/annotations/quadrant.ts
3
+ var _ = `${l}-name`, v = [
4
+ "top left",
5
+ "top right",
6
+ "bottom left",
7
+ "bottom right"
8
+ ], y = (e, t) => {
9
+ if (!e) return null;
10
+ if (e === "mean") return t.length ? t.reduce((e, t) => e + t, 0) / t.length : null;
11
+ if (e === "median") {
12
+ let e = [...t].sort((e, t) => e - t), n = e.length / 2;
13
+ return e.length ? e.length % 2 ? e[Math.floor(n)] : (e[n - 1] + e[n]) / 2 : null;
14
+ }
15
+ let n = Number.parseFloat(e);
16
+ return Number.isFinite(n) ? n : null;
17
+ }, b = {
18
+ create(e, t) {
19
+ for (let n of e.querySelectorAll("[data-corner]")) {
20
+ let e = (n.getAttribute("data-corner") ?? "").toLowerCase().split(/\s+/), i = `${e.find((e) => e === "top" || e === "bottom")} ${e.find((e) => e === "left" || e === "right")}`;
21
+ if (!v.includes(i)) {
22
+ console.warn(`[${r}] A quadrant has a corner "${n.getAttribute("data-corner")}". It takes ${v.join(", ")}.`);
23
+ continue;
24
+ }
25
+ let a = document.createElement("span");
26
+ if (a.className = _, a.dataset.corner = i, a.inert = !0, n.childElementCount) for (let e of Array.from(n.children)) a.append(e.cloneNode(!0));
27
+ else a.textContent = (n.textContent ?? "").trim();
28
+ for (let { name: e, value: t } of Array.from(n.attributes)) e !== "data-corner" && a.setAttribute(e, t);
29
+ t.append(a);
30
+ }
31
+ return null;
32
+ },
33
+ draw(t, n, i) {
34
+ let { frame: o, x: s, y: d, rows: v, series: b, height: x, unit: S, svg: C, plot: w, categories: T } = i;
35
+ if (!o || !s) {
36
+ console.warn(`[${r}] A quadrant works on a scatter chart, which has an axis across and an axis up.`);
37
+ return;
38
+ }
39
+ let [E, D, O, k] = o, A = b.flatMap((e, t) => e.group ? [] : [t]), j = (e) => v.flatMap((t) => t.cells[A[e]] && !t.cells[A[e]].missing ? [t.cells[A[e]].value] : []), M = y(t.getAttribute("data-x"), j(0)), N = y(t.getAttribute("data-y"), j(1)), P = M === null ? E + O / 2 : Math.min(Math.max(s(M), E), E + O), F = N === null ? D + k / 2 : Math.min(Math.max(d(N), D), D + k), I = C.querySelector(`:scope > .${a}`);
40
+ if (I) {
41
+ let e = (e, t) => Math.abs(e - t) < S / 2, t = (I.getAttribute("d") ?? "").match(/M[^M]+/g) ?? [];
42
+ I.setAttribute("d", t.filter((t) => {
43
+ let [, n, r, i] = t.match(/M(-?[\d.]+),(-?[\d.]+)([HV])/) ?? [];
44
+ return !(i === "V" && e(Number(n), P)) && !(i === "H" && e(Number(r), F));
45
+ }).join(""));
46
+ }
47
+ C.insertBefore(f("path", {
48
+ class: l,
49
+ d: `M${e(P)},${e(D)}V${e(D + k)}M${e(E)},${e(F)}H${e(E + O)}`
50
+ }), C.querySelector(`:scope > .${g}`));
51
+ let L = 6 * S, R = [];
52
+ for (let e of w.querySelectorAll(`:scope > .${_}`)) {
53
+ let t = e.dataset.corner ?? "", n = t.includes("right"), r = t.includes("bottom"), i = n ? E + O - L : E + L, a = r ? D + k - L : D + L;
54
+ p(e, i, a, u, x);
55
+ let o = c(e).width * S, s = c(e).height * S;
56
+ R.push([
57
+ n ? i - o : i,
58
+ r ? a - s : a,
59
+ o,
60
+ s
61
+ ]);
62
+ }
63
+ i.taken.push(...R);
64
+ for (let e of T) {
65
+ if (e.hidden || e.hasAttribute("data-left-out") || !e.style.left) continue;
66
+ let t = h(e, S, u, x, [0, 0]);
67
+ R.some((e) => m(e, t)) && e.setAttribute("data-left-out", "");
68
+ }
69
+ }
70
+ }, x = [
4
71
  "circle",
5
72
  "triangle",
6
73
  "square",
7
74
  "diamond"
8
- ], f = [
75
+ ], S = [
9
76
  "right",
10
77
  "left",
11
78
  "above",
@@ -14,7 +81,7 @@ var d = [
14
81
  "left",
15
82
  "right",
16
83
  "left"
17
- ], p = (e) => {
84
+ ], C = (e) => {
18
85
  let t = e.flatMap((e, t) => e.group ? [] : [t]);
19
86
  return {
20
87
  x: t[0],
@@ -22,119 +89,118 @@ var d = [
22
89
  size: t[2],
23
90
  group: e.findIndex((e) => e.group)
24
91
  };
25
- }, m = (e, t) => t < 0 ? [] : [...new Set(e.map((e) => e.cells[t]?.raw ?? ""))], h = (e, t, n) => e.cells[t]?.missing || e.cells[n]?.missing ? null : `${e.cells[t]?.value} ${e.cells[n]?.value}`, g = {
92
+ }, w = (e, t) => t < 0 ? [] : [...new Set(e.map((e) => e.cells[t]?.raw ?? ""))], T = (e, t, n) => e.cells[t]?.missing || e.cells[n]?.missing ? null : `${e.cells[t]?.value} ${e.cells[n]?.value}`, E = {
26
93
  aspect: .62,
27
94
  categories: "beside",
28
95
  columns: !0,
29
96
  axes: !0,
30
- text({ figure: t, plot: n, rows: r, series: a, config: o }) {
31
- let { x: s, y: l, size: u, group: f } = p(a), g = n.querySelector(":scope > .tablechart-categories")?.children ?? [], _ = /* @__PURE__ */ new Map();
32
- r.forEach((e, t) => {
33
- let n = h(e, s, l);
34
- n && (_.get(n)?.push(t) ?? _.set(n, [t]));
97
+ numbers: !1,
98
+ annotations: { quadrant: b },
99
+ text({ figure: e, plot: t, rows: n, series: r, config: a }) {
100
+ let { x: c, y: l, size: u, group: d } = C(r), f = t.querySelector(":scope > .tablechart-categories")?.children ?? [], p = /* @__PURE__ */ new Map();
101
+ n.forEach((e, t) => {
102
+ let n = T(e, c, l);
103
+ n && (p.get(n)?.push(t) ?? p.set(n, [t]));
35
104
  });
36
- let v = (e) => u === void 0 ? 0 : Math.abs(r[e].cells[u]?.value ?? 0);
37
- for (let e of _.values()) {
105
+ let m = (e) => u === void 0 ? 0 : Math.abs(n[e].cells[u]?.value ?? 0);
106
+ for (let e of p.values()) {
38
107
  if (e.length < 2) continue;
39
- let t = e.reduce((e, t) => v(t) > v(e) ? t : e);
108
+ let t = e.reduce((e, t) => m(t) > m(e) ? t : e);
40
109
  for (let n of e) {
41
- let r = g[n];
110
+ let r = f[n];
42
111
  n === t ? r?.setAttribute("data-more", String(e.length - 1)) : r?.setAttribute("data-same", "");
43
112
  }
44
113
  }
45
- let y = m(r, f);
46
- if (y.length < 2 && u === void 0) return;
47
- let b = document.createElement("div");
48
- b.className = e, b.setAttribute("aria-hidden", "true"), b.inert = !0;
49
- let x = Array.from(t.querySelectorAll(`tbody tr > td:nth-of-type(${f + 1})`)), S = (e, t) => {
50
- let n = i(c, b);
51
- return n.dataset.place = "legend", n.dataset.line = "none", n.textContent = e, t && o.langattribute && n.setAttribute(o.langattribute, t), n;
114
+ let h = w(n, d);
115
+ if (h.length < 2 && u === void 0) return;
116
+ let g = document.createElement("div");
117
+ g.className = s, g.setAttribute("aria-hidden", "true"), g.inert = !0;
118
+ let _ = Array.from(e.querySelectorAll(`tbody tr > td:nth-of-type(${d + 1})`)), v = (e, t) => {
119
+ let n = o(i, g);
120
+ return n.dataset.place = "legend", n.dataset.line = "none", n.textContent = e, t && a.langattribute && n.setAttribute(a.langattribute, t), n;
52
121
  };
53
- y.length > 1 && y.forEach((e, t) => {
54
- let n = o.langattribute ? x.find((t) => t.textContent?.trim() === e)?.getAttribute(o.langattribute) : null, r = S(e, n);
55
- r.dataset.marker = d[t % d.length], r.style.setProperty("--n", String(t)), r.style.setProperty("--palette", `var(--tablechart-color-${t + 1})`);
56
- }), u !== void 0 && (S(a[u].name, a[u].key).dataset.marker = "size"), t.appendChild(b);
122
+ h.length > 1 && h.forEach((e, t) => {
123
+ let n = a.langattribute ? _.find((t) => t.textContent?.trim() === e)?.getAttribute(a.langattribute) : null, r = v(e, n);
124
+ r.dataset.marker = x[t % x.length], r.style.setProperty("--n", String(t)), r.style.setProperty("--palette", `var(--tablechart-color-${t + 1})`);
125
+ }), u !== void 0 && (v(r[u].name, r[u].key).dataset.marker = "size"), t.after(g);
57
126
  },
58
- draw(e) {
59
- let { rows: i, series: c, x: h, y: g, frame: _, unit: v, svg: y, categories: b, seriesValues: x, height: S, config: C, plot: w } = e;
60
- if (!h || !_) return;
61
- let T = p(c), E = m(i, T.group), D = E.length > 1;
62
- for (let e of x.flat()) e.dataset.place = "none";
63
- let O = (e.length("--tablechart-marker-size", "10px") ?? 10 * v) / 2, k = (e.length("--tablechart-bubble-max", "24px") ?? 24 * v) / 2, A = (e) => T.size === void 0 ? void 0 : e.cells[T.size], j = u(i.map((e) => Math.abs(A(e)?.value ?? 0))), M = i.flatMap((e, t) => {
64
- let n = e.cells[T.x], r = e.cells[T.y];
127
+ draw(r) {
128
+ let { rows: i, series: a, x: o, y: s, frame: l, unit: f, svg: m, categories: h, height: _, config: v, plot: y } = r;
129
+ if (!o || !l) return;
130
+ let b = C(a), T = w(i, b.group), E = T.length > 1, D = (r.length("--tablechart-marker-size", "10px") ?? 10 * f) / 2, O = (r.length("--tablechart-bubble-max", "24px") ?? 24 * f) / 2, k = (r.length("--tablechart-bubble-min", "") ?? D * 1.2) / 2, A = (e) => b.size === void 0 ? void 0 : e.cells[b.size], j = n(i.map((e) => Math.abs(A(e)?.value ?? 0))), M = i.flatMap((e, t) => {
131
+ let n = e.cells[b.x], r = e.cells[b.y];
65
132
  if (!n || !r || n.missing || r.missing) return [];
66
- let i = A(e), a = i && !i.missing && j > 0 ? Math.max(O * .6, Math.sqrt(Math.abs(i.value) / j) * k) : O, o = D ? E.indexOf(e.cells[T.group]?.raw ?? "") : -1;
133
+ let i = A(e), a = i && !i.missing && j > 0 ? Math.max(k, Math.sqrt(Math.abs(i.value) / j) * O) : D, c = E ? T.indexOf(e.cells[b.group]?.raw ?? "") : -1;
67
134
  return [{
68
135
  i: t,
69
- px: h(n.value),
70
- py: g(r.value),
136
+ px: o(n.value),
137
+ py: s(r.value),
71
138
  r: a,
72
- g: o
139
+ g: c
73
140
  }];
74
141
  }), N = Math.min(M.length - 1, 6);
75
- w.style.setProperty("--count", String(N + 1));
76
- let P = new Map([...M].sort((e, t) => e.px - t.px || e.i - t.i).map((e, t) => [e.i, N ? n(t * N / (M.length - 1)) : 0])), F = [...M].sort((e, t) => t.r - e.r || e.i - t.i);
142
+ y.style.setProperty("--count", String(N + 1));
143
+ let P = new Map([...M].sort((e, t) => e.px - t.px || e.i - t.i).map((t, n) => [t.i, N ? e(n * N / (M.length - 1)) : 0])), F = [...M].sort((e, t) => t.r - e.r || e.i - t.i);
77
144
  for (let e of F) {
78
- let n = D ? d[e.g % d.length] : "circle";
79
- y.appendChild(a(n, e.px, e.py, e.r, {
80
- class: t,
145
+ let t = E ? x[e.g % x.length] : "circle";
146
+ m.appendChild(d(t, e.px, e.py, e.r, {
147
+ class: g,
81
148
  "data-row": e.i + 1,
82
- "data-marker": n,
83
- style: `--i:${P.get(e.i)}; ${D ? `--n:${e.g}; --palette:var(--tablechart-color-${e.g + 1})` : ""}`
149
+ "data-marker": t,
150
+ style: `--i:${P.get(e.i)}; ${E ? `--n:${e.g}; --palette:var(--tablechart-color-${e.g + 1})` : ""}`
84
151
  }));
85
152
  }
86
- let I = w.parentElement?.getAttribute("data-chart-names"), L = I == null ? C.names : I !== "false", R = 4 * v, z = [
87
- _[0] + 1.5 * R,
88
- _[1],
89
- _[2] - 1.5 * R,
90
- _[3] - 1.5 * R
91
- ], [B, V, H, U] = z, W = F.map(({ i: e, px: t, py: n, r: i }) => {
92
- if (b[e].hasAttribute("data-same")) return [];
93
- let { width: a, height: o } = r(b[e]), s = a * v, c = o * v, l = c * .1, u = i * .7 + R / 2, d = (e, t) => [
94
- Math.max(B, Math.min(e, B + H - s)),
95
- Math.max(V, Math.min(t, V + U - c)) + l,
96
- s,
97
- c - 2 * l
153
+ let I = y.parentElement?.getAttribute("data-chart-names"), L = I == null ? v.names : I !== "false", R = 4 * f, z = [
154
+ l[0] + 1.5 * R,
155
+ l[1],
156
+ l[2] - 1.5 * R,
157
+ l[3] - 1.5 * R
158
+ ], [B, V, H, U] = z, W = F.map(({ i: e, px: t, py: n, r }) => {
159
+ if (h[e].hasAttribute("data-same")) return [];
160
+ let { width: i, height: a } = c(h[e]), o = i * f, s = a * f, l = s * .1, u = r * .7 + R / 2, d = (e, t) => [
161
+ Math.max(B, Math.min(e, B + H - o)),
162
+ Math.max(V, Math.min(t, V + U - s)) + l,
163
+ o,
164
+ s - 2 * l
98
165
  ];
99
166
  return [
100
- d(t + i + R, n - c / 2),
101
- d(t - i - R - s, n - c / 2),
102
- d(t - s / 2, n - i - R - c),
103
- d(t - s / 2, n + i + R),
104
- d(t + u, n - u - c),
105
- d(t - u - s, n - u - c),
167
+ d(t + r + R, n - s / 2),
168
+ d(t - r - R - o, n - s / 2),
169
+ d(t - o / 2, n - r - R - s),
170
+ d(t - o / 2, n + r + R),
171
+ d(t + u, n - u - s),
172
+ d(t - u - o, n - u - s),
106
173
  d(t + u, n + u),
107
- d(t - u - s, n + u)
174
+ d(t - u - o, n + u)
108
175
  ];
109
- }), G = L ? s(W, M.map(({ px: e, py: t, r: n }) => [
176
+ }), G = L ? t(W, M.map(({ px: e, py: t, r: n }) => [
110
177
  e - n,
111
178
  t - n,
112
179
  2 * n,
113
180
  2 * n
114
181
  ]), z) : F.map(() => null);
115
- for (let e of b) e.hidden = !0, e.dataset.missing = "";
182
+ for (let e of h) e.hidden = !0, e.dataset.missing = "";
116
183
  F.forEach((e, t) => {
117
- let n = b[e.i], i = G[t];
118
- if (n.hidden = !1, delete n.dataset.missing, n.style.setProperty("--i", String(P.get(e.i))), n.toggleAttribute("data-left-out", i === null), i === null) return;
119
- n.dataset.side = f[i];
120
- let [a, s, , c] = W[t][i];
121
- l(n, a, s - (r(n).height * v - c) / 2, o, S);
122
- }), e.rowAt = (e, t) => {
123
- let n = null, r = 6 * v;
184
+ let n = h[e.i], r = G[t];
185
+ if (n.hidden = !1, delete n.dataset.missing, n.style.setProperty("--i", String(P.get(e.i))), n.toggleAttribute("data-left-out", r === null), r === null) return;
186
+ n.dataset.side = S[r];
187
+ let [i, a, , o] = W[t][r];
188
+ p(n, i, a - (c(n).height * f - o) / 2, u, _);
189
+ }), r.rowAt = (e, t) => {
190
+ let n = null, r = 6 * f;
124
191
  for (let i of M) {
125
- let a = Math.max(Math.hypot(i.px - e * v, i.py - t * v) - i.r, 0);
192
+ let a = Math.max(Math.hypot(i.px - e * f, i.py - t * f) - i.r, 0);
126
193
  (a < r || a === r && n && i.r < n.r) && (r = a, n = i);
127
194
  }
128
195
  return n ? n.i : null;
129
196
  };
130
197
  },
131
198
  tooltip(e, t) {
132
- let { x: n, y: r } = p(e.series), i = h(e.rows[t], n, r), a = e.rows.flatMap((e, a) => a === t || i !== null && h(e, n, r) === i ? [a] : []), o = a.filter((t) => e.categories[t]?.hasAttribute("data-more"));
199
+ let { x: n, y: r } = C(e.series), i = T(e.rows[t], n, r), a = e.rows.flatMap((e, a) => a === t || i !== null && T(e, n, r) === i ? [a] : []), o = a.filter((t) => e.categories[t]?.hasAttribute("data-more"));
133
200
  return [...o, ...a.filter((e) => !o.includes(e))].map((t) => ({
134
201
  name: e.categories[t]?.textContent ?? "",
135
202
  value: ""
136
203
  }));
137
204
  }
138
205
  };
139
- //#endregion
140
- export { g as scatter };
206
+ export { E as scatter };
@@ -1,2 +1,2 @@
1
- import { A as defaultConfig, C as stepTo, D as PRINTING_CLASS, E as PLOT_CLASS, O as QUEUED_CLASS, S as StepPlan, T as PLACED_CLASS, b as Row, c as entranceOf, d as addType, f as build, g as unbuild, h as setShown, i as readMarkdownRows, k as SHOWN_CLASS, m as relabel, n as adoptTables, p as layout, r as loadSources, t as assignKeys, u as prepare, v as ChartState, w as Config, x as Series, y as ChartType } from "./tablechart-shared.mjs";
2
- export { type ChartState, type ChartType, type Config, PLACED_CLASS, PLOT_CLASS, PRINTING_CLASS, QUEUED_CLASS, type Row, SHOWN_CLASS, type Series, type StepPlan, addType, adoptTables, assignKeys, build, defaultConfig, entranceOf, layout, loadSources, prepare, readMarkdownRows, relabel, setShown, stepTo, unbuild };
1
+ import { A as SHOWN_CLASS, C as stepTo, D as PLOT_CLASS, E as PLACED_CLASS, O as PRINTING_CLASS, S as StepPlan, T as Config, b as Row, c as entranceOf, d as addType, f as build, g as unbuild, h as setShown, i as readMarkdownRows, j as defaultConfig, k as QUEUED_CLASS, m as relabel, n as adoptTables, p as layout, r as loadSources, t as assignKeys, u as prepare, v as ChartState, w as AT_ONCE_CLASS, x as Series, y as ChartType } from "./tablechart-shared.mjs";
2
+ export { AT_ONCE_CLASS, type ChartState, type ChartType, type Config, PLACED_CLASS, PLOT_CLASS, PRINTING_CLASS, QUEUED_CLASS, type Row, SHOWN_CLASS, type Series, type StepPlan, addType, adoptTables, assignKeys, build, defaultConfig, entranceOf, layout, loadSources, prepare, readMarkdownRows, relabel, setShown, stepTo, unbuild };
@@ -1 +1 @@
1
- export { T as PLACED_CLASS, E as PLOT_CLASS, O as PRINTING_CLASS, k as QUEUED_CLASS, A as SHOWN_CLASS, c as addType, a as adoptTables, h as assignKeys, l as build, M as defaultConfig, n as entranceOf, u as layout, o as loadSources, i as prepare, s as readMarkdownRows, d as relabel, f as setShown, m as stepTo, p as unbuild } from "./tablechart-shared.mjs";
1
+ export { M as AT_ONCE_CLASS, L as PLACED_CLASS, R as PLOT_CLASS, B as PRINTING_CLASS, H as QUEUED_CLASS, U as SHOWN_CLASS, c as addType, a as adoptTables, h as assignKeys, l as build, G as defaultConfig, n as entranceOf, u as layout, o as loadSources, i as prepare, s as readMarkdownRows, d as relabel, f as setShown, m as stepTo, p as unbuild } from "./tablechart-shared.mjs";
@@ -1,8 +1,8 @@
1
- //#region src/js/config.d.ts
2
1
  declare const PLOT_CLASS = "tablechart-plot";
3
2
  declare const PLACED_CLASS = "is-placed";
4
3
  declare const SHOWN_CLASS = "is-shown";
5
4
  declare const PRINTING_CLASS = "is-printing";
5
+ declare const AT_ONCE_CLASS = "is-at-once";
6
6
  declare const QUEUED_CLASS = "tablechart-queued";
7
7
  interface Config {
8
8
  /** Which elements Tablechart turns into charts. */
@@ -21,6 +21,13 @@ interface Config {
21
21
  entrance: number;
22
22
  /** Where a column's number goes: on the bar, or inside it. A figure overrides it with `data-chart-labels`. */
23
23
  labels: "outside" | "inside";
24
+ /**
25
+ * Which values a line or area chart shows: `"auto"`, all if they fit and else the first, last, highest and lowest; `"all"`;
26
+ * `"none"`; or a set, such as `"first last"`. `"auto last"` shows all if they fit, and else the last. A figure overrides it with `data-chart-values`.
27
+ */
28
+ values: string;
29
+ /** A tick above each name under a line or area chart, at the place of its row. A figure overrides it with `data-chart-ticks`. */
30
+ ticks: boolean;
24
31
  /** How a line or area runs between points: `straight`, `step` or `smooth`. A figure overrides it with `data-chart-shape`. */
25
32
  shape: "straight" | "step" | "smooth";
26
33
  /** Names of several lines in a legend instead of at the end of each line. A figure overrides it with `data-chart-legend`. */
@@ -37,6 +44,10 @@ interface Config {
37
44
  tooltipparent: HTMLElement | string | null;
38
45
  /** The table under the chart: `"always"`, only in `"print"`, in print when numbers are left out (`"auto"`), or `"never"`. A figure overrides it with `data-chart-table`. */
39
46
  table: "never" | "auto" | "print" | "always";
47
+ /** With `auto`, the table is printed only up to this many values, rows times value columns; a longer one prints the summary instead. */
48
+ tablelimit: number;
49
+ /** The summary of a chart, in a `.chart-summary`: `"always"`, only in `"print"`, in print when the table is not (`"auto"`), or `"never"`, for screen readers only. A figure overrides it with `data-chart-summary`. */
50
+ summary: "never" | "auto" | "print" | "always";
40
51
  /** The name of the total that a donut without a total row works out, for screen readers. */
41
52
  totallabel: string;
42
53
  /** Previous and Next buttons under a chart that steps (`data-fragment`). `false` leaves them out, for buttons of your own. */
@@ -57,8 +68,6 @@ interface Config {
57
68
  debug?: boolean;
58
69
  }
59
70
  declare const defaultConfig: Config;
60
- //#endregion
61
- //#region src/js/functions/scale.d.ts
62
71
  /** A break in the scale: values from `lo` to `hi` get `share` of the length, and the cut is at `gap`. */
63
72
  interface Squeeze {
64
73
  lo: number;
@@ -66,8 +75,6 @@ interface Squeeze {
66
75
  gap: number;
67
76
  share: number;
68
77
  }
69
- //#endregion
70
- //#region src/js/steps.d.ts
71
78
  /** The steps of one chart: which step shows each row, column and annotation, and how many steps there are. */
72
79
  interface StepPlan {
73
80
  rows: {
@@ -95,8 +102,6 @@ interface StepPlan {
95
102
  * Says so with a `tablechart:step` event on the figure, unless `quietly`.
96
103
  */
97
104
  declare const stepTo: (state: ChartState, step: number, quietly?: boolean) => void;
98
- //#endregion
99
- //#region src/js/types.d.ts
100
105
  /** How a line or an area runs from one point to the next. */
101
106
  type Shape = "straight" | "step" | "smooth";
102
107
  /** How a chart builds: each element grows from zero, or the chart is revealed in one movement. */
@@ -114,6 +119,13 @@ interface Cell {
114
119
  }
115
120
  interface Row extends Cell {
116
121
  label: string;
122
+ /** `data-short` on the row's header: the name under the chart, where room is short. The table and the summary have `label`. */
123
+ short: string | null;
124
+ /** `data-value="show"` on the row: its value is always shown on a line. */
125
+ pinned: boolean;
126
+ /** The text of its cell in a column with `data-note`, such as "Share issue", and its translation key. */
127
+ note: string | null;
128
+ noteKey: string | null;
117
129
  key: string | null;
118
130
  cells: Cell[];
119
131
  /** Copied to everything drawn for the row, so one class styles all of it. */
@@ -132,12 +144,20 @@ interface Series {
132
144
  line: "dashed" | "dotted" | null;
133
145
  /** `data-group` on the header: the cells name a group, such as "Predefined". */
134
146
  group: boolean;
147
+ /** `data-chart="line"` on the header, in a chart that takes it: a line over the other marks. */
148
+ chart: "line" | null;
135
149
  }
136
150
  interface Segment extends Row {
137
151
  from: number;
138
152
  to: number;
139
153
  kind: "bar" | "up" | "down" | "total";
140
154
  }
155
+ /** Which values a line shows, from `data-chart-values`: all, none, a set of rows, or all if they fit and else the set. */
156
+ interface ValuePick {
157
+ mode: "auto" | "all" | "set";
158
+ /** `first`, `last`, `highest`, `lowest`, `outliers`. Rows with `data-value="show"` are always in the set. */
159
+ keys: string[];
160
+ }
141
161
  /** Scales only, no pixel positions, so an annotation follows the data. */
142
162
  interface Geometry {
143
163
  rows: Row[];
@@ -150,6 +170,8 @@ interface Geometry {
150
170
  frame?: [number, number, number, number];
151
171
  /** A chart that knows better which row is pointed at sets this, in screen pixels from the plot's corner. */
152
172
  rowAt?: (x: number, y: number) => number | null;
173
+ /** Where the tooltip of a row points, in viewBox units, for a row whose mark may not be drawn, such as a point of a long line. */
174
+ pointOf?: (index: number) => [number, number] | null;
153
175
  /** A CSS length set on the figure, such as `--tablechart-marker-size`, in viewBox units. */
154
176
  length: (name: string, fallback: string) => number | null;
155
177
  barWidth: number;
@@ -178,6 +200,8 @@ interface Geometry {
178
200
  insideAlign: "start" | "center" | "end" | null;
179
201
  stacked: boolean;
180
202
  shape: Shape;
203
+ /** Which values a line or an area shows. */
204
+ pick: ValuePick;
181
205
  /** Empty when the chart is not stacked or its kind shows no totals. */
182
206
  totals: HTMLElement[];
183
207
  svg: SVGSVGElement;
@@ -185,6 +209,8 @@ interface Geometry {
185
209
  /** Made by `build`; a chart only places them. */
186
210
  values: HTMLElement[];
187
211
  series: Series[];
212
+ /** The series this drawing draws: all of them, or in a chart with columns and a line, those of its own kind. */
213
+ own: number[];
188
214
  seriesValues: HTMLElement[][];
189
215
  /** Empty with one series. */
190
216
  names: HTMLElement[];
@@ -196,6 +222,8 @@ interface Geometry {
196
222
  checks: Check[];
197
223
  /** The labels of annotations placed so far, as left, top, width and height, so the next one finds room. */
198
224
  taken: [number, number, number, number][];
225
+ /** Holes in the lines and areas, such as under an open marker: the core leaves them out with a clip, from the start. */
226
+ lineHoles: SVGElement[];
199
227
  }
200
228
  interface Check {
201
229
  label: string;
@@ -221,6 +249,12 @@ interface ChartType {
221
249
  columns?: boolean;
222
250
  /** An axis across and an axis up, from the first two value columns, with their headers as titles. */
223
251
  axes?: boolean;
252
+ /** A series with `data-chart="line"` on its header is a line over the marks, on the same scale. */
253
+ lines?: boolean;
254
+ /** Several series side by side, as groups of bars: their numbers are smaller, so the plot is marked `data-grouped` before anything is measured. */
255
+ groups?: boolean;
256
+ /** False: no numbers on the chart. They stay out of the layout, which costs a lot with many rows. */
257
+ numbers?: false;
224
258
  /** Text of its own, made with the rest of the text so a translation script finds it, such as a legend. */
225
259
  text?: (input: {
226
260
  figure: HTMLElement;
@@ -247,6 +281,8 @@ interface ChartType {
247
281
  draw: (geo: Geometry) => void;
248
282
  /** The lines of the tooltip of a row. Default: each value with the name of its series, and the total of a stack. */
249
283
  tooltip?: (state: ChartState, index: number, config: Config) => TooltipLine[];
284
+ /** Annotations that come with this type, such as `quadrant` with `scatter`; `addType` adds them. */
285
+ annotations?: Record<string, AnnotationType>;
250
286
  }
251
287
  /** One line of a tooltip, under the name of the row. */
252
288
  interface TooltipLine {
@@ -303,6 +339,8 @@ interface ChartState {
303
339
  rowAt: ((x: number, y: number) => number | null) | null;
304
340
  /** As percentages: left, top, width and height. */
305
341
  rowBox: ((index: number) => [number, number, number, number]) | null;
342
+ /** Where the tooltip of a row points, in viewBox units; set by a chart such as a line. */
343
+ pointOf: ((index: number) => [number, number] | null) | null;
306
344
  annotations: {
307
345
  spec: HTMLElement;
308
346
  type: AnnotationType;
@@ -319,12 +357,8 @@ interface ChartState {
319
357
  controls: HTMLElement | null;
320
358
  checks: Check[];
321
359
  }
322
- //#endregion
323
- //#region src/js/charts/scatter.d.ts
324
360
  /** One marker per row, across by its first value and up by its second; a third value makes it a bubble. */
325
361
  declare const scatter: ChartType;
326
- //#endregion
327
- //#region src/js/core.d.ts
328
362
  /** Adds a chart type for `data-chart="name"`, such as one from `@martinomagnifico/tablechart/additional-types`, before `init()`. */
329
363
  declare const addType: (name: string, type: ChartType) => void;
330
364
  /** Pass 2: everything a reader will read, before a translation script looks. */
@@ -335,10 +369,11 @@ declare const layout: (state: ChartState, config: Config) => boolean;
335
369
  declare const relabel: (state: ChartState, config: Config) => void;
336
370
  /** Removes what `build` added, so the chart can be built again. */
337
371
  declare const unbuild: (state: ChartState) => void;
338
- /** One class, `is-shown`, starts the whole build. */
339
- declare const setShown: (state: ChartState, shown: boolean) => void;
340
- //#endregion
341
- //#region src/js/init.d.ts
372
+ /**
373
+ * One class, `is-shown`, starts the whole build. `atOnce` skips it: the chart lands at its end, or back at its start,
374
+ * without transitions, such as a chart in view when the page opens, or one hidden where nobody sees it.
375
+ */
376
+ declare const setShown: (state: ChartState, shown: boolean, atOnce?: boolean) => void;
342
377
  interface TablechartChart {
343
378
  figure: HTMLElement;
344
379
  /** Lays the chart out again, for example after a script has changed its text. */
@@ -376,17 +411,12 @@ declare const prepare: (found: HTMLElement[], config: Config) => Promise<HTMLEle
376
411
  declare const create: (element: HTMLElement, options?: Partial<Config>) => Promise<TablechartChart | null>;
377
412
  /** Makes a chart of every figure in `root` that matches the selector, skipping existing charts. */
378
413
  declare const init: (options?: Partial<Config>, root?: ParentNode) => Promise<TablechartInstance>;
379
- //#endregion
380
- //#region src/js/functions/sources.d.ts
381
414
  /** A Markdown table, or a caption with chart attributes, is wrapped in a figure with the caption and annotations beside it. */
382
415
  declare const adoptTables: (figures: HTMLElement[]) => HTMLElement[];
383
416
  /** `data-chart-src` fills the figure's table with the rows of a JSON file, keeping a header row the author wrote; a file that fails is reported. */
384
417
  declare const loadSources: (figures: HTMLElement[]) => Promise<void>;
385
418
  /** In a Markdown table, a bold label makes the row a total, and a bold header puts that series in focus. */
386
419
  declare const readMarkdownRows: (figures: HTMLElement[]) => void;
387
- //#endregion
388
- //#region src/js/functions/read-table.d.ts
389
420
  /** Writes translation keys such as `harvest-row1` onto a figure with `data-chart-keys`; a duplicate prefix gets a number. */
390
421
  declare const assignKeys: (figures: HTMLElement[], langattribute: string | false) => void;
391
- //#endregion
392
- export { defaultConfig as A, stepTo as C, PRINTING_CLASS as D, PLOT_CLASS as E, QUEUED_CLASS as O, StepPlan as S, PLACED_CLASS as T, scatter as _, TablechartChart as a, Row as b, entranceOf as c, addType as d, build as f, unbuild as g, setShown as h, readMarkdownRows as i, SHOWN_CLASS as k, init as l, relabel as m, adoptTables as n, TablechartInstance as o, layout as p, loadSources as r, create as s, assignKeys as t, prepare as u, ChartState as v, Config as w, Series as x, ChartType as y };
422
+ export { SHOWN_CLASS as A, stepTo as C, PLOT_CLASS as D, PLACED_CLASS as E, PRINTING_CLASS as O, StepPlan as S, Config as T, scatter as _, TablechartChart as a, Row as b, entranceOf as c, addType as d, build as f, unbuild as g, setShown as h, readMarkdownRows as i, defaultConfig as j, QUEUED_CLASS as k, init as l, relabel as m, adoptTables as n, TablechartInstance as o, layout as p, loadSources as r, create as s, assignKeys as t, prepare as u, ChartState as v, AT_ONCE_CLASS as w, Series as x, ChartType as y };