smart-labels 0.0.14 → 0.0.15

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.
@@ -1,4 +1,4 @@
1
- // smart-labels v0.0.14 Copyright (c) 2024 John Alexis Guerra Gómez
1
+ // smart-labels v0.0.15 Copyright (c) 2024 John Alexis Guerra Gómez
2
2
  'use strict';
3
3
 
4
4
  var d3 = require('d3');
@@ -96,8 +96,16 @@ function smartLabels(
96
96
  y(d, index) !== null
97
97
  );
98
98
 
99
- if (typeof font === "string") font = () => font;
100
- if (typeof hoverFont === "string") hoverFont = () => hoverFont;
99
+ // Keep the string in its own constant: `font = () => font` would return the
100
+ // arrow function itself (font is reassigned), so string fonts were ignored.
101
+ if (typeof font === "string") {
102
+ const fontString = font;
103
+ font = () => fontString;
104
+ }
105
+ if (typeof hoverFont === "string") {
106
+ const hoverFontString = hoverFont;
107
+ hoverFont = () => hoverFontString;
108
+ }
101
109
 
102
110
  let xExtent = d3__namespace.extent(data, x),
103
111
  yExtent = d3__namespace.extent(data, y);
@@ -1,4 +1,4 @@
1
- // smart-labels v0.0.14 Copyright (c) 2024 John Alexis Guerra Gómez
1
+ // smart-labels v0.0.15 Copyright (c) 2024 John Alexis Guerra Gómez
2
2
  import * as d3 from 'd3';
3
3
 
4
4
  // Based on https://observablehq.com/@d3/voronoi-labels by Mike Bostock
@@ -75,8 +75,16 @@ function smartLabels(
75
75
  y(d, index) !== null
76
76
  );
77
77
 
78
- if (typeof font === "string") font = () => font;
79
- if (typeof hoverFont === "string") hoverFont = () => hoverFont;
78
+ // Keep the string in its own constant: `font = () => font` would return the
79
+ // arrow function itself (font is reassigned), so string fonts were ignored.
80
+ if (typeof font === "string") {
81
+ const fontString = font;
82
+ font = () => fontString;
83
+ }
84
+ if (typeof hoverFont === "string") {
85
+ const hoverFontString = hoverFont;
86
+ hoverFont = () => hoverFontString;
87
+ }
80
88
 
81
89
  let xExtent = d3.extent(data, x),
82
90
  yExtent = d3.extent(data, y);
