@martinomagnifico/tablechart 1.3.0 → 1.4.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,32 @@
1
1
  # Changelog
2
2
 
3
+ ## [1.4.0] - 2026-11-13
4
+
5
+ ### Added
6
+
7
+ - `groups` in a summary, a short table of the rows in groups, each with its lowest, average and highest value.
8
+ - `{rank}` and `{rank.low}` for each outlier and note in a summary, and `{values}`, the number of values.
9
+ - Summary settings per chart, with `data-summary-` and the name, or as JSON in `data-summary-options` on the figure or a parent.
10
+ - `tooltip: "crosshair"` and `data-chart-tooltip="crosshair"`, lines through the row and the nearest point of a line or area chart, with its values at the side.
11
+ - Rows next to each other with the same short name show it once under the chart, from the first of them, such as a year over its months.
12
+ - `pie`, a donut without a hole. A pie, really.
13
+ - `candlestick`, an additional type with the open, high, low and close of each row.
14
+ - `axis: "right"` and `data-chart-axis="right"`, the value axis on the right of a column, line, area or waterfall chart.
15
+
16
+ ### Changed
17
+
18
+ - A line chart with a scale that starts above zero starts at its scale.
19
+ - The value axis of a line chart reaches the line under the chart.
20
+ - A line chart without numbers keeps no room for them above and below its lines.
21
+ - A chart keeps room for the top number of its value axis.
22
+ - A legend comes in with the first marks.
23
+ - `Summary.fill(figure)` uses the settings of `Summary.init()`.
24
+ - A table inside a summary is not read as the data of the chart.
25
+ - On a crowded column or bar chart, the numbers of the largest values stay, where they are clear of the other columns.
26
+ - On a crowded line or area chart, the last number of each line goes next to its name.
27
+ - A number that does not fit next to its point is left out, not placed a line further away.
28
+
29
+
3
30
  ## [1.3.0] - 2026-11-10
4
31
 
5
32
  ### Added
package/README.md CHANGED
@@ -11,7 +11,7 @@
11
11
 
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
- * **Chart types:** column, bar, line, area, waterfall and donut, and scatter as an additional type
14
+ * **Chart types:** column, bar, line, area, waterfall, donut and pie, and scatter and candlestick as additional types
15
15
  * **The table:** the table is hidden from sight, but screen readers, search engines and translation scripts still read it.
16
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
17
  * **CSS styling:** colours, sizes and animation are CSS variables. Light and dark mode follow the page.
@@ -51,7 +51,7 @@ Or with a script tag:
51
51
  </script>
52
52
  ```
53
53
 
54
- The scatter chart is an additional type, in a script of its own. To use it, see [Additional types](#additional-types).
54
+ The scatter and candlestick charts are additional types, in a script of their own. To use them, see [Additional types](#additional-types).
55
55
 
56
56
  ## A chart
57
57
 
@@ -74,13 +74,14 @@ Use `bar`, `line`, `area`, `waterfall` or `donut` for the other types. With more
74
74
 
75
75
  ### Additional types
76
76
 
77
- The scatter chart is an additional type, in a script of its own. To use it, add it before `init()`:
77
+ The scatter and candlestick charts are additional types, in a script of their own. If you want to use one, add it before `init()`:
78
78
 
79
79
  ```js
80
80
  import { addType, init } from "@martinomagnifico/tablechart";
81
- import { scatter } from "@martinomagnifico/tablechart/additional-types";
81
+ import { candlestick, scatter } from "@martinomagnifico/tablechart/additional-types";
82
82
 
83
83
  addType("scatter", scatter);
84
+ addType("candlestick", candlestick);
84
85
  init();
