@trebco/treb 28.17.5 → 29.1.4
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/text_part.ts +7 -0
- 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 -2
- 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 +18 -8
- package/treb-grid/src/types/grid.ts +96 -67
- 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,376 +1,459 @@
|
|
|
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
|
-
import {
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
this.
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
*
|
|
130
|
-
*
|
|
131
|
-
*/
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
const
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
}
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
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
|
+
import { Area } from 'treb-base-types';
|
|
23
|
+
import type { SerializedArea, IArea } from 'treb-base-types';
|
|
24
|
+
import type { ExpressionUnit } from 'treb-parser';
|
|
25
|
+
|
|
26
|
+
export interface NamedExpression {
|
|
27
|
+
type: 'expression';
|
|
28
|
+
expression: ExpressionUnit;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export interface NamedRange {
|
|
32
|
+
type: 'range';
|
|
33
|
+
area: Area;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export type Named = (NamedExpression | NamedRange) & {
|
|
37
|
+
name: string; // canonical name
|
|
38
|
+
scope?: number; // scope to sheet by ID
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* serialized type
|
|
43
|
+
*
|
|
44
|
+
* @privateRemarks
|
|
45
|
+
*
|
|
46
|
+
* for the external type we switch on the presence of the area
|
|
47
|
+
* or the expression. area uses a type that includes sheet names
|
|
48
|
+
* (IArea should allow that?). expression here is a string.
|
|
49
|
+
*
|
|
50
|
+
* we could theoretically switch the internal type the same way
|
|
51
|
+
* and drop the string keys. something to think about.
|
|
52
|
+
*
|
|
53
|
+
* when serialized, scope is either the sheet name or nothing
|
|
54
|
+
* (implicit global scope).
|
|
55
|
+
*/
|
|
56
|
+
export interface SerializedNamed {
|
|
57
|
+
name: string;
|
|
58
|
+
area?: SerializedArea;
|
|
59
|
+
expression?: string;
|
|
60
|
+
scope?: string;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* this is a type we're using in imports. it consolidates the
|
|
65
|
+
* two types. we should maybe switch as well, at least for
|
|
66
|
+
* serialized representation? something to think about.
|
|
67
|
+
*/
|
|
68
|
+
export interface CompositeNamed {
|
|
69
|
+
|
|
70
|
+
name: string;
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* could be a address/range or a function expression. we'll distinguish
|
|
74
|
+
* when we parse it.
|
|
75
|
+
*/
|
|
76
|
+
expression: string;
|
|
77
|
+
|
|
78
|
+
/** resolved sheet name */
|
|
79
|
+
scope?: string;
|
|
80
|
+
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* this is a replacement for the name manager, which handles
|
|
85
|
+
* operations relating to named ranges.
|
|
86
|
+
*/
|
|
87
|
+
export class NamedRangeManager {
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* this map is stored with normalized names. normalized names
|
|
91
|
+
* here means we call `toLowerCase`. the objects themselves
|
|
92
|
+
* contain canonical names.
|
|
93
|
+
*
|
|
94
|
+
* ...we've always had a map for this, for fast lookups. but
|
|
95
|
+
* with scoping, we can't necessarily look up by name. let's try
|
|
96
|
+
* using scope:name keys. that way we can search for scope:name
|
|
97
|
+
* and then name and return the first match, if any.
|
|
98
|
+
*
|
|
99
|
+
*/
|
|
100
|
+
protected named: Map<string, Named> = new Map();
|
|
101
|
+
|
|
102
|
+
public get list() {
|
|
103
|
+
return this.named.values();
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/** shorthand for setting named expression */
|
|
107
|
+
public SetNamedExpression(name: string, expression: ExpressionUnit, scope?: number) {
|
|
108
|
+
return this.SetName({
|
|
109
|
+
type: 'expression',
|
|
110
|
+
name,
|
|
111
|
+
expression,
|
|
112
|
+
scope,
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/** shorthand for setting named range */
|
|
117
|
+
public SetNamedRange(name: string, area: IArea, scope?: number) {
|
|
118
|
+
return this.SetName({
|
|
119
|
+
type: 'range',
|
|
120
|
+
name,
|
|
121
|
+
area: new Area(area.start, area.end),
|
|
122
|
+
scope,
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* add name. names are case-insensitive. if the name already
|
|
128
|
+
* exists, it will be overwritten.
|
|
129
|
+
*
|
|
130
|
+
* update: returns success (FIXME: proper errors)
|
|
131
|
+
*/
|
|
132
|
+
private SetName(named: Named): boolean {
|
|
133
|
+
|
|
134
|
+
const name = named.name;
|
|
135
|
+
// console.info('set name', named.name, {named});
|
|
136
|
+
|
|
137
|
+
const validated = this.ValidateNamed(name);
|
|
138
|
+
if (!validated) {
|
|
139
|
+
console.warn('invalid name');
|
|
140
|
+
return false;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
if (named.type === 'range') {
|
|
144
|
+
|
|
145
|
+
// why is this considered invalid here? I've seen it done.
|
|
146
|
+
// maybe something we're doing with these ranges doesn't
|
|
147
|
+
// collapse them? (...)
|
|
148
|
+
|
|
149
|
+
if (named.area.entire_column || named.area.entire_row) {
|
|
150
|
+
console.info({named});
|
|
151
|
+
console.warn(`invalid range`);
|
|
152
|
+
return false;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// this.named.set(name.toLowerCase(), named);
|
|
158
|
+
this.named.set(this.ScopedName(name, named.scope), named);
|
|
159
|
+
|
|
160
|
+
return true;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
private ScopedName(name: string, scope?: number) {
|
|
164
|
+
if (typeof scope === 'number') {
|
|
165
|
+
return scope + ':' + name.toLowerCase();
|
|
166
|
+
}
|
|
167
|
+
return name.toLowerCase();
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
public ClearName(name: string, scope?: number): void {
|
|
171
|
+
|
|
172
|
+
if (typeof scope === 'number') {
|
|
173
|
+
this.named.delete(this.ScopedName(name, scope));
|
|
174
|
+
}
|
|
175
|
+
else {
|
|
176
|
+
this.named.delete(name.toLowerCase());
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
/**
|
|
181
|
+
* if we delete a sheet, remove ranges in that sheet. also remove
|
|
182
|
+
* anything that's scoped to the sheet.
|
|
183
|
+
*/
|
|
184
|
+
public RemoveRangesForSheet(sheet_id: number) {
|
|
185
|
+
|
|
186
|
+
const remove: string[] = [];
|
|
187
|
+
for (const [name, entry] of this.named) {
|
|
188
|
+
if (entry.type === 'range' && entry.area.start.sheet_id === sheet_id) {
|
|
189
|
+
remove.push(name);
|
|
190
|
+
}
|
|
191
|
+
else if (entry.scope === sheet_id) {
|
|
192
|
+
remove.push(name);
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
for (const name of remove) {
|
|
197
|
+
this.named.delete(name)
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
public Reset() {
|
|
203
|
+
this.named.clear();
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
/**
|
|
207
|
+
* requiring scope to help propgogate changes. we check the scoped
|
|
208
|
+
* version first; if that's not found, we default to the global version.
|
|
209
|
+
* that implies that if there are both, we'll prefer the scoped name.
|
|
210
|
+
*
|
|
211
|
+
* now possible to require scope, for qualified scoped names
|
|
212
|
+
*/
|
|
213
|
+
public Get_(name: string, scope: number, require_scope = false) {
|
|
214
|
+
|
|
215
|
+
if (require_scope) {
|
|
216
|
+
return this.named.get(this.ScopedName(name, scope));
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
return this.named.get(this.ScopedName(name, scope)) || this.named.get(name.toLowerCase());
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
/**
|
|
223
|
+
* named range rules:
|
|
224
|
+
*
|
|
225
|
+
* - legal characters are alphanumeric, underscore and dot.
|
|
226
|
+
* - must start with letter or underscore (not a number or dot).
|
|
227
|
+
* - cannot look like a spreadsheet address, which is 1-3 letters followed by numbers.
|
|
228
|
+
*
|
|
229
|
+
* returns a normalized name (just caps, atm)
|
|
230
|
+
*/
|
|
231
|
+
public ValidateNamed(name: string): string|false {
|
|
232
|
+
name = name.trim();
|
|
233
|
+
if (!name.length) return false;
|
|
234
|
+
if (/^[A-Za-z]{1,3}\d+$/.test(name)) return false;
|
|
235
|
+
if (/[^A-Za-z\d_.]/.test(name)) return false;
|
|
236
|
+
if (/^[^A-Za-z_]/.test(name)) return false;
|
|
237
|
+
return name.toUpperCase();
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
/**
|
|
241
|
+
* match an area, optionally a target within a larger area (for selections).
|
|
242
|
+
* we don't use the selection directly, as we may need to adjust target for
|
|
243
|
+
* merge area. returns the name only if the area is an exact match.
|
|
244
|
+
*/
|
|
245
|
+
public MatchSelection(area: Area, target?: Area): string|undefined {
|
|
246
|
+
|
|
247
|
+
if (!area.start.sheet_id) {
|
|
248
|
+
throw new Error('match selection without sheet id');
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
let label: string|undefined;
|
|
252
|
+
|
|
253
|
+
for (const entry of this.named.values()) {
|
|
254
|
+
if (entry.type === 'range') {
|
|
255
|
+
if (entry.area.start.sheet_id === area.start.sheet_id) {
|
|
256
|
+
if (area.Equals(entry.area)) {
|
|
257
|
+
label = entry.name; // don't break, in case there's a match for target which takes precendence.
|
|
258
|
+
}
|
|
259
|
+
if (target?.Equals(entry.area)) {
|
|
260
|
+
return entry.name;
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
return label;
|
|
267
|
+
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
|
|
271
|
+
/**
|
|
272
|
+
* fix named range references after row/column insert/delete
|
|
273
|
+
*
|
|
274
|
+
* surely there's overlap between this function and what we do in
|
|
275
|
+
* grid when columns are added/removed. can we consolidate? (FIXME/TODO)
|
|
276
|
+
*
|
|
277
|
+
*/
|
|
278
|
+
public PatchNamedRanges(sheet_id: number, before_column: number, column_count: number, before_row: number, row_count: number) {
|
|
279
|
+
|
|
280
|
+
const copy = [...this.list];
|
|
281
|
+
|
|
282
|
+
for (const entry of copy) {
|
|
283
|
+
|
|
284
|
+
if (entry.type === 'expression') {
|
|
285
|
+
continue;
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
const key = entry.name;
|
|
289
|
+
const range = entry.area;
|
|
290
|
+
|
|
291
|
+
if (range.start.sheet_id !== sheet_id) {
|
|
292
|
+
console.info('skipping name', key);
|
|
293
|
+
continue;
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
if (column_count && before_column <= range.end.column) {
|
|
297
|
+
|
|
298
|
+
/*
|
|
299
|
+
// (1) we are before the insert point, not affected
|
|
300
|
+
|
|
301
|
+
if (before_column > range.end.column) {
|
|
302
|
+
continue;
|
|
303
|
+
}
|
|
304
|
+
*/
|
|
305
|
+
|
|
306
|
+
if (column_count > 0) {
|
|
307
|
+
|
|
308
|
+
// (2) it's an insert and we are past the insert point:
|
|
309
|
+
// increment [start] and [end] by [count]
|
|
310
|
+
|
|
311
|
+
if (before_column <= range.start.column) {
|
|
312
|
+
range.Shift(0, column_count);
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
// (3) it's an insert and we contain the insert point:
|
|
316
|
+
// increment [end] by [count]
|
|
317
|
+
|
|
318
|
+
else if (before_column > range.start.column && before_column <= range.end.column) {
|
|
319
|
+
range.ConsumeAddress({row: range.end.row, column: range.end.column + column_count});
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
else {
|
|
323
|
+
console.warn(`PNR X case 1`, before_column, column_count, JSON.stringify(range));
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
}
|
|
327
|
+
else if (column_count < 0) {
|
|
328
|
+
|
|
329
|
+
// (4) it's a delete and we are past the delete point (before+count):
|
|
330
|
+
// decrement [start] and [end] by [count]
|
|
331
|
+
|
|
332
|
+
if (before_column - column_count <= range.start.column) {
|
|
333
|
+
range.Shift(0, column_count);
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
// (5) it's a delete and contains the entire range
|
|
337
|
+
|
|
338
|
+
else if (before_column <= range.start.column && before_column - column_count > range.end.column) {
|
|
339
|
+
this.ClearName(key);
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
// (6) it's a delete and contains part of the range. clip the range.
|
|
343
|
+
|
|
344
|
+
else if (before_column <= range.start.column) {
|
|
345
|
+
const last_column = before_column - column_count - 1;
|
|
346
|
+
this.SetName({
|
|
347
|
+
type: 'range', area: new Area({
|
|
348
|
+
row: range.start.row, column: last_column + 1 + column_count, sheet_id }, {
|
|
349
|
+
row: range.end.row, column: range.end.column + column_count }), name: key, });
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
else if (before_column <= range.end.column) {
|
|
353
|
+
const last_column = before_column - column_count - 1;
|
|
354
|
+
|
|
355
|
+
if (last_column >= range.end.column) {
|
|
356
|
+
this.SetName({ type: 'range', area: new Area({
|
|
357
|
+
row: range.start.row, column: range.start.column, sheet_id }, {
|
|
358
|
+
row: range.end.row, column: before_column - 1 }), name: key });
|
|
359
|
+
}
|
|
360
|
+
else {
|
|
361
|
+
this.SetName({ type: 'range', name: key, area: new Area({
|
|
362
|
+
row: range.start.row, column: range.start.column, sheet_id }, {
|
|
363
|
+
row: range.end.row, column: range.start.column + range.columns + column_count - 1})});
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
else {
|
|
369
|
+
console.warn(`PNR X case 2`, before_column, column_count, JSON.stringify(range));
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
|
|
376
|
+
if (row_count && before_row <= range.end.row) {
|
|
377
|
+
|
|
378
|
+
/*
|
|
379
|
+
// (1) we are before the insert point, not affected
|
|
380
|
+
|
|
381
|
+
if (before_row > range.end.row) {
|
|
382
|
+
continue;
|
|
383
|
+
}
|
|
384
|
+
*/
|
|
385
|
+
|
|
386
|
+
if (row_count > 0) {
|
|
387
|
+
|
|
388
|
+
// (2) it's an insert and we are past the insert point:
|
|
389
|
+
// increment [start] and [end] by [count]
|
|
390
|
+
|
|
391
|
+
if (before_row <= range.start.row) {
|
|
392
|
+
range.Shift(row_count, 0);
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
// (3) it's an insert and we contain the insert point:
|
|
396
|
+
// increment [end] by [count]
|
|
397
|
+
|
|
398
|
+
else if (before_row > range.start.row && before_row <= range.end.row) {
|
|
399
|
+
range.ConsumeAddress({row: range.end.row + row_count, column: range.end.column});
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
else {
|
|
403
|
+
console.warn(`PNR X case 3`, before_row, row_count, JSON.stringify(range));
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
}
|
|
407
|
+
else if (row_count < 0) {
|
|
408
|
+
|
|
409
|
+
// (4) it's a delete and we are past the delete point (before+count):
|
|
410
|
+
// decrement [start] and [end] by [count]
|
|
411
|
+
|
|
412
|
+
if (before_row - row_count <= range.start.row) {
|
|
413
|
+
range.Shift(row_count, 0);
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
// (5) it's a delete and contains the entire range
|
|
417
|
+
|
|
418
|
+
else if (before_row <= range.start.row && before_row - row_count > range.end.row) {
|
|
419
|
+
this.ClearName(key);
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
// (6) it's a delete and contains part of the range. clip the range.
|
|
423
|
+
|
|
424
|
+
else if (before_row <= range.start.row) {
|
|
425
|
+
const last_row = before_row - row_count - 1;
|
|
426
|
+
this.SetNamedRange(key, new Area({
|
|
427
|
+
column: range.start.column, row: last_row + 1 + row_count, sheet_id }, {
|
|
428
|
+
column: range.end.column, row: range.end.row + row_count }));
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
else if (before_row <= range.end.row) {
|
|
432
|
+
const last_row = before_row - row_count - 1;
|
|
433
|
+
if (last_row >= range.end.row) {
|
|
434
|
+
this.SetNamedRange(key, new Area({
|
|
435
|
+
column: range.start.column, row: range.start.row, sheet_id }, {
|
|
436
|
+
column: range.end.column, row: before_row - 1 }));
|
|
437
|
+
}
|
|
438
|
+
else {
|
|
439
|
+
this.SetNamedRange(key, new Area({
|
|
440
|
+
column: range.start.column, row: range.start.row, sheet_id }, {
|
|
441
|
+
column: range.end.column, row: range.start.row + range.rows + row_count - 1 }));
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
else {
|
|
447
|
+
console.warn(`PNR X case 4`, before_row, row_count, JSON.stringify(range));
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
|