@graphysdk/geom-boxplot 0.1.1-beta.1792399585685 → 0.1.1-beta.1792404455940

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/dist/index.cjs CHANGED
@@ -1 +1 @@
1
- Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});let s=require("@graphysdk/react"),W=require("react"),f=require("react/jsx-runtime");var x={q1:"boxplotQ1",median:"boxplotMedian",q3:"boxplotQ3",lowerWhisker:"boxplotLowerWhisker",upperWhisker:"boxplotUpperWhisker",count:"boxplotCount",mean:"boxplotMean",notchLower:"boxplotNotchLower",notchUpper:"boxplotNotchUpper",relativeWidth:"boxplotRelativeWidth",outlier:"boxplotOutlier"},_=e=>(e.start+e.end)/2,H=(e,t)=>{const r=t==="x"?(0,s.getXMin)(e):(0,s.getYMin)(e),o=t==="x"?(0,s.getXMax)(e):(0,s.getYMax)(e),a=t==="x"?(0,s.getYMin)(e):(0,s.getXMin)(e),n=t==="x"?(0,s.getYMax)(e):(0,s.getXMax)(e),l=(0,s.getScaledAesthetic)(e,"q1"),u=(0,s.getScaledAesthetic)(e,"median"),c=(0,s.getScaledAesthetic)(e,"q3");if(r===null||o===null||a===null||n===null||u===null||l===null||c===null)return null;const i=T(e,"notchLower"),d=T(e,"notchUpper"),[m,k]=ne(r,o,e);return{observation:e,start:m,end:k,lowerWhisker:a,q1:l,median:u,q3:c,upperWhisker:n,mean:T(e,"mean"),notch:i===null||d===null?null:{lower:i,upper:d}}},F=(e,t)=>{const r=t==="x"?(0,s.getXMin)(e):(0,s.getYMin)(e),o=t==="x"?(0,s.getXMax)(e):(0,s.getYMax)(e),a=(0,s.getScaledAesthetic)(e,"outlier");return r===null||o===null||a===null?null:{observation:e,band:_({start:r,end:o}),value:a}},S=(e,t)=>{const r=[],o=[];for(const a of e){const n=H(a,t);if(n!==null){r.push(n);continue}const l=F(a,t);l!==null&&o.push(l)}return{boxes:r,outliers:o}},T=(e,t)=>(0,s.getScaledAesthetic)(e,t)??null,ne=(e,t,r)=>{const o=r[x.relativeWidth];if(typeof o!="number")return[e,t];const a=(e+t)/2,n=(t-e)*o/2;return[a-n,a+n]},b=1.5,G=e=>e==="min-max"||typeof e=="number"&&Number.isFinite(e)&&e>=0,se=1.58,le=(e,t)=>{if(e.length===0)return null;const r=[...e].sort((m,k)=>m-k),o=B(r,.25),a=B(r,.5),n=B(r,.75),l=t==="min-max"?1/0:t*(n-o),u=o-l,c=n+l,i=r.filter(m=>m>=u&&m<=c),d=se*(n-o)/Math.sqrt(e.length);return{lowerWhisker:Math.min(i[0]??o,o),q1:o,median:a,q3:n,upperWhisker:Math.max(i[i.length-1]??n,n),count:e.length,mean:r.reduce((m,k)=>m+k,0)/r.length,notchLower:a-d,notchUpper:a+d}},B=(e,t)=>{const r=(e.length-1)*t,o=Math.floor(r),a=e[o]??0;return a+((e[o+1]??a)-a)*(r-o)},Y={q1:x.q1,median:x.median,q3:x.q3,lowerWhisker:x.lowerWhisker,upperWhisker:x.upperWhisker,count:x.count,outlier:x.outlier},z=new Set([...Object.keys(Y),"mean","notchLower","notchUpper"]),ie=class extends s.Stat{constructor(...e){super(...e),this.type="boxplot",this.computedVariables=z}computeStat({data:e,mapping:t,params:r}){const o=(0,s.readVariableName)(t.x),a=(0,s.readVariableName)(t.y);if(o===null||a===null||!e.hasVariable(o)||!e.hasVariable(a))return{data:e,mapping:{}};if(e.size()>0&&e.getType(a)!=="numeric"){const l=e.getType(a);throw new s.UserInputError({code:"INCOMPATIBLE_TYPE",message:`'boxplot' stat requires a numeric 'y' (got '${l}' for variable '${a}')`,context:{aesthetic:"y",variableName:a,variableType:l},suggestion:"Map the raw values to 'y', or summarised columns under stat 'identity'."})}const n=r?.extent;return{data:ue({data:e,xVariable:o,yVariable:a,groupVariables:(0,s.getGroupVariables)(e,t).filter(l=>l!==o),extent:G(n)?n:b}),mapping:Y}}},ue=({data:e,xVariable:t,yVariable:r,groupVariables:o,extent:a})=>{const n=ce(e,t,r,o),l=e.getVariableNames(),u=new Map(l.map(p=>[p,e.getValues(p)])),c=new Map(l.map(p=>[p,[]])),i={q1:[],median:[],q3:[],lowerWhisker:[],upperWhisker:[],count:[],mean:[],notchLower:[],notchUpper:[],outlier:[]},d=[];for(const p of n){const v=le(p.values,a);if(v===null)continue;for(const h of l)c.get(h)?.push(he(u.get(h)??[],p.indices));i.q1.push(v.q1),i.median.push(v.median),i.q3.push(v.q3),i.lowerWhisker.push(v.lowerWhisker),i.upperWhisker.push(v.upperWhisker),i.count.push(v.count),i.mean.push(v.mean),i.notchLower.push(v.notchLower),i.notchUpper.push(v.notchUpper),i.outlier.push(null);const w={low:v.lowerWhisker,high:v.upperWhisker};p.indices.forEach((h,y)=>{const M=p.values[y];M!==void 0&&(M<w.low||M>w.high)&&d.push({index:h,value:M})})}for(const{index:p,value:v}of d){for(const w of l)c.get(w)?.push(u.get(w)?.[p]??null);i.q1.push(null),i.median.push(null),i.q3.push(null),i.lowerWhisker.push(null),i.upperWhisker.push(null),i.count.push(null),i.mean.push(null),i.notchLower.push(null),i.notchUpper.push(null),i.outlier.push(v)}const m=e.getValueFormat(r),k=Object.fromEntries(l.map(p=>[p,{type:e.getType(p),values:c.get(p)??[],valueFormat:e.getValueFormat(p)}]));for(const[p,v]of Object.entries(i)){const w=x[p];k[w]=p==="count"?{type:"numeric",values:v}:{type:"numeric",values:v,valueFormat:m}}return new s.Dataset(k)},ce=(e,t,r,o)=>{const a=[t,...o].map(u=>e.getValues(u)),n=e.getValues(r,{type:"numeric"}),l=new Map;return n.forEach((u,c)=>{if(u===null||!Number.isFinite(u))return;const i=a.map(m=>V(m[c]??null)).join("\0"),d=l.get(i)??{indices:[],values:[]};d.indices.push(c),d.values.push(u),l.set(i,d)}),[...l.values()]},he=(e,t)=>{const r=e[t[0]??-1]??null,o=V(r);return t.every(a=>V(e[a]??null)===o)?r:null},V=e=>e===null?"\0null":e instanceof Date?`date:${e.getTime()}`:`${typeof e}:${String(e)}`,Q=.6,de=1.5,pe=.3,me=2,C=1.5,ve=6,ge=1.5,fe=7,ke=1.5,ye=["q1","median","q3"],xe={strokeWidth:"pixels"},q={stroke:"color",alpha:"unitInterval",strokeWidth:"pixels",dashArray:"dashArray"},R={fill:"color",fillAlpha:"unitInterval",stroke:"color",alpha:"unitInterval",size:"pixels",strokeWidth:"pixels"},Z=class extends s.Geom{constructor(...e){super(...e),this.type="boxplot",this.defaultParams={extent:b,outliers:!0,notch:!1,showMean:!1,varwidth:!1},this.defaultStat=new ie,this.derivedVariables=Object.values(x),this.positionRoles=[{axis:"x",role:"point",valueKind:"value"},{axis:"y",role:"min",valueKind:"value",aes:"lowerWhisker"},{axis:"y",role:"max",valueKind:"value",aes:"upperWhisker"},{axis:"y",role:"scalar",valueKind:"value",aes:"q1"},{axis:"y",role:"scalar",valueKind:"value",aes:"median"},{axis:"y",role:"scalar",valueKind:"value",aes:"q3"},{axis:"y",role:"scalar",valueKind:"value",aes:"outlier"},{axis:"y",role:"scalar",valueKind:"value",aes:"mean"},{axis:"y",role:"scalar",valueKind:"value",aes:"notchLower"},{axis:"y",role:"scalar",valueKind:"value",aes:"notchUpper"}],this.aesthetics=[{kind:"visual",name:"color"},{kind:"data",name:"y"},{kind:"data",name:"count"}],this.defaultPosition="dodge",this.supportedPositions=["identity","dodge"],this.supportedCoordTypes=["cartesian","flip"],this.scaleConstraints={discreteMainAxis:!0},this.legend={suppressWhenSingleItem:!0},this.spatialKind="rects",this.highlightStrategy="observation-rerender",this.tooltip=[{key:"Upper whisker",aes:"upperWhisker",optional:!0},{key:"Q3",aes:"q3",optional:!0},{key:"Median",aes:"median",optional:!0},{key:"Q1",aes:"q1",optional:!0},{key:"Lower whisker",aes:"lowerWhisker",optional:!0},{key:"Count",aes:"count",optional:!0},{key:"Mean",aes:"mean",optional:!0},{key:"Outlier",aes:"outlier",optional:!0}],this.styleTarget=(0,s.defineGeomStyleTarget)({observation:{vocabulary:xe,aesthetics:{fill:"color",stroke:"color"},rest:{fill:(0,s.token)("geom"),fillAlpha:pe,stroke:(0,s.token)("geom"),strokeWidth:de}},parts:{median:{vocabulary:q,aesthetics:{stroke:"color"},rest:{stroke:(0,s.token)("geom"),alpha:1,strokeWidth:me,dashArray:[]}},whisker:{vocabulary:q,aesthetics:{stroke:"color"},rest:{stroke:(0,s.token)("geom"),alpha:1,strokeWidth:C,dashArray:[]}},cap:{vocabulary:q,aesthetics:{stroke:"color"},rest:{stroke:(0,s.token)("geom"),alpha:1,strokeWidth:C,dashArray:[]}},outlier:{vocabulary:R,aesthetics:{fill:"color",stroke:"color"},rest:{fill:(0,s.token)("geom"),fillAlpha:0,stroke:(0,s.token)("geom"),alpha:1,size:ve,strokeWidth:ge}},mean:{vocabulary:R,aesthetics:{fill:"color"},rest:{fill:(0,s.token)("geom"),fillAlpha:1,stroke:(0,s.token)("graphBackground"),alpha:1,size:fe,strokeWidth:ke}}}}),this.resolveBandFraction=t=>D(t),this.resolveValueSource=t=>t.median??{variable:x.median},this.validateMapping=({mapping:t,computedVariables:r})=>r.has("y")?[{code:"CONFLICTING_STAT_MAPPING",message:"boxplot geom draws a summary, not a single 'y'; use the 'boxplot' stat or 'identity'"}]:r.has("median")?[...[...z].filter(o=>t[o]!==void 0).map(o=>({code:"CONFLICTING_STAT_MAPPING",message:`boxplot stat computes '${o}' from 'y'; remove its mapping, or set the layer's stat to 'identity'`})),...t.y===void 0?[{code:"MISSING_AESTHETIC",message:"boxplot stat summarises 'y'; map it to the raw values"}]:[]]:ye.filter(o=>t[o]===void 0).map(o=>({code:"MISSING_AESTHETIC",message:`boxplot geom requires '${o}' when its data is already summarised`,context:{aesthetic:o}})),this.resolveAnchorPosition=(t,{coordSystem:r})=>{const o=(0,s.readMainAxis)(r),a=we(t,o);return a===null?null:o==="x"?{x:a.band,y:a.value}:{x:a.value,y:a.band}}}resolveParams(e){const{extent:t,width:r,...o}=super.resolveParams(e);return{...o,extent:this.resolveExtent(t,e.diagnostics),...this.resolveWidth(r,e.diagnostics)}}compile({data:e,mapping:t,params:r}){const o=P(e,t.lowerWhisker),a=P(e,t.upperWhisker);if(o===null||a===null)return{data:e,mapping:{}};const{outliers:n,notch:l,showMean:u,varwidth:c}=r,i=P(e,t.outlier),d=n===!1&&i!==null?Se(e,i):e,m=P(d,t.count),k=D(r)/2;return{data:d.addVariable(x.relativeWidth,"numeric",c===!0&&m!==null?Me(d,m):We(d)).addVariable(s.POSITION_VARIABLES.yMin,"numeric",K(d,o,i)).addVariable(s.POSITION_VARIABLES.yMax,"numeric",K(d,a,i)).addConstantVariable(s.POSITION_VARIABLES.xMin,"numeric",-k).addConstantVariable(s.POSITION_VARIABLES.xMax,"numeric",k),mapping:{...t.y===void 0&&{y:t.median},...u===!0&&t.mean===void 0&&d.hasVariable(x.mean)&&{mean:x.mean},...l===!0&&t.notchLower===void 0&&d.hasVariable(x.notchLower)&&{notchLower:x.notchLower,notchUpper:x.notchUpper}}}}resolveExtent(e,t){return G(e)?e:(t?.addWarning({code:"INVALID_GEOM_PARAM",message:`Boxplot extent must be a non-negative number or 'min-max', got ${String(e)}; falling back to ${b}.`,context:{geom:this.type,param:"extent",expected:"extent >= 0 or 'min-max'",actual:String(e)},suggestion:"Set params.extent to a number of interquartile ranges, or 'min-max'."}),b)}resolveWidth(e,t){if(e===void 0)return{};const r=typeof e=="number"&&Number.isFinite(e)?e:null;if(r!==null&&r>0&&r<=1)return{width:r};const o=r!==null&&r>1?1:void 0;return t?.addWarning({code:"INVALID_GEOM_PARAM",message:`Boxplot width must be a fraction in (0, 1], got ${String(e)}; falling back to ${o??Q}.`,context:{geom:this.type,param:"width",expected:"0 < width <= 1",actual:r??String(e)},suggestion:"Set params.width to a fraction of the category band in (0, 1]."}),o!==void 0?{width:o}:{}}},we=(e,t)=>{const r=H(e,t);if(r!==null)return{band:_(r),value:r.median};const o=F(e,t);return o===null?null:{band:o.band,value:o.value}},D=({width:e})=>e??Q,Me=(e,t)=>{const r=e.getValues(t,{type:"numeric"}),o=Math.max(0,...r.map(a=>a??0));return r.map(a=>a===null||o===0?null:Math.sqrt(a/o))},We=e=>Array.from({length:e.size()},()=>null),K=(e,t,r)=>{const o=r===null?[]:e.getValues(r,{type:"numeric"});return e.getValues(t,{type:"numeric"}).map((a,n)=>a??o[n]??null)},Se=(e,t)=>{const r=e.getValues(t,{type:"numeric"});return e.take(r.flatMap((o,a)=>o===null?[a]:[]))},P=(e,t)=>{const r=(0,s.readVariableName)(t);return r!==null&&e.hasVariable(r)?r:null},Ie=5e-4,Oe=.5,Pe=.5,J=(e,t)=>{const r=(e.q1+e.q3)/2,o=Math.max(Math.abs(e.q3-e.q1),Ie)/2,a=g(t,e.start,r-o),n=g(t,e.end,r+o),[l,u]=j(a.x,n.x),[c,i]=j(a.y,n.y);return{x:l,y:c,width:u-l,height:i-c}},ee=(e,t)=>{if(e.notch===null)return null;const{lower:r,upper:o}=e.notch,[a,n]=re(e);return[g(t,e.start,e.q1),g(t,e.start,r),g(t,a,e.median),g(t,e.start,o),g(t,e.start,e.q3),g(t,e.end,e.q3),g(t,e.end,o),g(t,n,e.median),g(t,e.end,r),g(t,e.end,e.q1)]},be=(e,t)=>{const[r,o]=e.notch===null?[e.start,e.end]:re(e);return{from:g(t,r,e.median),to:g(t,o,e.median)}},te=(e,t)=>e.mean===null?null:g(t,_(e),e.mean),re=e=>{const t=(e.end-e.start)*(1-Pe)/2;return[e.start+t,e.end-t]},oe=(e,t)=>{const r=_(e),o=(e.end-e.start)*Oe/2;return[{side:"lower",quartile:e.q1,reach:e.lowerWhisker},{side:"upper",quartile:e.q3,reach:e.upperWhisker}].filter(({quartile:a,reach:n})=>a!==n).map(({side:a,quartile:n,reach:l})=>({side:a,stem:{from:g(t,r,n),to:g(t,r,l)},cap:{from:g(t,r-o,l),to:g(t,r+o,l)}}))},g=(e,t,r)=>{const o=(0,s.composePoint)(e,{main:t,cross:r});return{x:(0,s.toViewBoxX)(o.x),y:(0,s.toViewBoxY)(o.y)}},j=(e,t)=>e<=t?[e,t]:[t,e],O=(e,t)=>({x:e.x*t.width,y:e.y*t.height}),N=(e,t)=>`${e.map((r,o)=>{const{x:a,y:n}=O(r,t);return`${o===0?"M":"L"}${a},${n}`}).join("")}Z`,_e=(e,t)=>N([{x:e.x,y:e.y},{x:e.x+e.width,y:e.y},{x:e.x+e.width,y:e.y+e.height},{x:e.x,y:e.y+e.height}],t),Ee=(e,t)=>{const r=O(e.from,t),o=O(e.to,t);return`M${r.x},${r.y}L${o.x},${o.y}`},Te=({layer:e,coordSystem:t,panelRect:r,styleReaders:o})=>{const a=(0,s.readMainAxis)(t),{boxes:n,outliers:l}=S(e.data,a),u=n.map(h=>{const y=ee(h,a);return y!==null?{kind:"region",pathData:N(y,r)}:{kind:"region",pathData:_e(J(h,a),r)}}),c=o.part("whisker"),i=o.part("cap"),d=n.flatMap(h=>oe(h,a).flatMap(y=>[{segment:y.stem,readers:c},{segment:y.cap,readers:i}].flatMap(({segment:M,readers:E})=>{const I=E?.get("strokeWidth",h.observation)??0;return I>0?[{kind:"stroke",pathData:Ee(M,r),width:I}]:[]}))),m=o.part("outlier"),k=m===void 0?[]:l.map(h=>({center:O(g(a,h.band,h.value),r),size:U(m,h.observation)})),p=o.part("mean"),v=p===void 0?[]:n.flatMap(h=>{const y=te(h,a);return y===null?[]:[{center:O(y,r),size:U(p,h.observation)}]}),w=(0,s.buildDotsEditOutlineShapes)([...k,...v]);return[...d,...u,...w]},U=(e,t)=>(e.get("size",t)??0)+(e.get("strokeWidth",t)??0),A=e=>{const t=(0,s.createStableKeyGenerator)(e.data,e.mapping,e.id),r=(0,s.readVariableName)(e.mapping.outlier),o=a=>`${t(a.observation)}-outlier-${String(r===null?a.value:a.observation[r])}`;return{readBoxKey:t,readOutlierKeys:a=>{const n=new Map;return a.map(l=>{const u=o(l),c=n.get(u)??0;return n.set(u,c+1),c===0?u:`${u}-${c}`})}}},Be=(e,t,r)=>{const o=e.data.getVariableNames(),a=l=>JSON.stringify(o.map(u=>l.observation[u]??null)),n=new Set(r.map(a));return t.filter(l=>n.has(a(l)))},ae=(e,t,r)=>{const o=new Set(t.map(a=>r(a.observation)));return e.filter(a=>o.has(r(a.observation)))},L=({boxes:e,outliers:t,mainAxis:r,panelSize:o,source:a,styleReaders:n,keys:l,isHovered:u=!1,shouldAnimateTransitions:c=!1})=>{const i=u?"hovered":void 0,d=n.part("whisker"),m=n.part("cap"),k=n.part("median"),p=n.part("mean"),v=n.part("outlier"),w=l.readOutlierKeys(t);return(0,f.jsxs)(f.Fragment,{children:[e.flatMap(h=>oe(h,r).flatMap(y=>[{piece:"stem",testId:"boxplot-whisker",readers:d},{piece:"cap",testId:"boxplot-cap",readers:m}].map(({piece:M,testId:E,readers:I})=>I===void 0?null:(0,f.jsx)($,{testId:E,segment:y[M],observation:h.observation,readers:I,state:i,shouldAnimateTransitions:c},`${l.readBoxKey(h.observation)}-${y.side}-${M}`)))),e.map(h=>(0,f.jsx)(qe,{box:h,mainAxis:r,panelSize:o,source:a,styleReaders:n,state:i,shouldAnimateTransitions:c},l.readBoxKey(h.observation))),k&&e.map(h=>(0,f.jsx)($,{testId:"boxplot-median",segment:be(h,r),observation:h.observation,readers:k,state:i,shouldAnimateTransitions:c},l.readBoxKey(h.observation))),p&&e.map(h=>{const y=te(h,r);return y===null?null:(0,f.jsx)(X,{testId:"boxplot-mean",centre:y,observation:h.observation,readers:p,state:i,shouldAnimateTransitions:c},l.readBoxKey(h.observation))}),v&&t.map((h,y)=>(0,f.jsx)(X,{testId:"boxplot-outlier",centre:g(r,h.band,h.value),observation:h.observation,readers:v,state:i,shouldAnimateTransitions:c},w[y]))]})},$=({testId:e,segment:t,observation:r,readers:o,state:a,shouldAnimateTransitions:n})=>{const l=o.get("strokeWidth",r,a)??0,u=o.get("stroke",r,a);return(0,f.jsx)(s.GeomLine,{"data-testid":e,x1:(0,s.toPercent)(t.from.x),y1:(0,s.toPercent)(t.from.y),x2:(0,s.toPercent)(t.to.x),y2:(0,s.toPercent)(t.to.y),stroke:u===void 0?void 0:(0,s.toPaintColor)(u),strokeOpacity:o.get("alpha",r,a),strokeWidth:l,strokeDasharray:(0,s.toStrokeDasharray)(o.get("dashArray",r,a),l),shouldAnimateTransitions:n})},qe=({box:e,mainAxis:t,panelSize:r,source:o,styleReaders:a,state:n,shouldAnimateTransitions:l})=>{const u=a.get("fill",e.observation,n),c=a.get("stroke",e.observation,n),i={fill:u===void 0?void 0:(0,s.toPaintColor)(u),opacity:a.get("alpha",e.observation,n),fillOpacity:a.get("fillAlpha",e.observation,n),stroke:c===void 0?void 0:(0,s.toPaintColor)(c),strokeOpacity:a.get("strokeAlpha",e.observation,n),strokeWidth:a.get("strokeWidth",e.observation,n),shouldAnimateTransitions:l},d=ee(e,t);if(d!==null)return(0,f.jsx)(s.GeomPath,{"data-testid":"boxplot-box",d:N(d,r),source:o,...i});const m=J(e,t);return(0,f.jsx)(s.GeomRect,{"data-testid":"boxplot-box",x:(0,s.toPercent)(m.x),y:(0,s.toPercent)(m.y),width:(0,s.toPercent)(m.width),height:(0,s.toPercent)(m.height),...i})},X=({testId:e,centre:t,observation:r,readers:o,state:a,shouldAnimateTransitions:n})=>{const l=o.get("fill",r,a),u=o.get("stroke",r,a);return(0,f.jsx)(s.GeomCircle,{"data-testid":e,cx:(0,s.toPercent)(t.x),cy:(0,s.toPercent)(t.y),r:(o.get("size",r,a)??0)/2,fill:l===void 0?void 0:(0,s.toPaintColor)(l),opacity:o.get("alpha",r,a),fillOpacity:o.get("fillAlpha",r,a),stroke:u===void 0?void 0:(0,s.toPaintColor)(u),strokeWidth:o.get("strokeWidth",r,a),shouldAnimateTransitions:n})},Ve=({layer:e,sourceLayer:t,mainAxis:r,panelSize:o,styleReaders:a})=>{const n=(0,W.useMemo)(()=>A(t),[t]),l=(0,W.useMemo)(()=>S(t.data,r).outliers,[t.data,r]),{boxes:u,outliers:c}=(0,W.useMemo)(()=>{const i=S(e.data,r),d=new Set(Be(t,l,i.outliers)),m=new Set(ae(l,i.boxes,n.readBoxKey)),k=l.filter(p=>m.has(p)||d.has(p));return{boxes:i.boxes,outliers:k}},[e.data,r,t,l,n]);return(0,f.jsx)(L,{boxes:u,outliers:c,mainAxis:r,panelSize:o,source:t,styleReaders:a,keys:n})},Ne=({layer:e,primary:t,mainAxis:r,panelSize:o,styleReaders:a})=>{const n=(0,W.useMemo)(()=>S(e.data,r).outliers,[e.data,r]),l=(0,W.useMemo)(()=>A(e),[e]),{boxes:u,outliers:c}=S([t.observation],r);return(0,f.jsx)("g",{"data-testid":"hovered-boxes",children:(0,f.jsx)(L,{boxes:u,outliers:u.length>0?ae(n,u,l.readBoxKey):c,mainAxis:r,panelSize:o,source:e,styleReaders:a,keys:l,isHovered:!0})})},Ae=({layer:e,mainAxis:t,panelSize:r,styleReaders:o,shouldAnimateTransitions:a,intro:n})=>{const{boxes:l,outliers:u}=(0,W.useMemo)(()=>S(e.data,t),[e.data,t]),c=(0,f.jsx)(L,{boxes:l,outliers:u,mainAxis:t,panelSize:r,source:e,styleReaders:o,keys:(0,W.useMemo)(()=>A(e),[e]),shouldAnimateTransitions:a});return n==null?c:(0,f.jsx)(s.IntroFade,{intro:{type:"fade",layerId:n.layerId,durationSeconds:n.durationSeconds},children:c})},Le=(0,s.defineGeomRenderer)(new Z,{coord:"cartesian",swatchShape:"square",guideMode:"band",render:({layer:e,coordSystem:t,panelRect:r,styleReaders:o,shouldAnimateTransitions:a,intro:n})=>(0,f.jsx)(Ae,{layer:e,mainAxis:(0,s.readMainAxis)(t),panelSize:r,styleReaders:o,shouldAnimateTransitions:a,intro:n}),renderHighlight:({layer:e,sourceLayer:t,coordSystem:r,panelRect:o,styleReaders:a})=>(0,f.jsx)(Ve,{layer:e,sourceLayer:t,mainAxis:(0,s.readMainAxis)(r),panelSize:o,styleReaders:a}),renderHover:({layer:e,primary:t,coordSystem:r,panelRect:o,styleReaders:a})=>(0,f.jsx)(Ne,{layer:e,primary:t,mainAxis:(0,s.readMainAxis)(r),panelSize:o,styleReaders:a}),renderHoverCompanions:()=>null,getEditOutlineShapes:Te});exports.BOXPLOT_VARIABLES=x;exports.BoxplotGeom=Z;exports.boxplot=Le;
1
+ Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});let s=require("@graphysdk/react"),W=require("react"),f=require("react/jsx-runtime");var x={q1:"boxplotQ1",median:"boxplotMedian",q3:"boxplotQ3",lowerWhisker:"boxplotLowerWhisker",upperWhisker:"boxplotUpperWhisker",count:"boxplotCount",mean:"boxplotMean",notchLower:"boxplotNotchLower",notchUpper:"boxplotNotchUpper",relativeWidth:"boxplotRelativeWidth",outlier:"boxplotOutlier"},_=e=>(e.start+e.end)/2,G=(e,t)=>{const r=t==="x"?(0,s.getXMin)(e):(0,s.getYMin)(e),o=t==="x"?(0,s.getXMax)(e):(0,s.getYMax)(e),n=t==="x"?(0,s.getYMin)(e):(0,s.getXMin)(e),a=t==="x"?(0,s.getYMax)(e):(0,s.getXMax)(e),l=(0,s.getScaledAesthetic)(e,"q1"),u=(0,s.getScaledAesthetic)(e,"median"),c=(0,s.getScaledAesthetic)(e,"q3");if(r===null||o===null||n===null||a===null||u===null||l===null||c===null)return null;const i=T(e,"notchLower"),d=T(e,"notchUpper"),[m,k]=ae(r,o,e);return{observation:e,start:m,end:k,lowerWhisker:n,q1:l,median:u,q3:c,upperWhisker:a,mean:T(e,"mean"),notch:i===null||d===null?null:{lower:i,upper:d}}},F=(e,t)=>{const r=t==="x"?(0,s.getXMin)(e):(0,s.getYMin)(e),o=t==="x"?(0,s.getXMax)(e):(0,s.getYMax)(e),n=(0,s.getScaledAesthetic)(e,"outlier");return r===null||o===null||n===null?null:{observation:e,band:_({start:r,end:o}),value:n}},S=(e,t)=>{const r=[],o=[];for(const n of e){const a=G(n,t);if(a!==null){r.push(a);continue}const l=F(n,t);l!==null&&o.push(l)}return{boxes:r,outliers:o}},T=(e,t)=>(0,s.getScaledAesthetic)(e,t)??null,ae=(e,t,r)=>{const o=r[x.relativeWidth];if(typeof o!="number")return[e,t];const n=(e+t)/2,a=(t-e)*o/2;return[n-a,n+a]},b=1.5,H=e=>e==="min-max"||typeof e=="number"&&Number.isFinite(e)&&e>=0,se=1.58,le=(e,t)=>{if(e.length===0)return null;const r=[...e].sort((m,k)=>m-k),o=B(r,.25),n=B(r,.5),a=B(r,.75),l=t==="min-max"?1/0:t*(a-o),u=o-l,c=a+l,i=r.filter(m=>m>=u&&m<=c),d=se*(a-o)/Math.sqrt(e.length);return{lowerWhisker:Math.min(i[0]??o,o),q1:o,median:n,q3:a,upperWhisker:Math.max(i[i.length-1]??a,a),count:e.length,mean:r.reduce((m,k)=>m+k,0)/r.length,notchLower:n-d,notchUpper:n+d}},B=(e,t)=>{const r=(e.length-1)*t,o=Math.floor(r),n=e[o]??0;return n+((e[o+1]??n)-n)*(r-o)},Y={q1:x.q1,median:x.median,q3:x.q3,lowerWhisker:x.lowerWhisker,upperWhisker:x.upperWhisker,count:x.count,outlier:x.outlier},z=new Set([...Object.keys(Y),"mean","notchLower","notchUpper"]),ie=class extends s.Stat{constructor(...e){super(...e),this.type="boxplot",this.computedVariables=z}computeStat({data:e,mapping:t,params:r}){const o=(0,s.readVariableName)(t.x),n=(0,s.readVariableName)(t.y);if(o===null||n===null||!e.hasVariable(o)||!e.hasVariable(n))return{data:e,mapping:{}};if(e.size()>0&&e.getType(n)!=="numeric"){const l=e.getType(n);throw new s.UserInputError({code:"INCOMPATIBLE_TYPE",message:`'boxplot' stat requires a numeric 'y' (got '${l}' for variable '${n}')`,context:{aesthetic:"y",variableName:n,variableType:l},suggestion:"Map the raw values to 'y', or summarised columns under stat 'identity'."})}const a=r?.extent;return{data:ue({data:e,xVariable:o,yVariable:n,groupVariables:(0,s.getGroupVariables)(e,t).filter(l=>l!==o),extent:H(a)?a:b}),mapping:Y}}},ue=({data:e,xVariable:t,yVariable:r,groupVariables:o,extent:n})=>{const a=ce(e,t,r,o),l=e.getVariableNames(),u=new Map(l.map(p=>[p,e.getValues(p)])),c=new Map(l.map(p=>[p,[]])),i={q1:[],median:[],q3:[],lowerWhisker:[],upperWhisker:[],count:[],mean:[],notchLower:[],notchUpper:[],outlier:[]},d=[];for(const p of a){const v=le(p.values,n);if(v===null)continue;for(const h of l)c.get(h)?.push(he(u.get(h)??[],p.indices));i.q1.push(v.q1),i.median.push(v.median),i.q3.push(v.q3),i.lowerWhisker.push(v.lowerWhisker),i.upperWhisker.push(v.upperWhisker),i.count.push(v.count),i.mean.push(v.mean),i.notchLower.push(v.notchLower),i.notchUpper.push(v.notchUpper),i.outlier.push(null);const w={low:v.lowerWhisker,high:v.upperWhisker};p.indices.forEach((h,y)=>{const M=p.values[y];M!==void 0&&(M<w.low||M>w.high)&&d.push({index:h,value:M})})}for(const{index:p,value:v}of d){for(const w of l)c.get(w)?.push(u.get(w)?.[p]??null);i.q1.push(null),i.median.push(null),i.q3.push(null),i.lowerWhisker.push(null),i.upperWhisker.push(null),i.count.push(null),i.mean.push(null),i.notchLower.push(null),i.notchUpper.push(null),i.outlier.push(v)}const m=e.getValueFormat(r),k=Object.fromEntries(l.map(p=>[p,{type:e.getType(p),values:c.get(p)??[],valueFormat:e.getValueFormat(p)}]));for(const[p,v]of Object.entries(i)){const w=x[p];k[w]=p==="count"?{type:"numeric",values:v}:{type:"numeric",values:v,valueFormat:m}}return new s.Dataset(k)},ce=(e,t,r,o)=>{const n=[t,...o].map(u=>e.getValues(u)),a=e.getValues(r,{type:"numeric"}),l=new Map;return a.forEach((u,c)=>{if(u===null||!Number.isFinite(u))return;const i=n.map(m=>V(m[c]??null)).join("\0"),d=l.get(i)??{indices:[],values:[]};d.indices.push(c),d.values.push(u),l.set(i,d)}),[...l.values()]},he=(e,t)=>{const r=e[t[0]??-1]??null,o=V(r);return t.every(n=>V(e[n]??null)===o)?r:null},V=e=>e===null?"\0null":e instanceof Date?`date:${e.getTime()}`:`${typeof e}:${String(e)}`,Q=.6,de=1.5,pe=.3,me=2,C=1.5,ve=6,ge=1.5,fe=7,ke=1.5,ye=["q1","median","q3"],xe={strokeWidth:"pixels"},q={stroke:"color",alpha:"unitInterval",strokeWidth:"pixels",dashArray:"dashArray"},D={fill:"color",fillAlpha:"unitInterval",stroke:"color",alpha:"unitInterval",size:"pixels",strokeWidth:"pixels"},Z=class extends s.Geom{constructor(...e){super(...e),this.type="boxplot",this.defaultParams={extent:b,outliers:!0,notch:!1,showMean:!1,varwidth:!1},this.defaultStat=new ie,this.derivedVariables=Object.values(x),this.positionRoles=[{axis:"x",role:"point",valueKind:"value"},{axis:"y",role:"min",valueKind:"value",aes:"lowerWhisker"},{axis:"y",role:"max",valueKind:"value",aes:"upperWhisker"},{axis:"y",role:"scalar",valueKind:"value",aes:"q1"},{axis:"y",role:"scalar",valueKind:"value",aes:"median"},{axis:"y",role:"scalar",valueKind:"value",aes:"q3"},{axis:"y",role:"scalar",valueKind:"value",aes:"outlier"},{axis:"y",role:"scalar",valueKind:"value",aes:"mean"},{axis:"y",role:"scalar",valueKind:"value",aes:"notchLower"},{axis:"y",role:"scalar",valueKind:"value",aes:"notchUpper"}],this.aesthetics=[{kind:"visual",name:"color"},{kind:"data",name:"y"},{kind:"data",name:"count"}],this.defaultPosition="dodge",this.supportedPositions=["identity","dodge"],this.supportedCoordTypes=["cartesian","flip"],this.scaleConstraints={discreteMainAxis:!0},this.legend={suppressWhenSingleItem:!0},this.spatialKind="rects",this.highlightStrategy="observation-rerender",this.tooltip=[{key:"Upper whisker",aes:"upperWhisker",optional:!0},{key:"Q3",aes:"q3",optional:!0},{key:"Median",aes:"median",optional:!0},{key:"Q1",aes:"q1",optional:!0},{key:"Lower whisker",aes:"lowerWhisker",optional:!0},{key:"Count",aes:"count",optional:!0},{key:"Mean",aes:"mean",optional:!0},{key:"Outlier",aes:"outlier",optional:!0}],this.styleTarget=(0,s.defineGeomStyleTarget)({observation:{vocabulary:xe,aesthetics:{fill:"color",stroke:"color"},rest:{fill:(0,s.token)("geom"),fillAlpha:pe,stroke:(0,s.token)("geom"),strokeWidth:de}},parts:{median:{vocabulary:q,aesthetics:{stroke:"color"},rest:{stroke:(0,s.token)("geom"),alpha:1,strokeWidth:me,dashArray:[]}},whisker:{vocabulary:q,aesthetics:{stroke:"color"},rest:{stroke:(0,s.token)("geom"),alpha:1,strokeWidth:C,dashArray:[]}},cap:{vocabulary:q,aesthetics:{stroke:"color"},rest:{stroke:(0,s.token)("geom"),alpha:1,strokeWidth:C,dashArray:[]}},outlier:{vocabulary:D,aesthetics:{fill:"color",stroke:"color"},rest:{fill:(0,s.token)("geom"),fillAlpha:0,stroke:(0,s.token)("geom"),alpha:1,size:ve,strokeWidth:ge}},mean:{vocabulary:D,aesthetics:{fill:"color"},rest:{fill:(0,s.token)("geom"),fillAlpha:1,stroke:(0,s.token)("graphBackground"),alpha:1,size:fe,strokeWidth:ke}}}}),this.resolveBandFraction=t=>R(t),this.resolveValueSource=t=>t.median??{variable:x.median},this.validateMapping=({mapping:t,computedVariables:r})=>r.has("y")?[{code:"CONFLICTING_STAT_MAPPING",message:"boxplot geom draws a summary, not a single 'y'; use the 'boxplot' stat or 'identity'"}]:r.has("median")?[...[...z].filter(o=>t[o]!==void 0).map(o=>({code:"CONFLICTING_STAT_MAPPING",message:`boxplot stat computes '${o}' from 'y'; remove its mapping, or set the layer's stat to 'identity'`})),...t.y===void 0?[{code:"MISSING_AESTHETIC",message:"boxplot stat summarises 'y'; map it to the raw values"}]:[]]:ye.filter(o=>t[o]===void 0).map(o=>({code:"MISSING_AESTHETIC",message:`boxplot geom requires '${o}' when its data is already summarised`,context:{aesthetic:o}})),this.resolveAnchorPosition=(t,{coordSystem:r})=>{const o=(0,s.readMainAxis)(r),n=we(t,o);return n===null?null:o==="x"?{x:n.band,y:n.value}:{x:n.value,y:n.band}}}resolveParams(e){const{extent:t,width:r,...o}=super.resolveParams(e);return{...o,extent:this.resolveExtent(t,e.diagnostics),...this.resolveWidth(r,e.diagnostics)}}compile({data:e,mapping:t,params:r}){const o=P(e,t.lowerWhisker),n=P(e,t.upperWhisker);if(o===null||n===null)return{data:e,mapping:{}};const{outliers:a,notch:l,showMean:u,varwidth:c}=r,i=P(e,t.outlier),d=a===!1&&i!==null?Se(e,i):e,m=P(d,t.count),k=R(r)/2;return{data:d.addVariable(x.relativeWidth,"numeric",c===!0&&m!==null?Me(d,m):We(d)).addVariable(s.POSITION_VARIABLES.yMin,"numeric",K(d,o,i)).addVariable(s.POSITION_VARIABLES.yMax,"numeric",K(d,n,i)).addConstantVariable(s.POSITION_VARIABLES.xMin,"numeric",-k).addConstantVariable(s.POSITION_VARIABLES.xMax,"numeric",k),mapping:{...t.y===void 0&&{y:t.median},...u===!0&&t.mean===void 0&&d.hasVariable(x.mean)&&{mean:x.mean},...l===!0&&t.notchLower===void 0&&d.hasVariable(x.notchLower)&&{notchLower:x.notchLower,notchUpper:x.notchUpper}}}}resolveExtent(e,t){return H(e)?e:(t?.addWarning({code:"INVALID_GEOM_PARAM",message:`Boxplot extent must be a non-negative number or 'min-max', got ${String(e)}; falling back to ${b}.`,context:{geom:this.type,param:"extent",expected:"extent >= 0 or 'min-max'",actual:String(e)},suggestion:"Set params.extent to a number of interquartile ranges, or 'min-max'."}),b)}resolveWidth(e,t){if(e===void 0)return{};const r=typeof e=="number"&&Number.isFinite(e)?e:null;if(r!==null&&r>0&&r<=1)return{width:r};const o=r!==null&&r>1?1:void 0;return t?.addWarning({code:"INVALID_GEOM_PARAM",message:`Boxplot width must be a fraction in (0, 1], got ${String(e)}; falling back to ${o??Q}.`,context:{geom:this.type,param:"width",expected:"0 < width <= 1",actual:r??String(e)},suggestion:"Set params.width to a fraction of the category band in (0, 1]."}),o!==void 0?{width:o}:{}}},we=(e,t)=>{const r=G(e,t);if(r!==null)return{band:_(r),value:r.median};const o=F(e,t);return o===null?null:{band:o.band,value:o.value}},R=({width:e})=>e??Q,Me=(e,t)=>{const r=e.getValues(t,{type:"numeric"}),o=Math.max(0,...r.map(n=>n??0));return r.map(n=>n===null||o===0?null:Math.sqrt(n/o))},We=e=>Array.from({length:e.size()},()=>null),K=(e,t,r)=>{const o=r===null?[]:e.getValues(r,{type:"numeric"});return e.getValues(t,{type:"numeric"}).map((n,a)=>n??o[a]??null)},Se=(e,t)=>{const r=e.getValues(t,{type:"numeric"});return e.take(r.flatMap((o,n)=>o===null?[n]:[]))},P=(e,t)=>{const r=(0,s.readVariableName)(t);return r!==null&&e.hasVariable(r)?r:null},Ie=5e-4,Oe=.5,Pe=.5,J=(e,t)=>{const r=(e.q1+e.q3)/2,o=Math.max(Math.abs(e.q3-e.q1),Ie)/2,n=g(t,e.start,r-o),a=g(t,e.end,r+o),[l,u]=j(n.x,a.x),[c,i]=j(n.y,a.y);return{x:l,y:c,width:u-l,height:i-c}},ee=(e,t)=>{if(e.notch===null)return null;const{lower:r,upper:o}=e.notch,[n,a]=re(e);return[g(t,e.start,e.q1),g(t,e.start,r),g(t,n,e.median),g(t,e.start,o),g(t,e.start,e.q3),g(t,e.end,e.q3),g(t,e.end,o),g(t,a,e.median),g(t,e.end,r),g(t,e.end,e.q1)]},be=(e,t)=>{const[r,o]=e.notch===null?[e.start,e.end]:re(e);return{from:g(t,r,e.median),to:g(t,o,e.median)}},te=(e,t)=>e.mean===null?null:g(t,_(e),e.mean),re=e=>{const t=(e.end-e.start)*(1-Pe)/2;return[e.start+t,e.end-t]},oe=(e,t)=>{const r=_(e),o=(e.end-e.start)*Oe/2;return[{side:"lower",quartile:e.q1,reach:e.lowerWhisker},{side:"upper",quartile:e.q3,reach:e.upperWhisker}].filter(({quartile:n,reach:a})=>n!==a).map(({side:n,quartile:a,reach:l})=>({side:n,stem:{from:g(t,r,a),to:g(t,r,l)},cap:{from:g(t,r-o,l),to:g(t,r+o,l)}}))},g=(e,t,r)=>{const o=(0,s.composePoint)(e,{main:t,cross:r});return{x:(0,s.toViewBoxX)(o.x),y:(0,s.toViewBoxY)(o.y)}},j=(e,t)=>e<=t?[e,t]:[t,e],O=(e,t)=>({x:e.x*t.width,y:e.y*t.height}),N=(e,t)=>`${e.map((r,o)=>{const{x:n,y:a}=O(r,t);return`${o===0?"M":"L"}${n},${a}`}).join("")}Z`,_e=(e,t)=>N([{x:e.x,y:e.y},{x:e.x+e.width,y:e.y},{x:e.x+e.width,y:e.y+e.height},{x:e.x,y:e.y+e.height}],t),Ee=(e,t)=>{const r=O(e.from,t),o=O(e.to,t);return`M${r.x},${r.y}L${o.x},${o.y}`},Te=({layer:e,coordSystem:t,panelRect:r,styleReaders:o})=>{const n=(0,s.readMainAxis)(t),{boxes:a,outliers:l}=S(e.data,n),u=a.map(h=>{const y=ee(h,n);return y!==null?{kind:"region",pathData:N(y,r)}:{kind:"region",pathData:_e(J(h,n),r)}}),c=o.part("whisker"),i=o.part("cap"),d=a.flatMap(h=>oe(h,n).flatMap(y=>[{segment:y.stem,readers:c},{segment:y.cap,readers:i}].flatMap(({segment:M,readers:E})=>{const I=E?.get("strokeWidth",h.observation)??0;return I>0?[{kind:"stroke",pathData:Ee(M,r),width:I}]:[]}))),m=o.part("outlier"),k=m===void 0?[]:l.map(h=>({center:O(g(n,h.band,h.value),r),size:U(m,h.observation)})),p=o.part("mean"),v=p===void 0?[]:a.flatMap(h=>{const y=te(h,n);return y===null?[]:[{center:O(y,r),size:U(p,h.observation)}]}),w=(0,s.buildDotsEditOutlineShapes)([...k,...v]);return[...d,...u,...w]},U=(e,t)=>(e.get("size",t)??0)+(e.get("strokeWidth",t)??0),A=e=>{const t=(0,s.createStableKeyGenerator)(e.data,e.mapping,e.id),r=(0,s.readVariableName)(e.mapping.outlier),o=n=>`${t(n.observation)}-outlier-${String(r===null?n.value:n.observation[r])}`;return{readBoxKey:t,readOutlierKeys:n=>{const a=new Map;return n.map(l=>{const u=o(l),c=a.get(u)??0;return a.set(u,c+1),c===0?u:`${u}-${c}`})}}},Be=(e,t,r)=>{const o=e.data.getVariableNames(),n=l=>JSON.stringify(o.map(u=>l.observation[u]??null)),a=new Set(r.map(n));return t.filter(l=>a.has(n(l)))},ne=(e,t,r)=>{const o=new Set(t.map(n=>r(n.observation)));return e.filter(n=>o.has(r(n.observation)))},L=({boxes:e,outliers:t,mainAxis:r,panelSize:o,source:n,styleReaders:a,keys:l,hovered:u,shouldAnimateTransitions:c=!1})=>{const i=u===void 0?void 0:"hovered",d=a.part("whisker"),m=a.part("cap"),k=a.part("median"),p=a.part("mean"),v=a.part("outlier"),w=l.readOutlierKeys(t);return(0,f.jsxs)(f.Fragment,{children:[e.flatMap(h=>oe(h,r).flatMap(y=>[{piece:"stem",testId:"boxplot-whisker",readers:d},{piece:"cap",testId:"boxplot-cap",readers:m}].map(({piece:M,testId:E,readers:I})=>I===void 0?null:(0,f.jsx)($,{testId:E,segment:y[M],observation:h.observation,readers:I,state:i,shouldAnimateTransitions:c},`${l.readBoxKey(h.observation)}-${y.side}-${M}`)))),(0,f.jsx)(s.GeomOverpaint,{styleReaders:a,state:i,children:e.map(h=>(0,f.jsx)(qe,{box:h,mainAxis:r,panelSize:o,source:n,styleReaders:a,state:i,shouldAnimateTransitions:c},l.readBoxKey(h.observation)))}),k&&e.map(h=>(0,f.jsx)($,{testId:"boxplot-median",segment:be(h,r),observation:h.observation,readers:k,state:i,shouldAnimateTransitions:c},l.readBoxKey(h.observation))),p&&e.map(h=>{const y=te(h,r);return y===null?null:(0,f.jsx)(X,{testId:"boxplot-mean",centre:y,observation:h.observation,readers:p,state:i,shouldAnimateTransitions:c},l.readBoxKey(h.observation))}),v&&t.map((h,y)=>(0,f.jsx)(X,{testId:"boxplot-outlier",centre:g(r,h.band,h.value),observation:h.observation,readers:v,state:i,shouldAnimateTransitions:c},w[y]))]})},$=({testId:e,segment:t,observation:r,readers:o,state:n,shouldAnimateTransitions:a})=>{const l=o.get("strokeWidth",r,n)??0,u=o.get("stroke",r,n);return(0,f.jsx)(s.GeomLine,{"data-testid":e,x1:(0,s.toPercent)(t.from.x),y1:(0,s.toPercent)(t.from.y),x2:(0,s.toPercent)(t.to.x),y2:(0,s.toPercent)(t.to.y),stroke:u===void 0?void 0:(0,s.toPaintColor)(u),strokeOpacity:o.get("alpha",r,n),strokeWidth:l,strokeDasharray:(0,s.toStrokeDasharray)(o.get("dashArray",r,n),l),shouldAnimateTransitions:a})},qe=({box:e,mainAxis:t,panelSize:r,source:o,styleReaders:n,state:a,shouldAnimateTransitions:l})=>{const u=n.get("fill",e.observation,a),c=n.get("stroke",e.observation,a),i={fill:u===void 0?void 0:(0,s.toPaintColor)(u),opacity:n.get("alpha",e.observation,a),fillOpacity:n.get("fillAlpha",e.observation,a),stroke:c===void 0?void 0:(0,s.toPaintColor)(c),strokeOpacity:n.get("strokeAlpha",e.observation,a),strokeWidth:n.get("strokeWidth",e.observation,a),effects:(0,s.readGeomEffects)(n,e.observation,a),shouldAnimateTransitions:l},d=ee(e,t);if(d!==null)return(0,f.jsx)(s.GeomPath,{"data-testid":"boxplot-box",d:N(d,r),source:o,...i});const m=J(e,t);return(0,f.jsx)(s.GeomRect,{"data-testid":"boxplot-box",x:(0,s.toPercent)(m.x),y:(0,s.toPercent)(m.y),width:(0,s.toPercent)(m.width),height:(0,s.toPercent)(m.height),...i})},X=({testId:e,centre:t,observation:r,readers:o,state:n,shouldAnimateTransitions:a})=>{const l=o.get("fill",r,n),u=o.get("stroke",r,n);return(0,f.jsx)(s.GeomCircle,{"data-testid":e,cx:(0,s.toPercent)(t.x),cy:(0,s.toPercent)(t.y),r:(o.get("size",r,n)??0)/2,fill:l===void 0?void 0:(0,s.toPaintColor)(l),opacity:o.get("alpha",r,n),fillOpacity:o.get("fillAlpha",r,n),stroke:u===void 0?void 0:(0,s.toPaintColor)(u),strokeWidth:o.get("strokeWidth",r,n),shouldAnimateTransitions:a})},Ve=({layer:e,sourceLayer:t,mainAxis:r,panelSize:o,styleReaders:n})=>{const a=(0,W.useMemo)(()=>A(t),[t]),l=(0,W.useMemo)(()=>S(t.data,r).outliers,[t.data,r]),{boxes:u,outliers:c}=(0,W.useMemo)(()=>{const i=S(e.data,r),d=new Set(Be(t,l,i.outliers)),m=new Set(ne(l,i.boxes,a.readBoxKey)),k=l.filter(p=>m.has(p)||d.has(p));return{boxes:i.boxes,outliers:k}},[e.data,r,t,l,a]);return(0,f.jsx)(L,{boxes:u,outliers:c,mainAxis:r,panelSize:o,source:t,styleReaders:n,keys:a})},Ne=({layer:e,primary:t,mainAxis:r,panelSize:o,styleReaders:n})=>{const a=(0,W.useMemo)(()=>S(e.data,r).outliers,[e.data,r]),l=(0,W.useMemo)(()=>A(e),[e]),{boxes:u,outliers:c}=S([t.observation],r);return(0,f.jsx)("g",{"data-testid":"hovered-boxes",children:(0,f.jsx)(L,{boxes:u,outliers:u.length>0?ne(a,u,l.readBoxKey):c,mainAxis:r,panelSize:o,source:e,styleReaders:n,keys:l,hovered:t.observation})})},Ae=({layer:e,mainAxis:t,panelSize:r,styleReaders:o,shouldAnimateTransitions:n,intro:a})=>{const{boxes:l,outliers:u}=(0,W.useMemo)(()=>S(e.data,t),[e.data,t]),c=(0,f.jsx)(L,{boxes:l,outliers:u,mainAxis:t,panelSize:r,source:e,styleReaders:o,keys:(0,W.useMemo)(()=>A(e),[e]),shouldAnimateTransitions:n});return a==null?c:(0,f.jsx)(s.IntroFade,{intro:{type:"fade",layerId:a.layerId,durationSeconds:a.durationSeconds},children:c})},Le=(0,s.defineGeomRenderer)(new Z,{coord:"cartesian",swatchShape:"square",overpaintPlacement:"manual",guideMode:"band",render:({layer:e,coordSystem:t,panelRect:r,styleReaders:o,shouldAnimateTransitions:n,intro:a})=>(0,f.jsx)(Ae,{layer:e,mainAxis:(0,s.readMainAxis)(t),panelSize:r,styleReaders:o,shouldAnimateTransitions:n,intro:a}),renderHighlight:({layer:e,sourceLayer:t,coordSystem:r,panelRect:o,styleReaders:n})=>(0,f.jsx)(Ve,{layer:e,sourceLayer:t,mainAxis:(0,s.readMainAxis)(r),panelSize:o,styleReaders:n}),renderHover:({layer:e,primary:t,coordSystem:r,panelRect:o,styleReaders:n})=>(0,f.jsx)(Ne,{layer:e,primary:t,mainAxis:(0,s.readMainAxis)(r),panelSize:o,styleReaders:n}),renderHoverCompanions:()=>null,getEditOutlineShapes:Te});exports.BOXPLOT_VARIABLES=x;exports.BoxplotGeom=Z;exports.boxplot=Le;
package/dist/index.mjs CHANGED
@@ -1,7 +1,7 @@
1
- import { Dataset as me, Geom as ve, GeomCircle as ge, GeomLine as fe, GeomPath as ye, GeomRect as ke, IntroFade as we, POSITION_VARIABLES as B, Stat as xe, UserInputError as We, buildDotsEditOutlineShapes as Me, composePoint as Se, createStableKeyGenerator as Ie, defineGeomRenderer as Oe, defineGeomStyleTarget as be, getGroupVariables as Ee, getScaledAesthetic as b, getXMax as K, getXMin as A, getYMax as U, getYMin as $, readMainAxis as E, readVariableName as V, toPaintColor as T, toPercent as x, toStrokeDasharray as Te, toViewBoxX as _e, toViewBoxY as Be, token as M } from "@graphysdk/react";
1
+ import { Dataset as me, Geom as ve, GeomCircle as ge, GeomLine as fe, GeomOverpaint as ye, GeomPath as ke, GeomRect as we, IntroFade as xe, POSITION_VARIABLES as B, Stat as We, UserInputError as Me, buildDotsEditOutlineShapes as Se, composePoint as Ie, createStableKeyGenerator as Oe, defineGeomRenderer as be, defineGeomStyleTarget as Ee, getGroupVariables as Te, getScaledAesthetic as b, getXMax as R, getXMin as A, getYMax as U, getYMin as $, readGeomEffects as _e, readMainAxis as E, readVariableName as V, toPaintColor as T, toPercent as x, toStrokeDasharray as Be, toViewBoxX as qe, toViewBoxY as Pe, token as M } from "@graphysdk/react";
2
2
  import { useMemo as S } from "react";