85
86
  ```
86
87
 
@@ -1,2 +1,2 @@
1
- import { y as scatter } from "./tablechart-shared.mjs";
2
- export { scatter };
1
+ import { T as candlestick, w as scatter } from "./tablechart-shared.mjs";
2
+ export { candlestick, scatter };
@@ -1 +1 @@
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
+ !function(t){var o="tablechart-candle",n=t=>t.flatMap((t,o)=>t.group?[]:[o]).slice(0,4),r=(t,o)=>{const n=o.map(o=>t.cells[o]);return 4===n.length&&n.every(t=>t&&!t.missing)?n.map(t=>t.value):null},a={columns:!0,numbers:!1,floats:!0,valueAxis:!0,ticks:!0,crosshair:!0,headroom:({unit:t})=>4*t,draw(a){const{rows:e,cx:s,y:c,unit:i,svg:l,barWidth:u,height:d,domain:h,config:f}=a,m=n(a.series),g=e.map(t=>r(t,m)),p=g.flatMap(t=>t?[t[2]]:[]),$=Math.max(u,i);g.forEach((n,r)=>{if(!n)return;const[a,u,d,h]=n,f=h>=a,m=(c(a)+c(h))/2,g=Math.max(Math.abs(c(a)-c(h)),i),p=m-g/2,b=(0,t.svgEl)("g",{class:(0,t.withRow)(`${t.BAR_CLASS} ${o}`,e[r]),"data-row":r+1,"data-move":f?"up":"down",...!f&&{"data-grow":"down"},style:(0,t.timing)("--i",e[r].slot)}),y=(n,a)=>{a-n>.01&&b.append((0,t.svgEl)("line",{class:`${o}-wick`,x1:s(r),x2:s(r),y1:n,y2:a}))};y(c(u),p),y(p+g,c(d)),b.append((0,t.svgEl)("rect",{class:`${o}-body`,x:s(r)-$/2,y:p,width:$,height:g})),l.append(b)});const b=p.length?-(0,t.largest)(p.map(t=>-t)):0,y=(0,t.largest)(g.flatMap(t=>t?[t[1]]:[])),x=c(h?h[0]:b);l.append((0,t.svgEl)("line",{class:t.RULE_CLASS,x1:0,x2:t.VIEWBOX_WIDTH,y1:x,y2:x}));const M=m[3],w=e.find(t=>t.cells[M]&&!t.cells[M].missing)?.cells[M],v=Math.max(0,(0,t.largest)(e.map(o=>(0,t.decimalsIn)(o.cells[M]?.number??"")))),A=c(y)-c(b)||1,k=o=>{const[n,r]=(0,t.splitSign)(b+(o-c(b))/A*(y-b),v,f.locale);return`${w?.prefix??""}${n}${r}${w?.suffix??""}`},S="right"===a.axisSide;a.crossingOf=n=>{const r=g[n];if(!r)return null;const e=l.querySelector(`.${o}[data-row="${n+1}"] .${o}-body`),u=e?getComputedStyle(e).stroke:void 0;return{x:s(n),from:h?c(h[1]):0,to:x,edge:S?t.VIEWBOX_WIDTH:0,end:S?0:t.VIEWBOX_WIDTH,axis:a.axisSide,height:d,points:m.map((o,a)=>({series:o,y:c(r[a]),dot:(0,t.svgEl)("circle",{class:"tablechart-crosshair-point",cx:s(n),cy:c(r[a]),r:3*i,style:`--tablechart-point-color:${u}`}),colour:u})),textAt:k,snap:8,around:[c(r[1]),c(r[2])]}}},tooltip(t,o,a){const e=n(t.series),s=r(t.rows[o],e);return e.map((n,r)=>{const e=t.seriesValues[n]?.[o]?.textContent??"",c=3===r&&s&&s[0]?` (${new Intl.NumberFormat(a.locale,{style:"percent",signDisplay:"exceptZero",maximumFractionDigits:1}).format((s[3]-s[0])/s[0])})`:"";return{name:t.series[n].name,value:e+c}})}},e=`${t.QUADRANT_CLASS}-name`,s=["top left","top right","bottom left","bottom right"],c=(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},i=["circle","triangle","square","diamond"],l=["right","left","above","below","right","left","right","left"],u=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)}},d=(t,o)=>o<0?[]:[...new Set(t.map(t=>t.cells[o]?.raw??""))],h=(t,o,n)=>t.cells[o]?.missing||t.cells[n]?.missing?null:`${t.cells[o]?.value} ${t.cells[n]?.value}`,f={aspect:.62,categories:"beside",columns:!0,axes:!0,numbers:!1,annotations:{quadrant:{create(o,n){for(const r of o.querySelectorAll("[data-corner]")){const o=(r.getAttribute("data-corner")??"").toLowerCase().split(/\s+/),a=`${o.find(t=>"top"===t||"bottom"===t)} ${o.find(t=>"left"===t||"right"===t)}`;if(!s.includes(a)){console.warn(`[${t.LIBRARY_NAME}] A quadrant has a corner "${r.getAttribute("data-corner")}". It takes ${s.join(", ")}.`);continue}const c=document.createElement("span");if(c.className=e,c.dataset.corner=a,c.inert=!0,r.childElementCount)for(const t of Array.from(r.children))c.append(t.cloneNode(!0));else c.textContent=(r.textContent??"").trim();for(const{name:t,value:n}of Array.from(r.attributes))"data-corner"!==t&&c.setAttribute(t,n);n.append(c)}return null},draw(o,n,r){const{frame:a,x:s,y:i,rows:l,series:u,height:d,unit:h,svg:f,plot:m,categories:g}=r;if(!a||!s)return void console.warn(`[${t.LIBRARY_NAME}] A quadrant works on a scatter chart, which has an axis across and an axis up.`);const[p,$,b,y]=a,x=u.flatMap((t,o)=>t.group?[]:[o]),M=t=>l.flatMap(o=>o.cells[x[t]]&&!o.cells[x[t]].missing?[o.cells[x[t]].value]:[]),w=c(o.getAttribute("data-x"),M(0)),v=c(o.getAttribute("data-y"),M(1)),A=null===w?p+b/2:Math.min(Math.max(s(w),p),p+b),k=null===v?$+y/2:Math.min(Math.max(i(v),$),$+y),S=f.querySelector(`:scope > .${t.GRID_CLASS}`);if(S){const t=(t,o)=>Math.abs(t-o)<h/2,o=(S.getAttribute("d")??"").match(/M[^M]+/g)??[];S.setAttribute("d",o.filter(o=>{const[,n,r,a]=o.match(/M(-?[\d.]+),(-?[\d.]+)([HV])/)??[];return!("V"===a&&t(Number(n),A)||"H"===a&&t(Number(r),k))}).join(""))}f.insertBefore((0,t.svgEl)("path",{class:t.QUADRANT_CLASS,d:`M${(0,t.short)(A)},${(0,t.short)($)}V${(0,t.short)($+y)}M${(0,t.short)(p)},${(0,t.short)(k)}H${(0,t.short)(p+b)}`}),f.querySelector(`:scope > .${t.POINT_CLASS}`));const q=6*h,z=[];for(const c of m.querySelectorAll(`:scope > .${e}`)){const o=c.dataset.corner??"",n=o.includes("right"),r=o.includes("bottom"),a=n?p+b-q:p+q,e=r?$+y-q:$+q;(0,t.placeAt)(c,a,e,t.VIEWBOX_WIDTH,d);const s=(0,t.sizeOf)(c).width*h,i=(0,t.sizeOf)(c).height*h;z.push([n?a-s:a,r?e-i:e,s,i])}r.taken.push(...z);for(const e of g){if(e.hidden||e.hasAttribute("data-left-out")||!e.style.left)continue;const o=(0,t.boxOf)(e,h,t.VIEWBOX_WIDTH,d,[0,0]);z.some(n=>(0,t.overlaps)(n,o))&&e.setAttribute("data-left-out","")}}}},text({figure:o,plot:n,rows:r,series:a,config:e}){const{x:s,y:c,size:l,group:f}=u(a),m=n.querySelector(":scope > .tablechart-categories")?.children??[],g=new Map;r.forEach((t,o)=>{const n=h(t,s,c);n&&(g.get(n)?.push(o)??g.set(n,[o]))});const p=t=>void 0===l?0:Math.abs(r[t].cells[l]?.value??0);for(const t of g.values()){if(t.length<2)continue;const o=t.reduce((t,o)=>p(o)>p(t)?o:t);for(const n of t){const r=m[n];n===o?r?.setAttribute("data-more",String(t.length-1)):r?.setAttribute("data-same","")}}const $=d(r,f);if($.length<2&&void 0===l)return;const b=document.createElement("div");b.className=t.LEGEND_CLASS,b.setAttribute("aria-hidden","true"),b.inert=!0;const y=Array.from((t=>Array.from(t.querySelectorAll("table")).find(t=>!t.closest(".chart-summary"))??null)(o)?.querySelectorAll(`tbody tr > td:nth-of-type(${f+1})`)??[]),x=(o,n)=>{const r=(0,t.htmlEl)(t.NAME_CLASS,b);return r.dataset.place="legend",r.dataset.line="none",r.textContent=o,n&&e.langattribute&&r.setAttribute(e.langattribute,n),r};$.length>1&&$.forEach((t,o)=>{const n=e.langattribute?y.find(o=>o.textContent?.trim()===t)?.getAttribute(e.langattribute):null,r=x(t,n);r.dataset.marker=i[o%i.length],r.style.setProperty("--n",String(o)),r.style.setProperty("--palette",`var(--tablechart-color-${o+1})`)}),void 0!==l&&(x(a[l].name,a[l].key).dataset.marker="size"),n.after(b)},draw(o){const{rows:n,series:r,x:a,y:e,frame:s,unit:c,svg:h,categories:f,height:m,config:g,plot:p}=o;if(!a||!s)return;const $=u(r),b=d(n,$.group),y=b.length>1,x=(o.length("--tablechart-marker-size","10px")??10*c)/2,M=(o.length("--tablechart-bubble-max","24px")??24*c)/2,w=(o.length("--tablechart-bubble-min","")??1.2*x)/2,v=t=>void 0===$.size?void 0:t.cells[$.size],A=(0,t.largest)(n.map(t=>Math.abs(v(t)?.value??0))),k=n.flatMap((t,o)=>{const n=t.cells[$.x],r=t.cells[$.y];if(!n||!r||n.missing||r.missing)return[];const s=v(t),c=s&&!s.missing&&A>0?Math.max(w,Math.sqrt(Math.abs(s.value)/A)*M):x,i=y?b.indexOf(t.cells[$.group]?.raw??""):-1;return[{i:o,px:a(n.value),py:e(r.value),r:c,g:i}]}),S=Math.min(k.length-1,6);p.style.setProperty("--count",String(S+1));const q=new Map([...k].sort((t,o)=>t.px-o.px||t.i-o.i).map((o,n)=>[o.i,S?(0,t.short)(n*S/(k.length-1)):0])),z=[...k].sort((t,o)=>o.r-t.r||t.i-o.i);for(const l of z){const o=y?i[l.g%i.length]:"circle";h.appendChild((0,t.markerEl)(o,l.px,l.py,l.r,{class:t.POINT_CLASS,"data-row":l.i+1,"data-marker":o,style:`--i:${q.get(l.i)}; ${y?`--n:${l.g}; --palette:var(--tablechart-color-${l.g+1})`:""}`}))}const N=p.parentElement?.getAttribute("data-chart-names"),H=null==N?g.names:"false"!==N,V=4*c,D=[s[0]+1.5*V,s[1],s[2]-1.5*V,s[3]-1.5*V],[I,C,F,T]=D,W=z.map(({i:o,px:n,py:r,r:a})=>{if(f[o].hasAttribute("data-same"))return[];const{width:e,height:s}=(0,t.sizeOf)(f[o]),i=e*c,l=s*c,u=.1*l,d=.7*a+V/2,h=(t,o)=>[Math.max(I,Math.min(t,I+F-i)),Math.max(C,Math.min(o,C+T-l))+u,i,l-2*u];return[h(n+a+V,r-l/2),h(n-a-V-i,r-l/2),h(n-i/2,r-a-V-l),h(n-i/2,r+a+V),h(n+d,r-d-l),h(n-d-i,r-d-l),h(n+d,r+d),h(n-d-i,r+d)]}),Z=H?(0,t.avoidCollisions)(W,k.map(({px:t,py:o,r:n})=>[t-n,o-n,2*n,2*n]),D):z.map(()=>null);for(const t of f)t.hidden=!0,t.dataset.missing="";z.forEach((o,n)=>{const r=f[o.i],a=Z[n];if(r.hidden=!1,delete r.dataset.missing,r.style.setProperty("--i",String(q.get(o.i))),r.toggleAttribute("data-left-out",null===a),null===a)return;r.dataset.side=l[a];const[e,s,,i]=W[n][a];(0,t.placeAt)(r,e,s-((0,t.sizeOf)(r).height*c-i)/2,t.VIEWBOX_WIDTH,m)}),o.rowAt=(t,o)=>{let n=null,r=6*c;for(const a of k){const e=Math.max(Math.hypot(a.px-t*c,a.py-o*c)-a.r,0);(e<r||e===r&&n&&a.r<n.r)&&(r=e,n=a)}return n?n.i:null}},tooltip(t,o){const{x:n,y:r}=u(t.series),a=h(t.rows[o],n,r),e=t.rows.flatMap((t,e)=>e===o||null!==a&&h(t,n,r)===a?[e]:[]),s=e.filter(o=>t.categories[o]?.hasAttribute("data-more"));return[...s,...e.filter(t=>!s.includes(t))].map(o=>({name:t.categories[o]?.textContent??"",value:""}))}},m=window.Tablechart;m.addType("scatter",f),m.addType("candlestick",a)}(Tablechart.kit);
@@ -1,11 +1,105 @@
1
- import { A as e, D as t, E as n, F as r, I as i, K as a, L as o, M as s, N as c, O as l, R as u, S as d, U as f, V as p, j as m, k as h, w as g } from "./tablechart-shared.mjs";
2
- // src/js/annotations/quadrant.ts
3
- var _ = `${f}-name`, v = [
1
+ import { A as e, B as t, C as n, D as r, E as i, F as a, G as o, H as s, I as c, M as l, N as u, O as d, P as f, R as p, S as m, T as h, V as g, Y as _, Z as v, j as y, k as b, q as x, w as S, z as C } from "./tablechart-shared.mjs";
2
+ // src/js/charts/candlestick.ts
3
+ var w = "tablechart-candle", T = (e) => e.flatMap((e, t) => e.group ? [] : [t]).slice(0, 4), E = (e, t) => {
4
+ let n = t.map((t) => e.cells[t]);
5
+ return n.length === 4 && n.every((e) => e && !e.missing) ? n.map((e) => e.value) : null;
6
+ }, D = {
7
+ columns: !0,
8
+ numbers: !1,
9
+ floats: !0,
10
+ valueAxis: !0,
11
+ ticks: !0,
12
+ crosshair: !0,
13
+ headroom: ({ unit: e }) => 4 * e,
14
+ draw(t) {
15
+ let { rows: n, cx: r, y: a, unit: o, svg: s, barWidth: c, height: u, domain: d, config: f } = t, m = T(t.series), g = n.map((e) => E(e, m)), b = g.flatMap((e) => e ? [e[2]] : []), x = Math.max(c, o);
16
+ g.forEach((t, i) => {
17
+ if (!t) return;
18
+ let [c, u, d, f] = t, m = f >= c, h = (a(c) + a(f)) / 2, g = Math.max(Math.abs(a(c) - a(f)), o), _ = h - g / 2, v = e("g", {
19
+ class: y(`${p} ${w}`, n[i]),
20
+ "data-row": i + 1,
21
+ "data-move": m ? "up" : "down",
22
+ ...!m && { "data-grow": "down" },
23
+ style: l("--i", n[i].slot)
24
+ }), b = (t, n) => {
25
+ n - t > .01 && v.append(e("line", {
26
+ class: `${w}-wick`,
27
+ x1: r(i),
28
+ x2: r(i),
29
+ y1: t,
30
+ y2: n
31
+ }));
32
+ };
33
+ b(a(u), _), b(_ + g, a(d)), v.append(e("rect", {
34
+ class: `${w}-body`,
35
+ x: r(i) - x / 2,
36
+ y: _,
37
+ width: x,
38
+ height: g
39
+ })), s.append(v);
40
+ });
41
+ let C = b.length ? -h(b.map((e) => -e)) : 0, D = h(g.flatMap((e) => e ? [e[1]] : [])), O = a(d ? d[0] : C);
42
+ s.append(e("line", {
43
+ class: _,
44
+ x1: 0,
45
+ x2: v,
46
+ y1: O,
47
+ y2: O
48
+ }));
49
+ let k = m[3], A = n.find((e) => e.cells[k] && !e.cells[k].missing)?.cells[k], j = Math.max(0, h(n.map((e) => S(e.cells[k]?.number ?? "")))), M = a(D) - a(C) || 1, N = (e) => {
50
+ let [t, n] = i(C + (e - a(C)) / M * (D - C), j, f.locale);
51
+ return `${A?.prefix ?? ""}${t}${n}${A?.suffix ?? ""}`;
52
+ }, P = t.axisSide === "right";
53
+ t.crossingOf = (n) => {
54
+ let i = g[n];
55
+ if (!i) return null;
56
+ let c = s.querySelector(`.${w}[data-row="${n + 1}"] .${w}-body`), l = c ? getComputedStyle(c).stroke : void 0;
57
+ return {
58
+ x: r(n),
59
+ from: d ? a(d[1]) : 0,
60
+ to: O,
61
+ edge: P ? v : 0,
62
+ end: P ? 0 : v,
63
+ axis: t.axisSide,
64
+ height: u,
65
+ points: m.map((t, s) => ({
66
+ series: t,
67
+ y: a(i[s]),
68
+ dot: e("circle", {
69
+ class: "tablechart-crosshair-point",
70
+ cx: r(n),
71
+ cy: a(i[s]),
72
+ r: 3 * o,
73
+ style: `--tablechart-point-color:${l}`
74
+ }),
75
+ colour: l
76
+ })),
77
+ textAt: N,
78
+ snap: 8,
79
+ around: [a(i[1]), a(i[2])]
80
+ };
81
+ };
82
+ },
83
+ tooltip(e, t, n) {
84
+ let r = T(e.series), i = E(e.rows[t], r);
85
+ return r.map((r, a) => {
86
+ let o = e.seriesValues[r]?.[t]?.textContent ?? "", s = a === 3 && i && i[0] ? ` (${new Intl.NumberFormat(n.locale, {
87
+ style: "percent",
88
+ signDisplay: "exceptZero",
89
+ maximumFractionDigits: 1
90
+ }).format((i[3] - i[0]) / i[0])})` : "";
91
+ return {
92
+ name: e.series[r].name,
93
+ value: o + s
94
+ };
95
+ });
96
+ }
97
+ }, O = `${x}-name`, k = [
4
98
  "top left",
5
99
  "top right",
6
100
  "bottom left",
7
101
  "bottom right"
8
- ], y = (e, t) => {
102
+ ], A = (e, t) => {
9
103
  if (!e) return null;
10
104
  if (e === "mean") return t.length ? t.reduce((e, t) => e + t, 0) / t.length : null;
11
105
  if (e === "median") {
@@ -14,65 +108,65 @@ var _ = `${f}-name`, v = [
14
108
  }
15
109
  let n = Number.parseFloat(e);
16
110
  return Number.isFinite(n) ? n : null;
17
- }, b = {
111
+ }, j = {
18
112
  create(e, t) {
19
113
  for (let n of e.querySelectorAll("[data-corner]")) {
20
114
  let e = (n.getAttribute("data-corner") ?? "").toLowerCase().split(/\s+/), r = `${e.find((e) => e === "top" || e === "bottom")} ${e.find((e) => e === "left" || e === "right")}`;
21
- if (!v.includes(r)) {
22
- console.warn(`[${o}] A quadrant has a corner "${n.getAttribute("data-corner")}". It takes ${v.join(", ")}.`);
115
+ if (!k.includes(r)) {
116
+ console.warn(`[${g}] A quadrant has a corner "${n.getAttribute("data-corner")}". It takes ${k.join(", ")}.`);
23
117
  continue;
24
118
  }
25
119
  let i = document.createElement("span");
26
- if (i.className = _, i.dataset.corner = r, i.inert = !0, n.childElementCount) for (let e of Array.from(n.children)) i.append(e.cloneNode(!0));
120
+ if (i.className = O, i.dataset.corner = r, i.inert = !0, n.childElementCount) for (let e of Array.from(n.children)) i.append(e.cloneNode(!0));
27
121
  else i.textContent = (n.textContent ?? "").trim();
28
122
  for (let { name: e, value: t } of Array.from(n.attributes)) e !== "data-corner" && i.setAttribute(e, t);
29
123
  t.append(i);
30
124
  }
31
125
  return null;
32
126
  },
33
- draw(n, i, s) {
34
- let { frame: u, x: d, y: g, rows: v, series: b, height: x, unit: S, svg: C, plot: w, categories: T } = s;
35
- if (!u || !d) {
36
- console.warn(`[${o}] A quadrant works on a scatter chart, which has an axis across and an axis up.`);
127
+ draw(t, n, r) {
128
+ let { frame: i, x: a, y: s, rows: l, series: p, height: m, unit: h, svg: _, plot: y, categories: S } = r;
129
+ if (!i || !a) {
130
+ console.warn(`[${g}] A quadrant works on a scatter chart, which has an axis across and an axis up.`);
37
131
  return;
38
132
  }
39
- let [E, D, O, k] = u, 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(n.getAttribute("data-x"), j(0)), N = y(n.getAttribute("data-y"), j(1)), P = M === null ? E + O / 2 : Math.min(Math.max(d(M), E), E + O), F = N === null ? D + k / 2 : Math.min(Math.max(g(N), D), D + k), I = C.querySelector(`:scope > .${r}`);
133
+ let [w, T, E, D] = i, k = p.flatMap((e, t) => e.group ? [] : [t]), j = (e) => l.flatMap((t) => t.cells[k[e]] && !t.cells[k[e]].missing ? [t.cells[k[e]].value] : []), M = A(t.getAttribute("data-x"), j(0)), N = A(t.getAttribute("data-y"), j(1)), P = M === null ? w + E / 2 : Math.min(Math.max(a(M), w), w + E), F = N === null ? T + D / 2 : Math.min(Math.max(s(N), T), T + D), I = _.querySelector(`:scope > .${C}`);
40
134
  if (I) {
41
- let e = (e, t) => Math.abs(e - t) < S / 2, t = (I.getAttribute("d") ?? "").match(/M[^M]+/g) ?? [];
135
+ let e = (e, t) => Math.abs(e - t) < h / 2, t = (I.getAttribute("d") ?? "").match(/M[^M]+/g) ?? [];
42
136
  I.setAttribute("d", t.filter((t) => {
43
137
  let [, n, r, i] = t.match(/M(-?[\d.]+),(-?[\d.]+)([HV])/) ?? [];
44
138
  return !(i === "V" && e(Number(n), P)) && !(i === "H" && e(Number(r), F));
45
139
  }).join(""));
46
140
  }
47
- C.insertBefore(h("path", {
48
- class: f,
49
- d: `M${l(P)},${l(D)}V${l(D + k)}M${l(E)},${l(F)}H${l(E + O)}`
50
- }), C.querySelector(`:scope > .${p}`));
51
- let L = 6 * S, R = [];
52
- for (let e of w.querySelectorAll(`:scope > .${_}`)) {
53
- let n = e.dataset.corner ?? "", r = n.includes("right"), i = n.includes("bottom"), o = r ? E + O - L : E + L, s = i ? D + k - L : D + L;
54
- t(e, o, s, a, x);
55
- let c = m(e).width * S, l = m(e).height * S;
141
+ _.insertBefore(e("path", {
142
+ class: x,
143
+ d: `M${b(P)},${b(T)}V${b(T + D)}M${b(w)},${b(F)}H${b(w + E)}`
144
+ }), _.querySelector(`:scope > .${o}`));
145
+ let L = 6 * h, R = [];
146
+ for (let e of y.querySelectorAll(`:scope > .${O}`)) {
147
+ let t = e.dataset.corner ?? "", n = t.includes("right"), r = t.includes("bottom"), i = n ? w + E - L : w + L, a = r ? T + D - L : T + L;
148
+ d(e, i, a, v, m);
149
+ let o = f(e).width * h, s = f(e).height * h;
56
150
  R.push([
57
- r ? o - c : o,
58
- i ? s - l : s,
59
- c,
60
- l
151
+ n ? i - o : i,
152
+ r ? a - s : a,
153
+ o,
154
+ s
61
155
  ]);
62
156
  }
63
- s.taken.push(...R);
64
- for (let t of T) {
65
- if (t.hidden || t.hasAttribute("data-left-out") || !t.style.left) continue;
66
- let n = e(t, S, a, x, [0, 0]);
67
- R.some((e) => c(e, n)) && t.setAttribute("data-left-out", "");
157
+ r.taken.push(...R);
158
+ for (let e of S) {
159
+ if (e.hidden || e.hasAttribute("data-left-out") || !e.style.left) continue;
160
+ let t = u(e, h, v, m, [0, 0]);
161
+ R.some((e) => c(e, t)) && e.setAttribute("data-left-out", "");
68
162
  }
69
163
  }
70
- }, x = [
164
+ }, M = [
71
165
  "circle",
72
166
  "triangle",
73
167
  "square",
74
168
  "diamond"
75
- ], S = [
169
+ ], N = [
76
170
  "right",
77
171
  "left",
78
172
  "above",
@@ -81,7 +175,7 @@ var _ = `${f}-name`, v = [
81
175
  "left",
82
176
  "right",
83
177
  "left"
84
- ], C = (e) => {
178
+ ], P = (e) => {
85
179
  let t = e.flatMap((e, t) => e.group ? [] : [t]);
86
180
  return {
87
181
  x: t[0],
@@ -89,75 +183,75 @@ var _ = `${f}-name`, v = [
89
183
  size: t[2],
90
184
  group: e.findIndex((e) => e.group)
91
185
  };
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 = {
186
+ }, F = (e, t) => t < 0 ? [] : [...new Set(e.map((e) => e.cells[t]?.raw ?? ""))], I = (e, t, n) => e.cells[t]?.missing || e.cells[n]?.missing ? null : `${e.cells[t]?.value} ${e.cells[n]?.value}`, L = {
93
187
  aspect: .62,
94
188
  categories: "beside",
95
189
  columns: !0,
96
190
  axes: !0,
97
191
  numbers: !1,
98
- annotations: { quadrant: b },
99
- text({ figure: e, plot: t, rows: r, series: a, config: o }) {
100
- let { x: s, y: c, size: l, group: d } = C(a), f = t.querySelector(":scope > .tablechart-categories")?.children ?? [], p = /* @__PURE__ */ new Map();
101
- r.forEach((e, t) => {
102
- let n = T(e, s, c);
192
+ annotations: { quadrant: j },
193
+ text({ figure: e, plot: n, rows: i, series: a, config: o }) {
194
+ let { x: c, y: l, size: u, group: d } = P(a), f = n.querySelector(":scope > .tablechart-categories")?.children ?? [], p = /* @__PURE__ */ new Map();
195
+ i.forEach((e, t) => {
196
+ let n = I(e, c, l);
103
197
  n && (p.get(n)?.push(t) ?? p.set(n, [t]));
104
198
  });
105
- let m = (e) => l === void 0 ? 0 : Math.abs(r[e].cells[l]?.value ?? 0);
199
+ let h = (e) => u === void 0 ? 0 : Math.abs(i[e].cells[u]?.value ?? 0);
106
200
  for (let e of p.values()) {
107
201
  if (e.length < 2) continue;
108
- let t = e.reduce((e, t) => m(t) > m(e) ? t : e);
202
+ let t = e.reduce((e, t) => h(t) > h(e) ? t : e);
109
203
  for (let n of e) {
110
204
  let r = f[n];
111
205
  n === t ? r?.setAttribute("data-more", String(e.length - 1)) : r?.setAttribute("data-same", "");
112
206
  }
113
207
  }
114
- let h = w(r, d);
115
- if (h.length < 2 && l === void 0) return;
116
- let g = document.createElement("div");
117
- g.className = i, 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 r = n(u, g);
120
- return r.dataset.place = "legend", r.dataset.line = "none", r.textContent = e, t && o.langattribute && r.setAttribute(o.langattribute, t), r;
208
+ let g = F(i, d);
209
+ if (g.length < 2 && u === void 0) return;
210
+ let _ = document.createElement("div");
211
+ _.className = t, _.setAttribute("aria-hidden", "true"), _.inert = !0;
212
+ let v = Array.from(m(e)?.querySelectorAll(`tbody tr > td:nth-of-type(${d + 1})`) ?? []), y = (e, t) => {
213
+ let n = r(s, _);
214
+ return n.dataset.place = "legend", n.dataset.line = "none", n.textContent = e, t && o.langattribute && n.setAttribute(o.langattribute, t), n;
121
215
  };
122
- h.length > 1 && h.forEach((e, t) => {
123
- let n = o.langattribute ? _.find((t) => t.textContent?.trim() === e)?.getAttribute(o.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
- }), l !== void 0 && (v(a[l].name, a[l].key).dataset.marker = "size"), t.after(g);
216
+ g.length > 1 && g.forEach((e, t) => {
217
+ let n = o.langattribute ? v.find((t) => t.textContent?.trim() === e)?.getAttribute(o.langattribute) : null, r = y(e, n);
218
+ r.dataset.marker = M[t % M.length], r.style.setProperty("--n", String(t)), r.style.setProperty("--palette", `var(--tablechart-color-${t + 1})`);
219
+ }), u !== void 0 && (y(a[u].name, a[u].key).dataset.marker = "size"), n.after(_);
126
220
  },
127
221
  draw(e) {
128
- let { rows: n, series: r, x: i, y: o, frame: c, unit: u, svg: f, categories: h, height: _, config: v, plot: y } = e;
222
+ let { rows: t, series: r, x: i, y: s, frame: c, unit: l, svg: u, categories: p, height: m, config: g, plot: _ } = e;
129
223
  if (!i || !c) return;
130
- let b = C(r), T = w(n, b.group), E = T.length > 1, D = (e.length("--tablechart-marker-size", "10px") ?? 10 * u) / 2, O = (e.length("--tablechart-bubble-max", "24px") ?? 24 * u) / 2, k = (e.length("--tablechart-bubble-min", "") ?? D * 1.2) / 2, A = (e) => b.size === void 0 ? void 0 : e.cells[b.size], j = g(n.map((e) => Math.abs(A(e)?.value ?? 0))), M = n.flatMap((e, t) => {
131
- let n = e.cells[b.x], r = e.cells[b.y];
224
+ let y = P(r), x = F(t, y.group), S = x.length > 1, C = (e.length("--tablechart-marker-size", "10px") ?? 10 * l) / 2, w = (e.length("--tablechart-bubble-max", "24px") ?? 24 * l) / 2, T = (e.length("--tablechart-bubble-min", "") ?? C * 1.2) / 2, E = (e) => y.size === void 0 ? void 0 : e.cells[y.size], D = h(t.map((e) => Math.abs(E(e)?.value ?? 0))), O = t.flatMap((e, t) => {
225
+ let n = e.cells[y.x], r = e.cells[y.y];
132
226
  if (!n || !r || n.missing || r.missing) return [];
133
- let a = A(e), s = a && !a.missing && j > 0 ? Math.max(k, Math.sqrt(Math.abs(a.value) / j) * O) : D, c = E ? T.indexOf(e.cells[b.group]?.raw ?? "") : -1;
227
+ let a = E(e), o = a && !a.missing && D > 0 ? Math.max(T, Math.sqrt(Math.abs(a.value) / D) * w) : C, c = S ? x.indexOf(e.cells[y.group]?.raw ?? "") : -1;
134
228
  return [{
135
229
  i: t,
136
230
  px: i(n.value),
137
- py: o(r.value),
138
- r: s,
231
+ py: s(r.value),
232
+ r: o,
139
233
  g: c
140
234
  }];
141
- }), N = Math.min(M.length - 1, 6);
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((e, t) => [e.i, N ? l(t * N / (M.length - 1)) : 0])), F = [...M].sort((e, t) => t.r - e.r || e.i - t.i);
144
- for (let e of F) {
145
- let t = E ? x[e.g % x.length] : "circle";
146
- f.appendChild(d(t, e.px, e.py, e.r, {
147
- class: p,
235
+ }), k = Math.min(O.length - 1, 6);
236
+ _.style.setProperty("--count", String(k + 1));
237
+ let A = new Map([...O].sort((e, t) => e.px - t.px || e.i - t.i).map((e, t) => [e.i, k ? b(t * k / (O.length - 1)) : 0])), j = [...O].sort((e, t) => t.r - e.r || e.i - t.i);
238
+ for (let e of j) {
239
+ let t = S ? M[e.g % M.length] : "circle";
240
+ u.appendChild(n(t, e.px, e.py, e.r, {
241
+ class: o,
148
242
  "data-row": e.i + 1,
149
243
  "data-marker": t,
150
- style: `--i:${P.get(e.i)}; ${E ? `--n:${e.g}; --palette:var(--tablechart-color-${e.g + 1})` : ""}`
244
+ style: `--i:${A.get(e.i)}; ${S ? `--n:${e.g}; --palette:var(--tablechart-color-${e.g + 1})` : ""}`
151
245
  }));
152
246
  }
153
- let I = y.parentElement?.getAttribute("data-chart-names"), L = I == null ? v.names : I !== "false", R = 4 * u, z = [
247
+ let I = _.parentElement?.getAttribute("data-chart-names"), L = I == null ? g.names : I !== "false", R = 4 * l, z = [
154
248
  c[0] + 1.5 * R,
155
249
  c[1],
156
250
  c[2] - 1.5 * R,
157
251
  c[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 } = m(h[e]), o = i * u, s = a * u, c = s * .1, l = r * .7 + R / 2, d = (e, t) => [
252
+ ], [B, V, H, U] = z, W = j.map(({ i: e, px: t, py: n, r }) => {
253
+ if (p[e].hasAttribute("data-same")) return [];
254
+ let { width: i, height: a } = f(p[e]), o = i * l, s = a * l, c = s * .1, u = r * .7 + R / 2, d = (e, t) => [
161
255
  Math.max(B, Math.min(e, B + H - o)),
162
256
  Math.max(V, Math.min(t, V + U - s)) + c,
163
257
  o,
@@ -168,39 +262,39 @@ var _ = `${f}-name`, v = [
168
262
  d(t - r - R - o, n - s / 2),
169
263
  d(t - o / 2, n - r - R - s),
170
264
  d(t - o / 2, n + r + R),
171
- d(t + l, n - l - s),
172
- d(t - l - o, n - l - s),
173
- d(t + l, n + l),
174
- d(t - l - o, n + l)
265
+ d(t + u, n - u - s),
266
+ d(t - u - o, n - u - s),
267
+ d(t + u, n + u),
268
+ d(t - u - o, n + u)
175
269
  ];
176
- }), G = L ? s(W, M.map(({ px: e, py: t, r: n }) => [
270
+ }), G = L ? a(W, O.map(({ px: e, py: t, r: n }) => [
177
271
  e - n,
178
272
  t - n,
179
273
  2 * n,
180
274
  2 * n
181
- ]), z) : F.map(() => null);
182
- for (let e of h) e.hidden = !0, e.dataset.missing = "";
183
- F.forEach((e, n) => {
184
- let r = h[e.i], i = G[n];
185
- if (r.hidden = !1, delete r.dataset.missing, r.style.setProperty("--i", String(P.get(e.i))), r.toggleAttribute("data-left-out", i === null), i === null) return;
186
- r.dataset.side = S[i];
187
- let [o, s, , c] = W[n][i];
188
- t(r, o, s - (m(r).height * u - c) / 2, a, _);
275
+ ]), z) : j.map(() => null);
276
+ for (let e of p) e.hidden = !0, e.dataset.missing = "";
277
+ j.forEach((e, t) => {
278
+ let n = p[e.i], r = G[t];
279
+ if (n.hidden = !1, delete n.dataset.missing, n.style.setProperty("--i", String(A.get(e.i))), n.toggleAttribute("data-left-out", r === null), r === null) return;
280
+ n.dataset.side = N[r];
281
+ let [i, a, , o] = W[t][r];
282
+ d(n, i, a - (f(n).height * l - o) / 2, v, m);
189
283
  }), e.rowAt = (e, t) => {
190
- let n = null, r = 6 * u;
191
- for (let i of M) {
192
- let a = Math.max(Math.hypot(i.px - e * u, i.py - t * u) - i.r, 0);
284
+ let n = null, r = 6 * l;
285
+ for (let i of O) {
286
+ let a = Math.max(Math.hypot(i.px - e * l, i.py - t * l) - i.r, 0);
193
287
  (a < r || a === r && n && i.r < n.r) && (r = a, n = i);
194
288
  }
195
289
  return n ? n.i : null;
196
290
  };
197
291
  },
198
292
  tooltip(e, t) {
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"));
293
+ let { x: n, y: r } = P(e.series), i = I(e.rows[t], n, r), a = e.rows.flatMap((e, a) => a === t || i !== null && I(e, n, r) === i ? [a] : []), o = a.filter((t) => e.categories[t]?.hasAttribute("data-more"));
200
294
  return [...o, ...a.filter((e) => !o.includes(e))].map((t) => ({
201
295
  name: e.categories[t]?.textContent ?? "",
202
296
  value: ""
203
297
  }));
204
298
  }
205
299
  };
206
- export { E as scatter };
300
+ export { D as candlestick, L as scatter };
@@ -1,2 +1,2 @@
1
- import { A as PRINTING_CLASS, C as Series, D as Config, E as AT_ONCE_CLASS, M as SHOWN_CLASS, N as defaultConfig, O as PLACED_CLASS, S as Row, T as stepTo, _ as unbuild, a as loadSources, b as ChartState, f as prepare, g as relabel, h as build, i as adoptTables, j as QUEUED_CLASS, k as PLOT_CLASS, m as layout, n as checkTextContrast, o as readMarkdownRows, p as setShown, r as assignKeys, t as checkContrast, u as entranceOf, v as addType, w as StepPlan, x as ChartType } from "./tablechart-shared.mjs";
1
+ import { A as StepPlan, C as addType, D as ChartType, E as ChartState, F as PLOT_CLASS, I as PRINTING_CLASS, L as QUEUED_CLASS, M as AT_ONCE_CLASS, N as Config, O as Row, P as PLACED_CLASS, R as SHOWN_CLASS, S as unbuild, _ as prepare, b as build, c as assignKeys, d as readMarkdownRows, h as entranceOf, j as stepTo, k as Series, l as adoptTables, o as checkContrast, s as checkTextContrast, u as loadSources, v as setShown, x as relabel, y as layout, z as defaultConfig } from "./tablechart-shared.mjs";
2
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, checkContrast, checkTextContrast, defaultConfig, entranceOf, layout, loadSources, prepare, readMarkdownRows, relabel, setShown, stepTo, unbuild };
@@ -1 +1 @@
1
- export { P as AT_ONCE_CLASS, z as PLACED_CLASS, B as PLOT_CLASS, H as PRINTING_CLASS, W as QUEUED_CLASS, G as SHOWN_CLASS, h as addType, a as adoptTables, y as assignKeys, d as build, c as checkContrast, l as checkTextContrast, q as defaultConfig, n as entranceOf, u as layout, o as loadSources, i as prepare, s as readMarkdownRows, f as relabel, m as setShown, v as stepTo, p as unbuild } from "./tablechart-shared.mjs";
1
+ export { L as AT_ONCE_CLASS, U as PLACED_CLASS, W as PLOT_CLASS, K as PRINTING_CLASS, J as QUEUED_CLASS, X as SHOWN_CLASS, h as addType, a as adoptTables, y as assignKeys, d as build, c as checkContrast, l as checkTextContrast, Q as defaultConfig, n as entranceOf, u as layout, o as loadSources, i as prepare, s as readMarkdownRows, f as relabel, m as setShown, v as stepTo, p as unbuild } from "./tablechart-shared.mjs";