@trebco/treb 28.17.5 → 29.1.4

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 (88) hide show
  1. package/dist/treb-spreadsheet-light.mjs +12 -12
  2. package/dist/treb-spreadsheet.mjs +12 -12
  3. package/dist/treb.d.ts +121 -82
  4. package/eslint.config.js +21 -0
  5. package/package.json +6 -6
  6. package/treb-base-types/src/area.ts +4 -2
  7. package/treb-base-types/src/cell.ts +1 -1
  8. package/treb-base-types/src/cells.ts +16 -7
  9. package/treb-base-types/src/dom-utilities.ts +4 -2
  10. package/treb-base-types/src/import.ts +2 -2
  11. package/treb-base-types/src/rectangle.ts +5 -5
  12. package/treb-base-types/src/text_part.ts +7 -0
  13. package/treb-base-types/src/union.ts +6 -1
  14. package/treb-base-types/src/value-type.ts +1 -1
  15. package/treb-calculator/src/calculator.ts +114 -165
  16. package/treb-calculator/src/dag/calculation_leaf_vertex.ts +1 -2
  17. package/treb-calculator/src/dag/graph.ts +3 -3
  18. package/treb-calculator/src/dag/spreadsheet_vertex.ts +2 -2
  19. package/treb-calculator/src/dag/state_leaf_vertex.ts +2 -4
  20. package/treb-calculator/src/descriptors.ts +28 -2
  21. package/treb-calculator/src/expression-calculator.ts +25 -34
  22. package/treb-calculator/src/function-error.ts +2 -2
  23. package/treb-calculator/src/function-library.ts +16 -0
  24. package/treb-calculator/src/functions/base-functions.ts +185 -211
  25. package/treb-calculator/src/functions/checkbox.ts +0 -1
  26. package/treb-calculator/src/functions/complex-functions.ts +49 -47
  27. package/treb-calculator/src/functions/finance-functions.ts +10 -10
  28. package/treb-calculator/src/functions/function-utilities.ts +26 -0
  29. package/treb-calculator/src/functions/information-functions.ts +21 -41
  30. package/treb-calculator/src/functions/matrix-functions.ts +8 -1
  31. package/treb-calculator/src/functions/sparkline.ts +6 -4
  32. package/treb-calculator/src/functions/statistics-functions.ts +21 -17
  33. package/treb-calculator/src/functions/text-functions.ts +14 -13
  34. package/treb-calculator/src/primitives.ts +48 -37
  35. package/treb-calculator/src/utilities.ts +117 -134
  36. package/treb-charts/src/chart-functions.ts +3 -3
  37. package/treb-charts/src/chart-types.ts +42 -1
  38. package/treb-charts/src/chart-utils.ts +155 -113
  39. package/treb-charts/src/chart.ts +6 -5
  40. package/treb-charts/src/default-chart-renderer.ts +6 -5
  41. package/treb-charts/src/renderer.ts +12 -11
  42. package/treb-charts/src/util.ts +25 -36
  43. package/treb-data-model/package.json +5 -0
  44. package/{treb-grid/src/types → treb-data-model/src}/annotation.ts +2 -2
  45. package/{treb-grid/src/types → treb-data-model/src}/conditional_format.ts +20 -0
  46. package/{treb-grid/src/types → treb-data-model/src}/data_model.ts +231 -133
  47. package/treb-data-model/src/index.ts +45 -0
  48. package/{treb-grid/src/types/named_range.ts → treb-data-model/src/named.ts} +459 -376
  49. package/{treb-grid/src/types → treb-data-model/src}/sheet.ts +13 -5
  50. package/treb-data-model/src/sheet_collection.ts +114 -0
  51. package/{treb-grid/src/types → treb-data-model/src}/sheet_types.ts +6 -3
  52. package/treb-embed/modern.tsconfig.json +1 -0
  53. package/treb-embed/src/custom-element/spreadsheet-constructor.ts +2 -2
  54. package/treb-embed/src/embedded-spreadsheet.ts +125 -270
  55. package/treb-embed/src/selection-state.ts +1 -1
  56. package/treb-embed/src/toolbar-message.ts +1 -1
  57. package/treb-embed/src/types.ts +13 -5
  58. package/treb-export/src/export-worker/export-worker.ts +22 -7
  59. package/treb-export/src/export2.ts +110 -41
  60. package/treb-export/src/import2.ts +6 -5
  61. package/treb-export/src/workbook2.ts +31 -13
  62. package/treb-export/src/xml-utils.ts +5 -1
  63. package/treb-format/src/format.ts +8 -6
  64. package/treb-grid/src/editors/autocomplete.ts +2 -2
  65. package/treb-grid/src/editors/autocomplete_matcher.ts +57 -19
  66. package/treb-grid/src/editors/editor.ts +27 -25
  67. package/treb-grid/src/editors/formula_bar.ts +5 -5
  68. package/treb-grid/src/editors/overlay_editor.ts +1 -2
  69. package/treb-grid/src/index.ts +0 -11
  70. package/treb-grid/src/layout/base_layout.ts +20 -8
  71. package/treb-grid/src/layout/grid_layout.ts +2 -2
  72. package/treb-grid/src/layout/mock-layout.ts +5 -6
  73. package/treb-grid/src/render/selection-renderer.ts +2 -3
  74. package/treb-grid/src/render/tile_renderer.ts +18 -8
  75. package/treb-grid/src/types/grid.ts +96 -67
  76. package/treb-grid/src/types/grid_base.ts +76 -60
  77. package/treb-grid/src/types/grid_command.ts +3 -2
  78. package/treb-grid/src/types/grid_events.ts +12 -6
  79. package/treb-grid/src/types/tab_bar.ts +1 -2
  80. package/treb-parser/src/parser-types.ts +2 -1
  81. package/treb-parser/src/parser.ts +7 -5
  82. package/treb-utils/src/event_source.ts +1 -1
  83. package/treb-utils/src/serialize_html.ts +31 -6
  84. package/.eslintignore +0 -8
  85. package/.eslintrc.cjs +0 -168
  86. package/treb-grid/src/layout/rectangle_cache.ts +0 -86
  87. /package/{treb-grid/src/types → treb-data-model/src}/serialize_options.ts +0 -0
  88. /package/{treb-grid/src/types/grid_selection.ts → treb-data-model/src/sheet_selection.ts} +0 -0
