@trebco/treb 27.7.6 → 27.11.1

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 (55) hide show
  1. package/dist/treb-spreadsheet.mjs +14 -14
  2. package/dist/treb.d.ts +28 -26
  3. package/notes/conditional-fomratring.md +29 -0
  4. package/package.json +1 -1
  5. package/treb-base-types/src/area.ts +181 -0
  6. package/{treb-grid/src/util/dom_utilities.ts → treb-base-types/src/dom-utilities.ts} +29 -20
  7. package/treb-base-types/src/evaluate-options.ts +21 -0
  8. package/treb-base-types/src/gradient.ts +97 -0
  9. package/treb-base-types/src/import.ts +2 -1
  10. package/treb-base-types/src/index.ts +3 -0
  11. package/treb-base-types/src/theme.ts +3 -5
  12. package/treb-calculator/src/calculator.ts +190 -28
  13. package/treb-calculator/src/dag/calculation_leaf_vertex.ts +97 -0
  14. package/treb-calculator/src/dag/graph.ts +10 -22
  15. package/treb-calculator/src/dag/{leaf_vertex.ts → state_leaf_vertex.ts} +3 -3
  16. package/treb-calculator/src/descriptors.ts +10 -3
  17. package/treb-calculator/src/expression-calculator.ts +1 -1
  18. package/treb-calculator/src/function-library.ts +25 -22
  19. package/treb-calculator/src/functions/base-functions.ts +166 -5
  20. package/treb-calculator/src/index.ts +6 -6
  21. package/treb-calculator/src/notifier-types.ts +1 -1
  22. package/treb-calculator/src/utilities.ts +2 -2
  23. package/treb-charts/src/util.ts +2 -2
  24. package/treb-embed/src/custom-element/global.d.ts +3 -1
  25. package/treb-embed/src/custom-element/spreadsheet-constructor.ts +13 -19
  26. package/treb-embed/src/embedded-spreadsheet.ts +378 -132
  27. package/treb-embed/src/spinner.ts +3 -3
  28. package/treb-embed/style/layout.scss +1 -1
  29. package/treb-export/src/drawing2/chart2.ts +11 -2
  30. package/treb-export/src/export-worker/export-worker.ts +0 -13
  31. package/treb-export/src/export2.ts +197 -2
  32. package/treb-export/src/import2.ts +169 -4
  33. package/treb-export/src/workbook-style2.ts +59 -10
  34. package/treb-export/src/workbook2.ts +10 -1
  35. package/treb-grid/src/editors/autocomplete.ts +28 -24
  36. package/treb-grid/src/editors/editor.ts +3 -4
  37. package/treb-grid/src/editors/formula_bar.ts +1 -1
  38. package/treb-grid/src/index.ts +2 -1
  39. package/treb-grid/src/layout/base_layout.ts +34 -31
  40. package/treb-grid/src/layout/grid_layout.ts +17 -28
  41. package/treb-grid/src/render/selection-renderer.ts +2 -3
  42. package/treb-grid/src/render/svg_header_overlay.ts +4 -11
  43. package/treb-grid/src/render/svg_selection_block.ts +27 -34
  44. package/treb-grid/src/render/tile_renderer.ts +8 -6
  45. package/treb-grid/src/types/conditional_format.ts +168 -0
  46. package/treb-grid/src/types/grid.ts +37 -47
  47. package/treb-grid/src/types/grid_base.ts +188 -33
  48. package/treb-grid/src/types/scale-control.ts +2 -2
  49. package/treb-grid/src/types/sheet.ts +332 -28
  50. package/treb-grid/src/types/sheet_types.ts +4 -0
  51. package/treb-grid/src/types/tab_bar.ts +4 -8
  52. package/treb-utils/src/index.ts +0 -1
  53. package/treb-utils/src/resizable.ts +26 -27
  54. package/treb-utils/src/template.ts +0 -70
  55. /package/{README-shadow-DOM.md → notes/shadow-DOM.md} +0 -0
@@ -19,16 +19,16 @@
19
19
  *
20
20
  */
21
21
 
