@cehlers88/chart 1.1.0 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,6 +1,262 @@
1
1
  # Chart
2
- A library to create charts using JavaScript and Canvas-Element.
3
2
 
4
- ## Examples
5
- ######`javascipt`
6
- const obj = new ChartEngine(element);
3
+ A lightweight TypeScript library for building charts on top of the HTML `<canvas>` element. The library is fully plugin-based: axes, bars, legends, tooltips, camera navigation, pagination, and timelines are all separate, composable plugins instead of fixed chart types.
4
+
5
+ ## Features
6
+
7
+ - **Plugin architecture** – every visual or interactive feature (bars, lines, legend, tooltip, scroll/zoom, pagination, timeline) is a standalone plugin registered via `addPlugin()`.
8
+ - **Two usage styles** – control the chart directly through the `ChartEngine` class, or declaratively via the `<canvas-diagram>` custom element.
9
+ - **Flexible data formats** – values can be plain numbers, ranges (`[start, end]`), or objects with a label/color; `DataGroupFactory` normalizes them all.
10
+ - **Camera & interaction** – built-in support for hover, scroll/wheel, and click events per plugin.
11
+ - **TypeScript** – fully typed, ships its own `.d.ts` declarations.
12
+
13
+ ## Installation
14
+
15
+ ```bash
16
+ npm install @cehlers88/chart
17
+ ```
18
+
19
+ ## Usage
20
+
21
+ ### Option 1: `ChartEngine` (programmatic)
22
+
23
+ ```typescript
24
+ import { ChartEngine, DataGroupFactory, plugins } from "@cehlers88/chart";
25
+
26
+ const canvas = document.querySelector("canvas");
27
+
28
+ const engine = new ChartEngine(canvas, {
29
+ plugins: [
30
+ new plugins.DiagramLinePlugin(),
31
+ new plugins.LegendsPlugin(),
32
+ new plugins.CameraMovePlugin({ allowScrollX: true }),
33
+ ],
34
+ });
35
+
36
+ engine.addDataGroup(
37
+ DataGroupFactory.createDataGroup([1, 5, 3, 8, 2], "Revenue", "#1d91e3")
38
+ );
39
+
40
+ engine.start(); // starts the render/update loop (requestAnimationFrame)
41
+ ```
42
+
43
+ ### Option 2: Custom element `<canvas-diagram>`
44
+
45
+ The custom element registers itself automatically as soon as the library is imported.
46
+
47
+ ```html
48
+ <script type="module">
49
+ import "@cehlers88/chart";
50
+ </script>
51
+
52
+ <canvas-diagram
53
+ width="600"
54
+ height="300"
55
+ value='{"Revenue":[1,5,3,8,2],"Costs":{"data":[2,3,1,4,2],"color":"#e84949"}}'
56
+ ></canvas-diagram>
57
+ ```
58
+
59
+ Attributes:
60
+
61
+ | Attribute | Description | Default |
62
+ |-------------|----------------------------------------------------------------------|---------|
63
+ | `width` | Canvas width in pixels; `100%`/`auto` makes the diagram responsive (it takes the width of its container and is redrawn sharply whenever that changes) | `300` |
64
+ | `height` | Canvas height in pixels | `150` |
65
+ | `value` | JSON string with the data groups (see data format below) | `[]` |
66
+ | `autostart` | Starts the chart automatically once mounted (`"true"`/`"false"`) | `true` |
67
+
68
+ Additional plugins can be registered on the element before it starts via `getPlugin()`/`addPlugin()`.
69
+
70
+ ## Changing data at runtime
71
+
72
+ The data of a chart is not frozen after the page was loaded – it can be read,
73
+ replaced and manipulated at any time, either through the `<canvas-diagram>`
74
+ element or through the `ChartEngine`. A running chart picks the changes up with
75
+ the next frame, no restart needed.
76
+
77
+ ```html
78
+ <canvas-diagram id="sales" width="600" height="300"
79
+ value='{"Revenue":[1,5,3,8,2]}'></canvas-diagram>
80
+
81
+ <script type="module">
82
+ import { DataGroupFactory, diagramRegistry } from "@cehlers88/chart";
83
+
84
+ const diagram = document.getElementById("sales");
85
+ // ... or, without a dom reference:
86
+ // const diagram = diagramRegistry.getDiagram("sales");
87
+
88
+ diagram.addValue(6, "Revenue"); // append a value
89
+ diagram.setValue("Revenue", 0, 4); // replace a single value
90
+ diagram.removeValue("Revenue", 1); // remove a single value
91
+ diagram.addDataGroup( // add a whole group
92
+ DataGroupFactory.createDataGroup([2, 3, 1], "Costs", "#e84949")
93
+ );
94
+ diagram.removeDataGroup("Costs"); // remove a whole group
95
+ diagram.setData({ Revenue: [1, 2, 3] }); // replace everything
96
+ diagram.clearData(); // remove everything
97
+
98
+ console.log(diagram.getDataGroups()); // read the current data
99
+ </script>
100
+ ```
101
+
102
+ Setting the `value` attribute again works as well and replaces the complete data:
103
+
104
+ ```javascript
105
+ document.getElementById("sales").setAttribute("value", '{"Revenue":[3,4,5]}');
106
+ ```
107
+
108
+ Methods of `<canvas-diagram>`:
109
+
110
+ | Method | Description |
111
+ |-----------------------------------------|--------------------------------------------------------------------------|
112
+ | `setData(definition)` | Replaces all data. Accepts a JSON string, an array or an object |
113
+ | `setDataGroups(dataGroups)` | Replaces all data with ready-made `TDataGroup`s |
114
+ | `getDataGroups()` | Returns the current data groups |
115
+ | `getDataGroup(labelOrIndex)` | Returns a single data group |
116
+ | `addDataGroup(dataGroup)` | Adds a data group |
117
+ | `removeDataGroup(labelOrIndex)` | Removes a data group |
118
+ | `addValue(value, groupName?)` | Appends a value; creates the group if it does not exist yet |
119
+ | `setValue(labelOrIndex, index, value)` | Replaces a single value; missing values in between are filled up empty |
120
+ | `removeValue(labelOrIndex, index)` | Removes a single value |
121
+ | `clearData()` | Removes all data groups |
122
+ | `addPlugin(plugin)` / `getPlugin(name)` | Registers / returns a plugin – also while the chart is already running |
123
+ | `start()` / `stop()` | Starts / stops the render loop |
124
+ | `getEngine()` | Returns the underlying `ChartEngine` |
125
+
126
+ The same data methods exist on `ChartEngine`, so the programmatic usage works
127
+ identically:
128
+
129
+ ```typescript
130
+ const engine = new ChartEngine(canvas, { plugins: [new plugins.BarsPlugin()] });
131
+
132
+ engine.setData({ Revenue: [1, 5, 3] });
133
+ engine.start();
134
+
135
+ setInterval(() => engine.addValue(Math.random() * 10, "Revenue"), 1000);
136
+ ```
137
+
138
+ Data methods may be called before the element is mounted as well; those calls
139
+ are applied as soon as the element is connected (and take precedence over the
140
+ `value` attribute).
141
+
142
+ ### Live data
143
+
144
+ For values that arrive after the page was loaded (ajax, Mercure, websockets, …) give the
145
+ group a `maxLength` and append the new values with their timestamp. The oldest values are
146
+ pushed out, so the chart keeps its time window, and the new value is labelled like the
147
+ initial ones:
148
+
149
+ ```html
150
+ <canvas-diagram id="co2" width="100%" height="110"
151
+ value='{"CO2":{"data":[450,470],"timestamps":[1790000000,1790000600],"unit":"ppm","maxLength":2}}'></canvas-diagram>
152
+
153
+ <script type="module">
154
+ import { diagramRegistry } from "@cehlers88/chart";
155
+
156
+ // without a group name the value goes to the only group of the chart
157
+ diagramRegistry.getDiagram("co2").addValue({ value: 480, timestamp: 1790001200 });
158
+ </script>
159
+ ```
160
+
161
+ ## Data format
162
+
163
+ `DataGroupFactory` accepts several notations for the values within a group:
164
+
165
+ ```typescript
166
+ // plain numbers
167
+ DataGroupFactory.createDataGroup([1, 2, 3]);
168
+
169
+ // ranges [start, end]
170
+ DataGroupFactory.createDataGroup([[0, 10], [5, 12]]);
171
+
172
+ // objects with a label
173
+ DataGroupFactory.createDataGroup([{ value: 5, label: "May" }]);
174
+ ```
175
+
176
+ As a JSON string (e.g. for the custom element's `value` attribute), groups are given as an object keyed by group name:
177
+
178
+ ```json
179
+ {
180
+ "Group1": [1, 2, 3],
181
+ "Group2": { "data": [4, 5, 6], "color": "#30cb30" }
182
+ }
183
+ ```
184
+
185
+ The object notation takes a few optional keys more:
186
+
187
+ | Key | Description |
188
+ |--------------|-----------------------------------------------------------------------------------------------|
189
+ | `unit` | Unit of the values, shown in the tooltip and on the value axis (`"°C"`, `"ppm"`) |
190
+ | `labels` | One label per value (e.g. the time of a measurement), shown in the tooltip and on the x axis |
191
+ | `timestamps` | One unix timestamp per value (seconds or milliseconds), turned into time labels; `0`/`null` means "no label". Values below 1e11 are read as seconds (up to the year 5138), larger ones as milliseconds (from 1973 on). Single values take theirs as `{ "value": 1, "timestamp": 1790000000 }` |
192
+ | `maxLength` | Maximum number of values; values appended later push the oldest ones out |
193
+
194
+ `null` values are gaps: the line is interrupted there instead of dropping to 0, and they
195
+ do not count for the value range.
196
+
197
+ ```json
198
+ { "CO2": { "data": [null, 450, 470], "timestamps": [0, 1790000000, 1790000600], "unit": "ppm" } }
199
+ ```
200
+
201
+ ## Look & theming
202
+
203
+ `DiagramLinePlugin` draws a smooth (monotone, never overshooting) line with a gradient
204
+ fill, a value axis with "nice" steps on the right and - if the values carry labels - a
205
+ label axis at the bottom. Hovering (or tapping) shows a crosshair and a tooltip with the
206
+ values next to the pointer. Options: `smooth`, `fill`, `lineWidth`, `showGrid`,
207
+ `showYLabels`, `showXLabels`, `showTooltip`, `showBulletPoints`, `showZeroLine`,
208
+ `maxYTicks`, `decimals`.
209
+
210
+ Colours are read from CSS custom properties of the canvas, so the host page adapts the
211
+ charts to its theme:
212
+
213
+ ```css
214
+ canvas-diagram {
215
+ --chart-text-color: #8a93a6;
216
+ --chart-grid-color: rgba(255, 255, 255, .07);
217
+ --chart-tooltip-background: rgba(12, 12, 12, .94);
218
+ --chart-tooltip-border: rgba(255, 255, 255, .12);
219
+ --chart-tooltip-text-color: #e4e4e4;
220
+ --chart-font: 11px system-ui, sans-serif;
221
+ --chart-background: transparent; /* background of the canvas */
222
+ --chart-border-radius: 0;
223
+ }
224
+ ```
225
+
226
+ A plugin property of the same name (`textColor`, `gridColor`, `tooltipBackground`,
227
+ `tooltipBorder`, `tooltipTextColor`, `font`) wins over the CSS property. Numbers and
228
+ times are formatted in the language of the page (`<html lang>`).
229
+
230
+ ## Available plugins
231
+
232
+ | Plugin | Purpose |
233
+ |--------------------------|---------------------------------------------------------------------|
234
+ | `BarsPlugin` | Renders data groups as bars |
235
+ | `DiagramLinePlugin` | Renders data groups as lines with value axis, crosshair and tooltip |
236
+ | `LegendsPlugin` | Displays a legend for the data groups (only for two or more groups unless `showSingleGroup`; steps aside while a tooltip covers it) |
237
+ | `TooltipPlugin` | Shows tooltips on hover over data points |
238
+ | `CameraMovePlugin` | Enables scrolling/panning the view via mouse/wheel |
239
+ | `PaginationPlugin` | Splits large data sets into pages |
240
+ | `TimelinePlugin` | Timeline rendering (hour/day/week/month/year) |
241
+ | `EnrichGroupInfosPlugin` | Enriches data groups with extra internal info (enabled by default) |
242
+
243
+ Custom plugins can be implemented by extending `AbstractPlugin` or `AbstractDiagramPlugin` (for plugins that need their own drawing surface/buffer); the corresponding interface is `IChartPlugin`.
244
+
245
+ ## Development
246
+
247
+ ```bash
248
+ npm install # install dependencies
249
+ npm run build # production build via webpack into dist/
250
+ npm run watch # build in watch mode
251
+ npm test # run tests with Jest
252
+ ```
253
+
254
+ ## Release / Deployment
255
+
256
+ Pushes/merges to `main` trigger the `.github/workflows/publish.yml` GitHub Actions workflow, which installs dependencies, runs the tests and the build, and publishes the package to npm if `package.json`'s `version` differs from the version currently published on npm. To cut a release, bump `version` in `package.json` before merging to `main`.
257
+
258
+ This requires an `NPM_TOKEN` repository secret (an npm automation token with publish rights for `@cehlers88/chart`).
259
+
260
+ ## License
261
+
262
+ ISC © Christoph Ehlers
@@ -14,21 +14,75 @@ export default class ChartEngine implements IChartEngine {
14
14
  private _entityRegistry;
15
15
  private _ctx;
16
16
  private _dataGroups;
17
+ private _dataGroupsSignature;
18
+ private _processedDataGroups;
17
19
  private _plugins;
18
20
  private _pluginProperties;
21
+ private _created;
22
+ private _runStackEntry;
19
23
  constructor(canvasElement: HTMLCanvasElement, properties?: {
20
24
  plugins?: IChartPlugin[];
21
25
  });
22
- private getEngineCallbacks;
23
26
  addPlugin(plugin: IChartPlugin): ChartEngine;
24
27
  getDrawAreaRect(): TRect;
25
- getPlugin(pluginName: string): IChartPlugin;
28
+ getPlugin(pluginName: string): IChartPlugin | undefined;
26
29
  getPlugins(): IChartPlugin[];
27
30
  setPluginProperty(propertyName: string, value: any): ChartEngine;
28
31
  create(): ChartEngine;
29
32
  addDataGroup(dataGroup: TDataGroup): IChartEngine;
33
+ /**
34
+ * Returns the data groups currently held by the engine.
35
+ * The returned groups are the live objects; changes made to them are
36
+ * picked up with the next update cycle.
37
+ */
38
+ getDataGroups(): TDataGroup[];
39
+ /**
40
+ * Returns a single data group, addressed either by its label or by its index.
41
+ */
42
+ getDataGroup(group: string | number): TDataGroup | undefined;
43
+ /**
44
+ * Replaces all data groups at once.
45
+ */
46
+ setDataGroups(dataGroups: TDataGroup[]): IChartEngine;
47
+ /**
48
+ * Replaces the complete data of the chart. Accepts everything
49
+ * `DataGroupFactory.parseDataGroupsDefinition` understands: a JSON string,
50
+ * a plain array of values or an object keyed by group name.
51
+ */
52
+ setData(dataDefinition: string | any[] | object): IChartEngine;
53
+ /**
54
+ * Removes a data group, addressed either by its label or by its index.
55
+ */
56
+ removeDataGroup(group: string | number): IChartEngine;
57
+ /**
58
+ * Removes all data groups.
59
+ */
60
+ clearData(): IChartEngine;
30
61
  addValue(value: any, groupName?: string): IChartEngine;
62
+ /**
63
+ * Replaces a single value within a data group.
64
+ */
65
+ setValue(group: string | number, dataIndex: number, value: any): IChartEngine;
66
+ /**
67
+ * Removes a single value from a data group.
68
+ */
69
+ removeValue(group: string | number, dataIndex: number): IChartEngine;
31
70
  draw(): ChartEngine;
32
71
  update(): ChartEngine;
72
+ /**
73
+ * Returns the data groups handed to the plugins. They are a detached copy
74
+ * of the data owned by the engine and are renewed whenever that data
75
+ * changed. Without that separation the plugins would hold references to
76
+ * the very objects manipulated from outside, so neither they nor the
77
+ * engine could tell a manipulation from the state of the last frame.
78
+ */
79
+ private getPipelineDataGroups;
33
80
  start(): void;
81
+ /**
82
+ * Stops the render loop of this engine. The data stays untouched, so the
83
+ * chart can be started again later on.
84
+ */
85
+ stop(): ChartEngine;
86
+ isRunning(): boolean;
87
+ private getEngineCallbacks;
34
88
  }
@@ -0,0 +1,6 @@
1
+ export default class TooltipEvent extends Event {
2
+ reason: string;
3
+ data: any;
4
+ constructor(reason: string, data: any);
5
+ static dispatch(reason: string, data: any): void;
6
+ }
@@ -1,10 +1,23 @@
1
1
  import { TDataGroup } from "../type/TDataGroup";
2
+ import { TData } from "../type/TData";
2
3
  export default class DataGroupFactory {
4
+ static buildCounter: number;
3
5
  private static _colors;
4
6
  private static _lastColorIndex;
5
- static buildCounter: number;
7
+ /**
8
+ * Normalizes a single value definition into a TData object.
9
+ * Accepts plain numbers, ranges ([start, end]), null (empty value) and
10
+ * objects like {value, valueStart, label}.
11
+ */
12
+ static createDataValue(valueDef: any): TData;
6
13
  static createDataGroup(values: any[], groupLabel?: string, color?: string): TDataGroup;
7
- static parseDataGroupsDefinition(value: string): TDataGroup[];
14
+ /**
15
+ * Parses a data group definition. Beside the JSON notation used by the
16
+ * `value` attribute of `<canvas-diagram>` an already parsed object/array
17
+ * may be passed, so data can be handed over programmatically at runtime.
18
+ */
19
+ static parseDataGroupsDefinition(value: string | any[] | object): TDataGroup[];
20
+ static getNextColor(): string;
8
21
  static setColor(color: string, index: number): void;
9
22
  static setColors(colors: string[]): void;
10
23
  }
@@ -8,9 +8,17 @@ import CanvasDiagram from "../customElement/CanvasDiagram";
8
8
  import { ICanvasDiagram } from "../interface/ICanvasDiagram";
9
9
  declare class DiagramRegistry {
10
10
  private static _identPrefix;
11
+ private static _identCounter;
11
12
  private createUniqueIdentifier;
12
13
  getDiagram(identifier: string): ICanvasDiagram;
14
+ hasDiagram(identifier: string): boolean;
15
+ getDiagrams(): ICanvasDiagram[];
13
16
  register(diagram: CanvasDiagram, identifier?: string): DiagramRegistry;
17
+ /**
18
+ * Removes a diagram from the registry, either by its identifier or by the
19
+ * element itself, so removed diagrams are not kept alive by the registry.
20
+ */
21
+ unregister(diagram: CanvasDiagram | string): DiagramRegistry;
14
22
  }
15
23
  export default DiagramRegistry;
16
24
  export declare const diagramRegistry: DiagramRegistry;
@@ -7,13 +7,44 @@
7
7
  import '../style/CanvasDiagram.css';
8
8
  import { IChartPlugin } from "../interface/IChartPlugin";
9
9
  import { ICanvasDiagram } from "../interface/ICanvasDiagram";
10
+ import { TDataGroup } from "../type/TDataGroup";
11
+ import { IChartEngine } from "../interface/IChartEngine";
10
12
  declare class CanvasDiagram extends HTMLElement implements ICanvasDiagram {
11
13
  private _cleanDom;
12
14
  private _engine;
15
+ private _ignoreValueAttribute;
16
+ private _pendingActions;
17
+ static get observedAttributes(): string[];
13
18
  connectedCallback(): void;
19
+ disconnectedCallback(): void;
20
+ attributeChangedCallback(name: string, oldValue: string | null, newValue: string | null): void;
14
21
  addPlugin(plugin: IChartPlugin): ICanvasDiagram;
22
+ getPlugin(pluginName: string): IChartPlugin | undefined;
23
+ getEngine(): IChartEngine | undefined;
24
+ addDataGroup(dataGroup: TDataGroup): ICanvasDiagram;
25
+ addValue(value: any, groupName?: string): ICanvasDiagram;
26
+ clearData(): ICanvasDiagram;
27
+ getDataGroup(group: string | number): TDataGroup | undefined;
28
+ getDataGroups(): TDataGroup[];
29
+ removeDataGroup(group: string | number): ICanvasDiagram;
30
+ removeValue(group: string | number, dataIndex: number): ICanvasDiagram;
31
+ /**
32
+ * Replaces the complete data of the diagram, at any time - also long after
33
+ * the page was loaded.
34
+ */
35
+ setData(dataDefinition: string | any[] | object): ICanvasDiagram;
36
+ setDataGroups(dataGroups: TDataGroup[]): ICanvasDiagram;
37
+ setValue(group: string | number, dataIndex: number, value: any): ICanvasDiagram;
38
+ start(): ICanvasDiagram;
39
+ stop(): ICanvasDiagram;
40
+ /**
41
+ * Runs the given action against the engine. If the element is not mounted
42
+ * yet, the action is queued and executed as soon as the engine exists,
43
+ * so data can be set before as well as after the element was connected.
44
+ */
45
+ private withEngine;
46
+ private flushPendingActions;
15
47
  private createCanvas;
16
48
  private evalArguments;
17
- start(): ICanvasDiagram;
18
49
  }
19
50
  export default CanvasDiagram;
package/dist/index.d.ts CHANGED
@@ -9,31 +9,36 @@ import BarsPluginClass from "./plugin/BarsPlugin";
9
9
  import ChartEngineClass from './ChartEngine';
10
10
  import DataGroupFactoryClass from "./Factory/DataGroupFactory";
11
11
  import DiagramLine from "./plugin/DiagramLine";
12
- import DiagramRegistry from "./Registry/DiagramRegistry";
13
12
  import CameraMove from "./plugin/CameraMove";
14
13
  import EnrichGroupInfosPlugin from "./plugin/EnrichGroupInfosPlugin";
15
14
  import TimelinePlugin from "./plugin/TimelinePlugin";
15
+ import LegendsPlugin from "./plugin/LegendsPlugin";
16
+ import PaginationPlugin from "./plugin/PaginationPlugin";
16
17
  export declare const CanvasDiagram: typeof CanvasDiagramClass;
17
18
  export declare const ChartEngine: typeof ChartEngineClass;
18
19
  export declare const DataGroupFactory: typeof DataGroupFactoryClass;
19
- export declare const diagramRegistry: DiagramRegistry;
20
+ export declare const diagramRegistry: import("./Registry/DiagramRegistry").default;
20
21
  export declare const plugins: {
21
22
  BarsPlugin: typeof BarsPluginClass;
22
23
  CameraMovePlugin: typeof CameraMove;
23
24
  EnrichGroupInfosPlugin: typeof EnrichGroupInfosPlugin;
24
25
  DiagramLinePlugin: typeof DiagramLine;
26
+ LegendsPlugin: typeof LegendsPlugin;
27
+ PaginationPlugin: typeof PaginationPlugin;
25
28
  TimelinePlugin: typeof TimelinePlugin;
26
29
  };
27
30
  declare const _default: {
28
31
  CanvasDiagram: typeof CanvasDiagramClass;
29
32
  ChartEngine: typeof ChartEngineClass;
30
33
  DataGroupFactory: typeof DataGroupFactoryClass;
31
- diagramRegistry: DiagramRegistry;
34
+ diagramRegistry: import("./Registry/DiagramRegistry").default;
32
35
  plugins: {
33
36
  BarsPlugin: typeof BarsPluginClass;
34
37
  CameraMovePlugin: typeof CameraMove;
35
38
  EnrichGroupInfosPlugin: typeof EnrichGroupInfosPlugin;
36
39
  DiagramLinePlugin: typeof DiagramLine;
40
+ LegendsPlugin: typeof LegendsPlugin;
41
+ PaginationPlugin: typeof PaginationPlugin;
37
42
  TimelinePlugin: typeof TimelinePlugin;
38
43
  };
39
44
  };
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// 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,18 @@ 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, e.offsetY));\n });\n this._canvasElement.addEventListener('wheel', (e) => {\n console.log('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 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 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}\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 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 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 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 addValue(value, groupName = '') {\n const dataGroup = this._dataGroups.find((group) => group.label === groupName);\n if (dataGroup) {\n dataGroup.data.push(_Factory_DataGroupFactory__WEBPACK_IMPORTED_MODULE_1__[\"default\"].createDataValue(value));\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
+
151
+ /***/ },
152
+
153
+ /***/ "./src/Event/TooltipEvent.ts"
154
+ /*!***********************************!*\
155
+ !*** ./src/Event/TooltipEvent.ts ***!
156
+ \***********************************/
157
+ (__unused_webpack_module, __webpack_exports__, __webpack_require__) {
158
+
159
+ "use strict";
160
+ eval("{__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ \"default\": () => (/* binding */ TooltipEvent)\n/* harmony export */ });\nclass TooltipEvent extends Event {\n reason;\n data;\n constructor(reason, data) {\n super('CHART_TOOLTIP_EVENT', { bubbles: true, composed: true });\n this.reason = reason;\n this.data = data;\n }\n static dispatch(reason, data) {\n document.dispatchEvent(new TooltipEvent(reason, data));\n }\n}\n\n\n//# sourceURL=webpack://@cehlers88/chart/./src/Event/TooltipEvent.ts?\n}");
150
161
 
151
162
  /***/ },
