@trebco/treb 30.2.4 → 30.2.14

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 (32) 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 +1 -1
  4. package/esbuild-custom-element.mjs +2 -0
  5. package/esbuild-utils.mjs +2 -0
  6. package/package.json +6 -7
  7. package/treb-calculator/src/calculator.ts +41 -0
  8. package/treb-calculator/src/expression-calculator.ts +30 -1
  9. package/treb-calculator/src/functions/base-functions.ts +164 -1
  10. package/treb-calculator/src/functions/statistics-functions.ts +134 -0
  11. package/treb-calculator/src/functions/text-functions.ts +46 -1
  12. package/treb-calculator/src/primitives.ts +3 -1
  13. package/treb-calculator/src/utilities.ts +5 -1
  14. package/treb-data-model/src/data_model.ts +227 -2
  15. package/treb-data-model/src/index.ts +1 -0
  16. package/{treb-embed → treb-data-model}/src/language-model.ts +3 -0
  17. package/treb-data-model/src/sheet.ts +0 -13
  18. package/treb-embed/src/embedded-spreadsheet.ts +22 -39
  19. package/treb-embed/style/autocomplete.scss +2 -18
  20. package/treb-embed/style/dialog.scss +1 -1
  21. package/treb-embed/style/dropdown-select.scss +5 -6
  22. package/treb-embed/style/formula-bar.scss +5 -3
  23. package/treb-embed/style/grid.scss +7 -4
  24. package/treb-embed/style/layout.scss +23 -18
  25. package/treb-embed/style/tab-bar.scss +2 -2
  26. package/treb-embed/style/theme-defaults.scss +4 -4
  27. package/treb-embed/style/toolbar.scss +1 -1
  28. package/treb-grid/src/render/tile_renderer.ts +14 -0
  29. package/treb-grid/src/types/grid.ts +25 -139
  30. package/treb-parser/src/parser-types.ts +12 -0
  31. package/treb-parser/src/parser.ts +43 -2
  32. package/tsproject.json +1 -1
@@ -22,12 +22,13 @@
22
22
  import type { Sheet } from './sheet';
23
23
  import { SheetCollection } from './sheet_collection';
24
24
  import { type UnitAddress, type UnitStructuredReference, type UnitRange, Parser, QuotedSheetNameRegex, DecimalMarkType, ArgumentSeparatorType } from 'treb-parser';
25
- import type { IArea, ICellAddress, Table, CellStyle } from 'treb-base-types';
25
+ import type { IArea, ICellAddress, Table, CellStyle, CellValue } from 'treb-base-types';
26
+ import { Is2DArray } from 'treb-base-types';
26
27
  import { Area, IsCellAddress, Style } from 'treb-base-types';
27
28
  import type { SerializedNamed } from './named';
28
29
  import { NamedRangeManager } from './named';
29
30
  import type { ConnectedElementType, MacroFunction } from './types';
30
-
31
+ import type { LanguageModel } from './language-model';
31
32
 
32
33
  /**
33
34
  *
@@ -36,6 +37,9 @@ export class DataModel {
36
37
 
37
38
  public readonly parser: Parser = new Parser();
38
39
 
40
+ /** moved from embedded spreadsheet */
41
+ public language_model?: LanguageModel;
42
+
39
43
  /** document metadata */
40
44
  public document_name?: string;
41
45
 
