@trebco/treb 28.17.4 → 29.1.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/dist/treb-spreadsheet-light.mjs +12 -12
- package/dist/treb-spreadsheet.mjs +12 -12
- package/dist/treb.d.ts +121 -82
- package/eslint.config.js +21 -0
- package/package.json +6 -6
- package/treb-base-types/src/area.ts +4 -2
- package/treb-base-types/src/cell.ts +1 -1
- package/treb-base-types/src/cells.ts +16 -7
- package/treb-base-types/src/dom-utilities.ts +4 -2
- package/treb-base-types/src/import.ts +2 -2
- package/treb-base-types/src/rectangle.ts +5 -5
- package/treb-base-types/src/union.ts +6 -1
- package/treb-base-types/src/value-type.ts +1 -1
- package/treb-calculator/src/calculator.ts +114 -165
- package/treb-calculator/src/dag/calculation_leaf_vertex.ts +1 -2
- package/treb-calculator/src/dag/graph.ts +3 -3
- package/treb-calculator/src/dag/spreadsheet_vertex.ts +2 -2
- package/treb-calculator/src/dag/state_leaf_vertex.ts +2 -4
- package/treb-calculator/src/descriptors.ts +28 -2
- package/treb-calculator/src/expression-calculator.ts +25 -34
- package/treb-calculator/src/function-error.ts +2 -2
- package/treb-calculator/src/function-library.ts +16 -0
- package/treb-calculator/src/functions/base-functions.ts +185 -211
- package/treb-calculator/src/functions/checkbox.ts +0 -1
- package/treb-calculator/src/functions/complex-functions.ts +49 -47
- package/treb-calculator/src/functions/finance-functions.ts +10 -10
- package/treb-calculator/src/functions/function-utilities.ts +26 -0
- package/treb-calculator/src/functions/information-functions.ts +21 -41
- package/treb-calculator/src/functions/matrix-functions.ts +8 -1
- package/treb-calculator/src/functions/sparkline.ts +6 -4
- package/treb-calculator/src/functions/statistics-functions.ts +21 -17
- package/treb-calculator/src/functions/text-functions.ts +14 -13
- package/treb-calculator/src/primitives.ts +48 -37
- package/treb-calculator/src/utilities.ts +117 -134
- package/treb-charts/src/chart-functions.ts +3 -3
- package/treb-charts/src/chart-types.ts +42 -1
- package/treb-charts/src/chart-utils.ts +155 -113
- package/treb-charts/src/chart.ts +6 -5
- package/treb-charts/src/default-chart-renderer.ts +6 -5
- package/treb-charts/src/renderer.ts +12 -11
- package/treb-charts/src/util.ts +25 -36
- package/treb-data-model/package.json +5 -0
- package/{treb-grid/src/types → treb-data-model/src}/annotation.ts +2 -2
- package/{treb-grid/src/types → treb-data-model/src}/conditional_format.ts +20 -0
- package/{treb-grid/src/types → treb-data-model/src}/data_model.ts +231 -133
- package/treb-data-model/src/index.ts +45 -0
- package/{treb-grid/src/types/named_range.ts → treb-data-model/src/named.ts} +459 -376
- package/{treb-grid/src/types → treb-data-model/src}/sheet.ts +13 -5
- package/treb-data-model/src/sheet_collection.ts +114 -0
- package/{treb-grid/src/types → treb-data-model/src}/sheet_types.ts +6 -3
- package/treb-embed/modern.tsconfig.json +1 -0
- package/treb-embed/src/custom-element/spreadsheet-constructor.ts +2 -3
- package/treb-embed/src/embedded-spreadsheet.ts +125 -270
- package/treb-embed/src/selection-state.ts +1 -1
- package/treb-embed/src/toolbar-message.ts +1 -1
- package/treb-embed/src/types.ts +13 -5
- package/treb-export/src/export-worker/export-worker.ts +22 -7
- package/treb-export/src/export2.ts +110 -41
- package/treb-export/src/import2.ts +6 -5
- package/treb-export/src/workbook2.ts +31 -13
- package/treb-export/src/xml-utils.ts +5 -1
- package/treb-format/src/format.ts +8 -6
- package/treb-grid/src/editors/autocomplete.ts +2 -2
- package/treb-grid/src/editors/autocomplete_matcher.ts +57 -19
- package/treb-grid/src/editors/editor.ts +27 -25
- package/treb-grid/src/editors/formula_bar.ts +5 -5
- package/treb-grid/src/editors/overlay_editor.ts +1 -2
- package/treb-grid/src/index.ts +0 -11
- package/treb-grid/src/layout/base_layout.ts +20 -8
- package/treb-grid/src/layout/grid_layout.ts +2 -2
- package/treb-grid/src/layout/mock-layout.ts +5 -6
- package/treb-grid/src/render/selection-renderer.ts +2 -3
- package/treb-grid/src/render/tile_renderer.ts +1 -1
- package/treb-grid/src/types/grid.ts +95 -66
- package/treb-grid/src/types/grid_base.ts +76 -60
- package/treb-grid/src/types/grid_command.ts +3 -2
- package/treb-grid/src/types/grid_events.ts +12 -6
- package/treb-grid/src/types/tab_bar.ts +1 -2
- package/treb-parser/src/parser-types.ts +2 -1
- package/treb-parser/src/parser.ts +7 -5
- package/treb-utils/src/event_source.ts +1 -1
- package/treb-utils/src/serialize_html.ts +31 -6
- package/.eslintignore +0 -8
- package/.eslintrc.cjs +0 -168
- package/treb-grid/src/layout/rectangle_cache.ts +0 -86
- /package/{treb-grid/src/types → treb-data-model/src}/serialize_options.ts +0 -0
- /package/{treb-grid/src/types/grid_selection.ts → treb-data-model/src/sheet_selection.ts} +0 -0
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
|
|
2
|
-
import { type UnionValue, ValueType, type ArrayUnion } from 'treb-base-types';
|
|
3
|
-
import { LegendStyle } from './chart-types';
|
|
4
|
-
import type { SubSeries, SeriesType, BarData, ChartDataBaseType, ChartData, ScatterData2,
|
|
2
|
+
import { type UnionValue, ValueType, type ArrayUnion, IsComplex, type CellValue } from 'treb-base-types';
|
|
3
|
+
import { IsArrayUnion, IsMetadata, IsSeries, LegendStyle } from './chart-types';
|
|
4
|
+
import type { SubSeries, SeriesType, BarData, ChartDataBaseType, ChartData, ScatterData2, DonutSlice, BubbleChartData } from './chart-types';
|
|
5
5
|
import { NumberFormatCache } from 'treb-format';
|
|
6
6
|
import { Util } from './util';
|
|
7
|
+
import type { ReferenceSeries } from './chart-types';
|
|
7
8
|
|
|
8
9
|
/**
|
|
9
10
|
* this file is the concrete translation from function arguments
|
|
@@ -15,7 +16,9 @@ import { Util } from './util';
|
|
|
15
16
|
|
|
16
17
|
const DEFAULT_FORMAT = '#,##0.00'; // why not use "general", or whatever the usual default is?
|
|
17
18
|
|
|
18
|
-
export const ReadSeries = (data:
|
|
19
|
+
export const ReadSeries = (data: ReferenceSeries['value']): SeriesType => {
|
|
20
|
+
|
|
21
|
+
const [label, x, y, z, index, subtype, data_labels] = data;
|
|
19
22
|
|
|
20
23
|
// series type is (now)
|
|
21
24
|
//
|
|
@@ -33,80 +36,74 @@ export const ReadSeries = (data: Array<any>): SeriesType => {
|
|
|
33
36
|
y: { data: [] },
|
|
34
37
|
};
|
|
35
38
|
|
|
36
|
-
if (
|
|
37
|
-
series.index =
|
|
39
|
+
if (typeof index === 'number') {
|
|
40
|
+
series.index = index;
|
|
38
41
|
}
|
|
39
42
|
|
|
40
|
-
if (
|
|
41
|
-
series.subtype =
|
|
43
|
+
if (subtype) {
|
|
44
|
+
series.subtype = subtype.toString();
|
|
42
45
|
}
|
|
43
46
|
|
|
44
|
-
if (
|
|
45
|
-
const labels = Util.Flatten(Array.isArray(
|
|
47
|
+
if (data_labels) {
|
|
48
|
+
const labels = Util.Flatten<CellValue>(Array.isArray(data_labels) ? data_labels : [data_labels]);
|
|
46
49
|
series.labels = labels.map(value => (typeof value === 'undefined') ? '' : value.toString());
|
|
47
50
|
}
|
|
48
51
|
|
|
49
|
-
if (
|
|
52
|
+
if (label) {
|
|
53
|
+
series.label = label.toString();
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const ParseSubseries = (source?: UnionValue, apply_labels = false) => {
|
|
50
57
|
|
|
51
|
-
|
|
58
|
+
let subseries: SubSeries|undefined;
|
|
52
59
|
|
|
53
|
-
//
|
|
54
|
-
// [why would we want metadata?]
|
|
55
|
-
//
|
|
56
|
-
// OK, check that, should be a string (or other literal)
|
|
60
|
+
// convert single values -> array (is this still necessary?)
|
|
57
61
|
|
|
58
|
-
if (
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
62
|
+
if (IsMetadata(source)) {
|
|
63
|
+
source = {
|
|
64
|
+
type: ValueType.array,
|
|
65
|
+
value: [[source]],
|
|
66
|
+
};
|
|
63
67
|
}
|
|
64
|
-
}
|
|
65
68
|
|
|
66
|
-
|
|
69
|
+
if (IsArrayUnion(source)) {
|
|
67
70
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
71
|
+
subseries = { data: [] };
|
|
72
|
+
|
|
73
|
+
const flat = Util.Flatten<UnionValue>(source.value);
|
|
74
|
+
|
|
75
|
+
subseries.data = flat.map(item => {
|
|
76
|
+
if (IsMetadata(item)) {
|
|
77
|
+
if (typeof item.value.value === 'number') {
|
|
78
|
+
return item.value.value;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
else if (item.type === ValueType.number) {
|
|
82
|
+
return item.value;
|
|
83
|
+
}
|
|
84
|
+
return undefined;
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
if (IsMetadata(flat[0]) && flat[0].value?.format) {
|
|
88
|
+
subseries.format = (flat[0].value.format);
|
|
89
|
+
if (apply_labels) {
|
|
90
|
+
const format = NumberFormatCache.Get(subseries.format);
|
|
91
|
+
subseries.labels = subseries.data.map(value => (value === undefined) ? undefined : format.Format(value));
|
|
92
|
+
}
|
|
73
93
|
}
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
94
|
|
|
77
|
-
// read [2] first, so we can default for [1] if necessary
|
|
78
|
-
|
|
79
|
-
if (!!data[2] && (typeof data[2] === 'object') && data[2].type === ValueType.array) {
|
|
80
|
-
const flat = Util.Flatten(data[2].value);
|
|
81
|
-
series.y.data = flat.map(item => typeof item.value.value === 'number' ? item.value.value : undefined);
|
|
82
|
-
if (flat[0].value?.format) {
|
|
83
|
-
series.y.format = flat[0].value?.format as string;
|
|
84
|
-
const format = NumberFormatCache.Get(series.y.format);
|
|
85
|
-
series.y.labels = series.y.data.map(value => (value === undefined) ? undefined : format.Format(value));
|
|
86
95
|
}
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
if (!!data[1] && (typeof data[1] === 'object') && data[1].type === ValueType.array) {
|
|
90
|
-
const flat = Util.Flatten(data[1].value);
|
|
91
|
-
series.x.data = flat.map(item => typeof item.value.value === 'number' ? item.value.value : undefined);
|
|
92
|
-
if (flat[0].value.format) {
|
|
93
|
-
series.x.format = flat[0].value.format;
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
+
return subseries;
|
|
96
97
|
|
|
97
|
-
|
|
98
|
+
};
|
|
98
99
|
|
|
99
|
-
//
|
|
100
|
+
// read [2] first, so we can default for [1] if necessary
|
|
100
101
|
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
series.z.data = flat.map(item => typeof item.value.value === 'number' ? item.value.value : undefined);
|
|
105
|
-
if (flat[0].value.format) {
|
|
106
|
-
series.z.format = flat[0].value.format;
|
|
107
|
-
}
|
|
108
|
-
}
|
|
102
|
+
series.y = ParseSubseries(y, true) || { data: [] };
|
|
103
|
+
series.x = ParseSubseries(x) || { data: [] };
|
|
104
|
+
series.z = ParseSubseries(z);
|
|
109
105
|
|
|
106
|
+
const entries = [series.x, series.y]
|
|
110
107
|
|
|
111
108
|
for (const subseries of entries) {
|
|
112
109
|
|
|
@@ -129,7 +126,7 @@ export const ArrayToSeries = (array_data: ArrayUnion): SeriesType => {
|
|
|
129
126
|
// this is an array of Y, X not provided
|
|
130
127
|
|
|
131
128
|
const series: SeriesType = { x: { data: [] }, y: { data: [] }, };
|
|
132
|
-
const flat = Util.Flatten(array_data.value);
|
|
129
|
+
const flat = Util.Flatten<UnionValue>(array_data.value);
|
|
133
130
|
|
|
134
131
|
// series.y.data = flat.map(item => typeof item.value === 'number' ? item.value : undefined);
|
|
135
132
|
|
|
@@ -142,20 +139,35 @@ export const ArrayToSeries = (array_data: ArrayUnion): SeriesType => {
|
|
|
142
139
|
|
|
143
140
|
// ... ok, it's metadata (why not just test?) ...
|
|
144
141
|
|
|
145
|
-
|
|
146
|
-
|
|
142
|
+
if (IsMetadata(item)) {
|
|
143
|
+
|
|
144
|
+
// experimenting with complex... put real in X axis and imaginary in Y axis
|
|
145
|
+
// note should also function w/ complex not in a metadata structure
|
|
146
|
+
|
|
147
|
+
// if (typeof item.value.value?.real === 'number') {
|
|
148
|
+
if (IsComplex(item.value.value)) {
|
|
149
|
+
series.x.data[index] = item.value.value.real;
|
|
150
|
+
return item.value.value.imaginary;
|
|
151
|
+
}
|
|
152
|
+
if (typeof item.value.value === 'number') {
|
|
153
|
+
return item.value.value;
|
|
154
|
+
}
|
|
147
155
|
|
|
148
|
-
if (typeof item.value.value?.real === 'number') {
|
|
149
|
-
series.x.data[index] = item.value.value.real;
|
|
150
|
-
return item.value.value.imaginary;
|
|
151
156
|
}
|
|
152
157
|
|
|
153
|
-
return typeof item.value.value === 'number' ? item.value.value : undefined;
|
|
158
|
+
// return typeof item.value.value === 'number' ? item.value.value : undefined;
|
|
159
|
+
|
|
160
|
+
return undefined;
|
|
154
161
|
|
|
155
162
|
});
|
|
156
163
|
|
|
157
|
-
|
|
158
|
-
|
|
164
|
+
let first_format = '';
|
|
165
|
+
if (IsMetadata(flat[0])) {
|
|
166
|
+
first_format = flat[0].value.format || '';
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
if (first_format) {
|
|
170
|
+
series.y.format = first_format;
|
|
159
171
|
const format = NumberFormatCache.Get(series.y.format || '');
|
|
160
172
|
series.y.labels = series.y.data.map(value => (value === undefined) ? undefined : format.Format(value));
|
|
161
173
|
}
|
|
@@ -177,8 +189,8 @@ export const ArrayToSeries = (array_data: ArrayUnion): SeriesType => {
|
|
|
177
189
|
max: Math.max.apply(0, filtered),
|
|
178
190
|
}
|
|
179
191
|
|
|
180
|
-
if (
|
|
181
|
-
series.x.format =
|
|
192
|
+
if (first_format) {
|
|
193
|
+
series.x.format = first_format;
|
|
182
194
|
const format = NumberFormatCache.Get(series.x.format || '');
|
|
183
195
|
series.x.labels = series.x.data.map(value => (value === undefined) ? undefined : format.Format(value));
|
|
184
196
|
}
|
|
@@ -212,7 +224,7 @@ export const TransformSeriesData = (raw_data?: UnionValue, default_x?: UnionValu
|
|
|
212
224
|
if (Array.isArray(raw_data.value)) {
|
|
213
225
|
for (const [series_index, entry] of raw_data.value.entries()) {
|
|
214
226
|
if (!!entry && (typeof entry === 'object')) {
|
|
215
|
-
if (entry
|
|
227
|
+
if (IsSeries(entry)) {
|
|
216
228
|
const series = ReadSeries(entry.value);
|
|
217
229
|
if (typeof series.index === 'undefined') {
|
|
218
230
|
series.index = series_index + 1;
|
|
@@ -226,7 +238,7 @@ export const TransformSeriesData = (raw_data?: UnionValue, default_x?: UnionValu
|
|
|
226
238
|
}
|
|
227
239
|
}
|
|
228
240
|
}
|
|
229
|
-
else if (raw_data
|
|
241
|
+
else if (IsSeries(raw_data)) {
|
|
230
242
|
const series = ReadSeries(raw_data.value);
|
|
231
243
|
list.push(series);
|
|
232
244
|
}
|
|
@@ -247,18 +259,17 @@ export const TransformSeriesData = (raw_data?: UnionValue, default_x?: UnionValu
|
|
|
247
259
|
|
|
248
260
|
if (default_x?.type === ValueType.array) {
|
|
249
261
|
|
|
250
|
-
const values = Util.Flatten(default_x.value);
|
|
262
|
+
const values = Util.Flatten<UnionValue>(default_x.value);
|
|
251
263
|
|
|
252
264
|
let format = '0.00###';
|
|
253
265
|
|
|
254
|
-
if (values[0] && values[0].
|
|
266
|
+
if (IsMetadata(values[0]) && values[0].value.format) {
|
|
255
267
|
format = values[0].value.format;
|
|
256
268
|
}
|
|
257
269
|
|
|
258
270
|
const data = values.map(x => {
|
|
259
271
|
if (x.type === ValueType.number) { return x.value; }
|
|
260
|
-
if (x.
|
|
261
|
-
// if (UnionIs.Extended(x)) { // ?
|
|
272
|
+
if (IsMetadata(x) && typeof x.value.value === 'number') {
|
|
262
273
|
return x.value.value;
|
|
263
274
|
}
|
|
264
275
|
return undefined;
|
|
@@ -332,7 +343,7 @@ export const CommonData = (series: SeriesType[], y_floor?: number, y_ceiling?: n
|
|
|
332
343
|
let x_min = Math.min.apply(0, x.map(test => test.x.range?.min || 0));
|
|
333
344
|
let x_max = Math.max.apply(0, x.map(test => test.x.range?.max || 0));
|
|
334
345
|
|
|
335
|
-
const y = series.filter(test => test.y.range);
|
|
346
|
+
// const y = series.filter(test => test.y.range);
|
|
336
347
|
let y_min = Math.min.apply(0, x.map(test => test.y.range?.min || 0));
|
|
337
348
|
let y_max = Math.max.apply(0, x.map(test => test.y.range?.max || 0));
|
|
338
349
|
|
|
@@ -461,7 +472,7 @@ export const CreateBubbleChart = (args: UnionValue[]): ChartData => {
|
|
|
461
472
|
|
|
462
473
|
const common = CommonData(series, y_floor, undefined, x_floor);
|
|
463
474
|
const title = args[1]?.toString() || undefined;
|
|
464
|
-
const options = args[2]?.toString() || undefined;
|
|
475
|
+
// const options = args[2]?.toString() || undefined;
|
|
465
476
|
|
|
466
477
|
// console.info({ series, common, title, options });
|
|
467
478
|
|
|
@@ -583,7 +594,7 @@ export const CreateBubbleChart = (args: UnionValue[]): ChartData => {
|
|
|
583
594
|
*
|
|
584
595
|
* @param args
|
|
585
596
|
*/
|
|
586
|
-
export const CreateScatterChart = (args:
|
|
597
|
+
export const CreateScatterChart = (args: [UnionValue, string, string], style: 'plot' | 'line' = 'plot'): ChartData => {
|
|
587
598
|
|
|
588
599
|
// FIXME: transform the data, then have this function
|
|
589
600
|
// operate on clean data. that way the transform can
|
|
@@ -650,32 +661,40 @@ export const CreateScatterChart = (args: any[], style: 'plot' | 'line' = 'plot')
|
|
|
650
661
|
* @param args arguments: data, categories, title, options
|
|
651
662
|
* @param type
|
|
652
663
|
*/
|
|
653
|
-
export const CreateColumnChart = (
|
|
664
|
+
export const CreateColumnChart = (
|
|
665
|
+
args: [UnionValue?, UnionValue?, string?, string?],
|
|
666
|
+
type: 'bar' | 'column'): ChartData => {
|
|
654
667
|
|
|
655
|
-
const
|
|
668
|
+
const [data, labels, args_title, args_options] = args;
|
|
669
|
+
|
|
670
|
+
const series: SeriesType[] = TransformSeriesData(data);
|
|
656
671
|
const common = CommonData(series);
|
|
657
672
|
|
|
658
673
|
let category_labels: string[] | undefined;
|
|
659
674
|
|
|
660
|
-
if (
|
|
675
|
+
if (labels) {
|
|
676
|
+
|
|
677
|
+
const values = labels.type === ValueType.array ? Util.Flatten<UnionValue>(labels.value) : Util.Flatten<UnionValue>(labels);
|
|
661
678
|
|
|
662
|
-
const values = args[1].type === ValueType.array ? Util.Flatten(args[1].value) : Util.Flatten(args[1]);
|
|
663
679
|
category_labels = values.map((cell) => {
|
|
664
|
-
if (!cell) { return ''; }
|
|
665
680
|
|
|
666
|
-
if (cell
|
|
681
|
+
if (!cell || !cell.value) { return ''; }
|
|
682
|
+
|
|
683
|
+
if (IsMetadata(cell)) {
|
|
667
684
|
if (typeof cell.value.value === 'number') {
|
|
668
685
|
const format = NumberFormatCache.Get(cell.value.format || DEFAULT_FORMAT);
|
|
669
686
|
return format.Format(cell.value.value);
|
|
670
687
|
}
|
|
671
|
-
return cell.value.value;
|
|
688
|
+
return cell.value.value?.toString() || '';
|
|
672
689
|
}
|
|
673
690
|
|
|
674
691
|
if (typeof cell.value === 'number') {
|
|
675
|
-
const format = NumberFormatCache.Get(
|
|
692
|
+
const format = NumberFormatCache.Get(DEFAULT_FORMAT);
|
|
676
693
|
return format.Format(cell.value);
|
|
677
694
|
}
|
|
678
|
-
|
|
695
|
+
|
|
696
|
+
return cell.value.toString();
|
|
697
|
+
|
|
679
698
|
});
|
|
680
699
|
|
|
681
700
|
const count = series.reduce((a, entry) => Math.max(a, entry.y.data.length), 0);
|
|
@@ -688,8 +707,8 @@ export const CreateColumnChart = (args: [UnionValue?, UnionValue?, string?, stri
|
|
|
688
707
|
|
|
689
708
|
}
|
|
690
709
|
|
|
691
|
-
const title =
|
|
692
|
-
const options =
|
|
710
|
+
const title = args_title?.toString() || undefined;
|
|
711
|
+
const options = args_options?.toString() || undefined;
|
|
693
712
|
|
|
694
713
|
const chart_data = {
|
|
695
714
|
type,
|
|
@@ -734,7 +753,7 @@ export const CreateColumnChart = (args: [UnionValue?, UnionValue?, string?, stri
|
|
|
734
753
|
/**
|
|
735
754
|
* args: data, labels, title, callouts, "smooth"
|
|
736
755
|
*/
|
|
737
|
-
export const CreateLineChart = (args:
|
|
756
|
+
export const CreateLineChart = (args: [UnionValue, UnionValue, string, string], type: 'line' | 'area'): ChartData => {
|
|
738
757
|
|
|
739
758
|
const series: SeriesType[] = TransformSeriesData(args[0], args[1]);
|
|
740
759
|
const common = CommonData(series, 0, 0);
|
|
@@ -781,14 +800,23 @@ export const CreateLineChart = (args: any[], type: 'line' | 'area'): ChartData =
|
|
|
781
800
|
*/
|
|
782
801
|
export const CreateDonut = (args: [UnionValue?, UnionValue?, string?, string?, string?], pie_chart = false): ChartData => {
|
|
783
802
|
|
|
803
|
+
const [_a, _c, title, _options, _slice_title] = args;
|
|
804
|
+
|
|
784
805
|
const raw_data = args[0]?.type === ValueType.array ? args[0].value : args[0];
|
|
785
806
|
|
|
786
|
-
|
|
787
|
-
// so we need to unpack. could be an array... could be deep...
|
|
788
|
-
const flat = Util.Flatten(raw_data);
|
|
807
|
+
const flat = raw_data ? Util.Flatten<UnionValue>(raw_data) : [];
|
|
789
808
|
|
|
790
809
|
// we still need the aggregate for range, scale
|
|
791
|
-
let data = flat.map((x) => (typeof x.value.value === 'number') ? x.value.value : undefined) as number[];
|
|
810
|
+
// let data = flat.map((x) => (typeof x.value.value === 'number') ? x.value.value : undefined) as number[];
|
|
811
|
+
|
|
812
|
+
let data: (number|undefined)[] = flat.map(cell => {
|
|
813
|
+
if (IsMetadata(cell)) {
|
|
814
|
+
if (typeof cell.value.value === 'number') {
|
|
815
|
+
return cell.value.value;
|
|
816
|
+
}
|
|
817
|
+
}
|
|
818
|
+
return undefined;
|
|
819
|
+
})
|
|
792
820
|
|
|
793
821
|
|
|
794
822
|
// if labels are strings, just pass them in. if they're numbers then
|
|
@@ -796,34 +824,40 @@ export const CreateDonut = (args: [UnionValue?, UnionValue?, string?, string?, s
|
|
|
796
824
|
|
|
797
825
|
const raw_labels = args[1]?.type === ValueType.array ? args[1].value : args[1];
|
|
798
826
|
|
|
799
|
-
const labels = Util.Flatten(raw_labels).map(
|
|
800
|
-
if (label
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
return NumberFormatCache.Get(label.value?.format).Format(value);
|
|
827
|
+
const labels = Util.Flatten<UnionValue>(raw_labels||[]).map(label => {
|
|
828
|
+
if (IsMetadata(label)) {
|
|
829
|
+
if (typeof label.value.value === 'number' && label.value.format) {
|
|
830
|
+
return NumberFormatCache.Get(label.value.format).Format(label.value.value);
|
|
804
831
|
}
|
|
805
|
-
else return value
|
|
832
|
+
else return label.value.value?.toString() || '';
|
|
806
833
|
}
|
|
807
|
-
|
|
834
|
+
return label.value?.toString() || '';
|
|
808
835
|
});
|
|
809
836
|
|
|
810
837
|
// no negative numbers
|
|
811
838
|
|
|
839
|
+
let warned = false;
|
|
812
840
|
data = data.map((check) => {
|
|
813
|
-
if (check < 0) {
|
|
814
|
-
|
|
841
|
+
if (check && check < 0) {
|
|
842
|
+
if (!warned) {
|
|
843
|
+
console.warn('pie/donut chart does not support negative values (omitted)');
|
|
844
|
+
warned = true;
|
|
845
|
+
}
|
|
815
846
|
return 0;
|
|
816
847
|
}
|
|
817
848
|
return check;
|
|
818
849
|
});
|
|
819
850
|
|
|
820
|
-
const title = args[2] || '';
|
|
821
|
-
|
|
822
851
|
let sum = 0;
|
|
823
852
|
|
|
824
853
|
const slices: DonutSlice[] = data.map((value, i) => {
|
|
825
854
|
if (typeof value !== 'undefined') sum += value;
|
|
826
|
-
return {
|
|
855
|
+
return {
|
|
856
|
+
value: value || 0,
|
|
857
|
+
label: labels[i] || '',
|
|
858
|
+
index: i + 1,
|
|
859
|
+
percent: 0,
|
|
860
|
+
};
|
|
827
861
|
});
|
|
828
862
|
|
|
829
863
|
if (sum) {
|
|
@@ -835,7 +869,16 @@ export const CreateDonut = (args: [UnionValue?, UnionValue?, string?, string?, s
|
|
|
835
869
|
// titles? label/value/percent
|
|
836
870
|
// FIXME: number format(s)
|
|
837
871
|
|
|
838
|
-
|
|
872
|
+
let format_pattern = '';
|
|
873
|
+
for (const value of flat) {
|
|
874
|
+
if (IsMetadata(value)) {
|
|
875
|
+
format_pattern = value.value.format || '';
|
|
876
|
+
break;
|
|
877
|
+
}
|
|
878
|
+
}
|
|
879
|
+
|
|
880
|
+
// const format_pattern = (flat.length && flat[0].value?.format) ? flat[0].value.format : '';
|
|
881
|
+
|
|
839
882
|
const format = NumberFormatCache.Get(format_pattern || DEFAULT_FORMAT);
|
|
840
883
|
const percent_format = NumberFormatCache.Get('percent');
|
|
841
884
|
|
|
@@ -861,11 +904,10 @@ export const CreateDonut = (args: [UnionValue?, UnionValue?, string?, string?, s
|
|
|
861
904
|
|
|
862
905
|
// optionally sort...
|
|
863
906
|
|
|
864
|
-
const options = (args[3] || '').toString().trim();
|
|
865
907
|
|
|
866
908
|
// old-style...
|
|
867
909
|
|
|
868
|
-
let sort =
|
|
910
|
+
let sort = (_options||'').toUpperCase();
|
|
869
911
|
if (sort === 'ASC' || sort === 'ASCENDING' || sort === 'INC') {
|
|
870
912
|
slices.sort((a, b) => { return (a.value || 0) - (b.value || 0); });
|
|
871
913
|
}
|
|
@@ -873,7 +915,7 @@ export const CreateDonut = (args: [UnionValue?, UnionValue?, string?, string?, s
|
|
|
873
915
|
slices.sort((a, b) => { return (b.value || 0) - (a.value || 0); });
|
|
874
916
|
}
|
|
875
917
|
else {
|
|
876
|
-
const match =
|
|
918
|
+
const match = (_options||'').match(/sort=([\w]+)(?:\W|$)/i);
|
|
877
919
|
if (match) {
|
|
878
920
|
sort = match[1];
|
|
879
921
|
if (/^(asc|inc)/i.test(sort)) {
|
|
@@ -888,11 +930,11 @@ export const CreateDonut = (args: [UnionValue?, UnionValue?, string?, string?, s
|
|
|
888
930
|
const chart_data: ChartData = {
|
|
889
931
|
type: pie_chart ? 'pie' : 'donut',
|
|
890
932
|
slices,
|
|
891
|
-
title,
|
|
933
|
+
title: title || '',
|
|
892
934
|
};
|
|
893
935
|
|
|
894
|
-
if (
|
|
895
|
-
const match =
|
|
936
|
+
if (_options) {
|
|
937
|
+
const match = _options.match(/class=([_-\w]+)(?:\W|$)/);
|
|
896
938
|
if (match) {
|
|
897
939
|
chart_data.class_name = match[1];
|
|
898
940
|
}
|
package/treb-charts/src/chart.ts
CHANGED
|
@@ -31,7 +31,8 @@ export class Chart {
|
|
|
31
31
|
|
|
32
32
|
public Exec(func: string, union: ExtendedUnion) {
|
|
33
33
|
|
|
34
|
-
|
|
34
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
35
|
+
const args: any[] = (union?.value as any[]) || [];
|
|
35
36
|
|
|
36
37
|
switch (func.toLowerCase()) {
|
|
37
38
|
|
|
@@ -44,11 +45,11 @@ export class Chart {
|
|
|
44
45
|
break;
|
|
45
46
|
|
|
46
47
|
case 'line.chart':
|
|
47
|
-
this.chart_data = ChartUtils.CreateLineChart(args, 'line');
|
|
48
|
+
this.chart_data = ChartUtils.CreateLineChart(args as Parameters<typeof ChartUtils.CreateLineChart>[0], 'line');
|
|
48
49
|
break;
|
|
49
50
|
|
|
50
51
|
case 'area.chart':
|
|
51
|
-
this.chart_data = ChartUtils.CreateLineChart(args, 'area');
|
|
52
|
+
this.chart_data = ChartUtils.CreateLineChart(args as Parameters<typeof ChartUtils.CreateLineChart>[0], 'area');
|
|
52
53
|
break;
|
|
53
54
|
|
|
54
55
|
case 'donut.chart':
|
|
@@ -57,11 +58,11 @@ export class Chart {
|
|
|
57
58
|
break;
|
|
58
59
|
|
|
59
60
|
case 'scatter.plot':
|
|
60
|
-
this.chart_data = ChartUtils.CreateScatterChart(args, 'plot');
|
|
61
|
+
this.chart_data = ChartUtils.CreateScatterChart(args as Parameters<typeof ChartUtils.CreateScatterChart>[0], 'plot');
|
|
61
62
|
break;
|
|
62
63
|
|
|
63
64
|
case 'scatter.line':
|
|
64
|
-
this.chart_data = ChartUtils.CreateScatterChart(args, 'line');
|
|
65
|
+
this.chart_data = ChartUtils.CreateScatterChart(args as Parameters<typeof ChartUtils.CreateScatterChart>[0], 'line');
|
|
65
66
|
break;
|
|
66
67
|
|
|
67
68
|
case 'bubble.chart':
|
|
@@ -23,6 +23,7 @@ export class DefaultChartRenderer implements ChartRendererType {
|
|
|
23
23
|
|
|
24
24
|
private margin = { top: 0.025, left: 0.05, bottom: 0.025, right: 0.075 };
|
|
25
25
|
|
|
26
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
26
27
|
public Resize(target: HTMLElement, data: ChartData) {
|
|
27
28
|
this.renderer.Resize();
|
|
28
29
|
}
|
|
@@ -196,7 +197,7 @@ export class DefaultChartRenderer implements ChartRendererType {
|
|
|
196
197
|
|
|
197
198
|
// now do type-specific rendering
|
|
198
199
|
|
|
199
|
-
|
|
200
|
+
const zeros: number[] = [];
|
|
200
201
|
|
|
201
202
|
switch (chart_data.type) {
|
|
202
203
|
case 'scatter':
|
|
@@ -384,7 +385,7 @@ export class DefaultChartRenderer implements ChartRendererType {
|
|
|
384
385
|
|
|
385
386
|
let x = 0;
|
|
386
387
|
let width = 0;
|
|
387
|
-
let negative = false;
|
|
388
|
+
// let negative = false;
|
|
388
389
|
|
|
389
390
|
if (zero) {
|
|
390
391
|
if (value > 0) {
|
|
@@ -394,7 +395,7 @@ export class DefaultChartRenderer implements ChartRendererType {
|
|
|
394
395
|
else {
|
|
395
396
|
width = Util.ApplyScale(chart_data.scale.min - value, area.width, chart_data.scale);
|
|
396
397
|
x = area.left + zero - width;
|
|
397
|
-
negative = true;
|
|
398
|
+
// negative = true;
|
|
398
399
|
}
|
|
399
400
|
}
|
|
400
401
|
else {
|
|
@@ -484,7 +485,7 @@ export class DefaultChartRenderer implements ChartRendererType {
|
|
|
484
485
|
|
|
485
486
|
let height = 0;
|
|
486
487
|
let y = 0;
|
|
487
|
-
let negative = false;
|
|
488
|
+
// let negative = false;
|
|
488
489
|
|
|
489
490
|
if (zero) {
|
|
490
491
|
if (value > 0) {
|
|
@@ -494,7 +495,7 @@ export class DefaultChartRenderer implements ChartRendererType {
|
|
|
494
495
|
else {
|
|
495
496
|
height = Util.ApplyScale(chart_data.scale.min - value, area.height, chart_data.scale);
|
|
496
497
|
y = area.bottom - zero; // // area.bottom - height - zero;
|
|
497
|
-
negative = true;
|
|
498
|
+
// negative = true;
|
|
498
499
|
}
|
|
499
500
|
}
|
|
500
501
|
else {
|
|
@@ -33,11 +33,11 @@ export interface Metrics {
|
|
|
33
33
|
y_offset: number;
|
|
34
34
|
}
|
|
35
35
|
|
|
36
|
-
const SVGNode = (tag: string, attribute_map: {[index: string]:
|
|
36
|
+
const SVGNode = (tag: string, attribute_map: {[index: string]: string|number|unknown} = {}, text?: string): SVGElement => {
|
|
37
37
|
const node = document.createElementNS(SVGNS, tag);
|
|
38
38
|
for (const key of Object.keys(attribute_map)) {
|
|
39
39
|
if (attribute_map[key] !== undefined) {
|
|
40
|
-
const value = attribute_map[key];
|
|
40
|
+
const value = attribute_map[key] ?? '';
|
|
41
41
|
node.setAttribute(key, Array.isArray(value) ? value.join(' ') : value.toString());
|
|
42
42
|
}
|
|
43
43
|
}
|
|
@@ -836,10 +836,10 @@ export class ChartRenderer {
|
|
|
836
836
|
|
|
837
837
|
const shape = SVGNode('circle', {cx: circle.x, cy: circle.y, r: step});
|
|
838
838
|
|
|
839
|
-
shape.addEventListener('mouseenter', (
|
|
839
|
+
shape.addEventListener('mouseenter', () => {
|
|
840
840
|
this.parent.setAttribute('title', titles[circle.i] || '');
|
|
841
841
|
});
|
|
842
|
-
shape.addEventListener('mouseleave', (
|
|
842
|
+
shape.addEventListener('mouseleave', () => {
|
|
843
843
|
this.parent.setAttribute('title', '');
|
|
844
844
|
});
|
|
845
845
|
|
|
@@ -934,10 +934,10 @@ export class ChartRenderer {
|
|
|
934
934
|
|
|
935
935
|
const shape = SVGNode('circle', { cx: circle.x, cy: circle.y, r: step });
|
|
936
936
|
|
|
937
|
-
shape.addEventListener('mouseenter', (
|
|
937
|
+
shape.addEventListener('mouseenter', () => {
|
|
938
938
|
this.parent.setAttribute('title', titles[circle.i] || '');
|
|
939
939
|
});
|
|
940
|
-
shape.addEventListener('mouseleave', (
|
|
940
|
+
shape.addEventListener('mouseleave', () => {
|
|
941
941
|
this.parent.setAttribute('title', '');
|
|
942
942
|
});
|
|
943
943
|
|
|
@@ -1251,8 +1251,8 @@ export class ChartRenderer {
|
|
|
1251
1251
|
offset: number,
|
|
1252
1252
|
}> = [];
|
|
1253
1253
|
|
|
1254
|
-
const d: string[] = [];
|
|
1255
|
-
const areas: string[] = [];
|
|
1254
|
+
// const d: string[] = [];
|
|
1255
|
+
// const areas: string[] = [];
|
|
1256
1256
|
|
|
1257
1257
|
const group = SVGNode('g', {class: classes});
|
|
1258
1258
|
|
|
@@ -1652,10 +1652,10 @@ export class ChartRenderer {
|
|
|
1652
1652
|
});
|
|
1653
1653
|
|
|
1654
1654
|
if (title) {
|
|
1655
|
-
node.addEventListener('mouseenter', (
|
|
1655
|
+
node.addEventListener('mouseenter', () => {
|
|
1656
1656
|
this.parent.setAttribute('title', title);
|
|
1657
1657
|
});
|
|
1658
|
-
node.addEventListener('mouseleave', (
|
|
1658
|
+
node.addEventListener('mouseleave', () => {
|
|
1659
1659
|
this.parent.setAttribute('title', '');
|
|
1660
1660
|
});
|
|
1661
1661
|
}
|
|
@@ -1894,7 +1894,8 @@ export class ChartRenderer {
|
|
|
1894
1894
|
let break_index = -1;
|
|
1895
1895
|
let break_value = 1;
|
|
1896
1896
|
|
|
1897
|
-
const indices: number[] = [];
|
|
1897
|
+
// const indices: number[] = [];
|
|
1898
|
+
|
|
1898
1899
|
for (let i = 0; i < text.length; i++) {
|
|
1899
1900
|
if (break_regex.test(text[i])) {
|
|
1900
1901
|
const index_value = Math.abs(0.5 - (i / text.length));
|