@perses-dev/scatter-chart-plugin 0.6.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.
Files changed (98) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +41 -0
  3. package/__mf/css/async/341.9c79ef64.css +1 -0
  4. package/__mf/css/async/564.9c79ef64.css +1 -0
  5. package/__mf/font/lato-all-300-normal.322bdf14.woff +0 -0
  6. package/__mf/font/lato-all-400-normal.63513b00.woff +0 -0
  7. package/__mf/font/lato-all-700-normal.bb27db94.woff +0 -0
  8. package/__mf/font/lato-all-900-normal.a27049a3.woff +0 -0
  9. package/__mf/font/lato-latin-300-normal.c5195215.woff2 +0 -0
  10. package/__mf/font/lato-latin-400-normal.b7ffde23.woff2 +0 -0
  11. package/__mf/font/lato-latin-700-normal.d5eb20bc.woff2 +0 -0
  12. package/__mf/font/lato-latin-900-normal.d884a71c.woff2 +0 -0
  13. package/__mf/font/lato-latin-ext-300-normal.abcc64a9.woff2 +0 -0
  14. package/__mf/font/lato-latin-ext-400-normal.6ebed106.woff2 +0 -0
  15. package/__mf/font/lato-latin-ext-700-normal.8697d1d5.woff2 +0 -0
  16. package/__mf/font/lato-latin-ext-900-normal.20a2b415.woff2 +0 -0
  17. package/__mf/js/527.d3310177.js +2 -0
  18. package/__mf/js/527.d3310177.js.LICENSE.txt +24 -0
  19. package/__mf/js/792.6224e3b1.js +6 -0
  20. package/__mf/js/792.6224e3b1.js.LICENSE.txt +9 -0
  21. package/__mf/js/ScatterChart.907123a8.js +5 -0
  22. package/__mf/js/async/150.4fc9e087.js +2 -0
  23. package/__mf/js/async/150.4fc9e087.js.LICENSE.txt +21 -0
  24. package/__mf/js/async/156.c11b1700.js +1 -0
  25. package/__mf/js/async/173.c19d3b98.js +2 -0
  26. package/__mf/js/async/173.c19d3b98.js.LICENSE.txt +19 -0
  27. package/__mf/js/async/507.3d416130.js +174 -0
  28. package/__mf/js/async/507.3d416130.js.LICENSE.txt +55 -0
  29. package/__mf/js/async/564.e5b95c38.js +2 -0
  30. package/__mf/js/async/564.e5b95c38.js.LICENSE.txt +9 -0
  31. package/__mf/js/async/651.791f807e.js +1 -0
  32. package/__mf/js/async/694.903128f9.js +1 -0
  33. package/__mf/js/async/740.c78d3483.js +1 -0
  34. package/__mf/js/async/75.06e41df4.js +1 -0
  35. package/__mf/js/async/770.da7255d0.js +1 -0
  36. package/__mf/js/async/960.e791a996.js +2 -0
  37. package/__mf/js/async/960.e791a996.js.LICENSE.txt +8 -0
  38. package/__mf/js/async/964.34b88a18.js +2 -0
  39. package/__mf/js/async/964.34b88a18.js.LICENSE.txt +9 -0
  40. package/__mf/js/async/__federation_expose_ScatterChart.9e6627ee.js +2 -0
  41. package/__mf/js/async/__federation_expose_ScatterChart.9e6627ee.js.LICENSE.txt +9 -0
  42. package/__mf/js/index.b48f09c4.js +1 -0
  43. package/lib/ScatterChart.d.ts +8 -0
  44. package/lib/ScatterChart.d.ts.map +1 -0
  45. package/lib/ScatterChart.js +27 -0
  46. package/lib/ScatterChart.js.map +1 -0
  47. package/lib/ScatterChartPanel.d.ts +37 -0
  48. package/lib/ScatterChartPanel.d.ts.map +1 -0
  49. package/lib/ScatterChartPanel.js +182 -0
  50. package/lib/ScatterChartPanel.js.map +1 -0
  51. package/lib/Scatterplot.d.ts +11 -0
  52. package/lib/Scatterplot.d.ts.map +1 -0
  53. package/lib/Scatterplot.js +113 -0
  54. package/lib/Scatterplot.js.map +1 -0
  55. package/lib/bootstrap.d.ts +2 -0
  56. package/lib/bootstrap.d.ts.map +1 -0
  57. package/lib/bootstrap.js +19 -0
  58. package/lib/bootstrap.js.map +1 -0
  59. package/lib/cjs/ScatterChart.js +33 -0
  60. package/lib/cjs/ScatterChartPanel.js +183 -0
  61. package/lib/cjs/Scatterplot.js +121 -0
  62. package/lib/cjs/bootstrap.js +26 -0
  63. package/lib/cjs/env.d.js +14 -0
  64. package/lib/cjs/getPluginModule.js +27 -0
  65. package/lib/cjs/index-federation.js +55 -0
  66. package/lib/cjs/index.js +28 -0
  67. package/lib/cjs/mock-trace-data.js +145 -0
  68. package/lib/cjs/scatter-chart-model.js +25 -0
  69. package/lib/cjs/setup-tests.js +19 -0
  70. package/lib/env.d.js +15 -0
  71. package/lib/env.d.js.map +1 -0
  72. package/lib/getPluginModule.d.ts +6 -0
  73. package/lib/getPluginModule.d.ts.map +1 -0
  74. package/lib/getPluginModule.js +16 -0
  75. package/lib/getPluginModule.js.map +1 -0
  76. package/lib/index-federation.d.ts +1 -0
  77. package/lib/index-federation.d.ts.map +1 -0
  78. package/lib/index-federation.js +15 -0
  79. package/lib/index-federation.js.map +1 -0
  80. package/lib/index.d.ts +6 -0
  81. package/lib/index.d.ts.map +1 -0
  82. package/lib/index.js +7 -0
  83. package/lib/index.js.map +1 -0
  84. package/lib/mock-trace-data.d.ts +88 -0
  85. package/lib/mock-trace-data.d.ts.map +1 -0
  86. package/lib/mock-trace-data.js +129 -0
  87. package/lib/mock-trace-data.js.map +1 -0
  88. package/lib/scatter-chart-model.d.ts +14 -0
  89. package/lib/scatter-chart-model.d.ts.map +1 -0
  90. package/lib/scatter-chart-model.js +19 -0
  91. package/lib/scatter-chart-model.js.map +1 -0
  92. package/lib/setup-tests.d.ts +2 -0
  93. package/lib/setup-tests.d.ts.map +1 -0
  94. package/lib/setup-tests.js +17 -0
  95. package/lib/setup-tests.js.map +1 -0
  96. package/mf-manifest.json +212 -0
  97. package/mf-stats.json +235 -0
  98. package/package.json +55 -0
