@trebco/treb 28.15.0 → 28.17.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.
@@ -1846,6 +1846,18 @@ export class EmbeddedSpreadsheet<USER_DATA_TYPE = unknown> {
1846
1846
  });
1847
1847
  break;
1848
1848
 
1849
+ case 'indent':
1850
+ case 'outdent':
1851
+
1852
+ // hmmm. we should actually adjust selection cell-by-cell so
1853
+ // all entries get +1/-1 from their current level. we don't
1854
+ // want to normalize here. this is unusual.
1855
+
1856
+ // let's make a method here in case we want to use this again
1857
+
1858
+ this.grid.Indent(undefined, (event.command === 'indent') ? 1 : -1);
1859
+ break;
1860
+
1849
1861
  default:
1850
1862
  console.info('unhandled', event.command);
1851
1863
  break;
@@ -2238,11 +2250,15 @@ export class EmbeddedSpreadsheet<USER_DATA_TYPE = unknown> {
2238
2250
  }
2239
2251
 
2240
2252
  /**
2253
+ * this is a generic version that takes a callback, so we can use
2254
+ * separately from charts. however (atm) we're using leaf vertices that
2255
+ * don't actually calculate, they just indicate the dirty state. so we
2256
+ * won't have access to the result of a calculation in the callback.
2257
+ *
2241
2258
  * @internal
2242
2259
  *
2243
- * @returns an id that can be used to manage the reference
2244
2260
  */