152
163
 
@@ -157,7 +168,7 @@ eval("{__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpa
157
168
  (__unused_webpack_module, __webpack_exports__, __webpack_require__) {
158
169
 
159
170
  "use strict";
160
- 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 _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 buildCounter = 0;\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 return {\n color,\n label: groupLabel === '' ? 'Group#' + DataGroupFactory.buildCounter : groupLabel,\n description: \"\",\n data: values.map((valueDef) => {\n let value = valueDef;\n let label = valueDef;\n let valueStart = 0;\n if (valueDef !== null) {\n if (typeof valueDef === 'object') {\n if (Array.isArray(valueDef.value) && valueDef.value.length === 2) {\n valueStart = valueDef.value[0];\n value = valueDef.value[1];\n }\n else {\n value = valueDef.value;\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 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 valueStart\n };\n }),\n infos: {}\n };\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 */ });\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 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 }\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 || value === undefined),\n hasLabel: (valueDef !== null && valueDef !== undefined && valueDef.label !== undefined),\n label,\n value,\n valueEnd,\n valueStart\n };\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 result.push(DataGroupFactory.createDataGroup(valueObj[key].data, key, valueObj[key].color));\n }\n });\n }\n }\n return result;\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}");
161
172
 
162
173
  /***/ },
163
174
 
@@ -168,7 +179,7 @@ eval("{__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpa
168
179
  (__unused_webpack_module, __webpack_exports__, __webpack_require__) {
169
180
 
170
181
  "use strict";
171
- 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}");
172
183
 
173
184
  /***/ },
174
185
 
@@ -190,7 +201,7 @@ eval("{__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpa
190
201
  (__unused_webpack_module, __webpack_exports__, __webpack_require__) {
191
202
 
192
203
  "use strict";
193
- 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 //this._engine.addDataGroup(DataGroupFactory.createDataGroup([99,null,50,50,5]));\n //this._engine.addDataGroup(DataGroupFactory.createDataGroup([5,-10,-10,20],'Bezeichnung', '#880726'));\n /*\n this._engine.addPlugin(new DiagramLine({\n showBulletPoints: true,\n }));*/\n /*\n button.addEventListener('click',()=>{\n const plugin = engine.getPlugin('DiagramLine');\n plugin.setProperty('showZeroLine',!plugin.getProperty('showZeroLine'));\n });\n\n */\n }\n addPlugin(plugin) {\n this._engine.addPlugin(plugin);\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 return canvas;\n }\n evalArguments() {\n [\n { name: 'value', defaultValue: '[]', callback: (value) => {\n _Factory_DataGroupFactory__WEBPACK_IMPORTED_MODULE_3__[\"default\"].parseDataGroupsDefinition(value).map(dataGroupDef => {\n this._engine.addDataGroup(_Factory_DataGroupFactory__WEBPACK_IMPORTED_MODULE_3__[\"default\"].createDataGroup(dataGroupDef.data, dataGroupDef.label, dataGroupDef.color));\n });\n } },\n { name: 'autostart', defaultValue: 'true', callback: (value) => {\n if (value === 'true') {\n this.start();\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 start() {\n this._engine.start();\n return this;\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}");
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 _ignoreValueAttribute = false;\n _pendingActions = [];\n static get observedAttributes() {\n return ['value'];\n }\n connectedCallback() {\n if (this._engine) {\n // The element was moved within the dom, no need to build it again.\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 }\n disconnectedCallback() {\n if (this._engine) {\n this._engine.stop();\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 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 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 console.log('set', attrDef, value);\n canvas.setAttribute(attrDef.name, value);\n console.log(canvas);\n this.style[attrDef.name] = value + 'px';\n });\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 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}");
194
205
 
195
206
  /***/ },
196
207
 
@@ -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 */ 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/**\n * index.ts\n * Created 05.05.24\n *\n * @author Christoph Ehlers\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 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}");
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 = _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}");
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 */ \"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}");
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 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}");
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 */ AbstractPlugin)\n/* harmony export */ });\nclass AbstractPlugin {\n _buffer;\n _camera;\n _dataGroups = [];\n _drawAreaRect;\n _entityRegistry;\n _fixed = false;\n _properties = {};\n _priority = 50;\n bufferCtx;\n ctx;\n redrawBuffer = true;\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 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 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 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 - this.getProperty('x', 0),\n h: canvasElement.height - this.getProperty('y', 0)\n });\n this.ctx = canvasElement.getContext('2d');\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 }\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 get needsRedraw() {\n return this.redrawBuffer;\n }\n}\n\n\n//# sourceURL=webpack://@cehlers88/chart/./src/plugin/AbstractPlugin.ts?\n}");
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 _dataGroupsChanged = true;\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 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 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 try {\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 }\n catch (e) {\n console.warn(e);\n }\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 /**\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 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 * 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}\n\n\n//# sourceURL=webpack://@cehlers88/chart/./src/plugin/AbstractPlugin.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 */ 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 maxValue = this.rangeType === ERangeType.MIN_MAX ? range : maxValue;\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 totalValue = data.value + data.valueStart + minValueZeroDiff;\n const barHeight = data.value * viewHeight / maxValue;\n const bar = {\n height: this.getProperty('enableAnimation', false) ? 1 : data.value * viewHeight / maxValue,\n destinationH: data.value * viewHeight / maxValue,\n destinationY: drawArea.y + drawArea.h - (totalValue * viewHeight / maxValue),\n x: (groupIndex * barWidth) + (index * barWidth * dataGroups.length) + drawArea.x + 1,\n y: this.getProperty('enableAnimation', false) ? drawArea.y + drawArea.h - (totalValue * viewHeight / maxValue) + barHeight : drawArea.y + drawArea.h - (totalValue * 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}");
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 (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}");
238
249
 
239
250
  /***/ },