@@ -0,0 +1,182 @@
1
+ // Copyright 2023 The Perses Authors
2
+ // Licensed under the Apache License, Version 2.0 (the "License");
3
+ // you may not use this file except in compliance with the License.
4
+ // You may obtain a copy of the License at
5
+ //
6
+ // http://www.apache.org/licenses/LICENSE-2.0
7
+ //
8
+ // Unless required by applicable law or agreed to in writing, software
9
+ // distributed under the License is distributed on an "AS IS" BASIS,
10
+ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11
+ // See the License for the specific language governing permissions and
12
+ // limitations under the License.
13
+ import { jsx as _jsx } from "react/jsx-runtime";
14
+ import { useMemo } from 'react';
15
+ import { NoDataOverlay, useChartsTheme } from '@perses-dev/components';
16
+ import { Scatterplot } from './Scatterplot';
17
+ /** default size range of the circles diameter */ const DEFAULT_SIZE_RANGE = [
18
+ 6,
19
+ 20
20
+ ];
21
+ // Navigate to the Gantt Chart on the explore page by default
22
+ function defaultClickHandler(data) {
23
+ // clone the original query spec (including the datasource) and replace the query value with the trace id
24
+ const query = JSON.parse(JSON.stringify(data.query));
25
+ query.spec.plugin.spec.query = data.traceId;
26
+ const exploreParams = new URLSearchParams({
27
+ explorer: 'traces',
28
+ data: JSON.stringify({
29
+ queries: [
30
+ query
31
+ ]
32
+ })
33
+ });
34
+ // do not use react-router here, as downstream products, which embed this panel, may not have a compatible version of it
35
+ window.location.href = `/explore?${exploreParams}`;
36
+ }
37
+ /**
38
+ * ScatterChartPanel receives data from the DataQueriesProvider and transforms it
39
+ * into a `dataset` object that Apache ECharts can consume. Additionally,
40
+ * data formatting is also dictated in this component. Formatting includes
41
+ * datapoint size and color.
42
+ *
43
+ * Documentation for data structures accepted by Apache ECharts:
44
+ * https://echarts.apache.org/handbook/en/concepts/dataset
45
+ *
46
+ * Examples for scatter chart formatting in Apache ECharts:
47
+ * https://echarts.apache.org/examples/en/index.html#chart-type-scatter
48
+ *
49
+ * @returns a `ScatterPlot` component that contains an EChart which will handle
50
+ * visualization of the data.
51
+ */ export function ScatterChartPanel(props) {
52
+ const { spec, contentDimensions, queryResults: traceResults, onClick } = props;
53
+ const chartsTheme = useChartsTheme();
54
+ const defaultColor = chartsTheme.thresholds.defaultColor || 'blue';
55
+ const sizeRange = spec.sizeRange || DEFAULT_SIZE_RANGE;
56
+ // Generate dataset
57
+ // Transform Tempo API response to fit 'dataset' structure from Apache ECharts
58
+ // https://echarts.apache.org/handbook/en/concepts/dataset
59
+ const { dataset, minSpanCount, maxSpanCount } = useMemo(()=>{
60
+ const dataset = [];
61
+ let minSpanCount;
62
+ let maxSpanCount;
63
+ for (const result of traceResults){
64
+ if (result.data.searchResult === undefined) continue;
65
+ const dataSeries = result.data.searchResult.map((trace)=>{
66
+ let spanCount = 0;
67
+ let errorCount = 0;
68
+ for (const stats of Object.values(trace.serviceStats)){
69
+ spanCount += stats.spanCount;
70
+ errorCount += stats.errorCount ?? 0;
71
+ }
72
+ if (minSpanCount === undefined || spanCount < minSpanCount) {
73
+ minSpanCount = spanCount;
74
+ }
75
+ if (maxSpanCount === undefined || spanCount > maxSpanCount) {
76
+ maxSpanCount = spanCount;
77
+ }
78
+ const newTraceValue = {
79
+ ...trace,
80
+ query: result.definition,
81
+ name: `${trace.rootServiceName}: ${trace.rootTraceName}`,
82
+ startTime: new Date(trace.startTimeUnixMs),
83
+ spanCount,
84
+ errorCount
85
+ };
86
+ return newTraceValue;
87
+ });
88
+ dataset.push({
89
+ source: dataSeries
90
+ });
91
+ }
92
+ return {
93
+ dataset,
94
+ minSpanCount: minSpanCount ?? 0,
95
+ maxSpanCount: maxSpanCount ?? 0
96
+ };
97
+ }, [
98
+ traceResults
99
+ ]);
100
+ // Formatting for the dataset
101
+ // 1. Map x,y coordinates
102
+ // 2. Datapoint size corresponds to the number of spans in a trace
103
+ // 3. Color datapoint red if the trace contains an error
104
+ const series = useMemo(()=>{
105
+ const seriesTemplate2 = {
106
+ type: 'scatter',
107
+ encode: {
108
+ // Map to x-axis.
109
+ x: 'startTime',
110
+ // Map to y-axis.
111
+ y: 'durationMs'
112
+ },
113
+ symbolSize: function(data) {
114
+ // returns the diameter of the circles
115
+ return getSymbolSize(data.spanCount, [
116
+ minSpanCount,
117
+ maxSpanCount
118
+ ], sizeRange);
119
+ },
120
+ itemStyle: {
121
+ color: function(params) {
122
+ const traceData = params.data;
123
+ // If the trace contains an error, color the datapoint in red
124
+ if (traceData.errorCount > 0) {
125
+ return 'red';
126
+ }
127
+ // Else return default color
128
+ return defaultColor;
129
+ }
130
+ }
131
+ };
132
+ // Each data set needs to have a corresponding series formatting object
133
+ const series = [];
134
+ for(let i = 0; i < dataset.length; i++){
135
+ series.push({
136
+ ...seriesTemplate2,
137
+ datasetIndex: i
138
+ });
139
+ }
140
+ return series;
141
+ }, [
142
+ dataset,
143
+ defaultColor,
144
+ minSpanCount,
145
+ maxSpanCount,
146
+ sizeRange
147
+ ]);
148
+ const tracesFound = traceResults.some((traceData)=>(traceData.data?.searchResult ?? []).length > 0);
149
+ if (!tracesFound) {
150
+ return /*#__PURE__*/ _jsx(NoDataOverlay, {
151
+ resource: "traces"
152
+ });
153
+ }
154
+ const options = {
155
+ dataset: dataset,
156
+ series: series
157
+ };
158
+ if (contentDimensions === undefined) return null;
159
+ return /*#__PURE__*/ _jsx("div", {
160
+ "data-testid": "ScatterChartPanel_ScatterPlot",
161
+ children: /*#__PURE__*/ _jsx(Scatterplot, {
162
+ width: contentDimensions.width,
163
+ height: contentDimensions.height,
164
+ options: options,
165
+ onClick: onClick === null ? undefined : onClick ?? defaultClickHandler
166
+ })
167
+ });
168
+ }
169
+ // exported for tests
170
+ export function getSymbolSize(spanCount, spanCountRange, sizeRange) {
171
+ const [minSize, maxSize] = sizeRange;
172
+ const [minSpanCount, maxSpanCount] = spanCountRange;
173
+ // catch divison by zero
174
+ if (maxSpanCount - minSpanCount === 0) {
175
+ return maxSize;
176
+ }
177
+ // apply linear scale of spanCount from range [minSpanCount,maxSpanCount] to a value from range [minSize,maxSize]
178
+ const rel = (spanCount - minSpanCount) / (maxSpanCount - minSpanCount);
179
+ return minSize + (maxSize - minSize) * rel;
180
+ }
181
+
182
+ //# sourceMappingURL=ScatterChartPanel.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/ScatterChartPanel.tsx"],"sourcesContent":["// Copyright 2023 The Perses Authors\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n// http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nimport { PanelProps } from '@perses-dev/plugin-system';\nimport { ReactElement, useMemo } from 'react';\nimport { QueryDefinition, TraceData, TraceSearchResult } from '@perses-dev/core';\nimport { EChartsOption, SeriesOption } from 'echarts';\nimport { NoDataOverlay, useChartsTheme } from '@perses-dev/components';\nimport { Scatterplot } from './Scatterplot';\nimport { ScatterChartOptions } from './scatter-chart-model';\n\nexport interface EChartTraceValue extends Omit<TraceSearchResult, 'startTimeUnixMs' | 'serviceStats'> {\n name: string;\n query: QueryDefinition;\n startTime: Date;\n spanCount: number;\n errorCount: number;\n}\n\nexport interface ScatterChartPanelProps extends PanelProps<ScatterChartOptions, TraceData> {\n /**\n * Custom onClick event handler.\n * If this field is unset or undefined, a default handler which links to the Gantt chart on the explore page is configured.\n * Set this field explicitly to null to disable handling the click event.\n */\n onClick?: ((data: EChartTraceValue) => void) | null;\n}\n\n/** default size range of the circles diameter */\nconst DEFAULT_SIZE_RANGE: [number, number] = [6, 20];\n\n// Navigate to the Gantt Chart on the explore page by default\nfunction defaultClickHandler(data: EChartTraceValue): void {\n // clone the original query spec (including the datasource) and replace the query value with the trace id\n const query: QueryDefinition = JSON.parse(JSON.stringify(data.query));\n query.spec.plugin.spec.query = data.traceId;\n\n const exploreParams = new URLSearchParams({\n explorer: 'traces',\n data: JSON.stringify({ queries: [query] }),\n });\n\n // do not use react-router here, as downstream products, which embed this panel, may not have a compatible version of it\n window.location.href = `/explore?${exploreParams}`;\n}\n\n/**\n * ScatterChartPanel receives data from the DataQueriesProvider and transforms it\n * into a `dataset` object that Apache ECharts can consume. Additionally,\n * data formatting is also dictated in this component. Formatting includes\n * datapoint size and color.\n *\n * Documentation for data structures accepted by Apache ECharts:\n * https://echarts.apache.org/handbook/en/concepts/dataset\n *\n * Examples for scatter chart formatting in Apache ECharts:\n * https://echarts.apache.org/examples/en/index.html#chart-type-scatter\n *\n * @returns a `ScatterPlot` component that contains an EChart which will handle\n * visualization of the data.\n */\nexport function ScatterChartPanel(props: ScatterChartPanelProps): ReactElement | null {\n const { spec, contentDimensions, queryResults: traceResults, onClick } = props;\n const chartsTheme = useChartsTheme();\n const defaultColor = chartsTheme.thresholds.defaultColor || 'blue';\n const sizeRange = spec.sizeRange || DEFAULT_SIZE_RANGE;\n\n // Generate dataset\n // Transform Tempo API response to fit 'dataset' structure from Apache ECharts\n // https://echarts.apache.org/handbook/en/concepts/dataset\n const { dataset, minSpanCount, maxSpanCount } = useMemo(() => {\n const dataset = [];\n let minSpanCount: number | undefined;\n let maxSpanCount: number | undefined;\n for (const result of traceResults) {\n if (result.data.searchResult === undefined) continue;\n const dataSeries = result.data.searchResult.map((trace) => {\n let spanCount = 0;\n let errorCount = 0;\n for (const stats of Object.values(trace.serviceStats)) {\n spanCount += stats.spanCount;\n errorCount += stats.errorCount ?? 0;\n }\n\n if (minSpanCount === undefined || spanCount < minSpanCount) {\n minSpanCount = spanCount;\n }\n if (maxSpanCount === undefined || spanCount > maxSpanCount) {\n maxSpanCount = spanCount;\n }\n\n const newTraceValue: EChartTraceValue = {\n ...trace,\n query: result.definition,\n name: `${trace.rootServiceName}: ${trace.rootTraceName}`,\n startTime: new Date(trace.startTimeUnixMs), // convert unix epoch time to Date\n spanCount,\n errorCount,\n };\n return newTraceValue;\n });\n dataset.push({\n source: dataSeries,\n });\n }\n return { dataset, minSpanCount: minSpanCount ?? 0, maxSpanCount: maxSpanCount ?? 0 };\n }, [traceResults]);\n\n // Formatting for the dataset\n // 1. Map x,y coordinates\n // 2. Datapoint size corresponds to the number of spans in a trace\n // 3. Color datapoint red if the trace contains an error\n const series = useMemo(() => {\n const seriesTemplate2: SeriesOption = {\n type: 'scatter',\n encode: {\n // Map to x-axis.\n x: 'startTime',\n // Map to y-axis.\n y: 'durationMs',\n },\n symbolSize: function (data) {\n // returns the diameter of the circles\n return getSymbolSize(data.spanCount, [minSpanCount, maxSpanCount], sizeRange);\n },\n itemStyle: {\n color: function (params) {\n const traceData: EChartTraceValue = params.data as EChartTraceValue;\n // If the trace contains an error, color the datapoint in red\n if (traceData.errorCount > 0) {\n return 'red';\n }\n // Else return default color\n return defaultColor;\n },\n },\n };\n\n // Each data set needs to have a corresponding series formatting object\n const series = [];\n for (let i = 0; i < dataset.length; i++) {\n series.push({ ...seriesTemplate2, datasetIndex: i });\n }\n return series;\n }, [dataset, defaultColor, minSpanCount, maxSpanCount, sizeRange]);\n\n const tracesFound = traceResults.some((traceData) => (traceData.data?.searchResult ?? []).length > 0);\n if (!tracesFound) {\n return <NoDataOverlay resource=\"traces\" />;\n }\n\n const options: EChartsOption = {\n dataset: dataset,\n series: series,\n };\n\n if (contentDimensions === undefined) return null;\n\n return (\n <div data-testid=\"ScatterChartPanel_ScatterPlot\">\n <Scatterplot\n width={contentDimensions.width}\n height={contentDimensions.height}\n options={options}\n onClick={onClick === null ? undefined : (onClick ?? defaultClickHandler)}\n />\n </div>\n );\n}\n\n// exported for tests\nexport function getSymbolSize(\n spanCount: number,\n spanCountRange: [number, number],\n sizeRange: [number, number]\n): number {\n const [minSize, maxSize] = sizeRange;\n const [minSpanCount, maxSpanCount] = spanCountRange;\n\n // catch divison by zero\n if (maxSpanCount - minSpanCount === 0) {\n return maxSize;\n }\n\n // apply linear scale of spanCount from range [minSpanCount,maxSpanCount] to a value from range [minSize,maxSize]\n const rel = (spanCount - minSpanCount) / (maxSpanCount - minSpanCount);\n return minSize + (maxSize - minSize) * rel;\n}\n"],"names":["useMemo","NoDataOverlay","useChartsTheme","Scatterplot","DEFAULT_SIZE_RANGE","defaultClickHandler","data","query","JSON","parse","stringify","spec","plugin","traceId","exploreParams","URLSearchParams","explorer","queries","window","location","href","ScatterChartPanel","props","contentDimensions","queryResults","traceResults","onClick","chartsTheme","defaultColor","thresholds","sizeRange","dataset","minSpanCount","maxSpanCount","result","searchResult","undefined","dataSeries","map","trace","spanCount","errorCount","stats","Object","values","serviceStats","newTraceValue","definition","name","rootServiceName","rootTraceName","startTime","Date","startTimeUnixMs","push","source","series","seriesTemplate2","type","encode","x","y","symbolSize","getSymbolSize","itemStyle","color","params","traceData","i","length","datasetIndex","tracesFound","some","resource","options","div","data-testid","width","height","spanCountRange","minSize","maxSize","rel"],"mappings":"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"}
@@ -0,0 +1,11 @@
1
+ import { ReactElement } from 'react';
2
+ import { EChartsOption } from 'echarts';
3
+ interface ScatterplotProps<T> {
4
+ width: number;
5
+ height: number;
6
+ options: EChartsOption;
7
+ onClick?: (data: T) => void;
8
+ }
9
+ export declare function Scatterplot<T>(props: ScatterplotProps<T>): ReactElement;
10
+ export {};
11
+ //# sourceMappingURL=Scatterplot.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Scatterplot.d.ts","sourceRoot":"","sources":["../../src/Scatterplot.tsx"],"names":[],"mappings":"AAaA,OAAO,EAAE,YAAY,EAAW,MAAM,OAAO,CAAC;AAa9C,OAAO,EAAE,aAAa,EAAuB,MAAM,SAAS,CAAC;AAe7D,UAAU,gBAAgB,CAAC,CAAC;IAC1B,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,aAAa,CAAC;IACvB,OAAO,CAAC,EAAE,CAAC,IAAI,EAAE,CAAC,KAAK,IAAI,CAAC;CAC7B;AAOD,wBAAgB,WAAW,CAAC,CAAC,EAAE,KAAK,EAAE,gBAAgB,CAAC,CAAC,CAAC,GAAG,YAAY,CA2EvE"}
@@ -0,0 +1,113 @@
1
+ // Copyright 2023 The Perses Authors
2
+ // Licensed under the Apache License, Version 2.0 (the "License");
3
+ // you may not use this file except in compliance with the License.
4
+ // You may obtain a copy of the License at
5
+ //
6
+ // http://www.apache.org/licenses/LICENSE-2.0
7
+ //
8
+ // Unless required by applicable law or agreed to in writing, software
9
+ // distributed under the License is distributed on an "AS IS" BASIS,
10
+ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11
+ // See the License for the specific language governing permissions and
12
+ // limitations under the License.
13
+ import { jsx as _jsx } from "react/jsx-runtime";
14
+ import { useMemo } from 'react';
15
+ import { EChart, useChartsTheme } from '@perses-dev/components';
16
+ import { use } from 'echarts/core';
17
+ import { ScatterChart as EChartsScatterChart } from 'echarts/charts';
18
+ import { DatasetComponent, DataZoomComponent, LegendComponent, GridComponent, TitleComponent, TooltipComponent } from 'echarts/components';
19
+ import { CanvasRenderer } from 'echarts/renderers';
20
+ import { formatValue } from '@perses-dev/core';
21
+ use([
22
+ DatasetComponent,
23
+ DataZoomComponent,
24
+ LegendComponent,
25
+ EChartsScatterChart,
26
+ GridComponent,
27
+ TitleComponent,
28
+ TooltipComponent,
29
+ CanvasRenderer
30
+ ]);
31
+ const DATE_FORMATTER = new Intl.DateTimeFormat(undefined, {
32
+ dateStyle: 'long',
33
+ timeStyle: 'medium'
34
+ }).format;
35
+ export function Scatterplot(props) {
36
+ const { width, height, options, onClick } = props;
37
+ const chartsTheme = useChartsTheme();
38
+ // Apache EChart Options Docs: https://echarts.apache.org/en/option.html
39
+ const eChartOptions = {
40
+ dataset: options.dataset,
41
+ series: options.series,
42
+ dataZoom: options.dataZoom,
43
+ grid: {
44
+ bottom: 40,
45
+ top: 50,
46
+ left: 50,
47
+ right: 100
48
+ },
49
+ xAxis: {
50
+ type: 'time',
51
+ name: 'Local Time'
52
+ },
53
+ yAxis: {
54
+ scale: true,
55
+ type: 'value',
56
+ name: 'Duration',
57
+ axisLabel: {
58
+ formatter: (durationMs)=>formatValue(durationMs, {
59
+ unit: 'milliseconds'
60
+ })
61
+ }
62
+ },
63
+ animation: false,
64
+ tooltip: {
65
+ padding: 5,
66
+ borderWidth: 1,
67
+ trigger: 'axis',
68
+ axisPointer: {
69
+ type: 'cross'
70
+ },
71
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
72
+ formatter: function(params) {
73
+ // TODO: import type from ECharts instead of using any
74
+ const data = params[0].data;
75
+ return [
76
+ `<b>Service name</b>: ${data.rootServiceName}<br/>`,
77
+ `<b>Span name</b>: ${data.rootTraceName}<br/>`,
78
+ `<b>Time</b>: ${DATE_FORMATTER(data.startTime)}<br/>`,
79
+ `<b>Duration</b>: ${formatValue(data.durationMs, {
80
+ unit: 'milliseconds'
81
+ })}<br/>`,
82
+ `<b>Span count</b>: ${data.spanCount} (${data.errorCount} errors)<br/>`
83
+ ].join('');
84
+ }
85
+ },
86
+ legend: {
87
+ show: true,
88
+ type: 'scroll',
89
+ orient: 'horizontal',
90
+ bottom: 0
91
+ }
92
+ };
93
+ const handleEvents = useMemo(()=>{
94
+ const handlers = {};
95
+ if (onClick) {
96
+ handlers.click = (params)=>onClick(params.data);
97
+ }
98
+ return handlers;
99
+ }, [
100
+ onClick
101
+ ]);
102
+ return /*#__PURE__*/ _jsx(EChart, {
103
+ sx: {
104
+ width: width,
105
+ height: height
106
+ },
107
+ option: eChartOptions,
108
+ theme: chartsTheme.echartsTheme,
109
+ onEvents: handleEvents
110
+ });
111
+ }
112
+
113
+ //# sourceMappingURL=Scatterplot.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/Scatterplot.tsx"],"sourcesContent":["// Copyright 2023 The Perses Authors\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n// http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nimport { ReactElement, useMemo } from 'react';\nimport { EChart, OnEventsType, useChartsTheme } from '@perses-dev/components';\nimport { use, EChartsCoreOption } from 'echarts/core';\nimport { ScatterChart as EChartsScatterChart } from 'echarts/charts';\nimport {\n DatasetComponent,\n DataZoomComponent,\n LegendComponent,\n GridComponent,\n TitleComponent,\n TooltipComponent,\n} from 'echarts/components';\nimport { CanvasRenderer } from 'echarts/renderers';\nimport { EChartsOption, ScatterSeriesOption } from 'echarts';\nimport { formatValue } from '@perses-dev/core';\nimport { EChartTraceValue } from './ScatterChartPanel';\n\nuse([\n DatasetComponent,\n DataZoomComponent,\n LegendComponent,\n EChartsScatterChart,\n GridComponent,\n TitleComponent,\n TooltipComponent,\n CanvasRenderer,\n]);\n\ninterface ScatterplotProps<T> {\n width: number;\n height: number;\n options: EChartsOption;\n onClick?: (data: T) => void;\n}\n\nconst DATE_FORMATTER = new Intl.DateTimeFormat(undefined, {\n dateStyle: 'long',\n timeStyle: 'medium',\n}).format;\n\nexport function Scatterplot<T>(props: ScatterplotProps<T>): ReactElement {\n const { width, height, options, onClick } = props;\n const chartsTheme = useChartsTheme();\n\n // Apache EChart Options Docs: https://echarts.apache.org/en/option.html\n const eChartOptions: EChartsCoreOption = {\n dataset: options.dataset,\n series: options.series,\n dataZoom: options.dataZoom,\n grid: {\n bottom: 40,\n top: 50,\n left: 50,\n right: 100,\n },\n xAxis: {\n type: 'time',\n name: 'Local Time',\n },\n yAxis: {\n scale: true,\n type: 'value',\n name: 'Duration',\n axisLabel: {\n formatter: (durationMs: number) => formatValue(durationMs, { unit: 'milliseconds' }),\n },\n },\n animation: false,\n tooltip: {\n padding: 5,\n borderWidth: 1,\n trigger: 'axis',\n axisPointer: {\n type: 'cross',\n },\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n formatter: function (params: any) {\n // TODO: import type from ECharts instead of using any\n const data = params[0].data as EChartTraceValue;\n return [\n `<b>Service name</b>: ${data.rootServiceName}<br/>`,\n `<b>Span name</b>: ${data.rootTraceName}<br/>`,\n `<b>Time</b>: ${DATE_FORMATTER(data.startTime)}<br/>`,\n `<b>Duration</b>: ${formatValue(data.durationMs, { unit: 'milliseconds' })}<br/>`,\n `<b>Span count</b>: ${data.spanCount} (${data.errorCount} errors)<br/>`,\n ].join('');\n },\n },\n legend: {\n show: true,\n type: 'scroll',\n orient: 'horizontal',\n bottom: 0,\n },\n };\n\n const handleEvents: OnEventsType<ScatterSeriesOption['data'] | unknown> = useMemo(() => {\n const handlers: OnEventsType<ScatterSeriesOption['data'] | unknown> = {};\n if (onClick) {\n handlers.click = (params): void => onClick(params.data as T);\n }\n return handlers;\n }, [onClick]);\n\n return (\n <EChart\n sx={{\n width: width,\n height: height,\n }}\n option={eChartOptions}\n theme={chartsTheme.echartsTheme}\n onEvents={handleEvents}\n />\n );\n}\n"],"names":["useMemo","EChart","useChartsTheme","use","ScatterChart","EChartsScatterChart","DatasetComponent","DataZoomComponent","LegendComponent","GridComponent","TitleComponent","TooltipComponent","CanvasRenderer","formatValue","DATE_FORMATTER","Intl","DateTimeFormat","undefined","dateStyle","timeStyle","format","Scatterplot","props","width","height","options","onClick","chartsTheme","eChartOptions","dataset","series","dataZoom","grid","bottom","top","left","right","xAxis","type","name","yAxis","scale","axisLabel","formatter","durationMs","unit","animation","tooltip","padding","borderWidth","trigger","axisPointer","params","data","rootServiceName","rootTraceName","startTime","spanCount","errorCount","join","legend","show","orient","handleEvents","handlers","click","sx","option","theme","echartsTheme","onEvents"],"mappings":"AAAA,oCAAoC;AACpC,kEAAkE;AAClE,mEAAmE;AACnE,0CAA0C;AAC1C,EAAE;AACF,6CAA6C;AAC7C,EAAE;AACF,sEAAsE;AACtE,oEAAoE;AACpE,2EAA2E;AAC3E,sEAAsE;AACtE,iCAAiC;;AAEjC,SAAuBA,OAAO,QAAQ,QAAQ;AAC9C,SAASC,MAAM,EAAgBC,cAAc,QAAQ,yBAAyB;AAC9E,SAASC,GAAG,QAA2B,eAAe;AACtD,SAASC,gBAAgBC,mBAAmB,QAAQ,iBAAiB;AACrE,SACEC,gBAAgB,EAChBC,iBAAiB,EACjBC,eAAe,EACfC,aAAa,EACbC,cAAc,EACdC,gBAAgB,QACX,qBAAqB;AAC5B,SAASC,cAAc,QAAQ,oBAAoB;AAEnD,SAASC,WAAW,QAAQ,mBAAmB;AAG/CV,IAAI;IACFG;IACAC;IACAC;IACAH;IACAI;IACAC;IACAC;IACAC;CACD;AASD,MAAME,iBAAiB,IAAIC,KAAKC,cAAc,CAACC,WAAW;IACxDC,WAAW;IACXC,WAAW;AACb,GAAGC,MAAM;AAET,OAAO,SAASC,YAAeC,KAA0B;IACvD,MAAM,EAAEC,KAAK,EAAEC,MAAM,EAAEC,OAAO,EAAEC,OAAO,EAAE,GAAGJ;IAC5C,MAAMK,cAAczB;IAEpB,wEAAwE;IACxE,MAAM0B,gBAAmC;QACvCC,SAASJ,QAAQI,OAAO;QACxBC,QAAQL,QAAQK,MAAM;QACtBC,UAAUN,QAAQM,QAAQ;QAC1BC,MAAM;YACJC,QAAQ;YACRC,KAAK;YACLC,MAAM;YACNC,OAAO;QACT;QACAC,OAAO;YACLC,MAAM;YACNC,MAAM;QACR;QACAC,OAAO;YACLC,OAAO;YACPH,MAAM;YACNC,MAAM;YACNG,WAAW;gBACTC,WAAW,CAACC,aAAuB/B,YAAY+B,YAAY;wBAAEC,MAAM;oBAAe;YACpF;QACF;QACAC,WAAW;QACXC,SAAS;YACPC,SAAS;YACTC,aAAa;YACbC,SAAS;YACTC,aAAa;gBACXb,MAAM;YACR;YACA,8DAA8D;YAC9DK,WAAW,SAAUS,MAAW;gBAC9B,sDAAsD;gBACtD,MAAMC,OAAOD,MAAM,CAAC,EAAE,CAACC,IAAI;gBAC3B,OAAO;oBACL,CAAC,qBAAqB,EAAEA,KAAKC,eAAe,CAAC,KAAK,CAAC;oBACnD,CAAC,kBAAkB,EAAED,KAAKE,aAAa,CAAC,KAAK,CAAC;oBAC9C,CAAC,aAAa,EAAEzC,eAAeuC,KAAKG,SAAS,EAAE,KAAK,CAAC;oBACrD,CAAC,iBAAiB,EAAE3C,YAAYwC,KAAKT,UAAU,EAAE;wBAAEC,MAAM;oBAAe,GAAG,KAAK,CAAC;oBACjF,CAAC,mBAAmB,EAAEQ,KAAKI,SAAS,CAAC,EAAE,EAAEJ,KAAKK,UAAU,CAAC,aAAa,CAAC;iBACxE,CAACC,IAAI,CAAC;YACT;QACF;QACAC,QAAQ;YACNC,MAAM;YACNvB,MAAM;YACNwB,QAAQ;YACR7B,QAAQ;QACV;IACF;IAEA,MAAM8B,eAAoE/D,QAAQ;QAChF,MAAMgE,WAAgE,CAAC;QACvE,IAAItC,SAAS;YACXsC,SAASC,KAAK,GAAG,CAACb,SAAiB1B,QAAQ0B,OAAOC,IAAI;QACxD;QACA,OAAOW;IACT,GAAG;QAACtC;KAAQ;IAEZ,qBACE,KAACzB;QACCiE,IAAI;YACF3C,OAAOA;YACPC,QAAQA;QACV;QACA2C,QAAQvC;QACRwC,OAAOzC,YAAY0C,YAAY;QAC/BC,UAAUP;;AAGhB"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=bootstrap.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bootstrap.d.ts","sourceRoot":"","sources":["../../src/bootstrap.tsx"],"names":[],"mappings":""}
@@ -0,0 +1,19 @@
1
+ // Copyright 2024 The Perses Authors
2
+ // Licensed under the Apache License, Version 2.0 (the "License");
3
+ // you may not use this file except in compliance with the License.
4
+ // You may obtain a copy of the License at
5
+ //
6
+ // http://www.apache.org/licenses/LICENSE-2.0
7
+ //
8
+ // Unless required by applicable law or agreed to in writing, software
9
+ // distributed under the License is distributed on an "AS IS" BASIS,
10
+ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11
+ // See the License for the specific language governing permissions and
12
+ // limitations under the License.
13
+ import { jsx as _jsx } from "react/jsx-runtime";
14
+ import React from 'react';
15
+ import ReactDOM from 'react-dom/client';
16
+ const root = ReactDOM.createRoot(document.getElementById('root'));
17
+ root.render(/*#__PURE__*/ _jsx(React.StrictMode, {}));
18
+
19
+ //# sourceMappingURL=bootstrap.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/bootstrap.tsx"],"sourcesContent":["// Copyright 2024 The Perses Authors\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n// http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nimport React from 'react';\nimport ReactDOM from 'react-dom/client';\n\nconst root = ReactDOM.createRoot(document.getElementById('root')!);\nroot.render(<React.StrictMode></React.StrictMode>);\n"],"names":["React","ReactDOM","root","createRoot","document","getElementById","render","StrictMode"],"mappings":"AAAA,oCAAoC;AACpC,kEAAkE;AAClE,mEAAmE;AACnE,0CAA0C;AAC1C,EAAE;AACF,6CAA6C;AAC7C,EAAE;AACF,sEAAsE;AACtE,oEAAoE;AACpE,2EAA2E;AAC3E,sEAAsE;AACtE,iCAAiC;;AAEjC,OAAOA,WAAW,QAAQ;AAC1B,OAAOC,cAAc,mBAAmB;AAExC,MAAMC,OAAOD,SAASE,UAAU,CAACC,SAASC,cAAc,CAAC;AACzDH,KAAKI,MAAM,eAAC,KAACN,MAAMO,UAAU"}
@@ -0,0 +1,33 @@
1
+ // Copyright 2023 The Perses Authors
2
+ // Licensed under the Apache License, Version 2.0 (the "License");
3
+ // you may not use this file except in compliance with the License.
4
+ // You may obtain a copy of the License at
5
+ //
6
+ // http://www.apache.org/licenses/LICENSE-2.0
7
+ //
8
+ // Unless required by applicable law or agreed to in writing, software
9
+ // distributed under the License is distributed on an "AS IS" BASIS,
10
+ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11
+ // See the License for the specific language governing permissions and
12
+ // limitations under the License.
13
+ "use strict";
14
+ Object.defineProperty(exports, "__esModule", {
15
+ value: true
16
+ });
17
+ Object.defineProperty(exports, "ScatterChart", {
18
+ enumerable: true,
19
+ get: function() {
20
+ return ScatterChart;
21
+ }
22
+ });
23
+ const _scatterchartmodel = require("./scatter-chart-model");
24
+ const _ScatterChartPanel = require("./ScatterChartPanel");
25
+ const ScatterChart = {
26
+ PanelComponent: _ScatterChartPanel.ScatterChartPanel,
27
+ // TODO: add a chart options editor plugin, for example:
28
+ // panelOptionsEditorComponents: [{ label: 'Settings', content: ScatterChartOptionsEditorSettings }],
29
+ supportedQueryTypes: [
30
+ 'TraceQuery'
31
+ ],
32
+ createInitialOptions: _scatterchartmodel.createInitialScatterChartOptions
33
+ };
@@ -0,0 +1,183 @@
1
+ // Copyright 2023 The Perses Authors
2
+ // Licensed under the Apache License, Version 2.0 (the "License");
3
+ // you may not use this file except in compliance with the License.
4
+ // You may obtain a copy of the License at
5
+ //
6
+ // http://www.apache.org/licenses/LICENSE-2.0
7
+ //
8
+ // Unless required by applicable law or agreed to in writing, software
9
+ // distributed under the License is distributed on an "AS IS" BASIS,
10
+ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11
+ // See the License for the specific language governing permissions and
12
+ // limitations under the License.
13
+ "use strict";
14
+ Object.defineProperty(exports, "__esModule", {
15
+ value: true
16
+ });
17
+ function _export(target, all) {
18
+ for(var name in all)Object.defineProperty(target, name, {
19
+ enumerable: true,
20
+ get: all[name]
21
+ });
22
+ }
23
+ _export(exports, {
24
+ ScatterChartPanel: function() {
25
+ return ScatterChartPanel;
26
+ },
27
+ getSymbolSize: function() {
28
+ return getSymbolSize;
29
+ }
30
+ });
31
+ const _jsxruntime = require("react/jsx-runtime");
32
+ const _react = require("react");
33
+ const _components = require("@perses-dev/components");
34
+ const _Scatterplot = require("./Scatterplot");
35
+ /** default size range of the circles diameter */ const DEFAULT_SIZE_RANGE = [
36
+ 6,
37
+ 20
38
+ ];
39
+ // Navigate to the Gantt Chart on the explore page by default
40
+ function defaultClickHandler(data) {
41
+ // clone the original query spec (including the datasource) and replace the query value with the trace id
42
+ const query = JSON.parse(JSON.stringify(data.query));
43
+ query.spec.plugin.spec.query = data.traceId;
44
+ const exploreParams = new URLSearchParams({
45
+ explorer: 'traces',
46
+ data: JSON.stringify({
47
+ queries: [
48
+ query
49
+ ]
50
+ })
51
+ });
52
+ // do not use react-router here, as downstream products, which embed this panel, may not have a compatible version of it
53
+ window.location.href = `/explore?${exploreParams}`;
54
+ }
55
+ function ScatterChartPanel(props) {
56
+ const { spec, contentDimensions, queryResults: traceResults, onClick } = props;
57
+ const chartsTheme = (0, _components.useChartsTheme)();
58
+ const defaultColor = chartsTheme.thresholds.defaultColor || 'blue';
59
+ const sizeRange = spec.sizeRange || DEFAULT_SIZE_RANGE;
60
+ // Generate dataset
61
+ // Transform Tempo API response to fit 'dataset' structure from Apache ECharts
62
+ // https://echarts.apache.org/handbook/en/concepts/dataset
63
+ const { dataset, minSpanCount, maxSpanCount } = (0, _react.useMemo)(()=>{
64
+ const dataset = [];
65
+ let minSpanCount;
66
+ let maxSpanCount;
67
+ for (const result of traceResults){
68
+ if (result.data.searchResult === undefined) continue;
69
+ const dataSeries = result.data.searchResult.map((trace)=>{
70
+ let spanCount = 0;
71
+ let errorCount = 0;
72
+ for (const stats of Object.values(trace.serviceStats)){
73
+ spanCount += stats.spanCount;
74
+ errorCount += stats.errorCount ?? 0;
75
+ }
76
+ if (minSpanCount === undefined || spanCount < minSpanCount) {
77
+ minSpanCount = spanCount;
78
+ }
79
+ if (maxSpanCount === undefined || spanCount > maxSpanCount) {
80
+ maxSpanCount = spanCount;
81
+ }
82
+ const newTraceValue = {
83
+ ...trace,
84
+ query: result.definition,
85
+ name: `${trace.rootServiceName}: ${trace.rootTraceName}`,
86
+ startTime: new Date(trace.startTimeUnixMs),
87
+ spanCount,
88
+ errorCount
89
+ };
90
+ return newTraceValue;
91
+ });
92
+ dataset.push({
93
+ source: dataSeries
94
+ });
95
+ }
96
+ return {
97
+ dataset,
98
+ minSpanCount: minSpanCount ?? 0,
99
+ maxSpanCount: maxSpanCount ?? 0
100
+ };
101
+ }, [
102
+ traceResults
103
+ ]);
104
+ // Formatting for the dataset
105
+ // 1. Map x,y coordinates
106
+ // 2. Datapoint size corresponds to the number of spans in a trace
107
+ // 3. Color datapoint red if the trace contains an error
108
+ const series = (0, _react.useMemo)(()=>{
109
+ const seriesTemplate2 = {
110
+ type: 'scatter',
111
+ encode: {
112
+ // Map to x-axis.
113
+ x: 'startTime',
114
+ // Map to y-axis.
115
+ y: 'durationMs'
116
+ },
117
+ symbolSize: function(data) {
118
+ // returns the diameter of the circles
119
+ return getSymbolSize(data.spanCount, [
120
+ minSpanCount,
121
+ maxSpanCount
122
+ ], sizeRange);
123
+ },
124
+ itemStyle: {
125
+ color: function(params) {
126
+ const traceData = params.data;
127
+ // If the trace contains an error, color the datapoint in red
128
+ if (traceData.errorCount > 0) {
129
+ return 'red';
130
+ }
131
+ // Else return default color
132
+ return defaultColor;
133
+ }
134
+ }
135
+ };
136
+ // Each data set needs to have a corresponding series formatting object
137
+ const series = [];
138
+ for(let i = 0; i < dataset.length; i++){
139
+ series.push({
140
+ ...seriesTemplate2,
141
+ datasetIndex: i
142
+ });
143
+ }
144
+ return series;
145
+ }, [
146
+ dataset,
147
+ defaultColor,
148
+ minSpanCount,
149
+ maxSpanCount,
150
+ sizeRange
151
+ ]);
152
+ const tracesFound = traceResults.some((traceData)=>(traceData.data?.searchResult ?? []).length > 0);
153
+ if (!tracesFound) {
154
+ return /*#__PURE__*/ (0, _jsxruntime.jsx)(_components.NoDataOverlay, {
155
+ resource: "traces"
156
+ });
157
+ }
158
+ const options = {
159
+ dataset: dataset,
160
+ series: series
161
+ };
162
+ if (contentDimensions === undefined) return null;
163
+ return /*#__PURE__*/ (0, _jsxruntime.jsx)("div", {
164
+ "data-testid": "ScatterChartPanel_ScatterPlot",
165
+ children: /*#__PURE__*/ (0, _jsxruntime.jsx)(_Scatterplot.Scatterplot, {
166
+ width: contentDimensions.width,
167
+ height: contentDimensions.height,
168
+ options: options,
169
+ onClick: onClick === null ? undefined : onClick ?? defaultClickHandler
170
+ })
171
+ });
172
+ }
173
+ function getSymbolSize(spanCount, spanCountRange, sizeRange) {
174
+ const [minSize, maxSize] = sizeRange;
175
+ const [minSpanCount, maxSpanCount] = spanCountRange;
176
+ // catch divison by zero
177
+ if (maxSpanCount - minSpanCount === 0) {
178
+ return maxSize;
179
+ }
180
+ // apply linear scale of spanCount from range [minSpanCount,maxSpanCount] to a value from range [minSize,maxSize]
181
+ const rel = (spanCount - minSpanCount) / (maxSpanCount - minSpanCount);
182
+ return minSize + (maxSize - minSize) * rel;
183
+ }