2245
- public CreateConnectedChart(formula: string, target: HTMLElement, options: EvaluateOptions): number {
2261
+ public CreateConnectedFormula(formula: string, callback?: (instance: ConnectedElementType) => void, options: EvaluateOptions = {}): number {
2246
2262
 
2247
2263
  // FIXME: merge w/ insert annotation?
2248
2264
 
@@ -2254,15 +2270,9 @@ export class EmbeddedSpreadsheet<USER_DATA_TYPE = unknown> {
2254
2270
 
2255
2271
  if (argument_separator === ',') {
2256
2272
  this.parser.SetLocaleSettings(DecimalMarkType.Period);
2257
-
2258
- // this.parser.argument_separator = ArgumentSeparatorType.Comma;
2259
- // this.parser.decimal_mark = DecimalMarkType.Period;
2260
2273
  }
2261
2274
  else {
2262
2275
  this.parser.SetLocaleSettings(DecimalMarkType.Comma);
2263
-
2264
- // this.parser.argument_separator = ArgumentSeparatorType.Semicolon;
2265
- // this.parser.decimal_mark = DecimalMarkType.Comma;
2266
2276
  }
2267
2277
 
2268
2278
  const result = this.parser.Parse(formula);
@@ -2274,19 +2284,41 @@ export class EmbeddedSpreadsheet<USER_DATA_TYPE = unknown> {
2274
2284
  }
2275
2285
  else {
2276
2286
  console.warn("invalid formula", result.error);
2287
+ throw new Error(`invalid formula`);
2277
2288
  }
2278
2289
 
2279
- const chart = this.CreateChart();
2280
- chart.Initialize(target);
2281
-
2282
2290
  const id = this.model.AddConnectedElement({
2291
+
2283
2292
  formula,
2284
2293
 
2285
2294
  // this is circular, but I want to leave `this` bound to the sheet
2286
2295
  // instance in case we need it -- so what's a better approach? pass
2287
2296
  // in the formula explicitly, and update if we need to make changes?
2288
2297
 
2289
- update: (instance: ConnectedElementType) => {
2298
+ update: callback ? (instance: ConnectedElementType) => {
2299
+ callback.call(0, instance);
2300
+ } : undefined,
2301
+
2302
+ });
2303
+
2304
+ this.calculator.UpdateConnectedElements(this.grid.active_sheet);
2305
+ this.UpdateConnectedElements();
2306
+
2307
+ return id;
2308
+
2309
+ }
2310
+
2311
+ /**
2312
+ * @internal
2313
+ *
2314
+ * @returns an id that can be used to manage the reference
2315
+ */
2316
+ public CreateConnectedChart(formula: string, target: HTMLElement, options: EvaluateOptions): number {
2317
+
2318
+ const chart = this.CreateChart();
2319
+ chart.Initialize(target);
2320
+
2321
+ const callback = (instance: ConnectedElementType) => {
2290
2322
 
2291
2323
  const parse_result = this.parser.Parse(instance.formula);
2292
2324
 
@@ -2310,14 +2342,9 @@ export class EmbeddedSpreadsheet<USER_DATA_TYPE = unknown> {
2310
2342
 
2311
2343
  chart.Update();
2312
2344
 
2313
- },
2314
-
2315
- });
2316
-
2317
- this.calculator.UpdateConnectedElements(this.grid.active_sheet);
2318
- this.UpdateConnectedElements();
2345
+ };
2319
2346
 
2320
- return id;
2347
+ return this.CreateConnectedFormula(formula, callback, options);
2321
2348
 
2322
2349
  }
2323
2350
 
@@ -316,6 +316,11 @@ export interface EmbeddedSpreadsheetOptions {
316
316
  */
317
317
  insert_function_button?: boolean;
318
318
 
319
+ /**
320
+ * indent/outdent buttons; default false
321
+ */
322
+ indent_buttons?: boolean;
323
+
319
324
  }
320
325
 
321
326
  /**
@@ -80,6 +80,10 @@ export interface RevertIndicatorMessage {
80
80
  command: 'revert-indicator';
81
81
  }
82
82
 
83
+ export interface IndentMessage {
84
+ command: 'indent'|'outdent';
85
+ }
86
+
83
87
  export type ToolbarMessage
84
88
  = SetColorToolbarMessage
85
89
  | CommentToolbarMessage
@@ -96,6 +100,7 @@ export type ToolbarMessage
96
100
  | TableToolbarMessage
97
101
  | CommandToolbarMessage
98
102
  | StyleToolbarMessage
103
+ | IndentMessage
99
104
  | NumberFormatToolbarMessage
100
105
  | FontScaleToolbarMessage
101
106
  ;
@@ -264,6 +264,9 @@ $swatch-size: 18px;
264
264
  [data-command=justify-right] { --icon: var(--icon-text-align-right); }
265
265
  [data-command=justify-center] { --icon: var(--icon-text-align-center); }
266
266
 
267
+ [data-command=indent] { --icon: var(--icon-text-indent); }
268
+ [data-command=outdent] { --icon: var(--icon-text-outdent); }
269
+
267
270
  [data-command=align-top] { --icon: var(--icon-text-align-top); }
268
271
  [data-command=align-middle] { --icon: var(--icon-text-align-middle); }
269
272
  [data-command=align-bottom] { --icon: var(--icon-text-align-bottom); }
@@ -38,6 +38,12 @@
38
38
 
39
39
  // --- toolbar ---------------------------------------------------------------
40
40
 
41
+ /* source: bootstrap-icons-1.8.3/text-indent-left.svg */
42
+ --icon-text-indent: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-text-indent-left" viewBox="0 0 16 16"><path d="M2 3.5a.5.5 0 0 1 .5-.5h11a.5.5 0 0 1 0 1h-11a.5.5 0 0 1-.5-.5m.646 2.146a.5.5 0 0 1 .708 0l2 2a.5.5 0 0 1 0 .708l-2 2a.5.5 0 0 1-.708-.708L4.293 8 2.646 6.354a.5.5 0 0 1 0-.708M7 6.5a.5.5 0 0 1 .5-.5h6a.5.5 0 0 1 0 1h-6a.5.5 0 0 1-.5-.5m0 3a.5.5 0 0 1 .5-.5h6a.5.5 0 0 1 0 1h-6a.5.5 0 0 1-.5-.5m-5 3a.5.5 0 0 1 .5-.5h11a.5.5 0 0 1 0 1h-11a.5.5 0 0 1-.5-.5"/></svg>');
43
+
44
+ /* source: bootstrap-icons-1.8.3/text-indent-right.svg */
45
+ --icon-text-outdent: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-text-indent-right" viewBox="0 0 16 16"><path d="M2 3.5a.5.5 0 0 1 .5-.5h11a.5.5 0 0 1 0 1h-11a.5.5 0 0 1-.5-.5m10.646 2.146a.5.5 0 0 1 .708.708L11.707 8l1.647 1.646a.5.5 0 0 1-.708.708l-2-2a.5.5 0 0 1 0-.708zM2 6.5a.5.5 0 0 1 .5-.5h6a.5.5 0 0 1 0 1h-6a.5.5 0 0 1-.5-.5m0 3a.5.5 0 0 1 .5-.5h6a.5.5 0 0 1 0 1h-6a.5.5 0 0 1-.5-.5m0 3a.5.5 0 0 1 .5-.5h11a.5.5 0 0 1 0 1h-11a.5.5 0 0 1-.5-.5"/></svg>');
46
+
41
47
  /* source: bootstrap-icons-1.8.3/text-left.svg */
42
48
  --icon-text-align-left: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' width='16' height='16' fill='currentColor' viewBox='0 0 16 16'> <path fill-rule='evenodd' d='M2 12.5a.5.5 0 0 1 .5-.5h7a.5.5 0 0 1 0 1h-7a.5.5 0 0 1-.5-.5zm0-3a.5.5 0 0 1 .5-.5h11a.5.5 0 0 1 0 1h-11a.5.5 0 0 1-.5-.5zm0-3a.5.5 0 0 1 .5-.5h7a.5.5 0 0 1 0 1h-7a.5.5 0 0 1-.5-.5zm0-3a.5.5 0 0 1 .5-.5h11a.5.5 0 0 1 0 1h-11a.5.5 0 0 1-.5-.5z'/> </svg>");
43
49
 
@@ -93,7 +93,7 @@ export class Importer {
93
93
  ref?: string;
94
94
  },
95
95
  };
