@graphysdk/geom-boxplot 0.1.1-beta.1793017273153 → 0.1.1-beta.1793027249956

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.
@@ -80,47 +80,47 @@ export declare class BoxplotGeom extends Geom<BoxplotGeomParams> {
80
80
  readonly axis: "y";
81
81
  readonly role: "min";
82
82
  readonly valueKind: "value";
83
- readonly aes: "lowerWhisker";
83
+ readonly aesthetic: "lowerWhisker";
84
84
  }, {
85
85
  readonly axis: "y";
86
86
  readonly role: "max";
87
87
  readonly valueKind: "value";
88
- readonly aes: "upperWhisker";
88
+ readonly aesthetic: "upperWhisker";
89
89
  }, {
90
90
  readonly axis: "y";
91
91
  readonly role: "scalar";
92
92
  readonly valueKind: "value";
93
- readonly aes: "q1";
93
+ readonly aesthetic: "q1";
94
94
  }, {
95
95
  readonly axis: "y";
96
96
  readonly role: "scalar";
97
97
  readonly valueKind: "value";
98
- readonly aes: "median";
98
+ readonly aesthetic: "median";
99
99
  }, {
100
100
  readonly axis: "y";
101
101
  readonly role: "scalar";
102
102
  readonly valueKind: "value";
103
- readonly aes: "q3";
103
+ readonly aesthetic: "q3";
104
104
  }, {
105
105
  readonly axis: "y";
106
106
  readonly role: "scalar";
107
107
  readonly valueKind: "value";
108
- readonly aes: "outlier";
108
+ readonly aesthetic: "outlier";
109
109
  }, {
110
110
  readonly axis: "y";
111
111
  readonly role: "scalar";
112
112
  readonly valueKind: "value";
113
- readonly aes: "mean";
113
+ readonly aesthetic: "mean";
114
114
  }, {
115
115
  readonly axis: "y";
116
116
  readonly role: "scalar";
117
117
  readonly valueKind: "value";
118
- readonly aes: "notchLower";
118
+ readonly aesthetic: "notchLower";
119
119
  }, {
120
120
  readonly axis: "y";
121
121
  readonly role: "scalar";
122
122
  readonly valueKind: "value";
123
- readonly aes: "notchUpper";
123
+ readonly aesthetic: "notchUpper";
124
124
  }];
125
125
  /** `y` is the raw values the stat summarises; `count` how many values a box summarises, which `varwidth` reads. */