240
251
 
@@ -256,7 +267,7 @@ eval("{__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpa
256
267
  (__unused_webpack_module, __webpack_exports__, __webpack_require__) {
257
268
 
258
269
  "use strict";
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 */ DiagramLine)\n/* harmony export */ });\n/* harmony import */ var _AbstractDiagramPlugin__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./AbstractDiagramPlugin */ \"./src/plugin/AbstractDiagramPlugin.ts\");\n/**\n * Library DiagramLine\n * Created 26.11.22\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 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.ZERO_BASED;\n syncGroupsPosition = false;\n zeroLineValue = 0;\n zeroLineValuePercentage = 0;\n constructor(props = {}) {\n super({\n showBulletPoints: false,\n ...props\n });\n }\n _showZeroLine() {\n return this.getProperty('showZeroLine', false);\n }\n drawHelpLines() {\n this.helpLines.map((helpLine) => {\n this.bufferCtx.strokeStyle = '#dddddd';\n this.bufferCtx.fillStyle = '#ff0000';\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 this.dataPoints.map(group => {\n this.bufferCtx.beginPath();\n group.map((dataPoint, index) => {\n this.bufferCtx.strokeStyle = dataPoint.color;\n if (index === 0) {\n this.bufferCtx.moveTo(dataPoint.x, dataPoint.y);\n }\n else {\n this.bufferCtx.lineTo(dataPoint.x, dataPoint.y);\n }\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) +\n Math.pow(this.hoverPosition.y - dataPoint.y, 2));\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 calcPointGap(dataGroup) {\n return this.bufferCtx.canvas.width / (dataGroup.data.length - 1);\n // removed by dead control flow\n\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 const singlePercentageHeight = this.getDrawAreaRect().h / 100; // 1% height\n this._dataGroups = dataGroups;\n this.zeroLineValue = Math.min(0, ...dataGroups.map((group) => group.infos.min)) * -1;\n const maxValue = Math.max(...dataGroups.map((group) => group.infos.max));\n this.zeroLineValuePercentage = this.zeroLineValue / (maxValue + this.zeroLineValue) * 100;\n const minValue = Math.min(...this._dataGroups.map((group) => group.infos.min));\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 decimalStepPercentage = largestDecimalStep / (maxValue + this.zeroLineValue) * 100;\n this.helpLines = [];\n let currentDecimalStep = decimalStepPercentage;\n while (currentDecimalStep < 100) {\n this.helpLines.push({\n y: this.getDrawAreaRect().y + this.getDrawAreaRect().h - singlePercentageHeight * currentDecimalStep,\n label: largestDecimalStep.toString()\n });\n currentDecimalStep += decimalStepPercentage;\n }\n this.dataPoints = this._dataGroups.map((dataGroup) => {\n const rangeX = this.rangeType === ERangeType.ZERO_BASED ? (maxValue + this.zeroLineValue) : maxValue - minValue;\n return dataGroup.data.map((dataPoint, index) => {\n if (dataPoint.empty)\n return undefined;\n return {\n x: this.getDrawAreaRect().x + pointGap * index,\n y: this.getDrawAreaRect().y + this.getDrawAreaRect().h - (dataPoint.value + this.zeroLineValue) * this.getDrawAreaRect().h / rangeX,\n value: dataPoint.value,\n color: dataGroup.color,\n label: dataPoint.label\n };\n }).filter((dataPoint) => dataPoint !== undefined);\n });\n return { dataGroups };\n }\n getDrawAreaRect() {\n const { x, y, h, w } = super.getDrawAreaRect();\n const manipulator = 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}\n\n\n//# sourceURL=webpack://@cehlers88/chart/./src/plugin/DiagramLine.ts?\n}");
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 (!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.helpLines = [];\n return { dataGroups };\n }\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) === 0 ? 1 : 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 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 if (dataGroup.data.length < 2) {\n return this.getDrawAreaRect().w - (this.bulletPointRadius * 2);\n }\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}");
260
271
 
