@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
package/treb-charts/src/util.ts
CHANGED
|
@@ -19,6 +19,7 @@
|
|
|
19
19
|
*
|
|
20
20
|
*/
|
|
21
21
|
|
|
22
|
+
// import type { UnionValue } from 'treb-base-types';
|
|
22
23
|
import type { RangeScale} from 'treb-utils';
|
|
23
24
|
import { Scale } from 'treb-utils';
|
|
24
25
|
|
|
@@ -39,40 +40,7 @@ export class Util {
|
|
|
39
40
|
* suggestion -- we try to get as close as possible.
|
|
40
41
|
*/
|
|
41
42
|
public static Scale(min: number, max: number, count = 6.5, limit_count?: boolean, discrete?: boolean): RangeScale {
|
|
42
|
-
|
|
43
|
-
/*
|
|
44
|
-
const range = max - min;
|
|
45
|
-
const log10 = // Math.log10(range);
|
|
46
|
-
Math.log(range) / Math.log(10); // just avoid the problem
|
|
47
|
-
|
|
48
|
-
const scale = Math.floor(Math.abs(log10)) * (log10 < 0 ? -1 : 1) - 1;
|
|
49
|
-
const steps = [.1, .25, .5, 1, 2.5, 5, 10, 25, 100];
|
|
50
|
-
|
|
51
|
-
let step = -1;
|
|
52
|
-
let delta = 0;
|
|
53
|
-
|
|
54
|
-
for (const x of steps) {
|
|
55
|
-
const test_step = x * Math.pow(10, scale);
|
|
56
|
-
const test_min = Math.floor(min / test_step) * test_step;
|
|
57
|
-
const test_max = Math.ceil(max / test_step) * test_step;
|
|
58
|
-
const test_count = (test_max - test_min) / test_step;
|
|
59
|
-
const test_delta = Math.abs(test_count - count);
|
|
60
|
-
|
|
61
|
-
if (step < 0 || test_delta < delta){
|
|
62
|
-
delta = test_delta;
|
|
63
|
-
step = test_step;
|
|
64
|
-
}
|
|
65
|
-
else if (step >= 0 && test_delta > delta) break;
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
min = Math.floor(min / step) * step;
|
|
69
|
-
max = Math.ceil(max / step) * step;
|
|
70
|
-
count = Math.round((max - min) / step); // accounts for fp errors
|
|
71
|
-
|
|
72
|
-
return { scale, step, count, min, max };
|
|
73
|
-
*/
|
|
74
43
|
return Scale(min, max, count, limit_count, discrete);
|
|
75
|
-
|
|
76
44
|
}
|
|
77
45
|
|
|
78
46
|
public static Range(data: Array<number|undefined>) {
|
|
@@ -95,13 +63,13 @@ export class Util {
|
|
|
95
63
|
return range * (value - scale.min) / (scale.max - scale.min);
|
|
96
64
|
}
|
|
97
65
|
|
|
98
|
-
|
|
66
|
+
/* *
|
|
99
67
|
* flatten. we support holes in data, which means undefined values
|
|
100
68
|
* in arrays, but don't push an empty value at the top level (if
|
|
101
69
|
* that makes sense).
|
|
102
70
|
*
|
|
103
|
-
* @
|
|
104
|
-
|
|
71
|
+
* @deprecated
|
|
72
|
+
* /
|
|
105
73
|
public static Flatten(args: any) {
|
|
106
74
|
let flat: any[] = [];
|
|
107
75
|
if (Array.isArray(args)) {
|
|
@@ -119,5 +87,26 @@ export class Util {
|
|
|
119
87
|
}
|
|
120
88
|
return flat;
|
|
121
89
|
}
|
|
90
|
+
*/
|
|
91
|
+
|
|
92
|
+
public static Flatten<T>(args?: T|T[]|T[][]) {
|
|
93
|
+
let flat: T[] = [];
|
|
94
|
+
if (Array.isArray(args)) {
|
|
95
|
+
for (const element of args) {
|
|
96
|
+
if (Array.isArray(element)) {
|
|
97
|
+
flat = flat.concat(this.Flatten(element));
|
|
98
|
+
}
|
|
99
|
+
else {
|
|
100
|
+
flat.push(element);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
else if (typeof args !== 'undefined') {
|
|
105
|
+
flat.push(args);
|
|
106
|
+
}
|
|
107
|
+
return flat;
|
|
108
|
+
}
|
|
122
109
|
|
|
123
110
|
}
|
|
111
|
+
|
|
112
|
+
|
|
@@ -194,7 +194,7 @@ export interface AnnotationTextBoxData extends AnnotationDataBase {
|
|
|
194
194
|
}[],
|
|
195
195
|
}[];
|
|
196
196
|
};
|
|
197
|
-
}
|
|
197
|
+
}
|
|
198
198
|
|
|
199
199
|
export interface AnnotationExternalData extends AnnotationDataBase {
|
|
200
200
|
type: 'external';
|
|
@@ -230,7 +230,7 @@ export class Annotation {
|
|
|
230
230
|
public scaled_rect?: Rectangle;
|
|
231
231
|
|
|
232
232
|
/** also opaque data, but not serialized. */
|
|
233
|
-
public temp:
|
|
233
|
+
public temp: unknown = {};
|
|
234
234
|
|
|
235
235
|
public view: ViewData[] = [];
|
|
236
236
|
|
|
@@ -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 { CellStyle, EvaluateOptions, IArea, Color, Gradient, GradientStop, UnionValue } from 'treb-base-types';
|
|
3
23
|
|
|
@@ -20,11 +20,13 @@
|
|
|
20
20
|
*/
|
|
21
21
|
|
|
22
22
|
import type { Sheet } from './sheet';
|
|
23
|
+
import { SheetCollection } from './sheet_collection';
|
|
23
24
|
import type { IArea, ICellAddress, Table, CellStyle } from 'treb-base-types';
|
|
24
25
|
import type { SerializedSheet } from './sheet_types';
|
|
25
|
-
import {
|
|
26
|
-
import { type ExpressionUnit, type UnitAddress, type UnitStructuredReference, type UnitRange, Parser, QuotedSheetNameRegex } from 'treb-parser';
|
|
26
|
+
import { type ExpressionUnit, type UnitAddress, type UnitStructuredReference, type UnitRange, Parser, QuotedSheetNameRegex, DecimalMarkType, ArgumentSeparatorType } from 'treb-parser';
|
|
27
27
|
import { Area, IsCellAddress, Style } from 'treb-base-types';
|
|
28
|
+
import type { CompositeNamed, SerializedNamed } from './named';
|
|
29
|
+
import { NamedRangeManager } from './named';
|
|
28
30
|
|
|
29
31
|
export interface ConnectedElementType {
|
|
30
32
|
formula: string;
|
|
@@ -49,173 +51,255 @@ export interface MacroFunction extends SerializedMacroFunction {
|
|
|
49
51
|
}
|
|
50
52
|
|
|
51
53
|
/**
|
|
52
|
-
*
|
|
53
|
-
* sometimes index. it makes sense to have a class that can
|
|
54
|
-
* support all of these, ideally without looping.
|
|
55
|
-
*
|
|
56
|
-
* we just have to make sure that no one is assigning to the
|
|
57
|
-
* array, or we'll lose track.
|
|
58
|
-
*
|
|
59
|
-
* also there are some operations -- rename, in particular -- that
|
|
60
|
-
* require updating indexes.
|
|
61
|
-
*
|
|
62
|
-
*
|
|
63
|
-
* FIXME: new file (1 class per file)
|
|
54
|
+
* FIXME: this should move out of the grid module, grid should be focused on view
|
|
64
55
|
*/
|
|
65
|
-
export class
|
|
56
|
+
export class DataModel {
|
|
57
|
+
|
|
58
|
+
public readonly parser: Parser = new Parser();
|
|
59
|
+
|
|
60
|
+
/** document metadata */
|
|
61
|
+
public document_name?: string;
|
|
66
62
|
|
|
67
63
|
/**
|
|
68
|
-
*
|
|
69
|
-
*
|
|
70
|
-
*
|
|
64
|
+
* document metadata. this is opaque to everyone except the user, so
|
|
65
|
+
* we're intentionally leaving it as unknown except where users have
|
|
66
|
+
* direct access (embedded spreadsheet).
|
|
71
67
|
*/
|
|
72
|
-
public
|
|
73
|
-
return this.sheets_.slice(0);
|
|
74
|
-
}
|
|
68
|
+
public user_data?: unknown;
|
|
75
69
|
|
|
76
70
|
/**
|
|
77
|
-
*
|
|
71
|
+
*
|
|
78
72
|
*/
|
|
79
|
-
public
|
|
80
|
-
return this.sheets_.length;
|
|
81
|
-
}
|
|
73
|
+
public readonly sheets = new SheetCollection();
|
|
82
74
|
|
|
83
|
-
/**
|
|
84
|
-
|
|
75
|
+
/** new composite collection (TODO: add macro functions too?) */
|
|
76
|
+
public readonly named = new NamedRangeManager();
|
|
85
77
|
|
|
86
|
-
/**
|
|
87
|
-
|
|
78
|
+
/** macro functions are functions written in spreadsheet language */
|
|
79
|
+
public readonly macro_functions: Map<string, MacroFunction> = new Map();
|
|
88
80
|
|
|
89
|
-
/**
|
|
90
|
-
|
|
81
|
+
/** index for views */
|
|
82
|
+
public view_count = 0;
|
|
91
83
|
|
|
92
84
|
/**
|
|
93
|
-
*
|
|
85
|
+
* base style properties moved to model, so we can have a single
|
|
86
|
+
* and consistent reference.
|
|
94
87
|
*/
|
|
95
|
-
public
|
|
96
|
-
this.sheets_ = [...sheets];
|
|
97
|
-
this.UpdateIndexes();
|
|
98
|
-
}
|
|
88
|
+
public theme_style_properties: CellStyle = JSON.parse(JSON.stringify(Style.DefaultProperties));
|
|
99
89
|
|
|
100
|
-
/**
|
|
101
|
-
*
|
|
90
|
+
/**
|
|
91
|
+
* tables are global, because we need to reference them by name; and they
|
|
92
|
+
* need unique names, so we need to keep track of names. name matching is
|
|
93
|
+
* icase so we lc the names before inserting.
|
|
102
94
|
*/
|
|
103
|
-
public
|
|
104
|
-
this.sheets_.push(sheet);
|
|
105
|
-
this.UpdateIndexes();
|
|
106
|
-
}
|
|
95
|
+
public tables: Map<string, Table> = new Map();
|
|
107
96
|
|
|
108
97
|
/**
|
|
109
|
-
*
|
|
98
|
+
* we're wrapping up the get name method so we can check for a sheet
|
|
99
|
+
* name -- we have the list of sheet names. we could pass that to the
|
|
100
|
+
* name list manager but it's easier for us to intercept the call.
|
|
101
|
+
* I thought about wrapping up more API functions here, but that seems
|
|
102
|
+
* unecessary atm.
|
|
110
103
|
*/
|
|
111
|
-
public
|
|
112
|
-
|
|
113
|
-
this
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
/**
|
|
117
|
-
* so our new strategy is to add lookup methods first -- then
|
|
118
|
-
* we can fix the underlying storage implementation.
|
|
119
|
-
*
|
|
120
|
-
* NOTE we normalize strings here so you do not need to do it (don't)
|
|
121
|
-
*/
|
|
122
|
-
public Find(id: string|number): Sheet|undefined {
|
|
104
|
+
public GetName(name: string, scope: number) {
|
|
105
|
+
|
|
106
|
+
// helpfully this is not a legal character in names or sheets, so
|
|
107
|
+
// we don't need a full parser to handle the split. watch out for
|
|
108
|
+
// quoted sheet names.
|
|
123
109
|
|
|
124
|
-
|
|
110
|
+
const parts = name.split(/!/);
|
|
125
111
|
|
|
126
|
-
if (
|
|
127
|
-
return this.
|
|
112
|
+
if (parts.length === 1) {
|
|
113
|
+
return this.named.Get_(name, scope);
|
|
128
114
|
}
|
|
129
|
-
|
|
130
|
-
|
|
115
|
+
|
|
116
|
+
let sheet_name = parts[0];
|
|
117
|
+
|
|
118
|
+
// can we just test with indexes? surely faster
|
|
119
|
+
|
|
120
|
+
if (/^'.*?'$/.test(sheet_name)) {
|
|
121
|
+
sheet_name = sheet_name.substring(1, sheet_name.length - 1);
|
|
131
122
|
}
|
|
123
|
+
|
|
124
|
+
const sheet = this.sheets.ID(sheet_name);
|
|
125
|
+
return this.named.Get_(parts[1], sheet || 0, true); // require scope in this case
|
|
132
126
|
|
|
133
|
-
return undefined;
|
|
134
|
-
}
|
|
135
127
|
|
|
136
|
-
/** get name for sheet with given id */
|
|
137
|
-
public Name(id: number): string|undefined {
|
|
138
|
-
return this.ids.get(id)?.name || undefined;
|
|
139
128
|
}
|
|
140
129
|
|
|
141
|
-
/**
|
|
142
|
-
|
|
143
|
-
|
|
130
|
+
/**
|
|
131
|
+
* from import, we get ranges and expressions in the same format. we
|
|
132
|
+
* need to check if something is a range -- if so, it will just be
|
|
133
|
+
* a single address or range. in that case store it as a named range.
|
|
134
|
+
*/
|
|
135
|
+
public UnserializeComposite(names: CompositeNamed[], active_sheet?: Sheet) {
|
|
136
|
+
|
|
137
|
+
this.parser.Save();
|
|
138
|
+
this.parser.SetLocaleSettings(DecimalMarkType.Period, ArgumentSeparatorType.Comma);
|
|
139
|
+
|
|
140
|
+
const sorted = names.map(named => {
|
|
141
|
+
const parse_result = this.parser.Parse(named.expression);
|
|
142
|
+
if (parse_result.expression) {
|
|
143
|
+
if (parse_result.expression.type === 'address' || parse_result.expression.type === 'range') {
|
|
144
|
+
return {
|
|
145
|
+
...named,
|
|
146
|
+
expression: undefined,
|
|
147
|
+
area: parse_result.expression.type === 'address' ? {
|
|
148
|
+
start: parse_result.expression,
|
|
149
|
+
end: parse_result.expression,
|
|
150
|
+
} : parse_result.expression,
|
|
151
|
+
} as SerializedNamed;
|
|
152
|
+
}
|
|
153
|
+
return {
|
|
154
|
+
...named
|
|
155
|
+
} as SerializedNamed;
|
|
156
|
+
}
|
|
157
|
+
return undefined;
|
|
158
|
+
}).filter((test): test is SerializedNamed => !!test);
|
|
159
|
+
|
|
160
|
+
this.parser.Restore();
|
|
161
|
+
this.UnserializeNames(sorted, active_sheet);
|
|
162
|
+
|
|
144
163
|
}
|
|
145
164
|
|
|
146
|
-
|
|
147
|
-
|
|
165
|
+
public UnserializeNames(names: SerializedNamed[], active_sheet?: Sheet) {
|
|
166
|
+
|
|
167
|
+
for (const entry of names) {
|
|
168
|
+
|
|
169
|
+
const scope = (typeof entry.scope === 'string') ? this.sheets.ID(entry.scope) : undefined;
|
|
170
|
+
|
|
171
|
+
if (entry.expression) {
|
|
172
|
+
|
|
173
|
+
const parse_result = this.parser.Parse(entry.expression);
|
|
174
|
+
|
|
175
|
+
if (parse_result.valid && parse_result.expression) {
|
|
176
|
+
this.parser.Walk(parse_result.expression, unit => {
|
|
177
|
+
if (unit.type === 'address' || unit.type === 'range') {
|
|
178
|
+
if (unit.type === 'range') {
|
|
179
|
+
unit = unit.start;
|
|
180
|
+
}
|
|
181
|
+
if (!unit.sheet_id) {
|
|
182
|
+
if (unit.sheet) {
|
|
183
|
+
unit.sheet_id = this.sheets.ID(unit.sheet);
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
if (!unit.sheet_id) {
|
|
187
|
+
unit.sheet_id = active_sheet?.id;
|
|
188
|
+
}
|
|
189
|
+
return false; // don't continue in ranges
|
|
190
|
+
}
|
|
191
|
+
return true;
|
|
192
|
+
});
|
|
193
|
+
|
|
194
|
+
this.named.SetNamedExpression(entry.name, parse_result.expression, scope);
|
|
195
|
+
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
}
|
|
199
|
+
else if (entry.area) {
|
|
200
|
+
if (entry.area.start.sheet) {
|
|
148
201
|
|
|
149
|
-
|
|
150
|
-
|
|
202
|
+
const area = new Area({
|
|
203
|
+
...entry.area.start,
|
|
204
|
+
sheet_id: this.sheets.ID(entry.area.start.sheet),
|
|
205
|
+
}, entry.area.end);
|
|
151
206
|
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
207
|
+
if (area.start.sheet_id) {
|
|
208
|
+
this.named.SetNamedRange(entry.name, area, scope);
|
|
209
|
+
}
|
|
210
|
+
else {
|
|
211
|
+
console.warn("missing sheet ID?", entry.area.start.sheet);
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
else {
|
|
215
|
+
console.warn("missing sheet name?");
|
|
216
|
+
}
|
|
217
|
+
}
|
|
156
218
|
}
|
|
157
219
|
|
|
158
220
|
}
|
|
159
221
|
|
|
222
|
+
/**
|
|
223
|
+
* serialize names. ranges are easy, but make sure there's a sheet name
|
|
224
|
+
* in each address (and remove the ID). expressions are a little more
|
|
225
|
+
* complicated.
|
|
226
|
+
*/
|
|
227
|
+
public SerializeNames(active_sheet?: Sheet): SerializedNamed[] {
|
|
228
|
+
const list: SerializedNamed[] = [];
|
|
160
229
|
|
|
161
|
-
|
|
230
|
+
for (const entry of this.named.list) {
|
|
162
231
|
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
232
|
+
const named: SerializedNamed = {
|
|
233
|
+
name: entry.name,
|
|
234
|
+
// scope: entry.scope,
|
|
235
|
+
};
|
|
167
236
|
|
|
168
|
-
|
|
237
|
+
if (entry.scope) {
|
|
238
|
+
named.scope = this.sheets.Name(entry.scope);
|
|
239
|
+
}
|
|
169
240
|
|
|
170
|
-
|
|
171
|
-
public document_name?: string;
|
|
241
|
+
if (entry.type === 'expression') {
|
|
172
242
|
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
public user_data?: any;
|
|
243
|
+
this.parser.Walk(entry.expression, unit => {
|
|
244
|
+
if (unit.type === 'address' || unit.type === 'range') {
|
|
176
245
|
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
* don't have to look up. FIXME/TODO
|
|
180
|
-
*/
|
|
181
|
-
// public sheets: Sheet[] = [];
|
|
246
|
+
const test = unit.type === 'range' ? unit.start : unit;
|
|
247
|
+
test.absolute_column = test.absolute_row = true;
|
|
182
248
|
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
249
|
+
if (!test.sheet) {
|
|
250
|
+
if (test.sheet_id) {
|
|
251
|
+
test.sheet = this.sheets.Name(test.sheet_id);
|
|
252
|
+
}
|
|
253
|
+
if (!test.sheet) {
|
|
254
|
+
test.sheet = active_sheet?.name;
|
|
255
|
+
}
|
|
256
|
+
}
|
|
190
257
|
|
|
191
|
-
|
|
192
|
-
|
|
258
|
+
if (unit.type === 'range') {
|
|
259
|
+
unit.end.absolute_column = unit.end.absolute_row = true;
|
|
260
|
+
}
|
|
193
261
|
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
262
|
+
return false;
|
|
263
|
+
}
|
|
264
|
+
/*
|
|
197
265
|
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
//public named_expressions: Record<string, ExpressionUnit> = {};
|
|
202
|
-
public readonly named_expressions: Map<string, ExpressionUnit> = new Map();
|
|
266
|
+
// if we do the function export thing, we need to call that here
|
|
267
|
+
// (exporting functions to fix missing arguments or add decorators).
|
|
268
|
+
// we're not doing that, at least not yet.
|
|
203
269
|
|
|
204
|
-
|
|
205
|
-
|
|
270
|
+
else if (unit.type === 'call' && options.export_functions) {
|
|
271
|
+
// ...
|
|
272
|
+
}
|
|
273
|
+
*/
|
|
274
|
+
return true;
|
|
275
|
+
});
|
|
206
276
|
|
|
207
|
-
|
|
208
|
-
* base style properties moved to model, so we can have a single
|
|
209
|
-
* and consistent reference.
|
|
210
|
-
*/
|
|
211
|
-
public theme_style_properties: CellStyle = JSON.parse(JSON.stringify(Style.DefaultProperties));
|
|
277
|
+
named.expression = this.parser.Render(entry.expression, { missing: '' });
|
|
212
278
|
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
279
|
+
}
|
|
280
|
+
else {
|
|
281
|
+
named.area = {
|
|
282
|
+
start: {
|
|
283
|
+
...entry.area.start,
|
|
284
|
+
sheet: this.sheets.Name(entry.area.start.sheet_id || 0) || '',
|
|
285
|
+
sheet_id: undefined, // in favor of name
|
|
286
|
+
absolute_column: true,
|
|
287
|
+
absolute_row: true,
|
|
288
|
+
},
|
|
289
|
+
end: {
|
|
290
|
+
...entry.area.end,
|
|
291
|
+
absolute_column: true,
|
|
292
|
+
absolute_row: true,
|
|
293
|
+
},
|
|
294
|
+
};
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
list.push(named);
|
|
298
|
+
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
return list;
|
|
302
|
+
}
|
|
219
303
|
|
|
220
304
|
/**
|
|
221
305
|
* putting this here temporarily. it should probably move into a table
|
|
@@ -332,7 +416,7 @@ export class DataModel {
|
|
|
332
416
|
* @param address
|
|
333
417
|
* @param active_sheet
|
|
334
418
|
*/
|
|
335
|
-
public AddressToLabel(address: ICellAddress|IArea
|
|
419
|
+
public AddressToLabel(address: ICellAddress|IArea) {
|
|
336
420
|
|
|
337
421
|
const start = IsCellAddress(address) ? address : address.start;
|
|
338
422
|
const parts = IsCellAddress(address) ?
|
|
@@ -369,15 +453,6 @@ export class DataModel {
|
|
|
369
453
|
return true;
|
|
370
454
|
}
|
|
371
455
|
|
|
372
|
-
/*
|
|
373
|
-
const lc = target.sheet.toLowerCase();
|
|
374
|
-
for (const sheet of this.model.sheets.list) {
|
|
375
|
-
if (sheet.name.toLowerCase() === lc) {
|
|
376
|
-
target.sheet_id = sheet.id;
|
|
377
|
-
return true;
|
|
378
|
-
}
|
|
379
|
-
}
|
|
380
|
-
*/
|
|
381
456
|
}
|
|
382
457
|
else if (context?.sheet_id) {
|
|
383
458
|
target.sheet_id = context.sheet_id;
|
|
@@ -435,12 +510,20 @@ export class DataModel {
|
|
|
435
510
|
}
|
|
436
511
|
else if (parse_result.expression && parse_result.expression.type === 'identifier') {
|
|
437
512
|
|
|
513
|
+
const named = this.GetName(parse_result.expression.name, active_sheet.id);
|
|
514
|
+
if (named?.type === 'range') {
|
|
515
|
+
return named.area;
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
/*
|
|
438
519
|
// is named range guaranteed to have a sheet ID? (I think yes...)
|
|
439
520
|
|
|
440
521
|
const named_range = this.named_ranges.Get(parse_result.expression.name);
|
|
441
522
|
if (named_range) {
|
|
442
523
|
return named_range;
|
|
443
524
|
}
|
|
525
|
+
*/
|
|
526
|
+
|
|
444
527
|
}
|
|
445
528
|
|
|
446
529
|
return { row: 0, column: 0 }; // default for string types -- broken
|
|
@@ -479,6 +562,9 @@ export class DataModel {
|
|
|
479
562
|
|
|
480
563
|
}
|
|
481
564
|
|
|
565
|
+
/**
|
|
566
|
+
* @internal
|
|
567
|
+
*/
|
|
482
568
|
export interface ViewModel {
|
|
483
569
|
active_sheet: Sheet;
|
|
484
570
|
view_index: number;
|
|
@@ -492,9 +578,21 @@ export interface SerializedNamedExpression {
|
|
|
492
578
|
export interface SerializedModel {
|
|
493
579
|
sheet_data: SerializedSheet[];
|
|
494
580
|
active_sheet: number;
|
|
581
|
+
|
|
582
|
+
/** @deprecated */
|
|
495
583
|
named_ranges?: Record<string, IArea>;
|
|
584
|
+
|
|
585
|
+
/** @deprecated */
|
|
586
|
+
named_expressions?: SerializedNamedExpression[];
|
|
587
|
+
|
|
588
|
+
/**
|
|
589
|
+
* new type for consolidated named ranges & expressions. the old
|
|
590
|
+
* types are retained for backwards compatibility on import but we won't
|
|
591
|
+
* export them anymore.
|
|
592
|
+
*/
|
|
593
|
+
named?: SerializedNamed[];
|
|
594
|
+
|
|
496
595
|
macro_functions?: MacroFunction[];
|
|
497
596
|
tables?: Table[];
|
|
498
|
-
named_expressions?: SerializedNamedExpression[];
|
|
499
597
|
decimal_mark?: ','|'.';
|
|
500
598
|
}
|
|
@@ -0,0 +1,45 @@
|
|
|
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
|
+
export { DataModel } from './data_model';
|
|
23
|
+
|
|
24
|
+
export type {
|
|
25
|
+
MacroFunction,
|
|
26
|
+
ConnectedElementType,
|
|
27
|
+
SerializedNamedExpression,
|
|
28
|
+
SerializedModel,
|
|
29
|
+
ViewModel,
|
|
30
|
+
SerializedMacroFunction,
|
|
31
|
+
} from './data_model';
|
|
32
|
+
|
|
33
|
+
export type { SerializedNamed, CompositeNamed } from './named';
|
|
34
|
+
|
|
35
|
+
export { Sheet } from './sheet';
|
|
36
|
+
export type { SerializedSheet, FreezePane, LegacySerializedSheet } from './sheet_types';
|
|
37
|
+
export * from './conditional_format';
|
|
38
|
+
export type { GridSelection } from './sheet_selection';
|
|
39
|
+
export type { SerializeOptions } from './serialize_options';
|
|
40
|
+
|
|
41
|
+
export { Annotation } from './annotation';
|
|
42
|
+
export type { ViewData as AnnotationViewData } from './annotation';
|
|
43
|
+
export type { AnnotationData, AnnotationType } from './annotation';
|
|
44
|
+
|
|
45
|
+
|