3
- import { Fragment as qe, jsx as k, jsxs as Pe } from "react/jsx-runtime";
4
- var y = {
3
+ import { Fragment as Ve, jsx as y, jsxs as Ne } from "react/jsx-runtime";
4
+ var k = {
5
5
  q1: "boxplotQ1",
6
6
  median: "boxplotMedian",
7
7
  q3: "boxplotQ3",
@@ -14,9 +14,9 @@ var y = {
14
14
  relativeWidth: "boxplotRelativeWidth",
15
15
  outlier: "boxplotOutlier"
16
16
  }, N = (e) => (e.start + e.end) / 2, re = (e, t) => {
17
- const r = t === "x" ? A(e) : $(e), o = t === "x" ? K(e) : U(e), n = t === "x" ? $(e) : A(e), a = t === "x" ? U(e) : K(e), s = b(e, "q1"), i = b(e, "median"), u = b(e, "q3");
17
+ const r = t === "x" ? A(e) : $(e), o = t === "x" ? R(e) : U(e), n = t === "x" ? $(e) : A(e), a = t === "x" ? U(e) : R(e), s = b(e, "q1"), i = b(e, "median"), u = b(e, "q3");
18
18
  if (r === null || o === null || n === null || a === null || i === null || s === null || u === null) return null;
19
- const l = C(e, "notchLower"), h = C(e, "notchUpper"), [p, g] = Ve(r, o, e);
19
+ const l = C(e, "notchLower"), h = C(e, "notchUpper"), [p, g] = Le(r, o, e);
20
20
  return {
21
21
  observation: e,
22
22
  start: p,
@@ -33,7 +33,7 @@ var y = {
33
33
  }
34
34
  };
35
35
  }, oe = (e, t) => {
36
- const r = t === "x" ? A(e) : $(e), o = t === "x" ? K(e) : U(e), n = b(e, "outlier");
36
+ const r = t === "x" ? A(e) : $(e), o = t === "x" ? R(e) : U(e), n = b(e, "outlier");
37
37
  return r === null || o === null || n === null ? null : {
38
38
  observation: e,
39
39
  band: N({
@@ -57,14 +57,14 @@ var y = {
57
57
  boxes: r,
58
58
  outliers: o
59
59
  };
60
- }, C = (e, t) => b(e, t) ?? null, Ve = (e, t, r) => {
61
- const o = r[y.relativeWidth];
60
+ }, C = (e, t) => b(e, t) ?? null, Le = (e, t, r) => {
61
+ const o = r[k.relativeWidth];
62
62
  if (typeof o != "number") return [e, t];
63
63
  const n = (e + t) / 2, a = (t - e) * o / 2;
64
64
  return [n - a, n + a];
65
- }, P = 1.5, ne = (e) => e === "min-max" || typeof e == "number" && Number.isFinite(e) && e >= 0, Ne = 1.58, Le = (e, t) => {
65
+ }, P = 1.5, ne = (e) => e === "min-max" || typeof e == "number" && Number.isFinite(e) && e >= 0, Ce = 1.58, De = (e, t) => {
66
66
  if (e.length === 0) return null;
67
- const r = [...e].sort((p, g) => p - g), o = D(r, 0.25), n = D(r, 0.5), a = D(r, 0.75), s = t === "min-max" ? 1 / 0 : t * (a - o), i = o - s, u = a + s, l = r.filter((p) => p >= i && p <= u), h = Ne * (a - o) / Math.sqrt(e.length);
67
+ const r = [...e].sort((p, g) => p - g), o = D(r, 0.25), n = D(r, 0.5), a = D(r, 0.75), s = t === "min-max" ? 1 / 0 : t * (a - o), i = o - s, u = a + s, l = r.filter((p) => p >= i && p <= u), h = Ce * (a - o) / Math.sqrt(e.length);
68
68
  return {
69
69
  lowerWhisker: Math.min(l[0] ?? o, o),
70
70
  q1: o,
@@ -80,19 +80,19 @@ var y = {
80
80
  const r = (e.length - 1) * t, o = Math.floor(r), n = e[o] ?? 0;
81
81
  return n + ((e[o + 1] ?? n) - n) * (r - o);
82
82
  }, ae = {
83
- q1: y.q1,
84
- median: y.median,
85
- q3: y.q3,
86
- lowerWhisker: y.lowerWhisker,
87
- upperWhisker: y.upperWhisker,
88
- count: y.count,
89
- outlier: y.outlier
83
+ q1: k.q1,
84
+ median: k.median,
85
+ q3: k.q3,
86
+ lowerWhisker: k.lowerWhisker,
87
+ upperWhisker: k.upperWhisker,
88
+ count: k.count,
89
+ outlier: k.outlier
90
90
  }, se = /* @__PURE__ */ new Set([
91
91
  ...Object.keys(ae),
92
92
  "mean",
93
93
  "notchLower",
94
94
  "notchUpper"
95
- ]), Ce = class extends xe {
95
+ ]), Ke = class extends We {
96
96
  constructor(...e) {
97
97
  super(...e), this.type = "boxplot", this.computedVariables = se;
98
98
  }
@@ -104,7 +104,7 @@ var y = {
104
104
  };
105
105
  if (e.size() > 0 && e.getType(n) !== "numeric") {
106
106
  const s = e.getType(n);
107
- throw new We({
107
+ throw new Me({
108
108
  code: "INCOMPATIBLE_TYPE",
109
109
  message: `'boxplot' stat requires a numeric 'y' (got '${s}' for variable '${n}')`,
110
110
  context: {
@@ -117,18 +117,18 @@ var y = {
117
117
  }
118
118
  const a = r?.extent;
119
119
  return {
120
- data: De({
120
+ data: Re({
121
121
  data: e,
122
122
  xVariable: o,
123
123
  yVariable: n,
124
- groupVariables: Ee(e, t).filter((s) => s !== o),
124
+ groupVariables: Te(e, t).filter((s) => s !== o),
125
125
  extent: ne(a) ? a : P
126
126
  }),
127
127
  mapping: ae
128
128
  };
129
129
  }
130
- }, De = ({ data: e, xVariable: t, yVariable: r, groupVariables: o, extent: n }) => {
131
- const a = Re(e, t, r, o), s = e.getVariableNames(), i = new Map(s.map((d) => [d, e.getValues(d)])), u = new Map(s.map((d) => [d, []])), l = {
130
+ }, Re = ({ data: e, xVariable: t, yVariable: r, groupVariables: o, extent: n }) => {
131
+ const a = Ae(e, t, r, o), s = e.getVariableNames(), i = new Map(s.map((d) => [d, e.getValues(d)])), u = new Map(s.map((d) => [d, []])), l = {
132
132
  q1: [],
133
133
  median: [],
134
134
  q3: [],
@@ -141,9 +141,9 @@ var y = {
141
141
  outlier: []
142
142
  }, h = [];
143
143
  for (const d of a) {
144
- const m = Le(d.values, n);
144
+ const m = De(d.values, n);
145
145
  if (m === null) continue;
146
- for (const c of s) u.get(c)?.push(Ke(i.get(c) ?? [], d.indices));
146
+ for (const c of s) u.get(c)?.push(Ue(i.get(c) ?? [], d.indices));
147
147
  l.q1.push(m.q1), l.median.push(m.median), l.q3.push(m.q3), l.lowerWhisker.push(m.lowerWhisker), l.upperWhisker.push(m.upperWhisker), l.count.push(m.count), l.mean.push(m.mean), l.notchLower.push(m.notchLower), l.notchUpper.push(m.notchUpper), l.outlier.push(null);
148
148
  const w = {
149
149
  low: m.lowerWhisker,
@@ -167,7 +167,7 @@ var y = {
167
167
  valueFormat: e.getValueFormat(d)
168
168
  }]));
169
169
  for (const [d, m] of Object.entries(l)) {
170
- const w = y[d];
170
+ const w = k[d];
171
171
  g[w] = d === "count" ? {
172
172
  type: "numeric",
173
173
  values: m
@@ -178,24 +178,24 @@ var y = {
178
178
  };
179
179
  }
180
180
  return new me(g);
181
- }, Re = (e, t, r, o) => {
181
+ }, Ae = (e, t, r, o) => {
182
182
  const n = [t, ...o].map((i) => e.getValues(i)), a = e.getValues(r, { type: "numeric" }), s = /* @__PURE__ */ new Map();
183
183
  return a.forEach((i, u) => {
184
184
  if (i === null || !Number.isFinite(i)) return;
185
- const l = n.map((p) => H(p[u] ?? null)).join("\0"), h = s.get(l) ?? {
185
+ const l = n.map((p) => G(p[u] ?? null)).join("\0"), h = s.get(l) ?? {
186
186
  indices: [],
187
187
  values: []
188
188
  };
189
189
  h.indices.push(u), h.values.push(i), s.set(l, h);
190
190
  }), [...s.values()];
191
- }, Ke = (e, t) => {
192
- const r = e[t[0] ?? -1] ?? null, o = H(r);
193
- return t.every((n) => H(e[n] ?? null) === o) ? r : null;
194
- }, H = (e) => e === null ? "\0null" : e instanceof Date ? `date:${e.getTime()}` : `${typeof e}:${String(e)}`, le = 0.6, Ae = 1.5, Ue = 0.3, $e = 2, z = 1.5, He = 6, Fe = 1.5, Ge = 7, Xe = 1.5, ze = [
191
+ }, Ue = (e, t) => {
192
+ const r = e[t[0] ?? -1] ?? null, o = G(r);
193
+ return t.every((n) => G(e[n] ?? null) === o) ? r : null;
194
+ }, G = (e) => e === null ? "\0null" : e instanceof Date ? `date:${e.getTime()}` : `${typeof e}:${String(e)}`, le = 0.6, $e = 1.5, Ge = 0.3, Fe = 2, z = 1.5, He = 6, Xe = 1.5, ze = 7, Ye = 1.5, je = [
195
195
  "q1",
196
196
  "median",
197
197
  "q3"
198
- ], Ye = { strokeWidth: "pixels" }, R = {
198
+ ], Qe = { strokeWidth: "pixels" }, K = {
199
199
  stroke: "color",
200
200
  alpha: "unitInterval",
201
201
  strokeWidth: "pixels",
@@ -207,7 +207,7 @@ var y = {
207
207
  alpha: "unitInterval",
208
208
  size: "pixels",
209
209
  strokeWidth: "pixels"
210
- }, je = class extends ve {
210
+ }, Ze = class extends ve {
211
211
  constructor(...e) {
212
212
  super(...e), this.type = "boxplot", this.defaultParams = {
213
213
  extent: P,
@@ -215,7 +215,7 @@ var y = {
215
215
  notch: !1,
216
216
  showMean: !1,
217
217
  varwidth: !1
218
- }, this.defaultStat = new Ce(), this.derivedVariables = Object.values(y), this.positionRoles = [
218
+ }, this.defaultStat = new Ke(), this.derivedVariables = Object.values(k), this.positionRoles = [
219
219
  {
220
220
  axis: "x",
221
221
  role: "point",
@@ -329,33 +329,33 @@ var y = {
329
329
  aes: "outlier",
330
330
  optional: !0
331
331
  }
332
- ], this.styleTarget = be({
332
+ ], this.styleTarget = Ee({
333
333
  observation: {
334
- vocabulary: Ye,
334
+ vocabulary: Qe,
335
335
  aesthetics: {
336
336
  fill: "color",
337
337
  stroke: "color"
338
338
  },
339
339
  rest: {
340
340
  fill: M("geom"),
341
- fillAlpha: Ue,
341
+ fillAlpha: Ge,
342
342
  stroke: M("geom"),
343
- strokeWidth: Ae
343
+ strokeWidth: $e
344
344
  }
345
345
  },
346
346
  parts: {
347
347
  median: {
348
- vocabulary: R,
348
+ vocabulary: K,
349
349
  aesthetics: { stroke: "color" },
350
350
  rest: {
351
351
  stroke: M("geom"),
352
352
  alpha: 1,
353
- strokeWidth: $e,
353
+ strokeWidth: Fe,
354
354
  dashArray: []
355
355
  }
356
356
  },
357
357
  whisker: {
358
- vocabulary: R,
358
+ vocabulary: K,
359
359
  aesthetics: { stroke: "color" },
360
360
  rest: {
361
361
  stroke: M("geom"),
@@ -365,7 +365,7 @@ var y = {
365
365
  }
366
366
  },
367
367
  cap: {
368
- vocabulary: R,
368
+ vocabulary: K,
369
369
  aesthetics: { stroke: "color" },
370
370
  rest: {
371
371
  stroke: M("geom"),
@@ -386,7 +386,7 @@ var y = {
386
386
  stroke: M("geom"),
387
387
  alpha: 1,
388
388
  size: He,
389
- strokeWidth: Fe
389
+ strokeWidth: Xe
390
390
  }
391
391
  },
392
392
  mean: {
@@ -397,12 +397,12 @@ var y = {
397
397
  fillAlpha: 1,
398
398
  stroke: M("graphBackground"),
399
399
  alpha: 1,
400
- size: Ge,
401
- strokeWidth: Xe
400
+ size: ze,
401
+ strokeWidth: Ye
402
402
  }
403
403
  }
404
404
  }
405
- }), this.resolveBandFraction = (t) => j(t), this.resolveValueSource = (t) => t.median ?? { variable: y.median }, this.validateMapping = ({ mapping: t, computedVariables: r }) => r.has("y") ? [{
405
+ }), this.resolveBandFraction = (t) => j(t), this.resolveValueSource = (t) => t.median ?? { variable: k.median }, this.validateMapping = ({ mapping: t, computedVariables: r }) => r.has("y") ? [{
406
406
  code: "CONFLICTING_STAT_MAPPING",
407
407
  message: "boxplot geom draws a summary, not a single 'y'; use the 'boxplot' stat or 'identity'"
408
408
  }] : r.has("median") ? [...[...se].filter((o) => t[o] !== void 0).map((o) => ({
@@ -411,12 +411,12 @@ var y = {
411
411
  })), ...t.y === void 0 ? [{
412
412
  code: "MISSING_AESTHETIC",
413
413
  message: "boxplot stat summarises 'y'; map it to the raw values"
414
- }] : []] : ze.filter((o) => t[o] === void 0).map((o) => ({
414
+ }] : []] : je.filter((o) => t[o] === void 0).map((o) => ({
415
415
  code: "MISSING_AESTHETIC",
416
416
  message: `boxplot geom requires '${o}' when its data is already summarised`,
417
417
  context: { aesthetic: o }
418
418
  })), this.resolveAnchorPosition = (t, { coordSystem: r }) => {
419
- const o = E(r), n = Qe(t, o);
419
+ const o = E(r), n = Je(t, o);
420
420
  return n === null ? null : o === "x" ? {
421
421
  x: n.band,
422
422
  y: n.value
@@ -440,15 +440,15 @@ var y = {
440
440
  data: e,
441
441
  mapping: {}
442
442
  };
443
- const { outliers: a, notch: s, showMean: i, varwidth: u } = r, l = q(e, t.outlier), h = a === !1 && l !== null ? et(e, l) : e, p = q(h, t.count), g = j(r) / 2;
443
+ const { outliers: a, notch: s, showMean: i, varwidth: u } = r, l = q(e, t.outlier), h = a === !1 && l !== null ? rt(e, l) : e, p = q(h, t.count), g = j(r) / 2;
444
444
  return {
445
- data: h.addVariable(y.relativeWidth, "numeric", u === !0 && p !== null ? Ze(h, p) : Je(h)).addVariable(B.yMin, "numeric", Q(h, o, l)).addVariable(B.yMax, "numeric", Q(h, n, l)).addConstantVariable(B.xMin, "numeric", -g).addConstantVariable(B.xMax, "numeric", g),
445
+ data: h.addVariable(k.relativeWidth, "numeric", u === !0 && p !== null ? et(h, p) : tt(h)).addVariable(B.yMin, "numeric", Q(h, o, l)).addVariable(B.yMax, "numeric", Q(h, n, l)).addConstantVariable(B.xMin, "numeric", -g).addConstantVariable(B.xMax, "numeric", g),
446
446
  mapping: {
447
447
  ...t.y === void 0 && { y: t.median },
448
- ...i === !0 && t.mean === void 0 && h.hasVariable(y.mean) && { mean: y.mean },
449
- ...s === !0 && t.notchLower === void 0 && h.hasVariable(y.notchLower) && {
450
- notchLower: y.notchLower,
451
- notchUpper: y.notchUpper
448
+ ...i === !0 && t.mean === void 0 && h.hasVariable(k.mean) && { mean: k.mean },
449
+ ...s === !0 && t.notchLower === void 0 && h.hasVariable(k.notchLower) && {
450
+ notchLower: k.notchLower,
451
+ notchUpper: k.notchUpper
452
452
  }
453
453
  }
454
454
  };
@@ -483,7 +483,7 @@ var y = {
483
483
  suggestion: "Set params.width to a fraction of the category band in (0, 1]."
484
484
  }), o !== void 0 ? { width: o } : {};
485
485
  }
486
- }, Qe = (e, t) => {
486
+ }, Je = (e, t) => {
487
487
  const r = re(e, t);
488
488
  if (r !== null) return {
489
489
  band: N(r),
@@ -494,20 +494,20 @@ var y = {
494
494
  band: o.band,
495
495
  value: o.value
496
496
  };
497
- }, j = ({ width: e }) => e ?? le, Ze = (e, t) => {
497
+ }, j = ({ width: e }) => e ?? le, et = (e, t) => {
498
498
  const r = e.getValues(t, { type: "numeric" }), o = Math.max(0, ...r.map((n) => n ?? 0));
499
499
  return r.map((n) => n === null || o === 0 ? null : Math.sqrt(n / o));
500
- }, Je = (e) => Array.from({ length: e.size() }, () => null), Q = (e, t, r) => {
500
+ }, tt = (e) => Array.from({ length: e.size() }, () => null), Q = (e, t, r) => {
501
501
  const o = r === null ? [] : e.getValues(r, { type: "numeric" });
502
502
  return e.getValues(t, { type: "numeric" }).map((n, a) => n ?? o[a] ?? null);
503
- }, et = (e, t) => {
503
+ }, rt = (e, t) => {
504
504
  const r = e.getValues(t, { type: "numeric" });
505
505
  return e.take(r.flatMap((o, n) => o === null ? [n] : []));
506
506
  }, q = (e, t) => {
507
507
  const r = V(t);
508
508
  return r !== null && e.hasVariable(r) ? r : null;
509
- }, tt = 5e-4, rt = 0.5, ot = 0.5, ie = (e, t) => {
510
- const r = (e.q1 + e.q3) / 2, o = Math.max(Math.abs(e.q3 - e.q1), tt) / 2, n = v(t, e.start, r - o), a = v(t, e.end, r + o), [s, i] = Z(n.x, a.x), [u, l] = Z(n.y, a.y);
509
+ }, ot = 5e-4, nt = 0.5, at = 0.5, ie = (e, t) => {
510
+ const r = (e.q1 + e.q3) / 2, o = Math.max(Math.abs(e.q3 - e.q1), ot) / 2, n = v(t, e.start, r - o), a = v(t, e.end, r + o), [s, i] = Z(n.x, a.x), [u, l] = Z(n.y, a.y);
511
511
  return {
512
512
  x: s,
513
513
  y: u,
@@ -529,17 +529,17 @@ var y = {
529
529
  v(t, e.end, r),
530
530
  v(t, e.end, e.q1)
531
531
  ];
532
- }, nt = (e, t) => {
532
+ }, st = (e, t) => {
533
533
  const [r, o] = e.notch === null ? [e.start, e.end] : he(e);
534
534
  return {
535
535
  from: v(t, r, e.median),
536
536
  to: v(t, o, e.median)
537
537
  };
538
538
  }, ce = (e, t) => e.mean === null ? null : v(t, N(e), e.mean), he = (e) => {
539
- const t = (e.end - e.start) * (1 - ot) / 2;
539
+ const t = (e.end - e.start) * (1 - at) / 2;
540
540
  return [e.start + t, e.end - t];
541
541
  }, de = (e, t) => {
542
- const r = N(e), o = (e.end - e.start) * rt / 2;
542
+ const r = N(e), o = (e.end - e.start) * nt / 2;
543
543
  return [{
544
544
  side: "lower",
545
545
  quartile: e.q1,
@@ -560,13 +560,13 @@ var y = {
560
560
  }
561
561
  }));
562
562
  }, v = (e, t, r) => {
563
- const o = Se(e, {
563
+ const o = Ie(e, {
564
564
  main: t,
565
565
  cross: r
566
566
  });
567
567
  return {
568
- x: _e(o.x),
569
- y: Be(o.y)
568
+ x: qe(o.x),
569
+ y: Pe(o.y)
570
570
  };
571
571
  }, Z = (e, t) => e <= t ? [e, t] : [t, e], _ = (e, t) => ({
572
572
  x: e.x * t.width,
@@ -574,7 +574,7 @@ var y = {
574
574
  }), F = (e, t) => `${e.map((r, o) => {
575
575
  const { x: n, y: a } = _(r, t);
576
576
  return `${o === 0 ? "M" : "L"}${n},${a}`;
577
- }).join("")}Z`, at = (e, t) => F([
577
+ }).join("")}Z`, lt = (e, t) => F([
578
578
  {
579
579
  x: e.x,
580
580
  y: e.y
@@ -591,10 +591,10 @@ var y = {
591
591
  x: e.x,
592
592
  y: e.y + e.height
593
593
  }
594
- ], t), st = (e, t) => {
594
+ ], t), it = (e, t) => {
595
595
  const r = _(e.from, t), o = _(e.to, t);
596
596
  return `M${r.x},${r.y}L${o.x},${o.y}`;
597
- }, lt = ({ layer: e, coordSystem: t, panelRect: r, styleReaders: o }) => {
597
+ }, ut = ({ layer: e, coordSystem: t, panelRect: r, styleReaders: o }) => {
598
598
  const n = E(t), { boxes: a, outliers: s } = I(e.data, n), i = a.map((c) => {
599
599
  const f = ue(c, n);
600
600
  return f !== null ? {
@@ -602,7 +602,7 @@ var y = {
602
602
  pathData: F(f, r)
603
603
  } : {
604
604
  kind: "region",
605
- pathData: at(ie(c, n), r)
605
+ pathData: lt(ie(c, n), r)
606
606
  };
607
607
  }), u = o.part("whisker"), l = o.part("cap"), h = a.flatMap((c) => de(c, n).flatMap((f) => [{
608
608
  segment: f.stem,
@@ -614,7 +614,7 @@ var y = {
614
614
  const O = L?.get("strokeWidth", c.observation) ?? 0;
615
615
  return O > 0 ? [{
616
616
  kind: "stroke",
617
- pathData: st(W, r),
617
+ pathData: it(W, r),
618
618
  width: O
619
619
  }] : [];
620
620
  }))), p = o.part("outlier"), g = p === void 0 ? [] : s.map((c) => ({
@@ -626,14 +626,14 @@ var y = {
626
626
  center: _(f, r),
627
627
  size: J(d, c.observation)
628
628
  }];
629
- }), w = Me([...g, ...m]);
629
+ }), w = Se([...g, ...m]);
630
630
  return [
631
631
  ...h,
632
632
  ...i,
633
633
  ...w
634
634
  ];
635
- }, J = (e, t) => (e.get("size", t) ?? 0) + (e.get("strokeWidth", t) ?? 0), G = (e) => {
636
- const t = Ie(e.data, e.mapping, e.id), r = V(e.mapping.outlier), o = (n) => `${t(n.observation)}-outlier-${String(r === null ? n.value : n.observation[r])}`;
635
+ }, J = (e, t) => (e.get("size", t) ?? 0) + (e.get("strokeWidth", t) ?? 0), H = (e) => {
636
+ const t = Oe(e.data, e.mapping, e.id), r = V(e.mapping.outlier), o = (n) => `${t(n.observation)}-outlier-${String(r === null ? n.value : n.observation[r])}`;
637
637
  return {
638
638
  readBoxKey: t,
639
639
  readOutlierKeys: (n) => {
@@ -644,15 +644,15 @@ var y = {
644
644
  });
645
645
  }
646
646
  };
647
- }, it = (e, t, r) => {
647
+ }, ct = (e, t, r) => {
648
648
  const o = e.data.getVariableNames(), n = (s) => JSON.stringify(o.map((i) => s.observation[i] ?? null)), a = new Set(r.map(n));
649
649
  return t.filter((s) => a.has(n(s)));
650
650
  }, pe = (e, t, r) => {
651
651
  const o = new Set(t.map((n) => r(n.observation)));
652
652
  return e.filter((n) => o.has(r(n.observation)));
653
- }, X = ({ boxes: e, outliers: t, mainAxis: r, panelSize: o, source: n, styleReaders: a, keys: s, isHovered: i = !1, shouldAnimateTransitions: u = !1 }) => {
654
- const l = i ? "hovered" : void 0, h = a.part("whisker"), p = a.part("cap"), g = a.part("median"), d = a.part("mean"), m = a.part("outlier"), w = s.readOutlierKeys(t);
655
- return /* @__PURE__ */ Pe(qe, { children: [
653
+ }, X = ({ boxes: e, outliers: t, mainAxis: r, panelSize: o, source: n, styleReaders: a, keys: s, hovered: i, shouldAnimateTransitions: u = !1 }) => {
654
+ const l = i === void 0 ? void 0 : "hovered", h = a.part("whisker"), p = a.part("cap"), g = a.part("median"), d = a.part("mean"), m = a.part("outlier"), w = s.readOutlierKeys(t);
655
+ return /* @__PURE__ */ Ne(Ve, { children: [
656
656
  e.flatMap((c) => de(c, r).flatMap((f) => [{
657
657
  piece: "stem",
658
658
  testId: "boxplot-whisker",
@@ -661,7 +661,7 @@ var y = {
661
661
  piece: "cap",
662
662
  testId: "boxplot-cap",
663
663
  readers: p
664
- }].map(({ piece: W, testId: L, readers: O }) => O === void 0 ? null : /* @__PURE__ */ k(ee, {
664
+ }].map(({ piece: W, testId: L, readers: O }) => O === void 0 ? null : /* @__PURE__ */ y(ee, {
665
665
  testId: L,
666
666
  segment: f[W],
667
667
  observation: c.observation,
@@ -669,18 +669,22 @@ var y = {
669
669
  state: l,
670
670
  shouldAnimateTransitions: u
671
671
  }, `${s.readBoxKey(c.observation)}-${f.side}-${W}`)))),
672
- e.map((c) => /* @__PURE__ */ k(ut, {
673
- box: c,
674
- mainAxis: r,
675
- panelSize: o,
676
- source: n,
672
+ /* @__PURE__ */ y(ye, {
677
673
  styleReaders: a,
678
674
  state: l,
679
- shouldAnimateTransitions: u
680
- }, s.readBoxKey(c.observation))),
681
- g && e.map((c) => /* @__PURE__ */ k(ee, {
675
+ children: e.map((c) => /* @__PURE__ */ y(ht, {
676
+ box: c,
677
+ mainAxis: r,
678
+ panelSize: o,
679
+ source: n,
680
+ styleReaders: a,
681
+ state: l,
682
+ shouldAnimateTransitions: u
683
+ }, s.readBoxKey(c.observation)))
684
+ }),
685
+ g && e.map((c) => /* @__PURE__ */ y(ee, {
682
686
  testId: "boxplot-median",
683
- segment: nt(c, r),
687
+ segment: st(c, r),
684
688
  observation: c.observation,
685
689
  readers: g,
686
690
  state: l,
@@ -688,7 +692,7 @@ var y = {
688
692
  }, s.readBoxKey(c.observation))),
689
693
  d && e.map((c) => {
690
694
  const f = ce(c, r);
691
- return f === null ? null : /* @__PURE__ */ k(te, {
695
+ return f === null ? null : /* @__PURE__ */ y(te, {
692
696
  testId: "boxplot-mean",
693
697
  centre: f,
694
698
  observation: c.observation,
@@ -697,7 +701,7 @@ var y = {
697
701
  shouldAnimateTransitions: u
698
702
  }, s.readBoxKey(c.observation));
699
703
  }),
700
- m && t.map((c, f) => /* @__PURE__ */ k(te, {
704
+ m && t.map((c, f) => /* @__PURE__ */ y(te, {
701
705
  testId: "boxplot-outlier",
702
706
  centre: v(r, c.band, c.value),
703
707
  observation: c.observation,
@@ -708,7 +712,7 @@ var y = {
708
712
  ] });
709
713
  }, ee = ({ testId: e, segment: t, observation: r, readers: o, state: n, shouldAnimateTransitions: a }) => {
710
714
  const s = o.get("strokeWidth", r, n) ?? 0, i = o.get("stroke", r, n);
711
- return /* @__PURE__ */ k(fe, {
715
+ return /* @__PURE__ */ y(fe, {
712
716
  "data-testid": e,
713
717
  x1: x(t.from.x),
714
718
  y1: x(t.from.y),
@@ -717,10 +721,10 @@ var y = {
717
721
  stroke: i === void 0 ? void 0 : T(i),
718
722
  strokeOpacity: o.get("alpha", r, n),
719
723
  strokeWidth: s,
720
- strokeDasharray: Te(o.get("dashArray", r, n), s),
724
+ strokeDasharray: Be(o.get("dashArray", r, n), s),
721
725
  shouldAnimateTransitions: a
722
726
  });
723
- }, ut = ({ box: e, mainAxis: t, panelSize: r, source: o, styleReaders: n, state: a, shouldAnimateTransitions: s }) => {
727
+ }, ht = ({ box: e, mainAxis: t, panelSize: r, source: o, styleReaders: n, state: a, shouldAnimateTransitions: s }) => {
724
728
  const i = n.get("fill", e.observation, a), u = n.get("stroke", e.observation, a), l = {
725
729
  fill: i === void 0 ? void 0 : T(i),
726
730
  opacity: n.get("alpha", e.observation, a),
@@ -728,16 +732,17 @@ var y = {
728
732
  stroke: u === void 0 ? void 0 : T(u),
729
733
  strokeOpacity: n.get("strokeAlpha", e.observation, a),
730
734
  strokeWidth: n.get("strokeWidth", e.observation, a),
735
+ effects: _e(n, e.observation, a),
731
736
  shouldAnimateTransitions: s
732
737
  }, h = ue(e, t);
733
- if (h !== null) return /* @__PURE__ */ k(ye, {
738
+ if (h !== null) return /* @__PURE__ */ y(ke, {
734
739
  "data-testid": "boxplot-box",
735
740
  d: F(h, r),
736
741
  source: o,
737
742
  ...l
738
743
  });
739
744
  const p = ie(e, t);
740
- return /* @__PURE__ */ k(ke, {
745
+ return /* @__PURE__ */ y(we, {
741
746
  "data-testid": "boxplot-box",
742
747
  x: x(p.x),
743
748
  y: x(p.y),
@@ -747,7 +752,7 @@ var y = {
747
752
  });
748
753
  }, te = ({ testId: e, centre: t, observation: r, readers: o, state: n, shouldAnimateTransitions: a }) => {
749
754
  const s = o.get("fill", r, n), i = o.get("stroke", r, n);
750
- return /* @__PURE__ */ k(ge, {
755
+ return /* @__PURE__ */ y(ge, {
751
756
  "data-testid": e,
752
757
  cx: x(t.x),
753
758
  cy: x(t.y),
@@ -759,9 +764,9 @@ var y = {
759
764
  strokeWidth: o.get("strokeWidth", r, n),
760
765
  shouldAnimateTransitions: a
761
766
  });
762
- }, ct = ({ layer: e, sourceLayer: t, mainAxis: r, panelSize: o, styleReaders: n }) => {
763
- const a = S(() => G(t), [t]), s = S(() => I(t.data, r).outliers, [t.data, r]), { boxes: i, outliers: u } = S(() => {
764
- const l = I(e.data, r), h = new Set(it(t, s, l.outliers)), p = new Set(pe(s, l.boxes, a.readBoxKey)), g = s.filter((d) => p.has(d) || h.has(d));
767
+ }, dt = ({ layer: e, sourceLayer: t, mainAxis: r, panelSize: o, styleReaders: n }) => {
768
+ const a = S(() => H(t), [t]), s = S(() => I(t.data, r).outliers, [t.data, r]), { boxes: i, outliers: u } = S(() => {
769
+ const l = I(e.data, r), h = new Set(ct(t, s, l.outliers)), p = new Set(pe(s, l.boxes, a.readBoxKey)), g = s.filter((d) => p.has(d) || h.has(d));
765
770
  return {
766
771
  boxes: l.boxes,
767
772
  outliers: g
@@ -773,7 +778,7 @@ var y = {
773
778
  s,
774
779
  a
775
780
  ]);
776
- return /* @__PURE__ */ k(X, {
781
+ return /* @__PURE__ */ y(X, {
777
782
  boxes: i,
778
783
  outliers: u,
779
784
  mainAxis: r,
@@ -782,11 +787,11 @@ var y = {
782
787
  styleReaders: n,
783
788
  keys: a
784
789
  });
785
- }, ht = ({ layer: e, primary: t, mainAxis: r, panelSize: o, styleReaders: n }) => {
786
- const a = S(() => I(e.data, r).outliers, [e.data, r]), s = S(() => G(e), [e]), { boxes: i, outliers: u } = I([t.observation], r);
787
- return /* @__PURE__ */ k("g", {
790
+ }, pt = ({ layer: e, primary: t, mainAxis: r, panelSize: o, styleReaders: n }) => {
791
+ const a = S(() => I(e.data, r).outliers, [e.data, r]), s = S(() => H(e), [e]), { boxes: i, outliers: u } = I([t.observation], r);
792
+ return /* @__PURE__ */ y("g", {
788
793
  "data-testid": "hovered-boxes",
789
- children: /* @__PURE__ */ k(X, {
794
+ children: /* @__PURE__ */ y(X, {
790
795
  boxes: i,
791
796
  outliers: i.length > 0 ? pe(a, i, s.readBoxKey) : u,
792
797
  mainAxis: r,
@@ -794,21 +799,21 @@ var y = {
794
799
  source: e,
795
800
  styleReaders: n,
796
801
  keys: s,
797
- isHovered: !0
802
+ hovered: t.observation
798
803
  })
799
804
  });
800
- }, dt = ({ layer: e, mainAxis: t, panelSize: r, styleReaders: o, shouldAnimateTransitions: n, intro: a }) => {
801
- const { boxes: s, outliers: i } = S(() => I(e.data, t), [e.data, t]), u = /* @__PURE__ */ k(X, {
805
+ }, mt = ({ layer: e, mainAxis: t, panelSize: r, styleReaders: o, shouldAnimateTransitions: n, intro: a }) => {
806
+ const { boxes: s, outliers: i } = S(() => I(e.data, t), [e.data, t]), u = /* @__PURE__ */ y(X, {
802
807
  boxes: s,
803
808
  outliers: i,
804
809
  mainAxis: t,
805
810
  panelSize: r,
806
811
  source: e,
807
812
  styleReaders: o,
808
- keys: S(() => G(e), [e]),
813
+ keys: S(() => H(e), [e]),
809
814
  shouldAnimateTransitions: n
810
815
  });
811
- return a == null ? u : /* @__PURE__ */ k(we, {
816
+ return a == null ? u : /* @__PURE__ */ y(xe, {
812
817
  intro: {
813
818
  type: "fade",
814
819
  layerId: a.layerId,
@@ -816,11 +821,12 @@ var y = {
816
821
  },
817
822
  children: u
818
823
  });
819
- }, gt = Oe(new je(), {
824
+ }, yt = be(new Ze(), {
820
825
  coord: "cartesian",
821
826
  swatchShape: "square",
827
+ overpaintPlacement: "manual",
822
828
  guideMode: "band",
823
- render: ({ layer: e, coordSystem: t, panelRect: r, styleReaders: o, shouldAnimateTransitions: n, intro: a }) => /* @__PURE__ */ k(dt, {
829
+ render: ({ layer: e, coordSystem: t, panelRect: r, styleReaders: o, shouldAnimateTransitions: n, intro: a }) => /* @__PURE__ */ y(mt, {
824
830
  layer: e,
825
831
  mainAxis: E(t),
826
832
  panelSize: r,
@@ -828,14 +834,14 @@ var y = {
828
834
  shouldAnimateTransitions: n,
829
835
  intro: a
830
836
  }),
831
- renderHighlight: ({ layer: e, sourceLayer: t, coordSystem: r, panelRect: o, styleReaders: n }) => /* @__PURE__ */ k(ct, {
837
+ renderHighlight: ({ layer: e, sourceLayer: t, coordSystem: r, panelRect: o, styleReaders: n }) => /* @__PURE__ */ y(dt, {
832
838
  layer: e,
833
839
  sourceLayer: t,
834
840
  mainAxis: E(r),
835
841
  panelSize: o,
836
842
  styleReaders: n
837
843
  }),
838
- renderHover: ({ layer: e, primary: t, coordSystem: r, panelRect: o, styleReaders: n }) => /* @__PURE__ */ k(ht, {
844
+ renderHover: ({ layer: e, primary: t, coordSystem: r, panelRect: o, styleReaders: n }) => /* @__PURE__ */ y(pt, {
839
845
  layer: e,
840
846
  primary: t,
841
847
  mainAxis: E(r),
@@ -843,10 +849,10 @@ var y = {
843
849
  styleReaders: n
844
850
  }),
845
851
  renderHoverCompanions: () => null,
846
- getEditOutlineShapes: lt
852
+ getEditOutlineShapes: ut
847
853
  });
848
854
  export {
849
- y as BOXPLOT_VARIABLES,
850
- je as BoxplotGeom,
851
- gt as boxplot
855
+ k as BOXPLOT_VARIABLES,
856
+ Ze as BoxplotGeom,
857
+ yt as boxplot
852
858
  };
@@ -1,10 +1,10 @@
1
- import { GeomStyleReaders, MainAxis, SceneLayer } from '@graphysdk/react';
1
+ import { GeomStyleReaders, MainAxis, Observation, SceneLayer } from '@graphysdk/react';
2
2
  import { Box, Outlier } from '../utils/read-boxplot';
3
3
  import { BoxplotKeys } from './utils/boxplot-keys';
4
4
  import { PanelSize } from './utils/panel-path';
5
5
  /**
6
- * What one pass paints: every whisker below, every box above them with its median and mean, and the outliers
7
- * on top.
6
+ * What one pass paints: every whisker below, every box above them under the overpaint, then each box's median
7
+ * and mean, and the outliers on top.
8
8
  */
9
9
  export interface BoxplotPaintProps {
10
10
  boxes: readonly Box[];
@@ -17,12 +17,12 @@ export interface BoxplotPaintProps {
17
17
  styleReaders: GeomStyleReaders;
18
18
  /** What identifies each mark across a data change, so a spring moves a mark rather than swapping it. */
19
19
  keys: BoxplotKeys;
20
- /** Paints in the `hovered` state. */
21
- isHovered?: boolean;
20
+ /** The observation under the pointer: paints in the `hovered` state. */
21
+ hovered?: Observation;
22
22
  /**
23
23
  * Whether marks spring to their new positions when the data changes. The hover and highlight repaints
24
24
  * mount and unmount with the interaction, so only the layer's own paint animates.
25
25
  */
26
26
  shouldAnimateTransitions?: boolean;
27
27
  }
28
- export declare const BoxplotPaint: ({ boxes, outliers, mainAxis, panelSize, source, styleReaders, keys, isHovered, shouldAnimateTransitions, }: BoxplotPaintProps) => import("react/jsx-runtime").JSX.Element;
28
+ export declare const BoxplotPaint: ({ boxes, outliers, mainAxis, panelSize, source, styleReaders, keys, hovered, shouldAnimateTransitions, }: BoxplotPaintProps) => import("react/jsx-runtime").JSX.Element;
package/package.json CHANGED
@@ -13,7 +13,7 @@
13
13
  "boxplot",
14
14
  "box-plot"
15
15
  ],
16
- "version": "0.1.1-beta.1792399585685",
16
+ "version": "0.1.1-beta.1792404455940",
17
17
  "type": "module",
18
18
  "sideEffects": false,
19
19
  "types": "./dist/index.d.ts",
@@ -32,7 +32,7 @@
32
32
  },
33
33
  "peerDependencies": {
34
34
  "react": "^19.0.0",
35
- "@graphysdk/react": "^1.9.0-beta.1792399585685"
35
+ "@graphysdk/react": "^1.9.0-beta.1792404455940"
36
36
  },
37
37
  "devDependencies": {
38
38
  "@testing-library/react": "^16.3.1",
@@ -44,11 +44,11 @@
44
44
  "vite": "^8.1.5",
45
45
  "vite-plugin-dts": "^4.5.4",
46
46
  "vitest": "^4.1.11",
47
- "@graphysdk/react": "1.9.0-beta.1792399585685",
48
- "@graphysdk/viz-engine": "1.9.0-beta.1792399585685",
49
- "@graphytools/eslint-config": "0.0.1",
47
+ "@graphysdk/react": "1.9.0-beta.1792404455940",
50
48
  "@graphytools/typescript-config": "0.0.1",
51
- "@graphytools/vitest-config": "1.0.0"
49
+ "@graphytools/eslint-config": "0.0.1",
50
+ "@graphytools/vitest-config": "1.0.0",
51
+ "@graphysdk/viz-engine": "1.9.0-beta.1792404455940"
52
52
  },
53
53
  "publishConfig": {
54
54
  "access": "public"