@trebco/treb 27.7.6 → 27.11.1
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 +28 -26
- package/notes/conditional-fomratring.md +29 -0
- package/package.json +1 -1
- package/treb-base-types/src/area.ts +181 -0
- package/{treb-grid/src/util/dom_utilities.ts → treb-base-types/src/dom-utilities.ts} +29 -20
- 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 +3 -0
- package/treb-base-types/src/theme.ts +3 -5
- package/treb-calculator/src/calculator.ts +190 -28
- 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/custom-element/global.d.ts +3 -1
- package/treb-embed/src/custom-element/spreadsheet-constructor.ts +13 -19
- package/treb-embed/src/embedded-spreadsheet.ts +378 -132
- package/treb-embed/src/spinner.ts +3 -3
- package/treb-embed/style/layout.scss +1 -1
- package/treb-export/src/drawing2/chart2.ts +11 -2
- package/treb-export/src/export-worker/export-worker.ts +0 -13
- package/treb-export/src/export2.ts +197 -2
- package/treb-export/src/import2.ts +169 -4
- package/treb-export/src/workbook-style2.ts +59 -10
- package/treb-export/src/workbook2.ts +10 -1
- package/treb-grid/src/editors/autocomplete.ts +28 -24
- package/treb-grid/src/editors/editor.ts +3 -4
- package/treb-grid/src/editors/formula_bar.ts +1 -1
- package/treb-grid/src/index.ts +2 -1
- package/treb-grid/src/layout/base_layout.ts +34 -31
- package/treb-grid/src/layout/grid_layout.ts +17 -28
- package/treb-grid/src/render/selection-renderer.ts +2 -3
- package/treb-grid/src/render/svg_header_overlay.ts +4 -11
- package/treb-grid/src/render/svg_selection_block.ts +27 -34
- package/treb-grid/src/render/tile_renderer.ts +8 -6
- package/treb-grid/src/types/conditional_format.ts +168 -0
- package/treb-grid/src/types/grid.ts +37 -47
- package/treb-grid/src/types/grid_base.ts +188 -33
- package/treb-grid/src/types/scale-control.ts +2 -2
- package/treb-grid/src/types/sheet.ts +332 -28
- package/treb-grid/src/types/sheet_types.ts +4 -0
- package/treb-grid/src/types/tab_bar.ts +4 -8
- package/treb-utils/src/index.ts +0 -1
- package/treb-utils/src/resizable.ts +26 -27
- package/treb-utils/src/template.ts +0 -70
- /package/{README-shadow-DOM.md → notes/shadow-DOM.md} +0 -0
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
|
|
2
|
+
import type { CellStyle, EvaluateOptions, IArea, Color, Gradient, GradientStop, UnionValue } from 'treb-base-types';
|
|
3
|
+
|
|
4
|
+
interface VertexPlaceholder {
|
|
5
|
+
result: UnionValue;
|
|
6
|
+
updated: boolean;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export interface CondifionalFormatExpressionOptions {
|
|
10
|
+
style: CellStyle;
|
|
11
|
+
expression: string;
|
|
12
|
+
options?: EvaluateOptions;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* conditional format predicated on an expression. if the expression
|
|
17
|
+
* evaluates to true, we apply the style. otherwise no.
|
|
18
|
+
*/
|
|
19
|
+
export interface ConditionalFormatExpression extends CondifionalFormatExpressionOptions {
|
|
20
|
+
|
|
21
|
+
type: 'expression';
|
|
22
|
+
area: IArea;
|
|
23
|
+
|
|
24
|
+
/** @internal */
|
|
25
|
+
internal?: {
|
|
26
|
+
vertex?: VertexPlaceholder;
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export interface ConditionalFormatGradientOptions {
|
|
32
|
+
|
|
33
|
+
/** property defaults to fill */
|
|
34
|
+
property?: 'fill'|'text';
|
|
35
|
+
|
|
36
|
+
/** defaults to HSL */
|
|
37
|
+
color_space?: 'HSL'|'RGB';
|
|
38
|
+
|
|
39
|
+
/** gradient stops, required */
|
|
40
|
+
stops: Array<{ value: number, color: Color }>;
|
|
41
|
+
|
|
42
|
+
/** min and max are optional. if not provided, we use the min/max of the range of data. */
|
|
43
|
+
min?: number;
|
|
44
|
+
|
|
45
|
+
/** min and max are optional. if not provided, we use the min/max of the range of data. */
|
|
46
|
+
max?: number;
|
|
47
|
+
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
export const StandardGradientsList = {
|
|
52
|
+
'red-green': {
|
|
53
|
+
color_space: 'RGB',
|
|
54
|
+
stops: [
|
|
55
|
+
{ value: 0, color: { theme: 5, tint: .5 }},
|
|
56
|
+
{ value: 1, color: { theme: 9, tint: .5 }},
|
|
57
|
+
] as GradientStop[],
|
|
58
|
+
},
|
|
59
|
+
'red-yellow-green': {
|
|
60
|
+
color_space: 'RGB',
|
|
61
|
+
stops: [
|
|
62
|
+
{ value: 0, color: { theme: 5, tint: .5 }},
|
|
63
|
+
{ value: 0.5, color: { theme: 7, tint: .5 }},
|
|
64
|
+
{ value: 1, color: { theme: 9, tint: .5 }},
|
|
65
|
+
] as GradientStop[],
|
|
66
|
+
},
|
|
67
|
+
'green-red': {
|
|
68
|
+
color_space: 'RGB',
|
|
69
|
+
stops: [
|
|
70
|
+
{ value: 0, color: { theme: 9, tint: .5 }},
|
|
71
|
+
{ value: 1, color: { theme: 5, tint: .5 }},
|
|
72
|
+
] as GradientStop[],
|
|
73
|
+
},
|
|
74
|
+
'green-yellow-red': {
|
|
75
|
+
color_space: 'RGB',
|
|
76
|
+
stops: [
|
|
77
|
+
{ value: 0, color: { theme: 9, tint: .5 }},
|
|
78
|
+
{ value: 0.5, color: { theme: 7, tint: .5 }},
|
|
79
|
+
{ value: 1, color: { theme: 5, tint: .5 }},
|
|
80
|
+
] as GradientStop[],
|
|
81
|
+
},
|
|
82
|
+
} as const;
|
|
83
|
+
export type StandardGradient = keyof typeof StandardGradientsList;
|
|
84
|
+
|
|
85
|
+
export interface ConditionalFormatGradient extends ConditionalFormatGradientOptions {
|
|
86
|
+
type: 'gradient';
|
|
87
|
+
area: IArea;
|
|
88
|
+
|
|
89
|
+
/** @internal */
|
|
90
|
+
internal?: {
|
|
91
|
+
gradient?: Gradient;
|
|
92
|
+
vertex?: VertexPlaceholder;
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
export interface ConditionalFormatCellMatchOptions {
|
|
97
|
+
style: CellStyle;
|
|
98
|
+
expression: string;
|
|
99
|
+
options?: EvaluateOptions;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
export interface ConditionalFormatCellMatch extends ConditionalFormatCellMatchOptions {
|
|
103
|
+
type: 'cell-match';
|
|
104
|
+
area: IArea;
|
|
105
|
+
|
|
106
|
+
/** @internal */
|
|
107
|
+
internal?: {
|
|
108
|
+
vertex?: VertexPlaceholder;
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
export interface ConditionalFormatCellMatchOptions {
|
|
113
|
+
style: CellStyle;
|
|
114
|
+
expression: string;
|
|
115
|
+
options?: EvaluateOptions;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
export interface ConditionalFormatCellMatch extends ConditionalFormatCellMatchOptions {
|
|
119
|
+
type: 'cell-match';
|
|
120
|
+
area: IArea;
|
|
121
|
+
|
|
122
|
+
/** @internal */
|
|
123
|
+
internal?: {
|
|
124
|
+
vertex?: VertexPlaceholder;
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
export interface ConditionalFormatDuplicateValuesOptions {
|
|
129
|
+
style: CellStyle;
|
|
130
|
+
|
|
131
|
+
/** true to highlight unique cells, false to highlight duplicates. defaults to false. */
|
|
132
|
+
unique?: boolean;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
export interface ConditionalFormatDuplicateValues extends ConditionalFormatDuplicateValuesOptions {
|
|
136
|
+
|
|
137
|
+
type: 'duplicate-values';
|
|
138
|
+
area: IArea;
|
|
139
|
+
|
|
140
|
+
/** @internal */
|
|
141
|
+
internal?: {
|
|
142
|
+
vertex?: VertexPlaceholder;
|
|
143
|
+
};
|
|
144
|
+
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* union, plus we're adding a state used to track application.
|
|
149
|
+
* that state is serialized if it's true.
|
|
150
|
+
* we also add an internal field that will be type-specific, and not serialized.
|
|
151
|
+
*
|
|
152
|
+
* ...everybody has a vertex now, we could standardize it
|
|
153
|
+
*
|
|
154
|
+
*/
|
|
155
|
+
export type ConditionalFormat = { internal?: unknown } & (
|
|
156
|
+
ConditionalFormatDuplicateValues |
|
|
157
|
+
ConditionalFormatExpression |
|
|
158
|
+
ConditionalFormatCellMatch |
|
|
159
|
+
ConditionalFormatGradient
|
|
160
|
+
);
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* the list of formats, in reverse order of precedence. as a starting
|
|
164
|
+
* point we're using the naive approach, just applying everything in
|
|
165
|
+
* order. that may change.
|
|
166
|
+
*/
|
|
167
|
+
export type ConditionalFormatList = ConditionalFormat[];
|
|
168
|
+
|
|
@@ -111,7 +111,7 @@ import { CommandKey
|
|
|
111
111
|
|
|
112
112
|
import type { DataModel, SerializedModel } from './data_model';
|
|
113
113
|
|
|
114
|
-
import { DOMUtilities } from '
|
|
114
|
+
import { DOMUtilities } from 'treb-base-types';
|
|
115
115
|
import { GridBase } from './grid_base';
|
|
116
116
|
import type { SetRangeOptions } from './set_range_options';
|
|
117
117
|
import type { ClipboardCellData } from './clipboard_data';
|
|
@@ -262,6 +262,9 @@ export class Grid extends GridBase {
|
|
|
262
262
|
/** new key capture overlay/ICE */
|
|
263
263
|
private overlay_editor?: OverlayEditor;
|
|
264
264
|
|
|
265
|
+
/** moving autocomplete to a class field */
|
|
266
|
+
private autocomplete?: Autocomplete;
|
|
267
|
+
|
|
265
268
|
/** formula bar editor (optional) */
|
|
266
269
|
private formula_bar?: FormulaBar;
|
|
267
270
|
|
|
@@ -568,13 +571,13 @@ export class Grid extends GridBase {
|
|
|
568
571
|
|
|
569
572
|
// FIXME: why is this not in layout? it is layout.
|
|
570
573
|
|
|
571
|
-
view.node =
|
|
574
|
+
view.node = DOMUtilities.Div();
|
|
572
575
|
view.node.dataset.scale = this.layout.scale.toString();
|
|
573
576
|
view.node.style.fontSize = `${10 * this.layout.scale}pt`;
|
|
574
577
|
|
|
575
|
-
view.content_node = DOMUtilities.
|
|
576
|
-
const move_target = DOMUtilities.
|
|
577
|
-
const resize_target = DOMUtilities.
|
|
578
|
+
view.content_node = DOMUtilities.Div('annotation-content', view.node);
|
|
579
|
+
const move_target = DOMUtilities.Div('annotation-move-target', view.node);
|
|
580
|
+
const resize_target = DOMUtilities.Div('annotation-resize-target', view.node);
|
|
578
581
|
|
|
579
582
|
if (view.node) {
|
|
580
583
|
const node = view.node;
|
|
@@ -1275,13 +1278,13 @@ export class Grid extends GridBase {
|
|
|
1275
1278
|
const higher_level_container = view_node.querySelector('.treb-spreadsheet-body') as HTMLElement;
|
|
1276
1279
|
const container = higher_level_container.querySelector('div') as HTMLElement;
|
|
1277
1280
|
|
|
1278
|
-
let autocomplete: Autocomplete | undefined;
|
|
1281
|
+
// let autocomplete: Autocomplete | undefined;
|
|
1279
1282
|
|
|
1280
1283
|
if (this.options.formula_bar) {
|
|
1281
|
-
if (!autocomplete) {
|
|
1282
|
-
autocomplete = new Autocomplete({ theme: this.theme, container });
|
|
1284
|
+
if (!this.autocomplete) {
|
|
1285
|
+
this.autocomplete = new Autocomplete({ theme: this.theme, container });
|
|
1283
1286
|
}
|
|
1284
|
-
this.InitFormulaBar(view_node, autocomplete);
|
|
1287
|
+
this.InitFormulaBar(view_node, this.autocomplete);
|
|
1285
1288
|
}
|
|
1286
1289
|
|
|
1287
1290
|
if (this.options.tab_bar) {
|
|
@@ -1408,10 +1411,10 @@ export class Grid extends GridBase {
|
|
|
1408
1411
|
|
|
1409
1412
|
// Sheet.sheet_events.Subscribe(this.HandleSheetEvent.bind(this));
|
|
1410
1413
|
|
|
1411
|
-
if (!autocomplete) {
|
|
1412
|
-
autocomplete = new Autocomplete({ theme: this.theme, container });
|
|
1414
|
+
if (!this.autocomplete) {
|
|
1415
|
+
this.autocomplete = new Autocomplete({ theme: this.theme, container });
|
|
1413
1416
|
}
|
|
1414
|
-
this.InitOverlayEditor(autocomplete);
|
|
1417
|
+
this.InitOverlayEditor(this.autocomplete);
|
|
1415
1418
|
|
|
1416
1419
|
this.AttachListeners();
|
|
1417
1420
|
|
|
@@ -1720,15 +1723,16 @@ export class Grid extends GridBase {
|
|
|
1720
1723
|
this.RenderSelections();
|
|
1721
1724
|
}
|
|
1722
1725
|
|
|
1723
|
-
|
|
1726
|
+
/* *
|
|
1724
1727
|
* FIXME: who uses this? anyone?
|
|
1725
|
-
|
|
1728
|
+
* /
|
|
1726
1729
|
public GetNumberFormat(address: ICellAddress): string|undefined {
|
|
1727
1730
|
const style = this.active_sheet.CellStyleData(address);
|
|
1728
1731
|
if (style && style.number_format) {
|
|
1729
1732
|
return NumberFormatCache.Get(style.number_format).toString();
|
|
1730
1733
|
}
|
|
1731
1734
|
}
|
|
1735
|
+
*/
|
|
1732
1736
|
|
|
1733
1737
|
/**
|
|
1734
1738
|
* I can't figure out a way in typescript to overload the GetRange function
|
|
@@ -1986,7 +1990,7 @@ export class Grid extends GridBase {
|
|
|
1986
1990
|
}
|
|
1987
1991
|
|
|
1988
1992
|
/** repaint after an external event (calculation) */
|
|
1989
|
-
public Update(force = false, area?:
|
|
1993
|
+
public Update(force = false, area?: IArea|IArea[]): void {
|
|
1990
1994
|
this.DelayedRender(force, area);
|
|
1991
1995
|
}
|
|
1992
1996
|
|
|
@@ -2694,35 +2698,6 @@ export class Grid extends GridBase {
|
|
|
2694
2698
|
|
|
2695
2699
|
this.OverlayKeyDown(event.event);
|
|
2696
2700
|
|
|
2697
|
-
/*
|
|
2698
|
-
let cloned_event: KeyboardEvent;
|
|
2699
|
-
if (UA.trident) {
|
|
2700
|
-
cloned_event = document.createEvent('KeyboardEvent');
|
|
2701
|
-
const modifiers = [];
|
|
2702
|
-
if (event.event.ctrlKey) modifiers.push('Control');
|
|
2703
|
-
if (event.event.altKey) modifiers.push('Alt');
|
|
2704
|
-
if (event.event.shiftKey) modifiers.push('Shift');
|
|
2705
|
-
|
|
2706
|
-
// have to mask type for trident
|
|
2707
|
-
(cloned_event as any).initKeyboardEvent(
|
|
2708
|
-
event.event.type,
|
|
2709
|
-
false,
|
|
2710
|
-
false,
|
|
2711
|
-
event.event.view,
|
|
2712
|
-
event.event.key,
|
|
2713
|
-
event.event.location,
|
|
2714
|
-
modifiers.join(' '),
|
|
2715
|
-
event.event.repeat,
|
|
2716
|
-
Localization.locale);
|
|
2717
|
-
}
|
|
2718
|
-
else {
|
|
2719
|
-
cloned_event = new KeyboardEvent(event.event.type, event.event);
|
|
2720
|
-
}
|
|
2721
|
-
|
|
2722
|
-
if (cloned_event && this.container) {
|
|
2723
|
-
this.container.dispatchEvent(cloned_event);
|
|
2724
|
-
}
|
|
2725
|
-
*/
|
|
2726
2701
|
}
|
|
2727
2702
|
break;
|
|
2728
2703
|
|
|
@@ -2792,9 +2767,7 @@ export class Grid extends GridBase {
|
|
|
2792
2767
|
|
|
2793
2768
|
}
|
|
2794
2769
|
|
|
2795
|
-
|
|
2796
|
-
|
|
2797
|
-
private DelayedRender(force = false, area?: Area, full_tile = false) {
|
|
2770
|
+
private DelayedRender(force = false, area?: IArea|IArea[], full_tile = false) {
|
|
2798
2771
|
|
|
2799
2772
|
// if area is passed, set dirty before calling repaint
|
|
2800
2773
|
|
|
@@ -3764,6 +3737,17 @@ export class Grid extends GridBase {
|
|
|
3764
3737
|
|
|
3765
3738
|
const selecting_argument = this.SelectingArgument();
|
|
3766
3739
|
|
|
3740
|
+
/*
|
|
3741
|
+
let blocking_tooltip = false;
|
|
3742
|
+
if (selecting_argument) {
|
|
3743
|
+
if (this.autocomplete?.tooltip_visible) {
|
|
3744
|
+
this.autocomplete.SetBlock();
|
|
3745
|
+
this.autocomplete.Hide();
|
|
3746
|
+
blocking_tooltip = true;
|
|
3747
|
+
}
|
|
3748
|
+
}
|
|
3749
|
+
*/
|
|
3750
|
+
|
|
3767
3751
|
if (!selecting_argument && this.additional_selections.length) {
|
|
3768
3752
|
this.ClearAdditionalSelections();
|
|
3769
3753
|
}
|
|
@@ -4053,6 +4037,12 @@ export class Grid extends GridBase {
|
|
|
4053
4037
|
// console.info('end');
|
|
4054
4038
|
this.UpdateAddressLabel();
|
|
4055
4039
|
|
|
4040
|
+
/*
|
|
4041
|
+
if (blocking_tooltip) {
|
|
4042
|
+
this.autocomplete?.ResetBlock();
|
|
4043
|
+
}
|
|
4044
|
+
*/
|
|
4045
|
+
|
|
4056
4046
|
if (selecting_argument) {
|
|
4057
4047
|
if (this.overlay_editor?.editing) {
|
|
4058
4048
|
// ...
|
|
@@ -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
|
|
|
@@ -2146,6 +2151,29 @@ export class GridBase {
|
|
|
2146
2151
|
|
|
2147
2152
|
}
|
|
2148
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
|
+
|
|
2149
2177
|
/**
|
|
2150
2178
|
* splitting this logic into a new function so we can reuse it
|
|
2151
2179
|
* for invalidating broken references. generally we'll call this
|
|
@@ -2165,45 +2193,39 @@ export class GridBase {
|
|
|
2165
2193
|
// cells
|
|
2166
2194
|
sheet.cells.IterateAll((cell: Cell) => {
|
|
2167
2195
|
if (cell.ValueIsFormula()) {
|
|
2168
|
-
|
|
2169
|
-
|
|
2170
|
-
|
|
2171
|
-
|
|
2172
|
-
|
|
2173
|
-
|
|
2174
|
-
|
|
2175
|
-
|
|
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++;
|
|
2176
2215
|
}
|
|
2177
2216
|
}
|
|
2178
|
-
|
|
2179
|
-
});
|
|
2180
|
-
if (modified) {
|
|
2181
|
-
cell.value = '=' + this.parser.Render(parsed.expression, { missing: '' });
|
|
2182
|
-
changes++;
|
|
2183
|
-
}
|
|
2217
|
+
break;
|
|
2184
2218
|
}
|
|
2185
2219
|
}
|
|
2186
|
-
}
|
|
2220
|
+
}
|
|
2187
2221
|
|
|
2188
2222
|
// annotations
|
|
2189
2223
|
for (const annotation of sheet.annotations) {
|
|
2190
2224
|
if (annotation.data.formula) {
|
|
2191
|
-
|
|
2192
|
-
|
|
2193
|
-
|
|
2194
|
-
|
|
2195
|
-
if (element.type === 'address') {
|
|
2196
|
-
if (element.sheet && element.sheet.toLowerCase() === old_name) {
|
|
2197
|
-
element.sheet = name;
|
|
2198
|
-
modified = true;
|
|
2199
|
-
}
|
|
2200
|
-
}
|
|
2201
|
-
return true; // continue walk
|
|
2202
|
-
});
|
|
2203
|
-
if (modified) {
|
|
2204
|
-
annotation.data.formula = '=' + this.parser.Render(parsed.expression, { missing: '' });
|
|
2205
|
-
changes++;
|
|
2206
|
-
}
|
|
2225
|
+
const updated = this.PatchExpressionSheetName(annotation.data.formula, old_name, name);
|
|
2226
|
+
if (updated) {
|
|
2227
|
+
annotation.data.formula = updated;
|
|
2228
|
+
changes++;
|
|
2207
2229
|
}
|
|
2208
2230
|
}
|
|
2209
2231
|
}
|
|
@@ -2430,6 +2452,7 @@ export class GridBase {
|
|
|
2430
2452
|
});
|
|
2431
2453
|
if (transformed) {
|
|
2432
2454
|
|
|
2455
|
+
/*
|
|
2433
2456
|
if (!this.flags.warned_r1c1) {
|
|
2434
2457
|
|
|
2435
2458
|
// 1-time warning
|
|
@@ -2437,6 +2460,7 @@ export class GridBase {
|
|
|
2437
2460
|
this.flags.warned_r1c1 = true;
|
|
2438
2461
|
console.warn('NOTE: R1C1 support is experimental. the semantics may change in the future.');
|
|
2439
2462
|
}
|
|
2463
|
+
*/
|
|
2440
2464
|
|
|
2441
2465
|
value = '=' + this.parser.Render(result.expression, { missing: '' });
|
|
2442
2466
|
}
|
|
@@ -2634,7 +2658,121 @@ export class GridBase {
|
|
|
2634
2658
|
|
|
2635
2659
|
}
|
|
2636
2660
|
|
|
2637
|
-
|
|
2661
|
+
/**
|
|
2662
|
+
* patch an expression for insert/delete row/column operations.
|
|
2663
|
+
* FIXME: should move, maybe to parser? (...)
|
|
2664
|
+
*
|
|
2665
|
+
* NOTE: did I write this twice? we only need one. check which one is better.
|
|
2666
|
+
* @see PatchFormulasInternal
|
|
2667
|
+
*
|
|
2668
|
+
* @returns the updated expression, or `undefined` if no changes were made.
|
|
2669
|
+
*/
|
|
2670
|
+
protected PatchExpression(expression: string, options: PatchOptions) {
|
|
2671
|
+
|
|
2672
|
+
let count = 0;
|
|
2673
|
+
const parse_result = this.parser.Parse(expression);
|
|
2674
|
+
if (parse_result.expression) {
|
|
2675
|
+
|
|
2676
|
+
this.parser.Walk(parse_result.expression, unit => {
|
|
2677
|
+
if (unit.type === 'range') {
|
|
2678
|
+
if (!unit.start.sheet || (unit.start.sheet.toLowerCase() === options.sheet.name.toLowerCase())) {
|
|
2679
|
+
const updated = Area.PatchArea(unit, options);
|
|
2680
|
+
if (updated) {
|
|
2681
|
+
unit.start.row = updated.start.row;
|
|
2682
|
+
unit.start.column = updated.start.column;
|
|
2683
|
+
unit.end.row = updated.end.row;
|
|
2684
|
+
unit.end.column = updated.end.column;
|
|
2685
|
+
}
|
|
2686
|
+
else {
|
|
2687
|
+
|
|
2688
|
+
// FIXME: maybe have options for this? we don't really have a
|
|
2689
|
+
// good way to replace nodes atm
|
|
2690
|
+
unit.start.row = unit.end.row = unit.start.column = unit.end.column = -1;
|
|
2691
|
+
|
|
2692
|
+
}
|
|
2693
|
+
}
|
|
2694
|
+
count++;
|
|
2695
|
+
return false;
|
|
2696
|
+
}
|
|
2697
|
+
else if (unit.type === 'address') {
|
|
2698
|
+
const updated = Area.PatchArea({start: unit, end: unit}, options);
|
|
2699
|
+
if (updated) {
|
|
2700
|
+
unit.row = updated.start.row;
|
|
2701
|
+
unit.column = updated.start.column;
|
|
2702
|
+
}
|
|
2703
|
+
else {
|
|
2704
|
+
|
|
2705
|
+
// see above
|
|
2706
|
+
unit.row = unit.column = -1;
|
|
2707
|
+
}
|
|
2708
|
+
count++;
|
|
2709
|
+
return false;
|
|
2710
|
+
}
|
|
2711
|
+
return true;
|
|
2712
|
+
});
|
|
2713
|
+
|
|
2714
|
+
if (count) {
|
|
2715
|
+
const rendered = this.parser.Render(parse_result.expression, {
|
|
2716
|
+
missing: '',
|
|
2717
|
+
});
|
|
2718
|
+
// console.info("FROM", expression, "TO", rendered);
|
|
2719
|
+
return rendered;
|
|
2720
|
+
}
|
|
2721
|
+
|
|
2722
|
+
}
|
|
2723
|
+
|
|
2724
|
+
return undefined;
|
|
2725
|
+
|
|
2726
|
+
}
|
|
2727
|
+
|
|
2728
|
+
/**
|
|
2729
|
+
* patch sheet conditionals for insert/delete row/column operations.
|
|
2730
|
+
* some of them may be deleted.
|
|
2731
|
+
*/
|
|
2732
|
+
protected PatchConditionals(options: PatchOptions) {
|
|
2733
|
+
|
|
2734
|
+
if (options.sheet.conditional_formats?.length) {
|
|
2735
|
+
|
|
2736
|
+
const delete_list: Set<ConditionalFormat> = new Set();
|
|
2737
|
+
for (const format of options.sheet.conditional_formats) {
|
|
2738
|
+
|
|
2739
|
+
// first adjust the format area
|
|
2740
|
+
|
|
2741
|
+
const updated = Area.PatchArea(format.area, options);
|
|
2742
|
+
if (updated) {
|
|
2743
|
+
format.area = JSON.parse(JSON.stringify(updated));
|
|
2744
|
+
}
|
|
2745
|
+
else {
|
|
2746
|
+
delete_list.add(format);
|
|
2747
|
+
continue; // don't bother with formula
|
|
2748
|
+
}
|
|
2749
|
+
|
|
2750
|
+
// next update the formula, if necessary. what do we do if the
|
|
2751
|
+
// area has disappeared? should be a #REF error, not sure we
|
|
2752
|
+
// can encode that properly
|
|
2753
|
+
|
|
2754
|
+
switch (format.type) {
|
|
2755
|
+
case 'expression':
|
|
2756
|
+
case 'cell-match':
|
|
2757
|
+
{
|
|
2758
|
+
const updated = this.PatchExpression(format.expression, options);
|
|
2759
|
+
|
|
2760
|
+
if (updated) {
|
|
2761
|
+
format.expression = updated;
|
|
2762
|
+
}
|
|
2763
|
+
}
|
|
2764
|
+
break;
|
|
2765
|
+
}
|
|
2766
|
+
|
|
2767
|
+
}
|
|
2768
|
+
|
|
2769
|
+
if (delete_list.size) {
|
|
2770
|
+
options.sheet.conditional_formats = options.sheet.conditional_formats.filter(test => !delete_list.has(test));
|
|
2771
|
+
}
|
|
2772
|
+
|
|
2773
|
+
}
|
|
2774
|
+
|
|
2775
|
+
}
|
|
2638
2776
|
|
|
2639
2777
|
/**
|
|
2640
2778
|
* FIXME: should be API method
|
|
@@ -2664,6 +2802,15 @@ export class GridBase {
|
|
|
2664
2802
|
return { error: true };
|
|
2665
2803
|
}
|
|
2666
2804
|
|
|
2805
|
+
// conditionals
|
|
2806
|
+
this.PatchConditionals({
|
|
2807
|
+
sheet: target_sheet,
|
|
2808
|
+
before_column: 0,
|
|
2809
|
+
column_count: 0,
|
|
2810
|
+
before_row: command.before_row,
|
|
2811
|
+
row_count: command.count
|
|
2812
|
+
});
|
|
2813
|
+
|
|
2667
2814
|
// see InsertColumnsInternal re: tables. rows are less complicated,
|
|
2668
2815
|
// except that if you delete the header row we want to remove the
|
|
2669
2816
|
// table entirely.
|
|
@@ -2978,6 +3125,14 @@ export class GridBase {
|
|
|
2978
3125
|
return { error: true };
|
|
2979
3126
|
}
|
|
2980
3127
|
|
|
3128
|
+
// conditionals
|
|
3129
|
+
this.PatchConditionals({
|
|
3130
|
+
sheet: target_sheet,
|
|
3131
|
+
before_column: command.before_column,
|
|
3132
|
+
column_count: command.count,
|
|
3133
|
+
before_row: 0,
|
|
3134
|
+
row_count: 0 });
|
|
3135
|
+
|
|
2981
3136
|
// patch tables. we removed this from the sheet routine entirely,
|
|
2982
3137
|
// we need to rebuild any affected tables now.
|
|
2983
3138
|
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
*
|
|
20
20
|
*/
|
|
21
21
|
|
|
22
|
-
import { DOMUtilities as DOM } from '
|
|
22
|
+
import { DOMUtilities as DOM } from 'treb-base-types';
|
|
23
23
|
import { NumberFormat, NumberFormatCache, ValueParser } from 'treb-format';
|
|
24
24
|
import { ValueType } from 'treb-base-types';
|
|
25
25
|
import { EventSource } from 'treb-utils';
|
|
@@ -54,7 +54,7 @@ export class ScaleControl extends EventSource<ScaleEvent> {
|
|
|
54
54
|
// const div = DOM.CreateDiv('treb-scale-control-2', container);
|
|
55
55
|
|
|
56
56
|
this.input = DOM.Create('input', 'treb-scale-input', /* div */ container);
|
|
57
|
-
const popup = DOM.
|
|
57
|
+
const popup = DOM.Div('treb-slider-container', /* div */ container);
|
|
58
58
|
|
|
59
59
|
/*
|
|
60
60
|
this.input.addEventListener('keyup', (event) => {
|