@trebco/treb 28.11.1 → 28.15.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-light.mjs +11 -11
- package/dist/treb-spreadsheet.mjs +11 -11
- package/dist/treb.d.ts +27 -3
- package/package.json +1 -1
- package/treb-base-types/src/style.ts +3 -0
- package/treb-calculator/src/calculator.ts +235 -68
- package/treb-calculator/src/descriptors.ts +5 -0
- package/treb-calculator/src/expression-calculator.ts +9 -5
- package/treb-calculator/src/functions/base-functions.ts +410 -21
- package/treb-calculator/src/functions/text-functions.ts +45 -55
- package/treb-calculator/src/primitives.ts +11 -0
- package/treb-calculator/src/utilities.ts +55 -0
- package/treb-embed/markup/layout.html +15 -10
- package/treb-embed/markup/toolbar.html +5 -5
- package/treb-embed/src/custom-element/spreadsheet-constructor.ts +38 -2
- package/treb-embed/src/embedded-spreadsheet.ts +227 -29
- package/treb-embed/src/options.ts +5 -0
- package/treb-embed/style/dark-theme.scss +1 -0
- package/treb-embed/style/formula-bar.scss +20 -7
- package/treb-embed/style/theme-defaults.scss +20 -0
- package/treb-export/src/export-worker/export-worker.ts +1 -0
- package/treb-export/src/export2.ts +6 -1
- package/treb-export/src/import2.ts +76 -6
- package/treb-export/src/shared-strings2.ts +1 -1
- package/treb-export/src/workbook-style2.ts +89 -52
- package/treb-export/src/workbook2.ts +119 -1
- package/treb-grid/src/editors/editor.ts +7 -0
- package/treb-grid/src/editors/formula_bar.ts +23 -1
- package/treb-grid/src/render/tile_renderer.ts +46 -3
- package/treb-grid/src/types/annotation.ts +17 -3
- package/treb-grid/src/types/grid.ts +28 -9
- package/treb-grid/src/types/grid_base.ts +10 -105
- package/treb-grid/src/types/grid_options.ts +3 -2
- package/treb-grid/src/types/named_range.ts +8 -1
- package/treb-grid/src/types/serialize_options.ts +5 -0
- package/treb-parser/src/parser-types.ts +27 -4
- package/treb-parser/src/parser.ts +74 -36
|
@@ -38,7 +38,7 @@
|
|
|
38
38
|
<div data-bind='message' class='treb-embed-dialog-message'></div>
|
|
39
39
|
<div data-bind='about' class='treb-embed-dialog-body'></div>
|
|
40
40
|
</div>
|
|
41
|
-
<button type='button' title='
|
|
41
|
+
<button type='button' data-title='close_dialog' data-bind='close' class='treb-close-box'>
|
|
42
42
|
<svg viewBox='0 0 16 16'>
|
|
43
43
|
<path d='M11.854 4.146a.5.5 0 0 1 0 .708l-7 7a.5.5 0 0 1-.708-.708l7-7a.5.5 0 0 1 .708 0z'/>
|
|
44
44
|
<path d='M4.146 4.146a.5.5 0 0 0 0 .708l7 7a.5.5 0 0 0 .708-.708l-7-7a.5.5 0 0 0-.708 0z'/>
|
|
@@ -62,6 +62,9 @@
|
|
|
62
62
|
|
|
63
63
|
<div class="treb-formula-bar notranslate" hidden>
|
|
64
64
|
<div class="treb-address-label"><div></div></div>
|
|
65
|
+
<button class="treb-insert-function-button"
|
|
66
|
+
data-title="insert_function"
|
|
67
|
+
data-conditional="insert-function">𝑓<small>(x)</small></button>
|
|
65
68
|
<div class="treb-editor-container">
|
|
66
69
|
<div contenteditable="true"></div>
|
|
67
70
|
</div>
|
|
@@ -95,7 +98,7 @@
|
|
|
95
98
|
-->
|
|
96
99
|
|
|
97
100
|
<!-- converted to button, more appropriate -->
|
|
98
|
-
<button class="treb-delete-tab" title="
|
|
101
|
+
<button class="treb-delete-tab" data-title="delete_sheet" data-command="delete-tab" data-conditional="delete-tab">
|
|
99
102
|
<svg tabindex="-1" viewbox='0 0 16 16'><path d='M4,4 L12,12 M12,4 L4,12'/></svg>
|
|
100
103
|
</button>
|
|
101
104
|
|
|
@@ -105,7 +108,8 @@
|
|
|
105
108
|
</div>
|
|
106
109
|
|
|
107
110
|
<!-- converted to button, more appropriate -->
|
|
108
|
-
<button class="treb-add-tab" data-command="add-tab" data-conditional="add-tab"
|
|
111
|
+
<button class="treb-add-tab" data-command="add-tab" data-conditional="add-tab"
|
|
112
|
+
data-title="add_sheet">+</button>
|
|
109
113
|
|
|
110
114
|
<!--
|
|
111
115
|
we removed the junk node with "flex grow" to split the layout, in
|
|
@@ -125,7 +129,8 @@
|
|
|
125
129
|
|
|
126
130
|
<div class="treb-revert-indicator"
|
|
127
131
|
data-command="revert-indicator"
|
|
128
|
-
title="
|
|
132
|
+
data-title="document_modified"
|
|
133
|
+
></div>
|
|
129
134
|
|
|
130
135
|
</div> <!-- /treb-view -->
|
|
131
136
|
</template>
|
|
@@ -133,13 +138,13 @@
|
|
|
133
138
|
</div>
|
|
134
139
|
|
|
135
140
|
<div class="treb-layout-sidebar treb-animate">
|
|
136
|
-
<button data-command="recalculate" title="
|
|
137
|
-
<button data-command="toggle-toolbar" data-conditional="toolbar" title="
|
|
138
|
-
<button data-command="export-xlsx" data-conditional="export" title="
|
|
139
|
-
<button data-command="revert" data-conditional="revert" title="
|
|
140
|
-
<button data-command="about" title="
|
|
141
|
+
<button data-command="recalculate" data-title="recalculate"></button>
|
|
142
|
+
<button data-command="toggle-toolbar" data-conditional="toolbar" data-title="toggle_toolbar"></button>
|
|
143
|
+
<button data-command="export-xlsx" data-conditional="export" data-title="export"></button>
|
|
144
|
+
<button data-command="revert" data-conditional="revert" data-title="revert"></button>
|
|
145
|
+
<button data-command="about" data-title="about"></button>
|
|
141
146
|
</div>
|
|
142
147
|
|
|
143
|
-
<button class="treb-toggle-sidebar-button" title="
|
|
148
|
+
<button class="treb-toggle-sidebar-button" data-title="toggle_sidebar"></button>
|
|
144
149
|
|
|
145
150
|
</div>
|
|
@@ -49,12 +49,12 @@
|
|
|
49
49
|
|
|
50
50
|
<div class="group">
|
|
51
51
|
<button data-command="wrap-text" title="Wrap text"></button>
|
|
52
|
-
<button data-command="merge-cells" data-id="merge" data-title="Merge cells" data-active-title="Unmerge cells"></button>
|
|
53
|
-
<button data-command="lock-cells" data-title="Lock cells" data-active-title="Unlock cells"></button>
|
|
54
|
-
<button data-command="freeze-panes" data-title="Freeze panes" data-active-title="Unfreeze panes" freeze-button></button>
|
|
55
|
-
<button data-command="insert-table" data-icon="table" data-title="Insert table" data-active-title="Remove table" table-button></button>
|
|
52
|
+
<button data-command="merge-cells" data-id="merge" data-inactive-title="Merge cells" data-active-title="Unmerge cells"></button>
|
|
53
|
+
<button data-command="lock-cells" data-inactive-title="Lock cells" data-active-title="Unlock cells"></button>
|
|
54
|
+
<button data-command="freeze-panes" data-inactive-title="Freeze panes" data-active-title="Unfreeze panes" freeze-button></button>
|
|
55
|
+
<button data-command="insert-table" data-icon="table" data-inactive-title="Insert table" data-active-title="Remove table" table-button></button>
|
|
56
56
|
<div class="treb-menu">
|
|
57
|
-
<button data-icon="comment" data-title="Comment" data-active-title="Update comment"></button>
|
|
57
|
+
<button data-icon="comment" data-inactive-title="Comment" data-active-title="Update comment"></button>
|
|
58
58
|
<div class="treb-comment-box">
|
|
59
59
|
<textarea></textarea>
|
|
60
60
|
<div>
|
|
@@ -13,6 +13,23 @@ import type { ToolbarMessage } from '../toolbar-message';
|
|
|
13
13
|
|
|
14
14
|
import { DOMContext } from 'treb-base-types';
|
|
15
15
|
|
|
16
|
+
/** with a view towards i18n */
|
|
17
|
+
const default_titles: Record<string, string> = {
|
|
18
|
+
|
|
19
|
+
close_dialog: 'Close dialog',
|
|
20
|
+
insert_function: 'Insert function...',
|
|
21
|
+
delete_sheet: 'Delete current sheet',
|
|
22
|
+
add_sheet: 'Add sheet',
|
|
23
|
+
document_modified: 'This document has been modified from the original version.',
|
|
24
|
+
recalculate: 'Recalculate',
|
|
25
|
+
toggle_toolbar: 'Toggle toolbar',
|
|
26
|
+
export: 'Export as XLSX',
|
|
27
|
+
revert: 'Revert to original version',
|
|
28
|
+
about: `What's this?`,
|
|
29
|
+
toggle_sidebar: 'Toggle sidebar',
|
|
30
|
+
|
|
31
|
+
};
|
|
32
|
+
|
|
16
33
|
/** @internal */
|
|
17
34
|
export class SpreadsheetConstructor<USER_DATA_TYPE = unknown> {
|
|
18
35
|
|
|
@@ -391,6 +408,7 @@ export class SpreadsheetConstructor<USER_DATA_TYPE = unknown> {
|
|
|
391
408
|
'revert': !!sheet.options.revert_button,
|
|
392
409
|
'toolbar': !!sheet.options.toolbar,
|
|
393
410
|
'export': !!sheet.options.export,
|
|
411
|
+
'insert-function': !!sheet.options.insert_function_button,
|
|
394
412
|
|
|
395
413
|
// the following won't work as expected in split, because this
|
|
396
414
|
// code won't be run when the new view is created -- do something
|
|
@@ -545,6 +563,24 @@ export class SpreadsheetConstructor<USER_DATA_TYPE = unknown> {
|
|
|
545
563
|
|
|
546
564
|
}
|
|
547
565
|
|
|
566
|
+
// --- titles --------------------------------------------------------------
|
|
567
|
+
|
|
568
|
+
const elements = Array.from(this.layout_element.querySelectorAll('[data-title]'));
|
|
569
|
+
for (const element of elements) {
|
|
570
|
+
if (element instanceof HTMLElement) {
|
|
571
|
+
|
|
572
|
+
// temp workaround
|
|
573
|
+
if (element.dataset.activeTitle) {
|
|
574
|
+
continue;
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
if (element.dataset.title && default_titles[element.dataset.title]) {
|
|
578
|
+
element.title = default_titles[element.dataset.title];
|
|
579
|
+
}
|
|
580
|
+
|
|
581
|
+
}
|
|
582
|
+
}
|
|
583
|
+
|
|
548
584
|
// --- animated ------------------------------------------------------------
|
|
549
585
|
|
|
550
586
|
// requestAnimationFrame(() => {
|
|
@@ -580,8 +616,8 @@ export class SpreadsheetConstructor<USER_DATA_TYPE = unknown> {
|
|
|
580
616
|
if (value) {
|
|
581
617
|
// value.classList.remove('treb-active');
|
|
582
618
|
value.removeAttribute('active');
|
|
583
|
-
if (value.dataset.
|
|
584
|
-
value.title = value.dataset.
|
|
619
|
+
if (value.dataset.inactiveTitle) {
|
|
620
|
+
value.title = value.dataset.inactiveTitle;
|
|
585
621
|
}
|
|
586
622
|
}
|
|
587
623
|
}
|
|
@@ -42,6 +42,8 @@ import type {
|
|
|
42
42
|
CondifionalFormatExpressionOptions,
|
|
43
43
|
ConditionalFormatCellMatchOptions,
|
|
44
44
|
ConditionalFormatCellMatch,
|
|
45
|
+
MacroFunction,
|
|
46
|
+
SerializedNamedExpression,
|
|
45
47
|
} from 'treb-grid';
|
|
46
48
|
|
|
47
49
|
import {
|
|
@@ -413,7 +415,7 @@ export class EmbeddedSpreadsheet<USER_DATA_TYPE = unknown> {
|
|
|
413
415
|
}
|
|
414
416
|
|
|
415
417
|
/** for destruction */
|
|
416
|
-
protected
|
|
418
|
+
protected view_node?: HTMLElement;
|
|
417
419
|
|
|
418
420
|
/** for destruction */
|
|
419
421
|
protected key_listener?: (event: KeyboardEvent) => void;
|
|
@@ -721,8 +723,7 @@ export class EmbeddedSpreadsheet<USER_DATA_TYPE = unknown> {
|
|
|
721
723
|
|
|
722
724
|
const grid_options: GridOptions = {
|
|
723
725
|
// expand: false,
|
|
724
|
-
|
|
725
|
-
insert_function_button: false, // do we have this?
|
|
726
|
+
// insert_function_button: false, // do we have this?
|
|
726
727
|
in_cell_editor: true, // if this is always true, why is it an option?
|
|
727
728
|
repaint_on_cell_change: false,
|
|
728
729
|
scrollbars: this.options.scrollbars,
|
|
@@ -844,7 +845,7 @@ export class EmbeddedSpreadsheet<USER_DATA_TYPE = unknown> {
|
|
|
844
845
|
// elements. but we don't add a default; rather we use a template
|
|
845
846
|
|
|
846
847
|
const template = container.querySelector('.treb-view-template') as HTMLTemplateElement;
|
|
847
|
-
this.
|
|
848
|
+
this.view_node = template.content.firstElementChild?.cloneNode(true) as HTMLElement;
|
|
848
849
|
|
|
849
850
|
// this is a little weird but we're inserting at the front. the
|
|
850
851
|
// reason for this is that we only want to use one resize handle,
|
|
@@ -852,13 +853,13 @@ export class EmbeddedSpreadsheet<USER_DATA_TYPE = unknown> {
|
|
|
852
853
|
|
|
853
854
|
// we could work around this, really we're just being lazy.
|
|
854
855
|
|
|
855
|
-
container.prepend(this.
|
|
856
|
+
container.prepend(this.view_node);
|
|
856
857
|
|
|
857
858
|
|
|
858
859
|
// this.node = container;
|
|
859
860
|
// this.node = this.view;
|
|
860
861
|
|
|
861
|
-
this.
|
|
862
|
+
this.view_node.addEventListener('focusin', () => {
|
|
862
863
|
if (this.focus_target !== this) {
|
|
863
864
|
this.Publish({ type: 'focus-view' });
|
|
864
865
|
this.focus_target = this;
|
|
@@ -878,14 +879,14 @@ export class EmbeddedSpreadsheet<USER_DATA_TYPE = unknown> {
|
|
|
878
879
|
|
|
879
880
|
// const view = container.querySelector('.treb-view') as HTMLElement;
|
|
880
881
|
|
|
881
|
-
this.grid.Initialize(this.
|
|
882
|
+
this.grid.Initialize(this.view_node, toll_initial_render);
|
|
882
883
|
|
|
883
884
|
// dnd
|
|
884
885
|
|
|
885
886
|
if (this.options.dnd) {
|
|
886
|
-
this.
|
|
887
|
-
this.
|
|
888
|
-
this.
|
|
887
|
+
this.view_node.addEventListener('dragenter', (event) => this.HandleDrag(event));
|
|
888
|
+
this.view_node.addEventListener('dragover', (event) => this.HandleDrag(event));
|
|
889
|
+
this.view_node.addEventListener('drop', (event) => this.HandleDrop(event));
|
|
889
890
|
}
|
|
890
891
|
|
|
891
892
|
// set up grid events
|
|
@@ -1244,19 +1245,19 @@ export class EmbeddedSpreadsheet<USER_DATA_TYPE = unknown> {
|
|
|
1244
1245
|
sheet.grid.grid_events.CancelAll();
|
|
1245
1246
|
sheet.events.CancelAll();
|
|
1246
1247
|
|
|
1247
|
-
if (sheet.
|
|
1248
|
+
if (sheet.view_node?.parentElement) {
|
|
1248
1249
|
|
|
1249
1250
|
// remove listener
|
|
1250
1251
|
if (sheet.key_listener) {
|
|
1251
|
-
sheet.
|
|
1252
|
+
sheet.view_node.parentElement.removeEventListener('keydown', sheet.key_listener);
|
|
1252
1253
|
}
|
|
1253
1254
|
|
|
1254
1255
|
// remove node
|
|
1255
|
-
sheet.
|
|
1256
|
+
sheet.view_node.parentElement.removeChild(sheet.view_node);
|
|
1256
1257
|
}
|
|
1257
1258
|
|
|
1258
1259
|
// in case other view was focused
|
|
1259
|
-
this.
|
|
1260
|
+
this.view_node?.focus();
|
|
1260
1261
|
|
|
1261
1262
|
// usually this results in us getting larger, we may need to update
|
|
1262
1263
|
this.Resize();
|
|
@@ -1285,7 +1286,7 @@ export class EmbeddedSpreadsheet<USER_DATA_TYPE = unknown> {
|
|
|
1285
1286
|
const view = this.CreateView();
|
|
1286
1287
|
view.grid.EnsureActiveSheet(true);
|
|
1287
1288
|
|
|
1288
|
-
view.
|
|
1289
|
+
view.view_node?.addEventListener('focusin', () => {
|
|
1289
1290
|
if (this.focus_target !== view) {
|
|
1290
1291
|
this.Publish({ type: 'focus-view' });
|
|
1291
1292
|
this.focus_target = view;
|
|
@@ -2076,6 +2077,14 @@ export class EmbeddedSpreadsheet<USER_DATA_TYPE = unknown> {
|
|
|
2076
2077
|
for (const format of sheet.conditional_formats) {
|
|
2077
2078
|
format.internal = undefined;
|
|
2078
2079
|
}
|
|
2080
|
+
for (const annotation of sheet.annotations) {
|
|
2081
|
+
if (annotation.view && annotation.data.type === 'textbox') {
|
|
2082
|
+
const view: AnnotationViewData = annotation.view[this.grid.view_index] || {};
|
|
2083
|
+
if (view.update_callback) {
|
|
2084
|
+
view.update_callback();
|
|
2085
|
+
}
|
|
2086
|
+
}
|
|
2087
|
+
}
|
|
2079
2088
|
}
|
|
2080
2089
|
|
|
2081
2090
|
this.calculator.UpdateConditionals();
|
|
@@ -2244,12 +2253,16 @@ export class EmbeddedSpreadsheet<USER_DATA_TYPE = unknown> {
|
|
|
2244
2253
|
this.parser.flags.r1c1 = r1c1;
|
|
2245
2254
|
|
|
2246
2255
|
if (argument_separator === ',') {
|
|
2247
|
-
this.parser.
|
|
2248
|
-
|
|
2256
|
+
this.parser.SetLocaleSettings(DecimalMarkType.Period);
|
|
2257
|
+
|
|
2258
|
+
// this.parser.argument_separator = ArgumentSeparatorType.Comma;
|
|
2259
|
+
// this.parser.decimal_mark = DecimalMarkType.Period;
|
|
2249
2260
|
}
|
|
2250
2261
|
else {
|
|
2251
|
-
this.parser.
|
|
2252
|
-
|
|
2262
|
+
this.parser.SetLocaleSettings(DecimalMarkType.Comma);
|
|
2263
|
+
|
|
2264
|
+
// this.parser.argument_separator = ArgumentSeparatorType.Semicolon;
|
|
2265
|
+
// this.parser.decimal_mark = DecimalMarkType.Comma;
|
|
2253
2266
|
}
|
|
2254
2267
|
|
|
2255
2268
|
const result = this.parser.Parse(formula);
|
|
@@ -2354,12 +2367,16 @@ export class EmbeddedSpreadsheet<USER_DATA_TYPE = unknown> {
|
|
|
2354
2367
|
*/
|
|
2355
2368
|
|
|
2356
2369
|
if (argument_separator === ',') {
|
|
2357
|
-
this.parser.
|
|
2358
|
-
|
|
2370
|
+
this.parser.SetLocaleSettings(DecimalMarkType.Period);
|
|
2371
|
+
|
|
2372
|
+
// this.parser.argument_separator = ArgumentSeparatorType.Comma;
|
|
2373
|
+
// this.parser.decimal_mark = DecimalMarkType.Period;
|
|
2359
2374
|
}
|
|
2360
2375
|
else {
|
|
2361
|
-
this.parser.
|
|
2362
|
-
|
|
2376
|
+
this.parser.SetLocaleSettings(DecimalMarkType.Comma);
|
|
2377
|
+
|
|
2378
|
+
// this.parser.argument_separator = ArgumentSeparatorType.Semicolon;
|
|
2379
|
+
// this.parser.decimal_mark = DecimalMarkType.Comma;
|
|
2363
2380
|
}
|
|
2364
2381
|
|
|
2365
2382
|
// const r1c1_state = this.parser.flags.r1c1;
|
|
@@ -2749,13 +2766,14 @@ export class EmbeddedSpreadsheet<USER_DATA_TYPE = unknown> {
|
|
|
2749
2766
|
|
|
2750
2767
|
// FIXME: type
|
|
2751
2768
|
|
|
2752
|
-
const serialized: SerializedModel = this.grid.Serialize({
|
|
2769
|
+
const serialized: SerializedModel = this.Serialize({ // this.grid.Serialize({
|
|
2753
2770
|
rendered_values: true,
|
|
2754
2771
|
expand_arrays: true,
|
|
2755
2772
|
export_colors: true,
|
|
2756
2773
|
decorated_cells: true,
|
|
2757
2774
|
tables: true,
|
|
2758
2775
|
share_resources: false,
|
|
2776
|
+
export_functions: true,
|
|
2759
2777
|
});
|
|
2760
2778
|
|
|
2761
2779
|
// why do _we_ put this in, instead of the grid method?
|
|
@@ -3523,6 +3541,9 @@ export class EmbeddedSpreadsheet<USER_DATA_TYPE = unknown> {
|
|
|
3523
3541
|
/**
|
|
3524
3542
|
* Create a macro function.
|
|
3525
3543
|
*
|
|
3544
|
+
* FIXME: this needs a control for argument separator, like other
|
|
3545
|
+
* functions that use formulas (@see SetRange)
|
|
3546
|
+
*
|
|
3526
3547
|
* @public
|
|
3527
3548
|
*/
|
|
3528
3549
|
public DefineFunction(name: string, argument_names: string | string[] = '', function_def = '0'): void {
|
|
@@ -3592,7 +3613,8 @@ export class EmbeddedSpreadsheet<USER_DATA_TYPE = unknown> {
|
|
|
3592
3613
|
...options,
|
|
3593
3614
|
};
|
|
3594
3615
|
|
|
3595
|
-
const grid_data = this.grid.Serialize(options);
|
|
3616
|
+
// const grid_data = this.grid.Serialize(options);
|
|
3617
|
+
const grid_data = this.Serialize(options);
|
|
3596
3618
|
|
|
3597
3619
|
// NOTE: these are not really env vars. we replace them at build time
|
|
3598
3620
|
// via a webpack plugin. using the env syntax lets them look "real" at
|
|
@@ -4405,6 +4427,121 @@ export class EmbeddedSpreadsheet<USER_DATA_TYPE = unknown> {
|
|
|
4405
4427
|
|
|
4406
4428
|
// --- internal (protected) methods ------------------------------------------
|
|
4407
4429
|
|
|
4430
|
+
// --- moved from grid/grid base ---------------------------------------------
|
|
4431
|
+
|
|
4432
|
+
|
|
4433
|
+
/**
|
|
4434
|
+
* serialize data model. moved from grid/grid base. this is moved so we
|
|
4435
|
+
* have access to the calculator, which we want so we can do function
|
|
4436
|
+
* translation on some new functions that don't necessarily map 1:1 to
|
|
4437
|
+
* XLSX functions. we can also do cleanup on functions where we're less
|
|
4438
|
+
* strict about arguments (ROUND, for example).
|
|
4439
|
+
*
|
|
4440
|
+
*/
|
|
4441
|
+
protected Serialize(options: SerializeOptions = {}): SerializedModel {
|
|
4442
|
+
|
|
4443
|
+
const active_sheet = this.grid.active_sheet; // I thought this was in view? (...)
|
|
4444
|
+
|
|
4445
|
+
active_sheet.selection = JSON.parse(JSON.stringify(this.grid.GetSelection()));
|
|
4446
|
+
|
|
4447
|
+
// same for scroll offset
|
|
4448
|
+
|
|
4449
|
+
const scroll_offset = this.grid.ScrollOffset();
|
|
4450
|
+
if (scroll_offset) {
|
|
4451
|
+
active_sheet.scroll_offset = scroll_offset; // this.grid.layout.scroll_offset;
|
|
4452
|
+
}
|
|
4453
|
+
|
|
4454
|
+
// NOTE: annotations moved to sheets, they will be serialized in the sheets
|
|
4455
|
+
|
|
4456
|
+
const sheet_data = this.model.sheets.list.map((sheet) => sheet.toJSON(options));
|
|
4457
|
+
|
|
4458
|
+
// OK, not serializing tables in cells anymore. old comment about this:
|
|
4459
|
+
//
|
|
4460
|
+
// at the moment, tables are being serialized in cells. if we put them
|
|
4461
|
+
// in here, then we have two records of the same data. that would be bad.
|
|
4462
|
+
// I think this is probably the correct place, but if we put them here
|
|
4463
|
+
// we need to stop serializing in cells. and I'm not sure that there are
|
|
4464
|
+
// not some side-effects to that. hopefully not, but (...)
|
|
4465
|
+
//
|
|
4466
|
+
|
|
4467
|
+
let tables: Table[] | undefined;
|
|
4468
|
+
if (this.model.tables.size > 0) {
|
|
4469
|
+
tables = Array.from(this.model.tables.values());
|
|
4470
|
+
}
|
|
4471
|
+
|
|
4472
|
+
// NOTE: moving into a structured object (the sheet data is also structured,
|
|
4473
|
+
// of course) but we are moving things out of sheet (just named ranges atm))
|
|
4474
|
+
|
|
4475
|
+
let macro_functions: MacroFunction[] | undefined;
|
|
4476
|
+
|
|
4477
|
+
if (this.model.macro_functions.size) {
|
|
4478
|
+
macro_functions = [];
|
|
4479
|
+
for (const macro of this.model.macro_functions.values()) {
|
|
4480
|
+
macro_functions.push({
|
|
4481
|
+
...macro,
|
|
4482
|
+
expression: undefined,
|
|
4483
|
+
});
|
|
4484
|
+
}
|
|
4485
|
+
}
|
|
4486
|
+
|
|
4487
|
+
// when serializing named expressions, we have to make sure
|
|
4488
|
+
// that there's a sheet name in any address/range.
|
|
4489
|
+
|
|
4490
|
+
const named_expressions: SerializedNamedExpression[] = [];
|
|
4491
|
+
if (this.model.named_expressions) {
|
|
4492
|
+
|
|
4493
|
+
for (const [name, expr] of this.model.named_expressions) {
|
|
4494
|
+
this.parser.Walk(expr, unit => {
|
|
4495
|
+
if (unit.type === 'address' || unit.type === 'range') {
|
|
4496
|
+
const test = unit.type === 'range' ? unit.start : unit;
|
|
4497
|
+
|
|
4498
|
+
test.absolute_column = test.absolute_row = true;
|
|
4499
|
+
|
|
4500
|
+
if (!test.sheet) {
|
|
4501
|
+
if (test.sheet_id) {
|
|
4502
|
+
const sheet = this.model.sheets.Find(test.sheet_id);
|
|
4503
|
+
if (sheet) {
|
|
4504
|
+
test.sheet = sheet.name;
|
|
4505
|
+
}
|
|
4506
|
+
}
|
|
4507
|
+
if (!test.sheet) {
|
|
4508
|
+
test.sheet = active_sheet.name;
|
|
4509
|
+
}
|
|
4510
|
+
}
|
|
4511
|
+
|
|
4512
|
+
if (unit.type === 'range') {
|
|
4513
|
+
unit.end.absolute_column = unit.end.absolute_row = true;
|
|
4514
|
+
}
|
|
4515
|
+
|
|
4516
|
+
return false;
|
|
4517
|
+
}
|
|
4518
|
+
else if (unit.type === 'call' && options.export_functions) {
|
|
4519
|
+
// ...
|
|
4520
|
+
}
|
|
4521
|
+
return true;
|
|
4522
|
+
});
|
|
4523
|
+
const rendered = this.parser.Render(expr, { missing: '' });
|
|
4524
|
+
named_expressions.push({
|
|
4525
|
+
name, expression: rendered
|
|
4526
|
+
});
|
|
4527
|
+
}
|
|
4528
|
+
}
|
|
4529
|
+
|
|
4530
|
+
return {
|
|
4531
|
+
sheet_data,
|
|
4532
|
+
active_sheet: active_sheet.id,
|
|
4533
|
+
named_ranges: this.model.named_ranges.Count() ?
|
|
4534
|
+
this.model.named_ranges.Serialize() :
|
|
4535
|
+
undefined,
|
|
4536
|
+
macro_functions,
|
|
4537
|
+
tables,
|
|
4538
|
+
named_expressions: named_expressions.length ? named_expressions : undefined,
|
|
4539
|
+
};
|
|
4540
|
+
|
|
4541
|
+
}
|
|
4542
|
+
|
|
4543
|
+
// --- /moved ----------------------------------------------------------------
|
|
4544
|
+
|
|
4408
4545
|
/**
|
|
4409
4546
|
*
|
|
4410
4547
|
*/
|
|
@@ -5219,6 +5356,63 @@ export class EmbeddedSpreadsheet<USER_DATA_TYPE = unknown> {
|
|
|
5219
5356
|
}
|
|
5220
5357
|
|
|
5221
5358
|
}
|
|
5359
|
+
else if (annotation.data.type === 'textbox') {
|
|
5360
|
+
|
|
5361
|
+
if (annotation.data.data) {
|
|
5362
|
+
const container = document.createElement('div');
|
|
5363
|
+
container.classList.add('treb-annotation-textbox');
|
|
5364
|
+
|
|
5365
|
+
for (const paragraph of annotation.data.data.paragraphs) {
|
|
5366
|
+
const p = document.createElement('p');
|
|
5367
|
+
if (paragraph.style) {
|
|
5368
|
+
if (paragraph.style.horizontal_align === 'right' ||
|
|
5369
|
+
paragraph.style.horizontal_align === 'center') {
|
|
5370
|
+
p.style.textAlign = paragraph.style.horizontal_align;
|
|
5371
|
+
}
|
|
5372
|
+
}
|
|
5373
|
+
|
|
5374
|
+
for (const entry of paragraph.content) {
|
|
5375
|
+
const span = document.createElement('span');
|
|
5376
|
+
span.textContent = entry.text || '';
|
|
5377
|
+
if (entry.style?.bold) {
|
|
5378
|
+
span.style.fontWeight = '600';
|
|
5379
|
+
}
|
|
5380
|
+
if (entry.style?.italic) {
|
|
5381
|
+
span.style.fontStyle = 'italic';
|
|
5382
|
+
}
|
|
5383
|
+
p.appendChild(span);
|
|
5384
|
+
}
|
|
5385
|
+
container.append(p);
|
|
5386
|
+
}
|
|
5387
|
+
|
|
5388
|
+
view.content_node.append(container);
|
|
5389
|
+
|
|
5390
|
+
if (annotation.data.data.style) {
|
|
5391
|
+
|
|
5392
|
+
const style = annotation.data.data.style;
|
|
5393
|
+
const update_textbox = () => {
|
|
5394
|
+
|
|
5395
|
+
if (style.fill) {
|
|
5396
|
+
const color = ThemeColor2(this.grid.theme, style.fill);
|
|
5397
|
+
container.style.background = color;
|
|
5398
|
+
}
|
|
5399
|
+
|
|
5400
|
+
};
|
|
5401
|
+
|
|
5402
|
+
view.update_callback = () => {
|
|
5403
|
+
if (!this.grid.headless) {
|
|
5404
|
+
update_textbox();
|
|
5405
|
+
}
|
|
5406
|
+
};
|
|
5407
|
+
|
|
5408
|
+
update_textbox();
|
|
5409
|
+
|
|
5410
|
+
}
|
|
5411
|
+
|
|
5412
|
+
}
|
|
5413
|
+
|
|
5414
|
+
}
|
|
5415
|
+
|
|
5222
5416
|
}
|
|
5223
5417
|
}
|
|
5224
5418
|
|
|
@@ -5531,14 +5725,18 @@ export class EmbeddedSpreadsheet<USER_DATA_TYPE = unknown> {
|
|
|
5531
5725
|
// FIXME: also we should unify on types for decimal, argument separator
|
|
5532
5726
|
|
|
5533
5727
|
if (data.decimal_mark === '.') {
|
|
5534
|
-
parser.decimal_mark = DecimalMarkType.Period;
|
|
5535
|
-
parser.argument_separator = ArgumentSeparatorType.Comma;
|
|
5728
|
+
// parser.decimal_mark = DecimalMarkType.Period;
|
|
5729
|
+
// parser.argument_separator = ArgumentSeparatorType.Comma;
|
|
5730
|
+
parser.SetLocaleSettings(DecimalMarkType.Period);
|
|
5731
|
+
|
|
5536
5732
|
target_decimal_mark = DecimalMarkType.Comma;
|
|
5537
5733
|
target_argument_separator = ArgumentSeparatorType.Semicolon;
|
|
5538
5734
|
}
|
|
5539
5735
|
else {
|
|
5540
|
-
parser.decimal_mark = DecimalMarkType.Comma;
|
|
5541
|
-
parser.argument_separator = ArgumentSeparatorType.Semicolon;
|
|
5736
|
+
// parser.decimal_mark = DecimalMarkType.Comma;
|
|
5737
|
+
// parser.argument_separator = ArgumentSeparatorType.Semicolon;
|
|
5738
|
+
parser.SetLocaleSettings(DecimalMarkType.Comma);
|
|
5739
|
+
|
|
5542
5740
|
target_decimal_mark = DecimalMarkType.Period;
|
|
5543
5741
|
target_argument_separator = ArgumentSeparatorType.Comma;
|
|
5544
5742
|
}
|
|
@@ -25,11 +25,10 @@
|
|
|
25
25
|
|
|
26
26
|
grid-area: 1/1/2/2;
|
|
27
27
|
|
|
28
|
-
// min-height: 3em; // FIXME
|
|
29
|
-
|
|
30
28
|
display: flex;
|
|
31
29
|
flex-direction: row;
|
|
32
30
|
text-align: left;
|
|
31
|
+
gap: .5em;
|
|
33
32
|
|
|
34
33
|
&[hidden] {
|
|
35
34
|
display: none;
|
|
@@ -39,7 +38,6 @@
|
|
|
39
38
|
|
|
40
39
|
max-width: 100%;
|
|
41
40
|
overflow-x: hidden;
|
|
42
|
-
/* last ditch for IE11 */
|
|
43
41
|
|
|
44
42
|
/** label for selection address */
|
|
45
43
|
.treb-address-label {
|
|
@@ -56,7 +54,7 @@
|
|
|
56
54
|
justify-content: center;
|
|
57
55
|
padding-left: 3px;
|
|
58
56
|
|
|
59
|
-
margin-right: 6px;
|
|
57
|
+
// margin-right: 6px;
|
|
60
58
|
|
|
61
59
|
&>div {
|
|
62
60
|
outline: none;
|
|
@@ -66,6 +64,20 @@
|
|
|
66
64
|
}
|
|
67
65
|
}
|
|
68
66
|
|
|
67
|
+
.treb-insert-function-button {
|
|
68
|
+
min-height: 1.5em;
|
|
69
|
+
height: 1.75em;
|
|
70
|
+
border: 1px solid var(--treb-formula-bar-border-color, var(--treb-ui-border-color, #ccc));
|
|
71
|
+
border-radius: 2px;
|
|
72
|
+
background: transparent;
|
|
73
|
+
|
|
74
|
+
&[hidden] {
|
|
75
|
+
display: none;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/** expand formula box -- caret/arrow */
|
|
69
81
|
.expand-button {
|
|
70
82
|
background: transparent;
|
|
71
83
|
border: 0;
|
|
@@ -160,7 +172,7 @@
|
|
|
160
172
|
|
|
161
173
|
}
|
|
162
174
|
|
|
163
|
-
|
|
175
|
+
/* * f(x) button, optional * /
|
|
164
176
|
.formula-button {
|
|
165
177
|
border: 1px solid #ccc;
|
|
166
178
|
border-radius: 2px;
|
|
@@ -188,8 +200,9 @@
|
|
|
188
200
|
}
|
|
189
201
|
|
|
190
202
|
}
|
|
191
|
-
|
|
203
|
+
*/
|
|
204
|
+
|
|
192
205
|
|
|
193
|
-
|
|
206
|
+
}
|
|
194
207
|
|
|
195
208
|
}
|
|
@@ -519,4 +519,24 @@ $text-reference-color-5: rgb(254, 47, 1);
|
|
|
519
519
|
font-family: var(--treb-grid-font-family-osx, var(--treb-grid-font-family, inherit));
|
|
520
520
|
}
|
|
521
521
|
|
|
522
|
+
/**
|
|
523
|
+
*
|
|
524
|
+
*/
|
|
525
|
+
.treb-annotation-textbox {
|
|
526
|
+
background: Canvas;
|
|
527
|
+
position: relative;
|
|
528
|
+
top: 0px;
|
|
529
|
+
left: 0px;
|
|
530
|
+
overflow: hidden;
|
|
531
|
+
width: 100%;
|
|
532
|
+
height: 100%;
|
|
533
|
+
padding: .5em; // ?
|
|
534
|
+
|
|
535
|
+
p {
|
|
536
|
+
margin-block-start: 0;
|
|
537
|
+
margin-block-end: 0;
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
}
|
|
541
|
+
|
|
522
542
|
}
|