@cehlers88/chart 1.1.1 → 1.2.1

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/dist/index.js CHANGED
@@ -36,7 +36,7 @@ eval("{!function(t,e){ true?module.exports=e():0}(this,()=>(()=>{\"use strict\";
36
36
  (module, __webpack_exports__, __webpack_require__) {
37
37
 
38
38
  "use strict";
39
- eval("{__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ \"default\": () => (__WEBPACK_DEFAULT_EXPORT__)\n/* harmony export */ });\n/* harmony import */ var _node_modules_css_loader_dist_runtime_noSourceMaps_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../../node_modules/css-loader/dist/runtime/noSourceMaps.js */ \"./node_modules/css-loader/dist/runtime/noSourceMaps.js\");\n/* harmony import */ var _node_modules_css_loader_dist_runtime_noSourceMaps_js__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_node_modules_css_loader_dist_runtime_noSourceMaps_js__WEBPACK_IMPORTED_MODULE_0__);\n/* harmony import */ var _node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../../node_modules/css-loader/dist/runtime/api.js */ \"./node_modules/css-loader/dist/runtime/api.js\");\n/* harmony import */ var _node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(_node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1__);\n// Imports\n\n\nvar ___CSS_LOADER_EXPORT___ = _node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1___default()((_node_modules_css_loader_dist_runtime_noSourceMaps_js__WEBPACK_IMPORTED_MODULE_0___default()));\n// Module\n___CSS_LOADER_EXPORT___.push([module.id, `canvas-diagram {\n background: #dddddd;\n box-sizing: border-box;\n display: inline-block;\n}`, \"\"]);\n// Exports\n/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (___CSS_LOADER_EXPORT___);\n\n\n//# sourceURL=webpack://@cehlers88/chart/./src/style/CanvasDiagram.css?./node_modules/css-loader/dist/cjs.js\n}");
39
+ eval("{__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ \"default\": () => (__WEBPACK_DEFAULT_EXPORT__)\n/* harmony export */ });\n/* harmony import */ var _node_modules_css_loader_dist_runtime_noSourceMaps_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../../node_modules/css-loader/dist/runtime/noSourceMaps.js */ \"./node_modules/css-loader/dist/runtime/noSourceMaps.js\");\n/* harmony import */ var _node_modules_css_loader_dist_runtime_noSourceMaps_js__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_node_modules_css_loader_dist_runtime_noSourceMaps_js__WEBPACK_IMPORTED_MODULE_0__);\n/* harmony import */ var _node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../../node_modules/css-loader/dist/runtime/api.js */ \"./node_modules/css-loader/dist/runtime/api.js\");\n/* harmony import */ var _node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(_node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1__);\n// Imports\n\n\nvar ___CSS_LOADER_EXPORT___ = _node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1___default()((_node_modules_css_loader_dist_runtime_noSourceMaps_js__WEBPACK_IMPORTED_MODULE_0___default()));\n// Module\n___CSS_LOADER_EXPORT___.push([module.id, `canvas-diagram {\n box-sizing: border-box;\n display: inline-block;\n}\n\ncanvas-diagram > canvas {\n display: block;\n /* the chart draws on a transparent canvas; the host page may give it a\n background, e.g. the surface colour of its theme */\n background: var(--chart-background, transparent);\n border-radius: var(--chart-border-radius, 0);\n touch-action: pan-y;\n}\n`, \"\"]);\n// Exports\n/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (___CSS_LOADER_EXPORT___);\n\n\n//# sourceURL=webpack://@cehlers88/chart/./src/style/CanvasDiagram.css?./node_modules/css-loader/dist/cjs.js\n}");
40
40
 
41
41
  /***/ },
42
42
 
@@ -146,7 +146,7 @@ eval("{__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpa
146
146
  (__unused_webpack_module, __webpack_exports__, __webpack_require__) {
147
147
 
148
148
  "use strict";
149
- eval("{__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ \"default\": () => (/* binding */ ChartEngine)\n/* harmony export */ });\n/* harmony import */ var _plugin_EnrichGroupInfosPlugin__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./plugin/EnrichGroupInfosPlugin */ \"./src/plugin/EnrichGroupInfosPlugin.ts\");\n/* harmony import */ var _Factory_DataGroupFactory__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./Factory/DataGroupFactory */ \"./src/Factory/DataGroupFactory.ts\");\n/* harmony import */ var _plugin_LegendsPlugin__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./plugin/LegendsPlugin */ \"./src/plugin/LegendsPlugin.ts\");\n/* harmony import */ var _Registry_EntityRegistry__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./Registry/EntityRegistry */ \"./src/Registry/EntityRegistry.ts\");\n/* harmony import */ var _plugin_PaginationPlugin__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ./plugin/PaginationPlugin */ \"./src/plugin/PaginationPlugin.ts\");\n/* harmony import */ var _Camera__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ./Camera */ \"./src/Camera.ts\");\n/**\n * Library ChartEngine\n * Created 26.11.22\n *\n * @author Christoph Ehlers\n */\n\n\n\n\n\n\nconst _runStack = [];\nlet _isRunning = false;\nconst _exec = () => {\n if (_isRunning)\n return;\n _isRunning = true;\n window.requestAnimationFrame(() => {\n _runStack.map(f => f());\n _isRunning = false;\n if (_runStack.length > 0)\n _exec();\n });\n};\nclass ChartEngine {\n _camera = new _Camera__WEBPACK_IMPORTED_MODULE_5__[\"default\"]();\n _canvasElement;\n _entityRegistry = new _Registry_EntityRegistry__WEBPACK_IMPORTED_MODULE_3__[\"default\"]();\n _ctx;\n _dataGroups = [];\n _plugins = [];\n _pluginProperties = {};\n constructor(canvasElement, properties = {\n plugins: []\n }) {\n this._canvasElement = canvasElement;\n this._ctx = canvasElement.getContext('2d');\n this._canvasElement.height = this._canvasElement.offsetHeight;\n this._canvasElement.width = this._canvasElement.offsetWidth;\n [\n new _plugin_EnrichGroupInfosPlugin__WEBPACK_IMPORTED_MODULE_0__[\"default\"](),\n new _plugin_PaginationPlugin__WEBPACK_IMPORTED_MODULE_4__[\"default\"](),\n new _plugin_LegendsPlugin__WEBPACK_IMPORTED_MODULE_2__[\"default\"](),\n ...properties.plugins,\n ].map(plugin => {\n this.addPlugin(plugin);\n });\n this._canvasElement.addEventListener('pointermove', (e) => {\n this._plugins.map(p => p.handleHover(e.offsetX + this._camera.getX(), e.offsetY + this._camera.getY()));\n });\n this._canvasElement.addEventListener('wheel', (e) => {\n this._plugins.map(p => p.handleScroll(e.deltaX, e.deltaY));\n });\n this._canvasElement.addEventListener('pointerout', (e) => {\n this._plugins.map(p => p.handleLeave());\n });\n }\n addPlugin(plugin) {\n for (let key in this._pluginProperties) {\n plugin.setProperty(key, this._pluginProperties[key]);\n }\n plugin\n .setCamera(this._camera)\n .setEntityRegistry(this._entityRegistry);\n this._plugins.push(plugin);\n return this;\n }\n getDrawAreaRect() {\n return {\n x: 0,\n y: 0,\n w: this._canvasElement.width,\n h: this._canvasElement.height\n };\n }\n getPlugin(pluginName) {\n return this._plugins.find((plugin) => plugin.getName() === pluginName);\n }\n getPlugins() {\n return this._plugins.sort((p1, p2) => p1.getPriority() - p2.getPriority());\n }\n setPluginProperty(propertyName, value) {\n this._pluginProperties[propertyName] = value;\n this._plugins.map(p => p.setProperty(propertyName, value));\n return this;\n }\n create() {\n this._plugins.map(p => p.create(this._canvasElement, this._dataGroups));\n return this;\n }\n addDataGroup(dataGroup) {\n this._dataGroups.push(dataGroup);\n return this;\n }\n addValue(value, groupName = '') {\n const dataGroup = this._dataGroups.find((group) => group.label === groupName);\n if (dataGroup) {\n dataGroup.data.push(value);\n }\n else {\n this._dataGroups.push(_Factory_DataGroupFactory__WEBPACK_IMPORTED_MODULE_1__[\"default\"].createDataGroup([value], groupName));\n }\n return this;\n }\n draw() {\n this._ctx.clearRect(0, 0, this._canvasElement.width, this._canvasElement.height);\n this.getPlugins().map(p => p.draw());\n return this;\n }\n update() {\n this.getPlugins().map(p => {\n this._dataGroups = p.update(this._dataGroups).dataGroups;\n });\n return this;\n }\n start() {\n this.create();\n _runStack.push(() => {\n this.update();\n this.draw();\n });\n _exec();\n }\n getEngineCallbacks() {\n return {\n updateData: (indexGroup, indexData, newData) => {\n console.log('updateData context:', this);\n this._dataGroups[indexData].data[indexData] = newData;\n return this;\n }\n };\n }\n}\n\n\n//# sourceURL=webpack://@cehlers88/chart/./src/ChartEngine.ts?\n}");
149
+ eval("{__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ \"default\": () => (/* binding */ ChartEngine)\n/* harmony export */ });\n/* harmony import */ var _plugin_EnrichGroupInfosPlugin__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./plugin/EnrichGroupInfosPlugin */ \"./src/plugin/EnrichGroupInfosPlugin.ts\");\n/* harmony import */ var _Factory_DataGroupFactory__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./Factory/DataGroupFactory */ \"./src/Factory/DataGroupFactory.ts\");\n/* harmony import */ var _plugin_LegendsPlugin__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./plugin/LegendsPlugin */ \"./src/plugin/LegendsPlugin.ts\");\n/* harmony import */ var _Registry_EntityRegistry__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./Registry/EntityRegistry */ \"./src/Registry/EntityRegistry.ts\");\n/* harmony import */ var _plugin_PaginationPlugin__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ./plugin/PaginationPlugin */ \"./src/plugin/PaginationPlugin.ts\");\n/* harmony import */ var _Camera__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ./Camera */ \"./src/Camera.ts\");\n/**\n * Library ChartEngine\n * Created 26.11.22\n *\n * @author Christoph Ehlers\n */\n\n\n\n\n\n\nconst _runStack = [];\nlet _isRunning = false;\nconst _exec = () => {\n if (_isRunning)\n return;\n _isRunning = true;\n window.requestAnimationFrame(() => {\n _runStack.map(f => f());\n _isRunning = false;\n if (_runStack.length > 0)\n _exec();\n });\n};\nclass ChartEngine {\n _camera = new _Camera__WEBPACK_IMPORTED_MODULE_5__[\"default\"]();\n _canvasElement;\n _entityRegistry = new _Registry_EntityRegistry__WEBPACK_IMPORTED_MODULE_3__[\"default\"]();\n _ctx;\n _dataGroups = [];\n _dataGroupsSignature = '';\n _processedDataGroups = [];\n _plugins = [];\n _pluginProperties = {};\n _created = false;\n _runStackEntry;\n constructor(canvasElement, properties = {\n plugins: []\n }) {\n this._canvasElement = canvasElement;\n this._ctx = canvasElement.getContext('2d');\n this._canvasElement.height = this._canvasElement.offsetHeight;\n this._canvasElement.width = this._canvasElement.offsetWidth;\n [\n new _plugin_EnrichGroupInfosPlugin__WEBPACK_IMPORTED_MODULE_0__[\"default\"](),\n new _plugin_PaginationPlugin__WEBPACK_IMPORTED_MODULE_4__[\"default\"](),\n new _plugin_LegendsPlugin__WEBPACK_IMPORTED_MODULE_2__[\"default\"](),\n ...(properties.plugins || []),\n ].map(plugin => {\n this.addPlugin(plugin);\n });\n const hover = (e) => {\n const position = this.toCanvasPosition(e.offsetX, e.offsetY);\n this._plugins.map(p => p.handleHover(position.x + this._camera.getX(), position.y + this._camera.getY()));\n };\n this._canvasElement.addEventListener('pointermove', hover);\n // touch devices have no hover, a tap shows the values instead\n this._canvasElement.addEventListener('pointerdown', hover);\n this._canvasElement.addEventListener('wheel', (e) => {\n this._plugins.map(p => p.handleScroll(e.deltaX, e.deltaY));\n });\n this._canvasElement.addEventListener('pointerout', (e) => {\n this._plugins.map(p => p.handleLeave());\n });\n }\n addPlugin(plugin) {\n for (let key in this._pluginProperties) {\n plugin.setProperty(key, this._pluginProperties[key]);\n }\n plugin\n .setCamera(this._camera)\n .setEntityRegistry(this._entityRegistry);\n this._plugins.push(plugin);\n if (this._created) {\n // The chart is already running, so the plugin has to catch up.\n plugin.create(this._canvasElement, this.getPipelineDataGroups());\n }\n return this;\n }\n /**\n * Changes the size of the canvas (in canvas pixels). The plugins rebuild\n * their buffers, so the chart is drawn sharp in the new size instead of\n * being stretched by css.\n */\n resize(width, height) {\n width = Math.max(1, Math.round(width));\n height = Math.max(1, Math.round(height));\n if (width === this._canvasElement.width && height === this._canvasElement.height) {\n return this;\n }\n this._canvasElement.width = width;\n this._canvasElement.height = height;\n if (this._created) {\n this._plugins.map(p => p.resize ? p.resize(this._canvasElement) : p.invalidate?.());\n }\n return this;\n }\n /**\n * Converts a pointer position (css pixels) into canvas pixels - both differ\n * as soon as the canvas is scaled by css, e.g. by `width: 100%`.\n */\n toCanvasPosition(offsetX, offsetY) {\n const clientWidth = this._canvasElement.clientWidth;\n const clientHeight = this._canvasElement.clientHeight;\n return {\n x: clientWidth > 0 ? offsetX * this._canvasElement.width / clientWidth : offsetX,\n y: clientHeight > 0 ? offsetY * this._canvasElement.height / clientHeight : offsetY,\n };\n }\n getDrawAreaRect() {\n return {\n x: 0,\n y: 0,\n w: this._canvasElement.width,\n h: this._canvasElement.height\n };\n }\n getPlugin(pluginName) {\n return this._plugins.find((plugin) => plugin.getName() === pluginName);\n }\n getPlugins() {\n return this._plugins.sort((p1, p2) => p1.getPriority() - p2.getPriority());\n }\n setPluginProperty(propertyName, value) {\n this._pluginProperties[propertyName] = value;\n this._plugins.map(p => p.setProperty(propertyName, value));\n return this;\n }\n create() {\n this._plugins.map(p => p.create(this._canvasElement, this.getPipelineDataGroups()));\n this._created = true;\n return this;\n }\n addDataGroup(dataGroup) {\n this._dataGroups.push(dataGroup);\n return this;\n }\n /**\n * Returns the data groups currently held by the engine.\n * The returned groups are the live objects; changes made to them are\n * picked up with the next update cycle.\n */\n getDataGroups() {\n return this._dataGroups;\n }\n /**\n * Returns a single data group, addressed either by its label or by its index.\n */\n getDataGroup(group) {\n if (typeof group === 'number') {\n return this._dataGroups[group];\n }\n return this._dataGroups.find((dataGroup) => dataGroup.label === group);\n }\n /**\n * Replaces all data groups at once.\n */\n setDataGroups(dataGroups) {\n this._dataGroups = dataGroups || [];\n return this;\n }\n /**\n * Replaces the complete data of the chart. Accepts everything\n * `DataGroupFactory.parseDataGroupsDefinition` understands: a JSON string,\n * a plain array of values or an object keyed by group name.\n */\n setData(dataDefinition) {\n return this.setDataGroups(_Factory_DataGroupFactory__WEBPACK_IMPORTED_MODULE_1__[\"default\"].parseDataGroupsDefinition(dataDefinition));\n }\n /**\n * Removes a data group, addressed either by its label or by its index.\n */\n removeDataGroup(group) {\n const index = typeof group === 'number' ? group : this._dataGroups.findIndex((dataGroup) => dataGroup.label === group);\n if (index > -1 && index < this._dataGroups.length) {\n this._dataGroups.splice(index, 1);\n }\n return this;\n }\n /**\n * Removes all data groups.\n */\n clearData() {\n this._dataGroups = [];\n return this;\n }\n /**\n * Appends a value - e.g. a new measurement that arrived after the page was\n * loaded - to a data group. The value may be given as `{value, timestamp}`\n * or `{value, label}`. A group with a `maxLength` drops its oldest values,\n * so a live chart keeps its window. Without a group name the value goes to\n * the only group of the chart; an unknown group is created.\n */\n addValue(value, groupName = '') {\n const dataGroup = groupName === '' && this._dataGroups.length === 1\n ? this._dataGroups[0]\n : this._dataGroups.find((group) => group.label === groupName);\n if (dataGroup) {\n dataGroup.data.push(_Factory_DataGroupFactory__WEBPACK_IMPORTED_MODULE_1__[\"default\"].createDataValue(value));\n if (dataGroup.maxLength && dataGroup.data.length > dataGroup.maxLength) {\n dataGroup.data.splice(0, dataGroup.data.length - dataGroup.maxLength);\n }\n }\n else {\n this._dataGroups.push(_Factory_DataGroupFactory__WEBPACK_IMPORTED_MODULE_1__[\"default\"].createDataGroup([value], groupName));\n }\n return this;\n }\n /**\n * Replaces a single value within a data group.\n */\n setValue(group, dataIndex, value) {\n const dataGroup = this.getDataGroup(group);\n if (!dataGroup) {\n throw new Error('ChartEngine.setValue: data group \"' + group + '\" not found.');\n }\n if (!Number.isSafeInteger(dataIndex) || dataIndex < 0) {\n throw new Error('ChartEngine.setValue: data index must be a non-negative integer.');\n }\n while (dataGroup.data.length <= dataIndex) {\n dataGroup.data.push(_Factory_DataGroupFactory__WEBPACK_IMPORTED_MODULE_1__[\"default\"].createDataValue(null));\n }\n dataGroup.data[dataIndex] = _Factory_DataGroupFactory__WEBPACK_IMPORTED_MODULE_1__[\"default\"].createDataValue(value);\n return this;\n }\n /**\n * Removes a single value from a data group.\n */\n removeValue(group, dataIndex) {\n const dataGroup = this.getDataGroup(group);\n if (dataGroup && dataIndex > -1 && dataIndex < dataGroup.data.length) {\n dataGroup.data.splice(dataIndex, 1);\n }\n return this;\n }\n draw() {\n this._ctx.clearRect(0, 0, this._canvasElement.width, this._canvasElement.height);\n this.getPlugins().map(p => p.draw());\n return this;\n }\n update() {\n let dataGroups = this.getPipelineDataGroups();\n this.getPlugins().map(p => {\n dataGroups = p.update(dataGroups).dataGroups;\n });\n return this;\n }\n /**\n * Returns the data groups handed to the plugins. They are a detached copy\n * of the data owned by the engine and are renewed whenever that data\n * changed. Without that separation the plugins would hold references to\n * the very objects manipulated from outside, so neither they nor the\n * engine could tell a manipulation from the state of the last frame.\n */\n getPipelineDataGroups() {\n const signature = JSON.stringify(this._dataGroups);\n if (signature !== this._dataGroupsSignature) {\n this._dataGroupsSignature = signature;\n this._processedDataGroups = JSON.parse(signature);\n }\n return this._processedDataGroups;\n }\n start() {\n if (this._runStackEntry) {\n // already running\n return;\n }\n if (!this._created) {\n this.create();\n }\n this._runStackEntry = () => {\n this.update();\n this.draw();\n };\n _runStack.push(this._runStackEntry);\n _exec();\n }\n /**\n * Stops the render loop of this engine. The data stays untouched, so the\n * chart can be started again later on.\n */\n stop() {\n if (this._runStackEntry) {\n const index = _runStack.indexOf(this._runStackEntry);\n if (index > -1) {\n _runStack.splice(index, 1);\n }\n this._runStackEntry = undefined;\n }\n return this;\n }\n isRunning() {\n return this._runStackEntry !== undefined;\n }\n getEngineCallbacks() {\n return {\n updateData: (indexGroup, indexData, newData) => {\n this._dataGroups[indexGroup].data[indexData] = newData;\n return this;\n }\n };\n }\n}\n\n\n//# sourceURL=webpack://@cehlers88/chart/./src/ChartEngine.ts?\n}");
150
150
 
151
151
  /***/ },
152
152
 
@@ -168,7 +168,7 @@ eval("{__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpa
168
168
  (__unused_webpack_module, __webpack_exports__, __webpack_require__) {
169
169
 
170
170
  "use strict";
171
- eval("{__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ \"default\": () => (/* binding */ DataGroupFactory)\n/* harmony export */ });\nclass DataGroupFactory {\n static buildCounter = 0;\n static _colors = [\n '#e84949',\n '#30cb30',\n '#f0a30a',\n '#1d91e3',\n '#ec459e',\n '#9f4ef4',\n '#f4f44e',\n '#4ef4f4',\n '#4ef49f',\n '#f49f4e',\n '#4e9bf4',\n ];\n static _lastColorIndex = -1;\n static createDataGroup(values, groupLabel = '', color = '') {\n DataGroupFactory.buildCounter++;\n if (color === '') {\n DataGroupFactory._lastColorIndex++;\n if (DataGroupFactory._lastColorIndex >= DataGroupFactory._colors.length) {\n DataGroupFactory._lastColorIndex = 0;\n }\n color = DataGroupFactory._colors[DataGroupFactory._lastColorIndex];\n }\n const dataGroup = {\n color,\n label: groupLabel === '' ? 'Group#' + DataGroupFactory.buildCounter : groupLabel,\n description: \"\",\n data: values.map((valueDef) => {\n let value = valueDef;\n let label = undefined;\n let valueStart = valueDef;\n let valueEnd = valueDef;\n if (valueDef !== null) {\n if (typeof valueDef === 'object') {\n if (Array.isArray(valueDef.value)) {\n if (valueDef.value.length === 2) {\n valueStart = valueDef.value[0];\n value = valueDef.value[1];\n valueEnd = valueDef.value[1];\n }\n else {\n console.warn('valueDef.value must be an array with 2 elements', valueDef);\n }\n }\n else {\n value = valueDef.value;\n valueEnd = valueDef.value;\n valueStart = valueDef.valueStart;\n }\n label = valueDef.label;\n }\n else {\n if (Array.isArray(valueDef) && valueDef.length === 2) {\n valueStart = valueDef[0];\n value = valueDef[1];\n valueEnd = valueDef[1];\n label = valueDef[0] + ' - ' + valueDef[1];\n }\n }\n }\n return {\n empty: (value === null),\n hasLabel: (valueDef && valueDef.label !== undefined),\n label,\n value,\n valueEnd,\n valueStart\n };\n }),\n infos: {}\n };\n return dataGroup;\n }\n static parseDataGroupsDefinition(value) {\n const valueObj = JSON.parse(value);\n const result = [];\n if (Array.isArray(valueObj)) {\n if (typeof valueObj[0] === 'number' || Array.isArray(valueObj[0])) {\n result.push(DataGroupFactory.createDataGroup(valueObj));\n }\n }\n else {\n if (typeof valueObj === 'object') {\n Object.keys(valueObj).map((key) => {\n if (Array.isArray(valueObj[key])) {\n result.push(DataGroupFactory.createDataGroup(valueObj[key], key));\n }\n else {\n result.push(DataGroupFactory.createDataGroup(valueObj[key].data, key, valueObj[key].color));\n }\n });\n }\n }\n return result;\n }\n static setColor(color, index) {\n DataGroupFactory._colors[index] = color;\n }\n static setColors(colors) {\n DataGroupFactory._colors = colors;\n }\n}\n\n\n//# sourceURL=webpack://@cehlers88/chart/./src/Factory/DataGroupFactory.ts?\n}");
171
+ eval("{__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ \"default\": () => (/* binding */ DataGroupFactory)\n/* harmony export */ });\n/* harmony import */ var _Utils_ChartStyle__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../Utils/ChartStyle */ \"./src/Utils/ChartStyle.ts\");\n\nclass DataGroupFactory {\n static buildCounter = 0;\n static _colors = [\n '#e84949',\n '#30cb30',\n '#f0a30a',\n '#1d91e3',\n '#ec459e',\n '#9f4ef4',\n '#f4f44e',\n '#4ef4f4',\n '#4ef49f',\n '#f49f4e',\n '#4e9bf4',\n ];\n static _lastColorIndex = -1;\n /**\n * Normalizes a single value definition into a TData object.\n * Accepts plain numbers, ranges ([start, end]), null (empty value) and\n * objects like {value, valueStart, label}.\n */\n static createDataValue(valueDef) {\n let value = valueDef;\n let label = undefined;\n let timestamp = undefined;\n let valueStart = valueDef;\n let valueEnd = valueDef;\n if (valueDef !== null && valueDef !== undefined) {\n if (typeof valueDef === 'object' && !Array.isArray(valueDef)) {\n if (Array.isArray(valueDef.value)) {\n if (valueDef.value.length === 2) {\n valueStart = valueDef.value[0];\n value = valueDef.value[1];\n valueEnd = valueDef.value[1];\n }\n else {\n console.warn('valueDef.value must be an array with 2 elements', valueDef);\n }\n }\n else {\n value = valueDef.value;\n valueEnd = valueDef.value;\n valueStart = valueDef.valueStart === undefined ? 0 : valueDef.valueStart;\n }\n label = valueDef.label;\n timestamp = DataGroupFactory.normalizeTimestamp(valueDef.timestamp);\n }\n else {\n if (Array.isArray(valueDef) && valueDef.length === 2) {\n valueStart = valueDef[0];\n value = valueDef[1];\n valueEnd = valueDef[1];\n label = valueDef[0] + ' - ' + valueDef[1];\n }\n }\n }\n const data = {\n empty: (value === null || value === undefined),\n hasLabel: (valueDef !== null && valueDef !== undefined && valueDef.label !== undefined),\n label,\n value,\n valueEnd,\n valueStart\n };\n if (timestamp !== undefined) {\n data.timestamp = timestamp;\n }\n return data;\n }\n /**\n * Unix timestamps in seconds or milliseconds (see `toMilliseconds`),\n * stored as milliseconds; everything else means \"no timestamp\".\n */\n static normalizeTimestamp(timestamp) {\n return (0,_Utils_ChartStyle__WEBPACK_IMPORTED_MODULE_0__.toMilliseconds)(timestamp);\n }\n static createDataGroup(values, groupLabel = '', color = '') {\n DataGroupFactory.buildCounter++;\n if (color === '') {\n color = DataGroupFactory.getNextColor();\n }\n return {\n color,\n label: groupLabel === '' ? 'Group#' + DataGroupFactory.buildCounter : groupLabel,\n description: \"\",\n data: (values || []).map((valueDef) => DataGroupFactory.createDataValue(valueDef)),\n infos: {}\n };\n }\n /**\n * Parses a data group definition. Beside the JSON notation used by the\n * `value` attribute of `<canvas-diagram>` an already parsed object/array\n * may be passed, so data can be handed over programmatically at runtime.\n */\n static parseDataGroupsDefinition(value) {\n const valueObj = typeof value === 'string' ? JSON.parse(value) : value;\n const result = [];\n if (valueObj === null || valueObj === undefined) {\n return result;\n }\n if (Array.isArray(valueObj)) {\n if (valueObj.length > 0) {\n result.push(DataGroupFactory.createDataGroup(valueObj));\n }\n }\n else {\n if (typeof valueObj === 'object') {\n Object.keys(valueObj).map((key) => {\n if (Array.isArray(valueObj[key])) {\n result.push(DataGroupFactory.createDataGroup(valueObj[key], key));\n }\n else {\n const definition = valueObj[key] || {};\n const dataGroup = DataGroupFactory.createDataGroup(definition.data, key, definition.color || '');\n result.push(DataGroupFactory.applyGroupOptions(dataGroup, definition));\n }\n });\n }\n }\n return result;\n }\n /**\n * Applies the optional parts of an object definition to a data group:\n * `unit` (shown in tooltips and axis labels), `labels` (one label per value),\n * `timestamps` (one unix timestamp per value, in seconds or milliseconds -\n * shown as time labels) and `maxLength` (values added later push the oldest\n * ones out, so a live chart keeps its time window). Labels and timestamps of\n * single values given as `{value, label, timestamp}` win.\n */\n static applyGroupOptions(dataGroup, definition) {\n if (typeof definition.unit === 'string' && definition.unit !== '') {\n dataGroup.unit = definition.unit;\n }\n const maxLength = Number(definition.maxLength);\n if (Number.isSafeInteger(maxLength) && maxLength > 0) {\n dataGroup.maxLength = maxLength;\n }\n dataGroup.data.map((data, index) => {\n const label = Array.isArray(definition.labels) ? definition.labels[index] : undefined;\n if (!data.hasLabel && label !== undefined && label !== null && label !== '') {\n data.label = String(label);\n data.hasLabel = true;\n }\n const timestamp = Array.isArray(definition.timestamps)\n ? DataGroupFactory.normalizeTimestamp(definition.timestamps[index])\n : undefined;\n if (data.timestamp === undefined && timestamp !== undefined) {\n data.timestamp = timestamp;\n }\n });\n return dataGroup;\n }\n static getNextColor() {\n DataGroupFactory._lastColorIndex++;\n if (DataGroupFactory._lastColorIndex >= DataGroupFactory._colors.length) {\n DataGroupFactory._lastColorIndex = 0;\n }\n return DataGroupFactory._colors[DataGroupFactory._lastColorIndex];\n }\n static setColor(color, index) {\n DataGroupFactory._colors[index] = color;\n }\n static setColors(colors) {\n DataGroupFactory._colors = colors;\n }\n}\n\n\n//# sourceURL=webpack://@cehlers88/chart/./src/Factory/DataGroupFactory.ts?\n}");
172
172
 
173
173
  /***/ },
174
174
 
@@ -179,7 +179,7 @@ eval("{__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpa
179
179
  (__unused_webpack_module, __webpack_exports__, __webpack_require__) {
180
180
 
181
181
  "use strict";
182
- eval("{__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ \"default\": () => (__WEBPACK_DEFAULT_EXPORT__),\n/* harmony export */ diagramRegistry: () => (/* binding */ diagramRegistry)\n/* harmony export */ });\nconst _diagrams = {};\nclass DiagramRegistry {\n static _identPrefix = 'dia_';\n createUniqueIdentifier() {\n return 'unq' + Date.now().toString();\n }\n getDiagram(identifier) {\n const diagram = _diagrams[DiagramRegistry._identPrefix + identifier];\n if (diagram === undefined) {\n throw new Error('DiagramRegistry.getDiagram: diagram with identifier ' + identifier + ' not found.');\n }\n return diagram;\n }\n register(diagram, identifier = '') {\n if (identifier === '') {\n identifier = diagram.hasAttribute('id') ? diagram.getAttribute('id') : '';\n }\n if (identifier === '') {\n identifier = this.createUniqueIdentifier();\n }\n _diagrams[DiagramRegistry._identPrefix + identifier] = diagram;\n return this;\n }\n}\n/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (DiagramRegistry);\nconst diagramRegistry = new DiagramRegistry();\n\n\n//# sourceURL=webpack://@cehlers88/chart/./src/Registry/DiagramRegistry.ts?\n}");
182
+ eval("{__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ \"default\": () => (__WEBPACK_DEFAULT_EXPORT__),\n/* harmony export */ diagramRegistry: () => (/* binding */ diagramRegistry)\n/* harmony export */ });\nconst _diagrams = {};\nclass DiagramRegistry {\n static _identPrefix = 'dia_';\n static _identCounter = 0;\n createUniqueIdentifier() {\n DiagramRegistry._identCounter++;\n return 'unq' + Date.now().toString() + '_' + DiagramRegistry._identCounter;\n }\n getDiagram(identifier) {\n const diagram = _diagrams[DiagramRegistry._identPrefix + identifier];\n if (diagram === undefined) {\n throw new Error('DiagramRegistry.getDiagram: diagram with identifier ' + identifier + ' not found.');\n }\n return diagram;\n }\n hasDiagram(identifier) {\n return _diagrams[DiagramRegistry._identPrefix + identifier] !== undefined;\n }\n getDiagrams() {\n return Object.keys(_diagrams).map(ident => _diagrams[ident]);\n }\n register(diagram, identifier = '') {\n if (identifier === '') {\n identifier = diagram.hasAttribute('id') ? diagram.getAttribute('id') : '';\n }\n if (identifier === '') {\n identifier = this.createUniqueIdentifier();\n }\n _diagrams[DiagramRegistry._identPrefix + identifier] = diagram;\n return this;\n }\n /**\n * Removes a diagram from the registry, either by its identifier or by the\n * element itself, so removed diagrams are not kept alive by the registry.\n */\n unregister(diagram) {\n if (typeof diagram === 'string') {\n delete _diagrams[DiagramRegistry._identPrefix + diagram];\n return this;\n }\n Object.keys(_diagrams).map(ident => {\n if (_diagrams[ident] === diagram) {\n delete _diagrams[ident];\n }\n });\n return this;\n }\n}\n/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (DiagramRegistry);\nconst diagramRegistry = new DiagramRegistry();\n\n\n//# sourceURL=webpack://@cehlers88/chart/./src/Registry/DiagramRegistry.ts?\n}");
183
183
 
184
184
  /***/ },
185
185
 
@@ -194,6 +194,17 @@ eval("{__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpa
194
194
 
195
195
  /***/ },
196
196
 
197
+ /***/ "./src/Utils/ChartStyle.ts"
198
+ /*!*********************************!*\
199
+ !*** ./src/Utils/ChartStyle.ts ***!
200
+ \*********************************/
201
+ (__unused_webpack_module, __webpack_exports__, __webpack_require__) {
202
+
203
+ "use strict";
204
+ eval("{__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ DEFAULT_THEME: () => (/* binding */ DEFAULT_THEME),\n/* harmony export */ decimalsOf: () => (/* binding */ decimalsOf),\n/* harmony export */ formatTimestamps: () => (/* binding */ formatTimestamps),\n/* harmony export */ formatValue: () => (/* binding */ formatValue),\n/* harmony export */ niceTicks: () => (/* binding */ niceTicks),\n/* harmony export */ pageLocale: () => (/* binding */ pageLocale),\n/* harmony export */ readTheme: () => (/* binding */ readTheme),\n/* harmony export */ toMilliseconds: () => (/* binding */ toMilliseconds),\n/* harmony export */ withAlpha: () => (/* binding */ withAlpha)\n/* harmony export */ });\n/**\n * Library ChartStyle\n * Created 29.09.26\n *\n * Shared helpers for the look of the charts: theme colours, colour alpha,\n * number formatting and \"nice\" axis ticks.\n *\n * @author Christoph Ehlers\n */\n/**\n * The defaults are mid-tone colours, so a chart stays readable on a dark as\n * well as on a light background when the host page defines nothing.\n */\nconst DEFAULT_THEME = {\n textColor: '#9a9a9a',\n gridColor: 'rgba(128, 128, 128, 0.18)',\n tooltipBackground: 'rgba(18, 18, 18, 0.92)',\n tooltipBorder: 'rgba(255, 255, 255, 0.12)',\n tooltipTextColor: '#f0f0f0',\n font: '11px system-ui, -apple-system, \"Segoe UI\", Roboto, sans-serif',\n};\nconst THEME_CSS_PROPERTIES = {\n textColor: '--chart-text-color',\n gridColor: '--chart-grid-color',\n tooltipBackground: '--chart-tooltip-background',\n tooltipBorder: '--chart-tooltip-border',\n tooltipTextColor: '--chart-tooltip-text-color',\n font: '--chart-font',\n};\n/**\n * Reads the theme from the CSS custom properties `--chart-*` of the given\n * element, so the host page can adapt the charts to its own theme without\n * touching any plugin options.\n */\nfunction readTheme(element) {\n const theme = { ...DEFAULT_THEME };\n if (!element || typeof window === 'undefined' || typeof window.getComputedStyle !== 'function') {\n return theme;\n }\n let style;\n try {\n style = window.getComputedStyle(element);\n }\n catch (e) {\n // not a real dom element (e.g. an offscreen canvas)\n return theme;\n }\n Object.keys(THEME_CSS_PROPERTIES).map((key) => {\n const value = (style.getPropertyValue(THEME_CSS_PROPERTIES[key]) || '').trim();\n if (value !== '') {\n theme[key] = value;\n }\n });\n return theme;\n}\n/**\n * Returns the given colour with the given opacity. Hex colours (#rgb, #rrggbb,\n * #rrggbbaa) and rgb()/rgba() are supported; any other notation is returned\n * unchanged, which only costs the transparency.\n */\nfunction withAlpha(color, alpha) {\n const value = (color || '').trim();\n const a = Math.max(0, Math.min(1, alpha));\n let hex = value.startsWith('#') ? value.substring(1) : '';\n if (hex.length === 3 || hex.length === 4) {\n hex = hex.split('').map((c) => c + c).join('');\n }\n if (/^[0-9a-f]{6}([0-9a-f]{2})?$/i.test(hex)) {\n const r = parseInt(hex.substring(0, 2), 16);\n const g = parseInt(hex.substring(2, 4), 16);\n const b = parseInt(hex.substring(4, 6), 16);\n const baseAlpha = hex.length === 8 ? parseInt(hex.substring(6, 8), 16) / 255 : 1;\n return 'rgba(' + r + ', ' + g + ', ' + b + ', ' + round(baseAlpha * a, 3) + ')';\n }\n const rgb = value.match(/^rgba?\\(\\s*([\\d.]+)[\\s,]+([\\d.]+)[\\s,]+([\\d.]+)(?:[\\s,/]+([\\d.]+%?))?\\s*\\)$/i);\n if (rgb) {\n let baseAlpha = rgb[4] === undefined ? 1 : parseFloat(rgb[4]);\n if (rgb[4] !== undefined && rgb[4].endsWith('%')) {\n baseAlpha = baseAlpha / 100;\n }\n return 'rgba(' + rgb[1] + ', ' + rgb[2] + ', ' + rgb[3] + ', ' + round(baseAlpha * a, 3) + ')';\n }\n return value;\n}\n/**\n * The language of the page (`<html lang>`), so numbers and times are written\n * the way the rest of the page writes them.\n */\nfunction pageLocale() {\n if (typeof document === 'undefined' || !document.documentElement) {\n return undefined;\n }\n return document.documentElement.lang || undefined;\n}\nfunction round(value, decimals) {\n const factor = Math.pow(10, decimals);\n return Math.round(value * factor) / factor;\n}\n/**\n * Formats a value for axis labels and tooltips: at most `maxDecimals`\n * decimals, grouped by the locale of the page.\n */\nfunction formatValue(value, maxDecimals = 2, locale = pageLocale()) {\n if (value === null || value === undefined || value === '') {\n return '–';\n }\n if (typeof value !== 'number' || !isFinite(value)) {\n return String(value);\n }\n try {\n return value.toLocaleString(locale, { maximumFractionDigits: maxDecimals });\n }\n catch (e) {\n return String(round(value, maxDecimals));\n }\n}\n/**\n * Calculates evenly spaced, human friendly axis ticks (1, 2, 2.5, 5 × 10^n)\n * which enclose the given range.\n */\nfunction niceTicks(min, max, maxTicks = 5) {\n if (!isFinite(min) || !isFinite(max)) {\n return { min: 0, max: 1, step: 1, ticks: [0, 1] };\n }\n if (min > max) {\n [min, max] = [max, min];\n }\n if (min === max) {\n // A flat line still needs a visible range around it.\n const spread = min === 0 ? 1 : Math.abs(min) * 0.1;\n min -= spread;\n max += spread;\n }\n const roughStep = (max - min) / Math.max(1, maxTicks - 1);\n const magnitude = Math.pow(10, Math.floor(Math.log10(roughStep)));\n const step = [1, 2, 2.5, 5, 10]\n .map((factor) => factor * magnitude)\n .find((candidate) => candidate >= roughStep) ?? 10 * magnitude;\n const niceMin = Math.floor(min / step) * step;\n const niceMax = Math.ceil(max / step) * step;\n const ticks = [];\n for (let tick = niceMin; tick <= niceMax + step / 2; tick += step) {\n // removes floating point noise like 0.30000000000000004\n ticks.push(parseFloat(tick.toPrecision(12)));\n }\n return { min: niceMin, max: niceMax, step, ticks };\n}\n/**\n * Returns the number of decimals needed to show the given step exactly.\n */\nfunction decimalsOf(step) {\n if (!isFinite(step) || step <= 0) {\n return 0;\n }\n const text = parseFloat(step.toPrecision(12)).toString();\n const exponent = text.match(/e-(\\d+)$/);\n if (exponent) {\n return parseInt(exponent[1], 10);\n }\n const dot = text.indexOf('.');\n return dot === -1 ? 0 : text.length - dot - 1;\n}\n/**\n * Values below this are unix timestamps in seconds, values above in\n * milliseconds: 1e11 seconds is the year 5138, 1e11 milliseconds is March\n * 1973 - so seconds up to 5138 and milliseconds from 1973 on are told apart\n * reliably.\n */\nconst SECONDS_MILLISECONDS_BOUNDARY = 1e11;\n/**\n * Normalizes a unix timestamp in seconds or milliseconds (see\n * SECONDS_MILLISECONDS_BOUNDARY) to milliseconds. 0, negative values, null\n * and anything unparsable mean \"no timestamp\".\n */\nfunction toMilliseconds(timestamp) {\n const value = typeof timestamp === 'string' ? parseFloat(timestamp) : timestamp;\n if (typeof value !== 'number' || !isFinite(value) || value <= 0) {\n return undefined;\n }\n return value < SECONDS_MILLISECONDS_BOUNDARY ? value * 1000 : value;\n}\n/**\n * Turns unix timestamps (seconds or milliseconds) into short labels. The date\n * is only part of the label when the timestamps span more than one day.\n */\nfunction formatTimestamps(timestamps, locale = pageLocale()) {\n const dates = timestamps.map((timestamp) => {\n const milliseconds = toMilliseconds(timestamp);\n return milliseconds === undefined ? undefined : new Date(milliseconds);\n });\n const days = new Set(dates.filter((date) => date !== undefined).map((date) => date.toDateString()));\n const options = days.size > 1\n ? { day: '2-digit', month: '2-digit', hour: '2-digit', minute: '2-digit' }\n : { hour: '2-digit', minute: '2-digit' };\n return dates.map((date) => {\n if (date === undefined) {\n return undefined;\n }\n try {\n return date.toLocaleString(locale, options);\n }\n catch (e) {\n return date.toISOString();\n }\n });\n}\n\n\n//# sourceURL=webpack://@cehlers88/chart/./src/Utils/ChartStyle.ts?\n}");
205
+
206
+ /***/ },
207
+
197
208
  /***/ "./src/customElement/CanvasDiagram.ts"
198
209
  /*!********************************************!*\
199
210
  !*** ./src/customElement/CanvasDiagram.ts ***!
@@ -201,7 +212,7 @@ eval("{__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpa
201
212
  (__unused_webpack_module, __webpack_exports__, __webpack_require__) {
202
213
 
203
214
  "use strict";
204
- eval("{__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ \"default\": () => (__WEBPACK_DEFAULT_EXPORT__)\n/* harmony export */ });\n/* harmony import */ var _style_CanvasDiagram_css__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../style/CanvasDiagram.css */ \"./src/style/CanvasDiagram.css\");\n/* harmony import */ var _Registry_DiagramRegistry__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../Registry/DiagramRegistry */ \"./src/Registry/DiagramRegistry.ts\");\n/* harmony import */ var _cehlers88_ceutils__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! @cehlers88/ceutils */ \"./node_modules/@cehlers88/ceutils/dist/index.js\");\n/* harmony import */ var _cehlers88_ceutils__WEBPACK_IMPORTED_MODULE_2___default = /*#__PURE__*/__webpack_require__.n(_cehlers88_ceutils__WEBPACK_IMPORTED_MODULE_2__);\n/* harmony import */ var _Factory_DataGroupFactory__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../Factory/DataGroupFactory */ \"./src/Factory/DataGroupFactory.ts\");\n/* harmony import */ var _ChartEngine__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ../ChartEngine */ \"./src/ChartEngine.ts\");\n/**\n * Element CanvasDiagram\n * Created 03.10.24\n *\n * @author Christoph Ehlers\n */\n\n\n\n\n\nclass CanvasDiagram extends HTMLElement {\n _cleanDom = true;\n _engine;\n connectedCallback() {\n _Registry_DiagramRegistry__WEBPACK_IMPORTED_MODULE_1__.diagramRegistry.register(this);\n const canvas = this.createCanvas();\n this.appendChild(canvas);\n this._engine = new _ChartEngine__WEBPACK_IMPORTED_MODULE_4__[\"default\"](canvas);\n this.evalArguments();\n }\n addPlugin(plugin) {\n this._engine.addPlugin(plugin);\n return this;\n }\n getPlugin(pluginName) {\n return this._engine.getPlugin(pluginName);\n }\n start() {\n this._engine.start();\n return this;\n }\n createCanvas() {\n const canvas = _cehlers88_ceutils__WEBPACK_IMPORTED_MODULE_2__.domLib.createElement('canvas');\n this.setAttribute('style', 'width:0px; height:0px;');\n [\n { name: 'height', defaultValue: 150 },\n { name: 'width', defaultValue: 300 },\n ].map((attrDef) => {\n let value = (this.hasAttribute(attrDef.name) ? this.getAttribute(attrDef.name) : attrDef.defaultValue.toString());\n value = value.replace('px', '');\n canvas.setAttribute(attrDef.name, value);\n this.style[attrDef.name] = value + 'px';\n });\n console.log('createCanvas', this.getAttribute('style'), ' ->', canvas);\n return canvas;\n }\n evalArguments() {\n [\n {\n name: 'value', defaultValue: '[]', callback: (value) => {\n _Factory_DataGroupFactory__WEBPACK_IMPORTED_MODULE_3__[\"default\"].parseDataGroupsDefinition(value).map(dataGroupDef => {\n console.log(dataGroupDef, 'per argument');\n this._engine.addDataGroup(_Factory_DataGroupFactory__WEBPACK_IMPORTED_MODULE_3__[\"default\"].createDataGroup(dataGroupDef.data, dataGroupDef.label, dataGroupDef.color));\n });\n }\n },\n {\n name: 'autostart', defaultValue: 'true', callback: (value) => {\n if (value === 'true') {\n this.start();\n }\n }\n }\n ].map(attrDef => {\n const callbackArgument = this.hasAttribute(attrDef.name) ? this.getAttribute(attrDef.name) : attrDef.defaultValue;\n attrDef.callback(callbackArgument);\n if (this._cleanDom) {\n this.removeAttribute(attrDef.name);\n }\n });\n }\n}\n/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (CanvasDiagram);\ncustomElements.define('canvas-diagram', CanvasDiagram);\n\n\n//# sourceURL=webpack://@cehlers88/chart/./src/customElement/CanvasDiagram.ts?\n}");
215
+ eval("{__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ \"default\": () => (__WEBPACK_DEFAULT_EXPORT__)\n/* harmony export */ });\n/* harmony import */ var _style_CanvasDiagram_css__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../style/CanvasDiagram.css */ \"./src/style/CanvasDiagram.css\");\n/* harmony import */ var _Registry_DiagramRegistry__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../Registry/DiagramRegistry */ \"./src/Registry/DiagramRegistry.ts\");\n/* harmony import */ var _cehlers88_ceutils__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! @cehlers88/ceutils */ \"./node_modules/@cehlers88/ceutils/dist/index.js\");\n/* harmony import */ var _cehlers88_ceutils__WEBPACK_IMPORTED_MODULE_2___default = /*#__PURE__*/__webpack_require__.n(_cehlers88_ceutils__WEBPACK_IMPORTED_MODULE_2__);\n/* harmony import */ var _Factory_DataGroupFactory__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../Factory/DataGroupFactory */ \"./src/Factory/DataGroupFactory.ts\");\n/* harmony import */ var _ChartEngine__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ../ChartEngine */ \"./src/ChartEngine.ts\");\n/**\n * Element CanvasDiagram\n * Created 03.10.24\n *\n * @author Christoph Ehlers\n */\n\n\n\n\n\nclass CanvasDiagram extends HTMLElement {\n _cleanDom = true;\n _engine;\n _ignoreValueAttribute = false;\n _pendingActions = [];\n _resizeObserver;\n _responsive = false;\n _runningBeforeDisconnect = false;\n static get observedAttributes() {\n return ['value'];\n }\n connectedCallback() {\n if (this._engine) {\n // The element was moved within the dom or inserted again: no need to\n // build it again, but it has to pick up what disconnecting ended.\n _Registry_DiagramRegistry__WEBPACK_IMPORTED_MODULE_1__.diagramRegistry.register(this);\n this.observeSize();\n if (this._runningBeforeDisconnect) {\n this._engine.start();\n }\n return;\n }\n _Registry_DiagramRegistry__WEBPACK_IMPORTED_MODULE_1__.diagramRegistry.register(this);\n const canvas = this.createCanvas();\n this.appendChild(canvas);\n this._engine = new _ChartEngine__WEBPACK_IMPORTED_MODULE_4__[\"default\"](canvas);\n this.evalArguments();\n this.flushPendingActions();\n this.observeSize();\n }\n disconnectedCallback() {\n if (this._engine) {\n this._runningBeforeDisconnect = this._engine.isRunning();\n this._engine.stop();\n }\n if (this._resizeObserver) {\n this._resizeObserver.disconnect();\n this._resizeObserver = undefined;\n }\n _Registry_DiagramRegistry__WEBPACK_IMPORTED_MODULE_1__.diagramRegistry.unregister(this);\n }\n attributeChangedCallback(name, oldValue, newValue) {\n if (name !== 'value' || this._ignoreValueAttribute || newValue === null) {\n return;\n }\n if (!this._engine) {\n // Attribute is evaluated within connectedCallback anyway.\n return;\n }\n this.setData(newValue);\n }\n addPlugin(plugin) {\n return this.withEngine(engine => engine.addPlugin(plugin));\n }\n getPlugin(pluginName) {\n return this._engine ? this._engine.getPlugin(pluginName) : undefined;\n }\n /**\n * Changes the size of the diagram (in pixels) and redraws it sharply in\n * the new size.\n */\n resize(width, height) {\n this.style.height = Math.round(height) + 'px';\n if (!this._responsive) {\n this.style.width = Math.round(width) + 'px';\n }\n return this.withEngine(engine => engine.resize(width, height));\n }\n getEngine() {\n return this._engine;\n }\n addDataGroup(dataGroup) {\n return this.withEngine(engine => engine.addDataGroup(dataGroup));\n }\n addValue(value, groupName = '') {\n return this.withEngine(engine => engine.addValue(value, groupName));\n }\n clearData() {\n return this.withEngine(engine => engine.clearData());\n }\n getDataGroup(group) {\n return this._engine ? this._engine.getDataGroup(group) : undefined;\n }\n getDataGroups() {\n return this._engine ? this._engine.getDataGroups() : [];\n }\n removeDataGroup(group) {\n return this.withEngine(engine => engine.removeDataGroup(group));\n }\n removeValue(group, dataIndex) {\n return this.withEngine(engine => engine.removeValue(group, dataIndex));\n }\n /**\n * Replaces the complete data of the diagram, at any time - also long after\n * the page was loaded.\n */\n setData(dataDefinition) {\n return this.withEngine(engine => engine.setData(dataDefinition));\n }\n setDataGroups(dataGroups) {\n return this.withEngine(engine => engine.setDataGroups(dataGroups));\n }\n setValue(group, dataIndex, value) {\n return this.withEngine(engine => engine.setValue(group, dataIndex, value));\n }\n start() {\n return this.withEngine(engine => engine.start());\n }\n stop() {\n return this.withEngine(engine => engine.stop());\n }\n /**\n * Runs the given action against the engine. If the element is not mounted\n * yet, the action is queued and executed as soon as the engine exists,\n * so data can be set before as well as after the element was connected.\n */\n withEngine(action) {\n if (this._engine) {\n action(this._engine);\n }\n else {\n this._pendingActions.push(action);\n }\n return this;\n }\n flushPendingActions() {\n const actions = this._pendingActions;\n this._pendingActions = [];\n actions.map(action => action(this._engine));\n }\n /**\n * A width given in percent (`width=\"100%\"`) or as `auto` makes the diagram\n * responsive: it takes the width of its container and is redrawn in the\n * new size whenever that changes.\n */\n createCanvas() {\n const canvas = _cehlers88_ceutils__WEBPACK_IMPORTED_MODULE_2__.domLib.createElement('canvas');\n const widthAttribute = (this.getAttribute('width') || '').trim();\n this._responsive = widthAttribute === 'auto' || widthAttribute.endsWith('%');\n this.setAttribute('style', 'width:0px; height:0px;');\n [\n { name: 'height', defaultValue: 150 },\n { name: 'width', defaultValue: 300 },\n ].map((attrDef) => {\n let value = (this.hasAttribute(attrDef.name) ? this.getAttribute(attrDef.name) : attrDef.defaultValue.toString());\n value = value.replace('px', '');\n if (attrDef.name === 'width' && this._responsive) {\n this.style.width = widthAttribute === 'auto' ? '100%' : widthAttribute;\n this.style.display = 'block';\n canvas.style.width = '100%';\n // the real width is known once the element was laid out\n canvas.setAttribute('width', Math.floor(this.clientWidth || 300).toString());\n return;\n }\n canvas.setAttribute(attrDef.name, value);\n this.style[attrDef.name] = value + 'px';\n });\n return canvas;\n }\n observeSize() {\n if (!this._responsive || this._resizeObserver || typeof ResizeObserver === 'undefined') {\n return;\n }\n this._resizeObserver = new ResizeObserver(() => {\n const width = Math.floor(this.clientWidth);\n const canvas = this._engine?.getDrawAreaRect();\n if (!this._engine || width < 1 || !canvas) {\n return;\n }\n this._engine.resize(width, canvas.h);\n });\n this._resizeObserver.observe(this);\n }\n evalArguments() {\n [\n {\n name: 'value', defaultValue: '[]', callback: (value) => {\n _Factory_DataGroupFactory__WEBPACK_IMPORTED_MODULE_3__[\"default\"].parseDataGroupsDefinition(value).map(dataGroupDef => {\n this._engine.addDataGroup(dataGroupDef);\n });\n }\n },\n {\n name: 'autostart', defaultValue: 'true', callback: (value) => {\n if (value === 'true') {\n this.start();\n }\n }\n }\n ].map(attrDef => {\n const callbackArgument = this.hasAttribute(attrDef.name) ? this.getAttribute(attrDef.name) : attrDef.defaultValue;\n attrDef.callback(callbackArgument);\n if (this._cleanDom) {\n // Removing the attribute must not be mistaken for a data change.\n this._ignoreValueAttribute = true;\n this.removeAttribute(attrDef.name);\n this._ignoreValueAttribute = false;\n }\n });\n }\n}\n/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (CanvasDiagram);\ncustomElements.define('canvas-diagram', CanvasDiagram);\n\n\n//# sourceURL=webpack://@cehlers88/chart/./src/customElement/CanvasDiagram.ts?\n}");
205
216
 
206
217
  /***/ },
