@trebco/treb 30.6.0 → 30.6.3
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-export-worker.mjs +1 -1
- package/dist/treb-spreadsheet.mjs +11 -11
- package/esbuild-utils.mjs +3 -1
- package/package.json +1 -1
- package/treb-calculator/src/functions/base-functions.ts +0 -1
- package/treb-charts/src/chart-functions.ts +1 -0
- package/treb-charts/src/chart-utils.ts +81 -2
- package/treb-charts/src/quicksort.ts +54 -0
- package/treb-embed/src/embedded-spreadsheet.ts +2 -2
- package/treb-grid/src/editors/editor.ts +5 -3
- package/treb-grid/src/editors/formula_bar.ts +30 -4
- package/treb-grid/src/types/grid.ts +13 -1
package/esbuild-utils.mjs
CHANGED
|
@@ -74,7 +74,7 @@ export const FormatSize = (size, precision = 1) => {
|
|
|
74
74
|
|
|
75
75
|
/**
|
|
76
76
|
* @function
|
|
77
|
-
* @param {{verbose?: boolean, minify?: boolean}} [options]
|
|
77
|
+
* @param {{verbose?: boolean, minify?: boolean, define?: Record<string, string>}} [options]
|
|
78
78
|
* @returns {esbuild.Plugin}
|
|
79
79
|
*
|
|
80
80
|
* inlining the worker build. this works out well with one limitation:
|
|
@@ -147,6 +147,8 @@ export const WorkerPlugin = (options) => ({
|
|
|
147
147
|
bundle: true,
|
|
148
148
|
format: 'esm',
|
|
149
149
|
|
|
150
|
+
define: options?.define,
|
|
151
|
+
|
|
150
152
|
// don't write to filesystem
|
|
151
153
|
write: false,
|
|
152
154
|
|
package/package.json
CHANGED
|
@@ -196,6 +196,7 @@ export const ChartFunctions: Record<ChartFunction|SupportFunction, CompositeFunc
|
|
|
196
196
|
arguments: [
|
|
197
197
|
{ name: 'Data', metadata: true, },
|
|
198
198
|
{ name: 'Chart Title' },
|
|
199
|
+
{ name: 'Min/Max Style' }
|
|
199
200
|
],
|
|
200
201
|
fn: Identity,
|
|
201
202
|
category: ['chart functions'],
|
|
@@ -1,3 +1,23 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* This file is part of TREB.
|
|
3
|
+
*
|
|
4
|
+
* TREB is free software: you can redistribute it and/or modify it under the
|
|
5
|
+
* terms of the GNU General Public License as published by the Free Software
|
|
6
|
+
* Foundation, either version 3 of the License, or (at your option) any
|
|
7
|
+
* later version.
|
|
8
|
+
*
|
|
9
|
+
* TREB is distributed in the hope that it will be useful, but WITHOUT ANY
|
|
10
|
+
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
|
11
|
+
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
|
12
|
+
* details.
|
|
13
|
+
*
|
|
14
|
+
* You should have received a copy of the GNU General Public License along
|
|
15
|
+
* with TREB. If not, see <https://www.gnu.org/licenses/>.
|
|
16
|
+
*
|
|
17
|
+
* Copyright 2022-2024 trebco, llc.
|
|
18
|
+
* info@treb.app
|
|
19
|
+
*
|
|
20
|
+
*/
|
|
1
21
|
|
|
2
22
|
import { type UnionValue, ValueType, type ArrayUnion, IsComplex, type CellValue } from 'treb-base-types';
|
|
3
23
|
import { IsArrayUnion, IsMetadata, IsSeries, LegendStyle } from './chart-types';
|
|
@@ -7,6 +27,8 @@ import { Util } from './util';
|
|
|
7
27
|
import type { ReferenceSeries } from './chart-types';
|
|
8
28
|
import type { RangeScale } from 'treb-utils';
|
|
9
29
|
|
|
30
|
+
import { QuickSort } from './quicksort';
|
|
31
|
+
|
|
10
32
|
/**
|
|
11
33
|
* this file is the concrete translation from function arguments
|
|
12
34
|
* to chart data. chart data is a (somewhat complicated) type with
|
|
@@ -148,6 +170,48 @@ export const ArrayToSeries = (array_data: ArrayUnion): SeriesType => {
|
|
|
148
170
|
|
|
149
171
|
// series.y.data = flat.map(item => typeof item.value === 'number' ? item.value : undefined);
|
|
150
172
|
|
|
173
|
+
// console.trace();
|
|
174
|
+
|
|
175
|
+
const values: number[] = []; // filter any undefineds
|
|
176
|
+
|
|
177
|
+
for (const [index, item] of flat.entries()) {
|
|
178
|
+
|
|
179
|
+
let value = 0;
|
|
180
|
+
|
|
181
|
+
// why is this testing type instead of using the union type?
|
|
182
|
+
|
|
183
|
+
if (typeof item.value === 'number') {
|
|
184
|
+
value = item.value;
|
|
185
|
+
// series.y.data[index] = item.value;
|
|
186
|
+
values.push(item.value);
|
|
187
|
+
}
|
|
188
|
+
else if (IsMetadata(item)) {
|
|
189
|
+
if (IsComplex(item.value.value)) {
|
|
190
|
+
series.x.data[index] = item.value.value.real;
|
|
191
|
+
// series.y.data[index] = item.value.value.imaginary;
|
|
192
|
+
// values.push(item.value.value.imaginary);
|
|
193
|
+
value = item.value.value.imaginary;
|
|
194
|
+
}
|
|
195
|
+
else if (typeof item.value.value === 'number') {
|
|
196
|
+
// series.y.data[index] = item.value.value;
|
|
197
|
+
// values.push(item.value.value);
|
|
198
|
+
value = item.value.value;
|
|
199
|
+
}
|
|
200
|
+
else {
|
|
201
|
+
continue;
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
else {
|
|
205
|
+
// series.y.data[index] = undefined;
|
|
206
|
+
continue;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
series.y.data[index] = value;
|
|
210
|
+
values.push(value);
|
|
211
|
+
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
/*
|
|
151
215
|
series.y.data = flat.map((item, index) => {
|
|
152
216
|
|
|
153
217
|
// if the data is passed in from the output of a function, it will not
|
|
@@ -178,6 +242,7 @@ export const ArrayToSeries = (array_data: ArrayUnion): SeriesType => {
|
|
|
178
242
|
return undefined;
|
|
179
243
|
|
|
180
244
|
});
|
|
245
|
+
*/
|
|
181
246
|
|
|
182
247
|
let first_format = '';
|
|
183
248
|
if (IsMetadata(flat[0])) {
|
|
@@ -190,7 +255,9 @@ export const ArrayToSeries = (array_data: ArrayUnion): SeriesType => {
|
|
|
190
255
|
series.y.labels = series.y.data.map(value => (value === undefined) ? undefined : format.Format(value));
|
|
191
256
|
}
|
|
192
257
|
|
|
193
|
-
|
|
258
|
+
// moved up, integrated loops
|
|
259
|
+
// const values = series.y.data.filter(value => value || value === 0) as number[];
|
|
260
|
+
|
|
194
261
|
series.y.range = ArrayMinMax(values);
|
|
195
262
|
|
|
196
263
|
// experimenting with complex... this should only be set if we populated
|
|
@@ -589,19 +656,31 @@ export const CreateBoxPlot = (args: UnionValue[]): ChartData => {
|
|
|
589
656
|
|
|
590
657
|
let max_n = 0;
|
|
591
658
|
|
|
659
|
+
// change to min-max style
|
|
660
|
+
const minmax = !!args[2];
|
|
661
|
+
|
|
592
662
|
const stats: BoxPlotData['data'] = series.map(series => {
|
|
593
663
|
// const data = series.y.data.slice(0).filter((test): test is number => test !== undefined).sort((a, b) => a - b);
|
|
594
664
|
const data: number[] = [];
|
|
595
665
|
for (const entry of series.y.data) {
|
|
596
666
|
if (entry !== undefined) { data.push(entry); }
|
|
597
667
|
}
|
|
598
|
-
|
|
668
|
+
|
|
669
|
+
// data.sort((a, b) => a - b);
|
|
670
|
+
QuickSort(data);
|
|
599
671
|
|
|
600
672
|
const result = BoxStats(data);
|
|
601
673
|
max_n = Math.max(max_n, result.n);
|
|
674
|
+
|
|
675
|
+
if (minmax) {
|
|
676
|
+
result.whiskers[0] = result.min;
|
|
677
|
+
result.whiskers[1] = result.max;
|
|
678
|
+
}
|
|
679
|
+
|
|
602
680
|
return result;
|
|
603
681
|
});
|
|
604
682
|
|
|
683
|
+
|
|
605
684
|
const title = args[1]?.toString() || undefined;
|
|
606
685
|
const x_labels: string[] = [];
|
|
607
686
|
const series_names: string[] = [];
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* This file is part of TREB.
|
|
3
|
+
*
|
|
4
|
+
* TREB is free software: you can redistribute it and/or modify it under the
|
|
5
|
+
* terms of the GNU General Public License as published by the Free Software
|
|
6
|
+
* Foundation, either version 3 of the License, or (at your option) any
|
|
7
|
+
* later version.
|
|
8
|
+
*
|
|
9
|
+
* TREB is distributed in the hope that it will be useful, but WITHOUT ANY
|
|
10
|
+
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
|
11
|
+
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
|
12
|
+
* details.
|
|
13
|
+
*
|
|
14
|
+
* You should have received a copy of the GNU General Public License along
|
|
15
|
+
* with TREB. If not, see <https://www.gnu.org/licenses/>.
|
|
16
|
+
*
|
|
17
|
+
* Copyright 2022-2024 trebco, llc.
|
|
18
|
+
* info@treb.app
|
|
19
|
+
*
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
// FIXME: move
|
|
23
|
+
|
|
24
|
+
export const QuickSort = (data: number[]) => {
|
|
25
|
+
Sort(data, 0, data.length);
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
const Partition = (data: number[], left: number, right: number) => {
|
|
29
|
+
const compare = data[right - 1];
|
|
30
|
+
let min_end = left;
|
|
31
|
+
for (let max_end = left; max_end < right - 1; max_end += 1) {
|
|
32
|
+
if (data[max_end] <= compare) {
|
|
33
|
+
Swap(data, max_end, min_end);
|
|
34
|
+
min_end += 1;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
Swap(data, min_end, right - 1);
|
|
38
|
+
return min_end;
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
const Swap = (data: number[], i: number, j: number) => {
|
|
42
|
+
const temp = data[i];
|
|
43
|
+
data[i] = data[j];
|
|
44
|
+
data[j] = temp;
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
const Sort = (data: number[], left: number, right: number) => {
|
|
48
|
+
if (left < right) {
|
|
49
|
+
const p = Partition(data, left, right);
|
|
50
|
+
Sort(data, left, p);
|
|
51
|
+
Sort(data, p + 1, right);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
@@ -246,8 +246,8 @@ export class EmbeddedSpreadsheet<USER_DATA_TYPE = unknown> {
|
|
|
246
246
|
/* * @internal */
|
|
247
247
|
// public static export_worker_text = '';
|
|
248
248
|
|
|
249
|
-
|
|
250
|
-
public static treb_embedded_script_path = '';
|
|
249
|
+
/* * @internal */
|
|
250
|
+
// public static treb_embedded_script_path = '';
|
|
251
251
|
|
|
252
252
|
/**
|
|
253
253
|
* @internal
|
|
@@ -567,7 +567,7 @@ export class Editor<E = FormulaEditorEvent> extends EventSource<E|FormulaEditorE
|
|
|
567
567
|
* that's fine, but it needs a new name.
|
|
568
568
|
*
|
|
569
569
|
*/
|
|
570
|
-
protected UpdateColors(force_event = false) {
|
|
570
|
+
protected UpdateColors(force_event = false, toll_update = false) {
|
|
571
571
|
|
|
572
572
|
// const view = this.active_editor?.node.ownerDocument.defaultView as (Window & typeof globalThis);
|
|
573
573
|
|
|
@@ -623,8 +623,10 @@ export class Editor<E = FormulaEditorEvent> extends EventSource<E|FormulaEditorE
|
|
|
623
623
|
|
|
624
624
|
this.composite_dependencies = list;
|
|
625
625
|
|
|
626
|
-
|
|
627
|
-
|
|
626
|
+
if (!toll_update) {
|
|
627
|
+
this.Publish({ type: 'update', dependencies: this.composite_dependencies });
|
|
628
|
+
}
|
|
629
|
+
|
|
628
630
|
}
|
|
629
631
|
|
|
630
632
|
/**
|
|
@@ -57,6 +57,8 @@ export type FormulaBar2Event
|
|
|
57
57
|
export class FormulaBar extends Editor<FormulaBar2Event|FormulaEditorEvent> {
|
|
58
58
|
|
|
59
59
|
|
|
60
|
+
public committed = false;
|
|
61
|
+
|
|
60
62
|
/** is the _editor_ currently focused */
|
|
61
63
|
// tslint:disable-next-line:variable-name
|
|
62
64
|
public focused_ = false;
|
|
@@ -219,7 +221,7 @@ export class FormulaBar extends Editor<FormulaBar2Event|FormulaEditorEvent> {
|
|
|
219
221
|
this.active_editor.node.spellcheck = false; // change the default back
|
|
220
222
|
|
|
221
223
|
this.RegisterListener(descriptor, 'focusin', () => {
|
|
222
|
-
|
|
224
|
+
|
|
223
225
|
// this.editor_node.addEventListener('focusin', () => {
|
|
224
226
|
|
|
225
227
|
// can't happen
|
|
@@ -239,7 +241,9 @@ export class FormulaBar extends Editor<FormulaBar2Event|FormulaEditorEvent> {
|
|
|
239
241
|
this.autocomplete?.ResetBlock();
|
|
240
242
|
|
|
241
243
|
this.UpdateText(this.active_editor);
|
|
242
|
-
this.UpdateColors();
|
|
244
|
+
this.UpdateColors(undefined, true); // toll update event -- we will send in order, below
|
|
245
|
+
|
|
246
|
+
this.committed = false;
|
|
243
247
|
|
|
244
248
|
this.Publish([
|
|
245
249
|
{ type: 'start-editing', editor: 'formula-bar' },
|
|
@@ -256,11 +260,32 @@ export class FormulaBar extends Editor<FormulaBar2Event|FormulaEditorEvent> {
|
|
|
256
260
|
console.info('focusout, but selecting...');
|
|
257
261
|
}
|
|
258
262
|
|
|
259
|
-
// console.info('focus out');
|
|
260
|
-
|
|
261
263
|
this.autocomplete?.Hide();
|
|
264
|
+
|
|
265
|
+
const text = (this.active_editor ?
|
|
266
|
+
this.GetTextContent(this.active_editor.node).join('') : '').trim();
|
|
267
|
+
|
|
268
|
+
if (this.committed) {
|
|
269
|
+
this.Publish([
|
|
270
|
+
{ type: 'stop-editing' },
|
|
271
|
+
]);
|
|
272
|
+
}
|
|
273
|
+
else {
|
|
274
|
+
this.committed = true;
|
|
275
|
+
this.Publish({
|
|
276
|
+
type: 'commit',
|
|
277
|
+
value: text,
|
|
278
|
+
});
|
|
279
|
+
}
|
|
280
|
+
|
|
262
281
|
this.Publish([
|
|
263
282
|
{ type: 'stop-editing' },
|
|
283
|
+
/*
|
|
284
|
+
{
|
|
285
|
+
type: 'commit',
|
|
286
|
+
value: text,
|
|
287
|
+
}
|
|
288
|
+
*/
|
|
264
289
|
]);
|
|
265
290
|
|
|
266
291
|
this.focused_ = false;
|
|
@@ -441,6 +466,7 @@ export class FormulaBar extends Editor<FormulaBar2Event|FormulaEditorEvent> {
|
|
|
441
466
|
const text = (this.active_editor ?
|
|
442
467
|
this.GetTextContent(this.active_editor.node).join('') : '').trim();
|
|
443
468
|
|
|
469
|
+
this.committed = true;
|
|
444
470
|
this.Publish({
|
|
445
471
|
type: 'commit',
|
|
446
472
|
// selection: this.selection,
|
|
@@ -193,6 +193,9 @@ export class Grid extends GridBase {
|
|
|
193
193
|
/** if we are editing, what is the cell? */
|
|
194
194
|
private editing_cell: ICellAddress = { row: -1, column: -1, sheet_id: 0 };
|
|
195
195
|
|
|
196
|
+
/** */
|
|
197
|
+
private editing_selection: GridSelection|undefined;
|
|
198
|
+
|
|
196
199
|
/** */
|
|
197
200
|
private selected_annotation?: Annotation;
|
|
198
201
|
|
|
@@ -2859,6 +2862,7 @@ export class Grid extends GridBase {
|
|
|
2859
2862
|
|
|
2860
2863
|
this.editing_state = EditingState.FormulaBar;
|
|
2861
2864
|
this.editing_cell = { ...this.primary_selection.target };
|
|
2865
|
+
this.editing_selection = { ...this.primary_selection };
|
|
2862
2866
|
break;
|
|
2863
2867
|
|
|
2864
2868
|
case 'discard':
|
|
@@ -2924,7 +2928,15 @@ export class Grid extends GridBase {
|
|
|
2924
2928
|
|
|
2925
2929
|
if (this.container) this.Focus();
|
|
2926
2930
|
|
|
2927
|
-
|
|
2931
|
+
if (event.event) {
|
|
2932
|
+
this.SetInferredType(this.primary_selection, event.value, event.array);
|
|
2933
|
+
}
|
|
2934
|
+
else {
|
|
2935
|
+
if (this.editing_selection) {
|
|
2936
|
+
this.SetInferredType(this.editing_selection, event.value, event.array);
|
|
2937
|
+
}
|
|
2938
|
+
}
|
|
2939
|
+
|
|
2928
2940
|
this.ClearAdditionalSelections();
|
|
2929
2941
|
this.ClearSelection(this.active_selection);
|
|
2930
2942
|
|