@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.
Files changed (37) hide show
  1. package/dist/treb-spreadsheet-light.mjs +11 -11
  2. package/dist/treb-spreadsheet.mjs +11 -11
  3. package/dist/treb.d.ts +27 -3
  4. package/package.json +1 -1
  5. package/treb-base-types/src/style.ts +3 -0
  6. package/treb-calculator/src/calculator.ts +235 -68
  7. package/treb-calculator/src/descriptors.ts +5 -0
  8. package/treb-calculator/src/expression-calculator.ts +9 -5
  9. package/treb-calculator/src/functions/base-functions.ts +410 -21
  10. package/treb-calculator/src/functions/text-functions.ts +45 -55
  11. package/treb-calculator/src/primitives.ts +11 -0
  12. package/treb-calculator/src/utilities.ts +55 -0
  13. package/treb-embed/markup/layout.html +15 -10
  14. package/treb-embed/markup/toolbar.html +5 -5
  15. package/treb-embed/src/custom-element/spreadsheet-constructor.ts +38 -2
  16. package/treb-embed/src/embedded-spreadsheet.ts +227 -29
  17. package/treb-embed/src/options.ts +5 -0
  18. package/treb-embed/style/dark-theme.scss +1 -0
  19. package/treb-embed/style/formula-bar.scss +20 -7
  20. package/treb-embed/style/theme-defaults.scss +20 -0
  21. package/treb-export/src/export-worker/export-worker.ts +1 -0
  22. package/treb-export/src/export2.ts +6 -1
  23. package/treb-export/src/import2.ts +76 -6
  24. package/treb-export/src/shared-strings2.ts +1 -1
  25. package/treb-export/src/workbook-style2.ts +89 -52
  26. package/treb-export/src/workbook2.ts +119 -1
  27. package/treb-grid/src/editors/editor.ts +7 -0
  28. package/treb-grid/src/editors/formula_bar.ts +23 -1
  29. package/treb-grid/src/render/tile_renderer.ts +46 -3
  30. package/treb-grid/src/types/annotation.ts +17 -3
  31. package/treb-grid/src/types/grid.ts +28 -9
  32. package/treb-grid/src/types/grid_base.ts +10 -105
  33. package/treb-grid/src/types/grid_options.ts +3 -2
  34. package/treb-grid/src/types/named_range.ts +8 -1
  35. package/treb-grid/src/types/serialize_options.ts +5 -0
  36. package/treb-parser/src/parser-types.ts +27 -4
  37. 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='Close dialog' data-bind='close' class='treb-close-box'>
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="Delete current sheet" data-command="delete-tab" data-conditional="delete-tab">
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" title="Add sheet">+</button>
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="This document has been modified from the original version."></div>
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="Recalculate"></button>
137
- <button data-command="toggle-toolbar" data-conditional="toolbar" title="Toggle toolbar"></button>
138
- <button data-command="export-xlsx" data-conditional="export" title="Export as XLSX"></button>
139
- <button data-command="revert" data-conditional="revert" title="Revert to original version"></button>
140
- <button data-command="about" title="What's this?"></button>
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="Toggle sidebar"></button>
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.title) {
584
- value.title = value.dataset.title;
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 view?: HTMLElement;
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.view = template.content.firstElementChild?.cloneNode(true) as HTMLElement;
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.view);
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.view.addEventListener('focusin', () => {
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.view, toll_initial_render);
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.view.addEventListener('dragenter', (event) => this.HandleDrag(event));
887
- this.view.addEventListener('dragover', (event) => this.HandleDrag(event));
888
- this.view.addEventListener('drop', (event) => this.HandleDrop(event));
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.view?.parentElement) {
1248
+ if (sheet.view_node?.parentElement) {
1248
1249
 
1249
1250
  // remove listener
1250
1251
  if (sheet.key_listener) {
1251
- sheet.view.parentElement.removeEventListener('keydown', sheet.key_listener);
1252
+ sheet.view_node.parentElement.removeEventListener('keydown', sheet.key_listener);
1252
1253
  }
1253
1254
 
1254
1255
  // remove node
1255
- sheet.view.parentElement.removeChild(sheet.view);
1256
+ sheet.view_node.parentElement.removeChild(sheet.view_node);
1256
1257
  }
1257
1258
 
1258
1259
  // in case other view was focused
1259
- this.view?.focus();
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.view?.addEventListener('focusin', () => {
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.argument_separator = ArgumentSeparatorType.Comma;
2248
- this.parser.decimal_mark = DecimalMarkType.Period;
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.argument_separator = ArgumentSeparatorType.Semicolon;
2252
- this.parser.decimal_mark = DecimalMarkType.Comma;
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.argument_separator = ArgumentSeparatorType.Comma;
2358
- this.parser.decimal_mark = DecimalMarkType.Period;
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.argument_separator = ArgumentSeparatorType.Semicolon;
2362
- this.parser.decimal_mark = DecimalMarkType.Comma;
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
  }
@@ -311,6 +311,11 @@ export interface EmbeddedSpreadsheetOptions {
311
311
  */
312
312
  recalculate_on_f9?: boolean;
313
313
 
314
+ /**
315
+ * @internal
316
+ */
317
+ insert_function_button?: boolean;
318
+
314
319
  }
315
320
 
316
321
  /**
@@ -33,6 +33,7 @@
33
33
  --treb-autocomplete-tooltip-color: #fff;
34
34
  --treb-chart-background: #000;
35
35
  --treb-chart-grid-color: #976;
36
+ --treb-chart-grid-zero-color: #fdd;
36
37
  --treb-chart-text-color: #fff;
37
38
  --treb-dialog-background: #000;
38
39
  --treb-dialog-color: #fff;
@@ -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
- /** f(x) button, optional */
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
  }
@@ -57,6 +57,7 @@ const ImportSheet = (data: any) => {
57
57
  results.sheets.push(result);
58
58
  }
59
59
  }
60
+
60
61
  ctx.postMessage({ status: 'complete', results });
61
62
 
62
63
  }