@@ -21,7 +21,7 @@
21
21
 
22
22
  import { DOMContext } from 'treb-base-types';
23
23
  import type { Theme, Rectangle } from 'treb-base-types';
24
- import type { AutocompleteExecResult, DescriptorType } from './autocomplete_matcher';
24
+ import type { AutocompleteExecResult, FunctionDescriptor } from './autocomplete_matcher';
25
25
 
26
26
  export interface AutocompleteResult {
27
27
  handled: boolean;
@@ -29,7 +29,7 @@ export interface AutocompleteResult {
29
29
  data?: AutocompleteExecResult;
30
30
  value?: string;
31
31
  click?: boolean;
32
- type?: DescriptorType;
32
+ type?: FunctionDescriptor['type'];
33
33
  }
34
34
 
35
35
  /*
@@ -41,15 +41,25 @@ export interface ArgumentDescriptor {
41
41
  name?: string;
42
42
  }
43
43
 
44
+ /*
44
45
  export enum DescriptorType {
45
46
  Function, Token
46
47
  }
48
+ */
47
49
 
48
50
  export interface FunctionDescriptor {
49
51
  name: string;
50
52
  description?: string;
51
53
  arguments?: ArgumentDescriptor[];
52
- type?: DescriptorType;
54
+
55
+ /** switching to literals */
56
+ type?: 'function'|'token';
57
+
58
+ /** this is a named range or named expression. meaning a user/non-system name. */
59
+ named?: boolean;
60
+
61
+ /** scope refers to sheet IDs. names may be scoped. */
62
+ scope?: number;
53
63
  }
54
64
 
55
65
  export interface AutocompleteMatchData {
@@ -77,43 +87,61 @@ export interface TooltipParserResult {
77
87
  export class AutocompleteMatcher {
78
88
 
79
89
  private function_names: string[] = [];
80
-
81
- //private function_map: {[index: string]: FunctionDescriptor} = {};
90
+ // private function_map: {[index: string]: FunctionDescriptor} = {};
82
91
 
83
92
  private argument_separator = Localization.argument_separator.charCodeAt(0);
84
93
 
85
94
  /**
86
95
  * making this public (and scrubbing the type). we need it public so we
87
96
  * can check collisions. I'm not sure why it was originally private...
97
+ *
98
+ * that's backwards. this should be private, and anyone checking
99
+ * collisions should ask. also updating to modern type.
100
+ *
101
+ * OK so now a map. all names should be lower-cased for consistency (they
102
+ * can maintain canonical names inside the object).
88
103
  */
89
- public function_map: Record<string, FunctionDescriptor> = {};
104
+ private function_map: Map<string, FunctionDescriptor> = new Map();
90
105
 
91
106
  public RemoveFunctions(functions: FunctionDescriptor|FunctionDescriptor[]): void {
92
107
  if (!Array.isArray(functions)) { functions = [functions]; }
93
- let list = Object.keys(this.function_map).map((key) => this.function_map[key]);
94
- for (const func of functions) {
95
- list = list.filter(test => test.name !== func.name);
108
+ for (const entry of functions) {
109
+ this.function_map.delete(entry.name.toLowerCase());
96
110
  }
97
- this.SetFunctions(list);
111
+ this.UpdateNameList();
98
112
  }
99
113
 
100
114
  public AddFunctions(functions: FunctionDescriptor|FunctionDescriptor[]): void {
101
115
  if (!Array.isArray(functions)) { functions = [functions]; }
102
- const list = Object.keys(this.function_map).map((key) => this.function_map[key]).concat(...functions);
103
- this.SetFunctions(list);
116
+ for (const entry of functions) {
117
+ this.function_map.set(entry.name.toLowerCase(), entry);
118
+ }
119
+ this.UpdateNameList();
104
120
  }
105
121
 
106
122
  public SetFunctions(functions: FunctionDescriptor[]): void {
107
- this.function_map = {};
108
- this.function_names = functions.map((fn) => {
109
- this.function_map[fn.name.toLowerCase()] = fn;
110
- return fn.name;
111
- }).sort((a, b) => a.toLowerCase().localeCompare(b.toLowerCase()));
123
+ this.function_map.clear();
124
+ for (const entry of functions) {
125
+ this.function_map.set(entry.name.toLowerCase(), entry);
126
+ }
127
+ this.UpdateNameList();
112
128
  }
113
129
 
130
+ /**
131
+ * returns the canonical version of the name, if it exists.
132
+ * @param name
133
+ * @returns
134
+ */
114
135
  public NormalizeIdentifier(name: string): string|undefined {
115
- const identifier = this.function_map[name.toLowerCase()];
116
- return identifier ? identifier.name : undefined;
136
+ return this.function_map.get(name.toLowerCase())?.name;
137
+ }
138
+
139
+ /**
140
+ * accessor for entries. we're not stopping you from modifying
141
+ * in place, for now, but don't do that.
142
+ */
143
+ public Get(name: string): FunctionDescriptor|undefined {
144
+ return this.function_map.get(name.toLowerCase());
117
145
  }
118
146
 
119
147
  public Exec(data: AutocompleteMatchData): AutocompleteExecResult {
@@ -139,7 +167,9 @@ export class AutocompleteMatcher {
139
167
  if (match) {
140
168
  const token = match[1];
141
169
  const rex = new RegExp('^' + token.replace('.', '\\.'), 'i');
142
- const list = this.function_names.filter((name) => rex.test(name)).map((name) => this.function_map[name.toLowerCase()]);
170
+ const list = this.function_names.filter((name) =>
171
+ rex.test(name)).map((name) =>
172
+ this.function_map.get(name.toLowerCase())).filter((test): test is FunctionDescriptor => !!test);
143
173
 
144
174
  result = {
145
175
  completions: list,
@@ -154,7 +184,7 @@ export class AutocompleteMatcher {
154
184
  const parsed = this.ParseTooltip(data.text.substr(0, data.cursor));
155
185
 
156
186
  if (parsed.function) {
157
- const func = this.function_map[parsed.function.toLowerCase()];
187
+ const func = this.function_map.get(parsed.function.toLowerCase());
158
188
  if (func) {
159
189
  // if (func.canonical_name) result.tooltip = func.canonical_name;
160
190
  // else result.tooltip = tt.toUpperCase();
@@ -257,4 +287,12 @@ export class AutocompleteMatcher {
257
287
 
258
288
  }
259
289
 
290
+ /**
291
+ * we maintain a sorted list of names. this needs to get updated
292
+ * when the list changes.
293
+ */
294
+ private UpdateNameList() {
295
+ this.function_names = [...this.function_map.keys()].sort((a, b) => a.toLowerCase().localeCompare(b.toLowerCase()));
296
+ }
297
+
260
298
  }
@@ -40,13 +40,13 @@
40
40
  *
41
41
  */
42
42
 
43
- import { Area, type IArea, type ICellAddress, IsCellAddress, Localization, type Theme, Rectangle, type Cell, DOMContext } from 'treb-base-types';
43
+ import { Area, type ICellAddress, IsCellAddress, Localization, Rectangle, type Cell, DOMContext } from 'treb-base-types';
44
44
  import type { ExpressionUnit, ParseResult, UnitAddress, UnitRange } from 'treb-parser';
45
- import { Parser, QuotedSheetNameRegex } from 'treb-parser';
46
- import type { DataModel, ViewModel } from '../types/data_model';
45
+ import { Parser } from 'treb-parser';
46
+ import type { DataModel, ViewModel } from 'treb-data-model';
47
47
  import type { Autocomplete, AutocompleteResult } from './autocomplete';
48
48
  import { EventSource } from 'treb-utils';
49
- import { type AutocompleteExecResult, AutocompleteMatcher, DescriptorType } from './autocomplete_matcher';
49
+ import { type AutocompleteExecResult, AutocompleteMatcher, type FunctionDescriptor } from './autocomplete_matcher';
50
50
 
51
51
  export interface UpdateTextOptions {
52
52
  rewrite_addresses: boolean;
@@ -56,7 +56,7 @@ export interface UpdateTextOptions {
56
56
  toll_events: boolean;
57
57
  }
58
58
 
59
- type GenericEventListener = (event: Event) => any;
59
+ type GenericEventListener = (event: Event) => unknown;
60
60
 
61
61
  // ----------------
62
62
 
@@ -313,7 +313,7 @@ export class Editor<E = FormulaEditorEvent> extends EventSource<E|FormulaEditorE
313
313
  *
314
314
  * listeners moved to node descriptors so we can have multiple sets.
315
315
  */
316
- protected RegisterListener<K extends keyof HTMLElementEventMap>(descriptor: NodeDescriptor, key: K, handler: (event: HTMLElementEventMap[K]) => any) {
316
+ protected RegisterListener<K extends keyof HTMLElementEventMap>(descriptor: NodeDescriptor, key: K, handler: (event: HTMLElementEventMap[K]) => unknown) {
317
317
  descriptor.node.addEventListener(key, handler);
318
318
  if (!descriptor.listeners) {
319
319
  descriptor.listeners = new Map();
@@ -369,12 +369,12 @@ export class Editor<E = FormulaEditorEvent> extends EventSource<E|FormulaEditorE
369
369
  }
370
370
  }
371
371
 
372
- };
372
+ }
373
373
 
374
374
  /**
375
- * not sure what the ID was, we don't use it atm
375
+ *
376
376
  */
377
- public InsertReference(reference: string, id?: number) {
377
+ public InsertReference(reference: string) {
378
378
 
379
379
  if (!this.active_editor) {
380
380
  return;
@@ -392,7 +392,7 @@ export class Editor<E = FormulaEditorEvent> extends EventSource<E|FormulaEditorE
392
392
  return '';
393
393
  }
394
394
 
395
- let range = selection.getRangeAt(0);
395
+ const range = selection.getRangeAt(0);
396
396
  const text = this.active_editor.node.textContent || '';
397
397
 
398
398
  // so what we are doing here depends on where the caret is.
@@ -472,9 +472,9 @@ export class Editor<E = FormulaEditorEvent> extends EventSource<E|FormulaEditorE
472
472
  const substring = this.SubstringToCaret2(this.active_editor.node)[1];
473
473
 
474
474
  let leader = '';
475
- let trailer = '';
475
+ // let trailer = '';
476
476
 
477
- let trimmed = substring.trim();
477
+ const trimmed = substring.trim();
478
478
  if (trimmed.length) {
479
479
  const char = trimmed[trimmed.length - 1];
480
480
  if (!Editor.FormulaChars.includes(char)) {
@@ -675,12 +675,12 @@ export class Editor<E = FormulaEditorEvent> extends EventSource<E|FormulaEditorE
675
675
 
676
676
  case 'identifier':
677
677
  {
678
- const named_range = this.model.named_ranges.Get(unit.name);
679
- if (named_range) {
680
- if (named_range.count === 1) {
678
+ const named_range = this.model.GetName(unit.name, this.view.active_sheet.id); // FIXME: scoped?
679
+ if (named_range?.type === 'range') {
680
+ if (named_range.area.count === 1) {
681
681
  reference_list.push({
682
682
  type: 'address',
683
- ...named_range.start,
683
+ ...named_range.area.start,
684
684
  label: unit.name,
685
685
  position: unit.position,
686
686
  id: unit.id,
@@ -694,14 +694,14 @@ export class Editor<E = FormulaEditorEvent> extends EventSource<E|FormulaEditorE
694
694
  position: unit.position,
695
695
  id: unit.id,
696
696
  label: unit.name,
697
- ...named_range.start,
697
+ ...named_range.area.start,
698
698
  },
699
699
  end: {
700
700
  type: 'address',
701
701
  position: unit.position,
702
702
  label: unit.name,
703
703
  id: unit.id,
704
- ...named_range.end,
704
+ ...named_range.area.end,
705
705
  },
706
706
  label: unit.name,
707
707
  position: unit.position,
@@ -730,7 +730,7 @@ export class Editor<E = FormulaEditorEvent> extends EventSource<E|FormulaEditorE
730
730
 
731
731
  for (const entry of reference_list) {
732
732
 
733
- const label = this.model.AddressToLabel(entry, this.view.active_sheet);
733
+ const label = this.model.AddressToLabel(entry); // , this.view.active_sheet);
734
734
  const area = IsCellAddress(entry) ?
735
735
  new Area(entry) :
736
736
  new Area(entry.start, entry.end);
@@ -834,7 +834,7 @@ export class Editor<E = FormulaEditorEvent> extends EventSource<E|FormulaEditorE
834
834
 
835
835
  // console.info({text, substr, substr2});
836
836
 
837
- let caret_start = substring_start.length;
837
+ const caret_start = substring_start.length;
838
838
  let caret_end = substring_end.length;
839
839
 
840
840
  // this is a little hacky
@@ -1053,11 +1053,13 @@ export class Editor<E = FormulaEditorEvent> extends EventSource<E|FormulaEditorE
1053
1053
 
1054
1054
  if (!this.active_editor) return;
1055
1055
 
1056
- let type = DescriptorType.Function;
1056
+ // let type = DescriptorType.Function;
1057
+ let type: FunctionDescriptor['type'];
1058
+
1057
1059
  if (ac_result.data && ac_result.data.completions) {
1058
1060
  for (const completion of ac_result.data.completions) {
1059
1061
  if (completion.name.toLowerCase() === ac_result.value?.toLowerCase()) {
1060
- type = completion.type || DescriptorType.Function;
1062
+ type = completion.type ; // || DescriptorType.Function;
1061
1063
  break;
1062
1064
  }
1063
1065
  }
@@ -1073,11 +1075,11 @@ export class Editor<E = FormulaEditorEvent> extends EventSource<E|FormulaEditorE
1073
1075
  const start = ac_result.data?.position || 0;
1074
1076
  const end = start + (ac_result.data?.token?.length || 0);
1075
1077
 
1076
- const insertion = (type === DescriptorType.Token) ? ac_result.value : ac_result.value + '(';
1078
+ const insertion = (type === 'token') ? ac_result.value : ac_result.value + '(';
1077
1079
 
1078
1080
  const text = this.active_editor.node.textContent || '';
1079
1081
  let adjusted = text.substring(0, start) + insertion;
1080
- let caret = adjusted.length;
1082
+ const caret = adjusted.length;
1081
1083
  adjusted += text.substring(end);
1082
1084
 
1083
1085
  this.active_editor.node.textContent = adjusted;
@@ -1137,7 +1139,7 @@ export class Editor<E = FormulaEditorEvent> extends EventSource<E|FormulaEditorE
1137
1139
  // how about string concat instead of array join, it's faster in
1138
1140
  // chrome (!)
1139
1141
 
1140
- let complete = [ false, false ];
1142
+ const complete = [ false, false ];
1141
1143
 
1142
1144
  const Consume = (element: Node, range: Range) => {
1143
1145
 
@@ -19,10 +19,10 @@
19
19
  *
20
20
  */
21
21
 
22
- import type { Area, Cell, Theme } from 'treb-base-types';
22
+ // import type { Area, Cell, Theme } from 'treb-base-types';
23
23
  import { Editor, type NodeDescriptor, type FormulaEditorEvent } from './editor';
24
- import { Parser } from 'treb-parser';
25
- import type { DataModel, ViewModel } from '../types/data_model';
24
+ // import { Parser } from 'treb-parser';
25
+ import type { DataModel, ViewModel } from 'treb-data-model';
26
26
  import type { GridOptions } from '../types/grid_options';
27
27
  import { Autocomplete } from './autocomplete';
28
28
  import { DOMContext } from 'treb-base-types';
@@ -250,7 +250,7 @@ export class FormulaBar extends Editor<FormulaBar2Event|FormulaEditorEvent> {
250
250
 
251
251
  });
252
252
 
253
- this.RegisterListener(descriptor, 'focusout', (event: FocusEvent) => {
253
+ this.RegisterListener(descriptor, 'focusout', () => {
254
254
 
255
255
  if (this.selecting) {
256
256
  console.info('focusout, but selecting...');
@@ -328,7 +328,7 @@ export class FormulaBar extends Editor<FormulaBar2Event|FormulaEditorEvent> {
328
328
  // Q: do we do this in other places? we should consolidate
329
329
  // A: I don't think we do just this, usually there's additional logic for % and such
330
330
 
331
- this.address_label.addEventListener('focusin', (event) => {
331
+ this.address_label.addEventListener('focusin', () => {
332
332
 
333
333
  const doc = this.address_label.ownerDocument;
334
334
 
@@ -21,10 +21,9 @@
21
21
 
22
22
  import { Editor, type NodeDescriptor } from './editor';
23
23
  import { Area, Cell, type CellStyle, type CellValue, Rectangle, Style, type Theme, ThemeColor2 } from 'treb-base-types';
24
- import { DataModel, type ViewModel } from '../types/data_model';
24
+ import { DataModel, type ViewModel, type GridSelection } from 'treb-data-model';
25
25
  import { Autocomplete } from './autocomplete';
26
26
  import { UA } from '../util/ua';
27
- import type { GridSelection } from '../types/grid_selection';
28
27
 
29
28
  export type OverlayEditorResult = 'handled' | 'commit' | 'discard';
30
29
 
@@ -21,23 +21,12 @@
21
21
 
22
22
  export { Grid } from './types/grid';
23
23
  export { GridBase } from './types/grid_base';
24
- export { Sheet } from './types/sheet';
25
- export { DataModel, type MacroFunction, type ConnectedElementType } from './types/data_model';
26
- export type { SerializedNamedExpression, SerializedModel } from './types/data_model';
27
24
  export * from './types/grid_events';
28
- export type { SerializedSheet, FreezePane } from './types/sheet_types';
29
- export { Annotation } from './types/annotation';
30
- export type { ViewData as AnnotationViewData } from './types/annotation';
31
25
  export type { GridOptions } from './types/grid_options';
32
26
  export { CommandKey } from './types/grid_command';
33
27
  export type { Command } from './types/grid_command';
34
- export { NamedRangeCollection } from './types/named_range';
35
- export type { GridSelection } from './types/grid_selection';
36
28
  export { BorderConstants } from './types/border_constants';
37
- export type { SerializeOptions } from './types/serialize_options';
38
29
  export type { FunctionDescriptor, ArgumentDescriptor } from './editors/autocomplete_matcher';
39
30
  export { UA } from './util/ua';
40
31
  export type { SetRangeOptions } from './types/set_range_options';
41
- export type { AnnotationData, AnnotationType } from './types/annotation';
42
32
  export type { ExternalEditorConfig, DependencyList, ExternalEditorCallback } from './types/external_editor_config';
43
- export * from './types/conditional_format';
@@ -20,7 +20,7 @@
20
20
  */
21
21
 
22
22
  import { DOMContext } from 'treb-base-types';
23
- import type { DataModel, ViewModel } from '../types/data_model';
23
+ import type { DataModel, ViewModel, Annotation } from 'treb-data-model';
24
24
 
25
25
  import type { Tile } from '../types/tile';
26
26
  import type { Theme, Point, Extent, Size, Position, ICellAddress, Table, IArea } from 'treb-base-types';
@@ -42,7 +42,6 @@ import type { GridEvent } from '../types/grid_events';
42
42
 
43
43
  import type { CellValue, AnnotationLayout, Corner } from 'treb-base-types';
44
44
  import { Area as TileRange } from 'treb-base-types';
45
- import type { Annotation } from '../types/annotation';
46
45
 
47
46
  export { Area as TileRange } from 'treb-base-types';
48
47
 
@@ -282,7 +281,9 @@ export abstract class BaseLayout {
282
281
  this.dropdown_caret.setAttribute('class', 'treb-dropdown-caret');
283
282
  if (event.key === 'Enter' && this.dropdown_callback) {
284
283
  if (this.dropdown_selected) {
285
- this.dropdown_callback.call(0, (this.dropdown_selected as any).dropdown_value);
284
+ const value = this.dropdown_selected.dataset.dropdown_value;
285
+ // this.dropdown_callback.call(0, (this.dropdown_selected as any).dropdown_value);
286
+ this.dropdown_callback.call(0, value ? JSON.parse(value) : undefined);
286
287
  }
287
288
  }
288
289
  }
@@ -333,7 +334,11 @@ export abstract class BaseLayout {
333
334
  this.dropdown_caret.setAttribute('class', 'treb-dropdown-caret');
334
335
 
335
336
  if (this.dropdown_callback) {
336
- this.dropdown_callback.call(0, (target as any).dropdown_value);
337
+ // this.dropdown_callback.call(0, (target as any).dropdown_value);
338
+
339
+ const value = target.dataset.dropdown_value;
340
+ this.dropdown_callback.call(0, value ? JSON.parse(value) : undefined);
341
+
337
342
  }
338
343
  });
339
344
 
@@ -608,7 +613,7 @@ export abstract class BaseLayout {
608
613
  *
609
614
  */
610
615
  public PointToAnnotationCorner(point: Point): Corner {
611
- const address = this.PointToAddress_Grid(point, undefined, false);
616
+ const address = this.PointToAddress_Grid(point, false);
612
617
  const cell_rect = this.CellAddressToRectangle(address);
613
618
 
614
619
  return {
@@ -1417,7 +1422,14 @@ export abstract class BaseLayout {
1417
1422
  this.dropdown_selected = entry;
1418
1423
  entry.classList.add('selected');
1419
1424
  }
1420
- (entry as any).dropdown_value = value;
1425
+
1426
+ // we're attaching random data to DOM nodes here. that works, but
1427
+ // it's sloppy. I think the reason is we want to preserve type, and
1428
+ // this is simpler than any other solution.
1429
+
1430
+ // (entry as any).dropdown_value = value;
1431
+ entry.dataset.dropdown_value = JSON.stringify(value);
1432
+
1421
1433
  entry.textContent = value?.toString() || '';
1422
1434
  }
1423
1435
 
@@ -1519,7 +1531,7 @@ export abstract class BaseLayout {
1519
1531
  // in two scroll events. however in practice this is called on key events,
1520
1532
  // so it's unlikely.
1521
1533
 
1522
- let options: ScrollToOptions = {
1534
+ const options: ScrollToOptions = {
1523
1535
  behavior: smooth ? 'smooth' : 'auto',
1524
1536
  };
1525
1537
 
@@ -1662,7 +1674,7 @@ export abstract class BaseLayout {
1662
1674
  *
1663
1675
  * FIXME: implement cap_maximum parameter (not sure where we would need it)
1664
1676
  */
1665
- public PointToAddress_Grid(point: Point, cap_maximum = false, offset_freeze = true): ICellAddress {
1677
+ public PointToAddress_Grid(point: Point, offset_freeze = true): ICellAddress {
1666
1678
 
1667
1679
  // offset for freeze pane
1668
1680
 
@@ -22,7 +22,7 @@
22
22
  import { BaseLayout } from './base_layout';
23
23
  import type { Tile } from '../types/tile';
24
24
  import { DOMContext } from 'treb-base-types';
25
- import type { DataModel, ViewModel } from '../types/data_model';
25
+ import type { DataModel, ViewModel } from 'treb-data-model';
26
26
 
27
27
 
28
28
  /**
@@ -151,7 +151,7 @@ export class GridLayout extends BaseLayout {
151
151
 
152
152
  }
153
153
 
154
- protected UpdateGridTemplates(columns = true, rows = true): void {
154
+ protected UpdateGridTemplates(): void {
155
155
 
156
156
  let width = 0;
157
157
  let height = 0;
@@ -1,6 +1,5 @@
1
1
 
2
- import type { DataModel, ViewModel } from '../types/data_model';
3
- import type { Tile } from '../types/tile';
2
+ import type { DataModel, ViewModel } from 'treb-data-model';
4
3
  import { BaseLayout } from './base_layout';
5
4
 
6
5
  export class MockLayout extends BaseLayout {
@@ -9,15 +8,15 @@ export class MockLayout extends BaseLayout {
9
8
  super(model, view, true);
10
9
  }
11
10
 
12
- public InitializeInternal(container: HTMLElement, scroll_callback: () => void): void {
11
+ public InitializeInternal(): void {
13
12
 
14
13
  }
15
14
 
16
- protected UpdateGridTemplates(columns: boolean, rows: boolean): void {
15
+ protected UpdateGridTemplates(): void {
17
16
 
18
17
  }
19
18
 
20
- protected UpdateTileGridPosition(tile: Tile): void {
19
+ protected UpdateTileGridPosition(): void {
21
20
 
22
21
  }
23
22
 
@@ -25,7 +24,7 @@ export class MockLayout extends BaseLayout {
25
24
 
26
25
  }
27
26
 
28
- public ResizeCursor(resize?: 'row' | 'column' | undefined): void {
27
+ public ResizeCursor(): void {
29
28
 
30
29
  }
31
30
 
@@ -20,13 +20,12 @@
20
20
  */
21
21
 
22
22
  import type { Theme, ICellAddress } from 'treb-base-types';
23
- import { DOMContext, Rectangle } from 'treb-base-types';
23
+ import { Rectangle } from 'treb-base-types';
24
24
  import type { BaseLayout } from '../layout/base_layout';
25
25
  import type { SelectionOffset } from './svg_selection_block';
26
26
  import { SVGSelectionBlock } from './svg_selection_block';
27
- import type { GridSelection } from '../types/grid_selection';
28
27
  import { HeaderOverlay, Orientation } from './svg_header_overlay';
29
- import type { DataModel, ViewModel } from '../types/data_model';
28
+ import type { GridSelection, DataModel, ViewModel } from 'treb-data-model';
30
29
 
31
30
  // const SVGNS = 'http://www.w3.org/2000/svg';
32
31
 
@@ -32,7 +32,7 @@ import { FontMetricsCache as FontMetricsCache2 } from '../util/fontmetrics2';
32
32
  import type { FormattedString} from 'treb-parser';
33
33
  import { MDParser } from 'treb-parser';
34
34
  import type { BaseLayout, TileRange } from '../layout/base_layout';
35
- import type { DataModel, ViewModel } from '../types/data_model';
35
+ import type { DataModel, ViewModel } from 'treb-data-model';
36
36
  import type { GridOptions } from '../types/grid_options';
37
37
 
38
38
  const DEFAULT_INDENT = ' '; // two spaces in the current font
@@ -735,11 +735,17 @@ export class TileRenderer {
735
735
  let override_formatting: string | undefined;
736
736
  let formatted = cell.editing ? '' : cell.formatted; // <-- empty on editing, to remove overflows
737
737
 
738
+ // remove existing indent, if any
739
+
740
+ if (Array.isArray(formatted)) {
741
+ formatted = formatted.filter(test => test.flag !== TextPartFlag.indent);
742
+ }
743
+
738
744
  // precalculate indent as string so we can use layout
739
745
 
740
746
  let indent = '';
741
747
  let align: HorizontalAlign|undefined;
742
-
748
+
743
749
  if (style.indent) {
744
750
 
745
751
  for (let i = 0; i < style.indent; i++) {
@@ -751,9 +757,14 @@ export class TileRenderer {
751
757
  // default might be left or right based on type
752
758
 
753
759
  if (!align) {
754
- align = (cell.type === ValueType.number || cell.calculated_type === ValueType.number) ? 'right' : 'left';
760
+ align = (
761
+ cell.type === ValueType.number ||
762
+ cell.calculated_type === ValueType.number ||
763
+ cell.type === ValueType.complex ||
764
+ cell.calculated_type === ValueType.complex
765
+ ) ? 'right' : 'left';
755
766
  }
756
-
767
+
757
768
  }
758
769
 
759
770
  if (Array.isArray(formatted)) {
@@ -767,12 +778,11 @@ export class TileRenderer {
767
778
  // this is a single line, with number formatting
768
779
 
769
780
  if (indent) {
770
-
771
781
  if (align === 'right') {
772
- formatted.push({ text: indent });
782
+ formatted.push({ text: indent, flag: TextPartFlag.indent });
773
783
  }
774
- else if (align === 'left') {
775
- formatted.unshift({ text: indent });
784
+ else if (align === 'left' || typeof align === 'undefined') {
785
+ formatted.unshift({ text: indent, flag: TextPartFlag.indent });
776
786
  }
777
787
  }
778
788