@@ -1,4 +1,4 @@
1
- // smart-labels v0.0.14 Copyright (c) 2024 John Alexis Guerra Gómez
1
+ // smart-labels v0.0.15 Copyright (c) 2024 John Alexis Guerra Gómez
2
2
  (function (global, factory) {
3
3
  typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory(require('d3')) :
4
4
  typeof define === 'function' && define.amd ? define(['d3'], factory) :
@@ -98,8 +98,16 @@ function smartLabels(
98
98
  y(d, index) !== null
99
99
  );
100
100
 
101
- if (typeof font === "string") font = () => font;
102
- if (typeof hoverFont === "string") hoverFont = () => hoverFont;
101
+ // Keep the string in its own constant: `font = () => font` would return the
102
+ // arrow function itself (font is reassigned), so string fonts were ignored.
103
+ if (typeof font === "string") {
104
+ const fontString = font;
105
+ font = () => fontString;
106
+ }
107
+ if (typeof hoverFont === "string") {
108
+ const hoverFontString = hoverFont;
109
+ hoverFont = () => hoverFontString;
110
+ }
103
111
 
104
112
  let xExtent = d3__namespace.extent(data, x),
105
113
  yExtent = d3__namespace.extent(data, y);
@@ -1,2 +1,2 @@
1
- // smart-labels v0.0.14 Copyright (c) 2024 John Alexis Guerra Gómez
2
- !function(t,e){"object"==typeof exports&&"undefined"!=typeof module?module.exports=e(require("d3")):"function"==typeof define&&define.amd?define(["d3"],e):(t="undefined"!=typeof globalThis?globalThis:t||self).smartLabels=e(t.d3)}(this,function(t){"use strict";function e(t){var e=Object.create(null);return t&&Object.keys(t).forEach(function(o){if("default"!==o){var n=Object.getOwnPropertyDescriptor(t,o);Object.defineProperty(e,o,n.get?n:{enumerable:!0,get:function(){return t[o]}})}}),e.default=t,Object.freeze(e)}var o=e(t);function n(t,e="text"){const n=o.sort(t.selectAll(e),t=>+t.getAttribute("data-priority")).reverse().map(t=>{const{x:e,y:o,width:n,height:r}=t.getBoundingClientRect();return{node:t,x:e,y:o,width:n,height:r}}),r=[];for(const t of n){const e=r.some(e=>{return n=e,!((o=t).x+o.width<n.x||n.x+n.width<o.x||o.y+o.height<n.y||n.y+n.height<o.y);var o,n});o.select(t.node).classed("occluded",e),e||r.push(t)}return r}return function(t,{x:e=t=>t[0],y:r=t=>t[1],r:l=()=>3,label:a=(t,e)=>e,fill:s="#333",stroke:i="white",width:c=null,height:d=null,target:h=null,renderer:f="svg",font:u=()=>"10pt sans-serif",hover:g=!0,onHover:p=t=>t,hoverFont:y=()=>"bolder 12pt sans-serif",labelsInCentroids:b=!0,threshold:m=2e3,alwaysShow:v=t=>!1,showLabel:w=(t,e)=>v(t)||-o.polygonArea(e)>m,backgroundFill:x="#fefefe01",strokeWidth:k=5,showVoronoi:L=!1,voronoiStroke:A="#ccc",showAnchors:j=!1,anchorsStroke:S="orange",anchorsFill:C="none",useOcclusion:P=!0,occludedStyle:M="opacity: 0.2",showPoints:O=!1,pointsFill:T="#ccc",pointsSelectedFill:$="firebrick",pointsStroke:E="#ccc",debug:F=!1,selected:I=null,padding:B=3}={}){if(!t||0===t?.length)return console.log("smartLabels: No data to render"),h;t=t.filter((t,o)=>void 0!==e(t,o)&&null!==e(t,o)&&void 0!==r(t,o)&&null!==r(t,o)),"string"==typeof u&&(u=()=>u),"string"==typeof y&&(y=()=>y);let V=o.extent(t,e),D=o.extent(t,r);if(c=c||V[1]-V[0],d=d||D[1]-D[0],F&&console.log("✅ smartLabels renderer",f),h&&!function(t,e){return t&&t.node&&(t=t.node()),"canvas"===e.toLocaleLowerCase()?t instanceof HTMLCanvasElement:t instanceof SVGElement}(h,f))throw F&&console.log("❌ smartLabels Target doesn't match the renderer",h,f),new Error("Smartlabels Target doesn't match the renderer",h,f);"canvas"===f.toLocaleLowerCase()?(h=h?o.select(h.node?h.node():h):o.create("canvas").attr("width",c).attr("height",d),P=!1):h=(h=h?o.select(h.node?h.node():h):o.create("svg").attr("viewBox",[0,0,c,d]))||o.create("svg").attr("viewBox",[0,0,c,d]);const H=o.Delaunay.from(t,e,r),R=H.voronoi([V[0]-1,D[0]-1,V[1]+1,D[1]+1]);let W=t.map((t,e)=>[t,R.cellPolygon(e)]);function q(t,o,n){if(!o){const l=H.find(e(t,n),r(t,n));if(-1===l)return console.log("couldn't find cell",l,t,e(t,n),r(t,n)),null;o=W[l][1]}return o}function z(n){F&&console.log("renderCanvasAnchorsVoronoiPoints","showAnchors",j,"labelsInCentroids",b,"showVoronoi",L,"showPoints",O),(j||b)&&W.forEach(({d:t,cell:l,show:a},s)=>{if(!a)return;const[i,c]=o.polygonCentroid(l);n.beginPath(),n.moveTo(i,c),n.lineTo(e(t,s),r(t,s)),n.strokeStyle=S,n.stroke()}),L&&(n.beginPath(),R.render(n),n.strokeStyle=A,n.stroke()),O&&(n.beginPath(),n.save(),n.globalAlpha=.7,n.fillStyle="black",t.forEach((t,o)=>{n.beginPath(),n.arc(e(t,o),r(t,o),l(t,o),0,2*Math.PI)}),n.restore(),n.fillStyle=T,n.fill(),n.strokeStyle=E,n.stroke())}function G(t,n){W.forEach(({d:c,cell:d,show:h},f)=>{if(!h&&f!==n)return;const[g,p]=o.polygonCentroid(d),m=(Math.round(Math.atan2(p-r(c,f),g-e(c,f))/Math.PI*2)+4)%4;t.save();const v=l(c,f)+B,w=0===m?v:2===m?-v:v,x=1===m?v:3===m?-v:v;t.translate(b?g:e(c,f)+w,b?p:r(c,f)+x),t.textAlign=0===m?"left":1===m?"center":2===m?"right":"center",t.textBaseline=0===m?"middle":1===m?"top":2===m?"middle":"bottom",t.font=f===n?y(c,f):u(c,f),t.fillStyle=s,t.strokeStyle=i,t.lineWidth=k,t.strokeText(a(c,f),0,0),t.fillText(a(c,f),0,0),t.restore()})}return W=W.map(([t,e],o)=>[t,q(t,e,o)]).map(([t,e])=>({d:t,cell:e,show:w(t,e)})),"canvas"===f.toLocaleLowerCase()?function(){const t=h.node().getContext("2d");z(t),G(t,I),g&&(F&&console.log("setting onmousemove",h),h.on("mousemove",e=>{const[n,r]=o.pointer(e),l=H.find(n,r);l!==I&&(I=l,t.clearRect(0,0,c,d),z(t),p&&"function"==typeof p&&p(l),G(t,I))}))}():function(){let t=null;P&&h.selectAll("style.smartLabels").data([0]).join("style").attr("class","smartLabels").html(`\n svg g.labels > text.occluded:not(.selected) { ${M} }\n `);const f=h.selectAll("rect.smartLabels").data([0]).join("rect").attr("class","smartLabels").attr("width",c).attr("height",d).attr("fill",x).attr("stroke","none"),m={top:t=>t.attr("text-anchor","middle").attr("y",(t,e)=>-(l(t,e)+B)),right:t=>t.attr("text-anchor","start").attr("dy","0.35em").attr("x",(t,e)=>l(t,e)+B),bottom:t=>t.attr("text-anchor","middle").attr("dy","0.71em").attr("y",(t,e)=>l(t,e)+B),left:t=>t.attr("text-anchor","end").attr("dy","0.35em").attr("x",(t,e)=>-(l(t,e)+B))};let v;(j||b)&&(t=h.selectAll("g#anchors").data([W]).join("g").attr("pointer-events","none").attr("id","anchors").attr("stroke",S).attr("fill",C).selectAll("path.anchor").data(t=>t).join("path").attr("class","anchor").attr("display",({show:t})=>t?null:"none").attr("d",({d:t,cell:n},l)=>n?`M${o.polygonCentroid(n)}L${e(t,l)},${r(t,l)}`:null)),L&&h.selectAll("path#voronoi").data([1]).join("path").attr("id","voronoi").attr("stroke",A).attr("pointer-events","none").attr("d",R.render()),O&&(v=h.selectAll("circle#points").data([1]).join("path").attr("id","points").attr("pointer-events","none").attr("stroke",E).attr("fill",T).attr("d",H.renderPoints(null,2)));const w=h.selectAll("g.labels").data([W]).join("g").attr("class","labels").attr("fill",s).attr("stroke",i).attr("stroke-width",k).attr("paint-order","stroke").attr("pointer-events","none").selectAll("text").data(t=>t).join("text").style("font",({d:t},e)=>u(t,e)).each(function({d:t,cell:n},l){if(!n)return;const[a,s]=o.polygonCentroid(n),i=(Math.round(Math.atan2(s-r(t,l),a-e(t,l))/Math.PI*2)+4)%4;o.select(this).call(0===i?m.right:3===i?m.top:1===i?m.bottom:m.left)}).attr("transform",({d:t,cell:n},l)=>{if(b){const[t,e]=o.polygonCentroid(n);return`translate(${t}, ${e})`}return`translate(${e(t,l)}, ${r(t,l)})`}).attr("display",({show:t})=>t?null:"none").text(({d:t},e,o)=>a(t,e,o));let F=null;if(g&&f.on("mousemove",e=>{const n=H.find(...o.pointer(e));n!==F&&(F=n,w.attr("display",({show:t},e)=>e===F||t?null:"none").style("font",({d:t},e)=>e===F?y(t,e):u(t,e)).classed("selected",(t,e)=>e===F).filter((t,e)=>e===F).raise(),t&&t.attr("display",({show:t},e)=>t||e===F?null:"none"),O&&v.attr("fill",(t,e)=>$).classed("selected",(t,e)=>e===F),p&&"function"==typeof p&&p(n))}),P){const t=new MutationObserver(function(){document.contains(h.node())&&(h.call(n,"g.labels > text"),t.disconnect())});t.observe(document,{attributes:!1,childList:!0,characterData:!1,subtree:!0})}}(),Object.assign(h.node(),{delaunay:H,voronoi:R,cells:W})}});
1
+ // smart-labels v0.0.15 Copyright (c) 2024 John Alexis Guerra Gómez
2
+ !function(t,e){"object"==typeof exports&&"undefined"!=typeof module?module.exports=e(require("d3")):"function"==typeof define&&define.amd?define(["d3"],e):(t="undefined"!=typeof globalThis?globalThis:t||self).smartLabels=e(t.d3)}(this,function(t){"use strict";function e(t){var e=Object.create(null);return t&&Object.keys(t).forEach(function(o){if("default"!==o){var n=Object.getOwnPropertyDescriptor(t,o);Object.defineProperty(e,o,n.get?n:{enumerable:!0,get:function(){return t[o]}})}}),e.default=t,Object.freeze(e)}var o=e(t);function n(t,e="text"){const n=o.sort(t.selectAll(e),t=>+t.getAttribute("data-priority")).reverse().map(t=>{const{x:e,y:o,width:n,height:r}=t.getBoundingClientRect();return{node:t,x:e,y:o,width:n,height:r}}),r=[];for(const t of n){const e=r.some(e=>{return n=e,!((o=t).x+o.width<n.x||n.x+n.width<o.x||o.y+o.height<n.y||n.y+n.height<o.y);var o,n});o.select(t.node).classed("occluded",e),e||r.push(t)}return r}return function(t,{x:e=t=>t[0],y:r=t=>t[1],r:l=()=>3,label:a=(t,e)=>e,fill:s="#333",stroke:i="white",width:c=null,height:d=null,target:h=null,renderer:f="svg",font:u=()=>"10pt sans-serif",hover:g=!0,onHover:p=t=>t,hoverFont:y=()=>"bolder 12pt sans-serif",labelsInCentroids:b=!0,threshold:m=2e3,alwaysShow:v=t=>!1,showLabel:w=(t,e)=>v(t)||-o.polygonArea(e)>m,backgroundFill:x="#fefefe01",strokeWidth:k=5,showVoronoi:L=!1,voronoiStroke:A="#ccc",showAnchors:j=!1,anchorsStroke:S="orange",anchorsFill:C="none",useOcclusion:P=!0,occludedStyle:M="opacity: 0.2",showPoints:O=!1,pointsFill:T="#ccc",pointsSelectedFill:$="firebrick",pointsStroke:E="#ccc",debug:F=!1,selected:I=null,padding:B=3}={}){if(!t||0===t?.length)return console.log("smartLabels: No data to render"),h;if(t=t.filter((t,o)=>void 0!==e(t,o)&&null!==e(t,o)&&void 0!==r(t,o)&&null!==r(t,o)),"string"==typeof u){const t=u;u=()=>t}if("string"==typeof y){const t=y;y=()=>t}let V=o.extent(t,e),D=o.extent(t,r);if(c=c||V[1]-V[0],d=d||D[1]-D[0],F&&console.log("✅ smartLabels renderer",f),h&&!function(t,e){return t&&t.node&&(t=t.node()),"canvas"===e.toLocaleLowerCase()?t instanceof HTMLCanvasElement:t instanceof SVGElement}(h,f))throw F&&console.log("❌ smartLabels Target doesn't match the renderer",h,f),new Error("Smartlabels Target doesn't match the renderer",h,f);"canvas"===f.toLocaleLowerCase()?(h=h?o.select(h.node?h.node():h):o.create("canvas").attr("width",c).attr("height",d),P=!1):h=(h=h?o.select(h.node?h.node():h):o.create("svg").attr("viewBox",[0,0,c,d]))||o.create("svg").attr("viewBox",[0,0,c,d]);const H=o.Delaunay.from(t,e,r),R=H.voronoi([V[0]-1,D[0]-1,V[1]+1,D[1]+1]);let W=t.map((t,e)=>[t,R.cellPolygon(e)]);function q(t,o,n){if(!o){const l=H.find(e(t,n),r(t,n));if(-1===l)return console.log("couldn't find cell",l,t,e(t,n),r(t,n)),null;o=W[l][1]}return o}function z(n){F&&console.log("renderCanvasAnchorsVoronoiPoints","showAnchors",j,"labelsInCentroids",b,"showVoronoi",L,"showPoints",O),(j||b)&&W.forEach(({d:t,cell:l,show:a},s)=>{if(!a)return;const[i,c]=o.polygonCentroid(l);n.beginPath(),n.moveTo(i,c),n.lineTo(e(t,s),r(t,s)),n.strokeStyle=S,n.stroke()}),L&&(n.beginPath(),R.render(n),n.strokeStyle=A,n.stroke()),O&&(n.beginPath(),n.save(),n.globalAlpha=.7,n.fillStyle="black",t.forEach((t,o)=>{n.beginPath(),n.arc(e(t,o),r(t,o),l(t,o),0,2*Math.PI)}),n.restore(),n.fillStyle=T,n.fill(),n.strokeStyle=E,n.stroke())}function G(t,n){W.forEach(({d:c,cell:d,show:h},f)=>{if(!h&&f!==n)return;const[g,p]=o.polygonCentroid(d),m=(Math.round(Math.atan2(p-r(c,f),g-e(c,f))/Math.PI*2)+4)%4;t.save();const v=l(c,f)+B,w=0===m?v:2===m?-v:v,x=1===m?v:3===m?-v:v;t.translate(b?g:e(c,f)+w,b?p:r(c,f)+x),t.textAlign=0===m?"left":1===m?"center":2===m?"right":"center",t.textBaseline=0===m?"middle":1===m?"top":2===m?"middle":"bottom",t.font=f===n?y(c,f):u(c,f),t.fillStyle=s,t.strokeStyle=i,t.lineWidth=k,t.strokeText(a(c,f),0,0),t.fillText(a(c,f),0,0),t.restore()})}return W=W.map(([t,e],o)=>[t,q(t,e,o)]).map(([t,e])=>({d:t,cell:e,show:w(t,e)})),"canvas"===f.toLocaleLowerCase()?function(){const t=h.node().getContext("2d");z(t),G(t,I),g&&(F&&console.log("setting onmousemove",h),h.on("mousemove",e=>{const[n,r]=o.pointer(e),l=H.find(n,r);l!==I&&(I=l,t.clearRect(0,0,c,d),z(t),p&&"function"==typeof p&&p(l),G(t,I))}))}():function(){let t=null;P&&h.selectAll("style.smartLabels").data([0]).join("style").attr("class","smartLabels").html(`\n svg g.labels > text.occluded:not(.selected) { ${M} }\n `);const f=h.selectAll("rect.smartLabels").data([0]).join("rect").attr("class","smartLabels").attr("width",c).attr("height",d).attr("fill",x).attr("stroke","none"),m={top:t=>t.attr("text-anchor","middle").attr("y",(t,e)=>-(l(t,e)+B)),right:t=>t.attr("text-anchor","start").attr("dy","0.35em").attr("x",(t,e)=>l(t,e)+B),bottom:t=>t.attr("text-anchor","middle").attr("dy","0.71em").attr("y",(t,e)=>l(t,e)+B),left:t=>t.attr("text-anchor","end").attr("dy","0.35em").attr("x",(t,e)=>-(l(t,e)+B))};let v;(j||b)&&(t=h.selectAll("g#anchors").data([W]).join("g").attr("pointer-events","none").attr("id","anchors").attr("stroke",S).attr("fill",C).selectAll("path.anchor").data(t=>t).join("path").attr("class","anchor").attr("display",({show:t})=>t?null:"none").attr("d",({d:t,cell:n},l)=>n?`M${o.polygonCentroid(n)}L${e(t,l)},${r(t,l)}`:null)),L&&h.selectAll("path#voronoi").data([1]).join("path").attr("id","voronoi").attr("stroke",A).attr("pointer-events","none").attr("d",R.render()),O&&(v=h.selectAll("circle#points").data([1]).join("path").attr("id","points").attr("pointer-events","none").attr("stroke",E).attr("fill",T).attr("d",H.renderPoints(null,2)));const w=h.selectAll("g.labels").data([W]).join("g").attr("class","labels").attr("fill",s).attr("stroke",i).attr("stroke-width",k).attr("paint-order","stroke").attr("pointer-events","none").selectAll("text").data(t=>t).join("text").style("font",({d:t},e)=>u(t,e)).each(function({d:t,cell:n},l){if(!n)return;const[a,s]=o.polygonCentroid(n),i=(Math.round(Math.atan2(s-r(t,l),a-e(t,l))/Math.PI*2)+4)%4;o.select(this).call(0===i?m.right:3===i?m.top:1===i?m.bottom:m.left)}).attr("transform",({d:t,cell:n},l)=>{if(b){const[t,e]=o.polygonCentroid(n);return`translate(${t}, ${e})`}return`translate(${e(t,l)}, ${r(t,l)})`}).attr("display",({show:t})=>t?null:"none").text(({d:t},e,o)=>a(t,e,o));let F=null;if(g&&f.on("mousemove",e=>{const n=H.find(...o.pointer(e));n!==F&&(F=n,w.attr("display",({show:t},e)=>e===F||t?null:"none").style("font",({d:t},e)=>e===F?y(t,e):u(t,e)).classed("selected",(t,e)=>e===F).filter((t,e)=>e===F).raise(),t&&t.attr("display",({show:t},e)=>t||e===F?null:"none"),O&&v.attr("fill",(t,e)=>$).classed("selected",(t,e)=>e===F),p&&"function"==typeof p&&p(n))}),P){const t=new MutationObserver(function(){document.contains(h.node())&&(h.call(n,"g.labels > text"),t.disconnect())});t.observe(document,{attributes:!1,childList:!0,characterData:!1,subtree:!0})}}(),Object.assign(h.node(),{delaunay:H,voronoi:R,cells:W})}});
package/package.json CHANGED
@@ -1,7 +1,19 @@
1
1
  {
2
2
  "name": "smart-labels",
3
- "version": "0.0.14",
4
- "description": "A basic library for adding labels to a visualization when there is enough space",
3
+ "version": "0.0.15",
4
+ "description": "Fast automatic label placement for D3 scatterplots using Voronoi cells and occlusion detection",
5
+ "keywords": [
6
+ "d3",
7
+ "labels",
8
+ "label-placement",
9
+ "voronoi",
10
+ "delaunay",
11
+ "occlusion",
12
+ "scatterplot",
13
+ "annotation",
14
+ "visualization",
15
+ "dataviz"
16
+ ],
5
17
  "type": "module",
6
18
  "main": "dist/smartLabels.cjs",
7
19
  "module": "dist/smartLabels.es.js",
@@ -27,17 +39,14 @@
27
39
  },
28
40
  "devDependencies": {
29
41
  "@eslint/js": "^9.14.0",
30
- "@rollup/plugin-json": "^6.1.0",
31
- "@rollup/plugin-node-resolve": "^15.2.3",
32
- "@rollup/plugin-terser": "^1.0.0",
33
- "d3": "^7.9.0",
42
+ "@rollup/plugin-terser": "^1.0.2",
43
+ "d3": "^7.9.2",
34
44
  "eslint": "^9.14.0",
35
45
  "eslint-config-prettier": "^9.1.0",
36
- "eslint-plugin-html": "^8.1.2",
46
+ "eslint-plugin-html": "^8.2.1",
37
47
  "globals": "^15.12.0",
38
- "prettier": "^3.3.3",
39
- "rollup": "^4.20.0",
40
- "rollup-plugin-commonjs": "^10.1.0"
48
+ "prettier": "^3.10.1",
49
+ "rollup": "^4.68.2"
41
50
  },
42
51
  "peerDependencies": {
43
52
  "d3": "^7.9.0"