minerva-design 1.13.2 → 1.14.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +10 -0
- package/custom-elements.json +194 -12
- package/dist/angular/fesm2022/minerva-angular.mjs +20 -0
- package/dist/angular/fesm2022/minerva-angular.mjs.map +1 -1
- package/dist/angular/types/minerva-angular.d.ts +10 -1
- package/dist/charts/canvas.d.cts +1 -0
- package/dist/charts/canvas.d.ts +1 -0
- package/dist/charts/engine/canvas.cjs +1 -1
- package/dist/charts/engine/canvas.cjs.map +1 -1
- package/dist/charts/engine/canvas.js +152 -128
- package/dist/charts/engine/canvas.js.map +1 -1
- package/dist/charts/engine/chart.cjs +1 -1
- package/dist/charts/engine/chart.cjs.map +1 -1
- package/dist/charts/engine/chart.js +475 -405
- package/dist/charts/engine/chart.js.map +1 -1
- package/dist/charts/engine/controller.cjs +2 -2
- package/dist/charts/engine/controller.cjs.map +1 -1
- package/dist/charts/engine/controller.js +263 -248
- package/dist/charts/engine/controller.js.map +1 -1
- package/dist/charts/engine/dom.cjs +1 -1
- package/dist/charts/engine/dom.cjs.map +1 -1
- package/dist/charts/engine/dom.d.cts +34 -0
- package/dist/charts/engine/dom.d.ts +34 -0
- package/dist/charts/engine/dom.js +62 -51
- package/dist/charts/engine/dom.js.map +1 -1
- package/dist/charts/engine/graph/chart.cjs +1 -1
- package/dist/charts/engine/graph/chart.cjs.map +1 -1
- package/dist/charts/engine/graph/chart.js +835 -821
- package/dist/charts/engine/graph/chart.js.map +1 -1
- package/dist/charts/engine/graph/dom.cjs +1 -1
- package/dist/charts/engine/graph/dom.cjs.map +1 -1
- package/dist/charts/engine/graph/dom.js +44 -51
- package/dist/charts/engine/graph/dom.js.map +1 -1
- package/dist/charts/engine/hover-rect.cjs +2 -0
- package/dist/charts/engine/hover-rect.cjs.map +1 -0
- package/dist/charts/engine/hover-rect.d.cts +10 -0
- package/dist/charts/engine/hover-rect.d.ts +10 -0
- package/dist/charts/engine/hover-rect.js +47 -0
- package/dist/charts/engine/hover-rect.js.map +1 -0
- package/dist/charts/engine/items.cjs +1 -1
- package/dist/charts/engine/items.cjs.map +1 -1
- package/dist/charts/engine/items.js +8 -8
- package/dist/charts/engine/model.cjs +1 -1
- package/dist/charts/engine/model.cjs.map +1 -1
- package/dist/charts/engine/model.js +191 -191
- package/dist/charts/engine/model.js.map +1 -1
- package/dist/charts/engine/orderbook/depth.cjs +1 -1
- package/dist/charts/engine/orderbook/depth.cjs.map +1 -1
- package/dist/charts/engine/orderbook/depth.js +63 -63
- package/dist/charts/engine/orderbook/heatmap.cjs +1 -1
- package/dist/charts/engine/orderbook/heatmap.cjs.map +1 -1
- package/dist/charts/engine/orderbook/heatmap.js +155 -155
- package/dist/charts/engine/orderbook/host.cjs +1 -1
- package/dist/charts/engine/orderbook/host.cjs.map +1 -1
- package/dist/charts/engine/orderbook/host.js +87 -75
- package/dist/charts/engine/orderbook/host.js.map +1 -1
- package/dist/charts/engine/orderbook/ladder.cjs +1 -1
- package/dist/charts/engine/orderbook/ladder.cjs.map +1 -1
- package/dist/charts/engine/orderbook/ladder.js +41 -41
- package/dist/charts/engine/orderbook/tape.cjs +1 -1
- package/dist/charts/engine/orderbook/tape.cjs.map +1 -1
- package/dist/charts/engine/orderbook/tape.js +31 -31
- package/dist/charts/engine/presets.cjs +2 -2
- package/dist/charts/engine/presets.cjs.map +1 -1
- package/dist/charts/engine/presets.js +15 -15
- package/dist/charts/engine/renderers/vector-gpu.cjs +24 -0
- package/dist/charts/engine/renderers/vector-gpu.cjs.map +1 -0
- package/dist/charts/engine/renderers/vector-gpu.d.cts +22 -0
- package/dist/charts/engine/renderers/vector-gpu.d.ts +22 -0
- package/dist/charts/engine/renderers/vector-gpu.js +301 -0
- package/dist/charts/engine/renderers/vector-gpu.js.map +1 -0
- package/dist/charts/engine/renderers/vector.cjs +2 -0
- package/dist/charts/engine/renderers/vector.cjs.map +1 -0
- package/dist/charts/engine/renderers/vector.d.cts +140 -0
- package/dist/charts/engine/renderers/vector.d.ts +140 -0
- package/dist/charts/engine/renderers/vector.js +568 -0
- package/dist/charts/engine/renderers/vector.js.map +1 -0
- package/dist/charts/engine/scene.cjs +1 -1
- package/dist/charts/engine/scene.cjs.map +1 -1
- package/dist/charts/engine/scene.d.cts +43 -3
- package/dist/charts/engine/scene.d.ts +43 -3
- package/dist/charts/engine/scene.js +411 -338
- package/dist/charts/engine/scene.js.map +1 -1
- package/dist/charts/engine/shapes/bullet.cjs +1 -1
- package/dist/charts/engine/shapes/bullet.cjs.map +1 -1
- package/dist/charts/engine/shapes/bullet.js +36 -36
- package/dist/charts/engine/shapes/calendar.cjs +1 -1
- package/dist/charts/engine/shapes/calendar.cjs.map +1 -1
- package/dist/charts/engine/shapes/calendar.js +53 -53
- package/dist/charts/engine/shapes/chart.cjs +1 -1
- package/dist/charts/engine/shapes/chart.cjs.map +1 -1
- package/dist/charts/engine/shapes/chart.d.cts +3 -2
- package/dist/charts/engine/shapes/chart.d.ts +3 -2
- package/dist/charts/engine/shapes/chart.js +192 -162
- package/dist/charts/engine/shapes/chart.js.map +1 -1
- package/dist/charts/engine/shapes/draw.cjs +1 -1
- package/dist/charts/engine/shapes/draw.cjs.map +1 -1
- package/dist/charts/engine/shapes/draw.js +87 -86
- package/dist/charts/engine/shapes/draw.js.map +1 -1
- package/dist/charts/engine/shapes/layout.cjs +2 -2
- package/dist/charts/engine/shapes/layout.cjs.map +1 -1
- package/dist/charts/engine/shapes/layout.d.cts +8 -7
- package/dist/charts/engine/shapes/layout.d.ts +8 -7
- package/dist/charts/engine/shapes/layout.js +262 -166
- package/dist/charts/engine/shapes/layout.js.map +1 -1
- package/dist/charts/engine/shapes/types.d.cts +13 -1
- package/dist/charts/engine/shapes/types.d.ts +13 -1
- package/dist/charts/engine/three-d/chart.cjs +1 -1
- package/dist/charts/engine/three-d/chart.cjs.map +1 -1
- package/dist/charts/engine/three-d/chart.js +266 -257
- package/dist/charts/engine/three-d/chart.js.map +1 -1
- package/dist/charts/engine/three-d/dom.cjs +1 -1
- package/dist/charts/engine/three-d/dom.cjs.map +1 -1
- package/dist/charts/engine/three-d/dom.js +28 -35
- package/dist/charts/engine/three-d/dom.js.map +1 -1
- package/dist/charts/engine/three-d/geometry.cjs +1 -1
- package/dist/charts/engine/three-d/geometry.cjs.map +1 -1
- package/dist/charts/engine/three-d/geometry.js +70 -70
- package/dist/charts/engine/three-d/overlay.cjs +1 -1
- package/dist/charts/engine/three-d/overlay.cjs.map +1 -1
- package/dist/charts/engine/three-d/overlay.js +15 -15
- package/dist/charts/engine/tooltip.cjs +2 -0
- package/dist/charts/engine/tooltip.cjs.map +1 -0
- package/dist/charts/engine/tooltip.d.cts +94 -0
- package/dist/charts/engine/tooltip.d.ts +94 -0
- package/dist/charts/engine/tooltip.js +242 -0
- package/dist/charts/engine/tooltip.js.map +1 -0
- package/dist/charts/engine/trading/chart.cjs +1 -1
- package/dist/charts/engine/trading/chart.cjs.map +1 -1
- package/dist/charts/engine/trading/chart.js +135 -135
- package/dist/charts/react/shapes.cjs +1 -1
- package/dist/charts/react/shapes.cjs.map +1 -1
- package/dist/charts/react/shapes.d.cts +10 -1
- package/dist/charts/react/shapes.d.ts +10 -1
- package/dist/charts/react/shapes.js +24 -19
- package/dist/charts/react/shapes.js.map +1 -1
- package/dist/core/contracts.cjs +1 -1
- package/dist/core/contracts.cjs.map +1 -1
- package/dist/core/contracts.js +1 -1
- package/dist/core/contracts.js.map +1 -1
- package/dist/native/charts/src/canvas.cjs +1 -1
- package/dist/native/charts/src/canvas.js +8 -7
- package/dist/native/charts/src/engine/canvas.cjs +1 -1
- package/dist/native/charts/src/engine/canvas.js +2 -0
- package/dist/native/charts/src/engine/controller.cjs +2 -2
- package/dist/native/charts/src/engine/controller.cjs.map +1 -1
- package/dist/native/charts/src/engine/controller.js +102 -87
- package/dist/native/charts/src/engine/controller.js.map +1 -1
- package/dist/native/charts/src/engine/hover-rect.cjs +2 -0
- package/dist/native/charts/src/engine/hover-rect.cjs.map +1 -0
- package/dist/native/charts/src/engine/hover-rect.js +47 -0
- package/dist/native/charts/src/engine/hover-rect.js.map +1 -0
- package/dist/native/charts/src/engine/scene.cjs +1 -1
- package/dist/native/charts/src/engine/scene.cjs.map +1 -1
- package/dist/native/charts/src/engine/scene.js +318 -245
- package/dist/native/charts/src/engine/scene.js.map +1 -1
- package/dist/native/charts/src/engine/shapes/draw.cjs +1 -1
- package/dist/native/charts/src/engine/shapes/draw.cjs.map +1 -1
- package/dist/native/charts/src/engine/shapes/draw.js +81 -80
- package/dist/native/charts/src/engine/shapes/draw.js.map +1 -1
- package/dist/native/charts/src/engine/shapes/layout.cjs +2 -2
- package/dist/native/charts/src/engine/shapes/layout.cjs.map +1 -1
- package/dist/native/charts/src/engine/shapes/layout.js +239 -143
- package/dist/native/charts/src/engine/shapes/layout.js.map +1 -1
- package/dist/native/charts/src/engine/tooltip.cjs +2 -0
- package/dist/native/charts/src/engine/tooltip.cjs.map +1 -0
- package/dist/native/charts/src/engine/tooltip.js +164 -0
- package/dist/native/charts/src/engine/tooltip.js.map +1 -0
- package/dist/native/components/Chart/scene.cjs +1 -1
- package/dist/native/components/Chart/scene.cjs.map +1 -1
- package/dist/native/components/Chart/scene.js +210 -187
- package/dist/native/components/Chart/scene.js.map +1 -1
- package/dist/native/source/components/Chart/scene.tsx +55 -2
- package/dist/taro/index.js +2026 -1626
- package/dist/uni/charts-canvas.js +1 -0
- package/dist/vue/components/Chart/BulletChart.vue.d.ts +2 -0
- package/dist/vue/components/Chart/CalendarHeatmap.vue.d.ts +2 -0
- package/dist/vue/components/Chart/ChordChart.vue.d.ts +2 -0
- package/dist/vue/components/Chart/DonutChart.vue.d.ts +2 -0
- package/dist/vue/components/Chart/FunnelChart.vue.d.ts +2 -0
- package/dist/vue/components/Chart/GaugeChart.vue.d.ts +2 -0
- package/dist/vue/components/Chart/PieChart.vue.d.ts +2 -0
- package/dist/vue/components/Chart/RadarChart.vue.d.ts +2 -0
- package/dist/vue/components/Chart/RadialBarChart.vue.d.ts +2 -0
- package/dist/vue/components/Chart/SankeyChart.vue.d.ts +2 -0
- package/dist/vue/components/Chart/SunburstChart.vue.d.ts +2 -0
- package/dist/vue/components/Chart/TreemapChart.vue.d.ts +2 -0
- package/dist/vue/components/Chart/props.d.ts +2 -0
- package/dist/vue/components/Chart/props.js +1 -0
- package/dist/vue/components/Chart/props.js.map +1 -1
- package/dist/vue/components/Chart/shape.d.ts +2 -1
- package/dist/vue/components/Chart/shape.js +2 -0
- package/dist/vue/components/Chart/shape.js.map +1 -1
- package/dist/vue/components/Chart/types.d.ts +2 -0
- package/dist/weapp/charts/canvas.js +2576 -1969
- package/dist/weapp/charts/core.js +1 -0
- package/dist/web-components/components/chart/shape-chart.d.ts +9 -7
- package/dist/web-components/components/chart/shape-chart.js +3 -0
- package/dist/web-components/components/chart/shape-chart.js.map +1 -1
- package/dist/web-components/components/chart/shapes-extended.d.ts +6 -6
- package/dist/web-components/components/chart/shapes-extended.js.map +1 -1
- package/package.json +1 -1
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
const e=require("../
|
|
1
|
+
const e=require("../scale.cjs"),t=require("../color.cjs"),n=require("./model.cjs"),r=require("./math.cjs");var i=(e,n)=>({text:t.toCss(e.text),muted:t.toCss(e.textMuted),grid:t.toCss(e.grid),axis:t.toCss(e.axis),wall:t.toCss(e.text,n?.045:.025),background:t.toCss(e.background),font:e.fontFamily,fontSize:e.fontSize}),a=[`x`,`y`,`z`],o=e=>{if(e.categories){let t=[];for(let r=0;r<e.categories.length-1;r++)t.push(n.toWorld(e,r+.5));return t}return e.ticks.filter(t=>t>e.domain[0]&&t<e.domain[1]).map(t=>n.toWorld(e,t))},s=e=>[e.eye[0]>=0?-1:1,e.eye[1]>=0?-1:1,e.eye[2]>=0?-1:1],c=e=>[e.x.half,e.y.half,e.z.half],l=(e,t,n)=>{let r=[n[0],n[1],n[2]];return r[e]=t,r},u=(e,t,n,i)=>{let u=c(n),d=s(t),f=e=>r.project(t,e[0],e[1],e[2]);e.lineWidth=1;for(let t=0;t<3;t++){let r=(t+1)%3,s=(t+2)%3,c=[0,0,0];c[t]=d[t]*u[t];let p=(e,t)=>{let n=[c[0],c[1],c[2]];return n[r]=e*u[r],n[s]=t*u[s],f(n)},m=[p(-1,-1),p(1,-1),p(1,1),p(-1,1)];e.beginPath(),m.forEach((t,n)=>n?e.lineTo(t.x,t.y):e.moveTo(t.x,t.y)),e.closePath(),e.fillStyle=i.wall,e.fill(),e.beginPath();for(let[t,i]of[[r,s],[s,r]]){let r=n[a[t]];if(r.grid)for(let n of o(r)){let r=l(t,n,c),a=l(t,n,c);r[i]=-u[i],a[i]=u[i];let o=f(r),s=f(a);e.moveTo(o.x,o.y),e.lineTo(s.x,s.y)}}e.strokeStyle=i.grid,e.stroke(),e.beginPath(),m.forEach((t,n)=>n?e.lineTo(t.x,t.y):e.moveTo(t.x,t.y)),e.closePath(),e.strokeStyle=i.axis,e.stroke()}},d=(e,t,n=2)=>e.x<t.x+t.w+n&&t.x<e.x+e.w+n&&e.y<t.y+t.h+n&&t.y<e.y+e.h+n,f=10,p=(e,t,n)=>{let i=c(t),a=e.eye;if(n===0)return[0,(a[1]>=0?1:-1)*i[1],-i[2]];if(n===1)return[(a[0]>=0?1:-1)*i[0],0,-i[2]];let o=[-i[0],-i[1],0],s=1/0;for(let t of[-1,1])for(let n of[-1,1]){let a=r.project(e,t*i[0],n*i[1],0);a.x<s&&(s=a.x,o=[t*i[0],n*i[1],0])}return o},m=(e,t,i,o,s={left:0,top:0,right:t.width,bottom:t.height})=>{let c=r.project(t,0,0,0),u=[],m=o.fontSize,h=`${m}px ${o.font}`,g=`600 ${m+1}px ${o.font}`;for(let _=0;_<3;_++){let v=i[a[_]];if(!v.visible)continue;let y=p(t,i,_),b=r.project(t,...l(_,-v.half,y)),x=r.project(t,...l(_,v.half,y)),S=x.x-b.x,C=x.y-b.y,w=Math.hypot(S,C);if(w<24)continue;S/=w,C/=w;let T=-C,E=S,D=(b.x+x.x)/2,O=(b.y+x.y)/2;T*(D-c.x)+E*(O-c.y)<0&&(T=-T,E=-E),_===2&&(T=D<c.x?-1:1,E=0);let k=T>.35?`left`:T<-.35?`right`:`center`,A=E>.35?`top`:E<-.35?`bottom`:`middle`;e.font=h,e.textAlign=k,e.textBaseline=A;let j=v.ticks.map((i,a)=>{let o=r.project(t,...l(_,n.toWorld(v,i),y)),s=v.labels[a]??``,c=e.measureText(s).width,u=o.x+T*8,d=o.y+E*8;return{tick:o,text:s,x:u,y:d,rect:{x:k===`left`?u:k===`right`?u-c:u-c/2,y:A===`top`?d:A===`bottom`?d-m:d-m/2,w:c,h:m}}}),M=1;for(;M<j.length;M++){let e=j.filter((e,t)=>t%M===0);if(e.every((t,n)=>!e.slice(0,n).some(e=>d(t.rect,e.rect))))break}let N=0;if(e.beginPath(),j.forEach((t,n)=>{n%M||u.some(e=>d(t.rect,e,f))||(u.push(t.rect),e.moveTo(t.tick.x,t.tick.y),e.lineTo(t.tick.x+T*4,t.tick.y+E*4),N=Math.max(N,Math.abs(T)*t.rect.w+Math.abs(E)*t.rect.h))}),e.strokeStyle=o.axis,e.lineWidth=1,e.stroke(),e.fillStyle=o.muted,j.forEach((t,n)=>{n%M||!u.includes(t.rect)||e.fillText(t.text,t.x,t.y)}),!v.title)continue;let P=8+N+10,F=D+T*P,I=O+E*P;e.font=g,e.fillStyle=o.text;let L=e.measureText(v.title).width,R=m+1;if(_===2)F=Math.max(s.left+R,Math.min(s.right-R,F)),I=Math.max(s.top+L/2,Math.min(s.bottom-L/2,I)),e.save(),e.translate(F,I),e.rotate(-Math.PI/2),e.textAlign=`center`,e.textBaseline=T<0?`bottom`:`top`,e.fillText(v.title,0,0),e.restore();else{let t=(e,t)=>({x:k===`left`?e:k===`right`?e-L:e-L/2,y:A===`top`?t:A===`bottom`?t-R:t-R/2,w:L,h:R});for(let e=0;e<12&&u.some(e=>d(t(F,I),e));e++)F+=T*3,I+=E*3;let{x:n,y:r}=t(F,I);F+=Math.max(0,s.left-n)-Math.max(0,n+L-s.right),I+=Math.max(0,s.top-r)-Math.max(0,r+R-s.bottom),u.push(t(F,I)),e.textAlign=k,e.textBaseline=A,e.fillText(v.title,F,I)}}},h=(n,r,i,a,o,s,c,l=1/0)=>{let u=n.createLinearGradient(0,r.y+r.h,0,r.y);for(let e=0;e<=24;e++){let n=i.rgba[Math.round(e/24*(i.rgba.length-1))];n&&u.addColorStop(e/24,t.toCss(n))}n.fillStyle=u,n.beginPath(),typeof n.roundRect==`function`?n.roundRect(r.x,r.y,r.w,r.h,3):n.rect(r.x,r.y,r.w,r.h),n.fill(),n.strokeStyle=c.axis,n.lineWidth=1,n.stroke();let[d,f]=a,p=f-d||1;n.font=`${c.fontSize}px ${c.font}`,n.fillStyle=c.muted,n.textAlign=`left`,n.textBaseline=`middle`;let m=e.linearTicks(d,f,Math.max(2,Math.round(r.h/40)));n.beginPath();for(let e of m){let t=r.y+r.h-(e-d)/p*r.h;t<r.y-.5||t>r.y+r.h+.5||(n.moveTo(r.x+r.w,t),n.lineTo(r.x+r.w+4,t),n.fillText(o(e),r.x+r.w+7,t))}if(n.strokeStyle=c.axis,n.stroke(),s){n.font=`600 ${c.fontSize}px ${c.font}`,n.fillStyle=c.text,n.textAlign=`left`,n.textBaseline=`bottom`;let e=n.measureText(s).width;n.fillText(s,Math.max(4,Math.min(r.x,l-4-e)),r.y-8)}},g=(e,t,n,i,a)=>{if(e.save(),i.kind===`point`)e.beginPath(),e.arc(i.x,i.y,i.radius+3,0,Math.PI*2),e.lineWidth=3,e.strokeStyle=a.background,e.stroke(),e.lineWidth=1.5,e.strokeStyle=a.text,e.stroke();else if(i.kind===`box`){let{min:n,max:o}=i,s=e=>r.project(t,e&1?o[0]:n[0],e&2?o[1]:n[1],e&4?o[2]:n[2]),c=Array.from({length:8},(e,t)=>s(t));e.beginPath();for(let t=0;t<8;t++)for(let n of[1,2,4]){if(t&n)continue;let r=c[t],i=c[t|n];e.moveTo(r.x,r.y),e.lineTo(i.x,i.y)}e.lineJoin=`round`,e.lineWidth=3.5,e.strokeStyle=a.background,e.stroke(),e.lineWidth=1.5,e.strokeStyle=a.text,e.stroke()}else{let[o,s,c]=i.world,l=r.project(t,o,s,c),u=r.project(t,o,s,-n.z.half);e.beginPath(),e.setLineDash([3,3]),e.moveTo(l.x,l.y),e.lineTo(u.x,u.y),e.lineWidth=1,e.strokeStyle=a.text,e.stroke(),e.setLineDash([]),e.beginPath(),e.arc(u.x,u.y,2.5,0,Math.PI*2),e.fillStyle=a.text,e.fill(),e.beginPath(),e.arc(l.x,l.y,5,0,Math.PI*2),e.fillStyle=i.color,e.fill(),e.lineWidth=2,e.strokeStyle=a.background,e.stroke(),e.beginPath(),e.arc(l.x,l.y,7,0,Math.PI*2),e.lineWidth=1.25,e.strokeStyle=a.text,e.stroke()}e.restore()};exports.drawAxes=m,exports.drawColorBar=h,exports.drawHighlight=g,exports.drawWalls=u,exports.farWalls=s,exports.gridPositions=o,exports.labelEdge=p,exports.overlayStyle=i;
|
|
2
2
|
//# sourceMappingURL=overlay.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"overlay.cjs","names":["toCss","toWorld","project","linearTicks","require_color","require_scale","require_model","require_math","overlayStyle","theme","dark","AXES","gridPositions","axis","out","i","farWalls","frame","half","axes","at","p","rest","value","drawWalls","h","walls","P","a","b","c","base","corner","sb","sc","quad","ctx","q","style","along","across","w","p0","p1","s0","s1","overlaps","pad","CROSS_AXIS_GAP","labelEdge","eye","best","left","sx","sy","drawAxes","center","placed","size","labelFont","titleFont","edge","e0","e1","dx","dy","length","ox","oy","mx","my","align","baseline","labels","s","tick","text","x","y","step","kept","l","o","extent","distance","tx","ty","tw","th","bounds","rectAt","k","top","drawColorBar","gradient","rect","lut","min","max","domain","span","ticks","t","format","title","right","drawHighlight","shape","pts","bit","z","floor"],"sources":["../../../../../charts/src/engine/three-d/overlay.ts"],"sourcesContent":["// 2D overlay of the 3D charts: the far walls of the axis box with their grid\n// lines (below the data), then the tick labels, axis titles, color bar and\n// hover highlight (above the data). Text is always horizontal (screen\n// aligned) so it stays readable whatever the camera.\n\nimport { toCss, type Rgba } from \"../color\";\nimport { linearTicks } from \"../scale\";\nimport type { ColorLut } from \"./colormap\";\nimport { project, type CameraFrame, type Vec3 } from \"./math\";\nimport { toWorld, type AxisModel, type Model3D } from \"./model\";\n\nexport interface OverlayStyle {\n text: string;\n muted: string;\n grid: string;\n axis: string;\n /** Tint of the walls */\n wall: string;\n /** Background (rings of the hover marker) */\n background: string;\n font: string;\n fontSize: number;\n}\n\nexport const overlayStyle = (\n theme: {\n text: Rgba;\n textMuted: Rgba;\n grid: Rgba;\n axis: Rgba;\n background: Rgba;\n fontFamily: string;\n fontSize: number;\n },\n dark: boolean,\n): OverlayStyle => ({\n text: toCss(theme.text),\n muted: toCss(theme.textMuted),\n grid: toCss(theme.grid),\n axis: toCss(theme.axis),\n wall: toCss(theme.text, dark ? 0.045 : 0.025),\n background: toCss(theme.background),\n font: theme.fontFamily,\n fontSize: theme.fontSize,\n});\n\ntype Axes = Pick<Model3D, \"x\" | \"y\" | \"z\">;\n\nconst AXES: readonly (\"x\" | \"y\" | \"z\")[] = [\"x\", \"y\", \"z\"];\n\n/** World positions of the grid lines of an axis (category axes: between the categories) */\nexport const gridPositions = (axis: AxisModel): number[] => {\n if (axis.categories) {\n const out: number[] = [];\n for (let i = 0; i < axis.categories.length - 1; i++)\n out.push(toWorld(axis, i + 0.5));\n return out;\n }\n return axis.ticks\n .filter((t) => t > axis.domain[0] && t < axis.domain[1])\n .map((t) => toWorld(axis, t));\n};\n\n/** Side (-1 / 1) of the far wall perpendicular to every axis */\nexport const farWalls = (frame: CameraFrame): [number, number, number] => [\n frame.eye[0] >= 0 ? -1 : 1,\n frame.eye[1] >= 0 ? -1 : 1,\n frame.eye[2] >= 0 ? -1 : 1,\n];\n\nconst half = (axes: Axes): Vec3 => [axes.x.half, axes.y.half, axes.z.half];\n\nconst at = (axis: number, value: number, rest: Vec3): Vec3 => {\n const p: Vec3 = [rest[0], rest[1], rest[2]];\n p[axis] = value;\n return p;\n};\n\n/** Far walls, their grid lines and borders */\nexport const drawWalls = (\n ctx: CanvasRenderingContext2D,\n frame: CameraFrame,\n axes: Axes,\n style: OverlayStyle,\n): void => {\n const h = half(axes);\n const walls = farWalls(frame);\n const P = (p: Vec3) => project(frame, p[0], p[1], p[2]);\n ctx.lineWidth = 1;\n for (let a = 0; a < 3; a++) {\n const b = (a + 1) % 3;\n const c = (a + 2) % 3;\n const base: Vec3 = [0, 0, 0];\n base[a] = walls[a] * h[a];\n const corner = (sb: number, sc: number) => {\n const p: Vec3 = [base[0], base[1], base[2]];\n p[b] = sb * h[b];\n p[c] = sc * h[c];\n return P(p);\n };\n const quad = [corner(-1, -1), corner(1, -1), corner(1, 1), corner(-1, 1)];\n ctx.beginPath();\n quad.forEach((q, i) => (i ? ctx.lineTo(q.x, q.y) : ctx.moveTo(q.x, q.y)));\n ctx.closePath();\n ctx.fillStyle = style.wall;\n ctx.fill();\n // grid lines of the two axes spanning the wall\n ctx.beginPath();\n for (const [along, across] of [\n [b, c],\n [c, b],\n ] as const) {\n const axis = axes[AXES[along]!];\n if (!axis.grid) continue;\n for (const w of gridPositions(axis)) {\n const p0 = at(along, w, base);\n const p1 = at(along, w, base);\n p0[across] = -h[across];\n p1[across] = h[across];\n const s0 = P(p0);\n const s1 = P(p1);\n ctx.moveTo(s0.x, s0.y);\n ctx.lineTo(s1.x, s1.y);\n }\n }\n ctx.strokeStyle = style.grid;\n ctx.stroke();\n ctx.beginPath();\n quad.forEach((q, i) => (i ? ctx.lineTo(q.x, q.y) : ctx.moveTo(q.x, q.y)));\n ctx.closePath();\n ctx.strokeStyle = style.axis;\n ctx.stroke();\n }\n};\n\ninterface Rect {\n x: number;\n y: number;\n w: number;\n h: number;\n}\n\nconst overlaps = (a: Rect, b: Rect, pad = 2) =>\n a.x < b.x + b.w + pad &&\n b.x < a.x + a.w + pad &&\n a.y < b.y + b.h + pad &&\n b.y < a.y + a.h + pad;\n\n/** Smallest gap (px) between tick labels of different axes */\nconst CROSS_AXIS_GAP = 10;\n\n/** Edge (fixed coordinates of the two other axes) carrying the labels of an axis */\nexport const labelEdge = (\n frame: CameraFrame,\n axes: Axes,\n axis: number,\n): Vec3 => {\n const h = half(axes);\n const eye = frame.eye;\n if (axis === 0) return [0, (eye[1] >= 0 ? 1 : -1) * h[1], -h[2]];\n if (axis === 1) return [(eye[0] >= 0 ? 1 : -1) * h[0], 0, -h[2]];\n // vertical axis: the leftmost vertical edge on screen\n let best: Vec3 = [-h[0], -h[1], 0];\n let left = Infinity;\n for (const sx of [-1, 1]) {\n for (const sy of [-1, 1]) {\n const p = project(frame, sx * h[0], sy * h[1], 0);\n if (p.x < left) {\n left = p.x;\n best = [sx * h[0], sy * h[1], 0];\n }\n }\n }\n return best;\n};\n\n/** Area the labels and titles must stay in (CSS pixels of the stage) */\nexport interface OverlayBounds {\n left: number;\n top: number;\n right: number;\n bottom: number;\n}\n\n/** Tick labels and titles of the three axes */\nexport const drawAxes = (\n ctx: CanvasRenderingContext2D,\n frame: CameraFrame,\n axes: Axes,\n style: OverlayStyle,\n bounds: OverlayBounds = {\n left: 0,\n top: 0,\n right: frame.width,\n bottom: frame.height,\n },\n): void => {\n const center = project(frame, 0, 0, 0);\n const placed: Rect[] = [];\n const size = style.fontSize;\n const labelFont = `${size}px ${style.font}`;\n const titleFont = `600 ${size + 1}px ${style.font}`;\n for (let a = 0; a < 3; a++) {\n const axis = axes[AXES[a]!];\n if (!axis.visible) continue;\n const edge = labelEdge(frame, axes, a);\n const e0 = project(frame, ...at(a, -axis.half, edge));\n const e1 = project(frame, ...at(a, axis.half, edge));\n let dx = e1.x - e0.x;\n let dy = e1.y - e0.y;\n const length = Math.hypot(dx, dy);\n // seen end-on: no room for labels\n if (length < 24) continue;\n dx /= length;\n dy /= length;\n let ox = -dy;\n let oy = dx;\n const mx = (e0.x + e1.x) / 2;\n const my = (e0.y + e1.y) / 2;\n if (ox * (mx - center.x) + oy * (my - center.y) < 0) {\n ox = -ox;\n oy = -oy;\n }\n // vertical axis: labels go sideways\n if (a === 2) {\n ox = mx < center.x ? -1 : 1;\n oy = 0;\n }\n const align: CanvasTextAlign =\n ox > 0.35 ? \"left\" : ox < -0.35 ? \"right\" : \"center\";\n const baseline: CanvasTextBaseline =\n oy > 0.35 ? \"top\" : oy < -0.35 ? \"bottom\" : \"middle\";\n ctx.font = labelFont;\n ctx.textAlign = align;\n ctx.textBaseline = baseline;\n const gap = 8;\n const labels = axis.ticks.map((tick, i) => {\n const s = project(frame, ...at(a, toWorld(axis, tick), edge));\n const text = axis.labels[i] ?? \"\";\n const w = ctx.measureText(text).width;\n const x = s.x + ox * gap;\n const y = s.y + oy * gap;\n const rx = align === \"left\" ? x : align === \"right\" ? x - w : x - w / 2;\n const ry =\n baseline === \"top\"\n ? y\n : baseline === \"bottom\"\n ? y - size\n : y - size / 2;\n return { tick: s, text, x, y, rect: { x: rx, y: ry, w, h: size } };\n });\n // thin the labels until they do not overlap\n let step = 1;\n for (; step < labels.length; step++) {\n const kept = labels.filter((_, i) => i % step === 0);\n if (\n kept.every(\n (l, i) => !kept.slice(0, i).some((o) => overlaps(l.rect, o.rect)),\n )\n ) {\n break;\n }\n }\n let extent = 0;\n ctx.beginPath();\n labels.forEach((l, i) => {\n if (i % step) return;\n // wider gap to the labels of the other axes: at the shared corner, two\n // labels side by side would read as one (\"-2 -2\")\n if (placed.some((o) => overlaps(l.rect, o, CROSS_AXIS_GAP))) return;\n placed.push(l.rect);\n ctx.moveTo(l.tick.x, l.tick.y);\n ctx.lineTo(l.tick.x + ox * 4, l.tick.y + oy * 4);\n extent = Math.max(\n extent,\n Math.abs(ox) * l.rect.w + Math.abs(oy) * l.rect.h,\n );\n });\n ctx.strokeStyle = style.axis;\n ctx.lineWidth = 1;\n ctx.stroke();\n ctx.fillStyle = style.muted;\n labels.forEach((l, i) => {\n if (i % step || !placed.includes(l.rect)) return;\n ctx.fillText(l.text, l.x, l.y);\n });\n if (!axis.title) continue;\n const distance = gap + extent + 10;\n let tx = mx + ox * distance;\n let ty = my + oy * distance;\n ctx.font = titleFont;\n ctx.fillStyle = style.text;\n const tw = ctx.measureText(axis.title).width;\n const th = size + 1;\n if (a === 2) {\n tx = Math.max(bounds.left + th, Math.min(bounds.right - th, tx));\n ty = Math.max(bounds.top + tw / 2, Math.min(bounds.bottom - tw / 2, ty));\n ctx.save();\n ctx.translate(tx, ty);\n ctx.rotate(-Math.PI / 2);\n ctx.textAlign = \"center\";\n ctx.textBaseline = ox < 0 ? \"bottom\" : \"top\";\n ctx.fillText(axis.title, 0, 0);\n ctx.restore();\n } else {\n const rectAt = (x: number, y: number): Rect => ({\n x: align === \"left\" ? x : align === \"right\" ? x - tw : x - tw / 2,\n y: baseline === \"top\" ? y : baseline === \"bottom\" ? y - th : y - th / 2,\n w: tw,\n h: th,\n });\n // slanted axes: long titles move out until they clear the labels\n for (\n let k = 0;\n k < 12 && placed.some((o) => overlaps(rectAt(tx, ty), o));\n k++\n ) {\n tx += ox * 3;\n ty += oy * 3;\n }\n // keep the whole title inside the bounds\n const { x: left, y: top } = rectAt(tx, ty);\n tx +=\n Math.max(0, bounds.left - left) - Math.max(0, left + tw - bounds.right);\n ty +=\n Math.max(0, bounds.top - top) - Math.max(0, top + th - bounds.bottom);\n placed.push(rectAt(tx, ty));\n ctx.textAlign = align;\n ctx.textBaseline = baseline;\n ctx.fillText(axis.title, tx, ty);\n }\n }\n};\n\n/** Vertical color bar with its ticks and title */\nexport const drawColorBar = (\n ctx: CanvasRenderingContext2D,\n rect: Rect,\n lut: ColorLut,\n domain: readonly [number, number],\n format: (value: number) => string,\n title: string,\n style: OverlayStyle,\n /** Right end of the stage: the title is shifted left to fit */\n right = Infinity,\n): void => {\n const gradient = ctx.createLinearGradient(0, rect.y + rect.h, 0, rect.y);\n const samples = 24;\n for (let i = 0; i <= samples; i++) {\n const c = lut.rgba[Math.round((i / samples) * (lut.rgba.length - 1))];\n if (c) gradient.addColorStop(i / samples, toCss(c));\n }\n ctx.fillStyle = gradient;\n ctx.beginPath();\n if (typeof ctx.roundRect === \"function\")\n ctx.roundRect(rect.x, rect.y, rect.w, rect.h, 3);\n else ctx.rect(rect.x, rect.y, rect.w, rect.h);\n ctx.fill();\n ctx.strokeStyle = style.axis;\n ctx.lineWidth = 1;\n ctx.stroke();\n const [min, max] = domain;\n const span = max - min || 1;\n ctx.font = `${style.fontSize}px ${style.font}`;\n ctx.fillStyle = style.muted;\n ctx.textAlign = \"left\";\n ctx.textBaseline = \"middle\";\n const ticks = linearTicks(min, max, Math.max(2, Math.round(rect.h / 40)));\n ctx.beginPath();\n for (const t of ticks) {\n const y = rect.y + rect.h - ((t - min) / span) * rect.h;\n if (y < rect.y - 0.5 || y > rect.y + rect.h + 0.5) continue;\n ctx.moveTo(rect.x + rect.w, y);\n ctx.lineTo(rect.x + rect.w + 4, y);\n ctx.fillText(format(t), rect.x + rect.w + 7, y);\n }\n ctx.strokeStyle = style.axis;\n ctx.stroke();\n if (title) {\n ctx.font = `600 ${style.fontSize}px ${style.font}`;\n ctx.fillStyle = style.text;\n ctx.textAlign = \"left\";\n ctx.textBaseline = \"bottom\";\n const w = ctx.measureText(title).width;\n ctx.fillText(\n title,\n Math.max(4, Math.min(rect.x, right - 4 - w)),\n rect.y - 8,\n );\n }\n};\n\n/** Highlight of the hovered item */\nexport type HighlightShape =\n | { kind: \"point\"; x: number; y: number; radius: number; color: string }\n | { kind: \"box\"; min: Vec3; max: Vec3; color: string }\n | { kind: \"vertex\"; world: Vec3; color: string };\n\nexport const drawHighlight = (\n ctx: CanvasRenderingContext2D,\n frame: CameraFrame,\n axes: Axes,\n shape: HighlightShape,\n style: OverlayStyle,\n): void => {\n ctx.save();\n if (shape.kind === \"point\") {\n ctx.beginPath();\n ctx.arc(shape.x, shape.y, shape.radius + 3, 0, Math.PI * 2);\n ctx.lineWidth = 3;\n ctx.strokeStyle = style.background;\n ctx.stroke();\n ctx.lineWidth = 1.5;\n ctx.strokeStyle = style.text;\n ctx.stroke();\n } else if (shape.kind === \"box\") {\n const { min, max } = shape;\n const corner = (i: number) =>\n project(\n frame,\n i & 1 ? max[0] : min[0],\n i & 2 ? max[1] : min[1],\n i & 4 ? max[2] : min[2],\n );\n const pts = Array.from({ length: 8 }, (_, i) => corner(i));\n ctx.beginPath();\n for (let i = 0; i < 8; i++) {\n for (const bit of [1, 2, 4]) {\n if (i & bit) continue;\n const a = pts[i]!;\n const b = pts[i | bit]!;\n ctx.moveTo(a.x, a.y);\n ctx.lineTo(b.x, b.y);\n }\n }\n ctx.lineJoin = \"round\";\n ctx.lineWidth = 3.5;\n ctx.strokeStyle = style.background;\n ctx.stroke();\n ctx.lineWidth = 1.5;\n ctx.strokeStyle = style.text;\n ctx.stroke();\n } else {\n const [x, y, z] = shape.world;\n const top = project(frame, x, y, z);\n const floor = project(frame, x, y, -axes.z.half);\n ctx.beginPath();\n ctx.setLineDash([3, 3]);\n ctx.moveTo(top.x, top.y);\n ctx.lineTo(floor.x, floor.y);\n ctx.lineWidth = 1;\n ctx.strokeStyle = style.text;\n ctx.stroke();\n ctx.setLineDash([]);\n ctx.beginPath();\n ctx.arc(floor.x, floor.y, 2.5, 0, Math.PI * 2);\n ctx.fillStyle = style.text;\n ctx.fill();\n ctx.beginPath();\n ctx.arc(top.x, top.y, 5, 0, Math.PI * 2);\n ctx.fillStyle = shape.color;\n ctx.fill();\n ctx.lineWidth = 2;\n ctx.strokeStyle = style.background;\n ctx.stroke();\n ctx.beginPath();\n ctx.arc(top.x, top.y, 7, 0, Math.PI * 2);\n ctx.lineWidth = 1.25;\n ctx.strokeStyle = style.text;\n ctx.stroke();\n }\n ctx.restore();\n};\n"],"mappings":"2GAwBA,IAAaQ,GACX,EASA,KACkB,CAClB,KAAMR,EAAAA,MAAMS,EAAM,IAAI,EACtB,MAAOT,EAAAA,MAAMS,EAAM,SAAS,EAC5B,KAAMT,EAAAA,MAAMS,EAAM,IAAI,EACtB,KAAMT,EAAAA,MAAMS,EAAM,IAAI,EACtB,KAAMT,EAAAA,MAAMS,EAAM,KAAMC,EAAO,KAAQ,IAAK,EAC5C,WAAYV,EAAAA,MAAMS,EAAM,UAAU,EAClC,KAAMA,EAAM,WACZ,SAAUA,EAAM,QAClB,GAIME,EAAqC,CAAC,IAAK,IAAK,GAAG,EAG5CC,EAAiB,GAA8B,CAC1D,GAAIC,EAAK,WAAY,CACnB,IAAMC,EAAgB,CAAC,EACvB,IAAK,IAAIC,EAAI,EAAGA,EAAIF,EAAK,WAAW,OAAS,EAAG,IAC9CC,EAAI,KAAKb,EAAAA,QAAQY,EAAME,EAAI,EAAG,CAAC,EACjC,OAAOD,CACT,CACA,OAAOD,EAAK,MACT,OAAQ,GAAM,EAAIA,EAAK,OAAO,IAAM,EAAIA,EAAK,OAAO,EAAE,CAAC,CACvD,IAAK,GAAMZ,EAAAA,QAAQY,EAAM,CAAC,CAAC,CAChC,EAGaG,EAAY,GAAiD,CACxEC,EAAM,IAAI,IAAM,EAAI,GAAK,EACzBA,EAAM,IAAI,IAAM,EAAI,GAAK,EACzBA,EAAM,IAAI,IAAM,EAAI,GAAK,CAC3B,EAEMC,EAAQ,GAAqB,CAACC,EAAK,EAAE,KAAMA,EAAK,EAAE,KAAMA,EAAK,EAAE,IAAI,EAEnEC,GAAM,EAAc,EAAe,IAAqB,CAC5D,IAAMC,EAAU,CAACC,EAAK,GAAIA,EAAK,GAAIA,EAAK,EAAE,EAE1C,MADA,GAAET,GAAQU,EACHF,CACT,EAGaG,GACX,EACA,EACA,EACA,IACS,CACT,IAAMC,EAAIP,EAAKC,CAAI,EACbO,EAAQV,EAASC,CAAK,EACtBU,EAAK,GAAYzB,EAAAA,QAAQe,EAAOI,EAAE,GAAIA,EAAE,GAAIA,EAAE,EAAE,EACtD,EAAI,UAAY,EAChB,IAAK,IAAIO,EAAI,EAAGA,EAAI,EAAG,IAAK,CAC1B,IAAMC,GAAKD,EAAI,GAAK,EACdE,GAAKF,EAAI,GAAK,EACdG,EAAa,CAAC,EAAG,EAAG,CAAC,EAC3B,EAAKH,GAAKF,EAAME,GAAKH,EAAEG,GACvB,IAAMI,GAAU,EAAY,IAAe,CACzC,IAAMX,EAAU,CAACU,EAAK,GAAIA,EAAK,GAAIA,EAAK,EAAE,EAG1C,MAFA,GAAEF,GAAKI,EAAKR,EAAEI,GACd,EAAEC,GAAKI,EAAKT,EAAEK,GACPH,EAAEN,CAAC,CACZ,EACMc,EAAO,CAACH,EAAO,GAAI,EAAE,EAAGA,EAAO,EAAG,EAAE,EAAGA,EAAO,EAAG,CAAC,EAAGA,EAAO,GAAI,CAAC,CAAC,EACxEI,EAAI,UAAU,EACdD,EAAK,SAAS,EAAG,IAAOpB,EAAIqB,EAAI,OAAOC,EAAE,EAAGA,EAAE,CAAC,EAAID,EAAI,OAAOC,EAAE,EAAGA,EAAE,CAAC,CAAE,EACxED,EAAI,UAAU,EACd,EAAI,UAAYE,EAAM,KACtBF,EAAI,KAAK,EAETA,EAAI,UAAU,EACd,IAAK,GAAM,CAACG,EAAOC,IAAW,CAC5B,CAACX,EAAGC,CAAC,EACL,CAACA,EAAGD,CAAC,CACP,EAAY,CACV,IAAMhB,EAAOM,EAAKR,EAAK4B,IACvB,GAAK1B,EAAK,KACV,IAAK,IAAM4B,KAAK7B,EAAcC,CAAI,EAAG,CACnC,IAAM6B,EAAKtB,EAAGmB,EAAOE,EAAGV,CAAI,EACtBY,EAAKvB,EAAGmB,EAAOE,EAAGV,CAAI,EAC5B,EAAGS,GAAU,CAACf,EAAEe,GAChB,EAAGA,GAAUf,EAAEe,GACf,IAAMI,EAAKjB,EAAEe,CAAE,EACTG,EAAKlB,EAAEgB,CAAE,EACfP,EAAI,OAAOQ,EAAG,EAAGA,EAAG,CAAC,EACrBR,EAAI,OAAOS,EAAG,EAAGA,EAAG,CAAC,CACvB,CACF,CACA,EAAI,YAAcP,EAAM,KACxBF,EAAI,OAAO,EACXA,EAAI,UAAU,EACdD,EAAK,SAAS,EAAG,IAAOpB,EAAIqB,EAAI,OAAOC,EAAE,EAAGA,EAAE,CAAC,EAAID,EAAI,OAAOC,EAAE,EAAGA,EAAE,CAAC,CAAE,EACxED,EAAI,UAAU,EACd,EAAI,YAAcE,EAAM,KACxBF,EAAI,OAAO,CACb,CACF,EASMU,GAAY,EAAS,EAAS,EAAM,IACxClB,EAAE,EAAIC,EAAE,EAAIA,EAAE,EAAIkB,GAClBlB,EAAE,EAAID,EAAE,EAAIA,EAAE,EAAImB,GAClBnB,EAAE,EAAIC,EAAE,EAAIA,EAAE,EAAIkB,GAClBlB,EAAE,EAAID,EAAE,EAAIA,EAAE,EAAImB,EAGdC,EAAiB,GAGVC,GACX,EACA,EACA,IACS,CACT,IAAMxB,EAAIP,EAAKC,CAAI,EACb+B,EAAMjC,EAAM,IAClB,GAAIJ,IAAS,EAAG,MAAO,CAAC,GAAIqC,EAAI,IAAM,EAAI,EAAI,IAAMzB,EAAE,GAAI,CAACA,EAAE,EAAE,EAC/D,GAAIZ,IAAS,EAAG,MAAO,EAAEqC,EAAI,IAAM,EAAI,EAAI,IAAMzB,EAAE,GAAI,EAAG,CAACA,EAAE,EAAE,EAE/D,IAAI0B,EAAa,CAAC,CAAC1B,EAAE,GAAI,CAACA,EAAE,GAAI,CAAC,EAC7B2B,EAAO,IACX,IAAK,IAAMC,IAAM,CAAC,GAAI,CAAC,EACrB,IAAK,IAAMC,IAAM,CAAC,GAAI,CAAC,EAAG,CACxB,IAAMjC,EAAInB,EAAAA,QAAQe,EAAOoC,EAAK5B,EAAE,GAAI6B,EAAK7B,EAAE,GAAI,CAAC,EAC5CJ,EAAE,EAAI+B,IACR,EAAO/B,EAAE,EACT,EAAO,CAACgC,EAAK5B,EAAE,GAAI6B,EAAK7B,EAAE,GAAI,CAAC,EAEnC,CAEF,OAAO0B,CACT,EAWaI,GACX,EACA,EACA,EACA,EACA,EAAwB,CACtB,KAAM,EACN,IAAK,EACL,MAAOtC,EAAM,MACb,OAAQA,EAAM,MAChB,IACS,CACT,IAAMuC,EAAStD,EAAAA,QAAQe,EAAO,EAAG,EAAG,CAAC,EAC/BwC,EAAiB,CAAC,EAClBC,EAAOpB,EAAM,SACbqB,EAAY,GAAGD,EAAK,KAAKpB,EAAM,OAC/BsB,EAAY,OAAOF,EAAO,EAAE,KAAKpB,EAAM,OAC7C,IAAK,IAAIV,EAAI,EAAGA,EAAI,EAAG,IAAK,CAC1B,IAAMf,EAAOM,EAAKR,EAAKiB,IACvB,GAAI,CAACf,EAAK,QAAS,SACnB,IAAMgD,EAAOZ,EAAUhC,EAAOE,EAAMS,CAAC,EAC/BkC,EAAK5D,EAAAA,QAAQe,EAAO,GAAGG,EAAGQ,EAAG,CAACf,EAAK,KAAMgD,CAAI,CAAC,EAC9CE,EAAK7D,EAAAA,QAAQe,EAAO,GAAGG,EAAGQ,EAAGf,EAAK,KAAMgD,CAAI,CAAC,EAC/CG,EAAKD,EAAG,EAAID,EAAG,EACfG,EAAKF,EAAG,EAAID,EAAG,EACbI,EAAS,KAAK,MAAMF,EAAIC,CAAE,EAEhC,GAAIC,EAAS,GAAI,SACjB,GAAMA,EACN,GAAMA,EACN,IAAIC,EAAK,CAACF,EACNG,EAAKJ,EACHK,GAAMP,EAAG,EAAIC,EAAG,GAAK,EACrBO,GAAMR,EAAG,EAAIC,EAAG,GAAK,EACvBI,GAAME,EAAKb,EAAO,GAAKY,GAAME,EAAKd,EAAO,GAAK,IAChD,EAAK,CAACW,EACN,EAAK,CAACC,GAGJxC,IAAM,IACR,EAAKyC,EAAKb,EAAO,EAAI,GAAK,EAC1B,EAAK,GAEP,IAAMe,EACJJ,EAAK,IAAO,OAASA,EAAK,KAAQ,QAAU,SACxCK,EACJJ,EAAK,IAAO,MAAQA,EAAK,KAAQ,SAAW,SAC9C,EAAI,KAAOT,EACX,EAAI,UAAYY,EAChB,EAAI,aAAeC,EACnB,IACMC,EAAS5D,EAAK,MAAM,KAAK,EAAM,IAAM,CACzC,IAAM6D,EAAIxE,EAAAA,QAAQe,EAAO,GAAGG,EAAGQ,EAAG3B,EAAAA,QAAQY,EAAM8D,CAAI,EAAGd,CAAI,CAAC,EACtDe,EAAO/D,EAAK,OAAOE,IAAM,GACzB0B,EAAIL,EAAI,YAAYwC,CAAI,CAAC,CAAC,MAC1BC,EAAIH,EAAE,EAAIP,EAAK,EACfW,EAAIJ,EAAE,EAAIN,EAAK,EAQrB,MAAO,CAAE,KAAMM,EAAG,OAAM,IAAG,IAAG,KAAM,CAAE,EAP3BH,IAAU,OAASM,EAAIN,IAAU,QAAUM,EAAIpC,EAAIoC,EAAIpC,EAAI,EAOzB,EAL3C+B,IAAa,MACTM,EACAN,IAAa,SACXM,EAAIpB,EACJoB,EAAIpB,EAAO,EACiC,IAAG,EAAGA,CAAK,CAAE,CACnE,CAAC,EAEGqB,EAAO,EACX,KAAOA,EAAON,EAAO,OAAQ,IAAQ,CACnC,IAAMO,EAAOP,EAAO,QAAQ,EAAG,IAAM1D,EAAIgE,IAAS,CAAC,EACnD,GACEC,EAAK,OACF,EAAG,IAAM,CAACA,EAAK,MAAM,EAAGjE,CAAC,CAAC,CAAC,KAAM,GAAM+B,EAASmC,EAAE,KAAMC,EAAE,IAAI,CAAC,CAClE,EAEA,KAEJ,CACA,IAAIC,EAAS,EAuBb,GAtBA/C,EAAI,UAAU,EACdqC,EAAO,SAAS,EAAG,IAAM,CACnB1D,EAAIgE,GAGJtB,EAAO,KAAM,GAAMX,EAASmC,EAAE,KAAMC,EAAGlC,CAAc,CAAC,IAC1DS,EAAO,KAAKwB,EAAE,IAAI,EAClB7C,EAAI,OAAO6C,EAAE,KAAK,EAAGA,EAAE,KAAK,CAAC,EAC7B7C,EAAI,OAAO6C,EAAE,KAAK,EAAId,EAAK,EAAGc,EAAE,KAAK,EAAIb,EAAK,CAAC,EAC/C,EAAS,KAAK,IACZe,EACA,KAAK,IAAIhB,CAAE,EAAIc,EAAE,KAAK,EAAI,KAAK,IAAIb,CAAE,EAAIa,EAAE,KAAK,CAClD,EACF,CAAC,EACD,EAAI,YAAc3C,EAAM,KACxB,EAAI,UAAY,EAChBF,EAAI,OAAO,EACX,EAAI,UAAYE,EAAM,MACtBmC,EAAO,SAAS,EAAG,IAAM,CACnB1D,EAAIgE,GAAQ,CAACtB,EAAO,SAASwB,EAAE,IAAI,GACvC7C,EAAI,SAAS6C,EAAE,KAAMA,EAAE,EAAGA,EAAE,CAAC,CAC/B,CAAC,EACG,CAACpE,EAAK,MAAO,SACjB,IAAMuE,EAAW,EAAMD,EAAS,GAC5BE,EAAKhB,EAAKF,EAAKiB,EACfE,EAAKhB,EAAKF,EAAKgB,EACnB,EAAI,KAAOxB,EACX,EAAI,UAAYtB,EAAM,KACtB,IAAMiD,EAAKnD,EAAI,YAAYvB,EAAK,KAAK,CAAC,CAAC,MACjC2E,EAAK9B,EAAO,EAClB,GAAI9B,IAAM,EACR,EAAK,KAAK,IAAI6D,EAAO,KAAOD,EAAI,KAAK,IAAIC,EAAO,MAAQD,EAAIH,CAAE,CAAC,EAC/D,EAAK,KAAK,IAAII,EAAO,IAAMF,EAAK,EAAG,KAAK,IAAIE,EAAO,OAASF,EAAK,EAAGD,CAAE,CAAC,EACvElD,EAAI,KAAK,EACTA,EAAI,UAAUiD,EAAIC,CAAE,EACpBlD,EAAI,OAAO,CAAC,KAAK,GAAK,CAAC,EACvB,EAAI,UAAY,SAChB,EAAI,aAAe+B,EAAK,EAAI,SAAW,MACvC/B,EAAI,SAASvB,EAAK,MAAO,EAAG,CAAC,EAC7BuB,EAAI,QAAQ,MACP,CACL,IAAMsD,GAAU,EAAW,KAAqB,CAC9C,EAAGnB,IAAU,OAASM,EAAIN,IAAU,QAAUM,EAAIU,EAAKV,EAAIU,EAAK,EAChE,EAAGf,IAAa,MAAQM,EAAIN,IAAa,SAAWM,EAAIU,EAAKV,EAAIU,EAAK,EACtE,EAAGD,EACH,EAAGC,CACL,GAEA,IACE,IAAIG,EAAI,EACRA,EAAI,IAAMlC,EAAO,KAAM,GAAMX,EAAS4C,EAAOL,EAAIC,CAAE,EAAGJ,CAAC,CAAC,EACxD,IAEA,GAAMf,EAAK,EACX,GAAMC,EAAK,EAGb,GAAM,CAAE,EAAGhB,EAAM,EAAGwC,GAAQF,EAAOL,EAAIC,CAAE,EACzC,GACE,KAAK,IAAI,EAAGG,EAAO,KAAOrC,CAAI,EAAI,KAAK,IAAI,EAAGA,EAAOmC,EAAKE,EAAO,KAAK,EACxE,GACE,KAAK,IAAI,EAAGA,EAAO,IAAMG,CAAG,EAAI,KAAK,IAAI,EAAGA,EAAMJ,EAAKC,EAAO,MAAM,EACtEhC,EAAO,KAAKiC,EAAOL,EAAIC,CAAE,CAAC,EAC1B,EAAI,UAAYf,EAChB,EAAI,aAAeC,EACnBpC,EAAI,SAASvB,EAAK,MAAOwE,EAAIC,CAAE,CACjC,CACF,CACF,EAGaO,GACX,EACA,EACA,EACA,EACA,EACA,EACA,EAEA,EAAQ,MACC,CACT,IAAMC,EAAW1D,EAAI,qBAAqB,EAAG2D,EAAK,EAAIA,EAAK,EAAG,EAAGA,EAAK,CAAC,EAEvE,IAAK,IAAIhF,EAAI,EAAGA,GAAK,GAAS,IAAK,CACjC,IAAMe,EAAIkE,EAAI,KAAK,KAAK,MAAOjF,EAAI,IAAYiF,EAAI,KAAK,OAAS,EAAE,GAC/DlE,GAAGgE,EAAS,aAAa/E,EAAI,GAASf,EAAAA,MAAM8B,CAAC,CAAC,CACpD,CACA,EAAI,UAAYgE,EAChB1D,EAAI,UAAU,EACV,OAAOA,EAAI,WAAc,WAC3BA,EAAI,UAAU2D,EAAK,EAAGA,EAAK,EAAGA,EAAK,EAAGA,EAAK,EAAG,CAAC,EAC5C3D,EAAI,KAAK2D,EAAK,EAAGA,EAAK,EAAGA,EAAK,EAAGA,EAAK,CAAC,EAC5C3D,EAAI,KAAK,EACT,EAAI,YAAcE,EAAM,KACxB,EAAI,UAAY,EAChBF,EAAI,OAAO,EACX,GAAM,CAAC6D,EAAKC,GAAOC,EACbC,EAAOF,EAAMD,GAAO,EAC1B,EAAI,KAAO,GAAG3D,EAAM,SAAS,KAAKA,EAAM,OACxC,EAAI,UAAYA,EAAM,MACtB,EAAI,UAAY,OAChB,EAAI,aAAe,SACnB,IAAM+D,EAAQlG,EAAAA,YAAY8F,EAAKC,EAAK,KAAK,IAAI,EAAG,KAAK,MAAMH,EAAK,EAAI,EAAE,CAAC,CAAC,EACxE3D,EAAI,UAAU,EACd,IAAK,IAAMkE,KAAKD,EAAO,CACrB,IAAMvB,EAAIiB,EAAK,EAAIA,EAAK,GAAMO,EAAIL,GAAOG,EAAQL,EAAK,EAClDjB,EAAIiB,EAAK,EAAI,IAAOjB,EAAIiB,EAAK,EAAIA,EAAK,EAAI,KAC9C3D,EAAI,OAAO2D,EAAK,EAAIA,EAAK,EAAGjB,CAAC,EAC7B1C,EAAI,OAAO2D,EAAK,EAAIA,EAAK,EAAI,EAAGjB,CAAC,EACjC1C,EAAI,SAASmE,EAAOD,CAAC,EAAGP,EAAK,EAAIA,EAAK,EAAI,EAAGjB,CAAC,EAChD,CAGA,GAFA,EAAI,YAAcxC,EAAM,KACxBF,EAAI,OAAO,EACPoE,EAAO,CACT,EAAI,KAAO,OAAOlE,EAAM,SAAS,KAAKA,EAAM,OAC5C,EAAI,UAAYA,EAAM,KACtB,EAAI,UAAY,OAChB,EAAI,aAAe,SACnB,IAAMG,EAAIL,EAAI,YAAYoE,CAAK,CAAC,CAAC,MACjCpE,EAAI,SACFoE,EACA,KAAK,IAAI,EAAG,KAAK,IAAIT,EAAK,EAAGU,EAAQ,EAAIhE,CAAC,CAAC,EAC3CsD,EAAK,EAAI,CACX,CACF,CACF,EAQaW,GACX,EACA,EACA,EACA,EACA,IACS,CAET,GADAtE,EAAI,KAAK,EACLuE,EAAM,OAAS,QACjBvE,EAAI,UAAU,EACdA,EAAI,IAAIuE,EAAM,EAAGA,EAAM,EAAGA,EAAM,OAAS,EAAG,EAAG,KAAK,GAAK,CAAC,EAC1D,EAAI,UAAY,EAChB,EAAI,YAAcrE,EAAM,WACxBF,EAAI,OAAO,EACX,EAAI,UAAY,IAChB,EAAI,YAAcE,EAAM,KACxBF,EAAI,OAAO,OACN,GAAIuE,EAAM,OAAS,MAAO,CAC/B,GAAM,CAAE,MAAK,OAAQA,EACf3E,EAAU,GACd9B,EAAAA,QACEe,EACAF,EAAI,EAAImF,EAAI,GAAKD,EAAI,GACrBlF,EAAI,EAAImF,EAAI,GAAKD,EAAI,GACrBlF,EAAI,EAAImF,EAAI,GAAKD,EAAI,EACvB,EACIW,EAAM,MAAM,KAAK,CAAE,OAAQ,CAAE,GAAI,EAAG,IAAM5E,EAAOjB,CAAC,CAAC,EACzDqB,EAAI,UAAU,EACd,IAAK,IAAIrB,EAAI,EAAGA,EAAI,EAAG,IACrB,IAAK,IAAM8F,IAAO,CAAC,EAAG,EAAG,CAAC,EAAG,CAC3B,GAAI9F,EAAI8F,EAAK,SACb,IAAMjF,EAAIgF,EAAI7F,GACRc,EAAI+E,EAAI7F,EAAI8F,GAClBzE,EAAI,OAAOR,EAAE,EAAGA,EAAE,CAAC,EACnBQ,EAAI,OAAOP,EAAE,EAAGA,EAAE,CAAC,CACrB,CAEF,EAAI,SAAW,QACf,EAAI,UAAY,IAChB,EAAI,YAAcS,EAAM,WACxBF,EAAI,OAAO,EACX,EAAI,UAAY,IAChB,EAAI,YAAcE,EAAM,KACxBF,EAAI,OAAO,CACb,KAAO,CACL,GAAM,CAACyC,EAAGC,EAAGgC,GAAKH,EAAM,MAClBf,EAAM1F,EAAAA,QAAQe,EAAO4D,EAAGC,EAAGgC,CAAC,EAC5BC,EAAQ7G,EAAAA,QAAQe,EAAO4D,EAAGC,EAAG,CAAC3D,EAAK,EAAE,IAAI,EAC/CiB,EAAI,UAAU,EACdA,EAAI,YAAY,CAAC,EAAG,CAAC,CAAC,EACtBA,EAAI,OAAOwD,EAAI,EAAGA,EAAI,CAAC,EACvBxD,EAAI,OAAO2E,EAAM,EAAGA,EAAM,CAAC,EAC3B,EAAI,UAAY,EAChB,EAAI,YAAczE,EAAM,KACxBF,EAAI,OAAO,EACXA,EAAI,YAAY,CAAC,CAAC,EAClBA,EAAI,UAAU,EACdA,EAAI,IAAI2E,EAAM,EAAGA,EAAM,EAAG,IAAK,EAAG,KAAK,GAAK,CAAC,EAC7C,EAAI,UAAYzE,EAAM,KACtBF,EAAI,KAAK,EACTA,EAAI,UAAU,EACdA,EAAI,IAAIwD,EAAI,EAAGA,EAAI,EAAG,EAAG,EAAG,KAAK,GAAK,CAAC,EACvC,EAAI,UAAYe,EAAM,MACtBvE,EAAI,KAAK,EACT,EAAI,UAAY,EAChB,EAAI,YAAcE,EAAM,WACxBF,EAAI,OAAO,EACXA,EAAI,UAAU,EACdA,EAAI,IAAIwD,EAAI,EAAGA,EAAI,EAAG,EAAG,EAAG,KAAK,GAAK,CAAC,EACvC,EAAI,UAAY,KAChB,EAAI,YAActD,EAAM,KACxBF,EAAI,OAAO,CACb,CACAA,EAAI,QAAQ,CACd"}
|
|
1
|
+
{"version":3,"file":"overlay.cjs","names":["toCss","toWorld","project","linearTicks","require_scale","require_color","require_model","require_math","overlayStyle","theme","dark","AXES","gridPositions","axis","out","i","farWalls","frame","half","axes","at","p","rest","value","drawWalls","h","walls","P","a","b","c","base","corner","sb","sc","quad","ctx","q","style","along","across","w","p0","p1","s0","s1","overlaps","pad","CROSS_AXIS_GAP","labelEdge","eye","best","left","sx","sy","drawAxes","center","placed","size","labelFont","titleFont","edge","e0","e1","dx","dy","length","ox","oy","mx","my","align","baseline","labels","s","tick","text","x","y","step","kept","l","o","extent","distance","tx","ty","tw","th","bounds","rectAt","k","top","drawColorBar","gradient","rect","lut","min","max","domain","span","ticks","t","format","title","right","drawHighlight","shape","pts","bit","z","floor"],"sources":["../../../../../charts/src/engine/three-d/overlay.ts"],"sourcesContent":["// 2D overlay of the 3D charts: the far walls of the axis box with their grid\n// lines (below the data), then the tick labels, axis titles, color bar and\n// hover highlight (above the data). Text is always horizontal (screen\n// aligned) so it stays readable whatever the camera.\n\nimport { toCss, type Rgba } from \"../color\";\nimport { linearTicks } from \"../scale\";\nimport type { ColorLut } from \"./colormap\";\nimport { project, type CameraFrame, type Vec3 } from \"./math\";\nimport { toWorld, type AxisModel, type Model3D } from \"./model\";\n\nexport interface OverlayStyle {\n text: string;\n muted: string;\n grid: string;\n axis: string;\n /** Tint of the walls */\n wall: string;\n /** Background (rings of the hover marker) */\n background: string;\n font: string;\n fontSize: number;\n}\n\nexport const overlayStyle = (\n theme: {\n text: Rgba;\n textMuted: Rgba;\n grid: Rgba;\n axis: Rgba;\n background: Rgba;\n fontFamily: string;\n fontSize: number;\n },\n dark: boolean,\n): OverlayStyle => ({\n text: toCss(theme.text),\n muted: toCss(theme.textMuted),\n grid: toCss(theme.grid),\n axis: toCss(theme.axis),\n wall: toCss(theme.text, dark ? 0.045 : 0.025),\n background: toCss(theme.background),\n font: theme.fontFamily,\n fontSize: theme.fontSize,\n});\n\ntype Axes = Pick<Model3D, \"x\" | \"y\" | \"z\">;\n\nconst AXES: readonly (\"x\" | \"y\" | \"z\")[] = [\"x\", \"y\", \"z\"];\n\n/** World positions of the grid lines of an axis (category axes: between the categories) */\nexport const gridPositions = (axis: AxisModel): number[] => {\n if (axis.categories) {\n const out: number[] = [];\n for (let i = 0; i < axis.categories.length - 1; i++)\n out.push(toWorld(axis, i + 0.5));\n return out;\n }\n return axis.ticks\n .filter((t) => t > axis.domain[0] && t < axis.domain[1])\n .map((t) => toWorld(axis, t));\n};\n\n/** Side (-1 / 1) of the far wall perpendicular to every axis */\nexport const farWalls = (frame: CameraFrame): [number, number, number] => [\n frame.eye[0] >= 0 ? -1 : 1,\n frame.eye[1] >= 0 ? -1 : 1,\n frame.eye[2] >= 0 ? -1 : 1,\n];\n\nconst half = (axes: Axes): Vec3 => [axes.x.half, axes.y.half, axes.z.half];\n\nconst at = (axis: number, value: number, rest: Vec3): Vec3 => {\n const p: Vec3 = [rest[0], rest[1], rest[2]];\n p[axis] = value;\n return p;\n};\n\n/** Far walls, their grid lines and borders */\nexport const drawWalls = (\n ctx: CanvasRenderingContext2D,\n frame: CameraFrame,\n axes: Axes,\n style: OverlayStyle,\n): void => {\n const h = half(axes);\n const walls = farWalls(frame);\n const P = (p: Vec3) => project(frame, p[0], p[1], p[2]);\n ctx.lineWidth = 1;\n for (let a = 0; a < 3; a++) {\n const b = (a + 1) % 3;\n const c = (a + 2) % 3;\n const base: Vec3 = [0, 0, 0];\n base[a] = walls[a] * h[a];\n const corner = (sb: number, sc: number) => {\n const p: Vec3 = [base[0], base[1], base[2]];\n p[b] = sb * h[b];\n p[c] = sc * h[c];\n return P(p);\n };\n const quad = [corner(-1, -1), corner(1, -1), corner(1, 1), corner(-1, 1)];\n ctx.beginPath();\n quad.forEach((q, i) => (i ? ctx.lineTo(q.x, q.y) : ctx.moveTo(q.x, q.y)));\n ctx.closePath();\n ctx.fillStyle = style.wall;\n ctx.fill();\n // grid lines of the two axes spanning the wall\n ctx.beginPath();\n for (const [along, across] of [\n [b, c],\n [c, b],\n ] as const) {\n const axis = axes[AXES[along]!];\n if (!axis.grid) continue;\n for (const w of gridPositions(axis)) {\n const p0 = at(along, w, base);\n const p1 = at(along, w, base);\n p0[across] = -h[across];\n p1[across] = h[across];\n const s0 = P(p0);\n const s1 = P(p1);\n ctx.moveTo(s0.x, s0.y);\n ctx.lineTo(s1.x, s1.y);\n }\n }\n ctx.strokeStyle = style.grid;\n ctx.stroke();\n ctx.beginPath();\n quad.forEach((q, i) => (i ? ctx.lineTo(q.x, q.y) : ctx.moveTo(q.x, q.y)));\n ctx.closePath();\n ctx.strokeStyle = style.axis;\n ctx.stroke();\n }\n};\n\ninterface Rect {\n x: number;\n y: number;\n w: number;\n h: number;\n}\n\nconst overlaps = (a: Rect, b: Rect, pad = 2) =>\n a.x < b.x + b.w + pad &&\n b.x < a.x + a.w + pad &&\n a.y < b.y + b.h + pad &&\n b.y < a.y + a.h + pad;\n\n/** Smallest gap (px) between tick labels of different axes */\nconst CROSS_AXIS_GAP = 10;\n\n/** Edge (fixed coordinates of the two other axes) carrying the labels of an axis */\nexport const labelEdge = (\n frame: CameraFrame,\n axes: Axes,\n axis: number,\n): Vec3 => {\n const h = half(axes);\n const eye = frame.eye;\n if (axis === 0) return [0, (eye[1] >= 0 ? 1 : -1) * h[1], -h[2]];\n if (axis === 1) return [(eye[0] >= 0 ? 1 : -1) * h[0], 0, -h[2]];\n // vertical axis: the leftmost vertical edge on screen\n let best: Vec3 = [-h[0], -h[1], 0];\n let left = Infinity;\n for (const sx of [-1, 1]) {\n for (const sy of [-1, 1]) {\n const p = project(frame, sx * h[0], sy * h[1], 0);\n if (p.x < left) {\n left = p.x;\n best = [sx * h[0], sy * h[1], 0];\n }\n }\n }\n return best;\n};\n\n/** Area the labels and titles must stay in (CSS pixels of the stage) */\nexport interface OverlayBounds {\n left: number;\n top: number;\n right: number;\n bottom: number;\n}\n\n/** Tick labels and titles of the three axes */\nexport const drawAxes = (\n ctx: CanvasRenderingContext2D,\n frame: CameraFrame,\n axes: Axes,\n style: OverlayStyle,\n bounds: OverlayBounds = {\n left: 0,\n top: 0,\n right: frame.width,\n bottom: frame.height,\n },\n): void => {\n const center = project(frame, 0, 0, 0);\n const placed: Rect[] = [];\n const size = style.fontSize;\n const labelFont = `${size}px ${style.font}`;\n const titleFont = `600 ${size + 1}px ${style.font}`;\n for (let a = 0; a < 3; a++) {\n const axis = axes[AXES[a]!];\n if (!axis.visible) continue;\n const edge = labelEdge(frame, axes, a);\n const e0 = project(frame, ...at(a, -axis.half, edge));\n const e1 = project(frame, ...at(a, axis.half, edge));\n let dx = e1.x - e0.x;\n let dy = e1.y - e0.y;\n const length = Math.hypot(dx, dy);\n // seen end-on: no room for labels\n if (length < 24) continue;\n dx /= length;\n dy /= length;\n let ox = -dy;\n let oy = dx;\n const mx = (e0.x + e1.x) / 2;\n const my = (e0.y + e1.y) / 2;\n if (ox * (mx - center.x) + oy * (my - center.y) < 0) {\n ox = -ox;\n oy = -oy;\n }\n // vertical axis: labels go sideways\n if (a === 2) {\n ox = mx < center.x ? -1 : 1;\n oy = 0;\n }\n const align: CanvasTextAlign =\n ox > 0.35 ? \"left\" : ox < -0.35 ? \"right\" : \"center\";\n const baseline: CanvasTextBaseline =\n oy > 0.35 ? \"top\" : oy < -0.35 ? \"bottom\" : \"middle\";\n ctx.font = labelFont;\n ctx.textAlign = align;\n ctx.textBaseline = baseline;\n const gap = 8;\n const labels = axis.ticks.map((tick, i) => {\n const s = project(frame, ...at(a, toWorld(axis, tick), edge));\n const text = axis.labels[i] ?? \"\";\n const w = ctx.measureText(text).width;\n const x = s.x + ox * gap;\n const y = s.y + oy * gap;\n const rx = align === \"left\" ? x : align === \"right\" ? x - w : x - w / 2;\n const ry =\n baseline === \"top\"\n ? y\n : baseline === \"bottom\"\n ? y - size\n : y - size / 2;\n return { tick: s, text, x, y, rect: { x: rx, y: ry, w, h: size } };\n });\n // thin the labels until they do not overlap\n let step = 1;\n for (; step < labels.length; step++) {\n const kept = labels.filter((_, i) => i % step === 0);\n if (\n kept.every(\n (l, i) => !kept.slice(0, i).some((o) => overlaps(l.rect, o.rect)),\n )\n ) {\n break;\n }\n }\n let extent = 0;\n ctx.beginPath();\n labels.forEach((l, i) => {\n if (i % step) return;\n // wider gap to the labels of the other axes: at the shared corner, two\n // labels side by side would read as one (\"-2 -2\")\n if (placed.some((o) => overlaps(l.rect, o, CROSS_AXIS_GAP))) return;\n placed.push(l.rect);\n ctx.moveTo(l.tick.x, l.tick.y);\n ctx.lineTo(l.tick.x + ox * 4, l.tick.y + oy * 4);\n extent = Math.max(\n extent,\n Math.abs(ox) * l.rect.w + Math.abs(oy) * l.rect.h,\n );\n });\n ctx.strokeStyle = style.axis;\n ctx.lineWidth = 1;\n ctx.stroke();\n ctx.fillStyle = style.muted;\n labels.forEach((l, i) => {\n if (i % step || !placed.includes(l.rect)) return;\n ctx.fillText(l.text, l.x, l.y);\n });\n if (!axis.title) continue;\n const distance = gap + extent + 10;\n let tx = mx + ox * distance;\n let ty = my + oy * distance;\n ctx.font = titleFont;\n ctx.fillStyle = style.text;\n const tw = ctx.measureText(axis.title).width;\n const th = size + 1;\n if (a === 2) {\n tx = Math.max(bounds.left + th, Math.min(bounds.right - th, tx));\n ty = Math.max(bounds.top + tw / 2, Math.min(bounds.bottom - tw / 2, ty));\n ctx.save();\n ctx.translate(tx, ty);\n ctx.rotate(-Math.PI / 2);\n ctx.textAlign = \"center\";\n ctx.textBaseline = ox < 0 ? \"bottom\" : \"top\";\n ctx.fillText(axis.title, 0, 0);\n ctx.restore();\n } else {\n const rectAt = (x: number, y: number): Rect => ({\n x: align === \"left\" ? x : align === \"right\" ? x - tw : x - tw / 2,\n y: baseline === \"top\" ? y : baseline === \"bottom\" ? y - th : y - th / 2,\n w: tw,\n h: th,\n });\n // slanted axes: long titles move out until they clear the labels\n for (\n let k = 0;\n k < 12 && placed.some((o) => overlaps(rectAt(tx, ty), o));\n k++\n ) {\n tx += ox * 3;\n ty += oy * 3;\n }\n // keep the whole title inside the bounds\n const { x: left, y: top } = rectAt(tx, ty);\n tx +=\n Math.max(0, bounds.left - left) - Math.max(0, left + tw - bounds.right);\n ty +=\n Math.max(0, bounds.top - top) - Math.max(0, top + th - bounds.bottom);\n placed.push(rectAt(tx, ty));\n ctx.textAlign = align;\n ctx.textBaseline = baseline;\n ctx.fillText(axis.title, tx, ty);\n }\n }\n};\n\n/** Vertical color bar with its ticks and title */\nexport const drawColorBar = (\n ctx: CanvasRenderingContext2D,\n rect: Rect,\n lut: ColorLut,\n domain: readonly [number, number],\n format: (value: number) => string,\n title: string,\n style: OverlayStyle,\n /** Right end of the stage: the title is shifted left to fit */\n right = Infinity,\n): void => {\n const gradient = ctx.createLinearGradient(0, rect.y + rect.h, 0, rect.y);\n const samples = 24;\n for (let i = 0; i <= samples; i++) {\n const c = lut.rgba[Math.round((i / samples) * (lut.rgba.length - 1))];\n if (c) gradient.addColorStop(i / samples, toCss(c));\n }\n ctx.fillStyle = gradient;\n ctx.beginPath();\n if (typeof ctx.roundRect === \"function\")\n ctx.roundRect(rect.x, rect.y, rect.w, rect.h, 3);\n else ctx.rect(rect.x, rect.y, rect.w, rect.h);\n ctx.fill();\n ctx.strokeStyle = style.axis;\n ctx.lineWidth = 1;\n ctx.stroke();\n const [min, max] = domain;\n const span = max - min || 1;\n ctx.font = `${style.fontSize}px ${style.font}`;\n ctx.fillStyle = style.muted;\n ctx.textAlign = \"left\";\n ctx.textBaseline = \"middle\";\n const ticks = linearTicks(min, max, Math.max(2, Math.round(rect.h / 40)));\n ctx.beginPath();\n for (const t of ticks) {\n const y = rect.y + rect.h - ((t - min) / span) * rect.h;\n if (y < rect.y - 0.5 || y > rect.y + rect.h + 0.5) continue;\n ctx.moveTo(rect.x + rect.w, y);\n ctx.lineTo(rect.x + rect.w + 4, y);\n ctx.fillText(format(t), rect.x + rect.w + 7, y);\n }\n ctx.strokeStyle = style.axis;\n ctx.stroke();\n if (title) {\n ctx.font = `600 ${style.fontSize}px ${style.font}`;\n ctx.fillStyle = style.text;\n ctx.textAlign = \"left\";\n ctx.textBaseline = \"bottom\";\n const w = ctx.measureText(title).width;\n ctx.fillText(\n title,\n Math.max(4, Math.min(rect.x, right - 4 - w)),\n rect.y - 8,\n );\n }\n};\n\n/** Highlight of the hovered item */\nexport type HighlightShape =\n | { kind: \"point\"; x: number; y: number; radius: number; color: string }\n | { kind: \"box\"; min: Vec3; max: Vec3; color: string }\n | { kind: \"vertex\"; world: Vec3; color: string };\n\nexport const drawHighlight = (\n ctx: CanvasRenderingContext2D,\n frame: CameraFrame,\n axes: Axes,\n shape: HighlightShape,\n style: OverlayStyle,\n): void => {\n ctx.save();\n if (shape.kind === \"point\") {\n ctx.beginPath();\n ctx.arc(shape.x, shape.y, shape.radius + 3, 0, Math.PI * 2);\n ctx.lineWidth = 3;\n ctx.strokeStyle = style.background;\n ctx.stroke();\n ctx.lineWidth = 1.5;\n ctx.strokeStyle = style.text;\n ctx.stroke();\n } else if (shape.kind === \"box\") {\n const { min, max } = shape;\n const corner = (i: number) =>\n project(\n frame,\n i & 1 ? max[0] : min[0],\n i & 2 ? max[1] : min[1],\n i & 4 ? max[2] : min[2],\n );\n const pts = Array.from({ length: 8 }, (_, i) => corner(i));\n ctx.beginPath();\n for (let i = 0; i < 8; i++) {\n for (const bit of [1, 2, 4]) {\n if (i & bit) continue;\n const a = pts[i]!;\n const b = pts[i | bit]!;\n ctx.moveTo(a.x, a.y);\n ctx.lineTo(b.x, b.y);\n }\n }\n ctx.lineJoin = \"round\";\n ctx.lineWidth = 3.5;\n ctx.strokeStyle = style.background;\n ctx.stroke();\n ctx.lineWidth = 1.5;\n ctx.strokeStyle = style.text;\n ctx.stroke();\n } else {\n const [x, y, z] = shape.world;\n const top = project(frame, x, y, z);\n const floor = project(frame, x, y, -axes.z.half);\n ctx.beginPath();\n ctx.setLineDash([3, 3]);\n ctx.moveTo(top.x, top.y);\n ctx.lineTo(floor.x, floor.y);\n ctx.lineWidth = 1;\n ctx.strokeStyle = style.text;\n ctx.stroke();\n ctx.setLineDash([]);\n ctx.beginPath();\n ctx.arc(floor.x, floor.y, 2.5, 0, Math.PI * 2);\n ctx.fillStyle = style.text;\n ctx.fill();\n ctx.beginPath();\n ctx.arc(top.x, top.y, 5, 0, Math.PI * 2);\n ctx.fillStyle = shape.color;\n ctx.fill();\n ctx.lineWidth = 2;\n ctx.strokeStyle = style.background;\n ctx.stroke();\n ctx.beginPath();\n ctx.arc(top.x, top.y, 7, 0, Math.PI * 2);\n ctx.lineWidth = 1.25;\n ctx.strokeStyle = style.text;\n ctx.stroke();\n }\n ctx.restore();\n};\n"],"mappings":"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"}
|
|
@@ -1,17 +1,17 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { linearTicks as e } from "../scale.js";
|
|
2
|
+
import { toCss as t } from "../color.js";
|
|
3
3
|
import { toWorld as n } from "./model.js";
|
|
4
4
|
import { project as r } from "./math.js";
|
|
5
5
|
//#region src/engine/three-d/overlay.ts
|
|
6
|
-
var i = (
|
|
7
|
-
text: e
|
|
8
|
-
muted: e
|
|
9
|
-
grid: e
|
|
10
|
-
axis: e
|
|
11
|
-
wall: e
|
|
12
|
-
background: e
|
|
13
|
-
font:
|
|
14
|
-
fontSize:
|
|
6
|
+
var i = (e, n) => ({
|
|
7
|
+
text: t(e.text),
|
|
8
|
+
muted: t(e.textMuted),
|
|
9
|
+
grid: t(e.grid),
|
|
10
|
+
axis: t(e.axis),
|
|
11
|
+
wall: t(e.text, n ? .045 : .025),
|
|
12
|
+
background: t(e.background),
|
|
13
|
+
font: e.fontFamily,
|
|
14
|
+
fontSize: e.fontSize
|
|
15
15
|
}), a = [
|
|
16
16
|
"x",
|
|
17
17
|
"y",
|
|
@@ -159,14 +159,14 @@ var i = (t, n) => ({
|
|
|
159
159
|
}
|
|
160
160
|
}, h = (n, r, i, a, o, s, c, l = Infinity) => {
|
|
161
161
|
let u = n.createLinearGradient(0, r.y + r.h, 0, r.y);
|
|
162
|
-
for (let
|
|
163
|
-
let n = i.rgba[Math.round(
|
|
164
|
-
n && u.addColorStop(
|
|
162
|
+
for (let e = 0; e <= 24; e++) {
|
|
163
|
+
let n = i.rgba[Math.round(e / 24 * (i.rgba.length - 1))];
|
|
164
|
+
n && u.addColorStop(e / 24, t(n));
|
|
165
165
|
}
|
|
166
166
|
n.fillStyle = u, n.beginPath(), typeof n.roundRect == "function" ? n.roundRect(r.x, r.y, r.w, r.h, 3) : n.rect(r.x, r.y, r.w, r.h), n.fill(), n.strokeStyle = c.axis, n.lineWidth = 1, n.stroke();
|
|
167
167
|
let [d, f] = a, p = f - d || 1;
|
|
168
168
|
n.font = `${c.fontSize}px ${c.font}`, n.fillStyle = c.muted, n.textAlign = "left", n.textBaseline = "middle";
|
|
169
|
-
let m =
|
|
169
|
+
let m = e(d, f, Math.max(2, Math.round(r.h / 40)));
|
|
170
170
|
n.beginPath();
|
|
171
171
|
for (let e of m) {
|
|
172
172
|
let t = r.y + r.h - (e - d) / p * r.h;
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
var e={side:[`right`,`left`,`bottom`,`top`],vertical:[`top`,`bottom`,`right`,`left`],right:[`right`,`left`,`bottom`,`top`],left:[`left`,`right`,`bottom`,`top`],top:[`top`,`bottom`,`right`,`left`],bottom:[`bottom`,`top`,`right`,`left`]},t=(e,t)=>({x:e.x+t,y:e.y+t,width:Math.max(0,e.width-t*2),height:Math.max(0,e.height-t*2)}),n=(e,t)=>{let n=Math.max(e.x,t.x),r=Math.max(e.y,t.y);return{x:n,y:r,width:Math.max(0,Math.min(e.x+e.width,t.x+t.width)-n),height:Math.max(0,Math.min(e.y+e.height,t.y+t.height)-r)}},r=(e,t)=>{if(!e)return{x:t.x,y:t.y,width:t.width,height:t.height};let n=Math.min(e.x,t.x),r=Math.min(e.y,t.y);return{x:n,y:r,width:Math.max(e.x+e.width,t.x+t.width)-n,height:Math.max(e.y+e.height,t.y+t.height)-r}},i=(e,t)=>{let r=n(e,t);return r.width*r.height},a=(e,t,n,r)=>Math.max(n,Math.min(e,r-t)),o=(e,t,n,r,i)=>t>=e.x-.5&&n>=e.y-.5&&t+r<=e.x+e.width+.5&&n+i<=e.y+e.height+.5,s=s=>{let{anchor:c,prefer:l=`side`,gap:u=12,margin:d=4}=s,{width:f,height:p}=s.size,m={x:c.x,y:c.y,width:0,height:0},h=r(s.avoid??null,m),g={x:0,y:0,width:s.stage.width,height:s.stage.height},_=s.bounds&&s.bounds.width>0&&s.bounds.height>0?t(s.bounds,d):null,v=t(g,d),y=_?n(v,_):v,b=y.width>0&&y.height>0?y:v,x=e[l],S=(e,t)=>{let n,r;if(e===`right`||e===`left`){n=e===`right`?h.x+h.width+u:h.x-u-f,r=c.y-p/2;for(let e of t)r=a(r,p,e.y,e.y+e.height)}else{r=e===`bottom`?h.y+h.height+u:h.y-u-p,n=c.x-f/2;for(let e of t)n=a(n,f,e.x,e.x+e.width)}return{x:n,y:r}};for(let e of x){let t=S(e,[b]);if(o(b,t.x,t.y,f,p))return{...t,fit:`stage`}}if(_){let e=null,t=1/0;for(let n of x){let r=S(n,[b,_]);if(!o(_,r.x,r.y,f,p))continue;let a=i({x:r.x,y:r.y,width:f,height:p},g);a<t-.5&&(e=r,t=a)}if(e)return{...e,fit:`bounds`}}let C={x:b.x,y:b.y},w=1/0;for(let e of _?[b,_]:[b]){let t=[...x.map(t=>S(t,[e])),{x:e.x,y:e.y},{x:e.x+e.width-f,y:e.y},{x:e.x,y:e.y+e.height-p},{x:e.x+e.width-f,y:e.y+e.height-p}];for(let n of t){let t=a(n.x,f,e.x,e.x+e.width),r=a(n.y,p,e.y,e.y+e.height),o=i({x:t,y:r,width:f,height:p},h);o<w-.5&&(C={x:t,y:r},w=o)}}return{...C,fit:`none`}},c=`var(--surface-elevated-color, var(--background-color, #fff))`,l=`color-mix(in srgb, ${c} 80%, transparent)`,u=`blur(12px) saturate(160%)`,d=`transform 80ms ease-out`,f=.86,p={position:`absolute`,left:`0`,top:`0`,right:`auto`,bottom:`auto`,margin:`0`,overflow:`visible`,pointerEvents:`none`,border:`1px solid color-mix(in srgb, var(--border-color, #e5e7eb) 70%, transparent)`,background:c,color:`var(--text-color, #1f2937)`,boxShadow:`0 12px 32px -8px rgba(15, 23, 42, 0.2), 0 4px 10px -4px rgba(15, 23, 42, 0.1), inset 0 1px 0 rgba(255, 255, 255, 0.06)`},m=(e,t={})=>(Object.assign(e.style,p,t),e.style.background=l,e.style.backdropFilter=u,e.style.setProperty(`-webkit-backdrop-filter`,u),e),h=e=>{let t=e?.matchMedia;try{return!!t?.call(e,`(prefers-reduced-motion: reduce)`)?.matches}catch{return!1}},g=new WeakSet,_=e=>typeof e.showPopover==`function`&&typeof e.hidePopover==`function`,v=(e,t)=>{if(t===g.has(e))return!1;if(t){if(!_(e)||!e.isConnected)return!1;try{e.setAttribute(`popover`,`manual`),e.showPopover()}catch{return e.removeAttribute(`popover`),!1}return e.style.position=`fixed`,g.add(e),!0}try{e.hidePopover()}catch{}return e.removeAttribute(`popover`),e.style.position=`absolute`,g.delete(e),!0},y=/hidden|clip|auto|scroll/,b=(e,t)=>{let n=e.ownerDocument,r=n.defaultView;if(!r)return null;let i=e.getBoundingClientRect(),a=i.left+e.clientLeft,o=i.top+e.clientTop,s=0,c=0,l=n.documentElement.clientWidth||r.innerWidth,u=n.documentElement.clientHeight||r.innerHeight;if(!t)for(let t=e;t&&t!==n.body&&t!==n.documentElement;t=t.parentElement){let e=r.getComputedStyle(t);if(!y.test(`${e.overflowX} ${e.overflowY}`))continue;let n=t.getBoundingClientRect(),i=n.left+t.clientLeft,a=n.top+t.clientTop;s=Math.max(s,i),c=Math.max(c,a),l=Math.min(l,i+t.clientWidth),u=Math.min(u,a+t.clientHeight)}return{x:s-a,y:c-o,width:Math.max(0,l-s),height:Math.max(0,u-c)}},x=(e,t,n,r={})=>{let i=r.size??{width:e.offsetWidth,height:e.offsetHeight},a=r.stage??{width:t.clientWidth,height:t.clientHeight},o=_(e),s=n(i,a,null);if(s.fit!==`stage`){let e=b(t,o);e&&(s=n(i,a,e))}let c=v(e,(s.x<0||s.y<0||s.x+i.width>a.width||s.y+i.height>a.height)&&o),{x:l,y:u}=s;if(g.has(e)){let e=t.getBoundingClientRect();l+=e.left+t.clientLeft,u+=e.top+t.clientTop}if(r.animate){let n=t.ownerDocument.defaultView;e.style.transition=c||h(n)?`none`:d}return e.style.transform=`translate(${Math.round(l)}px, ${Math.round(u)}px)`,s},S=(e,t,n)=>x(e,t,(e,t,r)=>s({...n,size:e,stage:t,bounds:r}),n);exports.CANVAS_TOOLTIP_ALPHA=f,exports.TOOLTIP_TRANSITION=d,exports.overlapArea=i,exports.placeTooltip=s,exports.placeTooltipElement=x,exports.positionTooltip=S,exports.prefersReducedMotion=h,exports.styleTooltip=m,exports.tooltipBounds=b,exports.unionRect=r;
|
|
2
|
+
//# sourceMappingURL=tooltip.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tooltip.cjs","names":["ORDER","inset","r","m","intersection","x","a","b","y","unionRect","overlapArea","clampSpan","lo","v","hi","len","inside","area","w","h","placeTooltip","input","pointer","anchor","avoid","stageRect","bounds","margin","stageArea","visible","inner","order","prefer","beside","side","gap","areas","p","best","bestCover","cover","spots","s","SURFACE","GLASS","BLUR","TOOLTIP_TRANSITION","CANVAS_TOOLTIP_ALPHA","TOOLTIP_LOOK","styleTooltip","el","style","prefersReducedMotion","media","win","promoted","canPromote","setTopLayer","top","tip","CLIPS","tooltipBounds","doc","stage","origin","ox","oy","left","right","bottom","topLayer","cs","l","t","placeTooltipElement","size","options","place","moved","positionTooltip"],"sources":["../../../../charts/src/engine/tooltip.ts"],"sourcesContent":["// Tooltips of every chart family: the shared translucent look and a\n// placement that keeps the tooltip off what it describes (the hovered marks\n// and the pointer). `placeTooltip` is pure (canvas hosts use it too); the\n// DOM helpers measure the element and may let it overflow the stage.\n\nimport type { Rect } from \"./layout\";\n\nexport interface TooltipSize {\n width: number;\n height: number;\n}\n\n/**\n * Sides tried first: beside the marks (right, then left), above / below\n * them (above first), or one given side then its opposite\n */\nexport type TooltipPrefer =\n \"side\" | \"vertical\" | \"right\" | \"left\" | \"top\" | \"bottom\";\n\nexport interface TooltipPlacementInput {\n /** Pointer or focused point (stage pixels): the tooltip is aligned on it and never covers it */\n anchor: { x: number; y: number };\n /** Box of the hovered marks (stage pixels) the tooltip must not cover */\n avoid?: Rect | null;\n size: TooltipSize;\n stage: TooltipSize;\n /**\n * Area (stage pixels) the tooltip may overflow the stage into when it\n * fits nowhere inside, e.g. the visible part of the viewport\n */\n bounds?: Rect | null;\n prefer?: TooltipPrefer;\n /** Distance between the tooltip and the avoided box */\n gap?: number;\n /** Distance kept from the edges of the stage and of the bounds */\n margin?: number;\n}\n\nexport interface TooltipPlacement {\n /** Top-left corner, stage pixels */\n x: number;\n y: number;\n /**\n * `stage`: inside the stage, off the marks; `bounds`: off the marks,\n * overflowing the stage; `none`: no room off the marks, the spot\n * covering them the least\n */\n fit: \"stage\" | \"bounds\" | \"none\";\n}\n\ntype Side = \"right\" | \"left\" | \"bottom\" | \"top\";\n\nconst ORDER: Record<TooltipPrefer, readonly Side[]> = {\n side: [\"right\", \"left\", \"bottom\", \"top\"],\n vertical: [\"top\", \"bottom\", \"right\", \"left\"],\n right: [\"right\", \"left\", \"bottom\", \"top\"],\n left: [\"left\", \"right\", \"bottom\", \"top\"],\n top: [\"top\", \"bottom\", \"right\", \"left\"],\n bottom: [\"bottom\", \"top\", \"right\", \"left\"],\n};\n\nconst inset = (r: Rect, m: number): Rect => ({\n x: r.x + m,\n y: r.y + m,\n width: Math.max(0, r.width - m * 2),\n height: Math.max(0, r.height - m * 2),\n});\n\nconst intersection = (a: Rect, b: Rect): Rect => {\n const x = Math.max(a.x, b.x);\n const y = Math.max(a.y, b.y);\n return {\n x,\n y,\n width: Math.max(0, Math.min(a.x + a.width, b.x + b.width) - x),\n height: Math.max(0, Math.min(a.y + a.height, b.y + b.height) - y),\n };\n};\n\n/** Smallest rectangle holding both */\nexport const unionRect = (a: Rect | null, b: Rect): Rect => {\n if (!a) return { x: b.x, y: b.y, width: b.width, height: b.height };\n const x = Math.min(a.x, b.x);\n const y = Math.min(a.y, b.y);\n return {\n x,\n y,\n width: Math.max(a.x + a.width, b.x + b.width) - x,\n height: Math.max(a.y + a.height, b.y + b.height) - y,\n };\n};\n\n/** Area covered by both rectangles */\nexport const overlapArea = (a: Rect, b: Rect): number => {\n const r = intersection(a, b);\n return r.width * r.height;\n};\n\n/** Start of a `len` long span at `v`, moved into [lo, hi] (at `lo` when longer) */\nconst clampSpan = (v: number, len: number, lo: number, hi: number) =>\n Math.max(lo, Math.min(v, hi - len));\n\n// half a pixel of slack: rounded sizes must not fail the fit\nconst inside = (area: Rect, x: number, y: number, w: number, h: number) =>\n x >= area.x - 0.5 &&\n y >= area.y - 0.5 &&\n x + w <= area.x + area.width + 0.5 &&\n y + h <= area.y + area.height + 0.5;\n\n/**\n * Top-left corner of a tooltip that covers neither the hovered marks\n * (`avoid`) nor the pointer. Candidates sit beside the avoided box, in the\n * order of `prefer`, centered on the pointer across that side and slid\n * into the area. The first one inside the stage wins; else, with `bounds`,\n * the candidate inside the bounds that covers the least of the stage (a\n * tooltip above a tiny sparkline rather than over it); else the clamped\n * spot covering the marks the least.\n */\nexport const placeTooltip = (\n input: TooltipPlacementInput,\n): TooltipPlacement => {\n const { anchor, prefer = \"side\", gap = 12, margin = 4 } = input;\n const { width: w, height: h } = input.size;\n const pointer: Rect = { x: anchor.x, y: anchor.y, width: 0, height: 0 };\n const avoid = unionRect(input.avoid ?? null, pointer);\n const stageRect: Rect = {\n x: 0,\n y: 0,\n width: input.stage.width,\n height: input.stage.height,\n };\n const bounds =\n input.bounds && input.bounds.width > 0 && input.bounds.height > 0\n ? inset(input.bounds, margin)\n : null;\n const stageArea = inset(stageRect, margin);\n const visible = bounds ? intersection(stageArea, bounds) : stageArea;\n // a stage scrolled out of sight keeps its own area\n const inner = visible.width > 0 && visible.height > 0 ? visible : stageArea;\n const order = ORDER[prefer];\n\n const beside = (side: Side, areas: readonly Rect[]) => {\n let x: number;\n let y: number;\n if (side === \"right\" || side === \"left\") {\n x = side === \"right\" ? avoid.x + avoid.width + gap : avoid.x - gap - w;\n y = anchor.y - h / 2;\n for (const r of areas) y = clampSpan(y, h, r.y, r.y + r.height);\n } else {\n y = side === \"bottom\" ? avoid.y + avoid.height + gap : avoid.y - gap - h;\n x = anchor.x - w / 2;\n for (const r of areas) x = clampSpan(x, w, r.x, r.x + r.width);\n }\n return { x, y };\n };\n\n for (const side of order) {\n const p = beside(side, [inner]);\n if (inside(inner, p.x, p.y, w, h)) return { ...p, fit: \"stage\" };\n }\n\n if (bounds) {\n let best: { x: number; y: number } | null = null;\n let bestCover = Infinity;\n for (const side of order) {\n // across the side: inside the stage when it is wide enough\n const p = beside(side, [inner, bounds]);\n if (!inside(bounds, p.x, p.y, w, h)) continue;\n const cover = overlapArea(\n { x: p.x, y: p.y, width: w, height: h },\n stageRect,\n );\n if (cover < bestCover - 0.5) {\n best = p;\n bestCover = cover;\n }\n }\n if (best) return { ...best, fit: \"bounds\" };\n }\n\n // no room off the marks: beside them or in a corner, whichever covers\n // them the least (inside the stage first)\n let best = { x: inner.x, y: inner.y };\n let bestCover = Infinity;\n for (const area of bounds ? [inner, bounds] : [inner]) {\n const spots = [\n ...order.map((side) => beside(side, [area])),\n { x: area.x, y: area.y },\n { x: area.x + area.width - w, y: area.y },\n { x: area.x, y: area.y + area.height - h },\n { x: area.x + area.width - w, y: area.y + area.height - h },\n ];\n for (const s of spots) {\n const x = clampSpan(s.x, w, area.x, area.x + area.width);\n const y = clampSpan(s.y, h, area.y, area.y + area.height);\n const cover = overlapArea({ x, y, width: w, height: h }, avoid);\n if (cover < bestCover - 0.5) {\n best = { x, y };\n bestCover = cover;\n }\n }\n }\n return { ...best, fit: \"none\" };\n};\n\n// ------------------------------------------------------------------- DOM\n\nconst SURFACE = \"var(--surface-elevated-color, var(--background-color, #fff))\";\nconst GLASS = `color-mix(in srgb, ${SURFACE} 80%, transparent)`;\nconst BLUR = \"blur(12px) saturate(160%)\";\n\n/** Transition of the tooltip moves (none when reduced motion is preferred) */\nexport const TOOLTIP_TRANSITION = \"transform 80ms ease-out\";\n\n/** Opacity of the fill of canvas-drawn tooltips (a canvas cannot blur what is below) */\nexport const CANVAS_TOOLTIP_ALPHA = 0.86;\n\n/** Look shared by the tooltips; sizes and type stay with each host */\nconst TOOLTIP_LOOK: Partial<CSSStyleDeclaration> = {\n position: \"absolute\",\n left: \"0\",\n top: \"0\",\n // also overrides the user agent style of a popover (top layer)\n right: \"auto\",\n bottom: \"auto\",\n margin: \"0\",\n overflow: \"visible\",\n pointerEvents: \"none\",\n border:\n \"1px solid color-mix(in srgb, var(--border-color, #e5e7eb) 70%, transparent)\",\n background: SURFACE,\n color: \"var(--text-color, #1f2937)\",\n // the light inner edge reads on dark themes, the shadow on light ones\n boxShadow:\n \"0 12px 32px -8px rgba(15, 23, 42, 0.2), 0 4px 10px -4px rgba(15, 23, 42, 0.1), inset 0 1px 0 rgba(255, 255, 255, 0.06)\",\n};\n\n/**\n * Styles a tooltip element: translucent over a blurred backdrop where\n * supported. Unsupported inline values are ignored, so the opaque\n * background set first stays on older browsers.\n */\nexport const styleTooltip = <T extends HTMLElement>(\n el: T,\n style: Partial<CSSStyleDeclaration> = {},\n): T => {\n Object.assign(el.style, TOOLTIP_LOOK, style);\n el.style.background = GLASS;\n el.style.backdropFilter = BLUR;\n el.style.setProperty(\"-webkit-backdrop-filter\", BLUR);\n return el;\n};\n\n/** Whether the user prefers reduced motion (false without `matchMedia`) */\nexport const prefersReducedMotion = (\n win: Window | null | undefined,\n): boolean => {\n // typed loosely: the React Native build has no DOM `matchMedia` typings\n const media = (\n win as\n | { matchMedia?: (query: string) => { matches: boolean } | null }\n | null\n | undefined\n )?.matchMedia;\n try {\n return !!media?.call(win, \"(prefers-reduced-motion: reduce)\")?.matches;\n } catch {\n return false;\n }\n};\n\n/** Tooltips currently shown in the top layer (popover) */\nconst promoted = new WeakSet<HTMLElement>();\n\nconst canPromote = (el: HTMLElement) =>\n typeof el.showPopover === \"function\" && typeof el.hidePopover === \"function\";\n\n/**\n * Moves a tooltip into the top layer (out of any clipping ancestor and\n * above the page) or back into its stage; `true` when it moved\n */\nconst setTopLayer = (tip: HTMLElement, top: boolean): boolean => {\n if (top === promoted.has(tip)) return false;\n if (top) {\n if (!canPromote(tip) || !tip.isConnected) return false;\n try {\n tip.setAttribute(\"popover\", \"manual\");\n tip.showPopover();\n } catch {\n tip.removeAttribute(\"popover\");\n return false;\n }\n tip.style.position = \"fixed\";\n promoted.add(tip);\n return true;\n }\n try {\n tip.hidePopover();\n } catch {\n // already closed (e.g. removed from the document)\n }\n tip.removeAttribute(\"popover\");\n tip.style.position = \"absolute\";\n promoted.delete(tip);\n return true;\n};\n\n/** Overflow values clipping the descendants */\nconst CLIPS = /hidden|clip|auto|scroll/;\n\n/**\n * Area (stage pixels) a tooltip of `stage` can be seen in: the viewport,\n * narrowed to the ancestors clipping their overflow unless the tooltip\n * can go to the top layer (`topLayer`)\n */\nexport const tooltipBounds = (\n stage: HTMLElement,\n topLayer: boolean,\n): Rect | null => {\n const doc = stage.ownerDocument;\n const win = doc.defaultView;\n if (!win) return null;\n const origin = stage.getBoundingClientRect();\n const ox = origin.left + stage.clientLeft;\n const oy = origin.top + stage.clientTop;\n let left = 0;\n let top = 0;\n // without the scroll bars\n let right = doc.documentElement.clientWidth || win.innerWidth;\n let bottom = doc.documentElement.clientHeight || win.innerHeight;\n if (!topLayer) {\n // the overflow of <html> and <body> is the viewport's\n for (\n let el: HTMLElement | null = stage;\n el && el !== doc.body && el !== doc.documentElement;\n el = el.parentElement\n ) {\n const cs = win.getComputedStyle(el);\n if (!CLIPS.test(`${cs.overflowX} ${cs.overflowY}`)) continue;\n const r = el.getBoundingClientRect();\n const l = r.left + el.clientLeft;\n const t = r.top + el.clientTop;\n left = Math.max(left, l);\n top = Math.max(top, t);\n right = Math.min(right, l + el.clientWidth);\n bottom = Math.min(bottom, t + el.clientHeight);\n }\n }\n return {\n x: left - ox,\n y: top - oy,\n width: Math.max(0, right - left),\n height: Math.max(0, bottom - top),\n };\n};\n\nexport interface TooltipElementOptions {\n /** Size of the tooltip, measured on the element by default */\n size?: TooltipSize;\n /** Size of the stage, its client size by default */\n stage?: TooltipSize;\n /** Animates the moves, unless the user prefers reduced motion */\n animate?: boolean;\n}\n\n/**\n * Places a shown tooltip (a child of `stage`) with `place`, called with\n * the bounds it may overflow into only when it fits nowhere inside the\n * stage. A tooltip leaving the stage goes to the top layer where the\n * Popover API exists (its ancestors may clip it); elsewhere the bounds\n * stop at the clipping ancestors.\n */\nexport const placeTooltipElement = (\n tip: HTMLElement,\n stage: HTMLElement,\n place: (\n size: TooltipSize,\n stage: TooltipSize,\n bounds: Rect | null,\n ) => TooltipPlacement,\n options: TooltipElementOptions = {},\n): TooltipPlacement => {\n const size = options.size ?? {\n width: tip.offsetWidth,\n height: tip.offsetHeight,\n };\n const area = options.stage ?? {\n width: stage.clientWidth,\n height: stage.clientHeight,\n };\n const topLayer = canPromote(tip);\n let p = place(size, area, null);\n if (p.fit !== \"stage\") {\n const bounds = tooltipBounds(stage, topLayer);\n if (bounds) p = place(size, area, bounds);\n }\n const outside =\n p.x < 0 ||\n p.y < 0 ||\n p.x + size.width > area.width ||\n p.y + size.height > area.height;\n const moved = setTopLayer(tip, outside && topLayer);\n let { x, y } = p;\n if (promoted.has(tip)) {\n // fixed in the top layer: viewport pixels\n const r = stage.getBoundingClientRect();\n x += r.left + stage.clientLeft;\n y += r.top + stage.clientTop;\n }\n if (options.animate) {\n const win = stage.ownerDocument.defaultView;\n tip.style.transition =\n moved || prefersReducedMotion(win) ? \"none\" : TOOLTIP_TRANSITION;\n }\n tip.style.transform = `translate(${Math.round(x)}px, ${Math.round(y)}px)`;\n return p;\n};\n\nexport interface TooltipElementPlacement\n extends\n Omit<TooltipPlacementInput, \"size\" | \"stage\" | \"bounds\">,\n TooltipElementOptions {}\n\n/** Places a shown tooltip off the hovered marks (see `placeTooltip`) */\nexport const positionTooltip = (\n tip: HTMLElement,\n stage: HTMLElement,\n input: TooltipElementPlacement,\n): TooltipPlacement =>\n placeTooltipElement(\n tip,\n stage,\n (size, area, bounds) =>\n placeTooltip({ ...input, size, stage: area, bounds }),\n input,\n );\n"],"mappings":"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"}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import { Rect } from './layout.cjs';
|
|
2
|
+
export interface TooltipSize {
|
|
3
|
+
width: number;
|
|
4
|
+
height: number;
|
|
5
|
+
}
|
|
6
|
+
/**
|
|
7
|
+
* Sides tried first: beside the marks (right, then left), above / below
|
|
8
|
+
* them (above first), or one given side then its opposite
|
|
9
|
+
*/
|
|
10
|
+
export type TooltipPrefer = "side" | "vertical" | "right" | "left" | "top" | "bottom";
|
|
11
|
+
export interface TooltipPlacementInput {
|
|
12
|
+
/** Pointer or focused point (stage pixels): the tooltip is aligned on it and never covers it */
|
|
13
|
+
anchor: {
|
|
14
|
+
x: number;
|
|
15
|
+
y: number;
|
|
16
|
+
};
|
|
17
|
+
/** Box of the hovered marks (stage pixels) the tooltip must not cover */
|
|
18
|
+
avoid?: Rect | null;
|
|
19
|
+
size: TooltipSize;
|
|
20
|
+
stage: TooltipSize;
|
|
21
|
+
/**
|
|
22
|
+
* Area (stage pixels) the tooltip may overflow the stage into when it
|
|
23
|
+
* fits nowhere inside, e.g. the visible part of the viewport
|
|
24
|
+
*/
|
|
25
|
+
bounds?: Rect | null;
|
|
26
|
+
prefer?: TooltipPrefer;
|
|
27
|
+
/** Distance between the tooltip and the avoided box */
|
|
28
|
+
gap?: number;
|
|
29
|
+
/** Distance kept from the edges of the stage and of the bounds */
|
|
30
|
+
margin?: number;
|
|
31
|
+
}
|
|
32
|
+
export interface TooltipPlacement {
|
|
33
|
+
/** Top-left corner, stage pixels */
|
|
34
|
+
x: number;
|
|
35
|
+
y: number;
|
|
36
|
+
/**
|
|
37
|
+
* `stage`: inside the stage, off the marks; `bounds`: off the marks,
|
|
38
|
+
* overflowing the stage; `none`: no room off the marks, the spot
|
|
39
|
+
* covering them the least
|
|
40
|
+
*/
|
|
41
|
+
fit: "stage" | "bounds" | "none";
|
|
42
|
+
}
|
|
43
|
+
/** Smallest rectangle holding both */
|
|
44
|
+
export declare const unionRect: (a: Rect | null, b: Rect) => Rect;
|
|
45
|
+
/** Area covered by both rectangles */
|
|
46
|
+
export declare const overlapArea: (a: Rect, b: Rect) => number;
|
|
47
|
+
/**
|
|
48
|
+
* Top-left corner of a tooltip that covers neither the hovered marks
|
|
49
|
+
* (`avoid`) nor the pointer. Candidates sit beside the avoided box, in the
|
|
50
|
+
* order of `prefer`, centered on the pointer across that side and slid
|
|
51
|
+
* into the area. The first one inside the stage wins; else, with `bounds`,
|
|
52
|
+
* the candidate inside the bounds that covers the least of the stage (a
|
|
53
|
+
* tooltip above a tiny sparkline rather than over it); else the clamped
|
|
54
|
+
* spot covering the marks the least.
|
|
55
|
+
*/
|
|
56
|
+
export declare const placeTooltip: (input: TooltipPlacementInput) => TooltipPlacement;
|
|
57
|
+
/** Transition of the tooltip moves (none when reduced motion is preferred) */
|
|
58
|
+
export declare const TOOLTIP_TRANSITION = "transform 80ms ease-out";
|
|
59
|
+
/** Opacity of the fill of canvas-drawn tooltips (a canvas cannot blur what is below) */
|
|
60
|
+
export declare const CANVAS_TOOLTIP_ALPHA = 0.86;
|
|
61
|
+
/**
|
|
62
|
+
* Styles a tooltip element: translucent over a blurred backdrop where
|
|
63
|
+
* supported. Unsupported inline values are ignored, so the opaque
|
|
64
|
+
* background set first stays on older browsers.
|
|
65
|
+
*/
|
|
66
|
+
export declare const styleTooltip: <T extends HTMLElement>(el: T, style?: Partial<CSSStyleDeclaration>) => T;
|
|
67
|
+
/** Whether the user prefers reduced motion (false without `matchMedia`) */
|
|
68
|
+
export declare const prefersReducedMotion: (win: Window | null | undefined) => boolean;
|
|
69
|
+
/**
|
|
70
|
+
* Area (stage pixels) a tooltip of `stage` can be seen in: the viewport,
|
|
71
|
+
* narrowed to the ancestors clipping their overflow unless the tooltip
|
|
72
|
+
* can go to the top layer (`topLayer`)
|
|
73
|
+
*/
|
|
74
|
+
export declare const tooltipBounds: (stage: HTMLElement, topLayer: boolean) => Rect | null;
|
|
75
|
+
export interface TooltipElementOptions {
|
|
76
|
+
/** Size of the tooltip, measured on the element by default */
|
|
77
|
+
size?: TooltipSize;
|
|
78
|
+
/** Size of the stage, its client size by default */
|
|
79
|
+
stage?: TooltipSize;
|
|
80
|
+
/** Animates the moves, unless the user prefers reduced motion */
|
|
81
|
+
animate?: boolean;
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Places a shown tooltip (a child of `stage`) with `place`, called with
|
|
85
|
+
* the bounds it may overflow into only when it fits nowhere inside the
|
|
86
|
+
* stage. A tooltip leaving the stage goes to the top layer where the
|
|
87
|
+
* Popover API exists (its ancestors may clip it); elsewhere the bounds
|
|
88
|
+
* stop at the clipping ancestors.
|
|
89
|
+
*/
|
|
90
|
+
export declare const placeTooltipElement: (tip: HTMLElement, stage: HTMLElement, place: (size: TooltipSize, stage: TooltipSize, bounds: Rect | null) => TooltipPlacement, options?: TooltipElementOptions) => TooltipPlacement;
|
|
91
|
+
export interface TooltipElementPlacement extends Omit<TooltipPlacementInput, "size" | "stage" | "bounds">, TooltipElementOptions {
|
|
92
|
+
}
|
|
93
|
+
/** Places a shown tooltip off the hovered marks (see `placeTooltip`) */
|
|
94
|
+
export declare const positionTooltip: (tip: HTMLElement, stage: HTMLElement, input: TooltipElementPlacement) => TooltipPlacement;
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import { Rect } from './layout.js';
|
|
2
|
+
export interface TooltipSize {
|
|
3
|
+
width: number;
|
|
4
|
+
height: number;
|
|
5
|
+
}
|
|
6
|
+
/**
|
|
7
|
+
* Sides tried first: beside the marks (right, then left), above / below
|
|
8
|
+
* them (above first), or one given side then its opposite
|
|
9
|
+
*/
|
|
10
|
+
export type TooltipPrefer = "side" | "vertical" | "right" | "left" | "top" | "bottom";
|
|
11
|
+
export interface TooltipPlacementInput {
|
|
12
|
+
/** Pointer or focused point (stage pixels): the tooltip is aligned on it and never covers it */
|
|
13
|
+
anchor: {
|
|
14
|
+
x: number;
|
|
15
|
+
y: number;
|
|
16
|
+
};
|
|
17
|
+
/** Box of the hovered marks (stage pixels) the tooltip must not cover */
|
|
18
|
+
avoid?: Rect | null;
|
|
19
|
+
size: TooltipSize;
|
|
20
|
+
stage: TooltipSize;
|
|
21
|
+
/**
|
|
22
|
+
* Area (stage pixels) the tooltip may overflow the stage into when it
|
|
23
|
+
* fits nowhere inside, e.g. the visible part of the viewport
|
|
24
|
+
*/
|
|
25
|
+
bounds?: Rect | null;
|
|
26
|
+
prefer?: TooltipPrefer;
|
|
27
|
+
/** Distance between the tooltip and the avoided box */
|
|
28
|
+
gap?: number;
|
|
29
|
+
/** Distance kept from the edges of the stage and of the bounds */
|
|
30
|
+
margin?: number;
|
|
31
|
+
}
|
|
32
|
+
export interface TooltipPlacement {
|
|
33
|
+
/** Top-left corner, stage pixels */
|
|
34
|
+
x: number;
|
|
35
|
+
y: number;
|
|
36
|
+
/**
|
|
37
|
+
* `stage`: inside the stage, off the marks; `bounds`: off the marks,
|
|
38
|
+
* overflowing the stage; `none`: no room off the marks, the spot
|
|
39
|
+
* covering them the least
|
|
40
|
+
*/
|
|
41
|
+
fit: "stage" | "bounds" | "none";
|
|
42
|
+
}
|
|
43
|
+
/** Smallest rectangle holding both */
|
|
44
|
+
export declare const unionRect: (a: Rect | null, b: Rect) => Rect;
|
|
45
|
+
/** Area covered by both rectangles */
|
|
46
|
+
export declare const overlapArea: (a: Rect, b: Rect) => number;
|
|
47
|
+
/**
|
|
48
|
+
* Top-left corner of a tooltip that covers neither the hovered marks
|
|
49
|
+
* (`avoid`) nor the pointer. Candidates sit beside the avoided box, in the
|
|
50
|
+
* order of `prefer`, centered on the pointer across that side and slid
|
|
51
|
+
* into the area. The first one inside the stage wins; else, with `bounds`,
|
|
52
|
+
* the candidate inside the bounds that covers the least of the stage (a
|
|
53
|
+
* tooltip above a tiny sparkline rather than over it); else the clamped
|
|
54
|
+
* spot covering the marks the least.
|
|
55
|
+
*/
|
|
56
|
+
export declare const placeTooltip: (input: TooltipPlacementInput) => TooltipPlacement;
|
|
57
|
+
/** Transition of the tooltip moves (none when reduced motion is preferred) */
|
|
58
|
+
export declare const TOOLTIP_TRANSITION = "transform 80ms ease-out";
|
|
59
|
+
/** Opacity of the fill of canvas-drawn tooltips (a canvas cannot blur what is below) */
|
|
60
|
+
export declare const CANVAS_TOOLTIP_ALPHA = 0.86;
|
|
61
|
+
/**
|
|
62
|
+
* Styles a tooltip element: translucent over a blurred backdrop where
|
|
63
|
+
* supported. Unsupported inline values are ignored, so the opaque
|
|
64
|
+
* background set first stays on older browsers.
|
|
65
|
+
*/
|
|
66
|
+
export declare const styleTooltip: <T extends HTMLElement>(el: T, style?: Partial<CSSStyleDeclaration>) => T;
|
|
67
|
+
/** Whether the user prefers reduced motion (false without `matchMedia`) */
|
|
68
|
+
export declare const prefersReducedMotion: (win: Window | null | undefined) => boolean;
|
|
69
|
+
/**
|
|
70
|
+
* Area (stage pixels) a tooltip of `stage` can be seen in: the viewport,
|
|
71
|
+
* narrowed to the ancestors clipping their overflow unless the tooltip
|
|
72
|
+
* can go to the top layer (`topLayer`)
|
|
73
|
+
*/
|
|
74
|
+
export declare const tooltipBounds: (stage: HTMLElement, topLayer: boolean) => Rect | null;
|
|
75
|
+
export interface TooltipElementOptions {
|
|
76
|
+
/** Size of the tooltip, measured on the element by default */
|
|
77
|
+
size?: TooltipSize;
|
|
78
|
+
/** Size of the stage, its client size by default */
|
|
79
|
+
stage?: TooltipSize;
|
|
80
|
+
/** Animates the moves, unless the user prefers reduced motion */
|
|
81
|
+
animate?: boolean;
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Places a shown tooltip (a child of `stage`) with `place`, called with
|
|
85
|
+
* the bounds it may overflow into only when it fits nowhere inside the
|
|
86
|
+
* stage. A tooltip leaving the stage goes to the top layer where the
|
|
87
|
+
* Popover API exists (its ancestors may clip it); elsewhere the bounds
|
|
88
|
+
* stop at the clipping ancestors.
|
|
89
|
+
*/
|
|
90
|
+
export declare const placeTooltipElement: (tip: HTMLElement, stage: HTMLElement, place: (size: TooltipSize, stage: TooltipSize, bounds: Rect | null) => TooltipPlacement, options?: TooltipElementOptions) => TooltipPlacement;
|
|
91
|
+
export interface TooltipElementPlacement extends Omit<TooltipPlacementInput, "size" | "stage" | "bounds">, TooltipElementOptions {
|
|
92
|
+
}
|
|
93
|
+
/** Places a shown tooltip off the hovered marks (see `placeTooltip`) */
|
|
94
|
+
export declare const positionTooltip: (tip: HTMLElement, stage: HTMLElement, input: TooltipElementPlacement) => TooltipPlacement;
|