126
126
  readonly aesthetics: readonly [{
@@ -150,14 +150,14 @@ export declare class BoxplotGeom extends Geom<BoxplotGeomParams> {
150
150
  * answers once the cursor is nearer it than the box.
151
151
  */
152
152
  readonly spatialKind = "rects";
153
- readonly highlightStrategy: "observation-rerender";
153
+ readonly highlightStrategy: "observationRerender";
154
154
  /**
155
155
  * A box's tooltip reads its summary top to bottom, and an outlier's its own value, each formatted like the
156
156
  * value axis. The two kinds of observation carry none of each other's fields, so each drops the other's rows.
157
157
  */
158
158
  readonly tooltip: {
159
159
  key: string;
160
- aes: string;
160
+ aesthetic: string;
161
161
  optional: boolean;
162
162
  }[];
163
163
  readonly styleTarget: BoxplotStyleTarget;
@@ -172,7 +172,7 @@ export declare class BoxplotGeom extends Geom<BoxplotGeomParams> {
172
172
  * be overwritten. Under any other stat the author maps the summary, and a box needs its quartiles and median;
173
173
  * a stat that computes `y` would leave nothing to draw a box from.
174
174
  */
175
- readonly validateMapping: ({ mapping, computedVariables, }: GeomMappingValidationInput) => readonly UserInputIssue[];
175
+ readonly validateMapping: ({ mapping, computedAesthetics, }: GeomMappingValidationInput) => readonly UserInputIssue[];
176
176
  /**
177
177
  * Where the editor raises its annotation trigger and where an annotation lands: on a box's median at the
178
178
  * centre of the box, which is where a box is read, or on an outlier itself.
@@ -10,6 +10,6 @@ export declare const BOXPLOT_STAT_AESTHETICS: ReadonlySet<string>;
10
10
  */
11
11
  export declare class BoxplotStat extends Stat {
12
12
  readonly type: "boxplot";
13
- readonly computedVariables: ReadonlySet<string>;
13
+ readonly computedAesthetics: ReadonlySet<string>;
14
14
  protected computeStat({ data, mapping, params }: StatCompilerInput): StatCompileResult;
15
15
  }
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"},B=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"),h=T(e,"notchUpper"),[m,k]=ne(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||h===null?null:{lower:i,upper:h}}},X=(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:B({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=X(n,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 n=(e+t)/2,a=(t-e)*o/2;return[n-a,n+a]},b=1.5,F=e=>e==="min-max"||typeof e=="number"&&Number.isFinite(e)&&e>=0,ae=1.58,se=(e,t)=>{if(e.length===0)return null;const r=[...e].sort((m,k)=>m-k),o=_(r,.25),n=_(r,.5),a=_(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),h=ae*(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-h,notchUpper:n+h}},_=(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)},H={q1:x.q1,median:x.median,q3:x.q3,lowerWhisker:x.lowerWhisker,upperWhisker:x.upperWhisker,count:x.count,outlier:x.outlier},Y=new Set([...Object.keys(H),"mean","notchLower","notchUpper"]),le=class extends s.Stat{constructor(...e){super(...e),this.type="boxplot",this.computedVariables=Y}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:ie({data:e,xVariable:o,yVariable:n,groupVariables:(0,s.getGroupVariables)(e,t).filter(l=>l!==o),extent:F(a)?a:b}),mapping:H}}},ie=({data:e,xVariable:t,yVariable:r,groupVariables:o,extent:n})=>{const a=ue(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:[]},h=[];for(const p of a){const v=se(p.values,n);if(v===null)continue;for(const d of l)c.get(d)?.push(ce(u.get(d)??[],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((d,y)=>{const M=p.values[y];M!==void 0&&(M<w.low||M>w.high)&&h.push({index:d,value:M})})}for(const{index:p,value:v}of h){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)},ue=(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"),h=l.get(i)??{indices:[],values:[]};h.indices.push(c),h.values.push(u),l.set(i,h)}),[...l.values()]},ce=(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)}`,z=.6,de=1,he=2,pe=1.5,me=7,ve=1,ge=7,fe=1.5,ke=["q1","median","q3"],ye={strokeWidth:"pixels"},q={stroke:"color",alpha:"unitInterval",strokeWidth:"pixels",dashArray:"dashArray"},C={fill:"color",fillAlpha:"unitInterval",stroke:"color",alpha:"unitInterval",size:"pixels",strokeWidth:"pixels"},Q=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 le,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={requireMainAxisBands:!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:ye,aesthetics:{fill:"color"},rest:{fill:(0,s.token)("geom"),fillAlpha:1,stroke:(0,s.token)("geomBorder"),strokeWidth:de}},parts:{median:{vocabulary:q,rest:{stroke:(0,s.token)("graphBackground"),alpha:1,strokeWidth:he,dashArray:[]}},whisker:{vocabulary:q,aesthetics:{stroke:"color"},rest:{stroke:(0,s.token)("geom"),alpha:1,strokeWidth:pe,dashArray:[]}},cap:{vocabulary:q,aesthetics:{stroke:"color"},rest:{stroke:(0,s.token)("geom"),alpha:1,strokeWidth:0,dashArray:[]}},outlier:{vocabulary:C,aesthetics:{fill:"color"},rest:{fill:(0,s.token)("geom"),fillAlpha:1,stroke:(0,s.token)("graphBackground"),alpha:1,size:me,strokeWidth:ve}},mean:{vocabulary:C,aesthetics:{stroke:"color"},rest:{fill:(0,s.token)("graphBackground"),fillAlpha:1,stroke:(0,s.token)("geom"),alpha:1,size:ge,strokeWidth:fe}}}}),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")?[...[...Y].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"}]:[]]:ke.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=xe(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),h=a===!1&&i!==null?We(e,i):e,m=P(h,t.count),k=D(r)/2;return{data:h.addVariable(x.relativeWidth,"numeric",c===!0&&m!==null?we(h,m):Me(h)).addVariable(s.POSITION_VARIABLES.yMin,"numeric",R(h,o,i)).addVariable(s.POSITION_VARIABLES.yMax,"numeric",R(h,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&&h.hasVariable(x.mean)&&{mean:x.mean},...l===!0&&t.notchLower===void 0&&h.hasVariable(x.notchLower)&&{notchLower:x.notchLower,notchUpper:x.notchUpper}}}}resolveExtent(e,t){return F(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??z}.`,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}:{}}},xe=(e,t)=>{const r=G(e,t);if(r!==null)return{band:B(r),value:r.median};const o=X(e,t);return o===null?null:{band:o.band,value:o.value}},D=({width:e})=>e??z,we=(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))},Me=e=>Array.from({length:e.size()},()=>null),R=(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)},We=(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},Se=5e-4,Ie=.5,Oe=.5,Z=(e,t)=>{const r=(e.q1+e.q3)/2,o=Math.max(Math.abs(e.q3-e.q1),Se)/2,n=g(t,e.start,r-o),a=g(t,e.end,r+o),[l,u]=K(n.x,a.x),[c,i]=K(n.y,a.y);return{x:l,y:c,width:u-l,height:i-c}},J=(e,t)=>{if(e.notch===null)return null;const{lower:r,upper:o}=e.notch,[n,a]=te(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)]},Pe=(e,t)=>{const[r,o]=e.notch===null?[e.start,e.end]:te(e);return{from:g(t,r,e.median),to:g(t,o,e.median)}},ee=(e,t)=>e.mean===null?null:g(t,B(e),e.mean),te=e=>{const t=(e.end-e.start)*(1-Oe)/2;return[e.start+t,e.end-t]},re=(e,t)=>{const r=B(e),o=(e.end-e.start)*Ie/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)}},K=(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`,be=(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),Be=(e,t)=>{const r=O(e.from,t),o=O(e.to,t);return`M${r.x},${r.y}L${o.x},${o.y}`},Ee=({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(d=>{const y=J(d,n);return y!==null?{kind:"region",pathData:N(y,r)}:{kind:"region",pathData:be(Z(d,n),r)}}),c=o.part("whisker"),i=o.part("cap"),h=a.flatMap(d=>re(d,n).flatMap(y=>[{segment:y.stem,readers:c},{segment:y.cap,readers:i}].flatMap(({segment:M,readers:E})=>{const I=E?.get("strokeWidth",d.observation)??0;return I>0?[{kind:"stroke",pathData:Be(M,r),width:I}]:[]}))),m=o.part("outlier"),k=m===void 0?[]:l.map(d=>({center:O(g(n,d.band,d.value),r),size:j(m,d.observation)})),p=o.part("mean"),v=p===void 0?[]:a.flatMap(d=>{const y=ee(d,n);return y===null?[]:[{center:O(y,r),size:j(p,d.observation)}]}),w=(0,s.buildDotsEditOutlineShapes)([...k,...v]);return[...h,...u,...w]},j=(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}`})}}},Te=(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)))},oe=(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,animateTransitions:c=!1})=>{const i=u===void 0?void 0:"hovered",h=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(d=>re(d,r).flatMap(y=>[{piece:"stem",testId:"boxplot-whisker",readers:h},{piece:"cap",testId:"boxplot-cap",readers:m}].map(({piece:M,testId:E,readers:I})=>I===void 0?null:(0,f.jsx)(U,{testId:E,segment:y[M],observation:d.observation,readers:I,state:i,animateTransitions:c},`${l.readBoxKey(d.observation)}-${y.side}-${M}`)))),(0,f.jsx)(s.GeomOverpaint,{styleReaders:a,state:i,children:e.map(d=>(0,f.jsx)(_e,{box:d,mainAxis:r,panelSize:o,source:n,styleReaders:a,state:i,animateTransitions:c},l.readBoxKey(d.observation)))}),k&&e.map(d=>(0,f.jsx)(U,{testId:"boxplot-median",segment:Pe(d,r),observation:d.observation,readers:k,state:i,animateTransitions:c},l.readBoxKey(d.observation))),p&&e.map(d=>{const y=ee(d,r);return y===null?null:(0,f.jsx)($,{testId:"boxplot-mean",centre:y,observation:d.observation,readers:p,state:i,animateTransitions:c},l.readBoxKey(d.observation))}),v&&t.map((d,y)=>(0,f.jsx)($,{testId:"boxplot-outlier",centre:g(r,d.band,d.value),observation:d.observation,readers:v,state:i,animateTransitions:c},w[y]))]})},U=({testId:e,segment:t,observation:r,readers:o,state:n,animateTransitions: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),animateTransitions:a})},_e=({box:e,mainAxis:t,panelSize:r,source:o,styleReaders:n,state:a,animateTransitions: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),animateTransitions:l},h=J(e,t);if(h!==null)return(0,f.jsx)(s.GeomPath,{"data-testid":"boxplot-box",d:N(h,r),source:o,...i});const m=Z(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),cornerRadius:n.get("cornerRadius",e.observation,a),...i})},$=({testId:e,centre:t,observation:r,readers:o,state:n,animateTransitions: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),animateTransitions:a})},qe=({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),h=new Set(Te(t,l,i.outliers)),m=new Set(oe(l,i.boxes,a.readBoxKey)),k=l.filter(p=>m.has(p)||h.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})},Ve=({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?oe(a,u,l.readBoxKey):c,mainAxis:r,panelSize:o,source:e,styleReaders:n,keys:l,hovered:t.observation})})},Ne=({layer:e,mainAxis:t,panelSize:r,styleReaders:o,animateTransitions: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]),animateTransitions:n});return a==null?c:(0,f.jsx)(s.IntroFade,{intro:{type:"fade",layerId:a.layerId,durationSeconds:a.durationSeconds},children:c})},Ae=(0,s.defineGeomRenderer)(new Q,{coord:"cartesian",swatchShape:"square",overpaintPlacement:"manual",guideMode:"band",render:({layer:e,coordSystem:t,panelRect:r,styleReaders:o,animateTransitions:n,intro:a})=>(0,f.jsx)(Ne,{layer:e,mainAxis:(0,s.readMainAxis)(t),panelSize:r,styleReaders:o,animateTransitions:n,intro:a}),renderHighlight:({layer:e,sourceLayer:t,coordSystem:r,panelRect:o,styleReaders:n})=>(0,f.jsx)(qe,{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)(Ve,{layer:e,primary:t,mainAxis:(0,s.readMainAxis)(r),panelSize:o,styleReaders:n}),renderHoverCompanions:()=>null,getEditOutlineShapes:Ee});exports.BOXPLOT_VARIABLES=x;exports.BoxplotGeom=Q;exports.boxplot=Ae;
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"},B=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),i=(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||i===null||c===null)return null;const l=T(e,"notchLower"),d=T(e,"notchUpper"),[m,k]=ne(r,o,e);return{observation:e,start:m,end:k,lowerWhisker:n,q1:i,median:u,q3:c,upperWhisker:a,mean:T(e,"mean"),notch:l===null||d===null?null:{lower:l,upper:d}}},X=(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:B({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 i=X(n,t);i!==null&&o.push(i)}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 n=(e+t)/2,a=(t-e)*o/2;return[n-a,n+a]},b=1.5,F=e=>e==="min-max"||typeof e=="number"&&Number.isFinite(e)&&e>=0,ae=1.58,se=(e,t)=>{if(e.length===0)return null;const r=[...e].sort((m,k)=>m-k),o=_(r,.25),n=_(r,.5),a=_(r,.75),i=t==="min-max"?1/0:t*(a-o),u=o-i,c=a+i,l=r.filter(m=>m>=u&&m<=c),d=ae*(a-o)/Math.sqrt(e.length);return{lowerWhisker:Math.min(l[0]??o,o),q1:o,median:n,q3:a,upperWhisker:Math.max(l[l.length-1]??a,a),count:e.length,mean:r.reduce((m,k)=>m+k,0)/r.length,notchLower:n-d,notchUpper:n+d}},_=(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)},H={q1:x.q1,median:x.median,q3:x.q3,lowerWhisker:x.lowerWhisker,upperWhisker:x.upperWhisker,count:x.count,outlier:x.outlier},Y=new Set([...Object.keys(H),"mean","notchLower","notchUpper"]),ie=class extends s.Stat{constructor(...e){super(...e),this.type="boxplot",this.computedAesthetics=Y}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 i=e.getType(n);throw new s.UserInputError({code:"INCOMPATIBLE_TYPE",message:`'boxplot' stat requires a numeric 'y' (got '${i}' for variable '${n}')`,context:{aesthetic:"y",variableName:n,variableType:i},suggestion:"Map the raw values to 'y', or summarised columns under stat 'identity'."})}const a=r?.extent;return{data:le({data:e,xVariable:o,yVariable:n,groupVariables:(0,s.getGroupVariables)(e,t).filter(i=>i!==o),extent:F(a)?a:b}),mapping:H}}},le=({data:e,xVariable:t,yVariable:r,groupVariables:o,extent:n})=>{const a=ue(e,t,r,o),i=e.getVariableNames(),u=new Map(i.map(p=>[p,e.getValues(p)])),c=new Map(i.map(p=>[p,[]])),l={q1:[],median:[],q3:[],lowerWhisker:[],upperWhisker:[],count:[],mean:[],notchLower:[],notchUpper:[],outlier:[]},d=[];for(const p of a){const v=se(p.values,n);if(v===null)continue;for(const h of i)c.get(h)?.push(ce(u.get(h)??[],p.indices));l.q1.push(v.q1),l.median.push(v.median),l.q3.push(v.q3),l.lowerWhisker.push(v.lowerWhisker),l.upperWhisker.push(v.upperWhisker),l.count.push(v.count),l.mean.push(v.mean),l.notchLower.push(v.notchLower),l.notchUpper.push(v.notchUpper),l.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 i)c.get(w)?.push(u.get(w)?.[p]??null);l.q1.push(null),l.median.push(null),l.q3.push(null),l.lowerWhisker.push(null),l.upperWhisker.push(null),l.count.push(null),l.mean.push(null),l.notchLower.push(null),l.notchUpper.push(null),l.outlier.push(v)}const m=e.getValueFormat(r),k=Object.fromEntries(i.map(p=>[p,{type:e.getType(p),values:c.get(p)??[],valueFormat:e.getValueFormat(p)}]));for(const[p,v]of Object.entries(l)){const w=x[p];k[w]=p==="count"?{type:"numeric",values:v}:{type:"numeric",values:v,valueFormat:m}}return new s.Dataset(k)},ue=(e,t,r,o)=>{const n=[t,...o].map(u=>e.getValues(u)),a=e.getValues(r,{type:"numeric"}),i=new Map;return a.forEach((u,c)=>{if(u===null||!Number.isFinite(u))return;const l=n.map(m=>V(m[c]??null)).join("\0"),d=i.get(l)??{indices:[],values:[]};d.indices.push(c),d.values.push(u),i.set(l,d)}),[...i.values()]},ce=(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)}`,z=.6,he=1,de=2,pe=1.5,me=7,ve=1,ge=7,fe=1.5,ke=["q1","median","q3"],ye={strokeWidth:"pixels"},q={stroke:"color",alpha:"unitInterval",strokeWidth:"pixels",dashArray:"dashArray"},C={fill:"color",fillAlpha:"unitInterval",stroke:"color",alpha:"unitInterval",size:"pixels",strokeWidth:"pixels"},Q=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",aesthetic:"lowerWhisker"},{axis:"y",role:"max",valueKind:"value",aesthetic:"upperWhisker"},{axis:"y",role:"scalar",valueKind:"value",aesthetic:"q1"},{axis:"y",role:"scalar",valueKind:"value",aesthetic:"median"},{axis:"y",role:"scalar",valueKind:"value",aesthetic:"q3"},{axis:"y",role:"scalar",valueKind:"value",aesthetic:"outlier"},{axis:"y",role:"scalar",valueKind:"value",aesthetic:"mean"},{axis:"y",role:"scalar",valueKind:"value",aesthetic:"notchLower"},{axis:"y",role:"scalar",valueKind:"value",aesthetic:"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={requireMainAxisBands:!0},this.legend={suppressWhenSingleItem:!0},this.spatialKind="rects",this.highlightStrategy="observationRerender",this.tooltip=[{key:"Upper whisker",aesthetic:"upperWhisker",optional:!0},{key:"Q3",aesthetic:"q3",optional:!0},{key:"Median",aesthetic:"median",optional:!0},{key:"Q1",aesthetic:"q1",optional:!0},{key:"Lower whisker",aesthetic:"lowerWhisker",optional:!0},{key:"Count",aesthetic:"count",optional:!0},{key:"Mean",aesthetic:"mean",optional:!0},{key:"Outlier",aesthetic:"outlier",optional:!0}],this.styleTarget=(0,s.defineGeomStyleTarget)({observation:{vocabulary:ye,aesthetics:{fill:"color"},rest:{fill:(0,s.token)("geom"),fillAlpha:1,stroke:(0,s.token)("geomBorder"),strokeWidth:he}},parts:{median:{vocabulary:q,rest:{stroke:(0,s.token)("graphBackground"),alpha:1,strokeWidth:de,dashArray:[]}},whisker:{vocabulary:q,aesthetics:{stroke:"color"},rest:{stroke:(0,s.token)("geom"),alpha:1,strokeWidth:pe,dashArray:[]}},cap:{vocabulary:q,aesthetics:{stroke:"color"},rest:{stroke:(0,s.token)("geom"),alpha:1,strokeWidth:0,dashArray:[]}},outlier:{vocabulary:C,aesthetics:{fill:"color"},rest:{fill:(0,s.token)("geom"),fillAlpha:1,stroke:(0,s.token)("graphBackground"),alpha:1,size:me,strokeWidth:ve}},mean:{vocabulary:C,aesthetics:{stroke:"color"},rest:{fill:(0,s.token)("graphBackground"),fillAlpha:1,stroke:(0,s.token)("geom"),alpha:1,size:ge,strokeWidth:fe}}}}),this.resolveBandFraction=t=>R(t),this.resolveValueSource=t=>t.median??{variable:x.median},this.validateMapping=({mapping:t,computedAesthetics: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")?[...[...Y].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"}]:[]]:ke.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=xe(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:i,showMean:u,varwidth:c}=r,l=P(e,t.outlier),d=a===!1&&l!==null?We(e,l):e,m=P(d,t.count),k=R(r)/2;return{data:d.addVariable(x.relativeWidth,"numeric",c===!0&&m!==null?we(d,m):Me(d)).addVariable(s.POSITION_VARIABLES.yMin,"numeric",D(d,o,l)).addVariable(s.POSITION_VARIABLES.yMax,"numeric",D(d,n,l)).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},...i===!0&&t.notchLower===void 0&&d.hasVariable(x.notchLower)&&{notchLower:x.notchLower,notchUpper:x.notchUpper}}}}resolveExtent(e,t){return F(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??z}.`,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}:{}}},xe=(e,t)=>{const r=G(e,t);if(r!==null)return{band:B(r),value:r.median};const o=X(e,t);return o===null?null:{band:o.band,value:o.value}},R=({width:e})=>e??z,we=(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))},Me=e=>Array.from({length:e.size()},()=>null),D=(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)},We=(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},Se=5e-4,Ie=.5,Oe=.5,Z=(e,t)=>{const r=(e.q1+e.q3)/2,o=Math.max(Math.abs(e.q3-e.q1),Se)/2,n=g(t,e.start,r-o),a=g(t,e.end,r+o),[i,u]=K(n.x,a.x),[c,l]=K(n.y,a.y);return{x:i,y:c,width:u-i,height:l-c}},J=(e,t)=>{if(e.notch===null)return null;const{lower:r,upper:o}=e.notch,[n,a]=te(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)]},Pe=(e,t)=>{const[r,o]=e.notch===null?[e.start,e.end]:te(e);return{from:g(t,r,e.median),to:g(t,o,e.median)}},ee=(e,t)=>e.mean===null?null:g(t,B(e),e.mean),te=e=>{const t=(e.end-e.start)*(1-Oe)/2;return[e.start+t,e.end-t]},re=(e,t)=>{const r=B(e),o=(e.end-e.start)*Ie/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:i})=>({side:n,stem:{from:g(t,r,a),to:g(t,r,i)},cap:{from:g(t,r-o,i),to:g(t,r+o,i)}}))},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)}},K=(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`,be=(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),Be=(e,t)=>{const r=O(e.from,t),o=O(e.to,t);return`M${r.x},${r.y}L${o.x},${o.y}`},Ee=({layer:e,coordSystem:t,panelRect:r,styleReaders:o})=>{const n=(0,s.readMainAxis)(t),{boxes:a,outliers:i}=S(e.data,n),u=a.map(h=>{const y=J(h,n);return y!==null?{kind:"region",pathData:N(y,r)}:{kind:"region",pathData:be(Z(h,n),r)}}),c=o.part("whisker"),l=o.part("cap"),d=a.flatMap(h=>re(h,n).flatMap(y=>[{segment:y.stem,readers:c},{segment:y.cap,readers:l}].flatMap(({segment:M,readers:E})=>{const I=E?.get("strokeWidth",h.observation)??0;return I>0?[{kind:"stroke",pathData:Be(M,r),width:I}]:[]}))),m=o.part("outlier"),k=m===void 0?[]:i.map(h=>({center:O(g(n,h.band,h.value),r),size:j(m,h.observation)})),p=o.part("mean"),v=p===void 0?[]:a.flatMap(h=>{const y=ee(h,n);return y===null?[]:[{center:O(y,r),size:j(p,h.observation)}]}),w=(0,s.buildDotsEditOutlineShapes)([...k,...v]);return[...d,...u,...w]},j=(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(i=>{const u=o(i),c=a.get(u)??0;return a.set(u,c+1),c===0?u:`${u}-${c}`})}}},Te=(e,t,r)=>{const o=e.data.getVariableNames(),n=i=>JSON.stringify(o.map(u=>i.observation[u]??null)),a=new Set(r.map(n));return t.filter(i=>a.has(n(i)))},oe=(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:i,hovered:u,animateTransitions:c=!1})=>{const l=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=i.readOutlierKeys(t);return(0,f.jsxs)(f.Fragment,{children:[e.flatMap(h=>re(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)(U,{testId:E,segment:y[M],observation:h.observation,readers:I,state:l,animateTransitions:c},`${i.readBoxKey(h.observation)}-${y.side}-${M}`)))),(0,f.jsx)(s.GeomOverpaint,{styleReaders:a,state:l,children:e.map(h=>(0,f.jsx)(_e,{box:h,mainAxis:r,panelSize:o,source:n,styleReaders:a,state:l,animateTransitions:c},i.readBoxKey(h.observation)))}),k&&e.map(h=>(0,f.jsx)(U,{testId:"boxplot-median",segment:Pe(h,r),observation:h.observation,readers:k,state:l,animateTransitions:c},i.readBoxKey(h.observation))),p&&e.map(h=>{const y=ee(h,r);return y===null?null:(0,f.jsx)($,{testId:"boxplot-mean",centre:y,observation:h.observation,readers:p,state:l,animateTransitions:c},i.readBoxKey(h.observation))}),v&&t.map((h,y)=>(0,f.jsx)($,{testId:"boxplot-outlier",centre:g(r,h.band,h.value),observation:h.observation,readers:v,state:l,animateTransitions:c},w[y]))]})},U=({testId:e,segment:t,observation:r,readers:o,state:n,animateTransitions:a})=>{const i=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:i,strokeDasharray:(0,s.toStrokeDasharray)(o.get("dashArray",r,n),i),animateTransitions:a})},_e=({box:e,mainAxis:t,panelSize:r,source:o,styleReaders:n,state:a,animateTransitions:i})=>{const u=n.get("fill",e.observation,a),c=n.get("stroke",e.observation,a),l={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),animateTransitions:i},d=J(e,t);if(d!==null)return(0,f.jsx)(s.GeomPath,{"data-testid":"boxplot-box",d:N(d,r),source:o,...l});const m=Z(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),cornerRadius:n.get("cornerRadius",e.observation,a),...l})},$=({testId:e,centre:t,observation:r,readers:o,state:n,animateTransitions:a})=>{const i=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:i===void 0?void 0:(0,s.toPaintColor)(i),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),animateTransitions:a})},qe=({layer:e,sourceLayer:t,mainAxis:r,panelSize:o,styleReaders:n})=>{const a=(0,W.useMemo)(()=>A(t),[t]),i=(0,W.useMemo)(()=>S(t.data,r).outliers,[t.data,r]),{boxes:u,outliers:c}=(0,W.useMemo)(()=>{const l=S(e.data,r),d=new Set(Te(t,i,l.outliers)),m=new Set(oe(i,l.boxes,a.readBoxKey)),k=i.filter(p=>m.has(p)||d.has(p));return{boxes:l.boxes,outliers:k}},[e.data,r,t,i,a]);return(0,f.jsx)(L,{boxes:u,outliers:c,mainAxis:r,panelSize:o,source:t,styleReaders:n,keys:a})},Ve=({layer:e,primary:t,mainAxis:r,panelSize:o,styleReaders:n})=>{const a=(0,W.useMemo)(()=>S(e.data,r).outliers,[e.data,r]),i=(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?oe(a,u,i.readBoxKey):c,mainAxis:r,panelSize:o,source:e,styleReaders:n,keys:i,hovered:t.observation})})},Ne=({layer:e,mainAxis:t,panelSize:r,styleReaders:o,animateTransitions:n,intro:a})=>{const{boxes:i,outliers:u}=(0,W.useMemo)(()=>S(e.data,t),[e.data,t]),c=(0,f.jsx)(L,{boxes:i,outliers:u,mainAxis:t,panelSize:r,source:e,styleReaders:o,keys:(0,W.useMemo)(()=>A(e),[e]),animateTransitions:n});return a==null?c:(0,f.jsx)(s.IntroFade,{intro:{type:"fade",layerId:a.layerId,durationSeconds:a.durationSeconds},children:c})},Ae=(0,s.defineGeomRenderer)(new Q,{coord:"cartesian",swatchShape:"square",overpaintPlacement:"manual",guideMode:"band",render:({layer:e,coordSystem:t,panelRect:r,styleReaders:o,animateTransitions:n,intro:a})=>(0,f.jsx)(Ne,{layer:e,mainAxis:(0,s.readMainAxis)(t),panelSize:r,styleReaders:o,animateTransitions:n,intro:a}),renderHighlight:({layer:e,sourceLayer:t,coordSystem:r,panelRect:o,styleReaders:n})=>(0,f.jsx)(qe,{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)(Ve,{layer:e,primary:t,mainAxis:(0,s.readMainAxis)(r),panelSize:o,styleReaders:n}),renderHoverCompanions:()=>null,getEditOutlineShapes:Ee});exports.BOXPLOT_VARIABLES=x;exports.BoxplotGeom=Q;exports.boxplot=Ae;
package/dist/index.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import { Dataset as pe, Geom as me, GeomCircle as ve, GeomLine as ge, GeomOverpaint as fe, GeomPath as ke, GeomRect as ye, IntroFade as we, POSITION_VARIABLES as _, 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 R, getYMax as U, getYMin as $, readGeomEffects as Be, readMainAxis as E, readVariableName as V, toPaintColor as B, toPercent as x, toStrokeDasharray as Te, toViewBoxX as _e, toViewBoxY as qe, token as M } from "@graphysdk/react";
1
+ import { Dataset as pe, Geom as me, GeomCircle as ve, GeomLine as ge, GeomOverpaint as fe, GeomPath as ke, GeomRect as ye, IntroFade as we, POSITION_VARIABLES as _, 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 A, getXMin as K, getYMax as U, getYMin as $, readGeomEffects as Be, readMainAxis as E, readVariableName as V, toPaintColor as B, toPercent as x, toStrokeDasharray as Te, toViewBoxX as _e, toViewBoxY as qe, token as M } from "@graphysdk/react";
2
2
  import { useMemo as S } from "react";
3
3
  import { Fragment as Pe, jsx as k, jsxs as Ve } from "react/jsx-runtime";
4
4
  var y = {
@@ -14,7 +14,7 @@ var y = {
14
14
  relativeWidth: "boxplotRelativeWidth",
15
15
  outlier: "boxplotOutlier"
16
16
  }, N = (e) => (e.start + e.end) / 2, te = (e, t) => {
17
- const r = t === "x" ? R(e) : $(e), o = t === "x" ? K(e) : U(e), n = t === "x" ? $(e) : R(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" ? K(e) : $(e), o = t === "x" ? A(e) : U(e), n = t === "x" ? $(e) : K(e), a = t === "x" ? U(e) : A(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
19
  const l = L(e, "notchLower"), h = L(e, "notchUpper"), [p, g] = Ne(r, o, e);
20
20
  return {
@@ -33,7 +33,7 @@ var y = {
33
33
  }
34
34
  };
35
35
  }, re = (e, t) => {
36
- const r = t === "x" ? R(e) : $(e), o = t === "x" ? K(e) : U(e), n = b(e, "outlier");
36
+ const r = t === "x" ? K(e) : $(e), o = t === "x" ? A(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({
@@ -94,7 +94,7 @@ var y = {
94
94
  "notchUpper"
95
95
  ]), De = class extends xe {
96
96
  constructor(...e) {
97
- super(...e), this.type = "boxplot", this.computedVariables = ae;
97
+ super(...e), this.type = "boxplot", this.computedAesthetics = ae;
98
98
  }
99
99
  computeStat({ data: e, mapping: t, params: r }) {
100
100
  const o = V(t.x), n = V(t.y);
@@ -117,7 +117,7 @@ var y = {
117
117
  }
118
118
  const a = r?.extent;
119
119
  return {
120
- data: Ae({
120
+ data: Re({
121
121
  data: e,
122
122
  xVariable: o,
123
123
  yVariable: n,
@@ -127,8 +127,8 @@ var y = {
127
127
  mapping: ne
128
128
  };
129
129
  }
130
- }, Ae = ({ data: e, xVariable: t, yVariable: r, groupVariables: o, extent: n }) => {
131
- const a = Ke(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: [],
@@ -143,7 +143,7 @@ var y = {
143
143
  for (const d of a) {
144
144
  const m = Le(d.values, n);
145
145
  if (m === null) continue;
146
- for (const c of s) u.get(c)?.push(Re(i.get(c) ?? [], d.indices));
146
+ for (const c of s) u.get(c)?.push(Ke(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,
@@ -178,7 +178,7 @@ var y = {
178
178
  };
179
179
  }
180
180
  return new pe(g);
181
- }, Ke = (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;
@@ -188,14 +188,14 @@ var y = {
188
188
  };
189
189
  h.indices.push(u), h.values.push(i), s.set(l, h);
190
190
  }), [...s.values()];
191
- }, Re = (e, t) => {
191
+ }, Ke = (e, t) => {
192
192
  const r = e[t[0] ?? -1] ?? null, o = G(r);
193
193
  return t.every((n) => G(e[n] ?? null) === o) ? r : null;
194
194
  }, G = (e) => e === null ? "\0null" : e instanceof Date ? `date:${e.getTime()}` : `${typeof e}:${String(e)}`, se = 0.6, Ue = 1, $e = 2, Ge = 1.5, Fe = 7, He = 1, Xe = 7, ze = 1.5, Ye = [
195
195
  "q1",
196
196
  "median",
197
197
  "q3"
198
- ], je = { strokeWidth: "pixels" }, A = {
198
+ ], je = { strokeWidth: "pixels" }, R = {
199
199
  stroke: "color",
200
200
  alpha: "unitInterval",
201
201
  strokeWidth: "pixels",
@@ -225,55 +225,55 @@ var y = {
225
225
  axis: "y",
226
226
  role: "min",
227
227
  valueKind: "value",
228
- aes: "lowerWhisker"
228
+ aesthetic: "lowerWhisker"
229
229
  },
230
230
  {
231
231
  axis: "y",
232
232
  role: "max",
233
233
  valueKind: "value",
234
- aes: "upperWhisker"
234
+ aesthetic: "upperWhisker"
235
235
  },
236
236
  {
237
237
  axis: "y",
238
238
  role: "scalar",
239
239
  valueKind: "value",
240
- aes: "q1"
240
+ aesthetic: "q1"
241
241
  },
242
242
  {
243
243
  axis: "y",
244
244
  role: "scalar",
245
245
  valueKind: "value",
246
- aes: "median"
246
+ aesthetic: "median"
247
247
  },
248
248
  {
249
249
  axis: "y",
250
250
  role: "scalar",
251
251
  valueKind: "value",
252
- aes: "q3"
252
+ aesthetic: "q3"
253
253
  },
254
254
  {
255
255
  axis: "y",
256
256
  role: "scalar",
257
257
  valueKind: "value",
258
- aes: "outlier"
258
+ aesthetic: "outlier"
259
259
  },
260
260
  {
261
261
  axis: "y",
262
262
  role: "scalar",
263
263
  valueKind: "value",
264
- aes: "mean"
264
+ aesthetic: "mean"
265
265
  },
266
266
  {
267
267
  axis: "y",
268
268
  role: "scalar",
269
269
  valueKind: "value",
270
- aes: "notchLower"
270
+ aesthetic: "notchLower"
271
271
  },
272
272
  {
273
273
  axis: "y",
274
274
  role: "scalar",
275
275
  valueKind: "value",
276
- aes: "notchUpper"
276
+ aesthetic: "notchUpper"
277
277
  }
278
278
  ], this.aesthetics = [
279
279
  {
@@ -288,45 +288,45 @@ var y = {
288
288
  kind: "data",
289
289
  name: "count"
290
290
  }
291
- ], this.defaultPosition = "dodge", this.supportedPositions = ["identity", "dodge"], this.supportedCoordTypes = ["cartesian", "flip"], this.scaleConstraints = { requireMainAxisBands: !0 }, this.legend = { suppressWhenSingleItem: !0 }, this.spatialKind = "rects", this.highlightStrategy = "observation-rerender", this.tooltip = [
291
+ ], this.defaultPosition = "dodge", this.supportedPositions = ["identity", "dodge"], this.supportedCoordTypes = ["cartesian", "flip"], this.scaleConstraints = { requireMainAxisBands: !0 }, this.legend = { suppressWhenSingleItem: !0 }, this.spatialKind = "rects", this.highlightStrategy = "observationRerender", this.tooltip = [
292
292
  {
293
293
  key: "Upper whisker",
294
- aes: "upperWhisker",
294
+ aesthetic: "upperWhisker",
295
295
  optional: !0
296
296
  },
297
297
  {
298
298
  key: "Q3",
299
- aes: "q3",
299
+ aesthetic: "q3",
300
300
  optional: !0
301
301
  },
302
302
  {
303
303
  key: "Median",
304
- aes: "median",
304
+ aesthetic: "median",
305
305
  optional: !0
306
306
  },
307
307
  {
308
308
  key: "Q1",
309
- aes: "q1",
309
+ aesthetic: "q1",
310
310
  optional: !0
311
311
  },
312
312
  {
313
313
  key: "Lower whisker",
314
- aes: "lowerWhisker",
314
+ aesthetic: "lowerWhisker",
315
315
  optional: !0
316
316
  },
317
317
  {
318
318
  key: "Count",
319
- aes: "count",
319
+ aesthetic: "count",
320
320
  optional: !0
321
321
  },
322
322
  {
323
323
  key: "Mean",
324
- aes: "mean",
324
+ aesthetic: "mean",
325
325
  optional: !0
326
326
  },
327
327
  {
328
328
  key: "Outlier",
329
- aes: "outlier",
329
+ aesthetic: "outlier",
330
330
  optional: !0
331
331
  }
332
332
  ], this.styleTarget = be({
@@ -342,7 +342,7 @@ var y = {
342
342
  },
343
343
  parts: {
344
344
  median: {
345
- vocabulary: A,
345
+ vocabulary: R,
346
346
  rest: {
347
347
  stroke: M("graphBackground"),
348
348
  alpha: 1,
@@ -351,7 +351,7 @@ var y = {
351
351
  }
352
352
  },
353
353
  whisker: {
354
- vocabulary: A,
354
+ vocabulary: R,
355
355
  aesthetics: { stroke: "color" },
356
356
  rest: {
357
357
  stroke: M("geom"),
@@ -361,7 +361,7 @@ var y = {
361
361
  }
362
362
  },
363
363
  cap: {
364
- vocabulary: A,
364
+ vocabulary: R,
365
365
  aesthetics: { stroke: "color" },
366
366
  rest: {
367
367
  stroke: M("geom"),
@@ -395,7 +395,7 @@ var y = {
395
395
  }
396
396
  }
397
397
  }
398
- }), this.resolveBandFraction = (t) => Y(t), this.resolveValueSource = (t) => t.median ?? { variable: y.median }, this.validateMapping = ({ mapping: t, computedVariables: r }) => r.has("y") ? [{
398
+ }), this.resolveBandFraction = (t) => Y(t), this.resolveValueSource = (t) => t.median ?? { variable: y.median }, this.validateMapping = ({ mapping: t, computedAesthetics: r }) => r.has("y") ? [{
399
399
  code: "CONFLICTING_STAT_MAPPING",
400
400
  message: "boxplot geom draws a summary, not a single 'y'; use the 'boxplot' stat or 'identity'"
401
401
  }] : r.has("median") ? [...[...ae].filter((o) => t[o] !== void 0).map((o) => ({
package/package.json CHANGED
@@ -13,7 +13,7 @@
13
13
  "boxplot",
14
14
  "box-plot"
15
15
  ],
16
- "version": "0.1.1-beta.1793017273153",
16
+ "version": "0.1.1-beta.1793027249956",
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.1793017273153"
35
+ "@graphysdk/react": "^1.9.0-beta.1793027249956"
36
36
  },
37
37
  "devDependencies": {
38
38
  "@testing-library/react": "^16.3.1",
@@ -44,8 +44,8 @@
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.1793017273153",
48
- "@graphysdk/viz-engine": "1.9.0-beta.1793017273153",
47
+ "@graphysdk/react": "1.9.0-beta.1793027249956",
48
+ "@graphysdk/viz-engine": "1.9.0-beta.1793027249956",
49
49
  "@graphytools/eslint-config": "0.0.1",
50
50
  "@graphytools/typescript-config": "0.0.1",
51
51
  "@graphytools/vitest-config": "1.0.0"