@cehlers88/chart 1.1.1 → 1.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -60,13 +60,104 @@ Attributes:
60
60
 
61
61
  | Attribute | Description | Default |
62
62
  |-------------|----------------------------------------------------------------------|---------|
63
- | `width` | Canvas width in pixels | `300` |
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
64
  | `height` | Canvas height in pixels | `150` |
65
65
  | `value` | JSON string with the data groups (see data format below) | `[]` |
66
66
  | `autostart` | Starts the chart automatically once mounted (`"true"`/`"false"`) | `true` |
67
67
 
68
68
  Additional plugins can be registered on the element before it starts via `getPlugin()`/`addPlugin()`.
69
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
+
70
161
  ## Data format
71
162
 
72
163
  `DataGroupFactory` accepts several notations for the values within a group:
@@ -91,13 +182,58 @@ As a JSON string (e.g. for the custom element's `value` attribute), groups are g
91
182
  }
92
183
  ```
93
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
+
94
230
  ## Available plugins
95
231
 
96
232
  | Plugin | Purpose |
97
233
  |--------------------------|---------------------------------------------------------------------|
98
234
  | `BarsPlugin` | Renders data groups as bars |
99
- | `DiagramLinePlugin` | Renders data groups as lines, including helper lines |
100
- | `LegendsPlugin` | Displays a legend for the data groups |
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) |
101
237
  | `TooltipPlugin` | Shows tooltips on hover over data points |
102
238
  | `CameraMovePlugin` | Enables scrolling/panning the view via mouse/wheel |
103
239
  | `PaginationPlugin` | Splits large data sets into pages |
@@ -115,6 +251,12 @@ npm run watch # build in watch mode
115
251
  npm test # run tests with Jest
116
252
  ```
117
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
+
118
260
  ## License
119
261
 
120
262
  ISC © Christoph Ehlers
@@ -14,21 +14,93 @@ 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
26
  addPlugin(plugin: IChartPlugin): ChartEngine;
27
+ /**
28
+ * Changes the size of the canvas (in canvas pixels). The plugins rebuild
29
+ * their buffers, so the chart is drawn sharp in the new size instead of
30
+ * being stretched by css.
31
+ */
32
+ resize(width: number, height: number): ChartEngine;
33
+ /**
34
+ * Converts a pointer position (css pixels) into canvas pixels - both differ
35
+ * as soon as the canvas is scaled by css, e.g. by `width: 100%`.
36
+ */
37
+ private toCanvasPosition;
23
38
  getDrawAreaRect(): TRect;
24
39
  getPlugin(pluginName: string): IChartPlugin | undefined;
25
40
  getPlugins(): IChartPlugin[];
26
41
  setPluginProperty(propertyName: string, value: any): ChartEngine;
27
42
  create(): ChartEngine;
28
43
  addDataGroup(dataGroup: TDataGroup): IChartEngine;
44
+ /**
45
+ * Returns the data groups currently held by the engine.
46
+ * The returned groups are the live objects; changes made to them are
47
+ * picked up with the next update cycle.
48
+ */
49
+ getDataGroups(): TDataGroup[];
50
+ /**
51
+ * Returns a single data group, addressed either by its label or by its index.
52
+ */
53
+ getDataGroup(group: string | number): TDataGroup | undefined;
54
+ /**
55
+ * Replaces all data groups at once.
56
+ */
57
+ setDataGroups(dataGroups: TDataGroup[]): IChartEngine;
58
+ /**
59
+ * Replaces the complete data of the chart. Accepts everything
60
+ * `DataGroupFactory.parseDataGroupsDefinition` understands: a JSON string,
61
+ * a plain array of values or an object keyed by group name.
62
+ */
63
+ setData(dataDefinition: string | any[] | object): IChartEngine;
64
+ /**
65
+ * Removes a data group, addressed either by its label or by its index.
66
+ */
67
+ removeDataGroup(group: string | number): IChartEngine;
68
+ /**
69
+ * Removes all data groups.
70
+ */
71
+ clearData(): IChartEngine;
72
+ /**
73
+ * Appends a value - e.g. a new measurement that arrived after the page was
74
+ * loaded - to a data group. The value may be given as `{value, timestamp}`
75
+ * or `{value, label}`. A group with a `maxLength` drops its oldest values,
76
+ * so a live chart keeps its window. Without a group name the value goes to
77
+ * the only group of the chart; an unknown group is created.
78
+ */
29
79
  addValue(value: any, groupName?: string): IChartEngine;
80
+ /**
81
+ * Replaces a single value within a data group.
82
+ */
83
+ setValue(group: string | number, dataIndex: number, value: any): IChartEngine;
84
+ /**
85
+ * Removes a single value from a data group.
86
+ */
87
+ removeValue(group: string | number, dataIndex: number): IChartEngine;
30
88
  draw(): ChartEngine;
