smart-labels 0.0.13 → 0.0.14

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,27 @@
1
- import * as d3 from "d3";
1
+ // smart-labels v0.0.14 Copyright (c) 2024 John Alexis Guerra Gómez
2
+ 'use strict';
3
+
4
+ var d3 = require('d3');
5
+
6
+ function _interopNamespaceDefault(e) {
7
+ var n = Object.create(null);
8
+ if (e) {
9
+ Object.keys(e).forEach(function (k) {
10
+ if (k !== 'default') {
11
+ var d = Object.getOwnPropertyDescriptor(e, k);
12
+ Object.defineProperty(n, k, d.get ? d : {
13
+ enumerable: true,
14
+ get: function () { return e[k]; }
15
+ });
16
+ }
17
+ });
18
+ }
19
+ n.default = e;
20
+ return Object.freeze(n);
21
+ }
22
+
23
+ var d3__namespace = /*#__PURE__*/_interopNamespaceDefault(d3);
24
+
2
25
  // Based on https://observablehq.com/@d3/voronoi-labels by Mike Bostock
3
26
 
4
27
  // ISC License
@@ -15,7 +38,7 @@ import * as d3 from "d3";
15
38
  // WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16
39
  // ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17
40
  // OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18
- export default function smartLabels(
41
+ function smartLabels(
19
42
  data,
20
43
  {
21
44
  x = (d) => d[0], // x coordinate accessor, expected to be in pixels
@@ -35,7 +58,7 @@ export default function smartLabels(
35
58
  labelsInCentroids = true,
36
59
  threshold = 2000, // Areas over this size would get labels
37
60
  alwaysShow = (d) => false, // If returns true for the node, it will always show the label
38
- showLabel = (d, cell) => alwaysShow(d) || -d3.polygonArea(cell) > threshold, // If true, show the label
61
+ showLabel = (d, cell) => alwaysShow(d) || -d3__namespace.polygonArea(cell) > threshold, // If true, show the label
39
62
 
40
63
  backgroundFill = "#fefefe01", // What to paint the bg rect of the labels. Needed for the onHover
41
64
  strokeWidth = 5,
@@ -76,8 +99,8 @@ export default function smartLabels(
76
99
  if (typeof font === "string") font = () => font;
77
100
  if (typeof hoverFont === "string") hoverFont = () => hoverFont;
78
101
 
79
- let xExtent = d3.extent(data, x),
80
- yExtent = d3.extent(data, y);
102
+ let xExtent = d3__namespace.extent(data, x),
103
+ yExtent = d3__namespace.extent(data, y);
81
104
  width = width || xExtent[1] - xExtent[0];
82
105
  height = height || yExtent[1] - yExtent[0];
83
106
 
@@ -111,21 +134,21 @@ export default function smartLabels(
111
134
  // Try to reuse the target
112
135
  if (renderer.toLocaleLowerCase() === "canvas") {
113
136
  if (target) {
114
- target = d3.select(target.node ? target.node() : target);
137
+ target = d3__namespace.select(target.node ? target.node() : target);
115
138
  } else {
116
- target = d3.create("canvas").attr("width", width).attr("height", height);
139
+ target = d3__namespace.create("canvas").attr("width", width).attr("height", height);
117
140
  }
118
141
  useOcclusion = false;
119
142
  } else {
120
143
  if (target) {
121
- target = d3.select(target.node ? target.node() : target);
144
+ target = d3__namespace.select(target.node ? target.node() : target);
122
145
  } else {
123
- target = d3.create("svg").attr("viewBox", [0, 0, width, height]);
146
+ target = d3__namespace.create("svg").attr("viewBox", [0, 0, width, height]);
124
147
  }
125
- target = target || d3.create("svg").attr("viewBox", [0, 0, width, height]);
148
+ target = target || d3__namespace.create("svg").attr("viewBox", [0, 0, width, height]);
126
149
  }
127
150
 
128
- const delaunay = d3.Delaunay.from(data, x, y);
151
+ const delaunay = d3__namespace.Delaunay.from(data, x, y);
129
152
  const voronoi = delaunay.voronoi([
130
153
  xExtent[0] - 1,
131
154
  yExtent[0] - 1,
@@ -213,7 +236,7 @@ export default function smartLabels(
213
236
  .attr("display", ({ show }) => (show ? null : "none"))
214
237
  .attr("d", ({ d, cell }, index) =>
215
238
  cell
216
- ? `M${d3.polygonCentroid(cell)}L${x(d, index)},${y(d, index)}`
239
+ ? `M${d3__namespace.polygonCentroid(cell)}L${x(d, index)},${y(d, index)}`
217
240
  : null
218
241
  );
219
242
  }
@@ -259,7 +282,7 @@ export default function smartLabels(
259
282
  .style("font", ({d}, i) => font(d, i))
260
283
  .each(function ({ d, cell }, index) {
261
284
  if (!cell) return;
262
- const [cx, cy] = d3.polygonCentroid(cell);
285
+ const [cx, cy] = d3__namespace.polygonCentroid(cell);
263
286
  const angle =
264
287
  (Math.round(
265
288
  (Math.atan2(cy - y(d, index), cx - x(d, index)) / Math.PI) * 2
@@ -267,7 +290,7 @@ export default function smartLabels(
267
290
  4) %
268
291
  4;
269
292
 
270
- d3.select(this).call(
293
+ d3__namespace.select(this).call(
271
294
  angle === 0
272
295
  ? orient.right
273
296
  : angle === 3
@@ -279,7 +302,7 @@ export default function smartLabels(
279
302
  })
280
303
  .attr("transform", ({ d, cell }, index) => {
281
304
  if (labelsInCentroids) {
282
- const [cx, cy] = d3.polygonCentroid(cell);
305
+ const [cx, cy] = d3__namespace.polygonCentroid(cell);
283
306
 
284
307
  return `translate(${cx}, ${cy})`;
285
308
  } else {
@@ -293,7 +316,7 @@ export default function smartLabels(
293
316
  if (hover) {
294
317
  mouseRect.on("mousemove", (event) => {
295
318
  // Find the selected point
296
- const i = delaunay.find(...d3.pointer(event));
319
+ const i = delaunay.find(...d3__namespace.pointer(event));
297
320
  if (i === selected) return;
298
321
  selected = i;
299
322
 
@@ -361,7 +384,7 @@ export default function smartLabels(
361
384
  if (showAnchors || labelsInCentroids) {
362
385
  cells.forEach(({ d, cell, show }, index) => {
363
386
  if (!show) return;
364
- const [cx, cy] = d3.polygonCentroid(cell);
387
+ const [cx, cy] = d3__namespace.polygonCentroid(cell);
365
388
  context.beginPath();
366
389
  context.moveTo(cx, cy);
367
390
  context.lineTo(x(d, index), y(d, index));
@@ -398,7 +421,7 @@ export default function smartLabels(
398
421
  function renderCanvasLabels(context, selected) {
399
422
  cells.forEach(({ d, cell, show }, index) => {
400
423
  if (!show && index !== selected) return;
401
- const [cx, cy] = d3.polygonCentroid(cell);
424
+ const [cx, cy] = d3__namespace.polygonCentroid(cell);
402
425
  const angle =
403
426
  (Math.round(
404
427
  (Math.atan2(cy - y(d, index), cx - x(d, index)) / Math.PI) * 2
@@ -451,7 +474,7 @@ export default function smartLabels(
451
474
  if (debug) console.log("setting onmousemove", target);
452
475
 
453
476
  target.on("mousemove", (event) => {
454
- const [mx, my] = d3.pointer(event);
477
+ const [mx, my] = d3__namespace.pointer(event);
455
478
  const i = delaunay.find(mx, my);
456
479
  if (i === selected) return;
457
480
  selected = i;
@@ -489,7 +512,7 @@ export default function smartLabels(
489
512
  // ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
490
513
  // OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
491
514
  function occlusion(svg, against = "text") {
492
- const nodes = d3
515
+ const nodes = d3__namespace
493
516
  .sort(svg.selectAll(against), (node) => +node.getAttribute("data-priority"))
494
517
  .reverse()
495
518
  .map((node) => {
@@ -500,7 +523,7 @@ function occlusion(svg, against = "text") {
500
523
  const visible = [];
501
524
  for (const d of nodes) {
502
525
  const occluded = visible.some((e) => intersectRect(d, e));
503
- d3.select(d.node).classed("occluded", occluded);
526
+ d3__namespace.select(d.node).classed("occluded", occluded);
504
527
  if (!occluded) visible.push(d);
505
528
  }
506
529
  return visible;
@@ -515,3 +538,5 @@ function intersectRect(a, b) {
515
538
  b.y + b.height < a.y
516
539
  );
517
540
  }
541
+
542
+ module.exports = smartLabels;
@@ -1,4 +1,4 @@
1
- // smart-labels v0.0.13 Copyright (c) 2024 John Alexis Guerra Gómez
1
+ // smart-labels v0.0.14 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
@@ -1,4 +1,4 @@
1
- // smart-labels v0.0.13 Copyright (c) 2024 John Alexis Guerra Gómez
1
+ // smart-labels v0.0.14 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) :
@@ -1,2 +1,2 @@
1
- // smart-labels v0.0.13 Copyright (c) 2024 John Alexis Guerra Gómez
1
+ // smart-labels v0.0.14 Copyright (c) 2024 John Alexis Guerra Gómez
2
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})}});
package/package.json CHANGED
@@ -1,13 +1,25 @@
1
1
  {
2
2
  "name": "smart-labels",
3
- "version": "0.0.13",
3
+ "version": "0.0.14",
4
4
  "description": "A basic library for adding labels to a visualization when there is enough space",
5
5
  "type": "module",
6
- "main": "dist/smartLabels.min.js",
6
+ "main": "dist/smartLabels.cjs",
7
7
  "module": "dist/smartLabels.es.js",
8
8
  "browser": "dist/smartLabels.min.js",
9
9
  "unpkg": "dist/smartLabels.min.js",
10
10
  "jsdelivr": "dist/smartLabels.min.js",
11
+ "exports": {
12
+ ".": {
13
+ "import": "./dist/smartLabels.es.js",
14
+ "require": "./dist/smartLabels.cjs",
15
+ "default": "./dist/smartLabels.min.js"
16
+ }
17
+ },
18
+ "files": [
19
+ "dist",
20
+ "LICENSE",
21
+ "README.md"
22
+ ],
11
23
  "scripts": {
12
24
  "build": "rollup -c",
13
25
  "dev": "rollup -c -w",
@@ -18,6 +30,7 @@
18
30
  "@rollup/plugin-json": "^6.1.0",
19
31
  "@rollup/plugin-node-resolve": "^15.2.3",
20
32
  "@rollup/plugin-terser": "^1.0.0",
33
+ "d3": "^7.9.0",
21
34
  "eslint": "^9.14.0",
22
35
  "eslint-config-prettier": "^9.1.0",
23
36
  "eslint-plugin-html": "^8.1.2",
@@ -26,7 +39,7 @@
26
39
  "rollup": "^4.20.0",
27
40
  "rollup-plugin-commonjs": "^10.1.0"
28
41
  },
29
- "dependencies": {
42
+ "peerDependencies": {
30
43
  "d3": "^7.9.0"
31
44
  },
32
45
  "author": {
@@ -34,6 +47,10 @@
34
47
  "url": "https://johnguerra.co"
35
48
  },
36
49
  "license": "MIT",
50
+ "repository": {
51
+ "type": "git",
52
+ "url": "git+https://github.com/john-guerra/smart-labels.git"
53
+ },
37
54
  "bugs": {
38
55
  "url": "https://github.com/john-guerra/smart-labels/issues"
39
56
  },
package/rollup.config.js DELETED
@@ -1,66 +0,0 @@
1
- import { readFileSync } from "fs";
2
- import node from "@rollup/plugin-node-resolve";
3
- import commonjs from "rollup-plugin-commonjs";
4
- import terser from "@rollup/plugin-terser";
5
- // import babel from "rollup-plugin-babel";
6
- import json from "@rollup/plugin-json";
7
- import meta from "./package.json" with { type: "json" };
8
-
9
- // Extract copyrights from the LICENSE.
10
- const copyrights = readFileSync("./LICENSE", "utf-8")
11
- .split(/\n/g)
12
- .filter((line) => /^copyright\s+/i.test(line))
13
- .map((line) => line.replace(/^copyright\s+/i, ""));
14
-
15
- const filename = "smartLabels";
16
-
17
- // Observable Plot style
18
- const config = {
19
- input: "src/index.js",
20
- external: ["d3"],
21
- output: {
22
- indent: false,
23
- banner: `// ${meta.name} v${meta.version} Copyright ${copyrights.join(", ")}`,
24
- extend: true,
25
- globals: {
26
- "d3": "d3",
27
- },
28
- name: "smartLabels",
29
- },
30
- plugins: [commonjs(), json(), node()],
31
- };
32
-
33
- export default [
34
- {
35
- ...config,
36
- output: {
37
- ...config.output,
38
- format: "umd",
39
- file: `dist/${filename}.js`,
40
- },
41
- },
42
- {
43
- ...config,
44
- output: {
45
- ...config.output,
46
- format: "es",
47
- file: `dist/${filename}.es.js`,
48
- },
49
- },
50
- {
51
- ...config,
52
- output: {
53
- ...config.output,
54
- format: "umd",
55
- file: `dist/${filename}.min.js`,
56
- },
57
- plugins: [
58
- ...config.plugins,
59
- terser({
60
- output: {
61
- preamble: config.output.banner,
62
- },
63
- }),
64
- ],
65
- },
66
- ];