bubble-chart-js 1.0.18 → 1.1.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.
Files changed (34) hide show
  1. package/README.md +39 -75
  2. package/dist/bubbleChart.cjs.js +1 -1
  3. package/dist/bubbleChart.esm.js +1 -1
  4. package/dist/bubbleChart.umd.js +1 -1
  5. package/examples/test.html +194 -0
  6. package/package.json +6 -3
  7. package/scripts/generate-fixtures.ts +45 -0
  8. package/spec/config-defaults.json +16 -0
  9. package/spec/fixtures/3-bubbles.input.json +10 -0
  10. package/spec/fixtures/equal-values.input.json +11 -0
  11. package/spec/fixtures/many-bubbles.input.json +17 -0
  12. package/spec/fixtures/single-bubble.input.json +8 -0
  13. package/spec/physics.json +11 -0
  14. package/dist/canvas.d.ts +0 -5
  15. package/dist/constants/app-constants.d.ts +0 -3
  16. package/dist/constants/physics.d.ts +0 -10
  17. package/dist/core/renderer.d.ts +0 -2
  18. package/dist/features/text-wrapper.d.ts +0 -2
  19. package/dist/features/tooltip.d.ts +0 -6
  20. package/dist/index.d.ts +0 -8
  21. package/dist/models/internal/data-item-info.d.ts +0 -7
  22. package/dist/models/public/bubble-chart.d.ts +0 -6
  23. package/dist/models/public/config/bubble-appearance.d.ts +0 -26
  24. package/dist/models/public/config/font-options.d.ts +0 -46
  25. package/dist/models/public/config/interaction-options.d.ts +0 -6
  26. package/dist/models/public/config/tooltip-config.d.ts +0 -4
  27. package/dist/models/public/config/tooltip-options.d.ts +0 -170
  28. package/dist/models/public/configuration.d.ts +0 -30
  29. package/dist/models/public/data-item.d.ts +0 -8
  30. package/dist/services/chart-service.d.ts +0 -5
  31. package/dist/services/render-service.d.ts +0 -3
  32. package/dist/utils/config.d.ts +0 -12
  33. package/dist/utils/helper.d.ts +0 -2
  34. package/dist/utils/validation.d.ts +0 -5
package/README.md CHANGED
@@ -1,41 +1,7 @@
1
- # bubble-chart-js
1
+ # bubbleChartJs
2
2
 
3
3
  bubbleChartJs is a lightweight and customizable JavaScript library for creating stacked bubble charts, arranging bubbles by size with the largest at the top.
4
4
 