31
89
  update(): ChartEngine;
90
+ /**
91
+ * Returns the data groups handed to the plugins. They are a detached copy
92
+ * of the data owned by the engine and are renewed whenever that data
93
+ * changed. Without that separation the plugins would hold references to
94
+ * the very objects manipulated from outside, so neither they nor the
95
+ * engine could tell a manipulation from the state of the last frame.
96
+ */
97
+ private getPipelineDataGroups;
32
98
  start(): void;
99
+ /**
100
+ * Stops the render loop of this engine. The data stays untouched, so the
101
+ * chart can be started again later on.
102
+ */
103
+ stop(): ChartEngine;
104
+ isRunning(): boolean;
33
105
  private getEngineCallbacks;
34
106
  }
@@ -1,10 +1,43 @@
1
1
  import { TDataGroup } from "../type/TDataGroup";
2
+ import { TData } from "../type/TData";
2
3
  export default class DataGroupFactory {
3
4
  static buildCounter: number;
4
5
  private static _colors;
5
6
  private static _lastColorIndex;
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;
13
+ /**
14
+ * Unix timestamps in seconds or milliseconds (see `toMilliseconds`),
15
+ * stored as milliseconds; everything else means "no timestamp".
16
+ */
17
+ static normalizeTimestamp(timestamp: any): number | undefined;
6
18
  static createDataGroup(values: any[], groupLabel?: string, color?: string): TDataGroup;
7
- static parseDataGroupsDefinition(value: string): TDataGroup[];
19
+ /**
20
+ * Parses a data group definition. Beside the JSON notation used by the
21
+ * `value` attribute of `<canvas-diagram>` an already parsed object/array
22
+ * may be passed, so data can be handed over programmatically at runtime.
23
+ */
24
+ static parseDataGroupsDefinition(value: string | any[] | object): TDataGroup[];
25
+ /**
26
+ * Applies the optional parts of an object definition to a data group:
27
+ * `unit` (shown in tooltips and axis labels), `labels` (one label per value),
28
+ * `timestamps` (one unix timestamp per value, in seconds or milliseconds -
29
+ * shown as time labels) and `maxLength` (values added later push the oldest
30
+ * ones out, so a live chart keeps its time window). Labels and timestamps of
31
+ * single values given as `{value, label, timestamp}` win.
32
+ */
33
+ static applyGroupOptions(dataGroup: TDataGroup, definition: {
34
+ unit?: string;
35
+ labels?: any[];
36
+ maxLength?: number;
37
+ timestamps?: any[];
38
+ [key: string]: any;
39
+ }): TDataGroup;
40
+ static getNextColor(): string;
8
41
  static setColor(color: string, index: number): void;
9
42
  static setColors(colors: string[]): void;
10
43
  }
@@ -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;
@@ -0,0 +1,69 @@
1
+ /**
2
+ * Library ChartStyle
3
+ * Created 29.09.26
4
+ *
5
+ * Shared helpers for the look of the charts: theme colours, colour alpha,
6
+ * number formatting and "nice" axis ticks.
7
+ *
8
+ * @author Christoph Ehlers
9
+ */
10
+ export type TChartTheme = {
11
+ textColor: string;
12
+ gridColor: string;
13
+ tooltipBackground: string;
14
+ tooltipBorder: string;
15
+ tooltipTextColor: string;
16
+ font: string;
17
+ };
18
+ /**
19
+ * The defaults are mid-tone colours, so a chart stays readable on a dark as
20
+ * well as on a light background when the host page defines nothing.
21
+ */
22
+ export declare const DEFAULT_THEME: TChartTheme;
23
+ /**
24
+ * Reads the theme from the CSS custom properties `--chart-*` of the given
25
+ * element, so the host page can adapt the charts to its own theme without
26
+ * touching any plugin options.
27
+ */
28
+ export declare function readTheme(element: any): TChartTheme;
29
+ /**
30
+ * Returns the given colour with the given opacity. Hex colours (#rgb, #rrggbb,
31
+ * #rrggbbaa) and rgb()/rgba() are supported; any other notation is returned
32
+ * unchanged, which only costs the transparency.
33
+ */
34
+ export declare function withAlpha(color: string, alpha: number): string;
35
+ /**
36
+ * The language of the page (`<html lang>`), so numbers and times are written
37
+ * the way the rest of the page writes them.
38
+ */
39
+ export declare function pageLocale(): string | undefined;
40
+ /**
41
+ * Formats a value for axis labels and tooltips: at most `maxDecimals`
42
+ * decimals, grouped by the locale of the page.
43
+ */
44
+ export declare function formatValue(value: any, maxDecimals?: number, locale?: string | undefined): string;
45
+ /**
46
+ * Calculates evenly spaced, human friendly axis ticks (1, 2, 2.5, 5 × 10^n)
47
+ * which enclose the given range.
48
+ */
49
+ export declare function niceTicks(min: number, max: number, maxTicks?: number): {
50
+ min: number;
51
+ max: number;
52
+ step: number;
53
+ ticks: number[];
54
+ };
55
+ /**
56
+ * Returns the number of decimals needed to show the given step exactly.
57
+ */
58
+ export declare function decimalsOf(step: number): number;
59
+ /**
60
+ * Normalizes a unix timestamp in seconds or milliseconds (see
61
+ * SECONDS_MILLISECONDS_BOUNDARY) to milliseconds. 0, negative values, null
62
+ * and anything unparsable mean "no timestamp".
63
+ */
64
+ export declare function toMilliseconds(timestamp: any): number | undefined;
65
+ /**
66
+ * Turns unix timestamps (seconds or milliseconds) into short labels. The date
67
+ * is only part of the label when the timestamps span more than one day.
68
+ */
69
+ export declare function formatTimestamps(timestamps: any[], locale?: string | undefined): (string | undefined)[];
@@ -7,14 +7,58 @@
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
+ private _resizeObserver;
18
+ private _responsive;
19
+ private _runningBeforeDisconnect;
20
+ static get observedAttributes(): string[];
13
21
  connectedCallback(): void;