22
+ import { DOMUtilities } from 'treb-base-types';
23
+
22
24
  export class Spinner {
23
25
 
24
26
  private node: HTMLDivElement;
25
27
  private visible = false;
26
28
 
27
29
  constructor(public container: HTMLElement) {
28
- this.node = document.createElement('div');
29
- this.node.classList.add('treb-spinner');
30
+ this.node = DOMUtilities.Div('treb-spinner', container);
30
31
  this.node.innerHTML = `<div><div></div><div></div><div></div><div></div></div>`;
31
- container.appendChild(this.node);
32
32
  }
33
33
 
34
34
  public Show(): void {
@@ -228,7 +228,7 @@
228
228
  justify-self: end;
229
229
  align-self: end;
230
230
 
231
- border: .5rem solid var(--treb-resize-handle-color, blue);
231
+ border: .5rem solid var(--treb-resize-handle-color, #0059B9);
232
232
  border-top-color: transparent;
233
233
  border-left-color: transparent;
234
234
 
@@ -169,7 +169,11 @@ export class Chart {
169
169
  }
170
170
  }
171
171
 
172
- series['c:spPr']['a:ln']['a:solidFill']['a:schemeClr'].a$['val'] = `accent${i+1}`;
172
+ // "accent7" will break
173
+
174
+ if (i < 6) {
175
+ series['c:spPr']['a:ln']['a:solidFill']['a:schemeClr'].a$['val'] = `accent${i+1}`;
176
+ }
173
177
 
174
178
  series['c:yVal']['c:numRef']['c:f'] = this.options.data[i]?.label;
175
179
 
@@ -255,7 +259,12 @@ export class Chart {
255
259
 
256
260
  series['c:idx'] = { a$: { val: i.toString() }};
257
261
  series['c:order'] = { a$: { val: i.toString() }};
258
- series['c:spPr']['a:solidFill']['a:schemeClr'].a$['val'] = `accent${i+1}`;
262
+
263
+ // "accent7" will break
264
+
265
+ if (i < 6) {
266
+ series['c:spPr']['a:solidFill']['a:schemeClr'].a$['val'] = `accent${i+1}`;
267
+ }
259
268
 
260
269
  if (!i && this.options.labels) {
261
270
  series['c:cat'] = {
@@ -40,19 +40,6 @@ const ExportSheets = async (data: any) => {
40
40
  ctx.postMessage({ status: 'complete', blob: corrected });
41
41
  }
42
42
 
43
- /*
44
- if (data.sheet) {
45
- await exporter.Init();
46
- await exporter.ExportSheets(data.sheet);
47
- const blob = await exporter.AsBlob(1);
48
-
49
- // correct the mime type for firefox
50
- const corrected = (blob as Blob).slice(0, (blob as Blob).size,
51
- 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet');
52
-
53
- ctx.postMessage({ status: 'complete', blob: corrected });
54
- }
55
- */
56
43
  };
57
44
 
58
45
  const ImportSheet = async (data: any) => {
@@ -60,7 +60,7 @@ import { Chart } from './drawing2/chart2';
60
60
  import type { ImageOptions } from './drawing2/embedded-image';
61
61
  import type { TwoCellAnchor } from './drawing2/drawing2';
62
62
  import { Drawing } from './drawing2/drawing2';
63
- import type { TableDescription, TableFooterType } from './workbook2';
63
+ import { ConditionalFormatOperators, type TableDescription, type TableFooterType } from './workbook2';
64
64
  import type { AnnotationData } from 'treb-grid/src/types/annotation';
65
65
 
66
66
  export class Exporter {
@@ -433,11 +433,67 @@ export class Exporter {
433
433
  };
434
434
  }
435
435
 
436
+ if (style_cache.dxf_styles.length) {
437
+ const dxf: any[] = [];
438
+ for (const style of style_cache.dxf_styles) {
439
+ const entry: any = {};
440
+ if (style.text || style.bold || style.italic || style.underline) {
441
+ const font: any = {};
442
+ if (style.text) {
443
+ font.color = { a$: {}};
444
+ if (style.text.text) {
445
+ font.color.a$.rgb = `FF` + style.text.text.substring(1);
446
+ }
447
+ else if (style.text.theme) {
448
+ font.color.a$.theme = style.text.theme;
449
+ if (style.text.tint) {
450
+ font.color.a$.tint = style.text.tint;
451
+ }
452
+ }
453
+ }
454
+ if (style.bold) {
455
+ font.b = {};
456
+ }
457
+ if (style.italic) {
458
+ font.i = {};
459
+ }
460
+ if (style.underline) {
461
+ font.u = {};
462
+ }
463
+ if (style.strike) {
464
+ font.strike = {};
465
+ }
466
+ entry.font = font;
467
+ }
468
+ if (style.fill) {
469
+ const color: any = { a$: {}};
470
+ if (style.fill.text) {
471
+ color.a$.rgb = `FF` + style.fill.text.substring(1);
472
+ }
473
+ else if (style.fill.theme) {
474
+ color.a$.theme = style.fill.theme;
475
+ if (style.fill.tint) {
476
+ color.a$.tint = style.fill.tint;
477
+ }
478
+ }
479
+ entry.fill = { patternFill: { bgColor: color }};
480
+ }
481
+ dxf.push(entry);
482
+ }
483
+
484
+ if (dxf.length) {
485
+ dom.styleSheet.dxfs = {
486
+ a$: { count: dxf.length },
487
+ dxf,
488
+ };
489
+ }
490
+
491
+ }
492
+
436
493
  // not used:
437
494
  //
438
495
  // cellStyleXfs
439
496
  // cellStyles
440
- // dxfs
441
497
  // tableStyles
442
498
 
443
499
  // ------------
@@ -586,6 +642,9 @@ export class Exporter {
586
642
  // is this backwards, vis a vis our rendering? I think it might be...
587
643
  // YES: should be row pattern -> row -> column -> cell [corrected]
588
644
 
645
+ // FIXME: can't we just ask the sheet? (A: no, because we don't have
646
+ // an actual sheet, although we could?)
647
+
589
648
  // if (sheet.row_style && sheet.row_style[row]) {
590
649
  // list.push(sheet.row_style[row]);
591
650
  // }
@@ -1214,6 +1273,7 @@ export class Exporter {
1214
1273
  },
1215
1274
  dataValidations: {},
1216
1275
  hyperlinks: {},
1276
+ conditionalFormatting: {},
1217
1277
  pageMargins: {
1218
1278
  a$: {
1219
1279
  left: 0.7,
@@ -1981,6 +2041,135 @@ export class Exporter {
1981
2041
 
1982
2042
  }
1983
2043
 
2044
+ // --- conditional formats -----------------------------------------------
2045
+
2046
+ if (sheet.conditional_formats?.length) {
2047
+ const conditionalFormatting: any[] = [];
2048
+ let priority_index = 1;
2049
+
2050
+ const reverse_operator_map: Record<string, string> = {};
2051
+ const operator_list: string[] = Object.entries(ConditionalFormatOperators).map(entry => {
2052
+ reverse_operator_map[entry[1]] = entry[0];
2053
+ return entry[1];
2054
+ });
2055
+
2056
+ operator_list.sort((a, b) => b.length - a.length);
2057
+
2058
+ for (const format of sheet.conditional_formats) {
2059
+
2060
+ let dxf_index = 0;
2061
+
2062
+ if (format.type !== 'gradient') {
2063
+
2064
+ // these are zero-based? I thought everything in there
2065
+ // was 1-based. [A: yes, these are indexed from 0].
2066
+
2067
+ dxf_index = style_cache.dxf_styles.length;
2068
+ style_cache.dxf_styles.push(format.style);
2069
+
2070
+ }
2071
+
2072
+ switch (format.type) {
2073
+ case 'cell-match':
2074
+ {
2075
+ let operator = '';
2076
+ let formula = '';
2077
+
2078
+ for (const test of operator_list) {
2079
+ if (new RegExp('^' + test + '\\s').test(format.expression)) {
2080
+ operator = reverse_operator_map[test];
2081
+ formula = format.expression.substring(test.length).trim();
2082
+ break;
2083
+ }
2084
+ }
2085
+ if (operator) {
2086
+
2087
+ conditionalFormatting.push({
2088
+ a$: { sqref: new Area(format.area.start, format.area.end).spreadsheet_label },
2089
+ cfRule: {
2090
+ a$: { type: 'cellIs', dxfId: dxf_index, operator, priority: priority_index++ },
2091
+ formula,
2092
+ }
2093
+ });
2094
+ }
2095
+ }
2096
+ break;
2097
+
2098
+ case 'expression':
2099
+ conditionalFormatting.push({
2100
+ a$: { sqref: new Area(format.area.start, format.area.end).spreadsheet_label },
2101
+ cfRule: {
2102
+ a$: { type: 'expression', dxfId: dxf_index, priority: priority_index++ },
2103
+ formula: format.expression,
2104
+ }
2105
+ });
2106
+ break;
2107
+
2108
+ case 'duplicate-values':
2109
+ conditionalFormatting.push({
2110
+ a$: { sqref: new Area(format.area.start, format.area.end).spreadsheet_label },
2111
+ cfRule: {
2112
+ a$: { type: format.unique ? 'uniqueValues' : 'duplicateValues', dxfId: dxf_index, priority: priority_index++ },
2113
+ }
2114
+ });
2115
+ break;
2116
+
2117
+ case 'gradient':
2118
+ {
2119
+ let cfvo: any[] = [];
2120
+ let color: any[] = [];
2121
+
2122
+ for (const stop of format.stops) {
2123
+ if (stop.value === 0) {
2124
+ cfvo.push({ a$: { type: 'min' }});
2125
+ }
2126
+ else if (stop.value === 1) {
2127
+ cfvo.push({ a$: { type: 'max' }});
2128
+ }
2129
+ else {
2130
+ cfvo.push({ a$: { type: 'percentile', val: stop.value * 100 }});
2131
+ }
2132
+ const stop_color: any = { a$: {}};
2133
+ if (stop.color.text) {
2134
+ stop_color.a$.rgb = 'FF' + stop.color.text.substring(1);
2135
+ }
2136
+ else if (stop.color.theme) {
2137
+ stop_color.a$.theme = stop.color.theme;
2138
+ stop_color.a$.tint = stop.color.tint || undefined;
2139
+ }
2140
+ color.push(stop_color);
2141
+ }
2142
+
2143
+ const generated = {
2144
+ a$: { sqref: new Area(format.area.start, format.area.end).spreadsheet_label },
2145
+ cfRule: {
2146
+ a$: { type: 'colorScale', priority: priority_index++ },
2147
+ colorScale: {
2148
+ cfvo,
2149
+ color,
2150
+ }
2151
+ }
2152
+ };
2153
+
2154
+ conditionalFormatting.push(generated);
2155
+
2156
+ }
2157
+ break;
2158
+ }
2159
+ }
2160
+
2161
+ if (conditionalFormatting.length) {
2162
+ dom.worksheet.conditionalFormatting = (conditionalFormatting.length > 1) ? conditionalFormatting : conditionalFormatting[0];
2163
+ }
2164
+ else {
2165
+ delete dom.worksheet.conditionalFormatting;
2166
+ }
2167
+
2168
+ }
2169
+ else {
2170
+ delete dom.worksheet.conditionalFormatting;
2171
+ }
2172
+
1984
2173
  // --- merges ------------------------------------------------------------
1985
2174
 
1986
2175
  if (merges.length) {
@@ -2152,6 +2341,12 @@ export class Exporter {
2152
2341
  delete dom.worksheet.drawing;
2153
2342
  }
2154
2343
 
2344
+ // --- move page margins -------------------------------------------------
2345
+
2346
+ // const margins = dom.worksheet.pageMargins;
2347
+ // delete dom.worksheet.pageMargins;
2348
+ // dom.worksheet.pageMargins = margins;
2349
+
2155
2350
  // --- end? --------------------------------------------------------------
2156
2351
 
2157
2352
  // it seems like chrome, at least, will maintain order. but this is
@@ -23,12 +23,12 @@
23
23
  import JSZip from 'jszip';
24
24
 
25
25
  import type { AnchoredChartDescription} from './workbook2';
26
- import { ChartType, Workbook } from './workbook2';
26
+ import { ChartType, ConditionalFormatOperators, Workbook } from './workbook2';
27
27
  import type { ParseResult } from 'treb-parser';
28
28
  import { Parser } from 'treb-parser';
29
29
  import type { RangeType, AddressType, HyperlinkType } from './address-type';
30
30
  import { is_range, ShiftRange, InRange, is_address } from './address-type';
31
- import type { ImportedSheetData, AnchoredAnnotation, CellParseResult, AnnotationLayout, Corner as LayoutCorner, ICellAddress, DataValidation, IArea } from 'treb-base-types/src';
31
+ import type { ImportedSheetData, AnchoredAnnotation, CellParseResult, AnnotationLayout, Corner as LayoutCorner, ICellAddress, DataValidation, IArea, GradientStop, Color } from 'treb-base-types/src';
32
32
  import { type ValueType, ValidationType, type SerializedValueType } from 'treb-base-types/src';
33
33
  import type { Sheet} from './workbook-sheet2';
34
34
  import { VisibleState } from './workbook-sheet2';
@@ -37,7 +37,7 @@ import { XMLUtils } from './xml-utils';
37
37
 
38
38
  // import { one_hundred_pixels } from './constants';
39
39
  import { ColumnWidthToPixels } from './column-width';
40
- import type { AnnotationType } from 'treb-grid';
40
+ import type { AnnotationType, ConditionalFormat } from 'treb-grid';
41
41
 
42
42
  interface SharedFormula {
43
43
  row: number;
@@ -320,6 +320,151 @@ export class Importer {
320
320
 
321
321
  }
322
322
 
323
+ public AddressToArea(address: RangeType|AddressType): IArea {
324
+
325
+ const area: IArea = is_address(address) ? {
326
+ start: { row: address.row - 1, column: address.col - 1 },
327
+ end: { row: address.row - 1, column: address.col - 1 },
328
+ } : {
329
+ start: { row: address.from.row - 1, column: address.from.col - 1 },
330
+ end: { row: address.to.row - 1, column: address.to.col - 1 },
331
+ };
332
+
333
+ return area;
334
+
335
+ }
336
+
337
+ public ParseConditionalFormat(address: RangeType|AddressType, rule: any): ConditionalFormat|undefined {
338
+
339
+ const area = this.AddressToArea(address);
340
+ const operators = ConditionalFormatOperators;
341
+
342
+ switch (rule.a$.type) {
343
+ case 'duplicateValues':
344
+ case 'uniqueValues':
345
+
346
+ let style = {};
347
+
348
+ if (rule.a$.dxfId) {
349
+ const index = Number(rule.a$.dxfId);
350
+ if (!isNaN(index)) {
351
+ style = this.workbook?.style_cache.dxf_styles[index] || {};
352
+ }
353
+ }
354
+
355
+ return {
356
+ type: 'duplicate-values',
357
+ area,
358
+ style,
359
+ unique: (rule.a$.type === 'uniqueValues'),
360
+ };
361
+
362
+ case 'cellIs':
363
+ if (rule.a$.operator && rule.formula) {
364
+ let style = {};
365
+
366
+ if (rule.a$.dxfId) {
367
+ const index = Number(rule.a$.dxfId);
368
+ if (!isNaN(index)) {
369
+ style = this.workbook?.style_cache.dxf_styles[index] || {};
370
+ }
371
+ }
372
+
373
+ const operator = operators[rule.a$.operator || ''];
374
+
375
+ if (!operator) {
376
+ console.info('unhandled cellIs operator:', rule.a$.operator);
377
+ }
378
+ else {
379
+ return {
380
+ type: 'cell-match',
381
+ expression: operator + ' ' + rule.formula,
382
+ area,
383
+ style,
384
+ };
385
+ }
386
+
387
+ }
388
+ break;
389
+
390
+ case 'expression':
391
+ if (rule.formula) {
392
+ let style = {};
393
+
394
+ if (rule.a$.dxfId) {
395
+ const index = Number(rule.a$.dxfId);
396
+ if (!isNaN(index)) {
397
+ style = this.workbook?.style_cache.dxf_styles[index] || {};
398
+ }
399
+ }
400
+
401
+ return {
402
+ type: 'expression',
403
+ expression: rule.formula,
404
+ area,
405
+ style,
406
+ };
407
+
408
+ }
409
+ break;
410
+
411
+ case 'colorScale':
412
+ if (rule.colorScale && Array.isArray(rule.colorScale.cfvo) && Array.isArray(rule.colorScale.color)) {
413
+
414
+ const stops: GradientStop[] = [];
415
+ for (const [index, entry] of rule.colorScale.cfvo.entries()) {
416
+ let value = 0;
417
+ let color: Color = {};
418
+
419
+ const color_element = rule.colorScale.color[index];
420
+ if (color_element.a$.rgb) {
421
+ color.text = '#' + color_element.a$.rgb.substring(2);
422
+ }
423
+ else if (color_element.a$.theme) {
424
+ color.theme = Number(color_element.a$.theme) || 0;
425
+ if (color_element.a$.tint) {
426
+ color.tint = Math.round(color_element.a$.tint * 1000) / 1000;
427
+ }
428
+ }
429
+
430
+ switch (entry.a$.type) {
431
+ case 'min':
432
+ value = 0;
433
+ break;
434
+
435
+ case 'max':
436
+ value = 1;
437
+ break;
438
+
439
+ case 'percentile':
440
+ value = (Number(entry.a$.val) || 0) / 100;
441
+ break;
442
+ }
443
+
444
+ stops.push({ color, value });
445
+
446
+ }
447
+
448
+ return {
449
+ type: 'gradient',
450
+ stops,
451
+ color_space: 'RGB',
452
+ area,
453
+ };
454
+
455
+ }
456
+ else {
457
+ console.info('unexpected colorScale', {rule});
458
+ }
459
+ break;
460
+
461
+ default:
462
+ console.info('unhandled cf type:', {rule});
463
+ }
464
+
465
+ return undefined;
466
+ }
467
+
323
468
  public async GetSheet(index = 0): Promise<ImportedSheetData> {
324
469
 
325
470
  if (!this.workbook) {
@@ -337,6 +482,7 @@ export class Importer {
337
482
  const shared_formulae: {[index: string]: SharedFormula} = {};
338
483
  const arrays: RangeType[] = [];
339
484
  const merges: RangeType[] = [];
485
+ const conditional_formats: ConditionalFormat[] = [];
340
486
  const links: HyperlinkType[] = [];
341
487
  const validations: Array<{
342
488
  address: ICellAddress,
@@ -344,8 +490,26 @@ export class Importer {
344
490
  }> = [];
345
491
  const annotations: AnchoredAnnotation[] = [];
346
492
 
347
- const FindAll = XMLUtils.FindAll.bind(XMLUtils, sheet.sheet_data);
493
+ const FindAll: (path: string) => any[] = XMLUtils.FindAll.bind(XMLUtils, sheet.sheet_data);
494
+
495
+ // conditionals
348
496
 
497
+ const conditional_formatting = FindAll('worksheet/conditionalFormatting');
498
+ for (const element of conditional_formatting) {
499
+ if (element.a$?.sqref ){
500
+ const area = sheet.TranslateAddress(element.a$.sqref);
501
+ if (element.cfRule) {
502
+ const rules = Array.isArray(element.cfRule) ? element.cfRule : [element.cfRule];
503
+ for (const rule of rules) {
504
+ const format = this.ParseConditionalFormat(area, rule);
505
+ if (format) {
506
+ conditional_formats.push(format);
507
+ }
508
+ }
509
+ }
510
+ }
511
+ }
512
+
349
513
  // merges
350
514
 
351
515
  const merge_cells = FindAll('worksheet/mergeCells/mergeCell');
@@ -878,6 +1042,7 @@ export class Importer {
878
1042
  column_widths,
879
1043
  row_heights,
880
1044
  annotations,
1045
+ conditional_formats,
881
1046
  styles: this.workbook?.style_cache?.CellXfToStyles() || [],
882
1047
  };
883
1048
 
@@ -22,7 +22,7 @@
22
22
  // import * as ElementTree from 'elementtree';
23
23
  // import { Element, ElementTree as Tree } from 'elementtree';
24
24
 
25
- import { type CompositeBorderEdge, Style, type CellStyle, type PropertyKeys } from 'treb-base-types';
25
+ import { type CompositeBorderEdge, Style, type CellStyle, type PropertyKeys, type Color } from 'treb-base-types';
26
26
  import { Theme } from './workbook-theme2';
27
27
  import { NumberFormatCache } from 'treb-format';
28
28
  import { XMLUtils } from './xml-utils';
@@ -202,6 +202,7 @@ export class StyleCache {
202
202
  public fills: Fill[] = [];
203
203
  public number_formats: NumberFormat[] = [];
204
204
  public base_number_format_id = 200; // ?
205
+ public dxf_styles: CellStyle[] = [];
205
206
 
206
207
  // public dom?: Tree;
207
208
 
@@ -423,17 +424,18 @@ export class StyleCache {
423
424
  fill.pattern_type = 'solid';
424
425
  if (composite.fill.text) {
425
426
  fill.fg_color = { argb: composite.fill.text };
427
+ options.fill = fill;
426
428
  }
427
429
  else if (typeof composite.fill.theme === 'number') {
428
430
  fill.fg_color = { theme: composite.fill.theme };
429
431
  if (composite.fill.tint) {
430
432
  fill.fg_color.tint = composite.fill.tint;
431
433
  }
434
+ options.fill = fill;
432
435
  }
433
436
  else {
434
- fill.fg_color = { theme: 1 };
437
+ // fill.fg_color = { theme: 1 };
435
438
  }
436
- options.fill = fill;
437
439
  }
438
440
 
439
441
  if (composite.wrap) {
@@ -1197,9 +1199,7 @@ export class StyleCache {
1197
1199
 
1198
1200
  // ---
1199
1201
 
1200
- composite = FindAll('styleSheet/fills/fill');
1201
-
1202
- this.fills = composite.map(element => {
1202
+ const ParseFill = (element: any) => {
1203
1203
 
1204
1204
  const fill: Fill = { pattern_type: 'none' };
1205
1205
  if (element.patternFill) {
@@ -1236,13 +1236,15 @@ export class StyleCache {
1236
1236
 
1237
1237
  return fill;
1238
1238
 
1239
- });
1239
+ };
1240
1240
 
1241
- // ---
1241
+ composite = FindAll('styleSheet/fills/fill');
1242
1242
 
1243
- composite = FindAll('styleSheet/fonts/font');
1243
+ this.fills = composite.map(ParseFill);
1244
1244
 
1245
- this.fonts = composite.map(element => {
1245
+ // ---
1246
+
1247
+ const ParseFont = (element: any) => {
1246
1248
 
1247
1249
  const font: Font = {};
1248
1250
 
@@ -1278,8 +1280,55 @@ export class StyleCache {
1278
1280
 
1279
1281
  return font;
1280
1282
 
1283
+ };
1284
+
1285
+ composite = FindAll('styleSheet/fonts/font');
1286
+ this.fonts = composite.map(ParseFont);
1287
+
1288
+ // dxfs (differential formats) are inline. because reasons? not sure
1289
+ // what's allowed in there, atm we're just looking at font color and
1290
+ // background color.
1291
+
1292
+ const ParseDXFColor = (element: any) => {
1293
+ const color: Color = {};
1294
+ if (element.a$.rgb) {
1295
+ color.text = '#' + element.a$.rgb.substring(2);
1296
+ }
1297
+ else if (element.a$.theme) {
1298
+ color.theme = Number(element.a$.theme) || 0;
1299
+ if (element.a$.tint) {
1300
+ color.tint = Math.round(element.a$.tint * 1000) / 1000;
1301
+ }
1302
+ }
1303
+ return color;
1304
+ };
1305
+
1306
+ const dxfs = FindAll('styleSheet/dxfs/dxf');
1307
+ this.dxf_styles = dxfs.map(dxf => {
1308
+
1309
+ const style: CellStyle = {};
1310
+
1311
+ // dxf fonts are different too? this is irritating
1312
+
1313
+ if (dxf.font) {
1314
+ style.bold = !!dxf.font.b;
1315
+ style.italic = !!dxf.font.i && dxf.font.i.a$.val !== '0';
1316
+ }
1317
+
1318
+ // dxfs fills are different? anyway we can't reuse the above code for fill, just grab the color
1319
+
1320
+ if (dxf.font?.color?.a$) {
1321
+ style.text = ParseDXFColor(dxf.font.color);
1322
+ }
1323
+ if (dxf.fill?.patternFill?.bgColor?.a$) {
1324
+ style.fill = ParseDXFColor(dxf.fill.patternFill.bgColor);
1325
+ }
1326
+
1327
+ return style;
1281
1328
  });
1282
1329
 
1330
+ // console.info({dxfs: this.dxf_styles});
1331
+
1283
1332
  }
1284
1333
 
1285
1334
  }
@@ -54,6 +54,15 @@ const XMLTypeMap = {
54
54
  };
55
55
  */
56
56
 
57
+ export const ConditionalFormatOperators: Record<string, string> = {
58
+ greaterThan: '>',
59
+ greaterThanOrEquals: '>=',
60
+ lessThan: '<',
61
+ lessThanOrEquals: '<=',
62
+ equal: '=',
63
+ notEqual: '<>',
64
+ };
65
+
57
66
  export enum ChartType {
58
67
  Unknown = 0, Column, Bar, Line, Scatter, Donut, Pie
59
68
  }
@@ -180,7 +189,7 @@ export class Workbook {
180
189
  xml = xmlparser2.parse(data);
181
190
  this.style_cache.FromXML(xml, this.theme);
182
191
 
183
- console.info({c: this.style_cache});
192
+ // console.info({c: this.style_cache});
184
193
 
185
194
  // read workbook
186
195
  data = await this.zip.file('xl/workbook.xml')?.async('text') as string;