@trebco/treb 27.2.1 → 27.5.3

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.
@@ -60,7 +60,7 @@ import {
60
60
 
61
61
  import { Yield, SerializeHTML } from 'treb-utils';
62
62
  import type { ParseResult as ParseResult2 } from 'treb-format';
63
- import { NumberFormatCache, LotusDate, ValueParser, Hints, NumberFormat } from 'treb-format';
63
+ import { NumberFormatCache, LotusDate, ValueParser, type Hints, NumberFormat } from 'treb-format';
64
64
  import { SelectionRenderer } from '../render/selection-renderer';
65
65
 
66
66
  import { TabBar } from './tab_bar';
@@ -114,6 +114,8 @@ import { GridBase } from './grid_base';
114
114
  import type { SetRangeOptions } from './set_range_options';
115
115
  import type { ClipboardCellData } from './clipboard_data';
116
116
 
117
+ import type { ExternalEditorType } from './external_editor';
118
+
117
119
  interface DoubleClickData {
118
120
  timeout?: number;
119
121
  address?: ICellAddress;
@@ -125,6 +127,7 @@ enum EditingState {
125
127
  FormulaBar = 2,
126
128
  }
127
129
 
130
+
128
131
  export class Grid extends GridBase {
129
132
 
130
133
  // --- public members --------------------------------------------------------
@@ -263,6 +266,13 @@ export class Grid extends GridBase {
263
266
 
264
267
  private select_argument = false; // temp, WIP
265
268
 
269
+ /**
270
+ * not sure what select_argument was for (possibly outside client), but
271
+ * we're formalizing the concept of external selection to support outside
272
+ * tooling.
273
+ */
274
+ private external_editor: Partial<ExternalEditorType> | undefined;
275
+
266
276
  /**
267
277
  * flag indicating we're resizing, or hovering over a resize.
268
278
  * we use this so we know what to do when we see a click on the headers.
@@ -1644,6 +1654,33 @@ export class Grid extends GridBase {
1644
1654
  this.RenderSelections();
1645
1655
  }
1646
1656
 
1657
+ /**
1658
+ * set or remove external editor. this is not an accessor because I don't
1659
+ * want to have to use a duplicate internal field, it's clumsy and this
1660
+ * class isn't public-facing so it's not super important.
1661
+ */
1662
+ public ExternalEditor(external_editor: Partial<ExternalEditorType>|undefined) {
1663
+
1664
+ this.external_editor = external_editor;
1665
+
1666
+ if (external_editor) {
1667
+ if (external_editor.dependencies) {
1668
+
1669
+ // FIXME: this is getting messy and we're doing it twice, consolidate
1670
+
1671
+ this.HighlightDependencies(external_editor.dependencies.filter(
1672
+ <T>(entry: T|undefined): entry is T => !!entry).map(reference =>
1673
+ IsCellAddress(reference) ? new Area(reference) : new Area(reference.start, reference.end)));
1674
+ }
1675
+ }
1676
+ else {
1677
+ this.ClearAdditionalSelections();
1678
+ this.RenderSelections(true);
1679
+ }
1680
+
1681
+ }
1682
+
1683
+
1647
1684
  /** API method */
1648
1685
  public SelectRange(range?: Area): void {
1649
1686
  this.Select(this.primary_selection, range);
@@ -3984,6 +4021,9 @@ export class Grid extends GridBase {
3984
4021
  if (this.overlay_editor?.editing) {
3985
4022
  // ...
3986
4023
  }
4024
+ else if (this.external_editor) {
4025
+ // ...
4026
+ }
3987
4027
  else if (this.select_argument) {
3988
4028
  // ...
3989
4029
  }
@@ -4237,8 +4277,6 @@ export class Grid extends GridBase {
4237
4277
  // if this is a single merged block, we want to insert it as the
4238
4278
  // root cell and not the range.
4239
4279
 
4240
- // let label = selection.area.spreadsheet_label;
4241
-
4242
4280
  const data = this.active_sheet.CellData(selection.area.start);
4243
4281
  const target = new Area(data.merge_area ? data.merge_area.start : selection.target);
4244
4282
 
@@ -4246,13 +4284,12 @@ export class Grid extends GridBase {
4246
4284
 
4247
4285
  if (!label) {
4248
4286
 
4249
- // label = Area.CellAddressToLabel(target.start);
4250
4287
  label = selection.area.spreadsheet_label;
4251
4288
  if (data.merge_area && data.merge_area.Equals(selection.area)) {
4252
4289
  label = Area.CellAddressToLabel(data.merge_area.start);
4253
4290
  }
4254
4291
 
4255
- if (this.active_sheet.id !== this.editing_cell.sheet_id) {
4292
+ if (this.external_editor || this.active_sheet.id !== this.editing_cell.sheet_id) {
4256
4293
  const name = this.active_sheet.name;
4257
4294
 
4258
4295
  if (QuotedSheetNameRegex.test(name)) {
@@ -4265,12 +4302,25 @@ export class Grid extends GridBase {
4265
4302
 
4266
4303
  }
4267
4304
 
4305
+ // the external editor should just handle normal select events
4306
+ // for now, we might update that in the future.
4307
+
4268
4308
  if (this.overlay_editor?.editing && this.overlay_editor.selecting) {
4269
4309
  this.overlay_editor.InsertReference(label, 0);
4270
4310
  }
4271
4311
  else if (this.formula_bar && this.formula_bar.selecting) {
4272
4312
  this.formula_bar.InsertReference(label, 0);
4273
4313
  }
4314
+ else if (this.external_editor) {
4315
+ if (this.external_editor.update) {
4316
+ const result = this.external_editor.update.call(0, label);
4317
+ if (result && Array.isArray(result)) {
4318
+ this.HighlightDependencies(
4319
+ result.filter(<T>(entry: T|undefined): entry is T => !!entry).map(reference =>
4320
+ IsCellAddress(reference) ? new Area(reference) : new Area(reference.start, reference.end)));
4321
+ }
4322
+ }
4323
+ }
4274
4324
  else if (this.select_argument) {
4275
4325
  this.grid_events.Publish({
4276
4326
  type: 'alternate-selection',
@@ -4288,6 +4338,7 @@ export class Grid extends GridBase {
4288
4338
  private SelectingArgument() {
4289
4339
  return (this.overlay_editor?.editing && this.overlay_editor?.selecting)
4290
4340
  || (this.formula_bar && this.formula_bar.selecting)
4341
+ || (this.external_editor)
4291
4342
  || (this.select_argument);
4292
4343
  }
4293
4344
 
@@ -4911,6 +4962,12 @@ export class Grid extends GridBase {
4911
4962
  if (!cell.style || !cell.style.number_format || NumberFormatCache.Equals(cell.style.number_format, 'General')) {
4912
4963
  const func = formula_parse_result.expression.name.toLowerCase();
4913
4964
  let number_format: string|undefined;
4965
+
4966
+ // FIXME: these should be defined on the functions themselves,
4967
+ // so we don't have to maintain a list. that also implies we
4968
+ // need the list of functions, which we don't have atm? maybe
4969
+ // through the AC instance? (...)
4970
+
4914
4971
  switch (func) {
4915
4972
  case 'today':
4916
4973
  number_format = 'Short Date';
@@ -4928,25 +4985,51 @@ export class Grid extends GridBase {
4928
4985
  }
4929
4986
  }
4930
4987
  if (formula_parse_result.dependencies) {
4988
+
4989
+ // this was set up to just use the first format we found.
4990
+ // updating to change priority -- if the first one is a
4991
+ // percentage formula, look for another one before using
4992
+ // the percentage. this is almost always what you want.
4993
+
4994
+ let found_number_format: string|undefined = undefined;
4995
+
4931
4996
  const list = formula_parse_result.dependencies;
4932
4997
  for (const key of Object.keys(list.addresses)) {
4933
4998
  const address = list.addresses[key];
4934
4999
  if (this.active_sheet.HasCellStyle({ ...address })) {
5000
+
5001
+ // FIXME: this should not be active_sheet
5002
+
4935
5003
  const test = this.active_sheet.CellData({ ...address });
4936
5004
  if (test.style && test.style.number_format) {
4937
- const style: CellStyle = {
4938
- number_format: test.style.number_format,
4939
- };
4940
- // if (array) this.model.sheet.UpdateAreaStyle(selection.area, style, true, true);
4941
- // else this.model.sheet.UpdateCellStyle(target, style, true, true);
4942
- commands.push({
4943
- key: CommandKey.UpdateStyle,
4944
- area: array ? selection.area : target, style, delta: true
4945
- });
5005
+ if (!found_number_format || /%/.test(found_number_format)) {
5006
+
5007
+ // convert to a string format if it's symbolic. that
5008
+ // is purely so we can check for a %. FIXME: I don't
5009
+ // like the name of this method (Translate)
5010
+
5011
+ found_number_format = NumberFormatCache.Translate(test.style.number_format);
5012
+ if (!/%/.test(found_number_format)) {
5013
+ break;
5014
+ }
5015
+
5016
+ }
4946
5017
  }
4947
- break;
4948
5018
  }
4949
5019
  }
5020
+
5021
+ if (found_number_format) {
5022
+
5023
+ const style: CellStyle = {
5024
+ number_format: found_number_format,
5025
+ };
5026
+
5027
+ commands.push({
5028
+ key: CommandKey.UpdateStyle,
5029
+ area: array ? selection.area : target, style, delta: true
5030
+ });
5031
+ }
5032
+
4950
5033
  }
4951
5034
  }
4952
5035
  }
@@ -4988,7 +5071,7 @@ export class Grid extends GridBase {
4988
5071
  // const text = value.toString();
4989
5072
 
4990
5073
  let number_format = '';
4991
- const hints = parse_result.hints || Hints.None;
5074
+ const hints: Hints = parse_result.hints || {};
4992
5075
 
4993
5076
  // be stricter about number format. don't implicitly /change/
4994
5077
  // the number format (you can /set/, but don't /change/).
@@ -4999,24 +5082,19 @@ export class Grid extends GridBase {
4999
5082
 
5000
5083
  if (!cell.style || !cell.style.number_format || NumberFormatCache.Equals(cell.style.number_format, 'General')) {
5001
5084
 
5002
- // tslint:disable-next-line:no-bitwise
5003
- if (hints & Hints.Date) {
5085
+ if (hints.Date) {
5004
5086
  number_format = 'Short Date';
5005
5087
  }
5006
- // tslint:disable-next-line:no-bitwise
5007
- else if (hints & Hints.Exponential) {
5088
+ else if (hints.Exponential) {
5008
5089
  number_format = 'Exponential';
5009
5090
  }
5010
- // tslint:disable-next-line:no-bitwise
5011
- else if (hints & Hints.Percent) {
5091
+ else if (hints.Percent) {
5012
5092
  number_format = 'Percent';
5013
5093
  }
5014
- // tslint:disable-next-line:no-bitwise
5015
- else if (hints & Hints.Currency) {
5094
+ else if (hints.Currency) {
5016
5095
  number_format = 'Currency';
5017
5096
  }
5018
- // tslint:disable-next-line:no-bitwise
5019
- else if ((hints & Hints.Grouping) || (hints & Hints.Parens)) {
5097
+ else if (hints.Grouping || hints.Parens) {
5020
5098
  number_format = 'Accounting';
5021
5099
  }
5022
5100