207
218
 
@@ -212,7 +223,7 @@ eval("{__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpa
212
223
  (__unused_webpack_module, __webpack_exports__, __webpack_require__) {
213
224
 
214
225
  "use strict";
215
- eval("{__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ CanvasDiagram: () => (/* binding */ CanvasDiagram),\n/* harmony export */ ChartEngine: () => (/* binding */ ChartEngine),\n/* harmony export */ DataGroupFactory: () => (/* binding */ DataGroupFactory),\n/* harmony export */ \"default\": () => (__WEBPACK_DEFAULT_EXPORT__),\n/* harmony export */ diagramRegistry: () => (/* binding */ diagramRegistry),\n/* harmony export */ plugins: () => (/* binding */ plugins)\n/* harmony export */ });\n/* harmony import */ var _customElement_CanvasDiagram__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./customElement/CanvasDiagram */ \"./src/customElement/CanvasDiagram.ts\");\n/* harmony import */ var _plugin_BarsPlugin__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./plugin/BarsPlugin */ \"./src/plugin/BarsPlugin.ts\");\n/* harmony import */ var _ChartEngine__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./ChartEngine */ \"./src/ChartEngine.ts\");\n/* harmony import */ var _Factory_DataGroupFactory__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./Factory/DataGroupFactory */ \"./src/Factory/DataGroupFactory.ts\");\n/* harmony import */ var _plugin_DiagramLine__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ./plugin/DiagramLine */ \"./src/plugin/DiagramLine.ts\");\n/* harmony import */ var _Registry_DiagramRegistry__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ./Registry/DiagramRegistry */ \"./src/Registry/DiagramRegistry.ts\");\n/* harmony import */ var _plugin_CameraMove__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ./plugin/CameraMove */ \"./src/plugin/CameraMove.ts\");\n/* harmony import */ var _plugin_EnrichGroupInfosPlugin__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! ./plugin/EnrichGroupInfosPlugin */ \"./src/plugin/EnrichGroupInfosPlugin.ts\");\n/* harmony import */ var _plugin_TimelinePlugin__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(/*! ./plugin/TimelinePlugin */ \"./src/plugin/TimelinePlugin.ts\");\n/* harmony import */ var _plugin_LegendsPlugin__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(/*! ./plugin/LegendsPlugin */ \"./src/plugin/LegendsPlugin.ts\");\n/* harmony import */ var _plugin_PaginationPlugin__WEBPACK_IMPORTED_MODULE_10__ = __webpack_require__(/*! ./plugin/PaginationPlugin */ \"./src/plugin/PaginationPlugin.ts\");\n/**\n * index.ts\n * Created 05.05.24\n *\n * @author Christoph Ehlers\n */\n\n\n\n\n\n\n\n\n\n\n\nconst CanvasDiagram = _customElement_CanvasDiagram__WEBPACK_IMPORTED_MODULE_0__[\"default\"];\nconst ChartEngine = _ChartEngine__WEBPACK_IMPORTED_MODULE_2__[\"default\"];\nconst DataGroupFactory = _Factory_DataGroupFactory__WEBPACK_IMPORTED_MODULE_3__[\"default\"];\nconst diagramRegistry = new _Registry_DiagramRegistry__WEBPACK_IMPORTED_MODULE_5__[\"default\"]();\nconst plugins = {\n BarsPlugin: _plugin_BarsPlugin__WEBPACK_IMPORTED_MODULE_1__[\"default\"],\n CameraMovePlugin: _plugin_CameraMove__WEBPACK_IMPORTED_MODULE_6__[\"default\"],\n EnrichGroupInfosPlugin: _plugin_EnrichGroupInfosPlugin__WEBPACK_IMPORTED_MODULE_7__[\"default\"],\n DiagramLinePlugin: _plugin_DiagramLine__WEBPACK_IMPORTED_MODULE_4__[\"default\"],\n LegendsPlugin: _plugin_LegendsPlugin__WEBPACK_IMPORTED_MODULE_9__[\"default\"],\n PaginationPlugin: _plugin_PaginationPlugin__WEBPACK_IMPORTED_MODULE_10__[\"default\"],\n TimelinePlugin: _plugin_TimelinePlugin__WEBPACK_IMPORTED_MODULE_8__[\"default\"]\n};\n/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = ({\n CanvasDiagram: _customElement_CanvasDiagram__WEBPACK_IMPORTED_MODULE_0__[\"default\"],\n ChartEngine: _ChartEngine__WEBPACK_IMPORTED_MODULE_2__[\"default\"],\n DataGroupFactory: _Factory_DataGroupFactory__WEBPACK_IMPORTED_MODULE_3__[\"default\"],\n diagramRegistry,\n plugins\n});\n\n\n//# sourceURL=webpack://@cehlers88/chart/./src/index.ts?\n}");
226
+ eval("{__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ CanvasDiagram: () => (/* binding */ CanvasDiagram),\n/* harmony export */ ChartEngine: () => (/* binding */ ChartEngine),\n/* harmony export */ DataGroupFactory: () => (/* binding */ DataGroupFactory),\n/* harmony export */ \"default\": () => (__WEBPACK_DEFAULT_EXPORT__),\n/* harmony export */ diagramRegistry: () => (/* binding */ diagramRegistry),\n/* harmony export */ plugins: () => (/* binding */ plugins)\n/* harmony export */ });\n/* harmony import */ var _customElement_CanvasDiagram__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./customElement/CanvasDiagram */ \"./src/customElement/CanvasDiagram.ts\");\n/* harmony import */ var _plugin_BarsPlugin__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./plugin/BarsPlugin */ \"./src/plugin/BarsPlugin.ts\");\n/* harmony import */ var _ChartEngine__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./ChartEngine */ \"./src/ChartEngine.ts\");\n/* harmony import */ var _Factory_DataGroupFactory__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./Factory/DataGroupFactory */ \"./src/Factory/DataGroupFactory.ts\");\n/* harmony import */ var _plugin_DiagramLine__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ./plugin/DiagramLine */ \"./src/plugin/DiagramLine.ts\");\n/* harmony import */ var _Registry_DiagramRegistry__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ./Registry/DiagramRegistry */ \"./src/Registry/DiagramRegistry.ts\");\n/* harmony import */ var _plugin_CameraMove__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ./plugin/CameraMove */ \"./src/plugin/CameraMove.ts\");\n/* harmony import */ var _plugin_EnrichGroupInfosPlugin__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! ./plugin/EnrichGroupInfosPlugin */ \"./src/plugin/EnrichGroupInfosPlugin.ts\");\n/* harmony import */ var _plugin_TimelinePlugin__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(/*! ./plugin/TimelinePlugin */ \"./src/plugin/TimelinePlugin.ts\");\n/* harmony import */ var _plugin_LegendsPlugin__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(/*! ./plugin/LegendsPlugin */ \"./src/plugin/LegendsPlugin.ts\");\n/* harmony import */ var _plugin_PaginationPlugin__WEBPACK_IMPORTED_MODULE_10__ = __webpack_require__(/*! ./plugin/PaginationPlugin */ \"./src/plugin/PaginationPlugin.ts\");\n/**\n * index.ts\n * Created 05.05.24\n *\n * @author Christoph Ehlers\n */\n\n\n\n\n\n\n\n\n\n\n\nconst CanvasDiagram = _customElement_CanvasDiagram__WEBPACK_IMPORTED_MODULE_0__[\"default\"];\nconst ChartEngine = _ChartEngine__WEBPACK_IMPORTED_MODULE_2__[\"default\"];\nconst DataGroupFactory = _Factory_DataGroupFactory__WEBPACK_IMPORTED_MODULE_3__[\"default\"];\nconst diagramRegistry = _Registry_DiagramRegistry__WEBPACK_IMPORTED_MODULE_5__.diagramRegistry;\nconst plugins = {\n BarsPlugin: _plugin_BarsPlugin__WEBPACK_IMPORTED_MODULE_1__[\"default\"],\n CameraMovePlugin: _plugin_CameraMove__WEBPACK_IMPORTED_MODULE_6__[\"default\"],\n EnrichGroupInfosPlugin: _plugin_EnrichGroupInfosPlugin__WEBPACK_IMPORTED_MODULE_7__[\"default\"],\n DiagramLinePlugin: _plugin_DiagramLine__WEBPACK_IMPORTED_MODULE_4__[\"default\"],\n LegendsPlugin: _plugin_LegendsPlugin__WEBPACK_IMPORTED_MODULE_9__[\"default\"],\n PaginationPlugin: _plugin_PaginationPlugin__WEBPACK_IMPORTED_MODULE_10__[\"default\"],\n TimelinePlugin: _plugin_TimelinePlugin__WEBPACK_IMPORTED_MODULE_8__[\"default\"]\n};\n/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = ({\n CanvasDiagram: _customElement_CanvasDiagram__WEBPACK_IMPORTED_MODULE_0__[\"default\"],\n ChartEngine: _ChartEngine__WEBPACK_IMPORTED_MODULE_2__[\"default\"],\n DataGroupFactory: _Factory_DataGroupFactory__WEBPACK_IMPORTED_MODULE_3__[\"default\"],\n diagramRegistry,\n plugins\n});\n\n\n//# sourceURL=webpack://@cehlers88/chart/./src/index.ts?\n}");
216
227
 
217
228
  /***/ },
218
229
 
@@ -223,7 +234,7 @@ eval("{__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpa
223
234
  (__unused_webpack_module, __webpack_exports__, __webpack_require__) {
224
235
 
225
236
  "use strict";
226
- eval("{__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ \"default\": () => (/* binding */ AbstractDiagramPlugin)\n/* harmony export */ });\n/* harmony import */ var _AbstractPlugin__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./AbstractPlugin */ \"./src/plugin/AbstractPlugin.ts\");\n/* harmony import */ var _cehlers88_ceutils__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! @cehlers88/ceutils */ \"./node_modules/@cehlers88/ceutils/dist/index.js\");\n/* harmony import */ var _cehlers88_ceutils__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(_cehlers88_ceutils__WEBPACK_IMPORTED_MODULE_1__);\n\n\nclass AbstractDiagramPlugin extends _AbstractPlugin__WEBPACK_IMPORTED_MODULE_0__[\"default\"] {\n constructor(props = {}) {\n super({\n viewSize: -1,\n ...props\n });\n this._priority = 50;\n }\n update(dataGroups) {\n if (this.getProperty('enabled', true) === false)\n return { dataGroups };\n if (JSON.stringify(this._dataGroups) !== JSON.stringify(dataGroups)) {\n this.updateBufferSize(dataGroups);\n }\n return super.update(dataGroups);\n }\n updateBufferSize(dataGroups) {\n const groupWithMostData = dataGroups.reduce((prev, current) => {\n return prev.data.length > current.data.length ? prev : current;\n });\n const viewSize = this.getProperty('viewSize', -1);\n const dataPerView = viewSize === -1 ? groupWithMostData.data.length : viewSize;\n const singleGroupWidth = this.getDrawAreaRect().w / dataPerView;\n const bufferWidth = groupWithMostData.data.length * singleGroupWidth;\n this.setBuffer(_cehlers88_ceutils__WEBPACK_IMPORTED_MODULE_1__.domLib.createElement('canvas', {\n height: this.getDrawAreaRect().h.toString(),\n width: bufferWidth.toString()\n }));\n this._camera.setMaxX(bufferWidth - this.getDrawAreaRect().w);\n return this.getDrawAreaRect().w / dataPerView;\n }\n}\n\n\n//# sourceURL=webpack://@cehlers88/chart/./src/plugin/AbstractDiagramPlugin.ts?\n}");
237
+ eval("{__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ \"default\": () => (/* binding */ AbstractDiagramPlugin)\n/* harmony export */ });\n/* harmony import */ var _AbstractPlugin__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./AbstractPlugin */ \"./src/plugin/AbstractPlugin.ts\");\n/* harmony import */ var _cehlers88_ceutils__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! @cehlers88/ceutils */ \"./node_modules/@cehlers88/ceutils/dist/index.js\");\n/* harmony import */ var _cehlers88_ceutils__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(_cehlers88_ceutils__WEBPACK_IMPORTED_MODULE_1__);\n\n\nclass AbstractDiagramPlugin extends _AbstractPlugin__WEBPACK_IMPORTED_MODULE_0__[\"default\"] {\n constructor(props = {}) {\n super({\n viewSize: -1,\n ...props\n });\n this._priority = 50;\n }\n onDataGroupsChanged(dataGroups) {\n this.updateBufferSize(dataGroups);\n }\n updateBufferSize(dataGroups) {\n const viewSize = this.getProperty('viewSize', -1);\n const maxDataLength = dataGroups.length === 0 ? 0 : Math.max(...dataGroups.map((dataGroup) => dataGroup.data.length));\n const dataPerView = viewSize === -1 ? maxDataLength : viewSize;\n if (maxDataLength < 1 || dataPerView < 1) {\n // Without data there is nothing to scale the buffer by.\n this.setBuffer(_cehlers88_ceutils__WEBPACK_IMPORTED_MODULE_1__.domLib.createElement('canvas', {\n height: this.getDrawAreaRect().h.toString(),\n width: this.getDrawAreaRect().w.toString()\n }));\n this._camera.setMaxX(0);\n return this.getDrawAreaRect().w;\n }\n const singleGroupWidth = this.getDrawAreaRect().w / dataPerView;\n const bufferWidth = maxDataLength * singleGroupWidth;\n this.setBuffer(_cehlers88_ceutils__WEBPACK_IMPORTED_MODULE_1__.domLib.createElement('canvas', {\n height: this.getDrawAreaRect().h.toString(),\n width: bufferWidth.toString()\n }));\n this._camera.setMaxX(bufferWidth - this.getDrawAreaRect().w);\n return singleGroupWidth;\n }\n}\n\n\n//# sourceURL=webpack://@cehlers88/chart/./src/plugin/AbstractDiagramPlugin.ts?\n}");
227
238
 
228
239
  /***/ },
229
240
 
@@ -234,7 +245,7 @@ eval("{__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpa
234
245
  (__unused_webpack_module, __webpack_exports__, __webpack_require__) {
235
246
 
236
247
  "use strict";
237
- eval("{__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ \"default\": () => (/* binding */ AbstractPlugin)\n/* harmony export */ });\nclass AbstractPlugin {\n _camera;\n _dataGroups = [];\n _entityRegistry;\n _fixed = false;\n _priority = 50;\n bufferCtx;\n ctx;\n redrawBuffer = true;\n _buffer;\n _stateChangedListener;\n _drawAreaRect;\n _properties = {};\n constructor(props = {}) {\n if (props.enabled === undefined) {\n props.enabled = true;\n }\n Object.keys(props).map(key => {\n this.setProperty(key, props[key]);\n });\n }\n get needsRedraw() {\n return this.redrawBuffer;\n }\n setStateChangedListener(listener) {\n this._stateChangedListener = listener;\n return this;\n }\n setState(newState) {\n return this;\n }\n update(dataGroups) {\n if (this.getProperty('enabled', true) === false)\n return { dataGroups };\n this._dataGroups = this.updateDataGroups(dataGroups).dataGroups;\n return { dataGroups: this._dataGroups };\n }\n updateDataGroups(dataGroups) {\n return { dataGroups };\n }\n addEntity(entity) {\n this._entityRegistry.addEntity(this.getName(), entity);\n return this;\n }\n create(canvasElement, dataGroups) {\n this.createBuffer(canvasElement);\n this.setDrawAreaRect({\n x: this.getProperty('x', 0),\n y: this.getProperty('y', 0),\n w: canvasElement.width,\n h: canvasElement.height\n });\n this.ctx = canvasElement.getContext('2d');\n console.log('so', this.getDrawAreaRect());\n return {\n dataGroups,\n plugin: this\n };\n }\n drawBuffer() {\n this.redrawBuffer = false;\n }\n draw(forceRedraw = false) {\n const padding = this.getProperty('padding', 0);\n if (this.needsRedraw || forceRedraw) {\n this.bufferCtx.clearRect(0, 0, this._buffer.width, this._buffer.height);\n this.drawBuffer();\n //console.log('redraw buffer', this.bufferCtx.canvas.width, this.bufferCtx.canvas.height);\n }\n this.ctx.drawImage(this._buffer, this._fixed ? 0 : this._camera.x, this._fixed ? 0 : this._camera.y, this._drawAreaRect.w, this._drawAreaRect.h, this._drawAreaRect.x + padding, this._drawAreaRect.y + padding, this._drawAreaRect.w - (2 * padding), this._drawAreaRect.h - (2 * padding));\n return this;\n }\n getDrawAreaRect() {\n return this._drawAreaRect;\n }\n getPriority() {\n return this._priority;\n }\n getProperty(propertyName, defaultResult) {\n return this._properties[propertyName] ?? defaultResult;\n }\n handleClick(x, y) {\n return this;\n }\n handleHover(x, y) {\n return this;\n }\n handleLeave() {\n this.handleHover(-1, -1);\n return this;\n }\n handleScroll(x, y) {\n return this;\n }\n setBuffer(buffer) {\n this._buffer = buffer;\n this.bufferCtx = buffer.getContext('2d');\n return this;\n }\n setCamera(camera) {\n this._camera = camera;\n return this;\n }\n setDrawAreaRect(rect) {\n this._drawAreaRect = rect;\n return this;\n }\n setEntityRegistry(entityRegistry) {\n this._entityRegistry = entityRegistry;\n return this;\n }\n setProperty(propertyName, value) {\n this._properties[propertyName] = value;\n return this;\n }\n init() {\n return this;\n }\n createBuffer(canvasElement, width = null, height = null) {\n console.log('create buffer', width, height, canvasElement, this);\n this._buffer = document.createElement('canvas');\n this._buffer.width = width ?? canvasElement.width;\n this._buffer.height = height ?? canvasElement.height;\n this.bufferCtx = this._buffer.getContext('2d');\n }\n}\n\n\n//# sourceURL=webpack://@cehlers88/chart/./src/plugin/AbstractPlugin.ts?\n}");
248
+ eval("{__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ \"default\": () => (/* binding */ AbstractPlugin)\n/* harmony export */ });\n/* harmony import */ var _Utils_ChartStyle__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../Utils/ChartStyle */ \"./src/Utils/ChartStyle.ts\");\n\nclass AbstractPlugin {\n _camera;\n _dataGroups = [];\n _entityRegistry;\n _fixed = false;\n _priority = 50;\n bufferCtx;\n ctx;\n redrawBuffer = true;\n _dataGroupsChanged = true;\n _theme = { ..._Utils_ChartStyle__WEBPACK_IMPORTED_MODULE_0__.DEFAULT_THEME };\n _buffer;\n _dataGroupsSignature = '';\n _stateChangedListener;\n _drawAreaRect;\n _properties = {};\n constructor(props = {}) {\n if (props.enabled === undefined) {\n props.enabled = true;\n }\n Object.keys(props).map(key => {\n this.setProperty(key, props[key]);\n });\n }\n get needsRedraw() {\n return this.redrawBuffer;\n }\n setStateChangedListener(listener) {\n this._stateChangedListener = listener;\n return this;\n }\n setState(newState) {\n return this;\n }\n update(dataGroups) {\n if (this.getProperty('enabled', true) === false)\n return { dataGroups };\n this.detectDataGroupsChange(dataGroups);\n if (this._dataGroupsChanged) {\n // Data was manipulated in the meantime, so the buffer is outdated.\n this.redrawBuffer = true;\n this.onDataGroupsChanged(dataGroups);\n }\n this._dataGroups = this.updateDataGroups(dataGroups).dataGroups;\n return { dataGroups: this._dataGroups };\n }\n /**\n * Hook for plugins that have to prepare themselves (e.g. resize their\n * buffer) before the changed data is processed.\n */\n onDataGroupsChanged(dataGroups) {\n }\n /**\n * Compares the given data groups by content against the ones seen by the\n * last update and remembers the result in `_dataGroupsChanged`. Comparing\n * by content (instead of holding on to the data groups themselves) is what\n * makes manipulations of already known data groups visible.\n */\n detectDataGroupsChange(dataGroups) {\n const signature = JSON.stringify(dataGroups);\n this._dataGroupsChanged = signature !== this._dataGroupsSignature;\n this._dataGroupsSignature = signature;\n }\n updateDataGroups(dataGroups) {\n return { dataGroups };\n }\n addEntity(entity) {\n this._entityRegistry.addEntity(this.getName(), entity);\n return this;\n }\n create(canvasElement, dataGroups) {\n this.createBuffer(canvasElement);\n this.setDrawAreaRect({\n x: this.getProperty('x', 0),\n y: this.getProperty('y', 0),\n w: canvasElement.width,\n h: canvasElement.height\n });\n this.ctx = canvasElement.getContext('2d');\n this._theme = (0,_Utils_ChartStyle__WEBPACK_IMPORTED_MODULE_0__.readTheme)(canvasElement);\n return {\n dataGroups,\n plugin: this\n };\n }\n /**\n * Adapts the plugin to a new size of the canvas. Unlike `create()` this\n * has no side effects besides rebuilding the buffer, so it can be called\n * as often as the canvas changes its size.\n */\n resize(canvasElement) {\n this.createBuffer(canvasElement);\n this.setDrawAreaRect({\n x: this.getProperty('x', 0),\n y: this.getProperty('y', 0),\n w: canvasElement.width,\n h: canvasElement.height\n });\n this._theme = (0,_Utils_ChartStyle__WEBPACK_IMPORTED_MODULE_0__.readTheme)(canvasElement);\n return this.invalidate();\n }\n /**\n * Forces the plugin to process its data and to redraw its buffer with the\n * next frame, even if the data did not change.\n */\n invalidate() {\n this._dataGroupsSignature = '';\n this.redrawBuffer = true;\n return this;\n }\n /**\n * Returns a colour/font of the theme. A plugin property of the same name\n * (e.g. `textColor`) wins over the `--chart-*` CSS properties of the canvas.\n */\n getThemeValue(key) {\n return this.getProperty(key, this._theme[key]);\n }\n drawBuffer() {\n this.redrawBuffer = false;\n }\n draw(forceRedraw = false) {\n const padding = this.getProperty('padding', 0);\n if (this.needsRedraw || forceRedraw) {\n this.bufferCtx.clearRect(0, 0, this._buffer.width, this._buffer.height);\n this.drawBuffer();\n //console.log('redraw buffer', this.bufferCtx.canvas.width, this.bufferCtx.canvas.height);\n }\n this.ctx.drawImage(this._buffer, this._fixed ? 0 : this._camera.x, this._fixed ? 0 : this._camera.y, this._drawAreaRect.w, this._drawAreaRect.h, this._drawAreaRect.x + padding, this._drawAreaRect.y + padding, this._drawAreaRect.w - (2 * padding), this._drawAreaRect.h - (2 * padding));\n return this;\n }\n getDrawAreaRect() {\n return this._drawAreaRect;\n }\n getPriority() {\n return this._priority;\n }\n getProperty(propertyName, defaultResult) {\n return this._properties[propertyName] ?? defaultResult;\n }\n handleClick(x, y) {\n return this;\n }\n handleHover(x, y) {\n return this;\n }\n handleLeave() {\n this.handleHover(-1, -1);\n return this;\n }\n handleScroll(x, y) {\n return this;\n }\n setBuffer(buffer) {\n this._buffer = buffer;\n this.bufferCtx = buffer.getContext('2d');\n return this;\n }\n setCamera(camera) {\n this._camera = camera;\n return this;\n }\n setDrawAreaRect(rect) {\n this._drawAreaRect = rect;\n return this;\n }\n setEntityRegistry(entityRegistry) {\n this._entityRegistry = entityRegistry;\n return this;\n }\n setProperty(propertyName, value) {\n this._properties[propertyName] = value;\n return this;\n }\n init() {\n return this;\n }\n createBuffer(canvasElement, width = null, height = null) {\n this._buffer = document.createElement('canvas');\n this._buffer.width = width ?? canvasElement.width;\n this._buffer.height = height ?? canvasElement.height;\n this.bufferCtx = this._buffer.getContext('2d');\n }\n}\n\n\n//# sourceURL=webpack://@cehlers88/chart/./src/plugin/AbstractPlugin.ts?\n}");
238
249
 
239
250
  /***/ },
240
251
 
@@ -245,7 +256,7 @@ eval("{__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpa
245
256
  (__unused_webpack_module, __webpack_exports__, __webpack_require__) {
246
257
 
247
258
  "use strict";
248
- eval("{__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ ERangeType: () => (/* binding */ ERangeType),\n/* harmony export */ \"default\": () => (/* binding */ BarsPlugin)\n/* harmony export */ });\n/* harmony import */ var _AbstractDiagramPlugin__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./AbstractDiagramPlugin */ \"./src/plugin/AbstractDiagramPlugin.ts\");\n/**\n * Library BarsPlugin\n * Created 05.05.24\n *\n * @author Christoph Ehlers\n */\n\nvar ERangeType;\n(function (ERangeType) {\n ERangeType[\"ZERO_BASED\"] = \"zero_based\";\n ERangeType[\"MIN_MAX\"] = \"min_max\";\n})(ERangeType || (ERangeType = {}));\nclass BarsPlugin extends _AbstractDiagramPlugin__WEBPACK_IMPORTED_MODULE_0__[\"default\"] {\n barGroups = [];\n hoverPosition = { x: 0, y: 0 };\n rangeType = ERangeType.MIN_MAX;\n constructor(props = {}) {\n super({\n animationSpeed: 1,\n enableAnimation: true,\n padding: 0,\n ...props,\n });\n }\n drawBuffer() {\n this.barGroups.map((group) => {\n group.map((bar) => {\n this.bufferCtx.fillStyle = bar.color;\n const hover = this.hoverPosition.x >= bar.x && this.hoverPosition.x <= bar.x + bar.width;\n this.bufferCtx.fillRect(bar.x - (hover ? 1 : 0), bar.y, bar.width + (hover ? 2 : 0), bar.height);\n });\n });\n super.drawBuffer();\n }\n handleHover(x, y) {\n this.hoverPosition = { x, y };\n this.barGroups.map((group) => {\n group.map((bar) => {\n let changed = false;\n if (x >= bar.x && x <= bar.x + bar.width) {\n if (!bar.hovered) {\n changed = true;\n }\n bar.hovered = true;\n }\n else {\n if (bar.hovered) {\n changed = true;\n }\n bar.hovered = false;\n }\n if (changed) {\n this.redrawBuffer = true;\n }\n });\n });\n return this;\n }\n updateDataGroups(dataGroups) {\n if (JSON.stringify(this._dataGroups) !== JSON.stringify(dataGroups)) {\n this.redrawBuffer = true;\n this._dataGroups = dataGroups;\n let maxValue = Math.max(...dataGroups.map((group) => group.infos.max));\n const minValue = Math.min(...dataGroups.map((group) => group.infos.min));\n const range = maxValue - minValue;\n //const minValueZeroDiff = minValue < 0 ? Math.abs(minValue) : 0;\n const viewHeight = this.getDrawAreaRect().h;\n const maxDataLength = Math.max(...dataGroups.map((group) => group.data.length));\n const barWidth = (this.bufferCtx.canvas.width / (maxDataLength * dataGroups.length));\n //const zeroY = this.getDrawAreaRect().y + this.getDrawAreaRect().h - (minValueZeroDiff * viewHeight / maxValue);\n this.barGroups = [];\n this._dataGroups.map((group, groupIndex) => {\n group.data.map((data, index) => {\n while (this.barGroups.length <= index) {\n this.barGroups.push([]);\n }\n const drawArea = this.getDrawAreaRect();\n const percentageValue = Math.round((data.value - minValue) / range * 100);\n const height = (viewHeight / 100) * Math.round(percentageValue);\n const y = viewHeight - (viewHeight / 100) * Math.round(percentageValue);\n const bar = {\n height, // this.getProperty('enableAnimation',false) ? 1 : data.value * viewHeight / maxValue,\n destinationH: 10,\n destinationY: y, //drawArea.y + drawArea.h - (data.valueEnd * viewHeight / maxValue),\n x: (groupIndex * barWidth) + (index * barWidth * dataGroups.length) + drawArea.x + 1,\n y: y, //this.getProperty('enableAnimation',false) ? drawArea.h - barHeight : drawArea.y + drawArea.h - (data.valueEnd * viewHeight / maxValue),\n width: barWidth - 2,\n color: group.color\n };\n this.barGroups[index].push(bar);\n });\n });\n }\n this.updateAnimations();\n return { dataGroups: this._dataGroups };\n }\n updateAnimations() {\n this.barGroups.map((group) => {\n group.map((bar) => {\n if (bar.y > bar.destinationY) {\n this.redrawBuffer = true;\n bar.y -= Math.min(1 * this.getProperty('animationSpeed', 1), bar.y - bar.destinationY);\n }\n if (bar.height < bar.destinationH) {\n this.redrawBuffer = true;\n bar.height += Math.min(1 * this.getProperty('animationSpeed', 1), bar.destinationH - bar.height);\n }\n });\n });\n return this;\n }\n getName() {\n return \"BarsPlugin\";\n }\n}\n\n\n//# sourceURL=webpack://@cehlers88/chart/./src/plugin/BarsPlugin.ts?\n}");
259
+ eval("{__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ ERangeType: () => (/* binding */ ERangeType),\n/* harmony export */ \"default\": () => (/* binding */ BarsPlugin)\n/* harmony export */ });\n/* harmony import */ var _AbstractDiagramPlugin__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./AbstractDiagramPlugin */ \"./src/plugin/AbstractDiagramPlugin.ts\");\n/**\n * Library BarsPlugin\n * Created 05.05.24\n *\n * @author Christoph Ehlers\n */\n\nvar ERangeType;\n(function (ERangeType) {\n ERangeType[\"ZERO_BASED\"] = \"zero_based\";\n ERangeType[\"MIN_MAX\"] = \"min_max\";\n})(ERangeType || (ERangeType = {}));\nclass BarsPlugin extends _AbstractDiagramPlugin__WEBPACK_IMPORTED_MODULE_0__[\"default\"] {\n barGroups = [];\n hoverPosition = { x: 0, y: 0 };\n rangeType = ERangeType.MIN_MAX;\n constructor(props = {}) {\n super({\n animationSpeed: 1,\n enableAnimation: true,\n padding: 0,\n ...props,\n });\n }\n drawBuffer() {\n this.barGroups.map((group) => {\n group.map((bar) => {\n this.bufferCtx.fillStyle = bar.color;\n const hover = this.hoverPosition.x >= bar.x && this.hoverPosition.x <= bar.x + bar.width;\n this.bufferCtx.fillRect(bar.x - (hover ? 1 : 0), bar.y, bar.width + (hover ? 2 : 0), bar.height);\n });\n });\n super.drawBuffer();\n }\n handleHover(x, y) {\n this.hoverPosition = { x, y };\n this.barGroups.map((group) => {\n group.map((bar) => {\n let changed = false;\n if (x >= bar.x && x <= bar.x + bar.width) {\n if (!bar.hovered) {\n changed = true;\n }\n bar.hovered = true;\n }\n else {\n if (bar.hovered) {\n changed = true;\n }\n bar.hovered = false;\n }\n if (changed) {\n this.redrawBuffer = true;\n }\n });\n });\n return this;\n }\n updateDataGroups(dataGroups) {\n if (this._dataGroupsChanged) {\n this.redrawBuffer = true;\n this._dataGroups = dataGroups;\n this.barGroups = [];\n const maxDataLength = dataGroups.length === 0 ? 0 : Math.max(...dataGroups.map((group) => group.data.length));\n if (maxDataLength === 0) {\n // Nothing (left) to draw - e.g. after the data was cleared at runtime.\n this.updateAnimations();\n return { dataGroups: this._dataGroups };\n }\n let maxValue = Math.max(...dataGroups.map((group) => group.infos.max));\n const minValue = Math.min(...dataGroups.map((group) => group.infos.min));\n const range = (maxValue - minValue) === 0 ? 1 : maxValue - minValue;\n //const minValueZeroDiff = minValue < 0 ? Math.abs(minValue) : 0;\n const viewHeight = this.getDrawAreaRect().h;\n const barWidth = (this.bufferCtx.canvas.width / (maxDataLength * dataGroups.length));\n //const zeroY = this.getDrawAreaRect().y + this.getDrawAreaRect().h - (minValueZeroDiff * viewHeight / maxValue);\n this._dataGroups.map((group, groupIndex) => {\n group.data.map((data, index) => {\n while (this.barGroups.length <= index) {\n this.barGroups.push([]);\n }\n const drawArea = this.getDrawAreaRect();\n const percentageValue = Math.round((data.value - minValue) / range * 100);\n const height = (viewHeight / 100) * Math.round(percentageValue);\n const y = viewHeight - (viewHeight / 100) * Math.round(percentageValue);\n const bar = {\n height, // this.getProperty('enableAnimation',false) ? 1 : data.value * viewHeight / maxValue,\n destinationH: 10,\n destinationY: y, //drawArea.y + drawArea.h - (data.valueEnd * viewHeight / maxValue),\n x: (groupIndex * barWidth) + (index * barWidth * dataGroups.length) + drawArea.x + 1,\n y: y, //this.getProperty('enableAnimation',false) ? drawArea.h - barHeight : drawArea.y + drawArea.h - (data.valueEnd * viewHeight / maxValue),\n width: barWidth - 2,\n color: group.color\n };\n this.barGroups[index].push(bar);\n });\n });\n }\n this.updateAnimations();\n return { dataGroups: this._dataGroups };\n }\n updateAnimations() {\n this.barGroups.map((group) => {\n group.map((bar) => {\n if (bar.y > bar.destinationY) {\n this.redrawBuffer = true;\n bar.y -= Math.min(1 * this.getProperty('animationSpeed', 1), bar.y - bar.destinationY);\n }\n if (bar.height < bar.destinationH) {\n this.redrawBuffer = true;\n bar.height += Math.min(1 * this.getProperty('animationSpeed', 1), bar.destinationH - bar.height);\n }\n });\n });\n return this;\n }\n getName() {\n return \"BarsPlugin\";\n }\n}\n\n\n//# sourceURL=webpack://@cehlers88/chart/./src/plugin/BarsPlugin.ts?\n}");
249
260
 
250
261
  /***/ },
251
262
 
@@ -267,7 +278,7 @@ eval("{__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpa
267
278
  (__unused_webpack_module, __webpack_exports__, __webpack_require__) {
268
279
 
269
280
  "use strict";
270
- eval("{__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ ERangeType: () => (/* binding */ ERangeType),\n/* harmony export */ \"default\": () => (/* binding */ DiagramLine)\n/* harmony export */ });\n/* harmony import */ var _AbstractDiagramPlugin__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./AbstractDiagramPlugin */ \"./src/plugin/AbstractDiagramPlugin.ts\");\n/* harmony import */ var _Event_TooltipEvent__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../Event/TooltipEvent */ \"./src/Event/TooltipEvent.ts\");\n/**\n * Library DiagramLine\n * Created 26.11.22\n *\n * @author Christoph Ehlers\n */\n\n\nvar ERangeType;\n(function (ERangeType) {\n ERangeType[\"ZERO_BASED\"] = \"zero_based\";\n ERangeType[\"MIN_MAX\"] = \"min_max\";\n})(ERangeType || (ERangeType = {}));\nclass DiagramLine extends _AbstractDiagramPlugin__WEBPACK_IMPORTED_MODULE_0__[\"default\"] {\n bulletPointRadius = 2;\n bulletPointRadiusHover = 5;\n dataPoints = [];\n helpLines = [];\n hoverPosition = { x: 0, y: 0 };\n rangeType = ERangeType.MIN_MAX;\n syncGroupsPosition = false;\n zeroLineValue = 0;\n zeroLineValuePercentage = 0;\n constructor(props = {}) {\n super({\n fill: true,\n showBulletPoints: false,\n showHelpLines: false,\n showZeroLine: false,\n padding: 0,\n ...props\n });\n }\n drawHelpLines() {\n if (!this.getProperty('showHelpLines', false))\n return this;\n this.helpLines.map((helpLine) => {\n this.bufferCtx.strokeStyle = helpLine.color;\n this.bufferCtx.fillStyle = helpLine.color;\n this.bufferCtx.beginPath();\n this.bufferCtx.moveTo(super.getDrawAreaRect().x, helpLine.y);\n this.bufferCtx.lineTo(super.getDrawAreaRect().x + this.bufferCtx.canvas.width, helpLine.y);\n this.bufferCtx.stroke();\n this.bufferCtx.fillText(helpLine.label, super.getDrawAreaRect().x, helpLine.y);\n });\n return this;\n }\n drawBuffer() {\n const singlePercentageHeight = this.getDrawAreaRect().h / 100; // 1% height\n this.drawHelpLines();\n if (this._showZeroLine()) {\n this.bufferCtx.strokeStyle = '#000000';\n this.bufferCtx.beginPath();\n this.bufferCtx.moveTo(this.getDrawAreaRect().x, this.getDrawAreaRect().y + this.getDrawAreaRect().h - singlePercentageHeight * this.zeroLineValuePercentage);\n this.bufferCtx.lineTo(this.getDrawAreaRect().x + this.bufferCtx.canvas.width, this.getDrawAreaRect().y + this.getDrawAreaRect().h - singlePercentageHeight * this.zeroLineValuePercentage);\n this.bufferCtx.stroke();\n }\n //console.log('viewHeight', this.getDrawAreaRect().h, this);\n this.dataPoints.map(group => {\n this.bufferCtx.beginPath();\n let lastX = 0;\n group.map((dataPoint, index) => {\n this.bufferCtx.strokeStyle = dataPoint.color;\n this.bufferCtx.fillStyle = dataPoint.color + '88';\n this.bufferCtx.lineWidth = dataPoint.isHighlighted ? 2 : 1;\n if (index === 0) {\n if (this.getProperty('fill', false)) {\n this.bufferCtx.moveTo(dataPoint.x, this.getDrawAreaRect().y + this.getDrawAreaRect().h);\n this.bufferCtx.lineTo(dataPoint.x, dataPoint.y);\n return;\n }\n this.bufferCtx.moveTo(dataPoint.x, dataPoint.y);\n }\n else {\n this.bufferCtx.lineTo(dataPoint.x, dataPoint.y);\n }\n lastX = dataPoint.x;\n });\n if (this.getProperty('fill', false)) {\n this.bufferCtx.lineTo(lastX, this.getDrawAreaRect().y + this.getDrawAreaRect().h);\n this.bufferCtx.fill();\n }\n this.bufferCtx.stroke();\n });\n if (this.getProperty('showBulletPoints', false)) {\n this.dataPoints.map(group => {\n group.map((dataPoint) => {\n const mouseDistance = Math.sqrt(Math.pow(this.hoverPosition.x - dataPoint.x, 2) + Math.pow(this.hoverPosition.y - dataPoint.y, 2));\n if (mouseDistance < this.bulletPointRadiusHover) {\n _Event_TooltipEvent__WEBPACK_IMPORTED_MODULE_1__[\"default\"].dispatch('test', {});\n }\n this.bufferCtx.fillStyle = dataPoint.color;\n this.bufferCtx.beginPath();\n this.bufferCtx.arc(dataPoint.x, dataPoint.y, (mouseDistance < this.bulletPointRadiusHover ? this.bulletPointRadiusHover : this.bulletPointRadius), 0, 2 * Math.PI);\n this.bufferCtx.fill();\n });\n });\n }\n return this;\n }\n handleHover(x, y) {\n this.hoverPosition = { x, y };\n return this;\n }\n updateDataGroups(dataGroups) {\n if (JSON.stringify(this._dataGroups) === JSON.stringify(dataGroups))\n return { dataGroups };\n this._dataGroups = dataGroups;\n const maxValue = Math.max(...dataGroups.map((group) => group.infos.max));\n const minValue = Math.min(...this._dataGroups.map((group) => group.infos.min), (this._showZeroLine() ? 0 : maxValue));\n this.zeroLineValue = Math.min(0, ...dataGroups.map((group) => group.infos.min)) * -1;\n this.zeroLineValuePercentage = this.zeroLineValue / (maxValue + this.zeroLineValue) * 100;\n const groupWithMostData = this._dataGroups.reduce((prev, current) => prev.data.length > current.data.length ? prev : current);\n const pointGap = this.calcPointGap(groupWithMostData);\n const largestDecimalStep = Math.max(...this._dataGroups.map((group) => group.infos.largestDecimalStep));\n const globalRange = maxValue - minValue;\n this.helpLines = [];\n for (let i = 1; i <= 10; i++) {\n let height = (this.getDrawAreaRect().h / 100) * Math.round(((i * largestDecimalStep) - minValue) / globalRange * 100);\n if (largestDecimalStep * i < minValue || largestDecimalStep * i > maxValue) {\n continue;\n }\n this.helpLines.push({\n color: '#ee4444',\n y: Math.round(this.getDrawAreaRect().h - height),\n label: (i * largestDecimalStep).toString()\n });\n }\n this.dataPoints = this._dataGroups.map((dataGroup) => {\n return dataGroup.data.map((dataPoint, index) => {\n let height = this.getDrawAreaRect().h - (this.getDrawAreaRect().h / 100) * Math.round((dataPoint.value - minValue) / globalRange * 100);\n console.log(height, 'height', dataPoint.value, {\n height,\n globalRange,\n minValue,\n maxValue,\n largestDecimalStep,\n i: largestDecimalStep * index,\n viewHeight: this.getDrawAreaRect().h,\n pointGap,\n dataPoint,\n dataGroup,\n index\n });\n if (dataPoint.empty)\n return undefined;\n return {\n ...dataPoint,\n x: this.getDrawAreaRect().x + pointGap * index,\n y: height,\n color: dataGroup.color,\n };\n }).filter((dataPoint) => dataPoint !== undefined);\n });\n return { dataGroups };\n }\n getDrawAreaRect() {\n const { x, y, h, w } = super.getDrawAreaRect();\n const manipulator = 0; //(this.getProperty('showBulletPoints', false) ? this.bulletPointRadiusHover : 0);\n return {\n x: x + manipulator,\n y: y + manipulator,\n h: h - (manipulator * 2),\n w: w - (manipulator * 2)\n };\n }\n getName() {\n return \"DiagramLine\";\n }\n _showZeroLine() {\n return this.getProperty('showZeroLine', false);\n }\n calcPointGap(dataGroup) {\n return (this.getDrawAreaRect().w - (this.bulletPointRadius * 2)) / (dataGroup.data.length - 1);\n /*return this.getDrawAreaRect().w /\n (this.getProperty('viewSize', -1) === -1 ?\n (this.syncGroupsPosition ? Math.max(...this._dataGroups.map((group) => group.data.length - 1)) : dataGroup.data.length - 1) :\n this.getProperty('viewSize', -1) - 1)\n ;*/\n }\n}\n\n\n//# sourceURL=webpack://@cehlers88/chart/./src/plugin/DiagramLine.ts?\n}");
281
+ eval("{__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ ERangeType: () => (/* binding */ ERangeType),\n/* harmony export */ \"default\": () => (/* binding */ DiagramLine)\n/* harmony export */ });\n/* harmony import */ var _AbstractDiagramPlugin__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./AbstractDiagramPlugin */ \"./src/plugin/AbstractDiagramPlugin.ts\");\n/* harmony import */ var _Event_TooltipEvent__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../Event/TooltipEvent */ \"./src/Event/TooltipEvent.ts\");\n/* harmony import */ var _Utils_ChartStyle__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../Utils/ChartStyle */ \"./src/Utils/ChartStyle.ts\");\n/**\n * Library DiagramLine\n * Created 26.11.22\n * Last modified 29.09.26\n *\n * @author Christoph Ehlers\n */\n\n\n\nvar ERangeType;\n(function (ERangeType) {\n ERangeType[\"ZERO_BASED\"] = \"zero_based\";\n ERangeType[\"MIN_MAX\"] = \"min_max\";\n})(ERangeType || (ERangeType = {}));\nclass DiagramLine extends _AbstractDiagramPlugin__WEBPACK_IMPORTED_MODULE_0__[\"default\"] {\n static X_AXIS_HEIGHT = 14;\n static PLOT_INSET_TOP = 6;\n static PLOT_INSET_BOTTOM = 3;\n static TOOLTIP_ENTITY_SOURCE = 'tooltip';\n static _instanceCounter = 0;\n _tooltipEntityId = 'DiagramLine.tooltip.' + (++DiagramLine._instanceCounter);\n bulletPointRadius = 2;\n bulletPointRadiusHover = 4;\n dataPoints = [];\n hoverIndex = -1;\n hoverPosition = { x: -1, y: -1 };\n maxDataLength = 0;\n plotRect = { x: 0, y: 0, w: 0, h: 0 };\n pointGap = 0;\n rangeType = ERangeType.MIN_MAX;\n yAxis = { min: 0, max: 1, step: 1, ticks: [] };\n constructor(props = {}) {\n super({\n decimals: 2,\n fill: true,\n lineWidth: 2,\n maxYTicks: 5,\n showBulletPoints: false,\n showGrid: true,\n showTooltip: true,\n showXLabels: true,\n showYLabels: true,\n showZeroLine: false,\n smooth: true,\n padding: 0,\n ...props\n });\n }\n drawBuffer() {\n const ctx = this.bufferCtx;\n this.drawGrid();\n if (this._showZeroLine() && this.yAxis.min <= 0 && this.yAxis.max >= 0) {\n const y = Math.round(this.valueToY(0)) + .5;\n ctx.strokeStyle = (0,_Utils_ChartStyle__WEBPACK_IMPORTED_MODULE_2__.withAlpha)(this.getThemeValue('textColor'), .45);\n ctx.lineWidth = 1;\n ctx.beginPath();\n ctx.moveTo(0, y);\n ctx.lineTo(ctx.canvas.width, y);\n ctx.stroke();\n }\n this.dataPoints.map(group => {\n this.splitIntoSegments(group).map(segment => this.drawSegment(segment));\n });\n if (this.getProperty('showBulletPoints', false)) {\n this.dataPoints.map(group => {\n group.map((dataPoint) => {\n ctx.fillStyle = dataPoint.color;\n ctx.beginPath();\n ctx.arc(dataPoint.x, dataPoint.y, this.bulletPointRadius, 0, 2 * Math.PI);\n ctx.fill();\n });\n });\n }\n this.drawXLabels();\n super.drawBuffer();\n }\n /**\n * Draws the buffer and on top of it everything that must not scroll with\n * the camera or changes with every pointer move: the value axis and the\n * hover crosshair with its tooltip.\n */\n draw(forceRedraw = false) {\n super.draw(forceRedraw);\n if (this.getProperty('enabled', true) === false || this.dataPoints.length === 0) {\n return this;\n }\n this.drawYLabels();\n this.drawHover();\n return this;\n }\n handleHover(x, y) {\n this.hoverPosition = { x, y };\n return this;\n }\n updateDataGroups(dataGroups) {\n if (!this._dataGroupsChanged)\n return { dataGroups };\n this._dataGroups = dataGroups;\n if (dataGroups.length === 0 || dataGroups.every((dataGroup) => dataGroup.data.length === 0)) {\n // Nothing (left) to draw - e.g. after the data was cleared at runtime.\n this.dataPoints = [];\n this.maxDataLength = 0;\n return { dataGroups };\n }\n const filledGroups = dataGroups.filter((group) => group.data.some((data) => !data.empty));\n let minValue = filledGroups.length === 0 ? 0 : Math.min(...filledGroups.map((group) => group.infos.minValue ?? group.infos.min ?? 0));\n let maxValue = filledGroups.length === 0 ? 0 : Math.max(...filledGroups.map((group) => group.infos.max ?? 0));\n if (this._showZeroLine() || this.rangeType === ERangeType.ZERO_BASED) {\n minValue = Math.min(0, minValue);\n maxValue = Math.max(0, maxValue);\n }\n this.plotRect = this.calcPlotRect(dataGroups);\n const maxTicks = Math.max(2, Math.min(this.getProperty('maxYTicks', 5), Math.floor(this.plotRect.h / 22) + 1));\n this.yAxis = (0,_Utils_ChartStyle__WEBPACK_IMPORTED_MODULE_2__.niceTicks)(minValue, maxValue, maxTicks);\n this.maxDataLength = Math.max(...dataGroups.map((group) => group.data.length));\n this.pointGap = this.calcPointGap(this.maxDataLength);\n this.dataPoints = this._dataGroups.map((dataGroup) => {\n // time labels are formatted per group, so values appended later are\n // written the same way as the initial ones\n const timeLabels = (0,_Utils_ChartStyle__WEBPACK_IMPORTED_MODULE_2__.formatTimestamps)(dataGroup.data.map((data) => data.timestamp));\n return dataGroup.data.map((dataPoint, index) => {\n if (dataPoint.empty)\n return undefined;\n const label = dataPoint.hasLabel ? dataPoint.label : timeLabels[index];\n return {\n ...dataPoint,\n hasLabel: label !== undefined && label !== '',\n label,\n index,\n x: this.indexToX(index),\n y: this.valueToY(Number(dataPoint.value)),\n color: dataGroup.color,\n };\n }).filter((dataPoint) => dataPoint !== undefined);\n });\n return { dataGroups };\n }\n getName() {\n return \"DiagramLine\";\n }\n _showZeroLine() {\n return this.getProperty('showZeroLine', false);\n }\n calcPlotRect(dataGroups) {\n const { x, y, w, h } = this.getDrawAreaRect();\n const hasXLabels = this.getProperty('showXLabels', true)\n && dataGroups.some((group) => group.data.some((data) => data.hasLabel || data.timestamp !== undefined));\n const top = DiagramLine.PLOT_INSET_TOP;\n const bottom = DiagramLine.PLOT_INSET_BOTTOM + (hasXLabels ? DiagramLine.X_AXIS_HEIGHT : 0);\n return {\n x,\n y: y + top,\n w,\n h: Math.max(1, h - top - bottom),\n };\n }\n calcPointGap(dataLength) {\n const usableWidth = this.plotRect.w - (this.bulletPointRadiusHover * 2);\n if (dataLength < 2) {\n return usableWidth;\n }\n return usableWidth / (dataLength - 1);\n }\n indexToX(index) {\n if (this.maxDataLength < 2) {\n return this.plotRect.x + this.plotRect.w / 2;\n }\n return this.plotRect.x + this.bulletPointRadiusHover + this.pointGap * index;\n }\n valueToY(value) {\n const range = this.yAxis.max - this.yAxis.min;\n const ratio = range === 0 ? .5 : (value - this.yAxis.min) / range;\n return this.plotRect.y + this.plotRect.h - ratio * this.plotRect.h;\n }\n /**\n * Empty values interrupt the line instead of being drawn as 0.\n */\n splitIntoSegments(points) {\n const segments = [];\n let current = [];\n points.map((point) => {\n if (current.length > 0 && point.index !== current[current.length - 1].index + 1) {\n segments.push(current);\n current = [];\n }\n current.push(point);\n });\n if (current.length > 0) {\n segments.push(current);\n }\n return segments;\n }\n drawSegment(segment) {\n const ctx = this.bufferCtx;\n const color = segment[0].color;\n const baseline = this.plotRect.y + this.plotRect.h;\n if (segment.length === 1) {\n // A lonely value has no neighbour to connect to - show it as a dot.\n ctx.fillStyle = color;\n ctx.beginPath();\n ctx.arc(segment[0].x, segment[0].y, this.bulletPointRadius + .5, 0, 2 * Math.PI);\n ctx.fill();\n return;\n }\n if (this.getProperty('fill', false)) {\n ctx.beginPath();\n ctx.moveTo(segment[0].x, baseline);\n ctx.lineTo(segment[0].x, segment[0].y);\n this.tracePath(segment);\n ctx.lineTo(segment[segment.length - 1].x, baseline);\n ctx.closePath();\n ctx.fillStyle = this.createFillStyle(color);\n ctx.fill();\n }\n ctx.beginPath();\n ctx.moveTo(segment[0].x, segment[0].y);\n this.tracePath(segment);\n ctx.strokeStyle = color;\n ctx.lineWidth = this.getProperty('lineWidth', 2);\n ctx.lineJoin = 'round';\n ctx.lineCap = 'round';\n ctx.stroke();\n ctx.lineWidth = 1;\n }\n createFillStyle(color) {\n const top = (0,_Utils_ChartStyle__WEBPACK_IMPORTED_MODULE_2__.withAlpha)(color, .35);\n const bottom = (0,_Utils_ChartStyle__WEBPACK_IMPORTED_MODULE_2__.withAlpha)(color, .03);\n if (top === color || typeof this.bufferCtx.createLinearGradient !== 'function') {\n // unknown colour notation - a plain fill is better than an opaque one\n return top === color ? 'rgba(128, 128, 128, 0.2)' : top;\n }\n const gradient = this.bufferCtx.createLinearGradient(0, this.plotRect.y, 0, this.plotRect.y + this.plotRect.h);\n gradient.addColorStop(0, top);\n gradient.addColorStop(1, bottom);\n return gradient;\n }\n /**\n * Adds the line through the given points to the current path (the path\n * has to be at the first point already). With `smooth` the points are\n * connected by a monotone cubic curve (Fritsch-Carlson): it runs through\n * every value and never overshoots, so no peak is drawn that was never\n * measured.\n */\n tracePath(points) {\n const ctx = this.bufferCtx;\n if (!this.getProperty('smooth', true) || points.length < 3 || typeof ctx.bezierCurveTo !== 'function') {\n points.slice(1).map((point) => ctx.lineTo(point.x, point.y));\n return;\n }\n const n = points.length;\n const secants = [];\n for (let i = 0; i < n - 1; i++) {\n const dx = points[i + 1].x - points[i].x;\n secants.push(dx === 0 ? 0 : (points[i + 1].y - points[i].y) / dx);\n }\n const tangents = [secants[0]];\n for (let i = 1; i < n - 1; i++) {\n tangents.push(secants[i - 1] * secants[i] <= 0 ? 0 : (secants[i - 1] + secants[i]) / 2);\n }\n tangents.push(secants[n - 2]);\n for (let i = 0; i < n - 1; i++) {\n if (secants[i] === 0) {\n tangents[i] = 0;\n tangents[i + 1] = 0;\n continue;\n }\n const a = tangents[i] / secants[i];\n const b = tangents[i + 1] / secants[i];\n const sum = a * a + b * b;\n if (sum > 9) {\n const factor = 3 / Math.sqrt(sum);\n tangents[i] = factor * a * secants[i];\n tangents[i + 1] = factor * b * secants[i];\n }\n }\n for (let i = 0; i < n - 1; i++) {\n const p0 = points[i];\n const p1 = points[i + 1];\n const third = (p1.x - p0.x) / 3;\n ctx.bezierCurveTo(p0.x + third, p0.y + tangents[i] * third, p1.x - third, p1.y - tangents[i + 1] * third, p1.x, p1.y);\n }\n }\n drawGrid() {\n if (!this.isGridEnabled())\n return;\n const ctx = this.bufferCtx;\n ctx.strokeStyle = this.getThemeValue('gridColor');\n ctx.lineWidth = 1;\n this.yAxis.ticks.map((tick) => {\n const y = Math.round(this.valueToY(tick)) + .5;\n ctx.beginPath();\n ctx.moveTo(0, y);\n ctx.lineTo(ctx.canvas.width, y);\n ctx.stroke();\n });\n }\n isGridEnabled() {\n return this.getProperty('showGrid', true) || this.getProperty('showHelpLines', false);\n }\n /**\n * Labels of the values along the x axis (e.g. the time of a measurement).\n * Only as many as fit next to each other are drawn.\n */\n drawXLabels() {\n if (!this.getProperty('showXLabels', true))\n return;\n // the group with the most labelled values, not simply the longest one:\n // a long group without labels must not hide the labels of another\n const labelled = this.dataPoints\n .map((group) => group.filter((point) => point.hasLabel))\n .reduce((prev, current) => current.length > prev.length ? current : prev, []);\n if (labelled.length === 0)\n return;\n const ctx = this.bufferCtx;\n ctx.font = this.getThemeValue('font');\n ctx.fillStyle = this.getThemeValue('textColor');\n ctx.textBaseline = 'bottom';\n const labelWidth = Math.max(...labelled.map((point) => ctx.measureText(point.label).width)) + 16;\n const slots = Math.max(1, Math.floor(this.plotRect.w / labelWidth));\n const step = Math.max(1, Math.ceil(labelled.length / slots));\n const y = this.getDrawAreaRect().y + this.getDrawAreaRect().h;\n for (let i = 0; i < labelled.length; i += step) {\n const point = labelled[i];\n const half = ctx.measureText(point.label).width / 2;\n const x = Math.min(Math.max(point.x - half, 0), ctx.canvas.width - half * 2);\n ctx.textAlign = 'left';\n ctx.fillText(point.label, x, y);\n }\n }\n drawYLabels() {\n if (!this.getProperty('showYLabels', true))\n return;\n const ctx = this.ctx;\n // the step decides: fewer decimals would make neighbouring ticks look alike\n const decimals = (0,_Utils_ChartStyle__WEBPACK_IMPORTED_MODULE_2__.decimalsOf)(this.yAxis.step);\n const unit = this.commonUnit();\n const right = this.getDrawAreaRect().x + this.getDrawAreaRect().w - 3;\n ctx.save();\n ctx.font = this.getThemeValue('font');\n ctx.textAlign = 'right';\n ctx.textBaseline = 'middle';\n this.yAxis.ticks.map((tick) => {\n const y = this.toScreenY(this.valueToY(tick));\n const text = (0,_Utils_ChartStyle__WEBPACK_IMPORTED_MODULE_2__.formatValue)(tick, decimals) + (unit ? ' ' + unit : '');\n const width = ctx.measureText(text).width;\n // a backdrop keeps the label readable where the line runs through it\n ctx.fillStyle = (0,_Utils_ChartStyle__WEBPACK_IMPORTED_MODULE_2__.withAlpha)(this.getThemeValue('tooltipBackground'), .55);\n ctx.fillRect(right - width - 3, y - 7, width + 5, 14);\n ctx.fillStyle = this.getThemeValue('textColor');\n ctx.fillText(text, right, y);\n });\n ctx.restore();\n }\n drawHover() {\n this.publishTooltipRect(null);\n if (!this.getProperty('showTooltip', true))\n return;\n const index = this.findHoverIndex();\n if (index !== this.hoverIndex) {\n this.hoverIndex = index;\n _Event_TooltipEvent__WEBPACK_IMPORTED_MODULE_1__[\"default\"].dispatch('hover', { index, points: index === -1 ? [] : this.pointsAt(index) });\n }\n if (index === -1)\n return;\n const points = this.pointsAt(index);\n const ctx = this.ctx;\n const x = Math.round(this.toScreenX(this.indexToX(index))) + .5;\n const top = this.toScreenY(this.plotRect.y);\n const bottom = this.toScreenY(this.plotRect.y + this.plotRect.h);\n ctx.save();\n ctx.strokeStyle = (0,_Utils_ChartStyle__WEBPACK_IMPORTED_MODULE_2__.withAlpha)(this.getThemeValue('textColor'), .6);\n ctx.lineWidth = 1;\n if (typeof ctx.setLineDash === 'function') {\n ctx.setLineDash([3, 3]);\n }\n ctx.beginPath();\n ctx.moveTo(x, top);\n ctx.lineTo(x, bottom);\n ctx.stroke();\n if (typeof ctx.setLineDash === 'function') {\n ctx.setLineDash([]);\n }\n points.map(({ point }) => {\n if (!point)\n return;\n ctx.beginPath();\n ctx.arc(x, this.toScreenY(point.y), this.bulletPointRadiusHover, 0, 2 * Math.PI);\n ctx.fillStyle = point.color;\n ctx.fill();\n ctx.lineWidth = 2;\n ctx.strokeStyle = this.getThemeValue('tooltipBackground');\n ctx.stroke();\n });\n this.drawTooltip(x, points);\n ctx.restore();\n }\n drawTooltip(x, points) {\n const ctx = this.ctx;\n const decimals = this.getProperty('decimals', 2);\n const single = points.length === 1;\n const header = points.map(({ point }) => point && point.hasLabel ? point.label : '').find((label) => label !== '')\n ?? (single ? points[0].group.label : '');\n const rows = points.map(({ group, point }) => {\n const value = point ? (0,_Utils_ChartStyle__WEBPACK_IMPORTED_MODULE_2__.formatValue)(point.value, decimals) + (group.unit ? ' ' + group.unit : '') : '–';\n return { color: group.color, text: single ? value : group.label + ': ' + value };\n });\n const paddingX = 7;\n const paddingY = 5;\n const rowHeight = 14;\n const swatch = 8;\n ctx.font = this.getThemeValue('font');\n const headerWidth = header ? ctx.measureText(header).width : 0;\n const rowsWidth = Math.max(...rows.map((row) => ctx.measureText(row.text).width + swatch + 5));\n const width = Math.ceil(Math.max(headerWidth, rowsWidth) + paddingX * 2);\n const height = (rows.length + (header ? 1 : 0)) * rowHeight + paddingY * 2;\n const area = this.getDrawAreaRect();\n let boxX = x + 10;\n if (boxX + width > area.x + area.w - 2) {\n boxX = x - 10 - width;\n }\n boxX = Math.max(area.x + 2, boxX);\n const boxY = Math.max(area.y + 2, Math.min(this.toScreenY(this.plotRect.y), area.y + area.h - height - 2));\n ctx.fillStyle = this.getThemeValue('tooltipBackground');\n ctx.strokeStyle = this.getThemeValue('tooltipBorder');\n ctx.lineWidth = 1;\n ctx.beginPath();\n if (typeof ctx.roundRect === 'function') {\n ctx.roundRect(boxX + .5, boxY + .5, width, height, 4);\n }\n else {\n ctx.rect(boxX + .5, boxY + .5, width, height);\n }\n ctx.fill();\n ctx.stroke();\n this.publishTooltipRect({ x: boxX, y: boxY, width, height });\n ctx.textAlign = 'left';\n ctx.textBaseline = 'middle';\n let rowY = boxY + paddingY + rowHeight / 2;\n if (header) {\n ctx.fillStyle = (0,_Utils_ChartStyle__WEBPACK_IMPORTED_MODULE_2__.withAlpha)(this.getThemeValue('tooltipTextColor'), .7);\n ctx.fillText(header, boxX + paddingX, rowY);\n rowY += rowHeight;\n }\n rows.map((row) => {\n ctx.fillStyle = row.color;\n ctx.fillRect(boxX + paddingX, rowY - swatch / 2, swatch, swatch);\n ctx.fillStyle = this.getThemeValue('tooltipTextColor');\n ctx.fillText(row.text, boxX + paddingX + swatch + 5, rowY);\n rowY += rowHeight;\n });\n }\n /**\n * Tells the other plugins of the chart (e.g. the legend) where the tooltip\n * is drawn in this frame - `null` when there is none.\n */\n publishTooltipRect(rect) {\n if (!this._entityRegistry)\n return;\n this._entityRegistry.addEntity(DiagramLine.TOOLTIP_ENTITY_SOURCE, {\n ...(rect ?? { x: 0, y: 0, width: 0, height: 0 }),\n data: { visible: rect !== null },\n source: this.getName(),\n }, this._tooltipEntityId);\n }\n /**\n * The index of the value next to the pointer, -1 if the pointer is not\n * over the chart.\n */\n findHoverIndex() {\n const { x, y } = this.hoverPosition;\n if (x < 0 || y < 0 || this.maxDataLength === 0) {\n return -1;\n }\n const bufferX = this.toBufferX(x);\n if (this.maxDataLength < 2) {\n return 0;\n }\n const index = Math.round((bufferX - this.indexToX(0)) / this.pointGap);\n const clamped = Math.max(0, Math.min(this.maxDataLength - 1, index));\n return this.pointsAt(clamped).some(({ point }) => point !== undefined) ? clamped : -1;\n }\n pointsAt(index) {\n return this._dataGroups.map((group, groupIndex) => ({\n group,\n point: (this.dataPoints[groupIndex] || []).find((point) => point.index === index),\n }));\n }\n commonUnit() {\n const units = new Set(this._dataGroups.map((group) => group.unit || ''));\n return units.size === 1 ? [...units][0] : '';\n }\n /*\n * The buffer is drawn shifted by the camera and scaled into the padded\n * draw area (see AbstractPlugin.draw); these convert between both.\n */\n toScreenX(bufferX) {\n const area = this.getDrawAreaRect();\n const padding = this.getProperty('padding', 0);\n return area.x + padding + (bufferX - this._camera.x) * (area.w - 2 * padding) / area.w;\n }\n toScreenY(bufferY) {\n const area = this.getDrawAreaRect();\n const padding = this.getProperty('padding', 0);\n return area.y + padding + (bufferY - this._camera.y) * (area.h - 2 * padding) / area.h;\n }\n toBufferX(hoverX) {\n // hover positions already contain the camera offset\n const area = this.getDrawAreaRect();\n const padding = this.getProperty('padding', 0);\n return (hoverX - this._camera.x - area.x - padding) * area.w / (area.w - 2 * padding) + this._camera.x;\n }\n}\n\n\n//# sourceURL=webpack://@cehlers88/chart/./src/plugin/DiagramLine.ts?\n}");
271
282
 
272
283
  /***/ },
273
284
 
@@ -278,7 +289,7 @@ eval("{__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpa
278
289
  (__unused_webpack_module, __webpack_exports__, __webpack_require__) {
279
290
 
280
291
  "use strict";
281
- eval("{__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ \"default\": () => (/* binding */ EnrichGroupInfosPlugin)\n/* harmony export */ });\n/* harmony import */ var _AbstractPlugin__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./AbstractPlugin */ \"./src/plugin/AbstractPlugin.ts\");\n/**\n * Library EnrichGroupInfosPlugin\n * Created 05.05,24\n *\n * @author Christoph Ehlers\n */\n\nclass EnrichGroupInfosPlugin extends _AbstractPlugin__WEBPACK_IMPORTED_MODULE_0__[\"default\"] {\n constructor() {\n super();\n this._priority = 1;\n }\n updateDataGroups(dataGroups) {\n if (JSON.stringify(this._dataGroups) === JSON.stringify(dataGroups))\n return { dataGroups };\n console.log('EnrichGroupInfosPlugin:', dataGroups);\n this._dataGroups = dataGroups.map((dataGroup) => {\n const values = dataGroup.data.map((data) => data.value);\n const valueStarts = dataGroup.data.map((data) => data.valueStart ?? data.value);\n const min = Math.min(...valueStarts);\n const max = Math.max(...values);\n const range = max - min;\n return {\n ...dataGroup,\n infos: {\n ...dataGroup.infos,\n largestDecimalStep: Math.max(this.getLargestDecimalStep(Math.max(...values)), 0),\n max,\n min,\n range,\n valuePercentage: values.map((value) => (value - min) / (max - min) * 100)\n }\n };\n });\n console.log('EnrichGroupInfosPlugin:', this._dataGroups);\n return {\n dataGroups: this._dataGroups\n };\n }\n getLargestDecimalStep(x) {\n return Math.pow(10, Math.floor(Math.log10(x)));\n }\n getName() {\n return \"EnrichGroupInfosPlugin\";\n }\n}\n\n\n//# sourceURL=webpack://@cehlers88/chart/./src/plugin/EnrichGroupInfosPlugin.ts?\n}");
292
+ eval("{__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ \"default\": () => (/* binding */ EnrichGroupInfosPlugin)\n/* harmony export */ });\n/* harmony import */ var _AbstractPlugin__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./AbstractPlugin */ \"./src/plugin/AbstractPlugin.ts\");\n/**\n * Library EnrichGroupInfosPlugin\n * Created 05.05,24\n *\n * @author Christoph Ehlers\n */\n\nclass EnrichGroupInfosPlugin extends _AbstractPlugin__WEBPACK_IMPORTED_MODULE_0__[\"default\"] {\n constructor() {\n super();\n this._priority = 1;\n }\n updateDataGroups(dataGroups) {\n if (!this._dataGroupsChanged)\n return { dataGroups: this._dataGroups };\n this._dataGroups = dataGroups.map((dataGroup) => {\n const values = dataGroup.data.map((data) => data.value);\n // Empty values (gaps) must not pull the range down to 0.\n const filled = dataGroup.data.filter((data) => !data.empty && isFinite(Number(data.value)));\n const filledValues = filled.map((data) => Number(data.value));\n const valueStarts = filled.map((data) => Number(data.valueStart ?? data.value));\n // A group may be empty (e.g. right after clearing or before the\n // first value was added), so the infos have to stay finite.\n const min = filled.length === 0 ? 0 : Math.min(...valueStarts);\n const max = filled.length === 0 ? 0 : Math.max(...filledValues);\n const range = max - min;\n return {\n ...dataGroup,\n infos: {\n ...dataGroup.infos,\n largestDecimalStep: Math.max(this.getLargestDecimalStep(max), 0),\n max,\n min,\n // the smallest value itself - `min` includes the start of ranges\n minValue: filled.length === 0 ? 0 : Math.min(...filledValues),\n range,\n valuePercentage: values.map((value) => range === 0 ? 0 : (value - min) / range * 100)\n }\n };\n });\n return {\n dataGroups: this._dataGroups\n };\n }\n getLargestDecimalStep(x) {\n if (x <= 0) {\n return 0;\n }\n return Math.pow(10, Math.floor(Math.log10(x)));\n }\n getName() {\n return \"EnrichGroupInfosPlugin\";\n }\n}\n\n\n//# sourceURL=webpack://@cehlers88/chart/./src/plugin/EnrichGroupInfosPlugin.ts?\n}");
282
293
 
283
294
  /***/ },
284
295
 
@@ -289,7 +300,7 @@ eval("{__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpa
289
300
  (__unused_webpack_module, __webpack_exports__, __webpack_require__) {
290
301
 
291
302
  "use strict";
292
- eval("{__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ ERangeType: () => (/* binding */ ERangeType),\n/* harmony export */ \"default\": () => (/* binding */ LegendsPlugin)\n/* harmony export */ });\n/* harmony import */ var _AbstractPlugin__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./AbstractPlugin */ \"./src/plugin/AbstractPlugin.ts\");\n/* harmony import */ var _cehlers88_ceutils__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! @cehlers88/ceutils */ \"./node_modules/@cehlers88/ceutils/dist/index.js\");\n/* harmony import */ var _cehlers88_ceutils__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(_cehlers88_ceutils__WEBPACK_IMPORTED_MODULE_1__);\n/**\n * Library LegendsPlugin\n * Created 30.09.24\n * Last modified 05.06.26\n *\n * @author Christoph Ehlers\n */\n\n\nvar ERangeType;\n(function (ERangeType) {\n ERangeType[\"ZERO_BASED\"] = \"zero_based\";\n ERangeType[\"MIN_MAX\"] = \"min_max\";\n})(ERangeType || (ERangeType = {}));\nclass LegendsPlugin extends _AbstractPlugin__WEBPACK_IMPORTED_MODULE_0__[\"default\"] {\n static NAME = \"LegendsPlugin\";\n constructor(props = {}) {\n super({\n x: 5,\n y: 5,\n labelBoxHeight: 10,\n labelBoxWidth: 10,\n ...props\n });\n this._priority = 100;\n this._fixed = true;\n }\n drawBuffer() {\n const textBuffer = _cehlers88_ceutils__WEBPACK_IMPORTED_MODULE_1__.domLib.createElement('canvas', { width: this.getDrawAreaRect().w.toString(), height: this.getDrawAreaRect().h.toString() });\n const textBufferCtx = textBuffer.getContext('2d');\n let neededHeight = 0;\n let neededWidth = 0;\n const labelBoxHeight = this.getProperty('labelBoxHeight', 10);\n const labelBoxWidth = this.getProperty('labelBoxWidth', 10);\n this._dataGroups.map((dataGroup, index) => {\n textBufferCtx.fillStyle = dataGroup.color;\n textBufferCtx.fillRect(this.getDrawAreaRect().x + 3, (this.getDrawAreaRect().y + index * 15) + 1, labelBoxWidth, labelBoxHeight);\n textBufferCtx.fillStyle = '#000000';\n textBufferCtx.fillText(dataGroup.label, this.getDrawAreaRect().x + labelBoxWidth + 5, (this.getDrawAreaRect().y + labelBoxHeight + index * 15) - 1);\n neededWidth = Math.max(neededWidth, 20 + textBufferCtx.measureText(dataGroup.label).width);\n neededHeight += 15;\n });\n this.bufferCtx.fillStyle = '#ffffff44';\n this.bufferCtx.clearRect(this.getDrawAreaRect().x, this.getDrawAreaRect().y, neededWidth, neededHeight - 3);\n this.bufferCtx.fillRect(this.getDrawAreaRect().x, this.getDrawAreaRect().y, neededWidth, neededHeight - 3);\n this.bufferCtx.drawImage(textBuffer, 0, 0);\n super.drawBuffer();\n }\n getName() {\n return LegendsPlugin.NAME;\n }\n}\n\n\n//# sourceURL=webpack://@cehlers88/chart/./src/plugin/LegendsPlugin.ts?\n}");
303
+ eval("{__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ ERangeType: () => (/* binding */ ERangeType),\n/* harmony export */ \"default\": () => (/* binding */ LegendsPlugin)\n/* harmony export */ });\n/* harmony import */ var _AbstractPlugin__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./AbstractPlugin */ \"./src/plugin/AbstractPlugin.ts\");\n/* harmony import */ var _Utils_ChartStyle__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../Utils/ChartStyle */ \"./src/Utils/ChartStyle.ts\");\n/**\n * Library LegendsPlugin\n * Created 30.09.24\n * Last modified 29.09.26\n *\n * @author Christoph Ehlers\n */\n\n\nvar ERangeType;\n(function (ERangeType) {\n ERangeType[\"ZERO_BASED\"] = \"zero_based\";\n ERangeType[\"MIN_MAX\"] = \"min_max\";\n})(ERangeType || (ERangeType = {}));\nclass LegendsPlugin extends _AbstractPlugin__WEBPACK_IMPORTED_MODULE_0__[\"default\"] {\n static NAME = \"LegendsPlugin\";\n _legendRect = null;\n constructor(props = {}) {\n super({\n x: 5,\n y: 5,\n hideOnHover: true,\n labelBoxHeight: 8,\n labelBoxWidth: 8,\n showSingleGroup: false,\n ...props\n });\n this._priority = 100;\n this._fixed = true;\n }\n /**\n * The groups the legend lists. A single group needs no legend - the\n * tooltip and the surrounding page name it already.\n */\n getVisibleGroups() {\n if (this._dataGroups.length < 2 && !this.getProperty('showSingleGroup', false)) {\n return [];\n }\n return this._dataGroups;\n }\n drawBuffer() {\n const groups = this.getVisibleGroups();\n this._legendRect = null;\n if (groups.length === 0) {\n super.drawBuffer();\n return;\n }\n const ctx = this.bufferCtx;\n const area = this.getDrawAreaRect();\n const labelBoxHeight = this.getProperty('labelBoxHeight', 8);\n const labelBoxWidth = this.getProperty('labelBoxWidth', 8);\n const rowHeight = 14;\n const gap = 10;\n const padding = 5;\n const maxWidth = area.w - area.x - 2 * padding;\n ctx.font = this.getThemeValue('font');\n // the entries are laid out side by side and wrap into a new row when\n // the canvas is too narrow\n const entries = [];\n let x = 0;\n let row = 0;\n let neededWidth = 0;\n groups.map((group) => {\n const width = labelBoxWidth + 4 + ctx.measureText(group.label).width;\n if (x > 0 && x + width > maxWidth) {\n x = 0;\n row++;\n }\n entries.push({ group, x, row });\n x += width + gap;\n neededWidth = Math.max(neededWidth, x - gap);\n });\n const neededHeight = (row + 1) * rowHeight;\n this._legendRect = { x: area.x, y: area.y, width: neededWidth + 2 * padding, height: neededHeight + padding };\n ctx.fillStyle = (0,_Utils_ChartStyle__WEBPACK_IMPORTED_MODULE_1__.withAlpha)(this.getThemeValue('tooltipBackground'), .75);\n ctx.beginPath();\n if (typeof ctx.roundRect === 'function') {\n ctx.roundRect(area.x, area.y, neededWidth + 2 * padding, neededHeight + padding, 4);\n }\n else {\n ctx.rect(area.x, area.y, neededWidth + 2 * padding, neededHeight + padding);\n }\n ctx.fill();\n ctx.textAlign = 'left';\n ctx.textBaseline = 'middle';\n entries.map(({ group, x, row }) => {\n const centerY = area.y + padding / 2 + row * rowHeight + rowHeight / 2;\n ctx.fillStyle = group.color;\n ctx.fillRect(area.x + padding + x, centerY - labelBoxHeight / 2, labelBoxWidth, labelBoxHeight);\n ctx.fillStyle = this.getThemeValue('tooltipTextColor');\n ctx.fillText(group.label, area.x + padding + x + labelBoxWidth + 4, centerY);\n });\n super.drawBuffer();\n }\n draw(forceRedraw = false) {\n if (this.getProperty('hideOnHover', true) && this.isCoveredByTooltip()) {\n // the tooltip lists the groups itself and must stay readable\n return this;\n }\n return super.draw(forceRedraw);\n }\n /**\n * Whether a tooltip published by another plugin (see\n * DiagramLine.TOOLTIP_ENTITY_SOURCE) overlaps the legend in this frame.\n */\n isCoveredByTooltip() {\n const legend = this._legendRect;\n if (!legend || !this._entityRegistry) {\n return false;\n }\n return this._entityRegistry.getEntities('tooltip').some((tooltip) => tooltip.data?.visible\n && tooltip.x < legend.x + legend.width && tooltip.x + tooltip.width > legend.x\n && tooltip.y < legend.y + legend.height && tooltip.y + tooltip.height > legend.y);\n }\n getName() {\n return LegendsPlugin.NAME;\n }\n}\n\n\n//# sourceURL=webpack://@cehlers88/chart/./src/plugin/LegendsPlugin.ts?\n}");
293
304
 
294
305
  /***/ },
295
306
 
@@ -1,6 +1,21 @@
1
1
  import { IChartPlugin } from "./IChartPlugin";
2
+ import { TDataGroup } from "../type/TDataGroup";
3
+ import { IChartEngine } from "./IChartEngine";
2
4
  export interface ICanvasDiagram {
3
5
  addPlugin(plugin: any): ICanvasDiagram;
6
+ addDataGroup(dataGroup: TDataGroup): ICanvasDiagram;
7
+ addValue(value: any, groupName?: string): ICanvasDiagram;
8
+ clearData(): ICanvasDiagram;
9
+ getDataGroup(group: string | number): TDataGroup | undefined;
10
+ getDataGroups(): TDataGroup[];
11
+ getEngine(): IChartEngine | undefined;
4
12
  getPlugin(pluginName: string): IChartPlugin | undefined;
13
+ removeDataGroup(group: string | number): ICanvasDiagram;
14
+ removeValue(group: string | number, dataIndex: number): ICanvasDiagram;
15
+ resize(width: number, height: number): ICanvasDiagram;
16
+ setData(dataDefinition: string | any[] | object): ICanvasDiagram;
17
+ setDataGroups(dataGroups: TDataGroup[]): ICanvasDiagram;
18
+ setValue(group: string | number, dataIndex: number, value: any): ICanvasDiagram;
5
19
  start(): ICanvasDiagram;
20
+ stop(): ICanvasDiagram;
6
21
  }
@@ -4,7 +4,16 @@ export interface IChartEngine {
4
4
  addValue(value: any, groupName?: string): IChartEngine;
5
5
  addDataGroup(dataGroup: TDataGroup): IChartEngine;
6
6
  addPlugin(plugin: IChartPlugin): IChartEngine;
7
+ clearData(): IChartEngine;
8
+ getDataGroup(group: string | number): TDataGroup | undefined;
9
+ getDataGroups(): TDataGroup[];
7
10
  getPlugin(pluginName: string): IChartPlugin;
11
+ removeDataGroup(group: string | number): IChartEngine;
12
+ removeValue(group: string | number, dataIndex: number): IChartEngine;
13
+ resize(width: number, height: number): IChartEngine;
14
+ setData(dataDefinition: string | any[] | object): IChartEngine;
15
+ setDataGroups(dataGroups: TDataGroup[]): IChartEngine;
16
+ setValue(group: string | number, dataIndex: number, value: any): IChartEngine;
8
17
  draw(): IChartEngine;
9
18
  update(): IChartEngine;
10
19
  }
@@ -20,6 +20,8 @@ export interface IChartPlugin {
20
20
  handleHover(x: number, y: number): IChartPlugin;
21
21
  handleLeave(): IChartPlugin;
22
22
  handleScroll(x: number, y: number): IChartPlugin;
23
+ invalidate?(): IChartPlugin;
24
+ resize?(canvasElement: HTMLCanvasElement): IChartPlugin;
23
25
  setCamera(camera: any): IChartPlugin;
24
26
  setDrawAreaRect(rect: TRect): IChartPlugin;
25
27
  setEntityRegistry(entityRegistry: any): IChartPlugin;
@@ -6,8 +6,6 @@ export default abstract class AbstractDiagramPlugin extends AbstractPlugin {
6
6
  viewSize?: number;
7
7
  [key: string]: any;
8
8
  });
9
- update(dataGroups: TDataGroup[]): {
10
- dataGroups: TDataGroup[];
11
- };
12
- protected updateBufferSize(dataGroups: any): number;
9
+ protected onDataGroupsChanged(dataGroups: TDataGroup[]): void;
10
+ protected updateBufferSize(dataGroups: TDataGroup[]): number;
13
11
  }
@@ -10,6 +10,7 @@ import { TRect } from "../type/TRect";
10
10
  import Camera from "../Camera";
11
11
  import EntityRegistry from "../Registry/EntityRegistry";
12
12
  import { IChartEntity } from "../interface/IChartEntity";
13
+ import { TChartTheme } from "../Utils/ChartStyle";
13
14
  export default abstract class AbstractPlugin implements IChartPlugin {
14
15
  protected _camera: Camera;
15
16
  protected _dataGroups: TDataGroup[];
@@ -19,7 +20,10 @@ export default abstract class AbstractPlugin implements IChartPlugin {
19
20
  protected bufferCtx: CanvasRenderingContext2D;
20
21
  protected ctx: CanvasRenderingContext2D;
21
22
  protected redrawBuffer: boolean;
23
+ protected _dataGroupsChanged: boolean;
24
+ private _theme;
22
25
  private _buffer;
26
+ private _dataGroupsSignature;
23
27
  private _stateChangedListener;
24
28
  private _drawAreaRect;
25
29
  private _properties;
@@ -34,6 +38,18 @@ export default abstract class AbstractPlugin implements IChartPlugin {
34
38
  update(dataGroups: TDataGroup[]): {
35
39
  dataGroups: TDataGroup[];
36
40
  };
41
+ /**
42
+ * Hook for plugins that have to prepare themselves (e.g. resize their
43
+ * buffer) before the changed data is processed.
44
+ */
45
+ protected onDataGroupsChanged(dataGroups: TDataGroup[]): void;
46
+ /**
47
+ * Compares the given data groups by content against the ones seen by the
48
+ * last update and remembers the result in `_dataGroupsChanged`. Comparing
49
+ * by content (instead of holding on to the data groups themselves) is what
50
+ * makes manipulations of already known data groups visible.
51
+ */
52
+ private detectDataGroupsChange;
37
53
  updateDataGroups(dataGroups: TDataGroup[]): {
38
54
  dataGroups: TDataGroup[];
39
55
  };
@@ -42,6 +58,22 @@ export default abstract class AbstractPlugin implements IChartPlugin {
42
58
  dataGroups: TDataGroup[];
43
59
  plugin: AbstractPlugin;
44
60
  };
61
+ /**
62
+ * Adapts the plugin to a new size of the canvas. Unlike `create()` this
63
+ * has no side effects besides rebuilding the buffer, so it can be called
64
+ * as often as the canvas changes its size.
65
+ */
66
+ resize(canvasElement: HTMLCanvasElement): AbstractPlugin;
67
+ /**
68
+ * Forces the plugin to process its data and to redraw its buffer with the
69
+ * next frame, even if the data did not change.
70
+ */
71
+ invalidate(): AbstractPlugin;
72
+ /**
73
+ * Returns a colour/font of the theme. A plugin property of the same name
74
+ * (e.g. `textColor`) wins over the `--chart-*` CSS properties of the canvas.
75
+ */
76
+ protected getThemeValue(key: keyof TChartTheme): string;
45
77
  drawBuffer(): void;
46
78
  draw(forceRedraw?: boolean): IChartPlugin;
47
79
  getDrawAreaRect(): TRect;