@trebco/treb 27.5.3 → 27.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/treb-spreadsheet.mjs +14 -14
- package/dist/treb.d.ts +37 -23
- package/notes/conditional-fomratring.md +29 -0
- package/package.json +3 -3
- package/treb-base-types/src/area.ts +181 -0
- package/treb-base-types/src/evaluate-options.ts +21 -0
- package/treb-base-types/src/gradient.ts +97 -0
- package/treb-base-types/src/import.ts +2 -1
- package/treb-base-types/src/index.ts +2 -0
- package/treb-calculator/src/calculator.ts +205 -132
- package/treb-calculator/src/dag/calculation_leaf_vertex.ts +97 -0
- package/treb-calculator/src/dag/graph.ts +10 -22
- package/treb-calculator/src/dag/{leaf_vertex.ts → state_leaf_vertex.ts} +3 -3
- package/treb-calculator/src/descriptors.ts +10 -3
- package/treb-calculator/src/expression-calculator.ts +1 -1
- package/treb-calculator/src/function-library.ts +25 -22
- package/treb-calculator/src/functions/base-functions.ts +166 -5
- package/treb-calculator/src/index.ts +6 -6
- package/treb-calculator/src/notifier-types.ts +1 -1
- package/treb-calculator/src/utilities.ts +2 -2
- package/treb-charts/src/util.ts +2 -2
- package/treb-embed/src/embedded-spreadsheet.ts +382 -71
- package/treb-embed/style/formula-bar.scss +2 -0
- package/treb-embed/style/theme-defaults.scss +46 -15
- package/treb-export/src/export-worker/export-worker.ts +0 -13
- package/treb-export/src/export2.ts +187 -2
- package/treb-export/src/import2.ts +169 -4
- package/treb-export/src/workbook-style2.ts +56 -8
- package/treb-export/src/workbook2.ts +10 -1
- package/treb-grid/src/editors/editor.ts +1276 -0
- package/treb-grid/src/editors/external_editor.ts +113 -0
- package/treb-grid/src/editors/formula_bar.ts +450 -474
- package/treb-grid/src/editors/overlay_editor.ts +437 -512
- package/treb-grid/src/index.ts +2 -1
- package/treb-grid/src/layout/base_layout.ts +24 -16
- package/treb-grid/src/render/tile_renderer.ts +2 -1
- package/treb-grid/src/types/conditional_format.ts +168 -0
- package/treb-grid/src/types/data_model.ts +130 -3
- package/treb-grid/src/types/external_editor_config.ts +47 -0
- package/treb-grid/src/types/grid.ts +96 -45
- package/treb-grid/src/types/grid_base.ts +187 -35
- package/treb-grid/src/types/scale-control.ts +1 -1
- package/treb-grid/src/types/sheet.ts +330 -26
- package/treb-grid/src/types/sheet_types.ts +4 -0
- package/treb-grid/src/util/dom_utilities.ts +58 -25
- package/treb-grid/src/editors/formula_editor_base.ts +0 -912
- package/treb-grid/src/types/external_editor.ts +0 -27
- /package/{README-shadow-DOM.md → notes/shadow-DOM.md} +0 -0
|
@@ -80,13 +80,15 @@ import { TileRange } from '../layout/base_layout';
|
|
|
80
80
|
import { GridLayout } from '../layout/grid_layout';
|
|
81
81
|
|
|
82
82
|
import type { GridSelection } from './grid_selection';
|
|
83
|
-
import { OverlayEditor
|
|
83
|
+
import { OverlayEditor } from '../editors/overlay_editor';
|
|
84
84
|
|
|
85
85
|
import { TileRenderer } from '../render/tile_renderer';
|
|
86
86
|
import type { GridEvent } from './grid_events';
|
|
87
87
|
import { ErrorCode } from './grid_events';
|
|
88
88
|
import type { LegacySerializedSheet } from './sheet_types';
|
|
89
|
+
// import { FormulaBar } from '../editors/formula_bar';
|
|
89
90
|
import { FormulaBar } from '../editors/formula_bar';
|
|
91
|
+
|
|
90
92
|
import type { GridOptions } from './grid_options';
|
|
91
93
|
import { BorderConstants } from './border_constants';
|
|
92
94
|
import type { SerializeOptions } from './serialize_options';
|
|
@@ -114,7 +116,8 @@ import { GridBase } from './grid_base';
|
|
|
114
116
|
import type { SetRangeOptions } from './set_range_options';
|
|
115
117
|
import type { ClipboardCellData } from './clipboard_data';
|
|
116
118
|
|
|
117
|
-
import type {
|
|
119
|
+
import type { ExternalEditorConfig } from './external_editor_config';
|
|
120
|
+
import { ExternalEditor } from '../editors/external_editor';
|
|
118
121
|
|
|
119
122
|
interface DoubleClickData {
|
|
120
123
|
timeout?: number;
|
|
@@ -148,7 +151,7 @@ export class Grid extends GridBase {
|
|
|
148
151
|
this.UpdateAnnotationLayout();
|
|
149
152
|
this.layout.UpdateAnnotation(this.active_sheet.annotations);
|
|
150
153
|
this.layout.ApplyTheme(this.theme);
|
|
151
|
-
this.overlay_editor?.
|
|
154
|
+
this.overlay_editor?.UpdateScale(value);
|
|
152
155
|
this.tab_bar?.UpdateScale(value);
|
|
153
156
|
|
|
154
157
|
this.grid_events.Publish({
|
|
@@ -264,14 +267,18 @@ export class Grid extends GridBase {
|
|
|
264
267
|
|
|
265
268
|
private RESIZE_PIXEL_BUFFER = 5;
|
|
266
269
|
|
|
267
|
-
|
|
270
|
+
/**
|
|
271
|
+
* formalizing the concept of external selection to support outside tooling.
|
|
272
|
+
*
|
|
273
|
+
* FIXME: stop testing on this field. we need a better way to figure out
|
|
274
|
+
* if the external editor is active.
|
|
275
|
+
*/
|
|
276
|
+
private external_editor_config?: Partial<ExternalEditorConfig>;
|
|
268
277
|
|
|
269
278
|
/**
|
|
270
|
-
*
|
|
271
|
-
* we're formalizing the concept of external selection to support outside
|
|
272
|
-
* tooling.
|
|
279
|
+
* support for external editor. created on demand.
|
|
273
280
|
*/
|
|
274
|
-
private external_editor
|
|
281
|
+
private external_editor?: ExternalEditor;
|
|
275
282
|
|
|
276
283
|
/**
|
|
277
284
|
* flag indicating we're resizing, or hovering over a resize.
|
|
@@ -357,12 +364,11 @@ export class Grid extends GridBase {
|
|
|
357
364
|
*/
|
|
358
365
|
constructor(
|
|
359
366
|
options: GridOptions = {},
|
|
360
|
-
parser: Parser,
|
|
361
367
|
model: DataModel,
|
|
362
368
|
theme: Theme = DefaultTheme,
|
|
363
369
|
initialze_dom = true ) {
|
|
364
370
|
|
|
365
|
-
super(options,
|
|
371
|
+
super(options, model);
|
|
366
372
|
|
|
367
373
|
this.decimal_separator_code = Localization.decimal_separator.charCodeAt(0);
|
|
368
374
|
|
|
@@ -1234,7 +1240,7 @@ export class Grid extends GridBase {
|
|
|
1234
1240
|
this.UpdateLayout(); // in case we have changed font size
|
|
1235
1241
|
// this.selection_renderer.Flush();
|
|
1236
1242
|
|
|
1237
|
-
this.overlay_editor?.
|
|
1243
|
+
this.overlay_editor?.UpdateScale(this.layout.scale);
|
|
1238
1244
|
|
|
1239
1245
|
// if (this.formula_bar) this.formula_bar.UpdateTheme();
|
|
1240
1246
|
|
|
@@ -1659,21 +1665,48 @@ export class Grid extends GridBase {
|
|
|
1659
1665
|
* want to have to use a duplicate internal field, it's clumsy and this
|
|
1660
1666
|
* class isn't public-facing so it's not super important.
|
|
1661
1667
|
*/
|
|
1662
|
-
public ExternalEditor(
|
|
1668
|
+
public ExternalEditor(config: Partial<ExternalEditorConfig>|undefined) {
|
|
1669
|
+
|
|
1670
|
+
this.external_editor_config = config;
|
|
1671
|
+
|
|
1672
|
+
if (config) {
|
|
1663
1673
|
|
|
1664
|
-
|
|
1674
|
+
const areas: Area[] = (config.dependencies || []).filter(
|
|
1675
|
+
<T>(test: T|undefined): test is T => !!test).map(reference =>
|
|
1676
|
+
IsCellAddress(reference) ? new Area(reference) : new Area(reference.start, reference.end));
|
|
1665
1677
|
|
|
1666
|
-
|
|
1667
|
-
if (external_editor.dependencies) {
|
|
1678
|
+
if (config.nodes?.length) {
|
|
1668
1679
|
|
|
1669
|
-
|
|
1680
|
+
if (!this.external_editor) {
|
|
1681
|
+
const editor = new ExternalEditor(this.model, this.view);
|
|
1682
|
+
this.external_editor = editor;
|
|
1670
1683
|
|
|
1671
|
-
|
|
1672
|
-
|
|
1673
|
-
|
|
1684
|
+
// should this persist, or should we only subscribe when we're active? (...)
|
|
1685
|
+
// in theory, at least, it won't send any events unless something changes
|
|
1686
|
+
|
|
1687
|
+
editor.Subscribe(event => this.HighlightDependencies(editor.dependencies));
|
|
1688
|
+
}
|
|
1689
|
+
|
|
1690
|
+
this.external_editor.AttachNodes(config.nodes, config.assume_formula ?? true);
|
|
1691
|
+
|
|
1674
1692
|
}
|
|
1693
|
+
else {
|
|
1694
|
+
if (this.external_editor) {
|
|
1695
|
+
this.external_editor.Reset();
|
|
1696
|
+
}
|
|
1697
|
+
}
|
|
1698
|
+
|
|
1699
|
+
if (config.dependencies) {
|
|
1700
|
+
this.HighlightDependencies(areas);
|
|
1701
|
+
}
|
|
1702
|
+
|
|
1675
1703
|
}
|
|
1676
1704
|
else {
|
|
1705
|
+
|
|
1706
|
+
if (this.external_editor) {
|
|
1707
|
+
this.external_editor.Reset();
|
|
1708
|
+
}
|
|
1709
|
+
|
|
1677
1710
|
this.ClearAdditionalSelections();
|
|
1678
1711
|
this.RenderSelections(true);
|
|
1679
1712
|
}
|
|
@@ -1687,15 +1720,16 @@ export class Grid extends GridBase {
|
|
|
1687
1720
|
this.RenderSelections();
|
|
1688
1721
|
}
|
|
1689
1722
|
|
|
1690
|
-
|
|
1723
|
+
/* *
|
|
1691
1724
|
* FIXME: who uses this? anyone?
|
|
1692
|
-
|
|
1725
|
+
* /
|
|
1693
1726
|
public GetNumberFormat(address: ICellAddress): string|undefined {
|
|
1694
1727
|
const style = this.active_sheet.CellStyleData(address);
|
|
1695
1728
|
if (style && style.number_format) {
|
|
1696
1729
|
return NumberFormatCache.Get(style.number_format).toString();
|
|
1697
1730
|
}
|
|
1698
1731
|
}
|
|
1732
|
+
*/
|
|
1699
1733
|
|
|
1700
1734
|
/**
|
|
1701
1735
|
* I can't figure out a way in typescript to overload the GetRange function
|
|
@@ -1953,7 +1987,7 @@ export class Grid extends GridBase {
|
|
|
1953
1987
|
}
|
|
1954
1988
|
|
|
1955
1989
|
/** repaint after an external event (calculation) */
|
|
1956
|
-
public Update(force = false, area?:
|
|
1990
|
+
public Update(force = false, area?: IArea|IArea[]): void {
|
|
1957
1991
|
this.DelayedRender(force, area);
|
|
1958
1992
|
}
|
|
1959
1993
|
|
|
@@ -2534,8 +2568,8 @@ export class Grid extends GridBase {
|
|
|
2534
2568
|
|
|
2535
2569
|
this.formula_bar = new FormulaBar(
|
|
2536
2570
|
grid_container,
|
|
2537
|
-
this.parser,
|
|
2538
|
-
this.theme,
|
|
2571
|
+
// this.parser,
|
|
2572
|
+
// this.theme,
|
|
2539
2573
|
this.model,
|
|
2540
2574
|
this.view,
|
|
2541
2575
|
this.options, autocomplete);
|
|
@@ -2711,13 +2745,12 @@ export class Grid extends GridBase {
|
|
|
2711
2745
|
|
|
2712
2746
|
this.overlay_editor = new OverlayEditor(
|
|
2713
2747
|
this.container,
|
|
2714
|
-
this.parser,
|
|
2715
2748
|
this.theme,
|
|
2716
2749
|
this.model,
|
|
2717
2750
|
this.view,
|
|
2718
2751
|
autocomplete);
|
|
2719
2752
|
|
|
2720
|
-
this.overlay_editor.
|
|
2753
|
+
this.overlay_editor.UpdateScale(this.layout.scale);
|
|
2721
2754
|
this.overlay_editor.autocomplete_matcher = this.autocomplete_matcher;
|
|
2722
2755
|
|
|
2723
2756
|
this.overlay_editor.Subscribe(event => {
|
|
@@ -2745,7 +2778,11 @@ export class Grid extends GridBase {
|
|
|
2745
2778
|
break;
|
|
2746
2779
|
|
|
2747
2780
|
case 'end-selection':
|
|
2781
|
+
case 'reset-selection':
|
|
2748
2782
|
this.ClearSelection(this.active_selection);
|
|
2783
|
+
if (this.overlay_editor?.target_address?.sheet_id && this.active_sheet.id !== this.overlay_editor.target_address.sheet_id) {
|
|
2784
|
+
this.ActivateSheetID(this.overlay_editor.target_address.sheet_id);
|
|
2785
|
+
}
|
|
2749
2786
|
this.DelayedRender();
|
|
2750
2787
|
break;
|
|
2751
2788
|
|
|
@@ -2756,9 +2793,7 @@ export class Grid extends GridBase {
|
|
|
2756
2793
|
|
|
2757
2794
|
}
|
|
2758
2795
|
|
|
2759
|
-
|
|
2760
|
-
|
|
2761
|
-
private DelayedRender(force = false, area?: Area, full_tile = false) {
|
|
2796
|
+
private DelayedRender(force = false, area?: IArea|IArea[], full_tile = false) {
|
|
2762
2797
|
|
|
2763
2798
|
// if area is passed, set dirty before calling repaint
|
|
2764
2799
|
|
|
@@ -3732,7 +3767,7 @@ export class Grid extends GridBase {
|
|
|
3732
3767
|
this.ClearAdditionalSelections();
|
|
3733
3768
|
}
|
|
3734
3769
|
|
|
3735
|
-
if (!selecting_argument || !this.formula_bar?.selecting) {
|
|
3770
|
+
if (!selecting_argument || (!this.formula_bar?.selecting && !this.external_editor?.selecting)) {
|
|
3736
3771
|
|
|
3737
3772
|
// not sure why this breaks the formula bar handler
|
|
3738
3773
|
|
|
@@ -4021,11 +4056,19 @@ export class Grid extends GridBase {
|
|
|
4021
4056
|
if (this.overlay_editor?.editing) {
|
|
4022
4057
|
// ...
|
|
4023
4058
|
}
|
|
4024
|
-
else if (this.
|
|
4025
|
-
|
|
4026
|
-
|
|
4027
|
-
|
|
4028
|
-
|
|
4059
|
+
else if (this.external_editor_config) {
|
|
4060
|
+
|
|
4061
|
+
// there are two possible cases: either an update function
|
|
4062
|
+
// or a full-on editor. we should probably test for both?
|
|
4063
|
+
|
|
4064
|
+
if (this.external_editor?.active) {
|
|
4065
|
+
this.external_editor.FocusEditor();
|
|
4066
|
+
}
|
|
4067
|
+
if (this.external_editor_config.update) {
|
|
4068
|
+
// not necessary?
|
|
4069
|
+
// console.info('call update?');
|
|
4070
|
+
}
|
|
4071
|
+
|
|
4029
4072
|
}
|
|
4030
4073
|
else if (this.formula_bar) {
|
|
4031
4074
|
this.formula_bar.FocusEditor();
|
|
@@ -4289,7 +4332,7 @@ export class Grid extends GridBase {
|
|
|
4289
4332
|
label = Area.CellAddressToLabel(data.merge_area.start);
|
|
4290
4333
|
}
|
|
4291
4334
|
|
|
4292
|
-
if (this.
|
|
4335
|
+
if (this.external_editor_config || this.active_sheet.id !== this.editing_cell.sheet_id) {
|
|
4293
4336
|
const name = this.active_sheet.name;
|
|
4294
4337
|
|
|
4295
4338
|
if (QuotedSheetNameRegex.test(name)) {
|
|
@@ -4311,9 +4354,15 @@ export class Grid extends GridBase {
|
|
|
4311
4354
|
else if (this.formula_bar && this.formula_bar.selecting) {
|
|
4312
4355
|
this.formula_bar.InsertReference(label, 0);
|
|
4313
4356
|
}
|
|
4314
|
-
else if (this.
|
|
4315
|
-
|
|
4316
|
-
|
|
4357
|
+
else if (this.external_editor_config) {
|
|
4358
|
+
|
|
4359
|
+
if (this.external_editor?.active) {
|
|
4360
|
+
this.external_editor.FocusEditor();
|
|
4361
|
+
this.external_editor.InsertReference(label, 0);
|
|
4362
|
+
}
|
|
4363
|
+
|
|
4364
|
+
if (this.external_editor_config.update) {
|
|
4365
|
+
const result = this.external_editor_config.update.call(0, label);
|
|
4317
4366
|
if (result && Array.isArray(result)) {
|
|
4318
4367
|
this.HighlightDependencies(
|
|
4319
4368
|
result.filter(<T>(entry: T|undefined): entry is T => !!entry).map(reference =>
|
|
@@ -4321,12 +4370,14 @@ export class Grid extends GridBase {
|
|
|
4321
4370
|
}
|
|
4322
4371
|
}
|
|
4323
4372
|
}
|
|
4373
|
+
/*
|
|
4324
4374
|
else if (this.select_argument) {
|
|
4325
4375
|
this.grid_events.Publish({
|
|
4326
4376
|
type: 'alternate-selection',
|
|
4327
4377
|
selection: this.active_selection,
|
|
4328
4378
|
});
|
|
4329
4379
|
}
|
|
4380
|
+
*/
|
|
4330
4381
|
}
|
|
4331
4382
|
|
|
4332
4383
|
/**
|
|
@@ -4335,11 +4386,11 @@ export class Grid extends GridBase {
|
|
|
4335
4386
|
*
|
|
4336
4387
|
* FIXME: why is this not an accessor?
|
|
4337
4388
|
*/
|
|
4338
|
-
private SelectingArgument() {
|
|
4389
|
+
private SelectingArgument(): boolean {
|
|
4339
4390
|
return (this.overlay_editor?.editing && this.overlay_editor?.selecting)
|
|
4340
4391
|
|| (this.formula_bar && this.formula_bar.selecting)
|
|
4341
|
-
|| (this.
|
|
4342
|
-
|| (this.select_argument);
|
|
4392
|
+
|| (!!this.external_editor_config);
|
|
4393
|
+
// || (this.select_argument);
|
|
4343
4394
|
}
|
|
4344
4395
|
|
|
4345
4396
|
/**
|
|
@@ -4371,16 +4422,16 @@ export class Grid extends GridBase {
|
|
|
4371
4422
|
editor_open = true;
|
|
4372
4423
|
const result = this.overlay_editor.HandleKeyDown(event);
|
|
4373
4424
|
switch (result) {
|
|
4374
|
-
case
|
|
4425
|
+
case 'handled':
|
|
4375
4426
|
return;
|
|
4376
4427
|
|
|
4377
|
-
case
|
|
4428
|
+
case 'discard':
|
|
4378
4429
|
this.editing_state = EditingState.NotEditing;
|
|
4379
4430
|
this.DismissEditor();
|
|
4380
4431
|
this.DelayedRender();
|
|
4381
4432
|
return;
|
|
4382
4433
|
|
|
4383
|
-
case
|
|
4434
|
+
case 'commit':
|
|
4384
4435
|
|
|
4385
4436
|
// FIXME: unify this (to the extent possible) w/ the other editor
|
|
4386
4437
|
|
|
@@ -40,7 +40,7 @@ import type { DataModel, MacroFunction, SerializedModel, SerializedNamedExpressi
|
|
|
40
40
|
import type { Parser, UnitAddress} from 'treb-parser';
|
|
41
41
|
import { type ExpressionUnit, IllegalSheetNameRegex, ParseCSV, ArgumentSeparatorType, DecimalMarkType } from 'treb-parser';
|
|
42
42
|
import { Area, IsCellAddress, ValidationType, ValueType, DefaultTableSortOptions } from 'treb-base-types';
|
|
43
|
-
import type { ICellAddress, IArea, Cell, CellValue , Style, CellStyle, Table, TableSortOptions, TableTheme, Complex } from 'treb-base-types';
|
|
43
|
+
import type { ICellAddress, IArea, Cell, CellValue , Style, CellStyle, Table, TableSortOptions, TableTheme, Complex, PatchOptions as PatchAreaOptions } from 'treb-base-types';
|
|
44
44
|
import { Sheet } from './sheet';
|
|
45
45
|
import type { FunctionDescriptor} from '../editors/autocomplete_matcher';
|
|
46
46
|
import { AutocompleteMatcher, DescriptorType } from '../editors/autocomplete_matcher';
|
|
@@ -61,6 +61,11 @@ import type { UpdateFlags } from './update_flags';
|
|
|
61
61
|
import type { FreezePane, LegacySerializedSheet } from './sheet_types';
|
|
62
62
|
import type { Annotation } from './annotation';
|
|
63
63
|
import type { ClipboardCellData } from './clipboard_data';
|
|
64
|
+
import type { ConditionalFormat } from './conditional_format';
|
|
65
|
+
|
|
66
|
+
interface PatchOptions extends PatchAreaOptions {
|
|
67
|
+
sheet: Sheet;
|
|
68
|
+
}
|
|
64
69
|
|
|
65
70
|
export class GridBase {
|
|
66
71
|
|
|
@@ -132,7 +137,6 @@ export class GridBase {
|
|
|
132
137
|
|
|
133
138
|
constructor(
|
|
134
139
|
options: GridOptions = {},
|
|
135
|
-
parser: Parser,
|
|
136
140
|
model: DataModel) {
|
|
137
141
|
|
|
138
142
|
this.model = model;
|
|
@@ -144,7 +148,7 @@ export class GridBase {
|
|
|
144
148
|
|
|
145
149
|
// shared parser
|
|
146
150
|
|
|
147
|
-
this.parser = parser;
|
|
151
|
+
this.parser = model.parser;
|
|
148
152
|
|
|
149
153
|
// apply default options, meaning that you need to explicitly set/unset
|
|
150
154
|
// in order to change behavior. FIXME: this is ok for flat structure, but
|
|
@@ -2147,6 +2151,29 @@ export class GridBase {
|
|
|
2147
2151
|
|
|
2148
2152
|
}
|
|
2149
2153
|
|
|
2154
|
+
protected PatchExpressionSheetName(expression: string, old_name: string, name: string): string|undefined {
|
|
2155
|
+
|
|
2156
|
+
let modified = false;
|
|
2157
|
+
const parsed = this.parser.Parse(expression || '');
|
|
2158
|
+
if (parsed.expression) {
|
|
2159
|
+
this.parser.Walk(parsed.expression, (element: ExpressionUnit) => {
|
|
2160
|
+
if (element.type === 'address') {
|
|
2161
|
+
if (element.sheet && element.sheet.toLowerCase() === old_name) {
|
|
2162
|
+
element.sheet = name;
|
|
2163
|
+
modified = true;
|
|
2164
|
+
}
|
|
2165
|
+
}
|
|
2166
|
+
return true; // continue walk
|
|
2167
|
+
});
|
|
2168
|
+
if (modified) {
|
|
2169
|
+
return '=' + this.parser.Render(parsed.expression, { missing: '' });
|
|
2170
|
+
}
|
|
2171
|
+
}
|
|
2172
|
+
|
|
2173
|
+
return undefined;
|
|
2174
|
+
|
|
2175
|
+
}
|
|
2176
|
+
|
|
2150
2177
|
/**
|
|
2151
2178
|
* splitting this logic into a new function so we can reuse it
|
|
2152
2179
|
* for invalidating broken references. generally we'll call this
|
|
@@ -2166,45 +2193,39 @@ export class GridBase {
|
|
|
2166
2193
|
// cells
|
|
2167
2194
|
sheet.cells.IterateAll((cell: Cell) => {
|
|
2168
2195
|
if (cell.ValueIsFormula()) {
|
|
2169
|
-
|
|
2170
|
-
|
|
2171
|
-
|
|
2172
|
-
|
|
2173
|
-
|
|
2174
|
-
|
|
2175
|
-
|
|
2176
|
-
|
|
2196
|
+
const updated = this.PatchExpressionSheetName(cell.value||'', old_name, name);
|
|
2197
|
+
if (updated) {
|
|
2198
|
+
cell.value = updated;
|
|
2199
|
+
changes++;
|
|
2200
|
+
}
|
|
2201
|
+
}
|
|
2202
|
+
});
|
|
2203
|
+
|
|
2204
|
+
// conditionals
|
|
2205
|
+
if (sheet.conditional_formats?.length) {
|
|
2206
|
+
for (const format of sheet.conditional_formats) {
|
|
2207
|
+
switch (format.type) {
|
|
2208
|
+
case 'cell-match':
|
|
2209
|
+
case 'expression':
|
|
2210
|
+
{
|
|
2211
|
+
const updated = this.PatchExpressionSheetName(format.expression, old_name, name);
|
|
2212
|
+
if (updated) {
|
|
2213
|
+
format.expression = updated;
|
|
2214
|
+
changes++;
|
|
2177
2215
|
}
|
|
2178
2216
|
}
|
|
2179
|
-
|
|
2180
|
-
});
|
|
2181
|
-
if (modified) {
|
|
2182
|
-
cell.value = '=' + this.parser.Render(parsed.expression, { missing: '' });
|
|
2183
|
-
changes++;
|
|
2184
|
-
}
|
|
2217
|
+
break;
|
|
2185
2218
|
}
|
|
2186
2219
|
}
|
|
2187
|
-
}
|
|
2220
|
+
}
|
|
2188
2221
|
|
|
2189
2222
|
// annotations
|
|
2190
2223
|
for (const annotation of sheet.annotations) {
|
|
2191
2224
|
if (annotation.data.formula) {
|
|
2192
|
-
|
|
2193
|
-
|
|
2194
|
-
|
|
2195
|
-
|
|
2196
|
-
if (element.type === 'address') {
|
|
2197
|
-
if (element.sheet && element.sheet.toLowerCase() === old_name) {
|
|
2198
|
-
element.sheet = name;
|
|
2199
|
-
modified = true;
|
|
2200
|
-
}
|
|
2201
|
-
}
|
|
2202
|
-
return true; // continue walk
|
|
2203
|
-
});
|
|
2204
|
-
if (modified) {
|
|
2205
|
-
annotation.data.formula = '=' + this.parser.Render(parsed.expression, { missing: '' });
|
|
2206
|
-
changes++;
|
|
2207
|
-
}
|
|
2225
|
+
const updated = this.PatchExpressionSheetName(annotation.data.formula, old_name, name);
|
|
2226
|
+
if (updated) {
|
|
2227
|
+
annotation.data.formula = updated;
|
|
2228
|
+
changes++;
|
|
2208
2229
|
}
|
|
2209
2230
|
}
|
|
2210
2231
|
}
|
|
@@ -2635,7 +2656,121 @@ export class GridBase {
|
|
|
2635
2656
|
|
|
2636
2657
|
}
|
|
2637
2658
|
|
|
2638
|
-
|
|
2659
|
+
/**
|
|
2660
|
+
* patch an expression for insert/delete row/column operations.
|
|
2661
|
+
* FIXME: should move, maybe to parser? (...)
|
|
2662
|
+
*
|
|
2663
|
+
* NOTE: did I write this twice? we only need one. check which one is better.
|
|
2664
|
+
* @see PatchFormulasInternal
|
|
2665
|
+
*
|
|
2666
|
+
* @returns the updated expression, or `undefined` if no changes were made.
|
|
2667
|
+
*/
|
|
2668
|
+
protected PatchExpression(expression: string, options: PatchOptions) {
|
|
2669
|
+
|
|
2670
|
+
let count = 0;
|
|
2671
|
+
const parse_result = this.parser.Parse(expression);
|
|
2672
|
+
if (parse_result.expression) {
|
|
2673
|
+
|
|
2674
|
+
this.parser.Walk(parse_result.expression, unit => {
|
|
2675
|
+
if (unit.type === 'range') {
|
|
2676
|
+
if (!unit.start.sheet || (unit.start.sheet.toLowerCase() === options.sheet.name.toLowerCase())) {
|
|
2677
|
+
const updated = Area.PatchArea(unit, options);
|
|
2678
|
+
if (updated) {
|
|
2679
|
+
unit.start.row = updated.start.row;
|
|
2680
|
+
unit.start.column = updated.start.column;
|
|
2681
|
+
unit.end.row = updated.end.row;
|
|
2682
|
+
unit.end.column = updated.end.column;
|
|
2683
|
+
}
|
|
2684
|
+
else {
|
|
2685
|
+
|
|
2686
|
+
// FIXME: maybe have options for this? we don't really have a
|
|
2687
|
+
// good way to replace nodes atm
|
|
2688
|
+
unit.start.row = unit.end.row = unit.start.column = unit.end.column = -1;
|
|
2689
|
+
|
|
2690
|
+
}
|
|
2691
|
+
}
|
|
2692
|
+
count++;
|
|
2693
|
+
return false;
|
|
2694
|
+
}
|
|
2695
|
+
else if (unit.type === 'address') {
|
|
2696
|
+
const updated = Area.PatchArea({start: unit, end: unit}, options);
|
|
2697
|
+
if (updated) {
|
|
2698
|
+
unit.row = updated.start.row;
|
|
2699
|
+
unit.column = updated.start.column;
|
|
2700
|
+
}
|
|
2701
|
+
else {
|
|
2702
|
+
|
|
2703
|
+
// see above
|
|
2704
|
+
unit.row = unit.column = -1;
|
|
2705
|
+
}
|
|
2706
|
+
count++;
|
|
2707
|
+
return false;
|
|
2708
|
+
}
|
|
2709
|
+
return true;
|
|
2710
|
+
});
|
|
2711
|
+
|
|
2712
|
+
if (count) {
|
|
2713
|
+
const rendered = this.parser.Render(parse_result.expression, {
|
|
2714
|
+
missing: '',
|
|
2715
|
+
});
|
|
2716
|
+
// console.info("FROM", expression, "TO", rendered);
|
|
2717
|
+
return rendered;
|
|
2718
|
+
}
|
|
2719
|
+
|
|
2720
|
+
}
|
|
2721
|
+
|
|
2722
|
+
return undefined;
|
|
2723
|
+
|
|
2724
|
+
}
|
|
2725
|
+
|
|
2726
|
+
/**
|
|
2727
|
+
* patch sheet conditionals for insert/delete row/column operations.
|
|
2728
|
+
* some of them may be deleted.
|
|
2729
|
+
*/
|
|
2730
|
+
protected PatchConditionals(options: PatchOptions) {
|
|
2731
|
+
|
|
2732
|
+
if (options.sheet.conditional_formats?.length) {
|
|
2733
|
+
|
|
2734
|
+
const delete_list: Set<ConditionalFormat> = new Set();
|
|
2735
|
+
for (const format of options.sheet.conditional_formats) {
|
|
2736
|
+
|
|
2737
|
+
// first adjust the format area
|
|
2738
|
+
|
|
2739
|
+
const updated = Area.PatchArea(format.area, options);
|
|
2740
|
+
if (updated) {
|
|
2741
|
+
format.area = JSON.parse(JSON.stringify(updated));
|
|
2742
|
+
}
|
|
2743
|
+
else {
|
|
2744
|
+
delete_list.add(format);
|
|
2745
|
+
continue; // don't bother with formula
|
|
2746
|
+
}
|
|
2747
|
+
|
|
2748
|
+
// next update the formula, if necessary. what do we do if the
|
|
2749
|
+
// area has disappeared? should be a #REF error, not sure we
|
|
2750
|
+
// can encode that properly
|
|
2751
|
+
|
|
2752
|
+
switch (format.type) {
|
|
2753
|
+
case 'expression':
|
|
2754
|
+
case 'cell-match':
|
|
2755
|
+
{
|
|
2756
|
+
const updated = this.PatchExpression(format.expression, options);
|
|
2757
|
+
|
|
2758
|
+
if (updated) {
|
|
2759
|
+
format.expression = updated;
|
|
2760
|
+
}
|
|
2761
|
+
}
|
|
2762
|
+
break;
|
|
2763
|
+
}
|
|
2764
|
+
|
|
2765
|
+
}
|
|
2766
|
+
|
|
2767
|
+
if (delete_list.size) {
|
|
2768
|
+
options.sheet.conditional_formats = options.sheet.conditional_formats.filter(test => !delete_list.has(test));
|
|
2769
|
+
}
|
|
2770
|
+
|
|
2771
|
+
}
|
|
2772
|
+
|
|
2773
|
+
}
|
|
2639
2774
|
|
|
2640
2775
|
/**
|
|
2641
2776
|
* FIXME: should be API method
|
|
@@ -2665,6 +2800,15 @@ export class GridBase {
|
|
|
2665
2800
|
return { error: true };
|
|
2666
2801
|
}
|
|
2667
2802
|
|
|
2803
|
+
// conditionals
|
|
2804
|
+
this.PatchConditionals({
|
|
2805
|
+
sheet: target_sheet,
|
|
2806
|
+
before_column: 0,
|
|
2807
|
+
column_count: 0,
|
|
2808
|
+
before_row: command.before_row,
|
|
2809
|
+
row_count: command.count
|
|
2810
|
+
});
|
|
2811
|
+
|
|
2668
2812
|
// see InsertColumnsInternal re: tables. rows are less complicated,
|
|
2669
2813
|
// except that if you delete the header row we want to remove the
|
|
2670
2814
|
// table entirely.
|
|
@@ -2979,6 +3123,14 @@ export class GridBase {
|
|
|
2979
3123
|
return { error: true };
|
|
2980
3124
|
}
|
|
2981
3125
|
|
|
3126
|
+
// conditionals
|
|
3127
|
+
this.PatchConditionals({
|
|
3128
|
+
sheet: target_sheet,
|
|
3129
|
+
before_column: command.before_column,
|
|
3130
|
+
column_count: command.count,
|
|
3131
|
+
before_row: 0,
|
|
3132
|
+
row_count: 0 });
|
|
3133
|
+
|
|
2982
3134
|
// patch tables. we removed this from the sheet routine entirely,
|
|
2983
3135
|
// we need to rebuild any affected tables now.
|
|
2984
3136
|
|
|
@@ -137,7 +137,7 @@ export class ScaleControl extends EventSource<ScaleEvent> {
|
|
|
137
137
|
|
|
138
138
|
});
|
|
139
139
|
|
|
140
|
-
this.slider = DOM.Create
|
|
140
|
+
this.slider = DOM.Create('input', undefined, popup, undefined, {
|
|
141
141
|
type: 'range',
|
|
142
142
|
min: '50',
|
|
143
143
|
max: '200',
|