@trebco/treb 30.6.2 → 30.8.2

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.
@@ -246,8 +246,8 @@ export class EmbeddedSpreadsheet<USER_DATA_TYPE = unknown> {
246
246
  /* * @internal */
247
247
  // public static export_worker_text = '';
248
248
 
249
- /** @internal */
250
- public static treb_embedded_script_path = '';
249
+ /* * @internal */
250
+ // public static treb_embedded_script_path = '';
251
251
 
252
252
  /**
253
253
  * @internal
@@ -4840,6 +4840,53 @@ export class EmbeddedSpreadsheet<USER_DATA_TYPE = unknown> {
4840
4840
  return reject(event.data.error || 'unknown error');
4841
4841
  }
4842
4842
 
4843
+ if (this.parser.decimal_mark !== DecimalMarkType.Period) {
4844
+
4845
+ // console.info("IMPORT WARNING 2", event.data);
4846
+
4847
+ // FIXME: unify w/ the convert locale method
4848
+
4849
+ const target_decimal_mark = this.parser.decimal_mark;
4850
+ const target_argument_separator = this.parser.argument_separator;
4851
+ this.parser.Save();
4852
+ this.parser.SetLocaleSettings(DecimalMarkType.Period);
4853
+
4854
+ const translate = (formula: string): string | undefined => {
4855
+ const parse_result = this.parser.Parse(formula);
4856
+ if (!parse_result.expression) { return undefined; }
4857
+ return '=' + this.parser.Render(
4858
+ parse_result.expression, {
4859
+ missing: '',
4860
+ convert_decimal: target_decimal_mark,
4861
+ convert_argument_separator: target_argument_separator,
4862
+ });
4863
+ };
4864
+
4865
+ for (const named of event.data.results) {
4866
+ named.expression = translate(named.expression);
4867
+ }
4868
+
4869
+ for (const sheet of event.data.results.sheets || []) {
4870
+
4871
+ for (const cell of sheet.cells || []) {
4872
+ if (cell.type === 'formula' && cell.value) {
4873
+ cell.value = translate(cell.value);
4874
+ }
4875
+ }
4876
+
4877
+ if (sheet.annotations){
4878
+ for (const annotation of sheet.annotations) {
4879
+ if (annotation.formula) {
4880
+ annotation.formula = translate(annotation.formula);
4881
+ }
4882
+ }
4883
+ }
4884
+ }
4885
+
4886
+ this.parser.Restore();
4887
+
4888
+ }
4889
+
4843
4890
  this.grid.FromImportData(event.data.results);
4844
4891
 
4845
4892
  this.ResetInternal();
@@ -5914,6 +5961,15 @@ export class EmbeddedSpreadsheet<USER_DATA_TYPE = unknown> {
5914
5961
  }
5915
5962
  }
5916
5963
 
5964
+ if (data.named) {
5965
+ for (const named of data.named) {
5966
+ const translated = translate(named.expression);
5967
+ if (translated) {
5968
+ named.expression = translated;
5969
+ }
5970
+ }
5971
+ }
5972
+
5917
5973
  if (data.sheet_data) {
5918
5974
 
5919
5975
  const sheets = Array.isArray(data.sheet_data) ? data.sheet_data : [data.sheet_data];
@@ -27,10 +27,10 @@ import Base64JS from 'base64-js';
27
27
  import type { AnchoredChartDescription, AnchoredImageDescription, AnchoredTextBoxDescription} from './workbook2';
28
28
  import { ChartType, ConditionalFormatOperators, Workbook } from './workbook2';
29
29
  import type { ParseResult } from 'treb-parser';
30
- import { Parser } from 'treb-parser';
30
+ import { DecimalMarkType, Parser } from 'treb-parser';
31
31
  import type { RangeType, AddressType, HyperlinkType } from './address-type';
32
32
  import { is_range, ShiftRange, InRange, is_address } from './address-type';
33
- import type { ImportedSheetData, AnchoredAnnotation, CellParseResult, AnnotationLayout, Corner as LayoutCorner, IArea, GradientStop, Color, HTMLColor, ThemeColor } from 'treb-base-types';
33
+ import { type ImportedSheetData, type AnchoredAnnotation, type CellParseResult, type AnnotationLayout, type Corner as LayoutCorner, type IArea, type GradientStop, type Color, type HTMLColor, type ThemeColor, Area } from 'treb-base-types';
34
34
  import type { SerializedValueType } from 'treb-base-types';
35
35
  import type { Sheet} from './workbook-sheet2';
36
36
  import { VisibleState } from './workbook-sheet2';
@@ -341,7 +341,7 @@ export class Importer {
341
341
 
342
342
  }
343
343
 
344
- public ParseConditionalFormat(address: RangeType|AddressType, rule: any): ConditionalFormat|undefined {
344
+ public ParseConditionalFormat(address: RangeType|AddressType, rule: any): ConditionalFormat|ConditionalFormat[]|undefined {
345
345
 
346
346
  const area = this.AddressToArea(address);
347
347
  const operators = ConditionalFormatOperators;
@@ -365,11 +365,13 @@ export class Importer {
365
365
  area,
366
366
  style,
367
367
  unique: (rule.a$.type === 'uniqueValues'),
368
+ priority: rule.a$.priority ? Number(rule.a$.priority) : undefined,
369
+
368
370
  };
369
371
  }
370
372
 
371
373
  case 'cellIs':
372
- if (rule.a$.operator && rule.formula) {
374
+ if (rule.a$.operator && (rule.formula || typeof rule.formula === 'number')) {
373
375
  let style = {};
374
376
 
375
377
  if (rule.a$.dxfId) {
@@ -379,10 +381,26 @@ export class Importer {
379
381
  }
380
382
  }
381
383
 
384
+ if (rule.a$.operator === 'between') {
385
+ if (Array.isArray(rule.formula) && rule.formula.length === 2
386
+ && typeof rule.formula[0] === 'number' && typeof rule.formula[1] === 'number') {
387
+
388
+ return {
389
+ type: 'cell-match',
390
+ expression: '',
391
+ between: rule.formula, // special case? ugh
392
+ area,
393
+ style,
394
+ priority: rule.a$.priority ? Number(rule.a$.priority) : undefined,
395
+ };
396
+
397
+ }
398
+ }
399
+
382
400
  const operator = operators[rule.a$.operator || ''];
383
401
 
384
402
  if (!operator) {
385
- console.info('unhandled cellIs operator:', rule.a$.operator);
403
+ console.info('unhandled cellIs operator:', rule.a$.operator, {rule});
386
404
  }
387
405
  else {
388
406
  return {
@@ -390,10 +408,14 @@ export class Importer {
390
408
  expression: operator + ' ' + rule.formula,
391
409
  area,
392
410
  style,
411
+ priority: rule.a$.priority ? Number(rule.a$.priority) : undefined,
393
412
  };
394
413
  }
395
414
 
396
415
  }
416
+ else {
417
+ console.info("miss?", rule);
418
+ }
397
419
  break;
398
420
 
399
421
  case 'containsErrors':
@@ -427,11 +449,51 @@ export class Importer {
427
449
  }
428
450
  }
429
451
 
452
+ if (rule.a$.type === 'expression' && (area.start.row !== area.end.row || area.start.column !== area.end.column)) {
453
+
454
+ // (1) this is only required if there are relative references
455
+ // in the formula. so we could check and short-circuit.
456
+ //
457
+ // (2) I'd like to find a way to apply this as a single formula,
458
+ // so there's only one rule required.
459
+
460
+ this.parser.Save();
461
+ this.parser.SetLocaleSettings(DecimalMarkType.Period);
462
+
463
+ const list: ConditionalFormat[] = [];
464
+ const a2 = new Area(area.start, area.end);
465
+
466
+ const parse_result = this.parser.Parse(rule.formula);
467
+ if (parse_result.expression) {
468
+ for (const cell of a2) {
469
+ const f = this.parser.Render(parse_result.expression, {
470
+ missing: '',
471
+ offset: { rows: cell.row - area.start.row, columns: cell.column - area.start.column }
472
+ });
473
+
474
+ list.push({
475
+ type: 'expression',
476
+ expression: f,
477
+ style,
478
+ area: { start: cell, end: cell },
479
+ priority: rule.a$.priority ? Number(rule.a$.priority) : undefined,
480
+ })
481
+
482
+ // console.info(f);
483
+ }
484
+ }
485
+
486
+ this.parser.Restore();
487
+ return list;
488
+
489
+ }
490
+
430
491
  return {
431
492
  type: 'expression',
432
493
  expression: rule.formula,
433
494
  area,
434
495
  style,
496
+ priority: rule.a$.priority ? Number(rule.a$.priority) : undefined,
435
497
  };
436
498
 
437
499
  }
@@ -479,6 +541,8 @@ export class Importer {
479
541
  stops,
480
542
  color_space: 'RGB',
481
543
  area,
544
+ priority: rule.a$.priority ? Number(rule.a$.priority) : undefined,
545
+
482
546
  };
483
547
 
484
548
  }
@@ -545,7 +609,12 @@ export class Importer {
545
609
  for (const rule of rules) {
546
610
  const format = this.ParseConditionalFormat(area, rule);
547
611
  if (format) {
548
- conditional_formats.push(format);
612
+ if (Array.isArray(format)) {
613
+ conditional_formats.push(...format);
614
+ }
615
+ else {
616
+ conditional_formats.push(format);
617
+ }
549
618
  }
550
619
  }
551
620
  }
@@ -39,10 +39,15 @@ import { ZipWrapper } from './zip-wrapper';
39
39
  import type { CellStyle, ThemeColor } from 'treb-base-types';
40
40
  import type { SerializedNamed } from 'treb-data-model';
41
41
 
42
+ /**
43
+ * @privateRemarks -- FIXME: not sure about the equal/equals thing. need to check.
44
+ */
42
45
  export const ConditionalFormatOperators: Record<string, string> = {
43
46
  greaterThan: '>',
47
+ greaterThanOrEqual: '>=',
44
48
  greaterThanOrEquals: '>=',
45
49
  lessThan: '<',
50
+ lessThanOrEqual: '<=',
46
51
  lessThanOrEquals: '<=',
47
52
  equal: '=',
48
53
  notEqual: '<>',
@@ -163,7 +163,7 @@ export interface NodeDescriptor {
163
163
 
164
164
  export class Editor<E = FormulaEditorEvent> extends EventSource<E|FormulaEditorEvent> {
165
165
 
166
- protected static readonly FormulaChars = ('$^&*(-+={[<>/~%' + Localization.argument_separator).split(''); // FIXME: i18n
166
+ protected static readonly FormulaChars = ('$^&*(-+={[<>/~%@' + Localization.argument_separator).split(''); // FIXME: i18n
167
167
 
168
168
  /**
169
169
  * the current edit cell. in the event we're editing a merged or
@@ -136,9 +136,10 @@ const binary_operators_precendence: PrecedenceList = {
136
136
  ':': 13, // range operator
137
137
  };
138
138
 
139
- /**
139
+ /* *
140
140
  * binary ops are sorted by length so we can compare long ops first
141
- */
141
+ switching to a composite w/ unary operators
142
+ * /
142
143
  const binary_operators = Object.keys(binary_operators_precendence).sort(
143
144
  (a, b) => b.length - a.length,
144
145
  );
@@ -147,8 +148,17 @@ const binary_operators = Object.keys(binary_operators_precendence).sort(
147
148
  * unary operators. atm we have no precedence issues, unary operators
148
149
  * always have absolute precedence. (for numbers, these are properly part
149
150
  * of the number, but consider `=-SUM(1,2)` -- this is an operator).
151
+ *
152
+ * implicit intersection operator should now have precedence over +/-.
153
+ */
154
+ const unary_operators: PrecedenceList = { '@': 50, '-': 100, '+': 100 };
155
+
156
+ /**
157
+ * to avoid the double - and +, we're just adding our one extra unary
158
+ * operator. doing this dynamically would be silly, although this does
159
+ * make this code more fragile.
150
160
  */
151
- const unary_operators: PrecedenceList = { '-': 100, '+': 100 };
161
+ const composite_operators: string[] = [...Object.keys(binary_operators_precendence), '@'].sort((a, b) => b.length - a.length);
152
162
 
153
163
  /**
154
164
  * parser for spreadsheet language.
@@ -2092,7 +2102,7 @@ export class Parser {
2092
2102
  }
2093
2103
 
2094
2104
  protected ConsumeOperator(): ExpressionUnit | null {
2095
- for (const operator of binary_operators) {
2105
+ for (const operator of composite_operators) {
2096
2106
  if (this.expression.substr(this.index, operator.length) === operator) {
2097
2107
  const position = this.index;
2098
2108
  this.index += operator.length;