@@ -523,5 +527,226 @@ export class DataModel {
523
527
  */
524
528
  public connected_elements: Map<number, ConnectedElementType> = new Map();
525
529
 
530
+
531
+
532
+ // --- moving translation here ----------------------------------------------
533
+
534
+
535
+ /**
536
+ * maps common language (english) -> local language. this should
537
+ * be passed in (actually set via a function).
538
+ */
539
+ private language_map?: Record<string, string>;
540
+
541
+ /**
542
+ * maps local language -> common (english). this should be constructed
543
+ * when the forward function is passed in, so there's a 1-1 correspondence.
544
+ */
545
+ private reverse_language_map?: Record<string, string>;
546
+
547
+
548
+ /**
549
+ * set the language translation map. this is a set of function names
550
+ * (in english) -> the local equivalent. both should be in canonical form,
551
+ * as that will be used when we translate one way or the other.
552
+ */
553
+ public SetLanguageMap(language_map?: Record<string, string>) {
554
+
555
+ if (!language_map) {
556
+ this.language_map = this.reverse_language_map = undefined;
557
+ }
558
+ else {
559
+
560
+ const keys = Object.keys(language_map);
561
+
562
+ // normalize forward
563
+ this.language_map = {};
564
+ for (const key of keys) {
565
+ this.language_map[key.toUpperCase()] = language_map[key];
566
+ }
567
+
568
+ // normalize backward
569
+ this.reverse_language_map = {};
570
+ for (const key of keys) {
571
+ const value = language_map[key];
572
+ this.reverse_language_map[value.toUpperCase()] = key;
573
+ }
574
+
575
+ }
576
+
577
+ /*
578
+ // we might need to update the current displayed selection. depends
579
+ // on when we expect languages to be set.
580
+
581
+ if (!this.primary_selection.empty) {
582
+ this.Select(this.primary_selection, this.primary_selection.area, this.primary_selection.target);
583
+ }
584
+ */
585
+
586
+ }
587
+
588
+ /**
589
+ * translate function from common (english) -> local language. this could
590
+ * be inlined (assuming it's only called in one place), but we are breaking
591
+ * it out so we can develop/test/manage it.
592
+ */
593
+ public TranslateFunction(value: string): string {
594
+ if (this.language_map) {
595
+ return this.TranslateInternal(value, this.language_map, this.language_model?.boolean_true, this.language_model?.boolean_false);
596
+ }
597
+ return value;
598
+ }
599
+
600
+ /**
601
+ * translate from local language -> common (english).
602
+ * @see TranslateFunction
603
+ */
604
+ public UntranslateFunction(value: string): string {
605
+ if (this.reverse_language_map) {
606
+ return this.TranslateInternal(value, this.reverse_language_map, 'TRUE', 'FALSE');
607
+ }
608
+ return value;
609
+ }
610
+
611
+ public UntranslateData(value: CellValue|CellValue[]|CellValue[][]): CellValue|CellValue[]|CellValue[][] {
612
+
613
+ if (Array.isArray(value)) {
614
+
615
+ // could be 1d or 2d. typescript is complaining, not sure why...
616
+
617
+ if (Is2DArray(value)) {
618
+ return value.map(row => row.map(entry => {
619
+ if (entry && typeof entry === 'string' && entry[0] === '=') {
620
+ return this.UntranslateFunction(entry);
621
+ }
622
+ return entry;
623
+ }));
624
+ }
625
+ else {
626
+ return value.map(entry => {
627
+ if (entry && typeof entry === 'string' && entry[0] === '=') {
628
+ return this.UntranslateFunction(entry);
629
+ }
630
+ return entry;
631
+ });
632
+ }
633
+
634
+ }
635
+ else if (value && typeof value === 'string' && value[0] === '=') {
636
+
637
+ // single value
638
+ value = this.UntranslateFunction(value);
639
+
640
+ }
641
+
642
+ return value;
643
+
644
+ }
645
+
646
+
647
+ /**
648
+ * translation back and forth is the same operation, with a different
649
+ * (inverted) map. although it still might be worth inlining depending
650
+ * on cost.
651
+ *
652
+ * FIXME: it's about time we started using proper maps, we dropped
653
+ * support for IE11 some time ago.
654
+ */
655
+ private TranslateInternal(value: string, map: Record<string, string>, boolean_true?: string, boolean_false?: string): string {
656
+
657
+ const parse_result = this.parser.Parse(value);
658
+
659
+ if (parse_result.expression) {
660
+
661
+ let modified = false;
662
+ this.parser.Walk(parse_result.expression, unit => {
663
+ if (unit.type === 'call') {
664
+ const replacement = map[unit.name.toUpperCase()];
665
+ if (replacement) {
666
+ modified = true;
667
+ unit.name = replacement;
668
+ }
669
+ }
670
+ else if (unit.type === 'literal' && typeof unit.value === 'boolean') {
671
+
672
+ // to/from english/locale depends on the direction, but we're not
673
+ // passing that in? FIXME, pass it as a parameter. it doesn't matter
674
+ // here, but later when we render.
675
+
676
+ modified = true;
677
+
678
+ }
679
+
680
+ return true;
681
+ });
682
+
683
+ if (modified) {
684
+ return '=' + this.parser.Render(parse_result.expression, {
685
+ missing: '', boolean_true, boolean_false,
686
+ });
687
+ }
688
+ }
689
+
690
+ return value;
691
+
692
+ }
693
+
694
+
695
+ /**
696
+ * this is not public _yet_
697
+ *
698
+ * @internal
699
+ */
700
+ public SetLanguage(model?: LanguageModel): void {
701
+
702
+ this.language_model = model;
703
+
704
+ if (!model) {
705
+ this.SetLanguageMap(); // clear
706
+
707
+ // set defaults for parsing.
708
+
709
+ this.parser.flags.boolean_true = 'TRUE';
710
+ this.parser.flags.boolean_false = 'FALSE';
711
+
712
+ }
713
+ else {
714
+
715
+ // create a name map for grid
716
+
717
+ const map: Record< string, string > = {};
718
+
719
+ if (model.functions) {
720
+ for (const entry of model.functions || []) {
721
+ map[entry.base.toUpperCase()] = entry.name; // toUpperCase because of a data error -- fix at the source
722
+ }
723
+ }
724
+
725
+ this.SetLanguageMap(map);
726
+
727
+ // console.info({map});
728
+
729
+ if (!model.boolean_false) {
730
+ model.boolean_false = map['FALSE'];
731
+ }
732
+ if (!model.boolean_true) {
733
+ model.boolean_true = map['TRUE'];
734
+ }
735
+
736
+ // set defaults for parsing.
737
+
738
+ this.parser.flags.boolean_true = model.boolean_true || 'TRUE';
739
+ this.parser.flags.boolean_false = model.boolean_false || 'FALSE';
740
+
741
+ // console.info("booleans:", this.model.parser.flags.boolean_true, ",", this.model.parser.flags.boolean_false)
742
+
743
+ }
744
+
745
+ for (const sheet of this.sheets.list) {
746
+ sheet.FlushCellStyles();
747
+ }
748
+
749
+ }
750
+
526
751
  }
