bubble-chart-js 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.eslintrc.json +15 -0
- package/.gitattributes +2 -0
- package/.prettierrc +7 -0
- package/CHANGELOG.md +17 -0
- package/README.md +109 -0
- package/bubble-chart-js-1.0.0.tgz +0 -0
- package/dist/bundle.js +186 -0
- package/dist/canvas.d.ts +4 -0
- package/dist/constants/physics.d.ts +10 -0
- package/dist/core/renderer.d.ts +2 -0
- package/dist/features/textWrapper.d.ts +1 -0
- package/dist/features/tooltip.d.ts +3 -0
- package/dist/main.d.ts +1 -0
- package/dist/models/internal/dataItemInfo.d.ts +7 -0
- package/dist/models/public/configuration.d.ts +15 -0
- package/dist/models/public/dataItem.d.ts +4 -0
- package/dist/services/chartService.d.ts +5 -0
- package/dist/services/renderService.d.ts +3 -0
- package/dist/utils/config.d.ts +12 -0
- package/dist/utils/helper.d.ts +1 -0
- package/dist/utils/validation.d.ts +5 -0
- package/jest.config.js +5 -0
- package/package.json +35 -0
- package/src/canvas.ts +17 -0
- package/src/constants/physics.ts +10 -0
- package/src/core/renderer.ts +110 -0
- package/src/features/textWrapper.ts +168 -0
- package/src/features/tooltip.ts +69 -0
- package/src/main.ts +5 -0
- package/src/models/internal/dataItemInfo.ts +8 -0
- package/src/models/public/configuration.ts +16 -0
- package/src/models/public/dataItem.ts +4 -0
- package/src/services/chartService.ts +24 -0
- package/src/services/renderService.ts +262 -0
- package/src/utils/config.ts +33 -0
- package/src/utils/helper.ts +3 -0
- package/src/utils/validation.ts +18 -0
- package/tsconfig.json +15 -0
- package/webpack.config.js +28 -0
package/.eslintrc.json
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
{
|
|
2
|
+
"parser": "@typescript-eslint/parser",
|
|
3
|
+
"extends": [
|
|
4
|
+
"eslint:recommended",
|
|
5
|
+
"plugin:@typescript-eslint/recommended",
|
|
6
|
+
"plugin:prettier/recommended"
|
|
7
|
+
],
|
|
8
|
+
"parserOptions": {
|
|
9
|
+
"ecmaVersion": 2020,
|
|
10
|
+
"sourceType": "module"
|
|
11
|
+
},
|
|
12
|
+
"rules": {
|
|
13
|
+
"prettier/prettier": "error"
|
|
14
|
+
}
|
|
15
|
+
}
|
package/.gitattributes
ADDED
package/.prettierrc
ADDED
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# 📌 Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project will be documented in this file.
|
|
4
|
+
|
|
5
|
+
## [Unreleased] - Initial Commit
|
|
6
|
+
|
|
7
|
+
### 🚀 Added
|
|
8
|
+
|
|
9
|
+
- **Project Setup**: Initialized the project with essential configurations.
|
|
10
|
+
- **Stacked Bubble Chart**: Added initial implementation for rendering stacked bubble charts.
|
|
11
|
+
- **Project Structure**: Organized code into modules (`models`, `services`, `core`, `constants`).
|
|
12
|
+
- **Build Tools**: Configured TypeScript, Webpack, and Prettier for a streamlined development workflow.
|
|
13
|
+
- **Live Server Support**: Ensured auto-compilation and live reloading for a better development experience.
|
|
14
|
+
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
**📝 Note:** This is the first commit to the repository. Future updates will include feature enhancements, bug fixes, and optimizations. Stay tuned! 🚀
|
package/README.md
ADDED
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
# bubbleChartJs
|
|
2
|
+
|
|
3
|
+
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.
|
|
4
|
+
|
|
5
|
+
### ✨ Why Use a Stacked Bubble Chart?
|
|
6
|
+
|
|
7
|
+
Multi-Dimensional Data Representation – Visualizes multiple datasets at once.
|
|
8
|
+
|
|
9
|
+
Better Group Comparisons – Highlights relationships between different categories.
|
|
10
|
+
|
|
11
|
+
Enhanced Readability – Shows data trends with layered or clustered bubbles.
|
|
12
|
+
|
|
13
|
+
Customizable & Interactive – Allows tooltips.
|
|
14
|
+
|
|
15
|
+
### 🔧 Features
|
|
16
|
+
|
|
17
|
+
✔️ Supports stacked or grouped bubble layouts
|
|
18
|
+
|
|
19
|
+
✔️ Customizable bubble color
|
|
20
|
+
|
|
21
|
+
✔️ Fully compatible with JavaScript & Typescript
|
|
22
|
+
|
|
23
|
+
✔️ Interactive tooltips and hover effects
|
|
24
|
+
|
|
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
|
+
## Installation
|
|
34
|
+
|
|
35
|
+
You can install `bubbleChartJs` via npm:
|
|
36
|
+
|
|
37
|
+
```sh
|
|
38
|
+
npm install bubble-chart-js
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
## Usage
|
|
42
|
+
|
|
43
|
+
### Basic Example
|
|
44
|
+
|
|
45
|
+
```js
|
|
46
|
+
import BubbleChart from "bubblechartjs";
|
|
47
|
+
|
|
48
|
+
const config = {
|
|
49
|
+
canvasContainerId: "bubbleChartCanvas",
|
|
50
|
+
data: [
|
|
51
|
+
{ label: "A", value: 100 },
|
|
52
|
+
{ label: "B", value: 80 },
|
|
53
|
+
{ label: "C", value: 60 },
|
|
54
|
+
],
|
|
55
|
+
colorMap: {
|
|
56
|
+
A: "#ff5733",
|
|
57
|
+
B: "#33ff57",
|
|
58
|
+
C: "#3357ff",
|
|
59
|
+
},
|
|
60
|
+
defaultBubbleColor: "#cccccc",
|
|
61
|
+
fontSize: 14,
|
|
62
|
+
fontFamily: "Arial",
|
|
63
|
+
fontColor: "#000000",
|
|
64
|
+
minRadius: 10,
|
|
65
|
+
maxLines: 2,
|
|
66
|
+
textWrap: true,
|
|
67
|
+
isResizeCanvasOnWindowSizeChange: true,
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
initializeChart(config);
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
## Configuration Options
|
|
74
|
+
|
|
75
|
+
The `BubbleChart` class accepts a configuration object with the following properties:
|
|
76
|
+
|
|
77
|
+
## Configuration Options
|
|
78
|
+
|
|
79
|
+
The `BubbleChart` class accepts a configuration object with the following properties:
|
|
80
|
+
|
|
81
|
+
| Property | Type | Required | Optional | Description | Default |
|
|
82
|
+
| ---------------------------------- | ------------------------ | -------- | -------- | ------------------------------------------------------------------- | ----------- |
|
|
83
|
+
| `canvasContainerId` | `string` | ✔️ Yes | ❌ No | The ID of the container where the chart will be rendered. | `-` |
|
|
84
|
+
| `data` | `DataItem[]` | ✔️ Yes | ❌ No | An array of objects containing `label` and `value` for each bubble. | `-` |
|
|
85
|
+
| `colorMap` | `Record<string, string>` | ❌ No | ✔️ Yes | A mapping of labels to specific bubble colors. | `{}` |
|
|
86
|
+
| `defaultBubbleColor` | `string` | ❌ No | ✔️ Yes | Default color for bubbles if not specified in `colorMap`. | `"#3498db"` |
|
|
87
|
+
| `fontSize` | `number` | ❌ No | ✔️ Yes | Font size for bubble labels. | `14` |
|
|
88
|
+
| `fontFamily` | `string` | ❌ No | ✔️ Yes | Font family for text rendering. | `"Arial"` |
|
|
89
|
+
| `fontColor` | `string` | ❌ No | ✔️ Yes | Color of the text inside bubbles. | `"#ffffff"` |
|
|
90
|
+
| `minRadius` | `number` | ❌ No | ✔️ Yes | Minimum radius for the bubbles. | `10` |
|
|
91
|
+
| `maxLines` | `number` | ❌ No | ✔️ Yes | Maximum number of lines allowed for text wrapping. | `3` |
|
|
92
|
+
| `textWrap` | `boolean` | ❌ No | ✔️ Yes | Enables or disables text wrapping inside bubbles. | `true` |
|
|
93
|
+
| `isResizeCanvasOnWindowSizeChange` | `boolean` | ❌ No | ✔️ Yes | Whether the chart should resize when the window size changes. | `true` |
|
|
94
|
+
| `showToolTip` | `boolean` | ❌ No | ✔️ Yes | Whether the chart should display the tooltip or not. | `true` |
|
|
95
|
+
|
|
96
|
+
✔️ **Required**: These properties must be provided.
|
|
97
|
+
✔️ **Optional**: If not provided, the default value will be used.
|
|
98
|
+
|
|
99
|
+
## License
|
|
100
|
+
|
|
101
|
+
This project is licensed under the MIT License.
|
|
102
|
+
|
|
103
|
+
## Contributions
|
|
104
|
+
|
|
105
|
+
Contributions, issues, and feature requests are welcome!
|
|
106
|
+
|
|
107
|
+
---
|
|
108
|
+
|
|
109
|
+
Made with ❤️ by Pragadeeshwaran
|
|
Binary file
|
package/dist/bundle.js
ADDED
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* ATTENTION: The "eval" devtool has been used (maybe by default in mode: "development").
|
|
3
|
+
* This devtool is neither made for production nor for readable output files.
|
|
4
|
+
* It uses "eval()" calls to create a separate source file in the browser devtools.
|
|
5
|
+
* If you are trying to read the output file, select a different devtool (https://webpack.js.org/configuration/devtool/)
|
|
6
|
+
* or disable the default devtool with "devtool: false".
|
|
7
|
+
* If you are looking for production-ready output files, see mode: "production" (https://webpack.js.org/configuration/mode/).
|
|
8
|
+
*/
|
|
9
|
+
/******/ (() => { // webpackBootstrap
|
|
10
|
+
/******/ "use strict";
|
|
11
|
+
/******/ var __webpack_modules__ = ({
|
|
12
|
+
|
|
13
|
+
/***/ "./src/canvas.ts":
|
|
14
|
+
/*!***********************!*\
|
|
15
|
+
!*** ./src/canvas.ts ***!
|
|
16
|
+
\***********************/
|
|
17
|
+
/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
|
|
18
|
+
|
|
19
|
+
eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ createCanvas: () => (/* binding */ createCanvas)\n/* harmony export */ });\n/**\n * Creates and initializes the canvas inside the specified container.\n */\nfunction createCanvas(containerId) {\n const canvasContainer = document.getElementById(containerId);\n if (!canvasContainer) {\n console.error(`Canvas container with ID '${containerId}' not found.`);\n return null;\n }\n const canvas = document.createElement(\"canvas\");\n canvas.width = canvasContainer.offsetWidth;\n canvas.height = canvasContainer.offsetHeight;\n canvasContainer.appendChild(canvas);\n return canvas;\n}\n\n\n//# sourceURL=webpack://bubble-chart-js/./src/canvas.ts?");
|
|
20
|
+
|
|
21
|
+
/***/ }),
|
|
22
|
+
|
|
23
|
+
/***/ "./src/constants/physics.ts":
|
|
24
|
+
/*!**********************************!*\
|
|
25
|
+
!*** ./src/constants/physics.ts ***!
|
|
26
|
+
\**********************************/
|
|
27
|
+
/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
|
|
28
|
+
|
|
29
|
+
eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ PHYSICS: () => (/* binding */ PHYSICS)\n/* harmony export */ });\nconst PHYSICS = {\n forceStrength: 0.015,\n iterations: 1000,\n damping: 0.55,\n boundaryForce: 0.02,\n centerForce: 0.12,\n centerAttraction: 0.6,\n centerDamping: 0.3,\n centerRadiusBuffer: 35,\n};\n\n\n//# sourceURL=webpack://bubble-chart-js/./src/constants/physics.ts?");
|
|
30
|
+
|
|
31
|
+
/***/ }),
|
|
32
|
+
|
|
33
|
+
/***/ "./src/core/renderer.ts":
|
|
34
|
+
/*!******************************!*\
|
|
35
|
+
!*** ./src/core/renderer.ts ***!
|
|
36
|
+
\******************************/
|
|
37
|
+
/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
|
|
38
|
+
|
|
39
|
+
eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ renderChart: () => (/* binding */ renderChart)\n/* harmony export */ });\n/* harmony import */ var _features_textWrapper__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../features/textWrapper */ \"./src/features/textWrapper.ts\");\n/* harmony import */ var _features_tooltip__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../features/tooltip */ \"./src/features/tooltip.ts\");\n/* harmony import */ var _utils_validation__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../utils/validation */ \"./src/utils/validation.ts\");\n/* harmony import */ var _canvas__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../canvas */ \"./src/canvas.ts\");\n/* harmony import */ var _services_renderService__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ../services/renderService */ \"./src/services/renderService.ts\");\n/* harmony import */ var _utils_helper__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ../utils/helper */ \"./src/utils/helper.ts\");\n\n\n\n\n\n\nfunction renderChart(config) {\n if (!(0,_utils_validation__WEBPACK_IMPORTED_MODULE_2__.validateConfig)(config))\n return;\n // Create & setup canvas\n let canvas = (0,_canvas__WEBPACK_IMPORTED_MODULE_3__.createCanvas)(config.canvasContainerId);\n if (!canvas)\n return;\n const ctx = canvas.getContext(\"2d\");\n if (!ctx) {\n console.error(\"Invalid context\");\n return;\n }\n const sortedData = (0,_services_renderService__WEBPACK_IMPORTED_MODULE_4__.getChartData)(config, canvas, ctx);\n function draw() {\n if (!canvas || !ctx) {\n console.warn(\"canvas or ctx is not valid\");\n return;\n }\n ctx.clearRect(0, 0, canvas.width, canvas.height);\n sortedData.forEach((item) => {\n const color = config.colorMap[item.label] || config.defaultBubbleColor;\n // Ensure radius is at least minRadius\n const radius = Math.max(item.radius, config.minRadius);\n ctx.beginPath();\n ctx.arc(item.x, item.y, radius, 0, Math.PI * 2);\n ctx.fillStyle = color;\n ctx.fill();\n ctx.strokeStyle = \"black\"; // Border color\n ctx.lineWidth = 0.25; // Border thickness\n ctx.stroke(); // Apply border\n // Text styling\n ctx.fillStyle = config.fontColor;\n const fontSize = (0,_utils_helper__WEBPACK_IMPORTED_MODULE_5__.getFontSize)(radius, config.fontSize);\n ctx.font = `${fontSize}px ${config.fontFamily}`;\n ctx.textAlign = \"center\";\n ctx.textBaseline = \"middle\";\n const padding = 5; // Padding around text\n const maxWidth = radius * 1.5 - padding * 2; // Adjusted for padding\n if (config.textWrap) {\n // Calculate vertical position for lines\n const lineHeight = fontSize * 1.2;\n // Dynamically determine lines if maxLines is not set\n const lines = (0,_features_textWrapper__WEBPACK_IMPORTED_MODULE_0__.getWrappedLines)(ctx, item.label, maxWidth, config.maxLines, radius);\n const startY = item.y - ((lines.length - 1) * lineHeight) / 2;\n lines.forEach((line, index) => {\n ctx.fillText(line, item.x, startY + index * lineHeight);\n });\n }\n else {\n ctx.fillText(item.label, item.x, item.y);\n }\n });\n }\n // Robust approach that handles resizing:\n function resizeCanvas() {\n const canvasContainer = document.getElementById(config.canvasContainerId);\n if (canvasContainer && canvas) {\n canvas.width = canvasContainer.offsetWidth;\n canvas.height = canvasContainer.offsetHeight;\n draw(); // Call your drawing function\n }\n }\n if (config.isResizeCanvasOnWindowSizeChange) {\n resizeCanvas(); // Initial resize\n window.addEventListener(\"resize\", resizeCanvas); // Resize on window resize\n }\n // Initial draw\n draw();\n if (config.showToolTip) {\n const tooltip = (0,_features_tooltip__WEBPACK_IMPORTED_MODULE_1__.createTooltipElement)();\n let animationFrameId = null;\n canvas.addEventListener(\"mousemove\", (event) => {\n if (animationFrameId)\n return; // Prevent excessive calls\n animationFrameId = requestAnimationFrame(() => {\n (0,_features_tooltip__WEBPACK_IMPORTED_MODULE_1__.handleMouseMove)(event, sortedData, canvas, tooltip);\n animationFrameId = null; // Reset after execution\n });\n });\n }\n}\n\n\n//# sourceURL=webpack://bubble-chart-js/./src/core/renderer.ts?");
|
|
40
|
+
|
|
41
|
+
/***/ }),
|
|
42
|
+
|
|
43
|
+
/***/ "./src/features/textWrapper.ts":
|
|
44
|
+
/*!*************************************!*\
|
|
45
|
+
!*** ./src/features/textWrapper.ts ***!
|
|
46
|
+
\*************************************/
|
|
47
|
+
/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
|
|
48
|
+
|
|
49
|
+
eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ getWrappedLines: () => (/* binding */ getWrappedLines)\n/* harmony export */ });\nfunction getWrappedLines(ctx, text, maxLineWidth, maxAllowedLines, circleRadius, maxCharsPerWord = undefined) {\n if (!text || maxLineWidth <= 0)\n return [];\n let words = text.split(/\\s+/);\n // Set default for maxAllowedLines based on available space\n maxAllowedLines = determineMaxLines(ctx, maxAllowedLines, circleRadius, maxLineWidth);\n // Handle single-word case separately\n if (words.length === 1) {\n return [truncateTextToFit(ctx, words[0], maxLineWidth)];\n }\n if (maxCharsPerWord) {\n // For Now dont allow default word truncation\n // Set default for maxCharsPerWord if not provided\n maxCharsPerWord = determineMaxCharsPerWord(ctx, maxCharsPerWord, maxLineWidth);\n // Apply maxCharsPerWord truncation if needed\n words = words.map((word) => truncateWord(word, maxCharsPerWord));\n }\n return wrapTextIntoLines(ctx, words, maxLineWidth, maxAllowedLines);\n}\n/**\n * Determines maxAllowedLines based on available space.\n */\nfunction determineMaxLines(ctx, maxAllowedLines, circleRadius, // TODO later account circleRadius to handleLabeltext-NoOfLines\nmaxLineWidth) {\n if (maxAllowedLines && maxAllowedLines > 0)\n return maxAllowedLines;\n const fontSize = parseInt(ctx.font, 10) || 16;\n const lineHeight = fontSize * 1.2;\n return Math.floor(maxLineWidth / lineHeight) || 1; // Default: Fit within maxLineWidth\n}\n/**\n * Determines maxCharsPerWord based on maxLineWidth.\n */\nfunction determineMaxCharsPerWord(ctx, maxCharsPerWord, maxLineWidth) {\n if (maxCharsPerWord && maxCharsPerWord > 0)\n return maxCharsPerWord;\n const avgCharWidth = ctx.measureText(\"W\").width || 8; // Approximate avg char width\n return Math.floor(maxLineWidth / avgCharWidth); // Default: Fit within maxLineWidth\n}\n/**\n * Wraps text into multiple lines within maxLineWidth.\n */\nfunction wrapTextIntoLines(ctx, words, maxLineWidth, maxAllowedLines) {\n const wrappedLines = [];\n let currentLine = \"\";\n for (const word of words) {\n const testLine = currentLine ? `${currentLine} ${word}` : word;\n const testWidth = ctx.measureText(testLine).width;\n if (testWidth <= maxLineWidth) {\n currentLine = testLine;\n }\n else {\n if (currentLine)\n wrappedLines.push(currentLine);\n currentLine = word;\n if (wrappedLines.length >= maxAllowedLines - 1)\n break;\n }\n }\n if (currentLine)\n wrappedLines.push(currentLine);\n return finalizeWrappedLines(ctx, wrappedLines, maxLineWidth, maxAllowedLines);\n}\n/**\n * Ensures the final wrapped lines do not exceed maxAllowedLines.\n */\nfunction finalizeWrappedLines(ctx, wrappedLines, maxLineWidth, maxAllowedLines) {\n if (wrappedLines.length > maxAllowedLines) {\n wrappedLines.length = maxAllowedLines;\n }\n // Truncate the last line if needed\n if (wrappedLines.length === maxAllowedLines) {\n wrappedLines[maxAllowedLines - 1] = truncateTextToFit(ctx, wrappedLines[maxAllowedLines - 1], maxLineWidth);\n }\n return wrappedLines.map((line) => ctx.measureText(line).width > maxLineWidth\n ? truncateTextToFit(ctx, line, maxLineWidth)\n : line);\n}\n/**\n * Truncates text with an ellipsis if it exceeds maxLineWidth.\n */\nfunction truncateTextToFit(ctx, text, maxLineWidth) {\n text = text.trim();\n if (!text)\n return \"\"; // Handle empty or whitespace-only input\n if (ctx.measureText(text).width <= maxLineWidth)\n return text; // Return early if text fits\n let left = 0, right = text.length;\n // binary search\n while (left < right) {\n const mid = Math.ceil((left + right) / 2);\n const truncated = text.slice(0, mid) + \"…\";\n if (ctx.measureText(truncated).width > maxLineWidth) {\n right = mid - 1; // Reduce size\n }\n else {\n left = mid; // Expand size\n }\n }\n return text.slice(0, left) + \"…\";\n}\n/**\n * Truncates a word to maxCharsPerWord with an ellipsis.\n */\nfunction truncateWord(word, maxCharsPerWord) {\n return word.length > maxCharsPerWord\n ? word.slice(0, maxCharsPerWord) + \"…\"\n : word;\n}\n\n\n//# sourceURL=webpack://bubble-chart-js/./src/features/textWrapper.ts?");
|
|
50
|
+
|
|
51
|
+
/***/ }),
|
|
52
|
+
|
|
53
|
+
/***/ "./src/features/tooltip.ts":
|
|
54
|
+
/*!*********************************!*\
|
|
55
|
+
!*** ./src/features/tooltip.ts ***!
|
|
56
|
+
\*********************************/
|
|
57
|
+
/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
|
|
58
|
+
|
|
59
|
+
eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ createTooltipElement: () => (/* binding */ createTooltipElement),\n/* harmony export */ handleMouseMove: () => (/* binding */ handleMouseMove)\n/* harmony export */ });\n// let hoveredItem: DataItemInfo | null = null;\nfunction createTooltipElement() {\n const tooltip = document.createElement(\"div\");\n tooltip.id = \"tooltip\";\n tooltip.style.display = \"none\";\n document.body.appendChild(tooltip);\n return tooltip;\n}\nfunction handleMouseMove(event, data, canvas, tooltip) {\n const { mouseX, mouseY } = getMousePosition(event, canvas);\n const hoveredItem = findHoveredItem(mouseX, mouseY, data);\n if (hoveredItem) {\n updateTooltip(event, hoveredItem, canvas, tooltip);\n }\n else {\n canvas.style.cursor = \"default\";\n tooltip.style.display = \"none\";\n }\n}\n/**\n * Gets the mouse position relative to the canvas.\n */\nfunction getMousePosition(event, canvas) {\n const rect = canvas.getBoundingClientRect();\n return {\n mouseX: event.clientX - rect.left,\n mouseY: event.clientY - rect.top,\n };\n}\n/**\n * Finds the hovered item based on proximity to circles.\n */\nfunction findHoveredItem(mouseX, mouseY, data) {\n return (data.find((item) => Math.hypot(mouseX - item.x, mouseY - item.y) < item.radius) || null);\n}\n/**\n * Updates the tooltip and cursor based on the hovered item.\n */\nfunction updateTooltip(event, hovered, canvas, tooltip) {\n if ((hovered === null || hovered === void 0 ? void 0 : hovered.label) && (hovered === null || hovered === void 0 ? void 0 : hovered.value) && canvas && tooltip) {\n canvas.style.cursor = \"pointer\";\n tooltip.style.display = \"block\";\n tooltip.innerHTML = `<div>${hovered.label}<br>Value: ${hovered.value}</div>`;\n tooltip.style.left = `${event.pageX + 15}px`;\n tooltip.style.top = `${event.pageY - 30}px`;\n tooltip.style.zIndex = \"9999\";\n }\n}\n\n\n//# sourceURL=webpack://bubble-chart-js/./src/features/tooltip.ts?");
|
|
60
|
+
|
|
61
|
+
/***/ }),
|
|
62
|
+
|
|
63
|
+
/***/ "./src/main.ts":
|
|
64
|
+
/*!*********************!*\
|
|
65
|
+
!*** ./src/main.ts ***!
|
|
66
|
+
\*********************/
|
|
67
|
+
/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
|
|
68
|
+
|
|
69
|
+
eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _services_chartService__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./services/chartService */ \"./src/services/chartService.ts\");\n\n// export { initializeChart };\n// @ts-ignore (Ignore TypeScript error for missing window prop)\nwindow.initializeChart = _services_chartService__WEBPACK_IMPORTED_MODULE_0__.initializeChart;\n\n\n//# sourceURL=webpack://bubble-chart-js/./src/main.ts?");
|
|
70
|
+
|
|
71
|
+
/***/ }),
|
|
72
|
+
|
|
73
|
+
/***/ "./src/services/chartService.ts":
|
|
74
|
+
/*!**************************************!*\
|
|
75
|
+
!*** ./src/services/chartService.ts ***!
|
|
76
|
+
\**************************************/
|
|
77
|
+
/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
|
|
78
|
+
|
|
79
|
+
eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ initializeChart: () => (/* binding */ initializeChart)\n/* harmony export */ });\n/* harmony import */ var _core_renderer__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../core/renderer */ \"./src/core/renderer.ts\");\n/* harmony import */ var _utils_config__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../utils/config */ \"./src/utils/config.ts\");\n\n\n/**\n * Initializes the chart, but stops execution if no valid data is provided.\n */\nfunction initializeChart(config = {}) {\n var _a, _b;\n if (!config.data || config.data.length === 0) {\n console.warn(\"initializeChart: No valid data provided. Chart initialization aborted.\");\n return;\n }\n const safeConfig = Object.assign({ canvasContainerId: (_a = config.canvasContainerId) !== null && _a !== void 0 ? _a : \"chart-container\", data: (_b = config.data) !== null && _b !== void 0 ? _b : [] }, config);\n const finalConfig = (0,_utils_config__WEBPACK_IMPORTED_MODULE_1__.mergeConfig)(safeConfig);\n (0,_core_renderer__WEBPACK_IMPORTED_MODULE_0__.renderChart)(finalConfig);\n}\n\n\n//# sourceURL=webpack://bubble-chart-js/./src/services/chartService.ts?");
|
|
80
|
+
|
|
81
|
+
/***/ }),
|
|
82
|
+
|
|
83
|
+
/***/ "./src/services/renderService.ts":
|
|
84
|
+
/*!***************************************!*\
|
|
85
|
+
!*** ./src/services/renderService.ts ***!
|
|
86
|
+
\***************************************/
|
|
87
|
+
/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
|
|
88
|
+
|
|
89
|
+
eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ getChartData: () => (/* binding */ getChartData)\n/* harmony export */ });\n/* harmony import */ var _constants_physics__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../constants/physics */ \"./src/constants/physics.ts\");\n\nfunction getChartData(config, canvas, ctx) {\n // Add padding constant at the top\n const CANVAS_PADDING = 5; // pixels of padding around all edges\n // Calculate available space considering padding\n const maxPossibleRadius = Math.min((canvas.width - CANVAS_PADDING * 2) / 2, (canvas.height - CANVAS_PADDING * 2) / 2);\n // Calculate radii based on available space\n // const availableWidth = canvas.width - CANVAS_PADDING * 2;\n // const availableHeight = canvas.height - CANVAS_PADDING * 2;\n // const canvasMinDimension = Math.min(availableWidth, availableHeight);\n // Add this code for crisp rendering\n const devicePixelRatio = window.devicePixelRatio || 1;\n const rect = canvas.getBoundingClientRect();\n // reduce width & height\n // const rectWidth = rect.width - (rect.width / 100) * 10;\n // const rectHeight = rect.height - (rect.height / 100) * 10;\n canvas.width = rect.width * devicePixelRatio;\n canvas.height = rect.height * devicePixelRatio;\n canvas.style.width = rect.width + \"px\";\n canvas.style.height = rect.height + \"px\";\n ctx.scale(devicePixelRatio, devicePixelRatio);\n // Modify center calculations\n const centerX = rect.width / 2;\n const centerY = rect.height / 2;\n const sortedData = [...config.data]\n .sort((a, b) => b.value - a.value)\n .map((item) => (Object.assign(Object.assign({}, item), { radius: 0, x: 0, y: 0, fixed: false })));\n const maxValue = sortedData[0].value;\n // Define radius range dynamically\n // const internalMinRadius = canvasMinDimension * 0.25; // 5% of smallest dimension\n // const internalMaxRadius = canvasMinDimension * 0.65; // 15% of smallest dimension\n const internalMaxRadius = Math.min(maxPossibleRadius * 1.0, // Use 80% of maximum possible space\n 100 // Absolute maximum\n );\n const internalMinRadius = Math.max(internalMaxRadius * 0.3, // Minimum 30% of max radius\n 30 // Absolute minimum\n );\n // Value-based radius calculation with padding consideration\n sortedData.forEach((item) => {\n // Calculate radius proportional to value and canvas size\n const valueRatio = item.value / maxValue;\n item.radius =\n internalMinRadius + valueRatio * (internalMaxRadius - internalMinRadius);\n // Ensure radius respects padding\n item.radius = Math.min(item.radius, (canvas.width - CANVAS_PADDING * 2) / 2, (canvas.height - CANVAS_PADDING * 2) / 2);\n });\n // Add aspect ratio preservation in bubble positioning\n sortedData.forEach((item, index) => {\n if (index === 0) {\n item.x = centerX;\n item.y = centerY;\n item.fixed = true;\n }\n else {\n const baseDist = sortedData[0].radius + item.radius + 3;\n // Replace with deterministic positioning using golden angle\n const goldenAngle = Math.PI * (3 - Math.sqrt(5)); // ~137.5 degrees\n // Calculate position with padding constraints\n const maxX = canvas.width - CANVAS_PADDING - item.radius;\n const maxY = canvas.height - CANVAS_PADDING - item.radius;\n item.x = Math.min(maxX, Math.max(CANVAS_PADDING + item.radius, centerX + Math.cos(goldenAngle * index) * baseDist));\n item.y = Math.min(maxY, Math.max(CANVAS_PADDING + item.radius, centerY + Math.sin(goldenAngle * index) * baseDist));\n item.fixed = false;\n }\n });\n // Physics simulation\n // Adjust physics parameters for tighter packing\n // Unified physics simulation and collision resolution\n for (let i = 0; i < _constants_physics__WEBPACK_IMPORTED_MODULE_0__.PHYSICS.iterations; i++) {\n // Main physics simulation\n sortedData.forEach((current, index) => {\n // Apply special handling for center bubble\n if (index === 0) {\n // Soft center positioning with spring-like behavior\n const dx = centerX - current.x;\n const dy = centerY - current.y;\n const dist = Math.hypot(dx, dy);\n // Only apply correction if significantly off-center\n if (dist > 2) {\n current.x += dx * _constants_physics__WEBPACK_IMPORTED_MODULE_0__.PHYSICS.centerDamping;\n current.y += dy * _constants_physics__WEBPACK_IMPORTED_MODULE_0__.PHYSICS.centerDamping;\n }\n return;\n }\n let dxTotal = 0;\n let dyTotal = 0;\n // 1. Boundary constraints\n const boundaryPadding = current.radius + CANVAS_PADDING;\n if (current.x < boundaryPadding) {\n dxTotal += (boundaryPadding - current.x) * _constants_physics__WEBPACK_IMPORTED_MODULE_0__.PHYSICS.boundaryForce;\n }\n else if (current.x > canvas.width - boundaryPadding) {\n dxTotal +=\n (canvas.width - boundaryPadding - current.x) * _constants_physics__WEBPACK_IMPORTED_MODULE_0__.PHYSICS.boundaryForce;\n }\n // 2. Bubble repulsion with tight spacing\n sortedData.forEach((other) => {\n if (current === other)\n return;\n // Add additional center attraction\n const dx = centerX - current.x;\n const dy = centerY - current.y;\n const distance = Math.hypot(dx, dy);\n // Value-based attraction strength (weaker for smaller values)\n const attractionStrength = 0.02 * (current.value / maxValue);\n current.x += (dx / distance) * attractionStrength;\n current.y += (dy / distance) * attractionStrength;\n });\n // 3. Strong center attraction with value-based weighting\n const dxCenter = centerX - current.x;\n const dyCenter = centerY - current.y;\n const centerDist = Math.hypot(dxCenter, dyCenter);\n const minCenterDist = sortedData[0].radius + current.radius + _constants_physics__WEBPACK_IMPORTED_MODULE_0__.PHYSICS.centerRadiusBuffer;\n const attraction = _constants_physics__WEBPACK_IMPORTED_MODULE_0__.PHYSICS.centerForce * (1 - Math.pow(current.value / maxValue, 0.3));\n // Value-based attraction strength\n const attractionStrength = _constants_physics__WEBPACK_IMPORTED_MODULE_0__.PHYSICS.centerAttraction *\n (1 - current.value / maxValue) *\n (1 - Math.min(1, centerDist / minCenterDist));\n current.x += dxCenter * attractionStrength;\n current.y += dyCenter * attractionStrength;\n });\n // Combined collision resolution\n sortedData.forEach((current, i) => {\n sortedData.forEach((other, j) => {\n if (i >= j)\n return;\n // Special handling for center bubble collisions\n if (i === 0 || j === 0) {\n const centerBubble = i === 0 ? current : other;\n const normalBubble = i === 0 ? other : current;\n const dx = normalBubble.x - centerBubble.x;\n const dy = normalBubble.y - centerBubble.y;\n const distance = Math.hypot(dx, dy);\n const minDistance = centerBubble.radius + normalBubble.radius + 2;\n if (distance < minDistance) {\n const overlap = minDistance - distance;\n const angle = Math.atan2(dy, dx);\n // Only move the normal bubble\n normalBubble.x += Math.cos(angle) * overlap * 0.7;\n normalBubble.y += Math.sin(angle) * overlap * 0.7;\n }\n return;\n }\n const dx = current.x - other.x;\n const dy = current.y - other.y;\n const distance = Math.hypot(dx, dy);\n const minDistance = current.radius + other.radius - 5; // Allow 2px overlap\n if (distance < minDistance) {\n const overlap = (minDistance - distance) * 0.3; // Gentle correction\n const angle = Math.atan2(dy, dx);\n // Mass-weighted adjustment\n const massRatio = other.radius / (current.radius + other.radius);\n if (!current.fixed) {\n current.x += Math.cos(angle) * overlap * massRatio;\n current.y += Math.sin(angle) * overlap * massRatio;\n }\n if (!other.fixed) {\n other.x -= Math.cos(angle) * overlap * (1 - massRatio);\n other.y -= Math.sin(angle) * overlap * (1 - massRatio);\n }\n }\n });\n });\n }\n // Modify boundary clamping to include center bubble\n sortedData.forEach((bubble) => {\n const clampedX = Math.max(CANVAS_PADDING + bubble.radius, Math.min(canvas.width - CANVAS_PADDING - bubble.radius, bubble.x));\n const clampedY = Math.max(CANVAS_PADDING + bubble.radius, Math.min(canvas.height - CANVAS_PADDING - bubble.radius, bubble.y));\n // Only update position if not fixed or moved significantly\n if (!bubble.fixed ||\n Math.hypot(bubble.x - clampedX, bubble.y - clampedY) > 2) {\n bubble.x = clampedX;\n bubble.y = clampedY;\n }\n });\n return sortedData;\n}\n\n\n//# sourceURL=webpack://bubble-chart-js/./src/services/renderService.ts?");
|
|
90
|
+
|
|
91
|
+
/***/ }),
|
|
92
|
+
|
|
93
|
+
/***/ "./src/utils/config.ts":
|
|
94
|
+
/*!*****************************!*\
|
|
95
|
+
!*** ./src/utils/config.ts ***!
|
|
96
|
+
\*****************************/
|
|
97
|
+
/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
|
|
98
|
+
|
|
99
|
+
eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ DEFAULT_CONFIG: () => (/* binding */ DEFAULT_CONFIG),\n/* harmony export */ mergeConfig: () => (/* binding */ mergeConfig)\n/* harmony export */ });\n/**\n * Default configuration object.\n */\nconst DEFAULT_CONFIG = {\n colorMap: {},\n defaultBubbleColor: \"#3498db\",\n fontColor: \"#ffffff\",\n minRadius: 10,\n maxLines: 3,\n textWrap: true,\n isResizeCanvasOnWindowSizeChange: true,\n fontSize: 14,\n fontFamily: \"Arial\",\n showToolTip: true,\n};\n/**\n * Merges user config with defaults, ensuring `canvasContainerId` and `data` are required.\n */\nfunction mergeConfig(customConfig) {\n return Object.assign(Object.assign({}, DEFAULT_CONFIG), customConfig);\n}\n\n\n//# sourceURL=webpack://bubble-chart-js/./src/utils/config.ts?");
|
|
100
|
+
|
|
101
|
+
/***/ }),
|
|
102
|
+
|
|
103
|
+
/***/ "./src/utils/helper.ts":
|
|
104
|
+
/*!*****************************!*\
|
|
105
|
+
!*** ./src/utils/helper.ts ***!
|
|
106
|
+
\*****************************/
|
|
107
|
+
/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
|
|
108
|
+
|
|
109
|
+
eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ getFontSize: () => (/* binding */ getFontSize)\n/* harmony export */ });\nfunction getFontSize(radius, defaultFontSize = 14) {\n return Math.min(defaultFontSize, radius / 2);\n}\n\n\n//# sourceURL=webpack://bubble-chart-js/./src/utils/helper.ts?");
|
|
110
|
+
|
|
111
|
+
/***/ }),
|
|
112
|
+
|
|
113
|
+
/***/ "./src/utils/validation.ts":
|
|
114
|
+
/*!*********************************!*\
|
|
115
|
+
!*** ./src/utils/validation.ts ***!
|
|
116
|
+
\*********************************/
|
|
117
|
+
/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
|
|
118
|
+
|
|
119
|
+
eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ validateConfig: () => (/* binding */ validateConfig)\n/* harmony export */ });\n/**\n * Validates configuration and ensures required properties exist.\n */\nfunction validateConfig(config) {\n if (!config) {\n console.error(\"Invalid config object\");\n return false;\n }\n if (!Array.isArray(config.data) || config.data.length === 0) {\n console.error(\"Invalid or empty data array\");\n return false;\n }\n return true;\n}\n\n\n//# sourceURL=webpack://bubble-chart-js/./src/utils/validation.ts?");
|
|
120
|
+
|
|
121
|
+
/***/ })
|
|
122
|
+
|
|
123
|
+
/******/ });
|
|
124
|
+
/************************************************************************/
|
|
125
|
+
/******/ // The module cache
|
|
126
|
+
/******/ var __webpack_module_cache__ = {};
|
|
127
|
+
/******/
|
|
128
|
+
/******/ // The require function
|
|
129
|
+
/******/ function __webpack_require__(moduleId) {
|
|
130
|
+
/******/ // Check if module is in cache
|
|
131
|
+
/******/ var cachedModule = __webpack_module_cache__[moduleId];
|
|
132
|
+
/******/ if (cachedModule !== undefined) {
|
|
133
|
+
/******/ return cachedModule.exports;
|
|
134
|
+
/******/ }
|
|
135
|
+
/******/ // Create a new module (and put it into the cache)
|
|
136
|
+
/******/ var module = __webpack_module_cache__[moduleId] = {
|
|
137
|
+
/******/ // no module.id needed
|
|
138
|
+
/******/ // no module.loaded needed
|
|
139
|
+
/******/ exports: {}
|
|
140
|
+
/******/ };
|
|
141
|
+
/******/
|
|
142
|
+
/******/ // Execute the module function
|
|
143
|
+
/******/ __webpack_modules__[moduleId](module, module.exports, __webpack_require__);
|
|
144
|
+
/******/
|
|
145
|
+
/******/ // Return the exports of the module
|
|
146
|
+
/******/ return module.exports;
|
|
147
|
+
/******/ }
|
|
148
|
+
/******/
|
|
149
|
+
/************************************************************************/
|
|
150
|
+
/******/ /* webpack/runtime/define property getters */
|
|
151
|
+
/******/ (() => {
|
|
152
|
+
/******/ // define getter functions for harmony exports
|
|
153
|
+
/******/ __webpack_require__.d = (exports, definition) => {
|
|
154
|
+
/******/ for(var key in definition) {
|
|
155
|
+
/******/ if(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {
|
|
156
|
+
/******/ Object.defineProperty(exports, key, { enumerable: true, get: definition[key] });
|
|
157
|
+
/******/ }
|
|
158
|
+
/******/ }
|
|
159
|
+
/******/ };
|
|
160
|
+
/******/ })();
|
|
161
|
+
/******/
|
|
162
|
+
/******/ /* webpack/runtime/hasOwnProperty shorthand */
|
|
163
|
+
/******/ (() => {
|
|
164
|
+
/******/ __webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop))
|
|
165
|
+
/******/ })();
|
|
166
|
+
/******/
|
|
167
|
+
/******/ /* webpack/runtime/make namespace object */
|
|
168
|
+
/******/ (() => {
|
|
169
|
+
/******/ // define __esModule on exports
|
|
170
|
+
/******/ __webpack_require__.r = (exports) => {
|
|
171
|
+
/******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) {
|
|
172
|
+
/******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
|
|
173
|
+
/******/ }
|
|
174
|
+
/******/ Object.defineProperty(exports, '__esModule', { value: true });
|
|
175
|
+
/******/ };
|
|
176
|
+
/******/ })();
|
|
177
|
+
/******/
|
|
178
|
+
/************************************************************************/
|
|
179
|
+
/******/
|
|
180
|
+
/******/ // startup
|
|
181
|
+
/******/ // Load entry module and return exports
|
|
182
|
+
/******/ // This entry module can't be inlined because the eval devtool is used.
|
|
183
|
+
/******/ var __webpack_exports__ = __webpack_require__("./src/main.ts");
|
|
184
|
+
/******/
|
|
185
|
+
/******/ })()
|
|
186
|
+
;
|
package/dist/canvas.d.ts
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export declare const PHYSICS: {
|
|
2
|
+
readonly forceStrength: 0.015;
|
|
3
|
+
readonly iterations: 1000;
|
|
4
|
+
readonly damping: 0.55;
|
|
5
|
+
readonly boundaryForce: 0.02;
|
|
6
|
+
readonly centerForce: 0.12;
|
|
7
|
+
readonly centerAttraction: 0.6;
|
|
8
|
+
readonly centerDamping: 0.3;
|
|
9
|
+
readonly centerRadiusBuffer: 35;
|
|
10
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function getWrappedLines(ctx: CanvasRenderingContext2D, text: string, maxLineWidth: number, maxAllowedLines: number | undefined, circleRadius: number, maxCharsPerWord?: number | undefined): string[];
|
package/dist/main.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { DataItem } from "./dataItem";
|
|
2
|
+
export interface Configuration {
|
|
3
|
+
canvasContainerId: string;
|
|
4
|
+
data: DataItem[];
|
|
5
|
+
colorMap: Record<string, string>;
|
|
6
|
+
defaultBubbleColor: string;
|
|
7
|
+
fontSize: number;
|
|
8
|
+
fontFamily: string;
|
|
9
|
+
fontColor: string;
|
|
10
|
+
minRadius: number;
|
|
11
|
+
maxLines: number;
|
|
12
|
+
textWrap: boolean;
|
|
13
|
+
isResizeCanvasOnWindowSizeChange: boolean;
|
|
14
|
+
showToolTip: boolean;
|
|
15
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
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;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function getFontSize(radius: number, defaultFontSize?: number): number;
|
package/jest.config.js
ADDED
package/package.json
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "bubble-chart-js",
|
|
3
|
+
"version": "1.0.0",
|
|
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
|
+
"main": "dist/index.js",
|
|
6
|
+
"types": "dist/index.d.ts",
|
|
7
|
+
"scripts": {
|
|
8
|
+
"clean": "rimraf dist",
|
|
9
|
+
"build": "npm run clean && webpack --mode development",
|
|
10
|
+
"watch": "webpack --watch",
|
|
11
|
+
"start": "webpack serve --open",
|
|
12
|
+
"lint": "eslint src/**/*.ts",
|
|
13
|
+
"lint:fix": "eslint src/**/*.ts --fix",
|
|
14
|
+
"test": "jest"
|
|
15
|
+
},
|
|
16
|
+
"devDependencies": {
|
|
17
|
+
"@types/jest": "^29.5.14",
|
|
18
|
+
"@typescript-eslint/eslint-plugin": "^7.0.1",
|
|
19
|
+
"@typescript-eslint/parser": "^7.0.1",
|
|
20
|
+
"eslint": "^8.56.0",
|
|
21
|
+
"eslint-config-prettier": "^9.1.0",
|
|
22
|
+
"eslint-plugin-prettier": "^5.1.3",
|
|
23
|
+
"jest": "^29.7.0",
|
|
24
|
+
"prettier": "^3.2.4",
|
|
25
|
+
"rimraf": "^6.0.1",
|
|
26
|
+
"ts-jest": "^29.2.6",
|
|
27
|
+
"ts-loader": "^9.5.2",
|
|
28
|
+
"typescript": "^5.7.3",
|
|
29
|
+
"webpack": "^5.98.0",
|
|
30
|
+
"webpack-cli": "^6.0.1",
|
|
31
|
+
"webpack-dev-server": "^4.15.1"
|
|
32
|
+
},
|
|
33
|
+
"author": "Pragadeeshwaran Pasupathi <pragadeeshwaran.pasupathi@gmail.com>",
|
|
34
|
+
"license": "MIT"
|
|
35
|
+
}
|
package/src/canvas.ts
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Creates and initializes the canvas inside the specified container.
|
|
3
|
+
*/
|
|
4
|
+
export function createCanvas(containerId: string): HTMLCanvasElement | null {
|
|
5
|
+
const canvasContainer = document.getElementById(containerId);
|
|
6
|
+
if (!canvasContainer) {
|
|
7
|
+
console.error(`Canvas container with ID '${containerId}' not found.`);
|
|
8
|
+
return null;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
const canvas = document.createElement("canvas") as HTMLCanvasElement;
|
|
12
|
+
canvas.width = canvasContainer.offsetWidth;
|
|
13
|
+
canvas.height = canvasContainer.offsetHeight;
|
|
14
|
+
canvasContainer.appendChild(canvas);
|
|
15
|
+
|
|
16
|
+
return canvas;
|
|
17
|
+
}
|