@vuu-ui/vuu-chart 2.1.19-beta.1 → 2.1.19-beta.2

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/package.json CHANGED
@@ -1,17 +1,17 @@
1
1
  {
2
- "version": "2.1.19-beta.1",
2
+ "version": "2.1.19-beta.2",
3
3
  "description": "support for apache echarts using vuu data",
4
4
  "type": "module",
5
5
  "license": "Apache-2.0",
6
6
  "devDependencies": {
7
- "@vuu-ui/vuu-data-types": "2.1.19-beta.1",
8
- "@vuu-ui/vuu-protocol-types": "2.1.19-beta.1"
7
+ "@vuu-ui/vuu-data-types": "2.1.19-beta.2",
8
+ "@vuu-ui/vuu-protocol-types": "2.1.19-beta.2"
9
9
  },
10
10
  "dependencies": {
11
11
  "@salt-ds/styles": "0.2.1",
12
12
  "@salt-ds/window": "0.1.1",
13
- "@vuu-ui/vuu-context-menu": "2.1.19-beta.1",
14
- "@vuu-ui/vuu-utils": "2.1.19-beta.1",
13
+ "@vuu-ui/vuu-context-menu": "2.1.19-beta.2",
14
+ "@vuu-ui/vuu-utils": "2.1.19-beta.2",
15
15
  "clsx": "^2.0.0"
16
16
  },
17
17
  "peerDependencies": {
@@ -21,13 +21,12 @@
21
21
  },
22
22
  "files": [
23
23
  "README.md",
24
- "esm",
25
- "cjs",
24
+ "src",
26
25
  "/types"
27
26
  ],
28
27
  "exports": {
29
28
  ".": {
30
- "import": "./index.js",
29
+ "import": "./src/index.js",
31
30
  "types": "./types/index.d.ts"
32
31
  }
33
32
  },
@@ -0,0 +1,42 @@
1
+ const css = `
2
+ .vuuChartTooltip {
3
+ background-color: var(--salt-container-primary-background) !important;
4
+ border-color: var(--salt-selectable-borderColor-selected) !important;
5
+ font-family: var(--salt-text-fontFamily) !important;
6
+ border-radius: 6px !important;
7
+
8
+ & > div > div {
9
+ & > div:first-child {
10
+ color: var(--salt-content-primary-foreground) !important;
11
+ }
12
+
13
+ & > div:nth-child(2) > div {
14
+ & > div {
15
+ align-items: center;
16
+ display: flex;
17
+
18
+ & span:first-child {
19
+ flex: 0 0 12px;
20
+ }
21
+
22
+ & span:nth-child(2) {
23
+ text-transform: capitalize;
24
+ flex: none;
25
+ color: var(--salt-content-secondary-foreground) !important;
26
+ font-size: var(--salt-text-label-fontSize) !important;
27
+ }
28
+
29
+ & span:nth-child(3) {
30
+ text-align: right;
31
+ flex: auto;
32
+ color: var(--salt-content-primary-foreground) !important;
33
+ font-size: var(--salt-text-label-fontSize) !important;
34
+ }
35
+ }
36
+ }
37
+ }
38
+ }
39
+
40
+
41
+ `;
42
+ export default css;
package/src/Chart.js ADDED
@@ -0,0 +1,64 @@
1
+ import { jsx } from "react/jsx-runtime";
2
+ import { useEffect, useRef } from "react";
3
+ import { getInstanceByDom, init } from "echarts";
4
+ import { useComponentCssInjection } from "@salt-ds/styles";
5
+ import { useWindow } from "@salt-ds/window";
6
+ import { useChartOptions } from "./useChartOptions.js";
7
+ import { useChartContextMenu } from "./useChartContextMenu.js";
8
+ import clsx from "clsx";
9
+ import Chart from "./Chart.css";
10
+ const classBase = "vuuChart";
11
+ const Chart_Chart = ({ categoryColumnName, chartSettings = {
12
+ useCoarsePointer: true,
13
+ renderer: "svg"
14
+ }, className, dataSource, itemColorFunction, optionSettings = {
15
+ notMerge: true
16
+ }, palette, showTooltip, style = {
17
+ width: "100%",
18
+ height: "100%"
19
+ }, seriesColumnNames, ...htmlAttributes })=>{
20
+ const targetWindow = useWindow();
21
+ useComponentCssInjection({
22
+ testId: "vuu-toast-notification",
23
+ css: Chart,
24
+ window: targetWindow
25
+ });
26
+ const chartRef = useRef(null);
27
+ const option = useChartOptions({
28
+ categoryColumnName,
29
+ itemColorFunction,
30
+ palette,
31
+ dataSource,
32
+ seriesColumnNames,
33
+ showTooltip
34
+ });
35
+ const onContextMenu = useChartContextMenu({
36
+ categoryColumnName
37
+ });
38
+ useEffect(()=>{
39
+ const chart = init(chartRef.current, null, chartSettings);
40
+ chart.on("contextmenu", onContextMenu);
41
+ return ()=>{
42
+ chart?.dispose();
43
+ };
44
+ }, [
45
+ chartSettings,
46
+ onContextMenu
47
+ ]);
48
+ useEffect(()=>{
49
+ if (chartRef.current) {
50
+ const chart = getInstanceByDom(chartRef.current);
51
+ chart?.setOption(option, optionSettings);
52
+ }
53
+ }, [
54
+ option,
55
+ optionSettings
56
+ ]);
57
+ return /*#__PURE__*/ jsx("div", {
58
+ ...htmlAttributes,
59
+ className: clsx(classBase, className),
60
+ ref: chartRef,
61
+ style: style
62
+ });
63
+ };
64
+ export { Chart_Chart as Chart };
@@ -0,0 +1,101 @@
1
+ import { EventEmitter, Range, buildColumnMap, metadataKeys } from "@vuu-ui/vuu-utils";
2
+ import { MovingDataRowWindow } from "./DataRowMovingWindow.js";
3
+ const defaultItemColorFunction = ({ color })=>color;
4
+ const { KEY: KEY } = metadataKeys;
5
+ function DataSourceValue(key, row, value) {
6
+ return {
7
+ key,
8
+ row,
9
+ value
10
+ };
11
+ }
12
+ function getCategoriesAndSeries(columnMap, data, categoryColumn, seriesColumns, itemColorFunction = defaultItemColorFunction) {
13
+ const categoryValues = [];
14
+ const seriesMap = new Map();
15
+ data.forEach((row)=>{
16
+ const categoryValue = row[columnMap[categoryColumn]];
17
+ categoryValues.push(categoryValue);
18
+ for (const seriesColumn of seriesColumns){
19
+ let series = seriesMap.get(seriesColumn);
20
+ if (!series) {
21
+ series = {
22
+ id: seriesColumn,
23
+ name: seriesColumn,
24
+ label: seriesColumn,
25
+ data: [],
26
+ type: "line",
27
+ itemStyle: {
28
+ color: itemColorFunction
29
+ },
30
+ lineStyle: {
31
+ width: 1.5
32
+ },
33
+ symbol: "circle"
34
+ };
35
+ seriesMap.set(seriesColumn, series);
36
+ }
37
+ const key = row[KEY];
38
+ const value = row[columnMap[seriesColumn]];
39
+ series.data.push(DataSourceValue(key, row, value));
40
+ }
41
+ });
42
+ return [
43
+ categoryValues,
44
+ Array.from(seriesMap.values())
45
+ ];
46
+ }
47
+ class ChartSeries extends EventEmitter {
48
+ #categoryColumn = "";
49
+ #columnMap = {};
50
+ #itemColorFunction;
51
+ #dataWindow = new MovingDataRowWindow(Range(0, 1000));
52
+ #dates = [];
53
+ #series = [];
54
+ #seriesColumns = [];
55
+ handleData = (message)=>{
56
+ if ("subscribed" === message.type) this.#columnMap = buildColumnMap(message.tableSchema.columns);
57
+ else if ("viewport-update" === message.type) {
58
+ if ("size-only" === message.mode) ;
59
+ else if (message.rows) {
60
+ for (const row of message.rows)this.#dataWindow.add(row);
61
+ this.buildCategoriesAndSeries();
62
+ }
63
+ } else console.warn(JSON.stringify(message));
64
+ };
65
+ buildCategoriesAndSeries() {
66
+ if (this.#categoryColumn && this.#seriesColumns.length > 0) {
67
+ const [dates, series] = getCategoriesAndSeries(this.#columnMap, this.#dataWindow.data, this.#categoryColumn, this.#seriesColumns, this.#itemColorFunction);
68
+ this.#dates = dates;
69
+ this.#series = series;
70
+ this.emit("update");
71
+ }
72
+ }
73
+ constructor({ dataSource, itemColorFunction }){
74
+ super();
75
+ this.#itemColorFunction = itemColorFunction;
76
+ dataSource.subscribe({
77
+ range: Range(0, 1000)
78
+ }, this.handleData);
79
+ }
80
+ get categoryColumn() {
81
+ return this.#categoryColumn;
82
+ }
83
+ set categoryColumn(categoryColumn) {
84
+ this.#categoryColumn = categoryColumn;
85
+ if (this.#seriesColumns.length > 0) this.buildCategoriesAndSeries();
86
+ }
87
+ get seriesColumnNames() {
88
+ return this.#seriesColumns;
89
+ }
90
+ set seriesColumnNames(seriesColumnNames) {
91
+ this.#seriesColumns = seriesColumnNames;
92
+ if (this.#categoryColumn) this.buildCategoriesAndSeries();
93
+ }
94
+ get categories() {
95
+ return this.#dates;
96
+ }
97
+ get series() {
98
+ return this.#series;
99
+ }
100
+ }
101
+ export { ChartSeries };
@@ -0,0 +1,64 @@
1
+ import { WindowRange, metadataKeys } from "@vuu-ui/vuu-utils";
2
+ const { IDX: IDX, KEY: KEY } = metadataKeys;
3
+ class MovingDataRowWindow {
4
+ data;
5
+ rowCount = 0;
6
+ #range;
7
+ constructor({ from, to }){
8
+ this.#range = new WindowRange(from, to);
9
+ this.data = new Array(Math.max(0, to - from));
10
+ this.rowCount = 0;
11
+ }
12
+ setRowCount = (rowCount)=>{
13
+ if (rowCount < this.data.length) this.data.length = rowCount;
14
+ this.rowCount = rowCount;
15
+ };
16
+ add(data) {
17
+ const index = data[IDX];
18
+ if (this.isWithinRange(index)) {
19
+ const internalIndex = index - this.#range.from;
20
+ this.data[internalIndex] = data;
21
+ }
22
+ }
23
+ getAtIndex(index) {
24
+ return this.#range.isWithin(index) && null != this.data[index - this.#range.from] ? this.data[index - this.#range.from] : void 0;
25
+ }
26
+ getByKey(key) {
27
+ return this.data.find((row)=>row[KEY] === key);
28
+ }
29
+ isWithinRange(index) {
30
+ return this.#range.isWithin(index);
31
+ }
32
+ setRange({ from, to }) {
33
+ if (from !== this.#range.from || to !== this.#range.to) {
34
+ const [overlapFrom, overlapTo] = this.#range.overlap(from, to);
35
+ const newData = new Array(Math.max(0, to - from));
36
+ for(let i = overlapFrom; i < overlapTo; i++){
37
+ const data = this.getAtIndex(i);
38
+ if (data) {
39
+ const index = i - from;
40
+ newData[index] = data;
41
+ }
42
+ }
43
+ this.data = newData;
44
+ this.#range.from = from;
45
+ this.#range.to = to;
46
+ }
47
+ }
48
+ get hasData() {
49
+ return this.data.some((d)=>void 0 !== d);
50
+ }
51
+ get firstRow() {
52
+ for(let i = 0; i < this.data.length; i++)if (this.data[i]) return this.data[i];
53
+ throw Error("[DataRowMovingWinddow] firstRow, no data");
54
+ }
55
+ get range() {
56
+ return this.#range;
57
+ }
58
+ get hasAllRowsWithinRange() {
59
+ const { from, to } = this.#range;
60
+ for(let i = from; i < to; i++)if (void 0 === this.getAtIndex(i)) return false;
61
+ return true;
62
+ }
63
+ }
64
+ export { MovingDataRowWindow };
package/src/index.js ADDED
@@ -0,0 +1,2 @@
1
+ export { Chart } from "./Chart.js";
2
+ export { ChartSeries } from "./ChartSeries.js";
@@ -0,0 +1,29 @@
1
+ import { useContextMenu } from "@vuu-ui/vuu-context-menu";
2
+ import { useCallback } from "react";
3
+ const useChartContextMenu = ({ categoryColumnName })=>{
4
+ const showContextMenu = useContextMenu();
5
+ const onContextMenu = useCallback(({ data, event, name: categoryValue, seriesName: valueColumn, value: keyedValue, ...rest })=>{
6
+ event.stop();
7
+ console.log({
8
+ rest
9
+ });
10
+ const { row, value } = data;
11
+ const menuOptions = {
12
+ categoryColumn: categoryColumnName,
13
+ categoryValue,
14
+ row,
15
+ valueColumn,
16
+ value
17
+ };
18
+ showContextMenu(event.event, "series", menuOptions, {
19
+ onOpenChange: (isOpen)=>{
20
+ console.log(`[useTableContextMenu] onOpenChange ${isOpen}`);
21
+ }
22
+ });
23
+ }, [
24
+ categoryColumnName,
25
+ showContextMenu
26
+ ]);
27
+ return onContextMenu;
28
+ };
29
+ export { useChartContextMenu };
@@ -0,0 +1,76 @@
1
+ import { useMemo, useState } from "react";
2
+ import { ChartSeries } from "./ChartSeries.js";
3
+ import { itemsChanged } from "@vuu-ui/vuu-utils";
4
+ const defaultPalette = [
5
+ "#4676bf",
6
+ "#ab6528",
7
+ "#9f55c2",
8
+ "#2a8285",
9
+ "#697694",
10
+ "#B0549d"
11
+ ];
12
+ const useChartOptions = ({ categoryColumnName, dataSource, itemColorFunction, palette = defaultPalette, seriesColumnNames, showTooltip = true })=>{
13
+ const [, forceRender] = useState({});
14
+ const chartSeries = useMemo(()=>{
15
+ console.log("new ChartSeries");
16
+ const cs = new ChartSeries({
17
+ itemColorFunction,
18
+ dataSource
19
+ });
20
+ cs.on("update", ()=>forceRender({}));
21
+ return cs;
22
+ }, [
23
+ dataSource,
24
+ itemColorFunction
25
+ ]);
26
+ useMemo(()=>{
27
+ if (categoryColumnName !== chartSeries.categoryColumn) chartSeries.categoryColumn = categoryColumnName;
28
+ if (itemsChanged(seriesColumnNames, chartSeries.seriesColumnNames)) chartSeries.seriesColumnNames = seriesColumnNames;
29
+ }, [
30
+ categoryColumnName,
31
+ chartSeries,
32
+ seriesColumnNames
33
+ ]);
34
+ return {
35
+ animation: false,
36
+ color: palette,
37
+ dataZoom: [
38
+ {
39
+ backgroundColor: "var(--vuuChart-zoom-background, transparent)",
40
+ borderColor: "var(--vuuChart-zoom-borderColor, white)",
41
+ handleStyle: {
42
+ color: "var(--vuuChart-zoom-handleColor, white)"
43
+ },
44
+ moveHandleStyle: {
45
+ color: "var(--vuuChart-zoom-slideColor, white)"
46
+ },
47
+ type: "slider",
48
+ start: 0,
49
+ end: 100
50
+ }
51
+ ],
52
+ grid: {
53
+ left: "0",
54
+ bottom: 0,
55
+ right: 5
56
+ },
57
+ series: chartSeries.series,
58
+ tooltip: showTooltip ? {
59
+ backgroundColor: "",
60
+ borderColor: "",
61
+ className: "vuuChartTooltip",
62
+ trigger: "axis"
63
+ } : void 0,
64
+ xAxis: {
65
+ data: chartSeries.categories
66
+ },
67
+ yAxis: {
68
+ splitLine: {
69
+ lineStyle: {
70
+ color: "var(--salt-separable-tertiary-borderColor)"
71
+ }
72
+ }
73
+ }
74
+ };
75
+ };
76
+ export { useChartOptions };