527
752
 
@@ -34,4 +34,5 @@ export type { AnnotationData, AnnotationType } from './annotation';
34
34
 
35
35
  export * from './data-validation';
36
36
  export * from './types';
37
+ export * from './language-model';
37
38
 
@@ -37,5 +37,8 @@ export interface LanguageModel {
37
37
  version?: string;
38
38
  locale?: string;
39
39
  functions?: TranslatedFunctionDescriptor[];
40
+
41
+ boolean_true?: string;
42
+ boolean_false?: string;
40
43
  }
41
44
 
@@ -807,19 +807,6 @@ export class Sheet {
807
807
  }
808
808
  }
809
809
 
810
- /* *
811
- * factory method creates a sheet from a 2D array.
812
- *
813
- * /
814
- public static FromArray(data: any[] = [], transpose = false): Sheet {
815
- const sheet = new Sheet();
816
- sheet.cells.FromArray(data, transpose);
817
-
818
- return sheet;
819
- }
820
- */
821
-
822
-
823
810
  // --- public methods -------------------------------------------------------
824
811
 
825
812
  public MergeCells(area: Area): void {
@@ -49,7 +49,10 @@ import type {
49
49
  CondifionalFormatExpressionOptions,
50
50
  ConditionalFormatCellMatchOptions,
51
51
  ConditionalFormatCellMatch,
52
-
52
+
53
+ LanguageModel,
54
+ TranslatedFunctionDescriptor,
55
+
53
56
  } from 'treb-data-model';
54
57
 
55
58
 
@@ -88,7 +91,6 @@ import { Spinner } from './spinner';
88
91
  import { type EmbeddedSpreadsheetOptions, DefaultOptions, type ExportOptions } from './options';
89
92
  import { type TREBDocument, SaveFileType, LoadSource, type EmbeddedSheetEvent, type InsertTableOptions } from './types';
90
93
 
91
- import type { LanguageModel, TranslatedFunctionDescriptor } from './language-model';
92
94
  import type { SelectionState } from './selection-state';
93
95
  import type { BorderToolbarMessage, ToolbarMessage } from './toolbar-message';
94
96
 
@@ -341,8 +343,9 @@ export class EmbeddedSpreadsheet<USER_DATA_TYPE = unknown> {
341
343
  return Localization;
342
344
  }
343
345
 
344
- /** loaded language model, if any */
346
+ /* * loaded language model, if any (moved to data model) * /
345
347
  protected language_model?: LanguageModel;
348
+ */
346
349
 
347
350
  /** FIXME: fix type (needs to be extensible) */
348
351
  protected events = new EventSource<{ type: string }>();
@@ -800,7 +803,7 @@ export class EmbeddedSpreadsheet<USER_DATA_TYPE = unknown> {
800
803
  const obj = JSON.parse(json);
801
804
  grid_options.initial_scale = obj.scale || 1;
802
805
  }
803
- catch (e) {
806
+ catch {
804
807
  console.warn('parsing persisted scale failed');
805
808
  }
806
809
  }
@@ -1204,10 +1207,10 @@ export class EmbeddedSpreadsheet<USER_DATA_TYPE = unknown> {
1204
1207
 
1205
1208
  let list: FunctionDescriptor[] = this.calculator.SupportedFunctions();
1206
1209
 
1207
- if (this.language_model?.functions) {
1210
+ if (this.model.language_model?.functions) {
1208
1211
 
1209
1212
  const map: Record<string, TranslatedFunctionDescriptor> = {};
1210
- for (const entry of this.language_model.functions || []) {
1213
+ for (const entry of this.model.language_model.functions || []) {
1211
1214
  map[entry.base.toUpperCase()] = entry;
1212
1215
  }
1213
1216
 
@@ -2002,13 +2005,18 @@ export class EmbeddedSpreadsheet<USER_DATA_TYPE = unknown> {
2002
2005
  language = parts[0];
2003
2006
  }
2004
2007
 
2005
- this.SetLanguage(); // clear
2006
-
2007
2008
  language = language.toLowerCase();
2008
-
2009
2009
  let mod: { LanguageMap: LanguageModel } | undefined;
2010
2010
 
2011
- if (language && language !== 'en') {
2011
+ // FIXME: this should be generated and included, to allow for changes.
2012
+ // for the time being hardcoding as a quick fix
2013
+
2014
+ const supported_languages = [
2015
+ 'es', 'fr', 'pt', 'nl', 'de',
2016
+ 'pl', 'sv', 'no', 'da', 'it',
2017
+ ];
2018
+
2019
+ if (language && supported_languages.includes(language)) {
2012
2020
 
2013
2021
  // FIXME: even though we now have a dynamic import
2014
2022
  // working, we still probably want to use a filter
@@ -2029,39 +2037,14 @@ export class EmbeddedSpreadsheet<USER_DATA_TYPE = unknown> {
2029
2037
  }
2030
2038
 
2031
2039
  if (mod) {
2032
- this.SetLanguage(mod.LanguageMap);
2033
- }
2034
-
2035
- }
2036
-
2037
- /**
2038
- * this is not public _yet_
2039
- *
2040
- * @internal
2041
- */
2042
- public SetLanguage(model?: LanguageModel): void {
2043
-
2044
- this.language_model = model;
2045
-
2046
- if (!model) {
2047
- this.grid.SetLanguageMap(); // clear
2040
+ this.model.SetLanguage(mod.LanguageMap);
2048
2041
  }
2049
2042
  else {
2050
-
2051
- // create a name map for grid
2052
-
2053
- const map: Record< string, string > = {};
2054
-
2055
- if (model.functions) {
2056
- for (const entry of model.functions || []) {
2057
- map[entry.base] = entry.name;
2058
- }
2059
- }
2060
-
2061
- this.grid.SetLanguageMap(map);
2062
-
2043
+ this.model.SetLanguage();
2063
2044
  }
2064
2045
 
2046
+ this.grid.Reselect();
2047
+ this.grid.Update(true);
2065
2048
  this.UpdateAC();
2066
2049
 
2067
2050
  }
@@ -33,7 +33,6 @@
33
33
 
34
34
 
35
35
  box-sizing: border-box;
36
- * { box-sizing: border-box; }
37
36
 
38
37
  position: fixed;
39
38
  top: -1000px;
@@ -46,6 +45,8 @@
46
45
  overflow-y: auto;
47
46
  z-index: $z-index-autocomplete;
48
47
 
48
+ * { box-sizing: border-box; }
49
+
49
50
  ul {
50
51
 
51
52
  font-size: inherit;
@@ -73,23 +74,6 @@
73
74
  }
74
75
  }
75
76
 
76
- /*
77
- here we unwind the selected style if there's a hover,
78
- unless you're hovering over the selection. and so on.
79
- * /
80
-
81
- &:hover li a.selected {
82
- background: inherit;
83
- color: inherit;
84
-
85
- &:hover {
86
- background: #339966;
87
- color: #fff;
88
- }
89
-
90
- }
91
- */
92
-
93
77
  }
94
78
  }
95
79
 
@@ -98,6 +98,7 @@
98
98
  padding: 0;
99
99
  background: transparent;
100
100
  border: 0;
101
+ padding: 4px;
101
102
 
102
103
  &>svg {
103
104
 
@@ -112,7 +113,6 @@
112
113
  }
113
114
  }
114
115
 
115
- padding: 4px;
116
116
 
117
117
  }
118
118
 
@@ -56,6 +56,11 @@
56
56
  color: var(--treb-dropdown-color, inherit);
57
57
  border: 1px solid var(--treb-dropdown-border-color, unset);
58
58
 
59
+ text-align: left;
60
+ max-height: 10em;
61
+ overflow-y: auto;
62
+ outline: none;
63
+
59
64
  & div {
60
65
  padding: 2px;
61
66
  cursor: default;
@@ -66,12 +71,6 @@
66
71
  color: var(--treb-dropdown-selected-color, #fff);
67
72
  }
68
73
 
69
- text-align: left;
70
-
71
- max-height: 10em;
72
- overflow-y: auto;
73
- outline: none;
74
-
75
74
  }
76
75
 
77
76
  .treb-dropdown-caret.active {
@@ -30,15 +30,17 @@
30
30
  text-align: left;
31
31
  gap: .5em;
32
32
 
33
- &[hidden] {
34
- display: none;
35
- }
33
+
36
34
 
37
35
  padding: 0px 2px 12px 2px; // FIXME: use ems?
38
36
 
39
37
  max-width: 100%;
40
38
  overflow-x: hidden;
41
39
 
40
+ &[hidden] {
41
+ display: none;
42
+ }
43
+
42
44
  /** label for selection address */
43
45
  .treb-address-label {
44
46
 
@@ -207,23 +207,25 @@
207
207
  /** specific layout for column header cover */
208
208
  &.column-header-cover {
209
209
 
210
+ z-index: $z-index-header-tile-cover;
211
+
210
212
  /** style is attached when mousing over a column boundary */
211
213
  &.resize {
212
214
  cursor: col-resize;
213
215
  }
214
216
 
215
- z-index: $z-index-header-tile-cover;
216
217
  }
217
218
 
218
219
  /** specific layout for row header cover */
219
220
  &.row-header-cover {
220
221
 
222
+ z-index: $z-index-header-tile-cover;
223
+
221
224
  /** style is attached when mousing over a row boundary */
222
225
  &.resize {
223
226
  cursor: row-resize;
224
227
  }
225
228
 
226
- z-index: $z-index-header-tile-cover;
227
229
  }
228
230
 
229
231
  }
@@ -234,6 +236,9 @@
234
236
  top: 0px;
235
237
  left: 0px;
236
238
 
239
+ z-index: $z-index-annotations;
240
+ pointer-events: none;
241
+
237
242
  .annotation {
238
243
  position: absolute;
239
244
  overflow: hidden;
@@ -297,8 +302,6 @@
297
302
  cursor: move;
298
303
  }
299
304
 
300
- z-index: $z-index-annotations;
301
- pointer-events: none;
302
305
  }
303
306
 
304
307
  /** selection stacks over the grid but under the cover */
@@ -68,6 +68,28 @@
68
68
 
69
69
  line-height: normal;
70
70
 
71
+ // ---------------------------------------------------------------------------
72
+
73
+ color-scheme: var(--treb-color-scheme, unset);
74
+
75
+ font-family: $font-stack;
76
+
77
+ font-style: normal;
78
+ font-weight: normal;
79
+ color: inherit;
80
+ font-size: 14px; // ?
81
+
82
+ height: 100%;
83
+ width: 100%;
84
+
85
+ position: relative;
86
+ display: grid;
87
+ grid-template-rows: auto minmax(0, 1fr);
88
+ grid-template-columns: minmax(0, 1fr) auto;
89
+ // gap: 1em;
90
+
91
+ // ---------------------------------------------------------------------------
92
+
71
93
  div, button, input, ul, ol, li, a, textarea, svg {
72
94
 
73
95
  // maybe this is being too aggressive. we could be a little
@@ -110,23 +132,6 @@
110
132
 
111
133
  // ---------------------------------------------------------------------------
112
134
 
113
- color-scheme: var(--treb-color-scheme, unset);
114
-
115
- font-family: $font-stack;
116
-
117
- font-style: normal;
118
- font-weight: normal;
119
- color: inherit;
120
- font-size: 14px; // ?
121
-
122
- height: 100%;
123
- width: 100%;
124
-
125
- position: relative;
126
- display: grid;
127
- grid-template-rows: auto minmax(0, 1fr);
128
- grid-template-columns: minmax(0, 1fr) auto;
129
- // gap: 1em;
130
135
 
131
136
  &[animate] {
132
137
  .treb-layout-header {
@@ -354,11 +359,11 @@
354
359
  right: 0px;
355
360
  border-top-right-radius: 0px;
356
361
  border-bottom-right-radius: 0px;
362
+ background: var(--treb-toolbar-button-background, #fff);
357
363
  &::after {
358
364
  mask-image: var(--icon-chevron-left);
359
365
  -webkit-mask-image: var(--icon-chevron-left);
360
366
  }
361
- background: var(--treb-toolbar-button-background, #fff);
362
367
 
363
368
  }
364
369
  }
@@ -29,6 +29,8 @@
29
29
 
30
30
  grid-area: 3/1/4/2;
31
31
 
32
+ align-items: center;
33
+
32
34
  .treb-spreadsheet-tab-container {
33
35
  align-self: flex-start;
34
36
  overflow: hidden;
@@ -59,8 +61,6 @@
59
61
  display: none;
60
62
  }
61
63
 
62
- align-items: center;
63
-
64
64
  & .treb-spreadsheet-tabs>li {
65
65
  display: inline-block;
66
66
  position: relative;
@@ -234,6 +234,10 @@ $text-reference-color-5: rgb(254, 47, 1);
234
234
  color: var(--treb-autocomplete-tooltip-color, inherit);
235
235
  border: 1px solid var(--treb-autocomplete-tooltip-border-color, unset);
236
236
 
237
+ padding: 3px 8px;
238
+ margin: 4px 0px;
239
+ line-height: normal;
240
+
237
241
  & .active-argument {
238
242
  font-weight: 700;
239
243
  }
@@ -242,10 +246,6 @@ $text-reference-color-5: rgb(254, 47, 1);
242
246
  font-style: italic;
243
247
  }
244
248
 
245
- padding: 3px 8px;
246
- margin: 4px 0px;
247
- line-height: normal;
248
-
249
249
  }
250
250
 
251
251
  /**
@@ -167,10 +167,10 @@ $swatch-size: 18px;
167
167
  gap: 0;
168
168
 
169
169
  button[data-command] {
170
+ font-size: $split-font-size;
170
171
  &::before {
171
172
  display: none;
172
173
  }
173
- font-size: $split-font-size;
174
174
  }
175
175
 
176
176
  & > button {
@@ -727,6 +727,20 @@ export class TileRenderer {
727
727
  let pad_entry: RenderTextPart | undefined;
728
728
  let composite_width = 0;
729
729
 
730
+ // -------------------------------------------------------------------------
731
+
732
+ // moved translated boolean formatting here. I don't like this. it
733
+ // should be in sheet (at least that's where the rest of formatting
734
+ // is), but sheet doesn't have a reference to data model (and because
735
+ // sheets are owned by data model, I don't want to add a circular ref).
736
+
737
+ if (cell.rendered_type === ValueType.boolean) {
738
+ const value = cell.calculated_type ? cell.calculated : cell.value;
739
+ cell.formatted = value ? (this.model.language_model?.boolean_true || 'TRUE') : (this.model.language_model?.boolean_false || 'FALSE');
740
+ }
741
+
742
+ // -------------------------------------------------------------------------
743
+
730
744
  let override_formatting: string | undefined;
731
745
  let formatted = cell.editing ? '' : cell.formatted; // <-- empty on editing, to remove overflows
732
746