261
272
  /***/ },
262
273
 
@@ -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 */ \"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('enrich');\n this._dataGroups = dataGroups.map((dataGroup) => {\n const values = dataGroup.data.map((data) => data.value);\n const valueStarts = dataGroup.data.map((data) => data.valueStart || 0);\n const min = Math.min(...values, ...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 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}");
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 (!this._dataGroupsChanged)\n return { dataGroups: this._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 // 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 = values.length === 0 ? 0 : Math.min(...valueStarts);\n const max = values.length === 0 ? 0 : Math.max(...values);\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 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}");
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 */ 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 *\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 constructor(props = {}) {\n super({\n x: 5,\n y: 5,\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 this._dataGroups.map((dataGroup, index) => {\n textBufferCtx.fillStyle = dataGroup.color;\n textBufferCtx.fillRect(this.getDrawAreaRect().x + 3, (this.getDrawAreaRect().y + index * 15) + 1, 10, 10);\n textBufferCtx.fillStyle = '#000000';\n textBufferCtx.fillText(dataGroup.label, this.getDrawAreaRect().x + 15, (this.getDrawAreaRect().y + 10 + 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\";\n }\n}\n\n\n//# sourceURL=webpack://@cehlers88/chart/./src/plugin/LegendsPlugin.ts?\n}");
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}");
282
293
 
283
294
  /***/ },
284
295
 
@@ -1,4 +1,20 @@
1
+ import { IChartPlugin } from "./IChartPlugin";
2
+ import { TDataGroup } from "../type/TDataGroup";
3
+ import { IChartEngine } from "./IChartEngine";
1
4
  export interface ICanvasDiagram {
2
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;
12
+ getPlugin(pluginName: string): IChartPlugin | undefined;
13
+ removeDataGroup(group: string | number): ICanvasDiagram;
14
+ removeValue(group: string | number, dataIndex: number): ICanvasDiagram;
15
+ setData(dataDefinition: string | any[] | object): ICanvasDiagram;
16
+ setDataGroups(dataGroups: TDataGroup[]): ICanvasDiagram;
17
+ setValue(group: string | number, dataIndex: number, value: any): ICanvasDiagram;
3
18
  start(): ICanvasDiagram;
19
+ stop(): ICanvasDiagram;
4
20
  }
@@ -4,7 +4,15 @@ 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
+ setData(dataDefinition: string | any[] | object): IChartEngine;
14
+ setDataGroups(dataGroups: TDataGroup[]): IChartEngine;
15
+ setValue(group: string | number, dataIndex: number, value: any): IChartEngine;
8
16
  draw(): IChartEngine;
9
17
  update(): IChartEngine;
10
18
  }
@@ -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
  }
@@ -11,22 +11,28 @@ import Camera from "../Camera";
11
11
  import EntityRegistry from "../Registry/EntityRegistry";
12
12
  import { IChartEntity } from "../interface/IChartEntity";
13
13
  export default abstract class AbstractPlugin implements IChartPlugin {
14
- private _buffer;
15
14
  protected _camera: Camera;
16
15
  protected _dataGroups: TDataGroup[];
17
- private _drawAreaRect;
18
16
  protected _entityRegistry: EntityRegistry;
19
17
  protected _fixed: boolean;
20
- private _properties;
21
18
  protected _priority: number;
22
19
  protected bufferCtx: CanvasRenderingContext2D;
23
20
  protected ctx: CanvasRenderingContext2D;
24
21
  protected redrawBuffer: boolean;
25
- abstract getName(): string;
22
+ protected _dataGroupsChanged: boolean;
23
+ private _buffer;
24
+ private _dataGroupsSignature;
25
+ private _stateChangedListener;
26
+ private _drawAreaRect;
27
+ private _properties;
26
28
  constructor(props?: {
27
29
  enabled?: boolean;
28
30
  [key: string]: any;
29
31
  });
32
+ get needsRedraw(): boolean;
33
+ abstract getName(): string;
34
+ setStateChangedListener(listener: (newState: string) => void): AbstractPlugin;
35
+ setState(newState: string): AbstractPlugin;
30
36
  update(dataGroups: TDataGroup[]): {
31
37
  dataGroups: TDataGroup[];
32
38
  };
@@ -34,7 +40,6 @@ export default abstract class AbstractPlugin implements IChartPlugin {
34
40
  dataGroups: TDataGroup[];
35
41
  };
36
42
  addEntity(entity: IChartEntity): AbstractPlugin;
37
- protected createBuffer(canvasElement: HTMLCanvasElement, width?: number | null, height?: number | null): void;
38
43
  create(canvasElement: HTMLCanvasElement, dataGroups: TDataGroup[]): {
39
44
  dataGroups: TDataGroup[];
40
45
  plugin: AbstractPlugin;
@@ -54,5 +59,17 @@ export default abstract class AbstractPlugin implements IChartPlugin {
54
59
  setEntityRegistry(entityRegistry: EntityRegistry): AbstractPlugin;
55
60
  setProperty(propertyName: string, value: any): AbstractPlugin;
56
61
  init(): AbstractPlugin;
57
- get needsRedraw(): boolean;
62
+ /**
63
+ * Hook for plugins that have to prepare themselves (e.g. resize their
64
+ * buffer) before the changed data is processed.
65
+ */
66
+ protected onDataGroupsChanged(dataGroups: TDataGroup[]): void;
67
+ protected createBuffer(canvasElement: HTMLCanvasElement, width?: number | null, height?: number | null): void;
68
+ /**
69
+ * Compares the given data groups by content against the ones seen by the
70
+ * last update and remembers the result in `_dataGroupsChanged`. Comparing
71
+ * by content (instead of holding on to the data groups themselves) is what
72
+ * makes manipulations of already known data groups visible.
73
+ */
74
+ private detectDataGroupsChange;
58
75
  }
@@ -23,16 +23,20 @@ export default class DiagramLine extends AbstractDiagramPlugin {
23
23
  private zeroLineValue;
24
24
  private zeroLineValuePercentage;
25
25
  constructor(props?: {
26
+ fill?: boolean;
27
+ padding?: number;
26
28
  showBulletPoints?: boolean;
29
+ showHelpLines?: boolean;
30
+ showZeroLine?: boolean;
27
31
  });
28
- private _showZeroLine;
29
- drawHelpLines(): this;
32
+ drawHelpLines(): DiagramLine;
30
33
  drawBuffer(): this;
31
- private calcPointGap;
32
34
  handleHover(x: number, y: number): AbstractPlugin;
33
35
  updateDataGroups(dataGroups: TDataGroup[]): {
34
36
  dataGroups: TDataGroup[];
35
37
  };
36
38
  getDrawAreaRect(): TRect;
37
39
  getName(): string;
40
+ private _showZeroLine;
41
+ private calcPointGap;
38
42
  }
@@ -1,6 +1,7 @@
1
1
  /**
2
2
  * Library LegendsPlugin
3
3
  * Created 30.09.24
4
+ * Last modified 05.06.26
4
5
  *
5
6
  * @author Christoph Ehlers
6
7
  */
@@ -10,6 +11,7 @@ export declare enum ERangeType {
10
11
  MIN_MAX = "min_max"
11
12
  }
12
13
  export default class LegendsPlugin extends AbstractPlugin {
14
+ static NAME: string;
13
15
  constructor(props?: {
14
16
  x?: number;
15
17
  y?: number;
@@ -8,6 +8,7 @@ export type TData = {
8
8
  empty: boolean;
9
9
  label: string;
10
10
  value: number;
11
+ valueEnd?: number;
11
12
  valueStart?: number;
12
13
  hasLabel?: boolean;
13
14
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cehlers88/chart",
3
- "version": "1.1.0",
3
+ "version": "1.2.0",
4
4
  "description": "A library to create charts using JavaScript and Canvas-Element.",
5
5
  "main": "dist/index.js",
6
6
  "publishConfig": {
@@ -34,6 +34,7 @@
34
34
  "@types/jest": "^29.5.13",
35
35
  "css-loader": "^7.1.2",
36
36
  "jest": "^29.7.0",
37
+ "jest-environment-jsdom": "^29.7.0",
37
38
  "style-loader": "^4.0.0",
38
39
  "ts-jest": "^29.2.5",
39
40
  "ts-loader": "^9.5.1",