96
- }, // ElementTree.Element,
96
+ },
97
97
  shared_formulae: SharedFormulaMap,
98
98
  arrays: RangeType[],
99
99
  merges: RangeType[],
@@ -117,11 +117,9 @@ export class Importer {
117
117
  // console.info(element);
118
118
 
119
119
  let value: undefined | number | boolean | string;
120
- // let type: ValueType = ValueType.undefined;
121
120
  let type: SerializedValueType = 'undefined';
122
121
 
123
122
  let calculated_value: undefined | number | boolean | string;
124
- // let calculated_type: ValueType = ValueType.undefined;
125
123
  let calculated_type: SerializedValueType = 'undefined';
126
124
 
127
125
  // QUESTIONS:
@@ -397,7 +395,6 @@ export class Importer {
397
395
  if (rule.formula) {
398
396
 
399
397
  if (typeof rule.formula !== 'string') {
400
- console.info("conditional expression", {rule});
401
398
  if (rule.formula.t$) {
402
399
 
403
400
  // the only case (to date) we've seen here is that the attribute
@@ -407,6 +404,10 @@ export class Importer {
407
404
  rule.formula = rule.formula.t$;
408
405
 
409
406
  }
407
+ else {
408
+ console.info("unexpected conditional expression", {rule});
409
+
410
+ }
410
411
  }
411
412
 
412
413
  let style = {};
@@ -536,18 +537,6 @@ export class Importer {
536
537
  }
537
538
  }
538
539
 
539
- /*
540
- const area = sheet.TranslateAddress(element.a$.sqref);
541
- if (element.cfRule) {
542
- const rules = Array.isArray(element.cfRule) ? element.cfRule : [element.cfRule];
543
- for (const rule of rules) {
544
- const format = this.ParseConditionalFormat(area, rule);
545
- if (format) {
546
- conditional_formats.push(format);
547
- }
548
- }
549
- }
550
- */
551
540
  }
552
541
  }
553
542
 
@@ -669,7 +669,7 @@ export class StyleCache {
669
669
 
670
670
  // indent
671
671
 
672
- props.indent = xf.indent;
672
+ props.indent = (typeof xf.indent === 'string') ? Number(xf.indent) : xf.indent;
673
673
 
674
674
  // wrap
675
675
 
@@ -722,6 +722,19 @@ export class StyleCache {
722
722
 
723
723
  /** map all cell xfs to styles; retain order */
724
724
  public CellXfToStyles(): CellStyle[] {
725
+
726
+ /*
727
+ const mapped = this.cell_xfs.map((xf, index) => {
728
+ const style = this.CellXfToStyle(xf);
729
+ if (style.font_size?.value && style.font_size.value > 200) {
730
+ console.info({index, fs: JSON.stringify(style.font_size), style, xf});
731
+ console.info(this);
732
+ }
733
+ return style;
734
+ });
735
+ return mapped;
736
+ */
737
+
725
738
  return this.cell_xfs.map((xf) => this.CellXfToStyle(xf));
726
739
  }
727
740
 
@@ -1174,40 +1187,14 @@ export class StyleCache {
1174
1187
 
1175
1188
 
1176
1189
  this.borders = composite.map(element => {
1177
-
1178
1190
  const border: BorderStyle = JSON.parse(JSON.stringify(default_border));
1179
1191
 
1180
- /*
1181
- // we're relying on these being empty strings -> falsy, not a good look
1182
-
1183
- if (element.left) {
1184
- // border.left.style = element.left.a$.style;
1185
- // border.left.color = Number(element.left.color?.a$?.indexed);
1186
- }
1187
-
1188
- if (element.right) {
1189
- // border.right.style = element.right.a$.style;
1190
- // border.right.color = Number(element.right.color?.a$?.indexed);
1191
- }
1192
-
1193
- if (element.top) {
1194
- // border.top.style = element.top.a$.style;
1195
- // border.top.color = Number(element.top.color?.a$?.indexed);
1196
- }
1197
-
1198
- if (element.bottom) {
1199
- // border.bottom.style = element.bottom.a$.style;
1200
- // border.bottom.color = Number(element.bottom.color?.a$?.indexed);
1201
- }
1202
- */
1203
-
1204
1192
  ElementToBorderEdge(element.left, border.left);
1205
1193
  ElementToBorderEdge(element.right, border.right);
1206
1194
  ElementToBorderEdge(element.top, border.top);
1207
1195
  ElementToBorderEdge(element.bottom, border.bottom);
1208
1196
 
1209
1197
  return border;
1210
-
1211
1198
  });
1212
1199
 
1213
1200
  // ---
@@ -1313,6 +1300,7 @@ export class StyleCache {
1313
1300
  if (element.color.a$?.rgb) {
1314
1301
  font.color_argb = element.color.a$.rgb;
1315
1302
  }
1303
+
1316
1304
  }
1317
1305
 
1318
1306
  return font;
@@ -23,7 +23,8 @@ import type { ICellAddress,
23
23
  PreparedText, RenderTextPart,
24
24
  Cell, Size,
25
25
  CellStyle,
26
- Theme} from 'treb-base-types';
26
+ Theme,
27
+ HorizontalAlign} from 'treb-base-types';
27
28
  import { TextPartFlag, Style, ValueType, Area, Rectangle, ThemeColor, ThemeColor2 } from 'treb-base-types';
28
29
 
29
30
  import type { Tile } from '../types/tile';
@@ -146,12 +147,22 @@ export class TileRenderer {
146
147
  // flush all, mark dirty and drop areas.
147
148
 
148
149
  const cells = this.view.active_sheet.cells;
150
+
151
+ for (const cell of cells.Iterate()) {
152
+ if (cell.renderer_data?.overflowed) {
153
+ cell.renderer_data = undefined;
154
+ cell.render_clean[this.view.view_index] = false;
155
+ }
156
+ }
157
+
158
+ /*
149
159
  cells.IterateAll(cell => {
150
160
  if (cell.renderer_data?.overflowed) {
151
161
  cell.renderer_data = undefined;
152
162
  cell.render_clean[this.view.view_index] = false;
153
163
  }
154
164
  });
165
+ */
155
166
 
156
167
  for (const overflow_area of this.overflow_areas) {
157
168
  overflow_area.tile.dirty = true;
@@ -727,10 +738,22 @@ export class TileRenderer {
727
738
  // precalculate indent as string so we can use layout
728
739
 
729
740
  let indent = '';
741
+ let align: HorizontalAlign|undefined;
742
+
730
743
  if (style.indent) {
744
+
731
745
  for (let i = 0; i < style.indent; i++) {
732
746
  indent += DEFAULT_INDENT;
733
747
  }
748
+
749
+ align = style.horizontal_align;
750
+
751
+ // default might be left or right based on type
752
+
753
+ if (!align) {
754
+ align = (cell.type === ValueType.number || cell.calculated_type === ValueType.number) ? 'right' : 'left';
755
+ }
756
+
734
757
  }
735
758
 
736
759
  if (Array.isArray(formatted)) {
@@ -744,12 +767,13 @@ export class TileRenderer {
744
767
  // this is a single line, with number formatting
745
768
 
746
769
  if (indent) {
747
- if (style.horizontal_align === 'left') {
748
- formatted.unshift({ text: indent });
749
- }
750
- else if (style.horizontal_align === 'right') {
770
+
771
+ if (align === 'right') {
751
772
  formatted.push({ text: indent });
752
773
  }
774
+ else if (align === 'left') {
775
+ formatted.unshift({ text: indent });
776
+ }
753
777
  }
754
778
 
755
779
  for (const part of formatted) {
@@ -953,12 +977,12 @@ export class TileRenderer {
953
977
  });
954
978
 
955
979
  if (style.indent) {
956
- if (style.horizontal_align === 'left') {
957
- line_string.unshift({ text: indent, hidden: false, width: indent_width });
958
- }
959
- else if (style.horizontal_align === 'right') {
980
+ if (align === 'right') {
960
981
  line_string.push({ text: indent, hidden: false, width: indent_width });
961
982
  }
983
+ else if (align === 'left') {
984
+ line_string.unshift({ text: indent, hidden: false, width: indent_width });
985
+ }
962
986
  }
963
987
 
964
988
  strings.push(line_string);
@@ -977,12 +1001,12 @@ export class TileRenderer {
977
1001
  const parts: RenderTextPart[] = [];
978
1002
 
979
1003
  if (style.indent) {
980
- if (style.horizontal_align === 'left') {
981
- line.unshift({ text: indent });
982
- }
983
- else if (style.horizontal_align === 'right') {
1004
+ if (align === 'right') {
984
1005
  line.push({ text: indent });
985
1006
  }
1007
+ else if (align === 'left') {
1008
+ line.unshift({ text: indent });
1009
+ }
986
1010
  }
987
1011
 
988
1012
  let line_width = 0;
@@ -978,7 +978,7 @@ export class Grid extends GridBase {
978
978
 
979
979
  const validated = this.model.named_ranges.ValidateNamed(name);
980
980
  if (!validated) {
981
- console.warn(`invalid name: ${name}`);
981
+ console.warn(`invalid name: ${name}`, import_data.names[name]);
982
982
  continue;
983
983
  }
984
984
 
@@ -2044,6 +2044,21 @@ export class Grid extends GridBase {
2044
2044
  }
2045
2045
  */
2046
2046
 
2047
+ public Indent(area?: Area, delta = 0): void {
2048
+
2049
+ if (!area) {
2050
+ if (this.primary_selection.empty) { return; }
2051
+ area = this.primary_selection.area;
2052
+ }
2053
+
2054
+ this.ExecCommand({
2055
+ key: CommandKey.Indent,
2056
+ area,
2057
+ delta,
2058
+ });
2059
+
2060
+ }
2061
+
2047
2062
  /** updated API method, probably change the name */
2048
2063
  public ApplyBorders2(area?: Area, borders: BorderConstants = BorderConstants.None, color?: Color, width = 1): void {
2049
2064
 
@@ -4972,6 +4987,15 @@ export class Grid extends GridBase {
4972
4987
  }
4973
4988
  }
4974
4989
  else if (array) {
4990
+
4991
+ for (const cell of this.active_sheet.cells.Iterate(selection.area, false)) {
4992
+ if (cell.area) {
4993
+ this.Error(ErrorCode.array);
4994
+ return;
4995
+ }
4996
+ }
4997
+
4998
+ /*
4975
4999
  let existing_array = false;
4976
5000
  this.active_sheet.cells.Apply(selection.area, (element: Cell) => {
4977
5001
  if (element.area) {
@@ -4982,6 +5006,8 @@ export class Grid extends GridBase {
4982
5006
  this.Error(ErrorCode.array);
4983
5007
  return;
4984
5008
  }
5009
+ */
5010
+
4985
5011
  }
4986
5012
 
4987
5013
  if (cell.validation && cell.validation.error) {
@@ -6099,9 +6125,28 @@ export class Grid extends GridBase {
6099
6125
  let real_area = this.active_sheet.RealArea(area);
6100
6126
  if (!target) target = real_area.start;
6101
6127
 
6102
- let recheck = true;
6128
+ // there has to be a better way to do this...
6129
+
6130
+ // the operation here is composing the selection by adding all merge
6131
+ // areas. the issue is that when you do that, you might be adding other
6132
+ // cells that are not in the merge area (because the selection has to
6133
+ // be a rectangle) and one of those new cells might itself be merged.
6134
+ // so we need to iterate. there's a lot of duplication here, though.
6135
+
6136
+ recheck_loop: while (true) {
6137
+ for (const cell of this.active_sheet.cells.Iterate(real_area, false)) {
6138
+ if (cell.merge_area && !real_area.ContainsArea(cell.merge_area)) {
6139
+ area.ConsumeArea(cell.merge_area);
6140
+ real_area = this.active_sheet.RealArea(area);
6141
+ continue recheck_loop;
6142
+ }
6143
+ }
6144
+ break;
6145
+ }
6103
6146
 
6104
- // there has to be a better way to do this...
6147
+ /*
6148
+
6149
+ let recheck = true;
6105
6150
 
6106
6151
  while (recheck) {
6107
6152
  recheck = false;
@@ -6113,6 +6158,7 @@ export class Grid extends GridBase {
6113
6158
  }
6114
6159
  });
6115
6160
  }
6161
+ */
6116
6162
 
6117
6163
  selection.area = new Area({ ...area.start, sheet_id: this.active_sheet.id }, area.end);
6118
6164
  if (target) {