22
+ disconnectedCallback(): void;
23
+ attributeChangedCallback(name: string, oldValue: string | null, newValue: string | null): void;
14
24
  addPlugin(plugin: IChartPlugin): ICanvasDiagram;
15
25
  getPlugin(pluginName: string): IChartPlugin | undefined;
26
+ /**
27
+ * Changes the size of the diagram (in pixels) and redraws it sharply in
28
+ * the new size.
29
+ */
30
+ resize(width: number, height: number): ICanvasDiagram;
31
+ getEngine(): IChartEngine | undefined;
32
+ addDataGroup(dataGroup: TDataGroup): ICanvasDiagram;
33
+ addValue(value: any, groupName?: string): ICanvasDiagram;
34
+ clearData(): ICanvasDiagram;
35
+ getDataGroup(group: string | number): TDataGroup | undefined;
36
+ getDataGroups(): TDataGroup[];
37
+ removeDataGroup(group: string | number): ICanvasDiagram;
38
+ removeValue(group: string | number, dataIndex: number): ICanvasDiagram;
39
+ /**
40
+ * Replaces the complete data of the diagram, at any time - also long after
41
+ * the page was loaded.
42
+ */
43
+ setData(dataDefinition: string | any[] | object): ICanvasDiagram;
44
+ setDataGroups(dataGroups: TDataGroup[]): ICanvasDiagram;
45
+ setValue(group: string | number, dataIndex: number, value: any): ICanvasDiagram;
16
46
  start(): ICanvasDiagram;
47
+ stop(): ICanvasDiagram;
48
+ /**
49
+ * Runs the given action against the engine. If the element is not mounted
50
+ * yet, the action is queued and executed as soon as the engine exists,
51
+ * so data can be set before as well as after the element was connected.
52
+ */
53
+ private withEngine;
54
+ private flushPendingActions;
55
+ /**
56
+ * A width given in percent (`width="100%"`) or as `auto` makes the diagram
57
+ * responsive: it takes the width of its container and is redrawn in the
58
+ * new size whenever that changes.
59
+ */
17
60
  private createCanvas;
61
+ private observeSize;
18
62
  private evalArguments;
19
63
  }
20
64
  export default CanvasDiagram;
package/dist/index.d.ts CHANGED
@@ -9,7 +9,6 @@ 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";
@@ -18,7 +17,7 @@ import PaginationPlugin from "./plugin/PaginationPlugin";
18
17
  export declare const CanvasDiagram: typeof CanvasDiagramClass;
19
18
  export declare const ChartEngine: typeof ChartEngineClass;
20
19
  export declare const DataGroupFactory: typeof DataGroupFactoryClass;
21
- export declare const diagramRegistry: DiagramRegistry;
20
+ export declare const diagramRegistry: import("./Registry/DiagramRegistry").default;
22
21
  export declare const plugins: {
23
22
  BarsPlugin: typeof BarsPluginClass;
24
23
  CameraMovePlugin: typeof CameraMove;
@@ -32,7 +31,7 @@ declare const _default: {
32
31
  CanvasDiagram: typeof CanvasDiagramClass;
33
32
  ChartEngine: typeof ChartEngineClass;
34
33
  DataGroupFactory: typeof DataGroupFactoryClass;
35
- diagramRegistry: DiagramRegistry;
34
+ diagramRegistry: import("./Registry/DiagramRegistry").default;
36
35
  plugins: {
37
36
  BarsPlugin: typeof BarsPluginClass;
38
37
  CameraMovePlugin: typeof CameraMove;