5
- ![npm](https://img.shields.io/npm/v/bubble-chart-js)
6
- ![npm downloads](https://img.shields.io/npm/dm/bubble-chart-js)
7
- ![license](https://img.shields.io/npm/l/bubble-chart-js)
8
-
9
- > Built for dashboards with multiple widgets and real-world data visualization needs.
10
-
11
- ## Preview
12
-
13
- ![Stacked Bubble Chart](./assets/bubble-chart.png)
14
-
15
- ## Why bubble-chart-js?
16
-
17
- ✔ Automatically arranges bubbles by value (largest on top)
18
- ✔ Clean stacked layout – no overlaps
19
- ✔ Interactive tooltips & click events
20
- ✔ Works with plain JS & TypeScript
21
- ✔ Zero dependencies
22
-
23
- ## Quick Start
24
-
25
- ```js
26
- import { initializeChart } from "bubble-chart-js";
27
-
28
- initializeChart({
29
- canvasContainerId: "bubbleChart",
30
- data: [
31
- { label: "Orders", value: 207, bubbleColor: "#ff5733" },
32
- { label: "Returns", value: 96, bubbleColor: "#4caf50" },
33
- ],
34
- });
35
- ```
36
-
37
- That’s it. Chart rendered.
38
-
39
5
  ### ✨ Why Use a Stacked Bubble Chart?
40
6
 
41
7
  Multi-Dimensional Data Representation – Visualizes multiple datasets at once.
@@ -56,7 +22,21 @@ Customizable & Interactive – Allows tooltips.
56
22
 
57
23
  ✔️ Interactive tooltips and hover effects
58
24
 
59
- ### Installation
25
+ ### 📌 Use Cases
26
+
27
+ Financial Analysis – Display investment risks vs. returns for multiple assets.
28
+
29
+ Social Media Metrics – Visualize engagement levels across platforms.
30
+
31
+ Scientific Research – Show relationships in grouped experimental data.
32
+
33
+ ### 🎨 Example Output
34
+
35
+ Here’s an example of the bubble chart generated using this package:
36
+
37
+ ![Stacked Bubble Chart Example](https://github.com/Praga-Dev/bubbleChartJS/blob/HEAD/assets/bubble-chart.png)
38
+
39
+ ## Installation
60
40
 
61
41
  You can install `bubbleChartJs` via npm:
62
42
 
@@ -64,7 +44,9 @@ You can install `bubbleChartJs` via npm:
64
44
  npm install bubble-chart-js
65
45
  ```
66
46
 
67
- ## Advanced Usage
47
+ ## Usage
48
+
49
+ ### Basic Example
68
50
 
69
51
  ```js
70
52
  import BubbleChart from "bubblechartjs";
@@ -152,47 +134,29 @@ initializeChart(chartoptions);
152
134
 
153
135
  ## Configuration Options
154
136
 
155
- | Property | Type | Required | Optional | Description | Default |
156
- | ---------------------------------- | ------------ | -------- | -------- | ------------------------------------------------------------------------------------------------- | ----------- |
157
- | `canvasContainerId` | `string` | ✔️ Yes | ❌ No | The ID of the container where the chart will be rendered. | `-` |
158
- | `data` | `DataItem[]` | ✔️ Yes | ❌ No | An array of objects containing `label` and `value` for each bubble. | `-` |
159
- | `defaultBubbleColor` | `string` | ❌ No | ✔️ Yes | Default color for bubbles if not specified in `colorMap`. | `"#3498db"` |
160
- | `fontSize` | `number` | ❌ No | ✔️ Yes | Font size for bubble labels. | `14` |
161
- | `fontFamily` | `string` | ❌ No | ✔️ Yes | Font family for text rendering. | `"Arial"` |
162
- | `fontColor` | `string` | ❌ No | ✔️ Yes | Color of the text inside bubbles. | `"#ffffff"` |
163
- | `minRadius` | `number` | ❌ No | ✔️ Yes | Minimum radius for the bubbles. | `10` |
164
- | `maxLines` | `number` | ❌ No | ✔️ Yes | Maximum number of lines allowed for text wrapping. | `3` |
165
- | `textWrap` | `boolean` | ❌ No | ✔️ Yes | Enables or disables text wrapping inside bubbles. | `true` |
166
- | `isResizeCanvasOnWindowSizeChange` | `boolean` | ❌ No | ✔️ Yes | Whether the chart should resize when the window size changes. | `true` |
167
- | `showToolTip` | `boolean` | ❌ No | ✔️ Yes | Whether the chart should display the tooltip or not. | `true` |
168
- | `onBubbleClick` | `method` | ❌ No | ✔️ Yes | Callback function triggered when a bubble is clicked. Provides the clicked bubble data and event. | `true` |
169
-
170
- ✔️ **Required**: These properties must be provided.
171
- ✔️ **Optional**: If not provided, the default value will be used.
172
-
173
- ### 📌 Use Cases
174
-
175
- Financial Analysis – Display investment risks vs. returns for multiple assets.
137
+ The `BubbleChart` class accepts a configuration object with the following properties:
176
138
 
177
- Social Media Metrics – Visualize engagement levels across platforms.
178
-
179
- Scientific Research – Show relationships in grouped experimental data.
180
-
181
- ## Live Demo
182
-
183
- 🚀 **Try it live on Stackblitz**
184
- https://stackblitz.com/edit/bubble-chart-js?file=main.js
185
-
186
- 🚀 **Try it live on CodeSandbox**
187
- https://codesandbox.io/p/sandbox/xenodochial-cherry-4yrcnk
188
-
189
- ## 📦 Package
139
+ ## Configuration Options
190
140
 
191
- - **npm (public)**
192
- https://www.npmjs.com/package/bubble-chart-js
141
+ The `BubbleChart` class accepts a configuration object with the following properties:
142
+
143
+ | Property | Type | Required | Optional | Default |
144
+ | -------------------------------------------------------------------------------------- | ------------ | -------- | -------- | ----------- |
145
+ | **`canvasContainerId`**<br/>ID of the container where the chart will be rendered | `string` | ✔️ Yes | ❌ No | `-` |
146
+ | **`data`**<br/>Array of objects containing `label` and `value` for each bubble | `DataItem[]` | ✔️ Yes | ❌ No | `-` |
147
+ | **`defaultBubbleColor`**<br/>Default color used when bubble color is not provided | `string` | ❌ No | ✔️ Yes | `"#3498db"` |
148
+ | **`fontSize`**<br/>Font size for bubble labels | `number` | ❌ No | ✔️ Yes | `14` |
149
+ | **`fontFamily`**<br/>Font family used for text rendering | `string` | ❌ No | ✔️ Yes | `"Arial"` |
150
+ | **`fontColor`**<br/>Text color inside bubbles | `string` | ❌ No | ✔️ Yes | `"#ffffff"` |
151
+ | **`minRadius`**<br/>Minimum radius of a bubble | `number` | ❌ No | ✔️ Yes | `10` |
152
+ | **`maxLines`**<br/>Maximum number of lines allowed for text wrapping | `number` | ❌ No | ✔️ Yes | `3` |
153
+ | **`textWrap`**<br/>Enable or disable text wrapping inside bubbles | `boolean` | ❌ No | ✔️ Yes | `true` |
154
+ | **`isResizeCanvasOnWindowSizeChange`**<br/>Automatically resize chart on window resize | `boolean` | ❌ No | ✔️ Yes | `true` |
155
+ | **`showToolTip`**<br/>Toggle tooltip visibility | `boolean` | ❌ No | ✔️ Yes | `true` |
156
+ | **`onBubbleClick`**<br/>Callback fired when a bubble is clicked | `method` | ❌ No | ✔️ Yes | `-` |
193
157
 
194
- - **GitHub Packages (npm)**
195
- https://github.com/Praga-Dev?tab=packages
158
+ ✔️ **Required**: These properties must be provided.
159
+ ✔️ **Optional**: If not provided, the default value will be used.
196
160
 
197
161
  ## License
198
162
 
@@ -1 +1 @@
1
- (()=>{"use strict";var t={d:(e,n)=>{for(var o in n)t.o(n,o)&&!t.o(e,o)&&Object.defineProperty(e,o,{enumerable:!0,get:n[o]})},o:(t,e)=>Object.prototype.hasOwnProperty.call(t,e),r:t=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(t,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(t,"__esModule",{value:!0})}},e={};t.r(e),t.d(e,{BubbleChart:()=>h,initializeChart:()=>c});let n="default";function o(t,e){if(!e)return console.error("Invalid containerId."),null;const n=document.getElementById(e);if(!n)return console.error("Container not found."),null;const o=n.parentElement;return o?o.querySelector("#bubbleChartTooltip")||(o.prepend(t),t):(console.error("Parent container not found."),null)}function i(t,e){const n=e.getBoundingClientRect();return{mouseX:t.clientX-n.left,mouseY:t.clientY-n.top}}function a(t,e,n){return n.find(n=>Math.hypot(t-n.x,e-n.y)<n.radius)||null}class r{}r.TRANSPARENT="transparent";const l={forceStrength:.008,iterations:1e3,damping:.65,boundaryForce:.02,centerForce:.12,centerAttraction:.8,centerDamping:.3,centerRadiusBuffer:35};function s(t,e=14,n=400,o=6){const i=n>=700?1.25:n>=500?1.1:1,a=Math.min(t/o,t/1.2);let r=Math.min(e*i,a,.8*t);return r=Math.max(r,Math.max(8,t/6)),Math.round(r)}function d(t){if(!function(t){return t?!(!Array.isArray(t.data)||0===t.data.length)||(console.error("Invalid or empty data array"),!1):(console.error("Invalid config object"),!1)}(t))return;let e=function(t){var e,n;const o=document.getElementById(t.canvasContainerId);if(!o)return console.error(`Canvas container with ID '${t.canvasContainerId}' not found.`),null;if(o.querySelector("canvas"))return console.error("A canvas already exists inside the container."),null;const i=document.createElement("canvas");return Object.assign(i.style,{border:(null===(e=t.canvasBorderColor)||void 0===e?void 0:e.trim())?`1px solid #${t.canvasBorderColor}`:r.TRANSPARENT,background:(null===(n=t.canvasBackgroundColor)||void 0===n?void 0:n.trim())?`#${t.canvasBackgroundColor}`:r.TRANSPARENT,width:"100%",height:"100%",display:"block",imageRendering:"crisp-edges",aspectRatio:"1 / 1"}),i.style.setProperty("image-rendering","-moz-crisp-edges"),i.style.setProperty("image-rendering","-webkit-optimize-contrast"),i.style.setProperty("-ms-interpolation-mode","nearest-neighbor"),o.appendChild(i),i}(t);if(!e)return;const d=e.getContext("2d");if(!d)return void console.error("Invalid context");const{width:u,height:c}=m(e,d),h=function(t,e,n){const o=Math.min((e-10)/2,(n-10)/2),i=e/2,a=n/2,r=[...t.data].sort((t,e)=>e.value-t.value).map(t=>Object.assign(Object.assign({},t),{radius:0,x:0,y:0,fixed:!1})),s=r[0].value,d=Math.min(.5*o,.2*Math.min(e,n)),u=Math.max(.3*d,.05*Math.min(e,n));r.forEach(t=>{const o=t.value/s;t.radius=u+o*(d-u),t.radius=Math.min(t.radius,(e-10)/2,(n-10)/2)}),r.forEach((t,o)=>{if(0===o)t.x=i,t.y=a,t.fixed=!0;else{const l=r[0].radius+t.radius+3,s=Math.PI*(3-Math.sqrt(5)),d=e-5-t.radius,u=n-5-t.radius;t.x=Math.min(d,Math.max(5+t.radius,i+Math.cos(s*o)*l)),t.y=Math.min(u,Math.max(5+t.radius,a+Math.sin(s*o)*l)),t.fixed=!1}});for(let t=0;t<l.iterations;t++)r.forEach((t,n)=>{if(0===n){const e=i-t.x,n=a-t.y;return void(Math.hypot(e,n)>2&&(t.x+=e*l.centerDamping,t.y+=n*l.centerDamping))}let o=0,d=0;const u=t.radius+5;t.x<u?o+=(u-t.x)*l.boundaryForce:t.x>e-u&&(o+=(e-u-t.x)*l.boundaryForce),r.forEach(e=>{if(t===e)return;const n=t.x-e.x,r=t.y-e.y,u=Math.hypot(n,r),c=t.radius+e.radius;if(u<1.5*c){const t=l.forceStrength*(c/Math.max(u,.1));o+=n/u*t,d+=r/u*t}const h=i-t.x,f=a-t.y,m=Math.hypot(h,f),y=t.value/s*.02;t.x+=h/m*y,t.y+=f/m*y});const c=i-t.x,h=a-t.y,f=Math.hypot(c,h),m=r[0].radius+t.radius+l.centerRadiusBuffer,y=(l.centerForce,Math.pow(t.value/s,.3),l.centerAttraction*(1-t.value/s)*(1-Math.min(1,f/m)));t.x+=c*y,t.y+=h*y,t.x+=o*(1-l.damping),t.y+=d*(1-l.damping)}),r.forEach((t,e)=>{r.forEach((n,o)=>{if(e>=o)return;if(0===e||0===o){const o=0===e?t:n,i=0===e?n:t,a=i.x-o.x,r=i.y-o.y,l=Math.hypot(a,r),s=o.radius+i.radius+2;if(l<s){const t=s-l,e=Math.atan2(r,a);i.x+=Math.cos(e)*t*.7,i.y+=Math.sin(e)*t*.7}return}const i=t.x-n.x,a=t.y-n.y,r=Math.hypot(i,a),s=t.radius+n.radius-5;if(r<s){const e=(s-r)*(.3+5*l.forceStrength),o=Math.atan2(a,i),d=n.radius/(t.radius+n.radius);t.fixed||(t.x+=Math.cos(o)*e*d,t.y+=Math.sin(o)*e*d),n.fixed||(n.x-=Math.cos(o)*e*(1-d),n.y-=Math.sin(o)*e*(1-d))}})});return r.forEach(t=>{const o=Math.max(5+t.radius,Math.min(e-5-t.radius,t.x)),i=Math.max(5+t.radius,Math.min(n-5-t.radius,t.y));(!t.fixed||Math.hypot(t.x-o,t.y-i)>2)&&(t.x=o,t.y=i)}),r}(t,u,c);function f(){e&&d?(m(e,d),d.clearRect(0,0,e.width,e.height),h.forEach(e=>{const{x:n,y:o,radius:i,bubbleColor:a,borderColor:r,borderThickness:l,opacity:u,fontColor:c,fontFamily:h,fontSize:f,fontStyle:m,fontWeight:y,textAlign:v,textTransform:g,textBaseline:p}=function(t,e){var n,o,i,a,r,l,d,u,c,h,f,m;return{x:t.x,y:t.y,radius:Math.max(t.radius||0,e.minRadius||10),bubbleColor:null!==(o=null!==(n=t.bubbleColor)&&void 0!==n?n:e.defaultBubbleColor)&&void 0!==o?o:"#3498db",borderColor:null!==(i=t.borderColor)&&void 0!==i?i:"black",borderThickness:Math.max(null!==(a=t.borderThickness)&&void 0!==a?a:.25,0),opacity:void 0!==t.opacity?Math.max(0,Math.min(1,t.opacity)):1,fontStyle:t.fontStyle||"normal",fontWeight:"number"==typeof t.fontWeight&&t.fontWeight>=100&&t.fontWeight<=900?t.fontWeight:400,textAlign:null!==(r=t.textAlign)&&void 0!==r?r:"center",textTransform:null!==(l=t.textTransform)&&void 0!==l?l:"none",fontColor:null!==(u=null!==(d=t.fontColor)&&void 0!==d?d:e.defaultFontColor)&&void 0!==u?u:"#000",textBaseline:null!==(c=t.textBaseline)&&void 0!==c?c:"middle",fontSize:Math.max(s(t.radius||e.minRadius||10,null!==(h=e.fontSize)&&void 0!==h?h:14,null!==(f=t.fontWeight)&&void 0!==f?f:400),8),fontFamily:t.fontFamily&&"string"==typeof t.fontFamily&&"Arial"!==t.fontFamily?`${t.fontFamily}, Arial, sans-serif`:`${null!==(m=e.defaultFontFamily)&&void 0!==m?m:"Arial"}, sans-serif`}}(e,t);d.beginPath(),d.arc(n,o,i,0,2*Math.PI),d.fillStyle=a,d.fill(),d.strokeStyle=r,d.lineWidth=l,d.stroke();const x=`${m} ${y} ${f}px ${h}`;d.fillStyle=c,d.font=x,d.imageSmoothingEnabled=!0,d.imageSmoothingQuality="high",d.textAlign=v,d.textBaseline=p;let b=e.label||"";if("uppercase"===g?b=b.toUpperCase():"lowercase"===g?b=b.toLowerCase():"capitalize"===g&&(b=b.replace(/\b\w/g,t=>t.toUpperCase())),t.textWrap){const a=1.5*i-10,r=1.4*f,l=function(t,e,n,o,i,a=14,r=400,l="normal",s="Arial",d=0,u=5,c=1.2){const h=1.5*(i-u),f=a*c,m=Math.max(1,Math.floor(h/f)),y="auto"===o||void 0===o?m:Math.min(o,m);n=Math.max(0,n-2*d);const v=e.split(" ");let g="";const p=[];let x=!1,b=!1;t.font=`${r||""} ${l||""} ${a}px ${s}`,t.imageSmoothingEnabled=!0,t.imageSmoothingQuality="high";for(const e of v){const o=g?`${g} ${e}`:e;if(t.measureText(o).width<=n)g=o;else if(g.trim()&&p.push(g),g=e,t.measureText(g).width>n){let e=g;for(;t.measureText(e+"...").width>n&&e.length>0;)e=e.slice(0,-1);p.push(e+"..."),b=!0;break}if(p.length>=y){x=!0;break}}if(g&&p.length<y&&!b&&p.push(g),x&&p.length>0){let e=p[p.length-1];for(;t.measureText(e+"...").width>n&&e.length>0;)e=e.slice(0,-1);p[p.length-1]=e+"..."}return p}(d,e.label,a,t.maxLines,i,f,y,m,h),s=o-(l.length-1)*r/2;l.forEach((t,e)=>{const o=Math.round(n),i=Math.round(s+e*r);d.fillText(t,o,i)})}else d.fillText(e.label,Math.round(n),Math.round(o))})):console.warn("canvas or ctx is not valid")}function m(t,e){const n=window.devicePixelRatio||1,o=t.getBoundingClientRect();return t.width=o.width*n,t.height=o.height*n,t.style.width=`${o.width}px`,t.style.height=`${o.height}px`,e.setTransform(1,0,0,1,0,0),e.scale(n,n),{width:o.width,height:o.height,dpr:n}}function y(){const n=document.getElementById(t.canvasContainerId);n&&e&&(e.width=n.offsetWidth,e.height=n.offsetHeight,e.getContext("2d"),f())}let v;t.isResizeCanvasOnWindowSizeChange&&(y(),window.addEventListener("resize",y)),f(),t.showToolTip&&(v=function(t){var e,n,i,a,r,l,s,d,u,c,h,f,m,y,v,g,p,x;const b=null===(n=null===(e=t.data[0].toolTipConfig)||void 0===e?void 0:e.tooltipFormattedData)||void 0===n?void 0:n.trim();if(b){const e=document.createElement("div");e.innerHTML=b.trim();const n=e.firstElementChild;return n.style.display="none",n.style.visibility="hidden",n.style.opacity="0",n.style.position="absolute",o(n,t.canvasContainerId)}const M=document.createElement("div");M.id="bubbleChartTooltip",M.style.display="none";const C=null!==(i=null==t?void 0:t.tooltipOptions)&&void 0!==i?i:{},S=(t,e)=>"number"==typeof t?`${t}px`:null!=t?t:e;return Object.assign(M.style,{position:"absolute",padding:S(C.padding,"8px"),margin:S(C.margin,"0"),background:null!==(a=C.backgroundColor)&&void 0!==a?a:"rgba(0, 0, 0, 0.85)",color:null!==(r=C.fontColor)&&void 0!==r?r:"white",borderRadius:"4px",pointerEvents:null!==(l=C.pointerEvents)&&void 0!==l?l:"none",fontFamily:null!==(s=C.fontFamily)&&void 0!==s?s:"Arial, sans-serif",fontSize:S(C.fontSize,"14px"),fontWeight:String(null!==(d=C.fontWeight)&&void 0!==d?d:"400"),fontStyle:null!==(u=C.fontStyle)&&void 0!==u?u:"normal",textAlign:null!==(c=C.textAlign)&&void 0!==c?c:"left",textDecoration:null!==(h=C.textDecoration)&&void 0!==h?h:"none",textTransform:null!==(f=C.textTransform)&&void 0!==f?f:"none",letterSpacing:S(C.letterSpacing,"normal"),border:(()=>{var t;if(!C.borderStyle)return"none";const e=S(C.borderWidth,"1px"),n=null!==(t=C.borderColor)&&void 0!==t?t:"transparent";return`${e} ${C.borderStyle} ${n}`})(),boxShadow:null!==(m=C.boxShadow)&&void 0!==m?m:"none",maxWidth:S(C.maxWidth,"200px"),minWidth:S(C.minWidth,"auto"),maxHeight:S(C.maxHeight,"none"),minHeight:S(C.minHeight,"auto"),zIndex:String(null!==(y=C.zIndex)&&void 0!==y?y:1e3),transition:null!==(v=C.transition)&&void 0!==v?v:"opacity 0.2s",transform:null!==(g=C.transform)&&void 0!==g?g:"none",backdropFilter:null!==(p=C.backdropFilter)&&void 0!==p?p:"none",opacity:String(null!==(x=C.opacity)&&void 0!==x?x:1),display:"none",visibility:"hidden"}),o(M,t.canvasContainerId),M}(t));let g=null;if(e.addEventListener("mousemove",o=>{g||(g=requestAnimationFrame(()=>{(function(t,e,o,r,l){const{mouseX:s,mouseY:d}=i(t,o),u=a(s,d,e);n=(null==l?void 0:l.cursorType)||"pointer",o.style.cursor="default",r&&(u?function(t,e,o,i){if(e&&e.value&&o&&i){o.style.cursor=n,i.style.display="block",i.innerHTML=function(t){var e,n,o;if(!t)return"";const i=null===(n=null===(e=t.toolTipConfig)||void 0===e?void 0:e.tooltipText)||void 0===n?void 0:n.trim();if(i)return`<div>${i}</div>`;const a=null===(o=t.label)||void 0===o?void 0:o.trim();return a?`<div>${a}<br>Value: ${t.value}</div>`:`<div>${t.value}</div>`}(e);const a=o.getBoundingClientRect();i.style.left=t.clientX-a.left+15+"px",i.style.top=t.clientY-a.top+15+"px",i.style.zIndex="999999",i.style.visibility="visible",i.style.opacity="1",i.style.position="absolute"}}(t,u,o,r):(o.style.cursor="default",r.style.display="none",r.style.visibility="hidden",r.style.opacity="0"))})(o,h,e,v,t),g=null}))}),t.onBubbleClick){let n=null;e.addEventListener("click",o=>{n||(n=requestAnimationFrame(()=>{!function(t,e,n,o){const{mouseX:r,mouseY:l}=i(t,n),s=a(r,l,e);null!=s&&o.onBubbleClick&&o.onBubbleClick(s,t)}(o,h,e,t),n=null}))})}}const u={defaultBubbleColor:"#3498DB",defaultFontColor:"#ffffff",minRadius:10,maxLines:"auto",textWrap:!0,isResizeCanvasOnWindowSizeChange:!0,fontSize:14,defaultFontFamily:"Arial",showToolTip:!0};function c(t={}){var e,n;if(!t)return void console.error("Configuration is not valid. Chart initialization aborted.");if(!t.data||0===t.data.length)return void console.error("No valid data provided. Chart initialization aborted.");const o=(i=Object.assign({canvasContainerId:null!==(e=t.canvasContainerId)&&void 0!==e?e:"chart-container",data:null!==(n=t.data)&&void 0!==n?n:[]},t),Object.assign(Object.assign({},u),i));var i;return d(o),o}class h{constructor(t){const e=c(t);e&&(this.configuration=e)}render(){this.configuration&&d(this.configuration)}}window.initializeChart=c,module.exports=e})();
1
+ (()=>{"use strict";var t={d:(e,n)=>{for(var i in n)t.o(n,i)&&!t.o(e,i)&&Object.defineProperty(e,i,{enumerable:!0,get:n[i]})},o:(t,e)=>Object.prototype.hasOwnProperty.call(t,e),r:t=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(t,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(t,"__esModule",{value:!0})}},e={};t.r(e),t.d(e,{BubbleChart:()=>m,initializeChart:()=>v});const n={defaultBubbleColor:"#3498DB",defaultFontColor:"#ffffff",minRadius:10,maxLines:"auto",textWrap:!0,isResizeCanvasOnWindowSizeChange:!0,fontSize:14,defaultFontFamily:"Arial",showToolTip:!0},i=JSON.parse('{"FC":0.008,"rX":1000,"d6":0.65,"WH":0.02,"ND":0.12,"$l":0.8,"cg":0.3,"e0":35}');function o(t,e){t.forEach((n,i)=>{t.forEach((t,o)=>{if(i>=o)return;if(0===i||0===o){const e=0===i?n:t,o=0===i?t:n,a=o.x-e.x,r=o.y-e.y,l=Math.hypot(a,r),s=e.radius+o.radius+2;if(l<s){const t=s-l,e=Math.atan2(r,a);o.x+=Math.cos(e)*t*.7,o.y+=Math.sin(e)*t*.7}return}const a=n.x-t.x,r=n.y-t.y,l=Math.hypot(a,r),s=n.radius+t.radius-5;if(l<s){const i=(s-l)*(.3+5*e.forceStrength),o=Math.atan2(r,a),d=n.radius+t.radius,c=d>0?t.radius/d:.5;n.fixed||(n.x+=Math.cos(o)*i*c,n.y+=Math.sin(o)*i*c),t.fixed||(t.x-=Math.cos(o)*i*(1-c),t.y-=Math.sin(o)*i*(1-c))}})})}function a(t,e=14,n=400,i=6){const o=n>=700?1.25:n>=500?1.1:1,a=Math.min(t/i,t/1.2);let r=Math.min(e*o,a,.8*t);return r=Math.max(r,Math.max(8,t/6)),Math.round(r)}class r{constructor(t){this.canvas=t;const e=t.getContext("2d");if(!e)throw new Error("Failed to get 2D rendering context from canvas");this.ctx=e}getContext(){return this.ctx}getCanvas(){return this.canvas}setup(){const t=window.devicePixelRatio||1,e=this.canvas.getBoundingClientRect();return this.canvas.width=e.width*t,this.canvas.height=e.height*t,this.canvas.style.width=`${e.width}px`,this.canvas.style.height=`${e.height}px`,this.ctx.setTransform(1,0,0,1,0,0),this.ctx.scale(t,t),{width:e.width,height:e.height}}getSize(){const t=this.canvas.getBoundingClientRect();return{width:t.width,height:t.height}}clear(){this.ctx.clearRect(0,0,this.canvas.width,this.canvas.height)}drawCircle(t,e,n,i,o,a,r){this.ctx.beginPath(),this.ctx.arc(t,e,n,0,2*Math.PI),this.ctx.fillStyle=i,this.ctx.fill(),a>0&&(this.ctx.strokeStyle=o,this.ctx.lineWidth=a,this.ctx.stroke())}drawText(t,e,n,i){const o=`${i.fontStyle} ${i.fontWeight} ${i.fontSize}px ${i.fontFamily}`;this.ctx.fillStyle=i.fontColor,this.ctx.font=o,this.ctx.imageSmoothingEnabled=!0,this.ctx.imageSmoothingQuality="high",this.ctx.textAlign=i.textAlign,this.ctx.textBaseline=i.textBaseline,this.ctx.fillText(t,Math.round(e),Math.round(n))}destroy(){const t=this.canvas.parentElement;t&&t.removeChild(this.canvas)}}class l{constructor(t){this.ctx=t}measureWidth(t,e,n,i,o){return this.ctx.font=`${i} ${n} ${e}px ${o}`,this.ctx.imageSmoothingEnabled=!0,this.ctx.imageSmoothingQuality="high",this.ctx.measureText(t).width}}function s(t,e){const n=`bubbleChartTooltip-${e}`;return document.body.querySelector(`#${n}`)||(t.id=n,document.body.appendChild(t),t)}function d(t,e){const n=e.getBoundingClientRect(),i=window.devicePixelRatio||1;return{mouseX:(t.clientX-n.left)*i,mouseY:(t.clientY-n.top)*i}}function c(t,e,n){return n.find(n=>Math.hypot(t-n.x,e-n.y)<n.radius)||null}function u(t){let e=null;return{handle:n=>{e||(e=requestAnimationFrame(()=>{t(n),e=null}))},cancel:()=>{e&&(cancelAnimationFrame(e),e=null)}}}class h{}function f(t){if(!function(t){return t?!(!Array.isArray(t.data)||0===t.data.length)||(console.error("Invalid or empty data array"),!1):(console.error("Invalid config object"),!1)}(t))return;const e=function(t){var e,n;const i=document.getElementById(t.canvasContainerId);if(!i)return console.error(`Canvas container with ID '${t.canvasContainerId}' not found.`),null;if(i.querySelector("canvas"))return console.error("A canvas already exists inside the container."),null;const o=document.createElement("canvas");return Object.assign(o.style,{border:(null===(e=t.canvasBorderColor)||void 0===e?void 0:e.trim())?`1px solid #${t.canvasBorderColor}`:h.TRANSPARENT,background:(null===(n=t.canvasBackgroundColor)||void 0===n?void 0:n.trim())?`#${t.canvasBackgroundColor}`:h.TRANSPARENT,width:"100%",height:"100%",display:"block",imageRendering:"crisp-edges",aspectRatio:"1 / 1"}),o.style.setProperty("image-rendering","-moz-crisp-edges"),o.style.setProperty("image-rendering","-webkit-optimize-contrast"),o.style.setProperty("-ms-interpolation-mode","nearest-neighbor"),i.appendChild(o),o}(t);if(!e)return;const n=new r(e),m=new l(n.getContext()),{width:v,height:y}=n.setup();let x=function(t,e,n,a){const r=Object.assign({forceStrength:i.FC,iterations:i.rX,damping:i.d6,boundaryForce:i.WH,centerForce:i.ND,centerAttraction:i.$l,centerDamping:i.cg,centerRadiusBuffer:i.e0},a),l=Math.min((e-10)/2,(n-10)/2),s=e/2,d=n/2,c=[...t].sort((t,e)=>e.value-t.value).map(t=>Object.assign(Object.assign({},t),{radius:0,x:0,y:0,fixed:!1}));if(0===c.length)return c;const u=c[0].value,h=Math.min(.5*l,.2*Math.min(e,n)),f=Math.max(.3*h,.05*Math.min(e,n));return c.forEach(t=>{const i=u>0?t.value/u:1;t.radius=f+i*(h-f),t.radius=Math.max(0,Math.min(t.radius,(e-10)/2,(n-10)/2))}),c.forEach((t,i)=>{if(0===i)t.x=s,t.y=d,t.fixed=!0;else{const o=c[0].radius+t.radius+3,a=Math.PI*(3-Math.sqrt(5)),r=e-5-t.radius,l=n-5-t.radius;t.x=Math.min(r,Math.max(5+t.radius,s+Math.cos(a*i)*o)),t.y=Math.min(l,Math.max(5+t.radius,d+Math.sin(a*i)*o)),t.fixed=!1}}),function(t,e,n,i,a,r,l){for(let i=0;i<e.iterations;i++)t.forEach((i,o)=>{if(0===o){const t=a-i.x,n=r-i.y;return void(Math.hypot(t,n)>2&&(i.x+=t*e.centerDamping,i.y+=n*e.centerDamping))}let s=0,d=0;const c=i.radius+5;i.x<c?s+=(c-i.x)*e.boundaryForce:i.x>n-c&&(s+=(n-c-i.x)*e.boundaryForce),t.forEach(t=>{if(i===t)return;const n=i.x-t.x,o=i.y-t.y,c=Math.hypot(n,o),u=i.radius+t.radius;if(c<1.5*u){const t=e.forceStrength*(u/Math.max(c,.1));s+=n/c*t,d+=o/c*t}const h=a-i.x,f=r-i.y,m=Math.hypot(h,f),v=i.value/l*.02;i.x+=h/m*v,i.y+=f/m*v});const u=a-i.x,h=r-i.y,f=Math.hypot(u,h),m=t[0].radius+i.radius+e.centerRadiusBuffer,v=e.centerAttraction*(1-i.value/l)*(1-Math.min(1,f/m));i.x+=u*v,i.y+=h*v,i.x+=s*(1-e.damping),i.y+=d*(1-e.damping)}),o(t,e)}(c,r,e,0,s,d,u),function(t,e,n){t.forEach(t=>{const i=Math.max(5+t.radius,Math.min(e-5-t.radius,t.x)),o=Math.max(5+t.radius,Math.min(n-5-t.radius,t.y));(!t.fixed||Math.hypot(t.x-i,t.y-o)>2)&&(t.x=i,t.y=o)})}(c,e,n),c}(t.data,v,y);function g(){n.setup(),n.clear(),x.forEach(e=>{const i=function(t,e,n){var i,o,r,l,s,d,c,u,h,f,m;let v=null!==(i=n.defaultBubbleColor)&&void 0!==i?i:"#3498db";if(t.bubbleColor)v=t.bubbleColor;else if(n.colorPalette&&n.colorPalette.length>0){const i=e.findIndex(e=>e.label===t.label&&e.value===t.value),o=i>=0?i:0;v=n.colorPalette[o%n.colorPalette.length]}const y=Math.max(t.radius||0,n.minRadius||10);return{bubbleColor:v,borderColor:null!==(o=t.borderColor)&&void 0!==o?o:"black",borderThickness:Math.max(null!==(r=t.borderThickness)&&void 0!==r?r:.25,0),opacity:void 0!==t.opacity?Math.max(0,Math.min(1,t.opacity)):1,fontStyle:t.fontStyle||"normal",fontWeight:"number"==typeof t.fontWeight&&t.fontWeight>=100&&t.fontWeight<=900?t.fontWeight:400,textAlign:null!==(l=t.textAlign)&&void 0!==l?l:"center",textTransform:null!==(s=t.textTransform)&&void 0!==s?s:"none",fontColor:null!==(c=null!==(d=t.fontColor)&&void 0!==d?d:n.defaultFontColor)&&void 0!==c?c:"#000",textBaseline:null!==(u=t.textBaseline)&&void 0!==u?u:"middle",fontSize:Math.max(a(y,null!==(h=n.fontSize)&&void 0!==h?h:14,null!==(f=t.fontWeight)&&void 0!==f?f:400),8),fontFamily:t.fontFamily&&"string"==typeof t.fontFamily&&"Arial"!==t.fontFamily?`${t.fontFamily}, Arial, sans-serif`:`${null!==(m=n.defaultFontFamily)&&void 0!==m?m:"Arial"}, sans-serif`}}(e,x,t);n.drawCircle(e.x,e.y,Math.max(e.radius||0,t.minRadius||10),i.bubbleColor,i.borderColor,i.borderThickness,i.opacity);let o=e.label||"";"uppercase"===i.textTransform?o=o.toUpperCase():"lowercase"===i.textTransform?o=o.toLowerCase():"capitalize"===i.textTransform&&(o=o.replace(/\b\w/g,t=>t.toUpperCase()));const r=Math.max(e.radius||0,t.minRadius||10),l={fontSize:i.fontSize,fontFamily:i.fontFamily,fontWeight:i.fontWeight,fontStyle:i.fontStyle,fontColor:i.fontColor,textAlign:i.textAlign,textBaseline:i.textBaseline};if(t.textWrap){const o=1.5*r-10,a=1.4*i.fontSize,s=function(t,e,n,i,o,a=14,r=400,l="normal",s="Arial",d=0,c=5,u=1.2){const h=1.5*(o-c),f=a*u,m=Math.max(1,Math.floor(h/f)),v="auto"===i||void 0===i?m:Math.min(i,m);n=Math.max(0,n-2*d);const y=e.split(" ");let x="";const g=[];let p=!1,b=!1;const C=e=>t.measureWidth(e,a,r,l,s);for(const t of y){const e=x?`${x} ${t}`:t;if(C(e)<=n)x=e;else if(x.trim()&&g.push(x),x=t,C(x)>n){let t=x;for(;C(t+"...")>n&&t.length>0;)t=t.slice(0,-1);g.push(t+"..."),b=!0;break}if(g.length>=v){p=!0;break}}if(x&&g.length<v&&!b&&g.push(x),p&&g.length>0){let t=g[g.length-1];for(;C(t+"...")>n&&t.length>0;)t=t.slice(0,-1);g[g.length-1]=t+"..."}return g}(m,e.label,o,t.maxLines,r,i.fontSize,i.fontWeight,i.fontStyle,i.fontFamily),d=e.y-(s.length-1)*a/2;s.forEach((t,i)=>{n.drawText(t,e.x,d+i*a,l)})}else n.drawText(o,e.x,e.y,l)})}let p=null;if(t.isResizeCanvasOnWindowSizeChange){const e=document.getElementById(t.canvasContainerId);e&&(p=function(t,e){let n=null,i=null;const o=()=>{i||(i=requestAnimationFrame(()=>{e(),i=null}))};return"undefined"!=typeof ResizeObserver?(n=new ResizeObserver(o),n.observe(t)):(o(),window.addEventListener("resize",o)),{destroy:()=>{n&&(n.disconnect(),n=null),window.removeEventListener("resize",o),i&&(cancelAnimationFrame(i),i=null)}}}(e,()=>{const t=n.getCanvas();e&&t&&(t.width=e.offsetWidth,t.height=e.offsetHeight,g())}))}g();let b=null;t.showToolTip&&(b=function(t){var e,n,i,o,a,r,l,d,c,u,h,f,m,v,y,x,g,p,b;const C=null===(i=null===(n=null===(e=t.data[0])||void 0===e?void 0:e.toolTipConfig)||void 0===n?void 0:n.tooltipFormattedData)||void 0===i?void 0:i.trim();if(C){const e=document.createElement("div");e.innerHTML=C.trim();const n=e.firstElementChild;return n.style.display="none",n.style.visibility="hidden",n.style.opacity="0",n.style.position="absolute",s(n,t.canvasContainerId)}const M=document.createElement("div");M.style.display="none";const w=null!==(o=null==t?void 0:t.tooltipOptions)&&void 0!==o?o:{},S=(t,e)=>"number"==typeof t?`${t}px`:null!=t?t:e;return Object.assign(M.style,{position:"absolute",padding:S(w.padding,"8px"),margin:S(w.margin,"0"),background:null!==(a=w.backgroundColor)&&void 0!==a?a:"rgba(0, 0, 0, 0.85)",color:null!==(r=w.fontColor)&&void 0!==r?r:"white",borderRadius:"4px",pointerEvents:null!==(l=w.pointerEvents)&&void 0!==l?l:"none",fontFamily:null!==(d=w.fontFamily)&&void 0!==d?d:"Arial, sans-serif",fontSize:S(w.fontSize,"14px"),fontWeight:String(null!==(c=w.fontWeight)&&void 0!==c?c:"400"),fontStyle:null!==(u=w.fontStyle)&&void 0!==u?u:"normal",textAlign:null!==(h=w.textAlign)&&void 0!==h?h:"left",textDecoration:null!==(f=w.textDecoration)&&void 0!==f?f:"none",textTransform:null!==(m=w.textTransform)&&void 0!==m?m:"none",letterSpacing:S(w.letterSpacing,"normal"),border:(()=>{var t;if(!w.borderStyle)return"none";const e=S(w.borderWidth,"1px"),n=null!==(t=w.borderColor)&&void 0!==t?t:"transparent";return`${e} ${w.borderStyle} ${n}`})(),boxShadow:null!==(v=w.boxShadow)&&void 0!==v?v:"none",maxWidth:S(w.maxWidth,"200px"),minWidth:S(w.minWidth,"auto"),maxHeight:S(w.maxHeight,"none"),minHeight:S(w.minHeight,"auto"),zIndex:String(null!==(y=w.zIndex)&&void 0!==y?y:1e3),transition:null!==(x=w.transition)&&void 0!==x?x:"opacity 0.2s",transform:null!==(g=w.transform)&&void 0!==g?g:"none",backdropFilter:null!==(p=w.backdropFilter)&&void 0!==p?p:"none",opacity:String(null!==(b=w.opacity)&&void 0!==b?b:1),display:"none",visibility:"hidden"}),s(M,t.canvasContainerId),M}(t));const C=n.getCanvas(),M=u(e=>{const{mouseX:n,mouseY:i}=d(e,C),o=c(n,i,x);if(o){const n=(null==t?void 0:t.cursorType)||"pointer";if(C.style.cursor=n,b){const n=function(t,e){var n,i,o;if(!t)return"";if(e.tooltipOptions&&"function"==typeof e.tooltipOptions.formatter)return e.tooltipOptions.formatter(t);const a=null===(i=null===(n=t.toolTipConfig)||void 0===n?void 0:n.tooltipText)||void 0===i?void 0:i.trim();if(a)return`<div>${a}</div>`;const r=null===(o=t.label)||void 0===o?void 0:o.trim();return r?`<div>${r}<br>Value: ${t.value}</div>`:`<div>${t.value}</div>`}(o,t);!function(t,e,n,i){e&&n&&(e.style.display="block",e.innerHTML=i,e.style.left=`${t.pageX+15}px`,e.style.top=`${t.pageY+15}px`,e.style.zIndex="999999",e.style.visibility="visible",e.style.opacity="1",e.style.position="absolute")}(e,b,C,n)}}else C.style.cursor="default",function(t){t&&(t.style.display="none",t.style.visibility="hidden",t.style.opacity="0")}(b)});C.addEventListener("mousemove",M.handle);const w=u(e=>{const{mouseX:n,mouseY:i}=d(e,C),o=c(n,i,x);o&&t.onBubbleClick&&t.onBubbleClick(o,e)});t.onBubbleClick&&C.addEventListener("click",w.handle);const S=()=>{null==p||p.destroy(),M.cancel(),w.cancel(),C.removeEventListener("mousemove",M.handle),C.removeEventListener("click",w.handle),n.destroy(),(null==b?void 0:b.parentElement)&&b.parentElement.removeChild(b),b=null};return{destroy:S,update:e=>(S(),t.data=e,f(t))}}h.TRANSPARENT="transparent";class m{constructor(t){const e=function(t={}){var e,i;if(!t)return void console.error("Configuration is not valid. Chart initialization aborted.");if(!t.data||0===t.data.length)return void console.error("No valid data provided. Chart initialization aborted.");const o=(a=Object.assign({canvasContainerId:null!==(e=t.canvasContainerId)&&void 0!==e?e:"chart-container",data:null!==(i=t.data)&&void 0!==i?i:[]},t),Object.assign(Object.assign({},n),a));var a;const r=f(o);return r?{config:o,instance:r}:void 0}(t);e&&(this.configuration=e.config,this.instance=e.instance)}destroy(){this.instance&&this.instance.destroy()}update(t){this.instance&&(this.instance=this.instance.update(t))}}function v(t){return new m(t)}"undefined"!=typeof window&&(window.initializeChart=v,window.BubbleChart=m),module.exports=e})();
@@ -1 +1 @@
1
- let t="default";function n(t,n){if(!n)return console.error("Invalid containerId."),null;const e=document.getElementById(n);if(!e)return console.error("Container not found."),null;const o=e.parentElement;return o?o.querySelector("#bubbleChartTooltip")||(o.prepend(t),t):(console.error("Parent container not found."),null)}function e(t,n){const e=n.getBoundingClientRect();return{mouseX:t.clientX-e.left,mouseY:t.clientY-e.top}}function o(t,n,e){return e.find(e=>Math.hypot(t-e.x,n-e.y)<e.radius)||null}class i{}i.TRANSPARENT="transparent";function a(t,n=14,e=400,o=6){const i=e>=700?1.25:e>=500?1.1:1,a=Math.min(t/o,t/1.2);let r=Math.min(n*i,a,.8*t);return r=Math.max(r,Math.max(8,t/6)),Math.round(r)}function r(r){if(!function(t){return t?!(!Array.isArray(t.data)||0===t.data.length)||(console.error("Invalid or empty data array"),!1):(console.error("Invalid config object"),!1)}(r))return;let l=function(t){var n,e;const o=document.getElementById(t.canvasContainerId);if(!o)return console.error(`Canvas container with ID '${t.canvasContainerId}' not found.`),null;if(o.querySelector("canvas"))return console.error("A canvas already exists inside the container."),null;const a=document.createElement("canvas");return Object.assign(a.style,{border:(null===(n=t.canvasBorderColor)||void 0===n?void 0:n.trim())?`1px solid #${t.canvasBorderColor}`:i.TRANSPARENT,background:(null===(e=t.canvasBackgroundColor)||void 0===e?void 0:e.trim())?`#${t.canvasBackgroundColor}`:i.TRANSPARENT,width:"100%",height:"100%",display:"block",imageRendering:"crisp-edges",aspectRatio:"1 / 1"}),a.style.setProperty("image-rendering","-moz-crisp-edges"),a.style.setProperty("image-rendering","-webkit-optimize-contrast"),a.style.setProperty("-ms-interpolation-mode","nearest-neighbor"),o.appendChild(a),a}(r);if(!l)return;const s=l.getContext("2d");if(!s)return void console.error("Invalid context");const{width:d,height:u}=f(l,s),c=function(t,n,e){const o=Math.min((n-10)/2,(e-10)/2),i=n/2,a=e/2,r=[...t.data].sort((t,n)=>n.value-t.value).map(t=>Object.assign(Object.assign({},t),{radius:0,x:0,y:0,fixed:!1})),l=r[0].value,s=Math.min(.5*o,.2*Math.min(n,e)),d=Math.max(.3*s,.05*Math.min(n,e));r.forEach(t=>{const o=t.value/l;t.radius=d+o*(s-d),t.radius=Math.min(t.radius,(n-10)/2,(e-10)/2)}),r.forEach((t,o)=>{if(0===o)t.x=i,t.y=a,t.fixed=!0;else{const l=r[0].radius+t.radius+3,s=Math.PI*(3-Math.sqrt(5)),d=n-5-t.radius,u=e-5-t.radius;t.x=Math.min(d,Math.max(5+t.radius,i+Math.cos(s*o)*l)),t.y=Math.min(u,Math.max(5+t.radius,a+Math.sin(s*o)*l)),t.fixed=!1}});for(let t=0;t<1e3;t++)r.forEach((t,e)=>{if(0===e){const n=i-t.x,e=a-t.y;return void(Math.hypot(n,e)>2&&(t.x+=.3*n,t.y+=.3*e))}let o=0,s=0;const d=t.radius+5;t.x<d?o+=.02*(d-t.x):t.x>n-d&&(o+=.02*(n-d-t.x)),r.forEach(n=>{if(t===n)return;const e=t.x-n.x,r=t.y-n.y,d=Math.hypot(e,r),u=t.radius+n.radius;if(d<1.5*u){const t=u/Math.max(d,.1)*.008;o+=e/d*t,s+=r/d*t}const c=i-t.x,h=a-t.y,f=Math.hypot(c,h),m=t.value/l*.02;t.x+=c/f*m,t.y+=h/f*m});const u=i-t.x,c=a-t.y,h=Math.hypot(u,c),f=r[0].radius+t.radius+35,m=(Math.pow(t.value/l,.3),.8*(1-t.value/l)*(1-Math.min(1,h/f)));t.x+=u*m,t.y+=c*m,t.x+=.35*o,t.y+=.35*s}),r.forEach((t,n)=>{r.forEach((e,o)=>{if(n>=o)return;if(0===n||0===o){const o=0===n?t:e,i=0===n?e:t,a=i.x-o.x,r=i.y-o.y,l=Math.hypot(a,r),s=o.radius+i.radius+2;if(l<s){const t=s-l,n=Math.atan2(r,a);i.x+=Math.cos(n)*t*.7,i.y+=Math.sin(n)*t*.7}return}const i=t.x-e.x,a=t.y-e.y,r=Math.hypot(i,a),l=t.radius+e.radius-5;if(r<l){const n=(l-r)*(.3+.04),o=Math.atan2(a,i),s=e.radius/(t.radius+e.radius);t.fixed||(t.x+=Math.cos(o)*n*s,t.y+=Math.sin(o)*n*s),e.fixed||(e.x-=Math.cos(o)*n*(1-s),e.y-=Math.sin(o)*n*(1-s))}})});return r.forEach(t=>{const o=Math.max(5+t.radius,Math.min(n-5-t.radius,t.x)),i=Math.max(5+t.radius,Math.min(e-5-t.radius,t.y));(!t.fixed||Math.hypot(t.x-o,t.y-i)>2)&&(t.x=o,t.y=i)}),r}(r,d,u);function h(){l&&s?(f(l,s),s.clearRect(0,0,l.width,l.height),c.forEach(t=>{const{x:n,y:e,radius:o,bubbleColor:i,borderColor:l,borderThickness:d,opacity:u,fontColor:c,fontFamily:h,fontSize:f,fontStyle:m,fontWeight:y,textAlign:v,textTransform:x,textBaseline:g}=function(t,n){var e,o,i,r,l,s,d,u,c,h,f,m;return{x:t.x,y:t.y,radius:Math.max(t.radius||0,n.minRadius||10),bubbleColor:null!==(o=null!==(e=t.bubbleColor)&&void 0!==e?e:n.defaultBubbleColor)&&void 0!==o?o:"#3498db",borderColor:null!==(i=t.borderColor)&&void 0!==i?i:"black",borderThickness:Math.max(null!==(r=t.borderThickness)&&void 0!==r?r:.25,0),opacity:void 0!==t.opacity?Math.max(0,Math.min(1,t.opacity)):1,fontStyle:t.fontStyle||"normal",fontWeight:"number"==typeof t.fontWeight&&t.fontWeight>=100&&t.fontWeight<=900?t.fontWeight:400,textAlign:null!==(l=t.textAlign)&&void 0!==l?l:"center",textTransform:null!==(s=t.textTransform)&&void 0!==s?s:"none",fontColor:null!==(u=null!==(d=t.fontColor)&&void 0!==d?d:n.defaultFontColor)&&void 0!==u?u:"#000",textBaseline:null!==(c=t.textBaseline)&&void 0!==c?c:"middle",fontSize:Math.max(a(t.radius||n.minRadius||10,null!==(h=n.fontSize)&&void 0!==h?h:14,null!==(f=t.fontWeight)&&void 0!==f?f:400),8),fontFamily:t.fontFamily&&"string"==typeof t.fontFamily&&"Arial"!==t.fontFamily?`${t.fontFamily}, Arial, sans-serif`:`${null!==(m=n.defaultFontFamily)&&void 0!==m?m:"Arial"}, sans-serif`}}(t,r);s.beginPath(),s.arc(n,e,o,0,2*Math.PI),s.fillStyle=i,s.fill(),s.strokeStyle=l,s.lineWidth=d,s.stroke();const p=`${m} ${y} ${f}px ${h}`;s.fillStyle=c,s.font=p,s.imageSmoothingEnabled=!0,s.imageSmoothingQuality="high",s.textAlign=v,s.textBaseline=g;let b=t.label||"";if("uppercase"===x?b=b.toUpperCase():"lowercase"===x?b=b.toLowerCase():"capitalize"===x&&(b=b.replace(/\b\w/g,t=>t.toUpperCase())),r.textWrap){const i=1.5*o-10,a=1.4*f,l=function(t,n,e,o,i,a=14,r=400,l="normal",s="Arial",d=0,u=5,c=1.2){const h=1.5*(i-u),f=a*c,m=Math.max(1,Math.floor(h/f)),y="auto"===o||void 0===o?m:Math.min(o,m);e=Math.max(0,e-2*d);const v=n.split(" ");let x="";const g=[];let p=!1,b=!1;t.font=`${r||""} ${l||""} ${a}px ${s}`,t.imageSmoothingEnabled=!0,t.imageSmoothingQuality="high";for(const n of v){const o=x?`${x} ${n}`:n;if(t.measureText(o).width<=e)x=o;else if(x.trim()&&g.push(x),x=n,t.measureText(x).width>e){let n=x;for(;t.measureText(n+"...").width>e&&n.length>0;)n=n.slice(0,-1);g.push(n+"..."),b=!0;break}if(g.length>=y){p=!0;break}}if(x&&g.length<y&&!b&&g.push(x),p&&g.length>0){let n=g[g.length-1];for(;t.measureText(n+"...").width>e&&n.length>0;)n=n.slice(0,-1);g[g.length-1]=n+"..."}return g}(s,t.label,i,r.maxLines,o,f,y,m,h),d=e-(l.length-1)*a/2;l.forEach((t,e)=>{const o=Math.round(n),i=Math.round(d+e*a);s.fillText(t,o,i)})}else s.fillText(t.label,Math.round(n),Math.round(e))})):console.warn("canvas or ctx is not valid")}function f(t,n){const e=window.devicePixelRatio||1,o=t.getBoundingClientRect();return t.width=o.width*e,t.height=o.height*e,t.style.width=`${o.width}px`,t.style.height=`${o.height}px`,n.setTransform(1,0,0,1,0,0),n.scale(e,e),{width:o.width,height:o.height,dpr:e}}function m(){const t=document.getElementById(r.canvasContainerId);t&&l&&(l.width=t.offsetWidth,l.height=t.offsetHeight,l.getContext("2d"),h())}let y;r.isResizeCanvasOnWindowSizeChange&&(m(),window.addEventListener("resize",m)),h(),r.showToolTip&&(y=function(t){var e,o,i,a,r,l,s,d,u,c,h,f,m,y,v,x,g,p;const b=null===(o=null===(e=t.data[0].toolTipConfig)||void 0===e?void 0:e.tooltipFormattedData)||void 0===o?void 0:o.trim();if(b){const e=document.createElement("div");e.innerHTML=b.trim();const o=e.firstElementChild;return o.style.display="none",o.style.visibility="hidden",o.style.opacity="0",o.style.position="absolute",n(o,t.canvasContainerId)}const M=document.createElement("div");M.id="bubbleChartTooltip",M.style.display="none";const C=null!==(i=null==t?void 0:t.tooltipOptions)&&void 0!==i?i:{},w=(t,n)=>"number"==typeof t?`${t}px`:null!=t?t:n;return Object.assign(M.style,{position:"absolute",padding:w(C.padding,"8px"),margin:w(C.margin,"0"),background:null!==(a=C.backgroundColor)&&void 0!==a?a:"rgba(0, 0, 0, 0.85)",color:null!==(r=C.fontColor)&&void 0!==r?r:"white",borderRadius:"4px",pointerEvents:null!==(l=C.pointerEvents)&&void 0!==l?l:"none",fontFamily:null!==(s=C.fontFamily)&&void 0!==s?s:"Arial, sans-serif",fontSize:w(C.fontSize,"14px"),fontWeight:String(null!==(d=C.fontWeight)&&void 0!==d?d:"400"),fontStyle:null!==(u=C.fontStyle)&&void 0!==u?u:"normal",textAlign:null!==(c=C.textAlign)&&void 0!==c?c:"left",textDecoration:null!==(h=C.textDecoration)&&void 0!==h?h:"none",textTransform:null!==(f=C.textTransform)&&void 0!==f?f:"none",letterSpacing:w(C.letterSpacing,"normal"),border:(()=>{var t;if(!C.borderStyle)return"none";const n=w(C.borderWidth,"1px"),e=null!==(t=C.borderColor)&&void 0!==t?t:"transparent";return`${n} ${C.borderStyle} ${e}`})(),boxShadow:null!==(m=C.boxShadow)&&void 0!==m?m:"none",maxWidth:w(C.maxWidth,"200px"),minWidth:w(C.minWidth,"auto"),maxHeight:w(C.maxHeight,"none"),minHeight:w(C.minHeight,"auto"),zIndex:String(null!==(y=C.zIndex)&&void 0!==y?y:1e3),transition:null!==(v=C.transition)&&void 0!==v?v:"opacity 0.2s",transform:null!==(x=C.transform)&&void 0!==x?x:"none",backdropFilter:null!==(g=C.backdropFilter)&&void 0!==g?g:"none",opacity:String(null!==(p=C.opacity)&&void 0!==p?p:1),display:"none",visibility:"hidden"}),n(M,t.canvasContainerId),M}(r));let v=null;if(l.addEventListener("mousemove",n=>{v||(v=requestAnimationFrame(()=>{(function(n,i,a,r,l){const{mouseX:s,mouseY:d}=e(n,a),u=o(s,d,i);t=(null==l?void 0:l.cursorType)||"pointer",a.style.cursor="default",r&&(u?function(n,e,o,i){if(e&&e.value&&o&&i){o.style.cursor=t,i.style.display="block",i.innerHTML=function(t){var n,e,o;if(!t)return"";const i=null===(e=null===(n=t.toolTipConfig)||void 0===n?void 0:n.tooltipText)||void 0===e?void 0:e.trim();if(i)return`<div>${i}</div>`;const a=null===(o=t.label)||void 0===o?void 0:o.trim();return a?`<div>${a}<br>Value: ${t.value}</div>`:`<div>${t.value}</div>`}(e);const a=o.getBoundingClientRect();i.style.left=n.clientX-a.left+15+"px",i.style.top=n.clientY-a.top+15+"px",i.style.zIndex="999999",i.style.visibility="visible",i.style.opacity="1",i.style.position="absolute"}}(n,u,a,r):(a.style.cursor="default",r.style.display="none",r.style.visibility="hidden",r.style.opacity="0"))})(n,c,l,y,r),v=null}))}),r.onBubbleClick){let t=null;l.addEventListener("click",n=>{t||(t=requestAnimationFrame(()=>{!function(t,n,i,a){const{mouseX:r,mouseY:l}=e(t,i),s=o(r,l,n);null!=s&&a.onBubbleClick&&a.onBubbleClick(s,t)}(n,c,l,r),t=null}))})}}const l={defaultBubbleColor:"#3498DB",defaultFontColor:"#ffffff",minRadius:10,maxLines:"auto",textWrap:!0,isResizeCanvasOnWindowSizeChange:!0,fontSize:14,defaultFontFamily:"Arial",showToolTip:!0};function s(t={}){var n,e;if(!t)return void console.error("Configuration is not valid. Chart initialization aborted.");if(!t.data||0===t.data.length)return void console.error("No valid data provided. Chart initialization aborted.");const o=(i=Object.assign({canvasContainerId:null!==(n=t.canvasContainerId)&&void 0!==n?n:"chart-container",data:null!==(e=t.data)&&void 0!==e?e:[]},t),Object.assign(Object.assign({},l),i));var i;return r(o),o}class d{constructor(t){const n=s(t);n&&(this.configuration=n)}render(){this.configuration&&r(this.configuration)}}window.initializeChart=s;export{d as BubbleChart,s as initializeChart};
1
+ const t={defaultBubbleColor:"#3498DB",defaultFontColor:"#ffffff",minRadius:10,maxLines:"auto",textWrap:!0,isResizeCanvasOnWindowSizeChange:!0,fontSize:14,defaultFontFamily:"Arial",showToolTip:!0},e=JSON.parse('{"FC":0.008,"rX":1000,"d6":0.65,"WH":0.02,"ND":0.12,"$l":0.8,"cg":0.3,"e0":35}');function n(t,e){t.forEach((n,i)=>{t.forEach((t,o)=>{if(i>=o)return;if(0===i||0===o){const e=0===i?n:t,o=0===i?t:n,a=o.x-e.x,r=o.y-e.y,l=Math.hypot(a,r),s=e.radius+o.radius+2;if(l<s){const t=s-l,e=Math.atan2(r,a);o.x+=Math.cos(e)*t*.7,o.y+=Math.sin(e)*t*.7}return}const a=n.x-t.x,r=n.y-t.y,l=Math.hypot(a,r),s=n.radius+t.radius-5;if(l<s){const i=(s-l)*(.3+5*e.forceStrength),o=Math.atan2(r,a),d=n.radius+t.radius,c=d>0?t.radius/d:.5;n.fixed||(n.x+=Math.cos(o)*i*c,n.y+=Math.sin(o)*i*c),t.fixed||(t.x-=Math.cos(o)*i*(1-c),t.y-=Math.sin(o)*i*(1-c))}})})}function i(t,e=14,n=400,i=6){const o=n>=700?1.25:n>=500?1.1:1,a=Math.min(t/i,t/1.2);let r=Math.min(e*o,a,.8*t);return r=Math.max(r,Math.max(8,t/6)),Math.round(r)}class o{constructor(t){this.canvas=t;const e=t.getContext("2d");if(!e)throw new Error("Failed to get 2D rendering context from canvas");this.ctx=e}getContext(){return this.ctx}getCanvas(){return this.canvas}setup(){const t=window.devicePixelRatio||1,e=this.canvas.getBoundingClientRect();return this.canvas.width=e.width*t,this.canvas.height=e.height*t,this.canvas.style.width=`${e.width}px`,this.canvas.style.height=`${e.height}px`,this.ctx.setTransform(1,0,0,1,0,0),this.ctx.scale(t,t),{width:e.width,height:e.height}}getSize(){const t=this.canvas.getBoundingClientRect();return{width:t.width,height:t.height}}clear(){this.ctx.clearRect(0,0,this.canvas.width,this.canvas.height)}drawCircle(t,e,n,i,o,a,r){this.ctx.beginPath(),this.ctx.arc(t,e,n,0,2*Math.PI),this.ctx.fillStyle=i,this.ctx.fill(),a>0&&(this.ctx.strokeStyle=o,this.ctx.lineWidth=a,this.ctx.stroke())}drawText(t,e,n,i){const o=`${i.fontStyle} ${i.fontWeight} ${i.fontSize}px ${i.fontFamily}`;this.ctx.fillStyle=i.fontColor,this.ctx.font=o,this.ctx.imageSmoothingEnabled=!0,this.ctx.imageSmoothingQuality="high",this.ctx.textAlign=i.textAlign,this.ctx.textBaseline=i.textBaseline,this.ctx.fillText(t,Math.round(e),Math.round(n))}destroy(){const t=this.canvas.parentElement;t&&t.removeChild(this.canvas)}}class a{constructor(t){this.ctx=t}measureWidth(t,e,n,i,o){return this.ctx.font=`${i} ${n} ${e}px ${o}`,this.ctx.imageSmoothingEnabled=!0,this.ctx.imageSmoothingQuality="high",this.ctx.measureText(t).width}}function r(t,e){const n=`bubbleChartTooltip-${e}`;return document.body.querySelector(`#${n}`)||(t.id=n,document.body.appendChild(t),t)}function l(t,e){const n=e.getBoundingClientRect(),i=window.devicePixelRatio||1;return{mouseX:(t.clientX-n.left)*i,mouseY:(t.clientY-n.top)*i}}function s(t,e,n){return n.find(n=>Math.hypot(t-n.x,e-n.y)<n.radius)||null}function d(t){let e=null;return{handle:n=>{e||(e=requestAnimationFrame(()=>{t(n),e=null}))},cancel:()=>{e&&(cancelAnimationFrame(e),e=null)}}}class c{}function u(t){if(!function(t){return t?!(!Array.isArray(t.data)||0===t.data.length)||(console.error("Invalid or empty data array"),!1):(console.error("Invalid config object"),!1)}(t))return;const h=function(t){var e,n;const i=document.getElementById(t.canvasContainerId);if(!i)return console.error(`Canvas container with ID '${t.canvasContainerId}' not found.`),null;if(i.querySelector("canvas"))return console.error("A canvas already exists inside the container."),null;const o=document.createElement("canvas");return Object.assign(o.style,{border:(null===(e=t.canvasBorderColor)||void 0===e?void 0:e.trim())?`1px solid #${t.canvasBorderColor}`:c.TRANSPARENT,background:(null===(n=t.canvasBackgroundColor)||void 0===n?void 0:n.trim())?`#${t.canvasBackgroundColor}`:c.TRANSPARENT,width:"100%",height:"100%",display:"block",imageRendering:"crisp-edges",aspectRatio:"1 / 1"}),o.style.setProperty("image-rendering","-moz-crisp-edges"),o.style.setProperty("image-rendering","-webkit-optimize-contrast"),o.style.setProperty("-ms-interpolation-mode","nearest-neighbor"),i.appendChild(o),o}(t);if(!h)return;const f=new o(h),m=new a(f.getContext()),{width:v,height:x}=f.setup();let y=function(t,i,o,a){const r=Object.assign({forceStrength:e.FC,iterations:e.rX,damping:e.d6,boundaryForce:e.WH,centerForce:e.ND,centerAttraction:e.$l,centerDamping:e.cg,centerRadiusBuffer:e.e0},a),l=Math.min((i-10)/2,(o-10)/2),s=i/2,d=o/2,c=[...t].sort((t,e)=>e.value-t.value).map(t=>Object.assign(Object.assign({},t),{radius:0,x:0,y:0,fixed:!1}));if(0===c.length)return c;const u=c[0].value,h=Math.min(.5*l,.2*Math.min(i,o)),f=Math.max(.3*h,.05*Math.min(i,o));return c.forEach(t=>{const e=u>0?t.value/u:1;t.radius=f+e*(h-f),t.radius=Math.max(0,Math.min(t.radius,(i-10)/2,(o-10)/2))}),c.forEach((t,e)=>{if(0===e)t.x=s,t.y=d,t.fixed=!0;else{const n=c[0].radius+t.radius+3,a=Math.PI*(3-Math.sqrt(5)),r=i-5-t.radius,l=o-5-t.radius;t.x=Math.min(r,Math.max(5+t.radius,s+Math.cos(a*e)*n)),t.y=Math.min(l,Math.max(5+t.radius,d+Math.sin(a*e)*n)),t.fixed=!1}}),function(t,e,i,o,a,r,l){for(let o=0;o<e.iterations;o++)t.forEach((n,o)=>{if(0===o){const t=a-n.x,i=r-n.y;return void(Math.hypot(t,i)>2&&(n.x+=t*e.centerDamping,n.y+=i*e.centerDamping))}let s=0,d=0;const c=n.radius+5;n.x<c?s+=(c-n.x)*e.boundaryForce:n.x>i-c&&(s+=(i-c-n.x)*e.boundaryForce),t.forEach(t=>{if(n===t)return;const i=n.x-t.x,o=n.y-t.y,c=Math.hypot(i,o),u=n.radius+t.radius;if(c<1.5*u){const t=e.forceStrength*(u/Math.max(c,.1));s+=i/c*t,d+=o/c*t}const h=a-n.x,f=r-n.y,m=Math.hypot(h,f),v=n.value/l*.02;n.x+=h/m*v,n.y+=f/m*v});const u=a-n.x,h=r-n.y,f=Math.hypot(u,h),m=t[0].radius+n.radius+e.centerRadiusBuffer,v=e.centerAttraction*(1-n.value/l)*(1-Math.min(1,f/m));n.x+=u*v,n.y+=h*v,n.x+=s*(1-e.damping),n.y+=d*(1-e.damping)}),n(t,e)}(c,r,i,0,s,d,u),function(t,e,n){t.forEach(t=>{const i=Math.max(5+t.radius,Math.min(e-5-t.radius,t.x)),o=Math.max(5+t.radius,Math.min(n-5-t.radius,t.y));(!t.fixed||Math.hypot(t.x-i,t.y-o)>2)&&(t.x=i,t.y=o)})}(c,i,o),c}(t.data,v,x);function g(){f.setup(),f.clear(),y.forEach(e=>{const n=function(t,e,n){var o,a,r,l,s,d,c,u,h,f,m;let v=null!==(o=n.defaultBubbleColor)&&void 0!==o?o:"#3498db";if(t.bubbleColor)v=t.bubbleColor;else if(n.colorPalette&&n.colorPalette.length>0){const i=e.findIndex(e=>e.label===t.label&&e.value===t.value),o=i>=0?i:0;v=n.colorPalette[o%n.colorPalette.length]}const x=Math.max(t.radius||0,n.minRadius||10);return{bubbleColor:v,borderColor:null!==(a=t.borderColor)&&void 0!==a?a:"black",borderThickness:Math.max(null!==(r=t.borderThickness)&&void 0!==r?r:.25,0),opacity:void 0!==t.opacity?Math.max(0,Math.min(1,t.opacity)):1,fontStyle:t.fontStyle||"normal",fontWeight:"number"==typeof t.fontWeight&&t.fontWeight>=100&&t.fontWeight<=900?t.fontWeight:400,textAlign:null!==(l=t.textAlign)&&void 0!==l?l:"center",textTransform:null!==(s=t.textTransform)&&void 0!==s?s:"none",fontColor:null!==(c=null!==(d=t.fontColor)&&void 0!==d?d:n.defaultFontColor)&&void 0!==c?c:"#000",textBaseline:null!==(u=t.textBaseline)&&void 0!==u?u:"middle",fontSize:Math.max(i(x,null!==(h=n.fontSize)&&void 0!==h?h:14,null!==(f=t.fontWeight)&&void 0!==f?f:400),8),fontFamily:t.fontFamily&&"string"==typeof t.fontFamily&&"Arial"!==t.fontFamily?`${t.fontFamily}, Arial, sans-serif`:`${null!==(m=n.defaultFontFamily)&&void 0!==m?m:"Arial"}, sans-serif`}}(e,y,t);f.drawCircle(e.x,e.y,Math.max(e.radius||0,t.minRadius||10),n.bubbleColor,n.borderColor,n.borderThickness,n.opacity);let o=e.label||"";"uppercase"===n.textTransform?o=o.toUpperCase():"lowercase"===n.textTransform?o=o.toLowerCase():"capitalize"===n.textTransform&&(o=o.replace(/\b\w/g,t=>t.toUpperCase()));const a=Math.max(e.radius||0,t.minRadius||10),r={fontSize:n.fontSize,fontFamily:n.fontFamily,fontWeight:n.fontWeight,fontStyle:n.fontStyle,fontColor:n.fontColor,textAlign:n.textAlign,textBaseline:n.textBaseline};if(t.textWrap){const i=1.5*a-10,o=1.4*n.fontSize,l=function(t,e,n,i,o,a=14,r=400,l="normal",s="Arial",d=0,c=5,u=1.2){const h=1.5*(o-c),f=a*u,m=Math.max(1,Math.floor(h/f)),v="auto"===i||void 0===i?m:Math.min(i,m);n=Math.max(0,n-2*d);const x=e.split(" ");let y="";const g=[];let p=!1,b=!1;const C=e=>t.measureWidth(e,a,r,l,s);for(const t of x){const e=y?`${y} ${t}`:t;if(C(e)<=n)y=e;else if(y.trim()&&g.push(y),y=t,C(y)>n){let t=y;for(;C(t+"...")>n&&t.length>0;)t=t.slice(0,-1);g.push(t+"..."),b=!0;break}if(g.length>=v){p=!0;break}}if(y&&g.length<v&&!b&&g.push(y),p&&g.length>0){let t=g[g.length-1];for(;C(t+"...")>n&&t.length>0;)t=t.slice(0,-1);g[g.length-1]=t+"..."}return g}(m,e.label,i,t.maxLines,a,n.fontSize,n.fontWeight,n.fontStyle,n.fontFamily),s=e.y-(l.length-1)*o/2;l.forEach((t,n)=>{f.drawText(t,e.x,s+n*o,r)})}else f.drawText(o,e.x,e.y,r)})}let p=null;if(t.isResizeCanvasOnWindowSizeChange){const e=document.getElementById(t.canvasContainerId);e&&(p=function(t,e){let n=null,i=null;const o=()=>{i||(i=requestAnimationFrame(()=>{e(),i=null}))};return"undefined"!=typeof ResizeObserver?(n=new ResizeObserver(o),n.observe(t)):(o(),window.addEventListener("resize",o)),{destroy:()=>{n&&(n.disconnect(),n=null),window.removeEventListener("resize",o),i&&(cancelAnimationFrame(i),i=null)}}}(e,()=>{const t=f.getCanvas();e&&t&&(t.width=e.offsetWidth,t.height=e.offsetHeight,g())}))}g();let b=null;t.showToolTip&&(b=function(t){var e,n,i,o,a,l,s,d,c,u,h,f,m,v,x,y,g,p,b;const C=null===(i=null===(n=null===(e=t.data[0])||void 0===e?void 0:e.toolTipConfig)||void 0===n?void 0:n.tooltipFormattedData)||void 0===i?void 0:i.trim();if(C){const e=document.createElement("div");e.innerHTML=C.trim();const n=e.firstElementChild;return n.style.display="none",n.style.visibility="hidden",n.style.opacity="0",n.style.position="absolute",r(n,t.canvasContainerId)}const M=document.createElement("div");M.style.display="none";const w=null!==(o=null==t?void 0:t.tooltipOptions)&&void 0!==o?o:{},S=(t,e)=>"number"==typeof t?`${t}px`:null!=t?t:e;return Object.assign(M.style,{position:"absolute",padding:S(w.padding,"8px"),margin:S(w.margin,"0"),background:null!==(a=w.backgroundColor)&&void 0!==a?a:"rgba(0, 0, 0, 0.85)",color:null!==(l=w.fontColor)&&void 0!==l?l:"white",borderRadius:"4px",pointerEvents:null!==(s=w.pointerEvents)&&void 0!==s?s:"none",fontFamily:null!==(d=w.fontFamily)&&void 0!==d?d:"Arial, sans-serif",fontSize:S(w.fontSize,"14px"),fontWeight:String(null!==(c=w.fontWeight)&&void 0!==c?c:"400"),fontStyle:null!==(u=w.fontStyle)&&void 0!==u?u:"normal",textAlign:null!==(h=w.textAlign)&&void 0!==h?h:"left",textDecoration:null!==(f=w.textDecoration)&&void 0!==f?f:"none",textTransform:null!==(m=w.textTransform)&&void 0!==m?m:"none",letterSpacing:S(w.letterSpacing,"normal"),border:(()=>{var t;if(!w.borderStyle)return"none";const e=S(w.borderWidth,"1px"),n=null!==(t=w.borderColor)&&void 0!==t?t:"transparent";return`${e} ${w.borderStyle} ${n}`})(),boxShadow:null!==(v=w.boxShadow)&&void 0!==v?v:"none",maxWidth:S(w.maxWidth,"200px"),minWidth:S(w.minWidth,"auto"),maxHeight:S(w.maxHeight,"none"),minHeight:S(w.minHeight,"auto"),zIndex:String(null!==(x=w.zIndex)&&void 0!==x?x:1e3),transition:null!==(y=w.transition)&&void 0!==y?y:"opacity 0.2s",transform:null!==(g=w.transform)&&void 0!==g?g:"none",backdropFilter:null!==(p=w.backdropFilter)&&void 0!==p?p:"none",opacity:String(null!==(b=w.opacity)&&void 0!==b?b:1),display:"none",visibility:"hidden"}),r(M,t.canvasContainerId),M}(t));const C=f.getCanvas(),M=d(e=>{const{mouseX:n,mouseY:i}=l(e,C),o=s(n,i,y);if(o){const n=(null==t?void 0:t.cursorType)||"pointer";if(C.style.cursor=n,b){const n=function(t,e){var n,i,o;if(!t)return"";if(e.tooltipOptions&&"function"==typeof e.tooltipOptions.formatter)return e.tooltipOptions.formatter(t);const a=null===(i=null===(n=t.toolTipConfig)||void 0===n?void 0:n.tooltipText)||void 0===i?void 0:i.trim();if(a)return`<div>${a}</div>`;const r=null===(o=t.label)||void 0===o?void 0:o.trim();return r?`<div>${r}<br>Value: ${t.value}</div>`:`<div>${t.value}</div>`}(o,t);!function(t,e,n,i){e&&n&&(e.style.display="block",e.innerHTML=i,e.style.left=`${t.pageX+15}px`,e.style.top=`${t.pageY+15}px`,e.style.zIndex="999999",e.style.visibility="visible",e.style.opacity="1",e.style.position="absolute")}(e,b,C,n)}}else C.style.cursor="default",function(t){t&&(t.style.display="none",t.style.visibility="hidden",t.style.opacity="0")}(b)});C.addEventListener("mousemove",M.handle);const w=d(e=>{const{mouseX:n,mouseY:i}=l(e,C),o=s(n,i,y);o&&t.onBubbleClick&&t.onBubbleClick(o,e)});t.onBubbleClick&&C.addEventListener("click",w.handle);const S=()=>{null==p||p.destroy(),M.cancel(),w.cancel(),C.removeEventListener("mousemove",M.handle),C.removeEventListener("click",w.handle),f.destroy(),(null==b?void 0:b.parentElement)&&b.parentElement.removeChild(b),b=null};return{destroy:S,update:e=>(S(),t.data=e,u(t))}}c.TRANSPARENT="transparent";class h{constructor(e){const n=function(e={}){var n,i;if(!e)return void console.error("Configuration is not valid. Chart initialization aborted.");if(!e.data||0===e.data.length)return void console.error("No valid data provided. Chart initialization aborted.");const o=(a=Object.assign({canvasContainerId:null!==(n=e.canvasContainerId)&&void 0!==n?n:"chart-container",data:null!==(i=e.data)&&void 0!==i?i:[]},e),Object.assign(Object.assign({},t),a));var a;const r=u(o);return r?{config:o,instance:r}:void 0}(e);n&&(this.configuration=n.config,this.instance=n.instance)}destroy(){this.instance&&this.instance.destroy()}update(t){this.instance&&(this.instance=this.instance.update(t))}}function f(t){return new h(t)}"undefined"!=typeof window&&(window.initializeChart=f,window.BubbleChart=h);export{h as BubbleChart,f as initializeChart};
@@ -1 +1 @@
1
- !function(t,e){"object"==typeof exports&&"object"==typeof module?module.exports=e():"function"==typeof define&&define.amd?define("BubbleChart",[],e):"object"==typeof exports?exports.BubbleChart=e():t.BubbleChart=e()}(this,()=>(()=>{"use strict";var t={d:(e,n)=>{for(var o in n)t.o(n,o)&&!t.o(e,o)&&Object.defineProperty(e,o,{enumerable:!0,get:n[o]})},o:(t,e)=>Object.prototype.hasOwnProperty.call(t,e),r:t=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(t,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(t,"__esModule",{value:!0})}},e={};t.r(e),t.d(e,{BubbleChart:()=>h,initializeChart:()=>c});let n="default";function o(t,e){if(!e)return console.error("Invalid containerId."),null;const n=document.getElementById(e);if(!n)return console.error("Container not found."),null;const o=n.parentElement;return o?o.querySelector("#bubbleChartTooltip")||(o.prepend(t),t):(console.error("Parent container not found."),null)}function i(t,e){const n=e.getBoundingClientRect();return{mouseX:t.clientX-n.left,mouseY:t.clientY-n.top}}function r(t,e,n){return n.find(n=>Math.hypot(t-n.x,e-n.y)<n.radius)||null}class a{}a.TRANSPARENT="transparent";const l={forceStrength:.008,iterations:1e3,damping:.65,boundaryForce:.02,centerForce:.12,centerAttraction:.8,centerDamping:.3,centerRadiusBuffer:35};function s(t,e=14,n=400,o=6){const i=n>=700?1.25:n>=500?1.1:1,r=Math.min(t/o,t/1.2);let a=Math.min(e*i,r,.8*t);return a=Math.max(a,Math.max(8,t/6)),Math.round(a)}function d(t){if(!function(t){return t?!(!Array.isArray(t.data)||0===t.data.length)||(console.error("Invalid or empty data array"),!1):(console.error("Invalid config object"),!1)}(t))return;let e=function(t){var e,n;const o=document.getElementById(t.canvasContainerId);if(!o)return console.error(`Canvas container with ID '${t.canvasContainerId}' not found.`),null;if(o.querySelector("canvas"))return console.error("A canvas already exists inside the container."),null;const i=document.createElement("canvas");return Object.assign(i.style,{border:(null===(e=t.canvasBorderColor)||void 0===e?void 0:e.trim())?`1px solid #${t.canvasBorderColor}`:a.TRANSPARENT,background:(null===(n=t.canvasBackgroundColor)||void 0===n?void 0:n.trim())?`#${t.canvasBackgroundColor}`:a.TRANSPARENT,width:"100%",height:"100%",display:"block",imageRendering:"crisp-edges",aspectRatio:"1 / 1"}),i.style.setProperty("image-rendering","-moz-crisp-edges"),i.style.setProperty("image-rendering","-webkit-optimize-contrast"),i.style.setProperty("-ms-interpolation-mode","nearest-neighbor"),o.appendChild(i),i}(t);if(!e)return;const d=e.getContext("2d");if(!d)return void console.error("Invalid context");const{width:u,height:c}=m(e,d),h=function(t,e,n){const o=Math.min((e-10)/2,(n-10)/2),i=e/2,r=n/2,a=[...t.data].sort((t,e)=>e.value-t.value).map(t=>Object.assign(Object.assign({},t),{radius:0,x:0,y:0,fixed:!1})),s=a[0].value,d=Math.min(.5*o,.2*Math.min(e,n)),u=Math.max(.3*d,.05*Math.min(e,n));a.forEach(t=>{const o=t.value/s;t.radius=u+o*(d-u),t.radius=Math.min(t.radius,(e-10)/2,(n-10)/2)}),a.forEach((t,o)=>{if(0===o)t.x=i,t.y=r,t.fixed=!0;else{const l=a[0].radius+t.radius+3,s=Math.PI*(3-Math.sqrt(5)),d=e-5-t.radius,u=n-5-t.radius;t.x=Math.min(d,Math.max(5+t.radius,i+Math.cos(s*o)*l)),t.y=Math.min(u,Math.max(5+t.radius,r+Math.sin(s*o)*l)),t.fixed=!1}});for(let t=0;t<l.iterations;t++)a.forEach((t,n)=>{if(0===n){const e=i-t.x,n=r-t.y;return void(Math.hypot(e,n)>2&&(t.x+=e*l.centerDamping,t.y+=n*l.centerDamping))}let o=0,d=0;const u=t.radius+5;t.x<u?o+=(u-t.x)*l.boundaryForce:t.x>e-u&&(o+=(e-u-t.x)*l.boundaryForce),a.forEach(e=>{if(t===e)return;const n=t.x-e.x,a=t.y-e.y,u=Math.hypot(n,a),c=t.radius+e.radius;if(u<1.5*c){const t=l.forceStrength*(c/Math.max(u,.1));o+=n/u*t,d+=a/u*t}const h=i-t.x,f=r-t.y,m=Math.hypot(h,f),y=t.value/s*.02;t.x+=h/m*y,t.y+=f/m*y});const c=i-t.x,h=r-t.y,f=Math.hypot(c,h),m=a[0].radius+t.radius+l.centerRadiusBuffer,y=(l.centerForce,Math.pow(t.value/s,.3),l.centerAttraction*(1-t.value/s)*(1-Math.min(1,f/m)));t.x+=c*y,t.y+=h*y,t.x+=o*(1-l.damping),t.y+=d*(1-l.damping)}),a.forEach((t,e)=>{a.forEach((n,o)=>{if(e>=o)return;if(0===e||0===o){const o=0===e?t:n,i=0===e?n:t,r=i.x-o.x,a=i.y-o.y,l=Math.hypot(r,a),s=o.radius+i.radius+2;if(l<s){const t=s-l,e=Math.atan2(a,r);i.x+=Math.cos(e)*t*.7,i.y+=Math.sin(e)*t*.7}return}const i=t.x-n.x,r=t.y-n.y,a=Math.hypot(i,r),s=t.radius+n.radius-5;if(a<s){const e=(s-a)*(.3+5*l.forceStrength),o=Math.atan2(r,i),d=n.radius/(t.radius+n.radius);t.fixed||(t.x+=Math.cos(o)*e*d,t.y+=Math.sin(o)*e*d),n.fixed||(n.x-=Math.cos(o)*e*(1-d),n.y-=Math.sin(o)*e*(1-d))}})});return a.forEach(t=>{const o=Math.max(5+t.radius,Math.min(e-5-t.radius,t.x)),i=Math.max(5+t.radius,Math.min(n-5-t.radius,t.y));(!t.fixed||Math.hypot(t.x-o,t.y-i)>2)&&(t.x=o,t.y=i)}),a}(t,u,c);function f(){e&&d?(m(e,d),d.clearRect(0,0,e.width,e.height),h.forEach(e=>{const{x:n,y:o,radius:i,bubbleColor:r,borderColor:a,borderThickness:l,opacity:u,fontColor:c,fontFamily:h,fontSize:f,fontStyle:m,fontWeight:y,textAlign:p,textTransform:v,textBaseline:g}=function(t,e){var n,o,i,r,a,l,d,u,c,h,f,m;return{x:t.x,y:t.y,radius:Math.max(t.radius||0,e.minRadius||10),bubbleColor:null!==(o=null!==(n=t.bubbleColor)&&void 0!==n?n:e.defaultBubbleColor)&&void 0!==o?o:"#3498db",borderColor:null!==(i=t.borderColor)&&void 0!==i?i:"black",borderThickness:Math.max(null!==(r=t.borderThickness)&&void 0!==r?r:.25,0),opacity:void 0!==t.opacity?Math.max(0,Math.min(1,t.opacity)):1,fontStyle:t.fontStyle||"normal",fontWeight:"number"==typeof t.fontWeight&&t.fontWeight>=100&&t.fontWeight<=900?t.fontWeight:400,textAlign:null!==(a=t.textAlign)&&void 0!==a?a:"center",textTransform:null!==(l=t.textTransform)&&void 0!==l?l:"none",fontColor:null!==(u=null!==(d=t.fontColor)&&void 0!==d?d:e.defaultFontColor)&&void 0!==u?u:"#000",textBaseline:null!==(c=t.textBaseline)&&void 0!==c?c:"middle",fontSize:Math.max(s(t.radius||e.minRadius||10,null!==(h=e.fontSize)&&void 0!==h?h:14,null!==(f=t.fontWeight)&&void 0!==f?f:400),8),fontFamily:t.fontFamily&&"string"==typeof t.fontFamily&&"Arial"!==t.fontFamily?`${t.fontFamily}, Arial, sans-serif`:`${null!==(m=e.defaultFontFamily)&&void 0!==m?m:"Arial"}, sans-serif`}}(e,t);d.beginPath(),d.arc(n,o,i,0,2*Math.PI),d.fillStyle=r,d.fill(),d.strokeStyle=a,d.lineWidth=l,d.stroke();const x=`${m} ${y} ${f}px ${h}`;d.fillStyle=c,d.font=x,d.imageSmoothingEnabled=!0,d.imageSmoothingQuality="high",d.textAlign=p,d.textBaseline=g;let b=e.label||"";if("uppercase"===v?b=b.toUpperCase():"lowercase"===v?b=b.toLowerCase():"capitalize"===v&&(b=b.replace(/\b\w/g,t=>t.toUpperCase())),t.textWrap){const r=1.5*i-10,a=1.4*f,l=function(t,e,n,o,i,r=14,a=400,l="normal",s="Arial",d=0,u=5,c=1.2){const h=1.5*(i-u),f=r*c,m=Math.max(1,Math.floor(h/f)),y="auto"===o||void 0===o?m:Math.min(o,m);n=Math.max(0,n-2*d);const p=e.split(" ");let v="";const g=[];let x=!1,b=!1;t.font=`${a||""} ${l||""} ${r}px ${s}`,t.imageSmoothingEnabled=!0,t.imageSmoothingQuality="high";for(const e of p){const o=v?`${v} ${e}`:e;if(t.measureText(o).width<=n)v=o;else if(v.trim()&&g.push(v),v=e,t.measureText(v).width>n){let e=v;for(;t.measureText(e+"...").width>n&&e.length>0;)e=e.slice(0,-1);g.push(e+"..."),b=!0;break}if(g.length>=y){x=!0;break}}if(v&&g.length<y&&!b&&g.push(v),x&&g.length>0){let e=g[g.length-1];for(;t.measureText(e+"...").width>n&&e.length>0;)e=e.slice(0,-1);g[g.length-1]=e+"..."}return g}(d,e.label,r,t.maxLines,i,f,y,m,h),s=o-(l.length-1)*a/2;l.forEach((t,e)=>{const o=Math.round(n),i=Math.round(s+e*a);d.fillText(t,o,i)})}else d.fillText(e.label,Math.round(n),Math.round(o))})):console.warn("canvas or ctx is not valid")}function m(t,e){const n=window.devicePixelRatio||1,o=t.getBoundingClientRect();return t.width=o.width*n,t.height=o.height*n,t.style.width=`${o.width}px`,t.style.height=`${o.height}px`,e.setTransform(1,0,0,1,0,0),e.scale(n,n),{width:o.width,height:o.height,dpr:n}}function y(){const n=document.getElementById(t.canvasContainerId);n&&e&&(e.width=n.offsetWidth,e.height=n.offsetHeight,e.getContext("2d"),f())}let p;t.isResizeCanvasOnWindowSizeChange&&(y(),window.addEventListener("resize",y)),f(),t.showToolTip&&(p=function(t){var e,n,i,r,a,l,s,d,u,c,h,f,m,y,p,v,g,x;const b=null===(n=null===(e=t.data[0].toolTipConfig)||void 0===e?void 0:e.tooltipFormattedData)||void 0===n?void 0:n.trim();if(b){const e=document.createElement("div");e.innerHTML=b.trim();const n=e.firstElementChild;return n.style.display="none",n.style.visibility="hidden",n.style.opacity="0",n.style.position="absolute",o(n,t.canvasContainerId)}const M=document.createElement("div");M.id="bubbleChartTooltip",M.style.display="none";const C=null!==(i=null==t?void 0:t.tooltipOptions)&&void 0!==i?i:{},S=(t,e)=>"number"==typeof t?`${t}px`:null!=t?t:e;return Object.assign(M.style,{position:"absolute",padding:S(C.padding,"8px"),margin:S(C.margin,"0"),background:null!==(r=C.backgroundColor)&&void 0!==r?r:"rgba(0, 0, 0, 0.85)",color:null!==(a=C.fontColor)&&void 0!==a?a:"white",borderRadius:"4px",pointerEvents:null!==(l=C.pointerEvents)&&void 0!==l?l:"none",fontFamily:null!==(s=C.fontFamily)&&void 0!==s?s:"Arial, sans-serif",fontSize:S(C.fontSize,"14px"),fontWeight:String(null!==(d=C.fontWeight)&&void 0!==d?d:"400"),fontStyle:null!==(u=C.fontStyle)&&void 0!==u?u:"normal",textAlign:null!==(c=C.textAlign)&&void 0!==c?c:"left",textDecoration:null!==(h=C.textDecoration)&&void 0!==h?h:"none",textTransform:null!==(f=C.textTransform)&&void 0!==f?f:"none",letterSpacing:S(C.letterSpacing,"normal"),border:(()=>{var t;if(!C.borderStyle)return"none";const e=S(C.borderWidth,"1px"),n=null!==(t=C.borderColor)&&void 0!==t?t:"transparent";return`${e} ${C.borderStyle} ${n}`})(),boxShadow:null!==(m=C.boxShadow)&&void 0!==m?m:"none",maxWidth:S(C.maxWidth,"200px"),minWidth:S(C.minWidth,"auto"),maxHeight:S(C.maxHeight,"none"),minHeight:S(C.minHeight,"auto"),zIndex:String(null!==(y=C.zIndex)&&void 0!==y?y:1e3),transition:null!==(p=C.transition)&&void 0!==p?p:"opacity 0.2s",transform:null!==(v=C.transform)&&void 0!==v?v:"none",backdropFilter:null!==(g=C.backdropFilter)&&void 0!==g?g:"none",opacity:String(null!==(x=C.opacity)&&void 0!==x?x:1),display:"none",visibility:"hidden"}),o(M,t.canvasContainerId),M}(t));let v=null;if(e.addEventListener("mousemove",o=>{v||(v=requestAnimationFrame(()=>{(function(t,e,o,a,l){const{mouseX:s,mouseY:d}=i(t,o),u=r(s,d,e);n=(null==l?void 0:l.cursorType)||"pointer",o.style.cursor="default",a&&(u?function(t,e,o,i){if(e&&e.value&&o&&i){o.style.cursor=n,i.style.display="block",i.innerHTML=function(t){var e,n,o;if(!t)return"";const i=null===(n=null===(e=t.toolTipConfig)||void 0===e?void 0:e.tooltipText)||void 0===n?void 0:n.trim();if(i)return`<div>${i}</div>`;const r=null===(o=t.label)||void 0===o?void 0:o.trim();return r?`<div>${r}<br>Value: ${t.value}</div>`:`<div>${t.value}</div>`}(e);const r=o.getBoundingClientRect();i.style.left=t.clientX-r.left+15+"px",i.style.top=t.clientY-r.top+15+"px",i.style.zIndex="999999",i.style.visibility="visible",i.style.opacity="1",i.style.position="absolute"}}(t,u,o,a):(o.style.cursor="default",a.style.display="none",a.style.visibility="hidden",a.style.opacity="0"))})(o,h,e,p,t),v=null}))}),t.onBubbleClick){let n=null;e.addEventListener("click",o=>{n||(n=requestAnimationFrame(()=>{!function(t,e,n,o){const{mouseX:a,mouseY:l}=i(t,n),s=r(a,l,e);null!=s&&o.onBubbleClick&&o.onBubbleClick(s,t)}(o,h,e,t),n=null}))})}}const u={defaultBubbleColor:"#3498DB",defaultFontColor:"#ffffff",minRadius:10,maxLines:"auto",textWrap:!0,isResizeCanvasOnWindowSizeChange:!0,fontSize:14,defaultFontFamily:"Arial",showToolTip:!0};function c(t={}){var e,n;if(!t)return void console.error("Configuration is not valid. Chart initialization aborted.");if(!t.data||0===t.data.length)return void console.error("No valid data provided. Chart initialization aborted.");const o=(i=Object.assign({canvasContainerId:null!==(e=t.canvasContainerId)&&void 0!==e?e:"chart-container",data:null!==(n=t.data)&&void 0!==n?n:[]},t),Object.assign(Object.assign({},u),i));var i;return d(o),o}class h{constructor(t){const e=c(t);e&&(this.configuration=e)}render(){this.configuration&&d(this.configuration)}}return window.initializeChart=c,e})());
1
+ !function(t,e){"object"==typeof exports&&"object"==typeof module?module.exports=e():"function"==typeof define&&define.amd?define("BubbleChart",[],e):"object"==typeof exports?exports.BubbleChart=e():t.BubbleChart=e()}(this,()=>(()=>{"use strict";var t={d:(e,n)=>{for(var i in n)t.o(n,i)&&!t.o(e,i)&&Object.defineProperty(e,i,{enumerable:!0,get:n[i]})},o:(t,e)=>Object.prototype.hasOwnProperty.call(t,e),r:t=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(t,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(t,"__esModule",{value:!0})}},e={};t.r(e),t.d(e,{BubbleChart:()=>m,initializeChart:()=>y});const n={defaultBubbleColor:"#3498DB",defaultFontColor:"#ffffff",minRadius:10,maxLines:"auto",textWrap:!0,isResizeCanvasOnWindowSizeChange:!0,fontSize:14,defaultFontFamily:"Arial",showToolTip:!0},i=JSON.parse('{"FC":0.008,"rX":1000,"d6":0.65,"WH":0.02,"ND":0.12,"$l":0.8,"cg":0.3,"e0":35}');function o(t,e){t.forEach((n,i)=>{t.forEach((t,o)=>{if(i>=o)return;if(0===i||0===o){const e=0===i?n:t,o=0===i?t:n,a=o.x-e.x,r=o.y-e.y,l=Math.hypot(a,r),s=e.radius+o.radius+2;if(l<s){const t=s-l,e=Math.atan2(r,a);o.x+=Math.cos(e)*t*.7,o.y+=Math.sin(e)*t*.7}return}const a=n.x-t.x,r=n.y-t.y,l=Math.hypot(a,r),s=n.radius+t.radius-5;if(l<s){const i=(s-l)*(.3+5*e.forceStrength),o=Math.atan2(r,a),d=n.radius+t.radius,c=d>0?t.radius/d:.5;n.fixed||(n.x+=Math.cos(o)*i*c,n.y+=Math.sin(o)*i*c),t.fixed||(t.x-=Math.cos(o)*i*(1-c),t.y-=Math.sin(o)*i*(1-c))}})})}function a(t,e=14,n=400,i=6){const o=n>=700?1.25:n>=500?1.1:1,a=Math.min(t/i,t/1.2);let r=Math.min(e*o,a,.8*t);return r=Math.max(r,Math.max(8,t/6)),Math.round(r)}class r{constructor(t){this.canvas=t;const e=t.getContext("2d");if(!e)throw new Error("Failed to get 2D rendering context from canvas");this.ctx=e}getContext(){return this.ctx}getCanvas(){return this.canvas}setup(){const t=window.devicePixelRatio||1,e=this.canvas.getBoundingClientRect();return this.canvas.width=e.width*t,this.canvas.height=e.height*t,this.canvas.style.width=`${e.width}px`,this.canvas.style.height=`${e.height}px`,this.ctx.setTransform(1,0,0,1,0,0),this.ctx.scale(t,t),{width:e.width,height:e.height}}getSize(){const t=this.canvas.getBoundingClientRect();return{width:t.width,height:t.height}}clear(){this.ctx.clearRect(0,0,this.canvas.width,this.canvas.height)}drawCircle(t,e,n,i,o,a,r){this.ctx.beginPath(),this.ctx.arc(t,e,n,0,2*Math.PI),this.ctx.fillStyle=i,this.ctx.fill(),a>0&&(this.ctx.strokeStyle=o,this.ctx.lineWidth=a,this.ctx.stroke())}drawText(t,e,n,i){const o=`${i.fontStyle} ${i.fontWeight} ${i.fontSize}px ${i.fontFamily}`;this.ctx.fillStyle=i.fontColor,this.ctx.font=o,this.ctx.imageSmoothingEnabled=!0,this.ctx.imageSmoothingQuality="high",this.ctx.textAlign=i.textAlign,this.ctx.textBaseline=i.textBaseline,this.ctx.fillText(t,Math.round(e),Math.round(n))}destroy(){const t=this.canvas.parentElement;t&&t.removeChild(this.canvas)}}class l{constructor(t){this.ctx=t}measureWidth(t,e,n,i,o){return this.ctx.font=`${i} ${n} ${e}px ${o}`,this.ctx.imageSmoothingEnabled=!0,this.ctx.imageSmoothingQuality="high",this.ctx.measureText(t).width}}function s(t,e){const n=`bubbleChartTooltip-${e}`;return document.body.querySelector(`#${n}`)||(t.id=n,document.body.appendChild(t),t)}function d(t,e){const n=e.getBoundingClientRect(),i=window.devicePixelRatio||1;return{mouseX:(t.clientX-n.left)*i,mouseY:(t.clientY-n.top)*i}}function c(t,e,n){return n.find(n=>Math.hypot(t-n.x,e-n.y)<n.radius)||null}function u(t){let e=null;return{handle:n=>{e||(e=requestAnimationFrame(()=>{t(n),e=null}))},cancel:()=>{e&&(cancelAnimationFrame(e),e=null)}}}class h{}function f(t){if(!function(t){return t?!(!Array.isArray(t.data)||0===t.data.length)||(console.error("Invalid or empty data array"),!1):(console.error("Invalid config object"),!1)}(t))return;const e=function(t){var e,n;const i=document.getElementById(t.canvasContainerId);if(!i)return console.error(`Canvas container with ID '${t.canvasContainerId}' not found.`),null;if(i.querySelector("canvas"))return console.error("A canvas already exists inside the container."),null;const o=document.createElement("canvas");return Object.assign(o.style,{border:(null===(e=t.canvasBorderColor)||void 0===e?void 0:e.trim())?`1px solid #${t.canvasBorderColor}`:h.TRANSPARENT,background:(null===(n=t.canvasBackgroundColor)||void 0===n?void 0:n.trim())?`#${t.canvasBackgroundColor}`:h.TRANSPARENT,width:"100%",height:"100%",display:"block",imageRendering:"crisp-edges",aspectRatio:"1 / 1"}),o.style.setProperty("image-rendering","-moz-crisp-edges"),o.style.setProperty("image-rendering","-webkit-optimize-contrast"),o.style.setProperty("-ms-interpolation-mode","nearest-neighbor"),i.appendChild(o),o}(t);if(!e)return;const n=new r(e),m=new l(n.getContext()),{width:y,height:v}=n.setup();let x=function(t,e,n,a){const r=Object.assign({forceStrength:i.FC,iterations:i.rX,damping:i.d6,boundaryForce:i.WH,centerForce:i.ND,centerAttraction:i.$l,centerDamping:i.cg,centerRadiusBuffer:i.e0},a),l=Math.min((e-10)/2,(n-10)/2),s=e/2,d=n/2,c=[...t].sort((t,e)=>e.value-t.value).map(t=>Object.assign(Object.assign({},t),{radius:0,x:0,y:0,fixed:!1}));if(0===c.length)return c;const u=c[0].value,h=Math.min(.5*l,.2*Math.min(e,n)),f=Math.max(.3*h,.05*Math.min(e,n));return c.forEach(t=>{const i=u>0?t.value/u:1;t.radius=f+i*(h-f),t.radius=Math.max(0,Math.min(t.radius,(e-10)/2,(n-10)/2))}),c.forEach((t,i)=>{if(0===i)t.x=s,t.y=d,t.fixed=!0;else{const o=c[0].radius+t.radius+3,a=Math.PI*(3-Math.sqrt(5)),r=e-5-t.radius,l=n-5-t.radius;t.x=Math.min(r,Math.max(5+t.radius,s+Math.cos(a*i)*o)),t.y=Math.min(l,Math.max(5+t.radius,d+Math.sin(a*i)*o)),t.fixed=!1}}),function(t,e,n,i,a,r,l){for(let i=0;i<e.iterations;i++)t.forEach((i,o)=>{if(0===o){const t=a-i.x,n=r-i.y;return void(Math.hypot(t,n)>2&&(i.x+=t*e.centerDamping,i.y+=n*e.centerDamping))}let s=0,d=0;const c=i.radius+5;i.x<c?s+=(c-i.x)*e.boundaryForce:i.x>n-c&&(s+=(n-c-i.x)*e.boundaryForce),t.forEach(t=>{if(i===t)return;const n=i.x-t.x,o=i.y-t.y,c=Math.hypot(n,o),u=i.radius+t.radius;if(c<1.5*u){const t=e.forceStrength*(u/Math.max(c,.1));s+=n/c*t,d+=o/c*t}const h=a-i.x,f=r-i.y,m=Math.hypot(h,f),y=i.value/l*.02;i.x+=h/m*y,i.y+=f/m*y});const u=a-i.x,h=r-i.y,f=Math.hypot(u,h),m=t[0].radius+i.radius+e.centerRadiusBuffer,y=e.centerAttraction*(1-i.value/l)*(1-Math.min(1,f/m));i.x+=u*y,i.y+=h*y,i.x+=s*(1-e.damping),i.y+=d*(1-e.damping)}),o(t,e)}(c,r,e,0,s,d,u),function(t,e,n){t.forEach(t=>{const i=Math.max(5+t.radius,Math.min(e-5-t.radius,t.x)),o=Math.max(5+t.radius,Math.min(n-5-t.radius,t.y));(!t.fixed||Math.hypot(t.x-i,t.y-o)>2)&&(t.x=i,t.y=o)})}(c,e,n),c}(t.data,y,v);function g(){n.setup(),n.clear(),x.forEach(e=>{const i=function(t,e,n){var i,o,r,l,s,d,c,u,h,f,m;let y=null!==(i=n.defaultBubbleColor)&&void 0!==i?i:"#3498db";if(t.bubbleColor)y=t.bubbleColor;else if(n.colorPalette&&n.colorPalette.length>0){const i=e.findIndex(e=>e.label===t.label&&e.value===t.value),o=i>=0?i:0;y=n.colorPalette[o%n.colorPalette.length]}const v=Math.max(t.radius||0,n.minRadius||10);return{bubbleColor:y,borderColor:null!==(o=t.borderColor)&&void 0!==o?o:"black",borderThickness:Math.max(null!==(r=t.borderThickness)&&void 0!==r?r:.25,0),opacity:void 0!==t.opacity?Math.max(0,Math.min(1,t.opacity)):1,fontStyle:t.fontStyle||"normal",fontWeight:"number"==typeof t.fontWeight&&t.fontWeight>=100&&t.fontWeight<=900?t.fontWeight:400,textAlign:null!==(l=t.textAlign)&&void 0!==l?l:"center",textTransform:null!==(s=t.textTransform)&&void 0!==s?s:"none",fontColor:null!==(c=null!==(d=t.fontColor)&&void 0!==d?d:n.defaultFontColor)&&void 0!==c?c:"#000",textBaseline:null!==(u=t.textBaseline)&&void 0!==u?u:"middle",fontSize:Math.max(a(v,null!==(h=n.fontSize)&&void 0!==h?h:14,null!==(f=t.fontWeight)&&void 0!==f?f:400),8),fontFamily:t.fontFamily&&"string"==typeof t.fontFamily&&"Arial"!==t.fontFamily?`${t.fontFamily}, Arial, sans-serif`:`${null!==(m=n.defaultFontFamily)&&void 0!==m?m:"Arial"}, sans-serif`}}(e,x,t);n.drawCircle(e.x,e.y,Math.max(e.radius||0,t.minRadius||10),i.bubbleColor,i.borderColor,i.borderThickness,i.opacity);let o=e.label||"";"uppercase"===i.textTransform?o=o.toUpperCase():"lowercase"===i.textTransform?o=o.toLowerCase():"capitalize"===i.textTransform&&(o=o.replace(/\b\w/g,t=>t.toUpperCase()));const r=Math.max(e.radius||0,t.minRadius||10),l={fontSize:i.fontSize,fontFamily:i.fontFamily,fontWeight:i.fontWeight,fontStyle:i.fontStyle,fontColor:i.fontColor,textAlign:i.textAlign,textBaseline:i.textBaseline};if(t.textWrap){const o=1.5*r-10,a=1.4*i.fontSize,s=function(t,e,n,i,o,a=14,r=400,l="normal",s="Arial",d=0,c=5,u=1.2){const h=1.5*(o-c),f=a*u,m=Math.max(1,Math.floor(h/f)),y="auto"===i||void 0===i?m:Math.min(i,m);n=Math.max(0,n-2*d);const v=e.split(" ");let x="";const g=[];let p=!1,b=!1;const C=e=>t.measureWidth(e,a,r,l,s);for(const t of v){const e=x?`${x} ${t}`:t;if(C(e)<=n)x=e;else if(x.trim()&&g.push(x),x=t,C(x)>n){let t=x;for(;C(t+"...")>n&&t.length>0;)t=t.slice(0,-1);g.push(t+"..."),b=!0;break}if(g.length>=y){p=!0;break}}if(x&&g.length<y&&!b&&g.push(x),p&&g.length>0){let t=g[g.length-1];for(;C(t+"...")>n&&t.length>0;)t=t.slice(0,-1);g[g.length-1]=t+"..."}return g}(m,e.label,o,t.maxLines,r,i.fontSize,i.fontWeight,i.fontStyle,i.fontFamily),d=e.y-(s.length-1)*a/2;s.forEach((t,i)=>{n.drawText(t,e.x,d+i*a,l)})}else n.drawText(o,e.x,e.y,l)})}let p=null;if(t.isResizeCanvasOnWindowSizeChange){const e=document.getElementById(t.canvasContainerId);e&&(p=function(t,e){let n=null,i=null;const o=()=>{i||(i=requestAnimationFrame(()=>{e(),i=null}))};return"undefined"!=typeof ResizeObserver?(n=new ResizeObserver(o),n.observe(t)):(o(),window.addEventListener("resize",o)),{destroy:()=>{n&&(n.disconnect(),n=null),window.removeEventListener("resize",o),i&&(cancelAnimationFrame(i),i=null)}}}(e,()=>{const t=n.getCanvas();e&&t&&(t.width=e.offsetWidth,t.height=e.offsetHeight,g())}))}g();let b=null;t.showToolTip&&(b=function(t){var e,n,i,o,a,r,l,d,c,u,h,f,m,y,v,x,g,p,b;const C=null===(i=null===(n=null===(e=t.data[0])||void 0===e?void 0:e.toolTipConfig)||void 0===n?void 0:n.tooltipFormattedData)||void 0===i?void 0:i.trim();if(C){const e=document.createElement("div");e.innerHTML=C.trim();const n=e.firstElementChild;return n.style.display="none",n.style.visibility="hidden",n.style.opacity="0",n.style.position="absolute",s(n,t.canvasContainerId)}const M=document.createElement("div");M.style.display="none";const w=null!==(o=null==t?void 0:t.tooltipOptions)&&void 0!==o?o:{},S=(t,e)=>"number"==typeof t?`${t}px`:null!=t?t:e;return Object.assign(M.style,{position:"absolute",padding:S(w.padding,"8px"),margin:S(w.margin,"0"),background:null!==(a=w.backgroundColor)&&void 0!==a?a:"rgba(0, 0, 0, 0.85)",color:null!==(r=w.fontColor)&&void 0!==r?r:"white",borderRadius:"4px",pointerEvents:null!==(l=w.pointerEvents)&&void 0!==l?l:"none",fontFamily:null!==(d=w.fontFamily)&&void 0!==d?d:"Arial, sans-serif",fontSize:S(w.fontSize,"14px"),fontWeight:String(null!==(c=w.fontWeight)&&void 0!==c?c:"400"),fontStyle:null!==(u=w.fontStyle)&&void 0!==u?u:"normal",textAlign:null!==(h=w.textAlign)&&void 0!==h?h:"left",textDecoration:null!==(f=w.textDecoration)&&void 0!==f?f:"none",textTransform:null!==(m=w.textTransform)&&void 0!==m?m:"none",letterSpacing:S(w.letterSpacing,"normal"),border:(()=>{var t;if(!w.borderStyle)return"none";const e=S(w.borderWidth,"1px"),n=null!==(t=w.borderColor)&&void 0!==t?t:"transparent";return`${e} ${w.borderStyle} ${n}`})(),boxShadow:null!==(y=w.boxShadow)&&void 0!==y?y:"none",maxWidth:S(w.maxWidth,"200px"),minWidth:S(w.minWidth,"auto"),maxHeight:S(w.maxHeight,"none"),minHeight:S(w.minHeight,"auto"),zIndex:String(null!==(v=w.zIndex)&&void 0!==v?v:1e3),transition:null!==(x=w.transition)&&void 0!==x?x:"opacity 0.2s",transform:null!==(g=w.transform)&&void 0!==g?g:"none",backdropFilter:null!==(p=w.backdropFilter)&&void 0!==p?p:"none",opacity:String(null!==(b=w.opacity)&&void 0!==b?b:1),display:"none",visibility:"hidden"}),s(M,t.canvasContainerId),M}(t));const C=n.getCanvas(),M=u(e=>{const{mouseX:n,mouseY:i}=d(e,C),o=c(n,i,x);if(o){const n=(null==t?void 0:t.cursorType)||"pointer";if(C.style.cursor=n,b){const n=function(t,e){var n,i,o;if(!t)return"";if(e.tooltipOptions&&"function"==typeof e.tooltipOptions.formatter)return e.tooltipOptions.formatter(t);const a=null===(i=null===(n=t.toolTipConfig)||void 0===n?void 0:n.tooltipText)||void 0===i?void 0:i.trim();if(a)return`<div>${a}</div>`;const r=null===(o=t.label)||void 0===o?void 0:o.trim();return r?`<div>${r}<br>Value: ${t.value}</div>`:`<div>${t.value}</div>`}(o,t);!function(t,e,n,i){e&&n&&(e.style.display="block",e.innerHTML=i,e.style.left=`${t.pageX+15}px`,e.style.top=`${t.pageY+15}px`,e.style.zIndex="999999",e.style.visibility="visible",e.style.opacity="1",e.style.position="absolute")}(e,b,C,n)}}else C.style.cursor="default",function(t){t&&(t.style.display="none",t.style.visibility="hidden",t.style.opacity="0")}(b)});C.addEventListener("mousemove",M.handle);const w=u(e=>{const{mouseX:n,mouseY:i}=d(e,C),o=c(n,i,x);o&&t.onBubbleClick&&t.onBubbleClick(o,e)});t.onBubbleClick&&C.addEventListener("click",w.handle);const S=()=>{null==p||p.destroy(),M.cancel(),w.cancel(),C.removeEventListener("mousemove",M.handle),C.removeEventListener("click",w.handle),n.destroy(),(null==b?void 0:b.parentElement)&&b.parentElement.removeChild(b),b=null};return{destroy:S,update:e=>(S(),t.data=e,f(t))}}h.TRANSPARENT="transparent";class m{constructor(t){const e=function(t={}){var e,i;if(!t)return void console.error("Configuration is not valid. Chart initialization aborted.");if(!t.data||0===t.data.length)return void console.error("No valid data provided. Chart initialization aborted.");const o=(a=Object.assign({canvasContainerId:null!==(e=t.canvasContainerId)&&void 0!==e?e:"chart-container",data:null!==(i=t.data)&&void 0!==i?i:[]},t),Object.assign(Object.assign({},n),a));var a;const r=f(o);return r?{config:o,instance:r}:void 0}(t);e&&(this.configuration=e.config,this.instance=e.instance)}destroy(){this.instance&&this.instance.destroy()}update(t){this.instance&&(this.instance=this.instance.update(t))}}function y(t){return new m(t)}return"undefined"!=typeof window&&(window.initializeChart=y,window.BubbleChart=m),e})());
@@ -0,0 +1,194 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>BubbleChartJS Test</title>
7
+ <style>
8
+ body {
9
+ font-family: Arial, sans-serif;
10
+ margin: 20px;
11
+ background-color: #f4f4f9;
12
+ }
13
+ .container {
14
+ display: flex;
15
+ flex-direction: column;
16
+ gap: 20px;
17
+ max-width: 800px;
18
+ margin: 0 auto;
19
+ }
20
+ .controls {
21
+ display: flex;
22
+ gap: 10px;
23
+ padding: 15px;
24
+ background: white;
25
+ border-radius: 8px;
26
+ box-shadow: 0 2px 4px rgba(0,0,0,0.1);
27
+ }
28
+ button {
29
+ padding: 8px 16px;
30
+ cursor: pointer;
31
+ border: none;
32
+ border-radius: 4px;
33
+ background-color: #007bff;
34
+ color: white;
35
+ font-weight: bold;
36
+ }
37
+ button:hover {
38
+ background-color: #0056b3;
39
+ }
40
+ button.danger {
41
+ background-color: #dc3545;
42
+ }
43
+ button.danger:hover {
44
+ background-color: #a71d2a;
45
+ }
46
+ #chart-container {
47
+ width: 100%;
48
+ height: 400px;
49
+ background: white;
50
+ border-radius: 8px;
51
+ box-shadow: 0 2px 4px rgba(0,0,0,0.1);
52
+ position: relative; /* important for tooltip */
53
+ }
54
+ #log {
55
+ padding: 15px;
56
+ background: #333;
57
+ color: #0f0;
58
+ font-family: monospace;
59
+ border-radius: 8px;
60
+ min-height: 100px;
61
+ }
62
+ </style>
63
+ </head>
64
+ <body>
65
+ <div class="container">
66
+ <h2>BubbleChartJS Testing Playground</h2>
67
+
68
+ <div class="controls">
69
+ <button id="btn-update">Update Data (Dynamic Update)</button>
70
+ <button id="btn-destroy" class="danger">Destroy Chart</button>
71
+ <button id="btn-recreate">Recreate Chart</button>
72
+ <button id="btn-resize">Change Container Size (Responsive)</button>
73
+ </div>
74
+
75
+ <div id="chart-container"></div>
76
+
77
+ <div id="log">Logs will appear here...<br></div>
78
+ </div>
79
+
80
+ <!-- Load the UMD bundle -->
81
+ <script src="../dist/bubbleChart.umd.js?v=2"></script>
82
+
83
+ <script>
84
+ const logEl = document.getElementById('log');
85
+ function log(msg) {
86
+ logEl.innerHTML += `> ${msg}<br>`;
87
+ logEl.scrollTop = logEl.scrollHeight;
88
+ }
89
+
90
+ let chartInstance = null;
91
+
92
+ const dataset1 = [
93
+ { label: "Sales", value: 50 },
94
+ { label: "Marketing", value: 30 },
95
+ { label: "Engineering", value: 80 },
96
+ { label: "HR", value: 20 },
97
+ { label: "Customer Support", value: 45 }
98
+ ];
99
+
100
+ const dataset2 = [
101
+ { label: "Alpha", value: 90 },
102
+ { label: "Beta", value: 60 },
103
+ { label: "Gamma", value: 40 }
104
+ ];
105
+
106
+ function createChart(data) {
107
+ if (chartInstance) {
108
+ log("Chart already exists. Destroy it first.");
109
+ return;
110
+ }
111
+
112
+ log("Initializing chart...");
113
+ chartInstance = window.initializeChart({
114
+ canvasContainerId: "chart-container",
115
+ data: data,
116
+ minRadius: 20,
117
+ maxLines: 3,
118
+ textWrap: true,
119
+ isResizeCanvasOnWindowSizeChange: true, // Responsive Resize
120
+ colorPalette: ["#FF6B6B", "#4ECDC4", "#45B7D1", "#96CEB4", "#FFEEAD"], // Custom Color Palette
121
+ defaultFontColor: "#ffffff",
122
+ fontSize: 14,
123
+ showToolTip: true,
124
+ tooltipOptions: {
125
+ backgroundColor: "rgba(0, 0, 0, 0.9)",
126
+ fontColor: "#fff",
127
+ // GLOBAL TOOLTIP FORMATTER
128
+ formatter: (item) => {
129
+ return `<div style="text-align: center;">
130
+ <strong>🔥 Custom Formatter 🔥</strong><br>
131
+ <i>${item.label}</i><br>
132
+ <span style="color: #4ECDC4; font-size: 1.2em;">${item.value} units</span>
133
+ </div>`;
134
+ }
135
+ },
136
+ // BUBBLE CLICK EVENT
137
+ onBubbleClick: (bubble, event) => {
138
+ log(`Clicked on bubble: ${bubble.label} (Value: ${bubble.value})`);
139
+ }
140
+ });
141
+ log("Chart initialized.");
142
+
143
+ // Debugging the instance structure
144
+ log(`Instance Type: ${chartInstance.constructor ? chartInstance.constructor.name : 'Unknown'}`);
145
+ log(`Has update: ${!!chartInstance.update}`);
146
+ log(`Has destroy: ${!!chartInstance.destroy}`);
147
+ let props = [];
148
+ for (let k in chartInstance) { props.push(k); }
149
+ log(`Properties: ${props.join(', ')}`);
150
+ }
151
+
152
+ // 1. Initial Render
153
+ createChart(dataset1);
154
+
155
+ // 2. Dynamic Update binding
156
+ document.getElementById('btn-update').addEventListener('click', () => {
157
+ if (chartInstance && chartInstance.update) {
158
+ log("Updating chart with new dataset...");
159
+ chartInstance.update(dataset2);
160
+ } else {
161
+ log("Cannot update: Chart instance not found or doesn't support update().");
162
+ }
163
+ });
164
+
165
+ // 3. Destroy Method binding
166
+ document.getElementById('btn-destroy').addEventListener('click', () => {
167
+ if (chartInstance && chartInstance.destroy) {
168
+ log("Destroying chart...");
169
+ chartInstance.destroy();
170
+ chartInstance = null;
171
+ } else {
172
+ log("Cannot destroy: Chart instance not found or doesn't support destroy().");
173
+ }
174
+ });
175
+
176
+ // 4. Recreate binding
177
+ document.getElementById('btn-recreate').addEventListener('click', () => {
178
+ createChart(dataset1);
179
+ });
180
+
181
+ // 5. Responsive Resize binding
182
+ // Test ResizeObserver or Window Resize behavior
183
+ document.getElementById('btn-resize').addEventListener('click', () => {
184
+ const container = document.getElementById('chart-container');
185
+ const newWidth = container.style.width === '50%' ? '100%' : '50%';
186
+ container.style.width = newWidth;
187
+ log(`Resized container to ${newWidth}. Check if chart resizes correctly.`);
188
+ // Simulate window resize to trigger the window scale logic, since ResizeObserver is not implemented yet
189
+ window.dispatchEvent(new Event('resize'));
190
+ });
191
+
192
+ </script>
193
+ </body>
194
+ </html>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "bubble-chart-js",
3
- "version": "1.0.18",
3
+ "version": "1.1.0",
4
4
  "description": "bubbleChartJs is a lightweight, customizable JavaScript library for creating stacked bubble charts. It arranges bubbles based on their values, with the largest bubble positioned at the top and surrounding bubbles decreasing in size accordingly.",
5
5
  "main": "dist/bubbleChart.cjs.js",
6
6
  "module": "dist/bubbleChart.esm.js",
@@ -40,11 +40,14 @@
40
40
  "author": "Pragadeeshwaran Pasupathi <pragadeeshwaran.pasupathi@gmail.com>",
41
41
  "repository": {
42
42
  "type": "git",
43
- "url": "https://github.com/Praga-Dev/bubbleChartJS.git"
43
+ "url": "git+https://github.com/Praga-Dev/bubbleChartJS.git"
44
44
  },
45
45
  "license": "MIT",
46
46
  "keywords": [
47
+ "bubble",
48
+ "chart",
47
49
  "bubble chart",
50
+ "bubble chart js",
48
51
  "chartjs",
49
52
  "chart.js",
50
53
  "visualization",
@@ -58,4 +61,4 @@
58
61
  "data plotting",
59
62
  "bubble chartjs"
60
63
  ]
61
- }
64
+ }
@@ -0,0 +1,45 @@
1
+ /**
2
+ * Conformance Test Fixture Generator
3
+ *
4
+ * Run with: npx ts-node --project tsconfig.json scripts/generate-fixtures.ts
5
+ *
6
+ * Reads all input fixtures from spec/fixtures/, runs them through the layout engine,
7
+ * and writes the expected output alongside each input file.
8
+ */
9
+
10
+ import * as fs from 'fs';
11
+ import * as path from 'path';
12
+ import { computeLayout } from '../src/core/layout-engine';
13
+
14
+ const fixturesDir = path.join(__dirname, '..', 'spec', 'fixtures');
15
+
16
+ const inputFiles = fs
17
+ .readdirSync(fixturesDir)
18
+ .filter((f) => f.endsWith('.input.json'));
19
+
20
+ for (const inputFile of inputFiles) {
21
+ const inputPath = path.join(fixturesDir, inputFile);
22
+ const input = JSON.parse(fs.readFileSync(inputPath, 'utf8'));
23
+
24
+ const result = computeLayout(input.data, input.width, input.height);
25
+
26
+ const expected = {
27
+ specVersion: input.specVersion || '1.0',
28
+ tolerance: 1.0,
29
+ nodes: result.map((node) => ({
30
+ label: node.label,
31
+ x: Math.round(node.x * 100) / 100,
32
+ y: Math.round(node.y * 100) / 100,
33
+ radius: Math.round(node.radius * 100) / 100,
34
+ fixed: node.fixed,
35
+ })),
36
+ };
37
+
38
+ const expectedFile = inputFile.replace('.input.json', '.expected.json');
39
+ const expectedPath = path.join(fixturesDir, expectedFile);
40
+
41
+ fs.writeFileSync(expectedPath, JSON.stringify(expected, null, 2) + '\n');
42
+ console.log(`Generated: ${expectedFile} (${expected.nodes.length} nodes)`);
43
+ }
44
+
45
+ console.log('Done! All expected fixtures generated.');
@@ -0,0 +1,16 @@
1
+ {
2
+ "specVersion": "1.0",
3
+ "defaultBubbleColor": "#3498DB",
4
+ "defaultFontColor": "#ffffff",
5
+ "minRadius": 10,
6
+ "maxLines": "auto",
7
+ "textWrap": true,
8
+ "fontSize": 14,
9
+ "defaultFontFamily": "Arial",
10
+ "showTooltip": true,
11
+ "enableResize": true,
12
+ "borderThickness": 0.25,
13
+ "borderColor": "#000000",
14
+ "opacity": 1.0,
15
+ "canvasPadding": 5
16
+ }
@@ -0,0 +1,10 @@
1
+ {
2
+ "specVersion": "1.0",
3
+ "width": 800,
4
+ "height": 600,
5
+ "data": [
6
+ { "label": "Alpha", "value": 100 },
7
+ { "label": "Beta", "value": 50 },
8
+ { "label": "Gamma", "value": 25 }
9
+ ]
10
+ }
@@ -0,0 +1,11 @@
1
+ {
2
+ "specVersion": "1.0",
3
+ "width": 800,
4
+ "height": 600,
5
+ "data": [
6
+ { "label": "A", "value": 50 },
7
+ { "label": "B", "value": 50 },
8
+ { "label": "C", "value": 50 },
9
+ { "label": "D", "value": 50 }
10
+ ]
11
+ }
@@ -0,0 +1,17 @@
1
+ {
2
+ "specVersion": "1.0",
3
+ "width": 800,
4
+ "height": 600,
5
+ "data": [
6
+ { "label": "Item 1", "value": 100 },
7
+ { "label": "Item 2", "value": 90 },
8
+ { "label": "Item 3", "value": 80 },
9
+ { "label": "Item 4", "value": 70 },
10
+ { "label": "Item 5", "value": 60 },
11
+ { "label": "Item 6", "value": 50 },
12
+ { "label": "Item 7", "value": 40 },
13
+ { "label": "Item 8", "value": 30 },
14
+ { "label": "Item 9", "value": 20 },
15
+ { "label": "Item 10", "value": 10 }
16
+ ]
17
+ }
@@ -0,0 +1,8 @@
1
+ {
2
+ "specVersion": "1.0",
3
+ "width": 800,
4
+ "height": 600,
5
+ "data": [
6
+ { "label": "Solo", "value": 100 }
7
+ ]
8
+ }
@@ -0,0 +1,11 @@
1
+ {
2
+ "specVersion": "1.0",
3
+ "forceStrength": 0.008,
4
+ "iterations": 1000,
5
+ "damping": 0.65,
6
+ "boundaryForce": 0.02,
7
+ "centerForce": 0.12,
8
+ "centerAttraction": 0.8,
9
+ "centerDamping": 0.3,
10
+ "centerRadiusBuffer": 35
11
+ }
package/dist/canvas.d.ts DELETED
@@ -1,5 +0,0 @@
1
- import { Configuration } from "./models/public/configuration";
2
- /**
3
- * Creates and initializes the canvas inside the specified container.
4
- */
5
- export declare function createCanvas(config: Configuration): HTMLCanvasElement | null;
@@ -1,3 +0,0 @@
1
- export declare class AppConstants {
2
- static readonly TRANSPARENT = "transparent";
3
- }
@@ -1,10 +0,0 @@
1
- export declare const PHYSICS: {
2
- readonly forceStrength: 0.008;
3
- readonly iterations: 1000;
4
- readonly damping: 0.65;
5
- readonly boundaryForce: 0.02;
6
- readonly centerForce: 0.12;
7
- readonly centerAttraction: 0.8;
8
- readonly centerDamping: 0.3;
9
- readonly centerRadiusBuffer: 35;
10
- };
@@ -1,2 +0,0 @@
1
- import { Configuration } from "../models/public/configuration";
2
- export declare function renderChart(config: Configuration): void;
@@ -1,2 +0,0 @@
1
- export declare function getWrappedLines(ctx: CanvasRenderingContext2D, text: string, maxLineWidth: number, maxAllowedLines: number | "auto" | undefined, radius: number, fontSize?: number, // TODO : take all default values from constants
2
- fontWeight?: number, fontStyle?: "normal" | "italic" | "oblique", fontFamily?: string, horizontalPadding?: number, verticalPadding?: number, lineHeightFactor?: number, maxCharsPerWord?: number | undefined): string[];
@@ -1,6 +0,0 @@
1
- import { DataItemInfo } from "../models/internal/data-item-info";
2
- import { Configuration } from "../models/public/configuration";
3
- export declare function createTooltipElement(config: Configuration): HTMLDivElement | null;
4
- export declare function onBubbleClickEventHandler(// TODO : move to interactions.ts
5
- event: MouseEvent, data: DataItemInfo[], canvas: HTMLCanvasElement, config: Configuration): void;
6
- export declare function handleMouseMove(event: MouseEvent, data: DataItemInfo[], canvas: HTMLCanvasElement, tooltip: HTMLDivElement, config: Configuration): void;
package/dist/index.d.ts DELETED
@@ -1,8 +0,0 @@
1
- import { BubbleChart } from "./models/public/bubble-chart";
2
- import { initializeChart } from "./services/chart-service";
3
- export { initializeChart, BubbleChart };
4
- declare global {
5
- interface Window {
6
- initializeChart: typeof initializeChart;
7
- }
8
- }
@@ -1,7 +0,0 @@
1
- import { DataItem } from "../public/data-item";
2
- export interface DataItemInfo extends DataItem {
3
- radius: number;
4
- x: number;
5
- y: number;
6
- fixed: boolean;
7
- }
@@ -1,6 +0,0 @@
1
- import { Configuration } from "./configuration";
2
- export declare class BubbleChart {
3
- configuration: Configuration;
4
- constructor(config: Configuration);
5
- render(): void;
6
- }
@@ -1,26 +0,0 @@
1
- /**
2
- * Defines the appearance properties for a bubble.
3
- */
4
- export interface BubbleAppearance {
5
- /**
6
- * Bubble background color
7
- * @default "#3498DB"
8
- */
9
- bubbleColor?: string;
10
- /**
11
- * Border thickness (in pixels)
12
- * @default 0.25
13
- */
14
- borderThickness?: number;
15
- /**
16
- * Border color
17
- * @default "black"
18
- */
19
- borderColor?: string;
20
- /**
21
- * Opacity level (0: transparent, 1: fully visible)
22
- * @validValues 0 - 1
23
- * @default 1
24
- */
25
- opacity?: number;
26
- }
@@ -1,46 +0,0 @@
1
- /**
2
- * Defines the font-related styling options.
3
- */
4
- export interface FontOptions {
5
- /**
6
- * Custom font family
7
- * @example "Arial", "Verdana"
8
- * @default "Arial"
9
- */
10
- fontFamily?: string;
11
- /**
12
- * Font style
13
- * @validValues "normal" | "italic" | "oblique"
14
- * @default "normal"
15
- */
16
- fontStyle?: "normal" | "italic" | "oblique";
17
- /**
18
- * Font weight (numeric scale)
19
- * @validValues 100 | 200 | 300 | 400 | 500 | 600 | 700 | 800 | 900
20
- * @default 400
21
- */
22
- fontWeight?: 100 | 200 | 300 | 400 | 500 | 600 | 700 | 800 | 900;
23
- /**
24
- * Text alignment
25
- * @validValues "center" | "end" | "left" | "right" | "start"
26
- * @default "center"
27
- */
28
- textAlign?: "center" | "end" | "left" | "right" | "start";
29
- /**
30
- * Text transformation style
31
- * @validValues "none" | "uppercase" | "lowercase" | "capitalize"
32
- * @default "none"
33
- */
34
- textTransform?: "none" | "uppercase" | "lowercase" | "capitalize";
35
- /**
36
- * Font color
37
- * @default "#000000"
38
- */
39
- fontColor?: string;
40
- /**
41
- * Text baseline position
42
- * @validValues "alphabetic" | "bottom" | "hanging" | "ideographic" | "middle" | "top"
43
- * @default "middle"
44
- */
45
- textBaseline?: "alphabetic" | "bottom" | "hanging" | "ideographic" | "middle" | "top";
46
- }
@@ -1,6 +0,0 @@
1
- export interface InteractionOptions {
2
- showToolTip?: boolean;
3
- isResizeCanvasOnWindowSizeChange?: boolean;
4
- cursorType?: "default" | "pointer" | "grab" | "crosshair" | "move" | "not-allowed" | "help";
5
- onBubbleClick?: (bubbleData: any, event: MouseEvent) => void;
6
- }
@@ -1,4 +0,0 @@
1
- export declare class ToolTipConfig {
2
- tooltipText?: string;
3
- tooltipFormattedData?: string;
4
- }
@@ -1,170 +0,0 @@
1
- export interface TooltipOptions {
2
- /**
3
- * CSS font-family property
4
- * @default "Arial, sans-serif"
5
- */
6
- fontFamily?: string;
7
- /**
8
- * Font style for tooltip text
9
- * @validValues "normal", "italic", "oblique"
10
- * @default "normal"
11
- */
12
- fontStyle?: "normal" | "italic" | "oblique";
13
- /**
14
- * Font weight (numeric scale)
15
- * @validValues 100 | 200 | 300 | 400 | 500 | 600 | 700 | 800 | 900
16
- * @default 400
17
- */
18
- fontWeight?: 100 | 200 | 300 | 400 | 500 | 600 | 700 | 800 | 900;
19
- /**
20
- * Font size in pixels
21
- * @default 14
22
- */
23
- fontSize?: number;
24
- /**
25
- * Text alignment within tooltip
26
- * @validValues "left", "center", "right"
27
- * @default "left"
28
- */
29
- textAlign?: "left" | "center" | "right";
30
- /**
31
- * Text decoration style
32
- * @validValues "none", "underline", "line-through", "overline"
33
- * @default "none"
34
- */
35
- textDecoration?: "none" | "underline" | "line-through" | "overline";
36
- /**
37
- * Text transformation style
38
- * @validValues "none", "uppercase", "lowercase", "capitalize"
39
- * @default "none"
40
- */
41
- textTransform?: "none" | "uppercase" | "lowercase" | "capitalize";
42
- /**
43
- * Letter spacing in pixels (0 = normal)
44
- * @default undefined (normal spacing)
45
- */
46
- letterSpacing?: number;
47
- /**
48
- * Text color in CSS format
49
- * @default "white"
50
- */
51
- fontColor?: string;
52
- /**
53
- * Background color in CSS format
54
- * @default "rgba(0, 0, 0, 0.85)"
55
- */
56
- backgroundColor?: string;
57
- /**
58
- * Border color in CSS format
59
- * @default "transparent"
60
- */
61
- borderColor?: string;
62
- /**
63
- * Border style (requires borderWidth to be set)
64
- * @validValues "solid", "dashed", "dotted", "double", "none"
65
- * @default "none"
66
- */
67
- borderStyle?: "solid" | "dashed" | "dotted" | "double" | "none";
68
- /**
69
- * Border width (CSS value or number in pixels)
70
- * @example "2px", "0.5rem", 3
71
- */
72
- borderWidth?: string | number;
73
- /**
74
- * CSS padding value
75
- * @example "10px", "1em 2rem", "5% 10px"
76
- * @default "8px"
77
- */
78
- padding?: string | number;
79
- /**
80
- * CSS margin value
81
- * @example "10px auto", "2rem 1rem"
82
- * @default "0"
83
- */
84
- margin?: string | number;
85
- /**
86
- * CSS box-shadow property
87
- * @example "3px 3px 5px rgba(0,0,0,0.3)"
88
- * @default "none"
89
- */
90
- boxShadow?: string;
91
- /**
92
- * Opacity value (0 = fully transparent, 1 = fully opaque)
93
- * @minimum 0
94
- * @maximum 1
95
- * @default 1
96
- */
97
- opacity?: number;
98
- /**
99
- * Maximum width in pixels
100
- * @default 200
101
- */
102
- maxWidth?: number;
103
- /**
104
- * Minimum width in pixels
105
- * @default undefined (auto)
106
- */
107
- minWidth?: number;
108
- /**
109
- * Maximum height in pixels
110
- * @default undefined (none)
111
- */
112
- maxHeight?: number;
113
- /**
114
- * Minimum height in pixels
115
- * @default undefined (auto)
116
- */
117
- minHeight?: number;
118
- /**
119
- * Preferred position relative to target element
120
- * @validValues "top", "bottom", "left", "right"
121
- */
122
- position?: "top" | "bottom" | "left" | "right";
123
- /**
124
- * Horizontal offset in pixels
125
- * @default 0
126
- */
127
- offsetX?: number;
128
- /**
129
- * Vertical offset in pixels
130
- * @default 0
131
- */
132
- offsetY?: number;
133
- /**
134
- * CSS z-index property
135
- * @default 1000
136
- */
137
- zIndex?: number;
138
- /**
139
- * CSS pointer-events property
140
- * @validValues "auto", "none"
141
- * @default "none"
142
- */
143
- pointerEvents?: "auto" | "none";
144
- /**
145
- * Enable/disable animations
146
- * @default true
147
- */
148
- animation?: boolean;
149
- /**
150
- * Animation duration in milliseconds
151
- * @default 300
152
- */
153
- animationDuration?: number;
154
- /**
155
- * CSS backdrop-filter property
156
- * @example "blur(5px)"
157
- * @default "none"
158
- */
159
- backdropFilter?: string;
160
- /**
161
- * CSS transition property
162
- * @default "opacity 0.2s"
163
- */
164
- transition?: string;
165
- /**
166
- * CSS transform property
167
- * @default "none"
168
- */
169
- transform?: string;
170
- }
@@ -1,30 +0,0 @@
1
- import { InteractionOptions } from "./config/interaction-options";
2
- import { TooltipOptions } from "./config/tooltip-options";
3
- import { DataItem } from "./data-item";
4
- export interface Configuration extends InteractionOptions {
5
- data: DataItem[];
6
- canvasContainerId: string;
7
- /**
8
- * Background color of the canvas.
9
- *
10
- * @description Supports only HEX values (without `#`).
11
- * @default "transparent"
12
- */
13
- canvasBackgroundColor?: string;
14
- /**
15
- * Border color of the canvas.
16
- *
17
- * @description Supports only HEX values (without `#`).
18
- * @default "transparent"
19
- */
20
- canvasBorderColor?: string;
21
- minRadius: number;
22
- maxLines: number | "auto";
23
- textWrap: boolean;
24
- defaultBubbleColor: string;
25
- fontSize: number;
26
- defaultFontColor: string;
27
- defaultFontFamily: string;
28
- isResizeCanvasOnWindowSizeChange: boolean;
29
- tooltipOptions?: TooltipOptions;
30
- }
@@ -1,8 +0,0 @@
1
- import { BubbleAppearance } from "./config/bubble-appearance";
2
- import { FontOptions } from "./config/font-options";
3
- import { ToolTipConfig } from "./config/tooltip-config";
4
- export interface DataItem extends BubbleAppearance, FontOptions {
5
- label: string;
6
- value: number;
7
- toolTipConfig?: ToolTipConfig;
8
- }
@@ -1,5 +0,0 @@
1
- import { Configuration } from "../models/public/configuration";
2
- /**
3
- * Initializes the chart, but stops execution if no valid data is provided.
4
- */
5
- export declare function initializeChart(config?: Partial<Configuration>): Configuration | undefined;
@@ -1,3 +0,0 @@
1
- import { DataItemInfo } from "../models/internal/data-item-info";
2
- import { Configuration } from "../models/public/configuration";
3
- export declare function getChartData(config: Configuration, width: number, height: number): DataItemInfo[];
@@ -1,12 +0,0 @@
1
- import { Configuration } from "../models/public/configuration";
2
- /**
3
- * Default configuration object.
4
- */
5
- export declare const DEFAULT_CONFIG: Omit<Configuration, "canvasContainerId" | "data">;
6
- /**
7
- * Merges user config with defaults, ensuring `canvasContainerId` and `data` are required.
8
- */
9
- export declare function mergeConfig(customConfig: {
10
- canvasContainerId: string;
11
- data: Configuration["data"];
12
- } & Partial<Configuration>): Configuration;
@@ -1,2 +0,0 @@
1
- export declare function getFontSize(radius: number, fontSize?: number, fontWeight?: number, avgCharsPerLine?: number): number;
2
- export declare function isFontAvailable(font: string, fontSize?: string): boolean;
@@ -1,5 +0,0 @@
1
- import { Configuration } from "../models/public/configuration";
2
- /**
3
- * Validates configuration and ensures required properties exist.
4
- */
5
- export declare function validateConfig(config: Configuration): boolean;