@trebco/treb 27.9.0 → 27.11.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 (32) hide show
  1. package/dist/treb-spreadsheet.mjs +9 -9
  2. package/dist/treb.d.ts +8 -6
  3. package/package.json +1 -1
  4. package/{treb-grid/src/util/dom_utilities.ts → treb-base-types/src/dom-utilities.ts} +29 -20
  5. package/treb-base-types/src/index.ts +1 -0
  6. package/treb-base-types/src/theme.ts +3 -5
  7. package/treb-embed/src/custom-element/global.d.ts +3 -1
  8. package/treb-embed/src/custom-element/spreadsheet-constructor.ts +13 -19
  9. package/treb-embed/src/embedded-spreadsheet.ts +67 -93
  10. package/treb-embed/src/spinner.ts +3 -3
  11. package/treb-embed/style/layout.scss +1 -1
  12. package/treb-embed/style/overlay-editor.scss +9 -0
  13. package/treb-export/src/drawing2/chart2.ts +11 -2
  14. package/treb-export/src/export2.ts +14 -4
  15. package/treb-export/src/workbook-style2.ts +3 -2
  16. package/treb-grid/src/editors/autocomplete.ts +28 -24
  17. package/treb-grid/src/editors/editor.ts +32 -4
  18. package/treb-grid/src/editors/formula_bar.ts +1 -1
  19. package/treb-grid/src/layout/base_layout.ts +11 -16
  20. package/treb-grid/src/layout/grid_layout.ts +17 -28
  21. package/treb-grid/src/render/selection-renderer.ts +2 -3
  22. package/treb-grid/src/render/svg_header_overlay.ts +4 -11
  23. package/treb-grid/src/render/svg_selection_block.ts +27 -34
  24. package/treb-grid/src/render/tile_renderer.ts +6 -5
  25. package/treb-grid/src/types/grid.ts +32 -41
  26. package/treb-grid/src/types/grid_base.ts +2 -0
  27. package/treb-grid/src/types/scale-control.ts +2 -2
  28. package/treb-grid/src/types/sheet.ts +2 -2
  29. package/treb-grid/src/types/tab_bar.ts +4 -8
  30. package/treb-utils/src/index.ts +0 -1
  31. package/treb-utils/src/resizable.ts +26 -27
  32. package/treb-utils/src/template.ts +0 -70
package/dist/treb.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- /*! API v27.9. Copyright 2018-2023 trebco, llc. All rights reserved. LGPL: https://treb.app/license */
1
+ /*! API v27.11. Copyright 2018-2023 trebco, llc. All rights reserved. LGPL: https://treb.app/license */
2
2
 
3
3
  /**
4
4
  * add our tag to the map
@@ -6,7 +6,9 @@
6
6
  declare global {
7
7
  interface HTMLElementTagNameMap {
8
8
  'treb-spreadsheet': HTMLElement & {
9
- sheet: EmbeddedSpreadsheet|undefined;
9
+ instance: {
10
+ sheet: EmbeddedSpreadsheet | undefined;
11
+ } | undefined;
10
12
  };
11
13
  }
12
14
  }
@@ -385,11 +387,11 @@ export declare class EmbeddedSpreadsheet {
385
387
  * @param formula - annotation formula. For charts, the chart formula.
386
388
  * @param type - annotation type. Defaults to `treb-chart`.
387
389
  * @param rect - coordinates, or a range reference for layout.
388
- *
389
- * @param argument_separator - the argument separator to use when evaluating
390
- * the function. defaults to current locale.
390
+ * @param options - evaluate options. because this function used to take
391
+ * the argument separator, we allow that to be passed directly, but this
392
+ * is deprecated. new code should use the options object.
391
393
  */
392
- InsertAnnotation(formula: string, type?: AnnotationType, rect?: IRectangle | RangeReference, argument_separator?: ',' | ';'): void;
394
+ InsertAnnotation(formula: string, type?: AnnotationType, rect?: IRectangle | RangeReference, options?: EvaluateOptions | ',' | ';'): void;
393
395
 
394
396
  /**
395
397
  * Insert an image. This method will open a file chooser and (if an image
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@trebco/treb",
3
- "version": "27.9.0",
3
+ "version": "27.11.4",
4
4
  "license": "LGPL-3.0-or-later",
5
5
  "homepage": "https://treb.app",
6
6
  "repository": {
@@ -19,25 +19,50 @@
19
19
  *
20
20
  */
21
21
 
22
+ const SVGNS = 'http://www.w3.org/2000/svg';
23
+
22
24
  export class DOMUtilities {
23
25
 
24
26
  /** creates a div and assigns class name/names */
25
- public static CreateDiv(classes = '', parent?: HTMLElement, scope?: string): HTMLDivElement {
27
+ public static Div(classes?: string|string[], parent?: HTMLElement, scope?: string): HTMLDivElement {
26
28
  return this.Create('div', classes, parent, scope);
27
29
  }
28
30
 
31
+ public static ClassNames(element: HTMLElement|SVGElement, classes: string|string[]) {
32
+ element.classList.add(...(Array.isArray(classes) ? classes : [classes]).reduce((arr: string[], entry) => [...arr, ...entry.split(/\s+/g)], []));
33
+ }
34
+
35
+ public static SVG<K extends keyof SVGElementTagNameMap>(
36
+ tag: K,
37
+ classes?: string|string[],
38
+ parent?: HTMLElement|SVGElement
39
+ ): SVGElementTagNameMap[K] {
40
+
41
+ const element = document.createElementNS(SVGNS, tag);
42
+
43
+ if (classes) {
44
+ this.ClassNames(element, classes);
45
+ }
46
+
47
+ if (parent) {
48
+ parent.appendChild(element);
49
+ }
50
+
51
+ return element;
52
+ }
53
+
29
54
  /** better typing */
30
55
  public static Create<K extends keyof HTMLElementTagNameMap>(
31
56
  tag: K,
32
- class_name = '',
57
+ classes?: string|string[],
33
58
  parent?: HTMLElement,
34
59
  scope?: string,
35
60
  attrs?: Record<string, string>): HTMLElementTagNameMap[K] {
36
61
 
37
62
  const element = document.createElement(tag);
38
63
 
39
- if (class_name) {
40
- element.className = class_name;
64
+ if (classes) {
65
+ this.ClassNames(element, classes);
41
66
  }
42
67
 
43
68
  if (scope) {
@@ -58,20 +83,4 @@ export class DOMUtilities {
58
83
  return element;
59
84
  }
60
85
 
61
- /* * generic element constructor. shame we need the tag AND the type. * /
62
- public static Create1<E extends HTMLElement>(tag = '', classes = '', parent?: HTMLElement, scope?: string, attrs?: Record<string, string>){
63
- const element = document.createElement(tag) as E;
64
- if (classes) element.setAttribute('class', classes);
65
- if (scope) element.setAttribute(scope, '');
66
- if (attrs) {
67
- const keys = Object.keys(attrs);
68
- for (const key of keys) {
69
- element.setAttribute(key, attrs[key]);
70
- }
71
- }
72
- if (parent) parent.appendChild(element);
73
- return element;
74
- }
75
- */
76
-
77
86
  }
@@ -38,6 +38,7 @@ export * from './api_types';
38
38
  export * from './table';
39
39
  export * from './gradient';
40
40
  export * from './evaluate-options';
41
+ export * from './dom-utilities';
41
42
 
42
43
  // import * as Style from './style';
43
44
  // export { Style };
@@ -21,6 +21,7 @@
21
21
 
22
22
  import type { Color, CellStyle } from './style';
23
23
  import { ColorFunctions } from './color';
24
+ import { DOMUtilities } from './dom-utilities';
24
25
 
25
26
  /*
26
27
  * so this is a little strange. we use CSS to populate a theme object,
@@ -494,10 +495,7 @@ export const LoadThemeProperties = (container: HTMLElement): Theme => {
494
495
  const theme: Theme = JSON.parse(JSON.stringify(DefaultTheme));
495
496
 
496
497
  const Append = (parent: HTMLElement, classes: string): HTMLDivElement => {
497
- const node = document.createElement('div');
498
- node.setAttribute('class', classes);
499
- parent.appendChild(node);
500
- return node;
498
+ return DOMUtilities.Div(classes, parent);
501
499
  }
502
500
 
503
501
  const ElementCSS = (parent: HTMLElement, classes: string): CSSStyleDeclaration => {
@@ -574,7 +572,7 @@ export const LoadThemeProperties = (container: HTMLElement): Theme => {
574
572
  // we could just parse, we know the returned css format is going
575
573
  // to be an rgb triple (I think?)
576
574
 
577
- const canvas = document.createElement('canvas');
575
+ const canvas = DOMUtilities.Create('canvas');
578
576
 
579
577
  canvas.width = 3;
580
578
  canvas.height = 3;
@@ -5,7 +5,9 @@
5
5
  declare global {
6
6
  interface HTMLElementTagNameMap {
7
7
  'treb-spreadsheet': HTMLElement & {
8
- sheet: EmbeddedSpreadsheet|undefined;
8
+ instance: {
9
+ sheet: EmbeddedSpreadsheet | undefined;
10
+ } | undefined;
9
11
  };
10
12
  }
11
13
  }
@@ -19,8 +19,11 @@ interface ElementOptions {
19
19
  classes: string|string[];
20
20
  }
21
21
 
22
- const Element = <T extends HTMLElement>(tag: string, parent?: HTMLElement|DocumentFragment, options: Partial<ElementOptions> = {}, attrs: Record<string, string> = {}): T => {
23
- const element = document.createElement(tag) as T;
22
+ /**
23
+ * FIXME: unify this with DOMUtils
24
+ */
25
+ const Element = <K extends keyof HTMLElementTagNameMap>(tag: K, parent?: HTMLElement|DocumentFragment, options: Partial<ElementOptions> = {}, attrs: Record<string, string> = {}): HTMLElementTagNameMap[K] => {
26
+ const element = document.createElement(tag);
24
27
  if (options.classes) {
25
28
 
26
29
  // you can't use an array destructure in a ternary expression? TIL
@@ -105,21 +108,12 @@ export class SpreadsheetConstructor {
105
108
 
106
109
  const style_node = document.head.querySelector('style[treb-stylesheet]');
107
110
  if (!style_node) {
108
- const style = document.createElement('style');
111
+ const style = Element('style');
109
112
  style.setAttribute('treb-stylesheet', '');
110
113
  style.textContent = css;
111
114
  document.head.prepend(style);
112
115
  }
113
116
 
114
- /*
115
- if (!SpreadsheetConstructor.stylesheets_attached) {
116
- const style = document.createElement('style');
117
- style.textContent = css;
118
- document.head.prepend(style);
119
- SpreadsheetConstructor.stylesheets_attached = true;
120
- }
121
- */
122
-
123
117
  }
124
118
 
125
119
  }
@@ -509,9 +503,9 @@ export class SpreadsheetConstructor {
509
503
 
510
504
  const resize_parent = root.querySelector('.treb-main') as HTMLElement; // was document.body
511
505
 
512
- resizer = Element<HTMLDivElement>('div', resize_parent, { classes: 'treb-resize-rect' });
506
+ resizer = Element('div', resize_parent, { classes: 'treb-resize-rect' });
513
507
 
514
- mask = Element<HTMLDivElement>('div', resize_parent, {
508
+ mask = Element('div', resize_parent, {
515
509
  classes: 'treb-resize-mask',
516
510
  style: 'cursor: nw-resize;',
517
511
  });
@@ -724,7 +718,7 @@ export class SpreadsheetConstructor {
724
718
  }
725
719
 
726
720
  }
727
- Element<HTMLButtonElement>('button', fragment, { style, title, data: { command: 'set-color', color: JSON.stringify(entry.color) } });
721
+ Element('button', fragment, { style, title, data: { command: 'set-color', color: JSON.stringify(entry.color) } });
728
722
  }
729
723
  }
730
724
 
@@ -733,7 +727,7 @@ export class SpreadsheetConstructor {
733
727
  this.swatch_lists.theme?.replaceChildren(fragment);
734
728
 
735
729
  fragment = document.createDocumentFragment();
736
- Element<HTMLButtonElement>('button', fragment, {
730
+ Element('button', fragment, {
737
731
  classes: 'treb-default-color',
738
732
  title: 'Default color',
739
733
  data: { command: 'set-color', color: JSON.stringify({}) },
@@ -748,7 +742,7 @@ export class SpreadsheetConstructor {
748
742
 
749
743
  for (const text of [...colors, ...additional_colors]) {
750
744
  const style = `background: ${text.toLowerCase()};`;
751
- Element<HTMLButtonElement>('button', fragment, { style, title: text, data: { command: 'set-color', color: JSON.stringify({text: text.toLowerCase()})}});
745
+ Element('button', fragment, { style, title: text, data: { command: 'set-color', color: JSON.stringify({text: text.toLowerCase()})}});
752
746
  }
753
747
 
754
748
  this.swatch_lists.other?.replaceChildren(fragment);
@@ -777,7 +771,7 @@ export class SpreadsheetConstructor {
777
771
  }
778
772
 
779
773
  const Button = (format: string) => {
780
- return Element<HTMLButtonElement>('button', undefined, {
774
+ return Element('button', undefined, {
781
775
  text: format, data: { format, command: 'number-format' },
782
776
  });
783
777
  };
@@ -785,7 +779,7 @@ export class SpreadsheetConstructor {
785
779
  const fragment = document.createDocumentFragment();
786
780
  fragment.append(...number_formats.map(format => Button(format)));
787
781
 
788
- fragment.append(Element<HTMLDivElement>('div', undefined, {}, {separator: ''}));
782
+ fragment.append(Element('div', undefined, {}, {separator: ''}));
789
783
  fragment.append(...date_formats.map(format => Button(format)));
790
784
 
791
785
  format_menu.textContent = '';
@@ -65,7 +65,7 @@ import type {
65
65
 
66
66
  import {
67
67
  IsArea, ThemeColorTable, ComplexToString, Rectangle, IsComplex, type CellStyle,
68
- Localization, Style, type Color, ThemeColor2, IsCellAddress, Area, IsFlatData, IsFlatDataArray, Gradient, ValueType,
68
+ Localization, Style, type Color, ThemeColor2, IsCellAddress, Area, IsFlatData, IsFlatDataArray, Gradient, ValueType, DOMUtilities,
69
69
  } from 'treb-base-types';
70
70
 
71
71
  import { EventSource, Yield, ValidateURI } from 'treb-utils';
@@ -565,12 +565,33 @@ export class EmbeddedSpreadsheet {
565
565
  // this one should not be done for a split view, but we should still
566
566
  // do it if the toll flag is set, and storage key is set.
567
567
 
568
+ // FIXME: shouldn't this gate on dirty? (...)
569
+ // ALSO: check if we can use a different event. see
570
+ //
571
+ // https://developer.mozilla.org/en-US/docs/Web/API/Window/beforeunload_event#usage_notes
572
+ //
573
+ // and, including the alternatives:
574
+ //
575
+ // https://developer.mozilla.org/en-US/docs/Web/API/Window/unload_event
576
+ //
577
+
568
578
  if (this.options.local_storage && !options.model ) {
579
+
580
+ window.addEventListener('visibilitychange', event => {
581
+ if (document.visibilityState === 'hidden') {
582
+ if (this.options.local_storage && this.dirty) {
583
+ this.SaveLocalStorage(this.options.local_storage);
584
+ }
585
+ }
586
+ });
587
+
588
+ /*
569
589
  window.addEventListener('beforeunload', () => {
570
- if (this.options.local_storage) {
590
+ if (this.options.local_storage && this.dirty) {
571
591
  this.SaveLocalStorage(this.options.local_storage);
572
592
  }
573
593
  });
594
+ */
574
595
  }
575
596
 
576
597
  let container: HTMLElement | undefined;
@@ -2206,19 +2227,29 @@ export class EmbeddedSpreadsheet {
2206
2227
  * @param formula - annotation formula. For charts, the chart formula.
2207
2228
  * @param type - annotation type. Defaults to `treb-chart`.
2208
2229
  * @param rect - coordinates, or a range reference for layout.
2209
- *
2210
- * @param argument_separator - the argument separator to use when evaluating
2211
- * the function. defaults to current locale.
2230
+ * @param options - evaluate options. because this function used to take
2231
+ * the argument separator, we allow that to be passed directly, but this
2232
+ * is deprecated. new code should use the options object.
2212
2233
  */
2213
- public InsertAnnotation(formula: string, type: AnnotationType = 'treb-chart', rect?: IRectangle|RangeReference, argument_separator?: ','|';'): void {
2234
+ public InsertAnnotation(formula: string, type: AnnotationType = 'treb-chart', rect?: IRectangle|RangeReference, options?: EvaluateOptions|','|';'): void {
2214
2235
 
2215
2236
  let target: IRectangle | Partial<Area> | undefined;
2237
+ let argument_separator: ','|';'|undefined = undefined;
2238
+ let r1c1 = false;
2239
+
2240
+ if (typeof options === 'object') {
2241
+ argument_separator = options.argument_separator;
2242
+ r1c1 = !!options.r1c1;
2243
+ }
2244
+ else if (options === ',' || options === ';') {
2245
+ argument_separator = options;
2246
+ }
2216
2247
 
2217
2248
  if (rect) {
2218
2249
  target = Rectangle.IsRectangle(rect) ? rect : this.model.ResolveArea(rect, this.grid.active_sheet);
2219
2250
  }
2220
2251
 
2221
- if (argument_separator && argument_separator !== this.parser.argument_separator) {
2252
+ if (argument_separator && argument_separator !== this.parser.argument_separator || r1c1) {
2222
2253
  const current = {
2223
2254
  argument_separator: this.parser.argument_separator,
2224
2255
  decimal_mark: this.parser.decimal_mark,
@@ -2233,11 +2264,17 @@ export class EmbeddedSpreadsheet {
2233
2264
  this.parser.decimal_mark = DecimalMarkType.Comma;
2234
2265
  }
2235
2266
 
2267
+ const r1c1_state = this.parser.flags.r1c1;
2268
+ if (r1c1) {
2269
+ this.parser.flags.r1c1 = r1c1;
2270
+ }
2271
+
2236
2272
  const result = this.parser.Parse(formula);
2237
2273
 
2238
2274
  // reset
2239
2275
  this.parser.argument_separator = current.argument_separator;
2240
2276
  this.parser.decimal_mark = current.decimal_mark;
2277
+ this.parser.flags.r1c1 = r1c1_state;
2241
2278
 
2242
2279
  if (result.expression) {
2243
2280
  formula = '=' + this.parser.Render(result.expression, { missing: '' });
@@ -3106,14 +3143,6 @@ export class EmbeddedSpreadsheet {
3106
3143
 
3107
3144
  if (text && filename) {
3108
3145
  const blob = new Blob([text as any], { type: 'text/plain;charset=utf-8' });
3109
- /*
3110
- // FileSaver.saveAs(blob, filename, { autoBom: false });
3111
- const a = document.createElement('a');
3112
- a.href = URL.createObjectURL(blob);
3113
- a.download = filename;
3114
- a.click();
3115
- URL.revokeObjectURL(a.href);
3116
- */
3117
3146
  this.SaveAs(blob, filename);
3118
3147
  }
3119
3148
 
@@ -4312,7 +4341,7 @@ export class EmbeddedSpreadsheet {
4312
4341
  */
4313
4342
  protected SaveAs(blob: Blob, filename: string) {
4314
4343
 
4315
- const a = document.createElement('a');
4344
+ const a = DOMUtilities.Create('a');
4316
4345
  a.href = URL.createObjectURL(blob);
4317
4346
  a.download = filename;
4318
4347
  a.click();
@@ -4437,7 +4466,7 @@ export class EmbeddedSpreadsheet {
4437
4466
  return;
4438
4467
  }
4439
4468
 
4440
- const a = document.createElement('a');
4469
+ const a = DOMUtilities.Create('a');
4441
4470
  a.setAttribute('target', this.options.hyperlinks);
4442
4471
  a.setAttribute('href', data);
4443
4472
  a.setAttribute('noreferrer', 'true');
@@ -4578,7 +4607,7 @@ export class EmbeddedSpreadsheet {
4578
4607
  protected SelectFile2(accept: string, operation: FileChooserOperation) {
4579
4608
 
4580
4609
  if (!this.file_chooser) {
4581
- this.file_chooser = document.createElement('input');
4610
+ this.file_chooser = DOMUtilities.Create('input');
4582
4611
  this.file_chooser.type = 'file';
4583
4612
 
4584
4613
  const file_chooser = this.file_chooser;
@@ -4611,79 +4640,6 @@ export class EmbeddedSpreadsheet {
4611
4640
 
4612
4641
  }
4613
4642
 
4614
- /* *
4615
- * show file chooser and resolve with the selected file, or undefined
4616
- * /
4617
- protected SelectFile(accept?: string): Promise<File | undefined> {
4618
-
4619
- return new Promise((resolve) => {
4620
-
4621
- const file_chooser = document.createElement('input');
4622
- file_chooser.type = 'file';
4623
-
4624
- if (accept) {
4625
- file_chooser.accept = accept;
4626
- }
4627
-
4628
- // so the thing here is there is no way to trap a "cancel" event
4629
- // from the file chooser. if you are waiting on a promise, that will
4630
- // just get orphaned forever.
4631
-
4632
- // it's not the end of the world, really, to leave a few of these
4633
- // dangling, but this should allow it to clean up.
4634
-
4635
- // the concept is that since file chooser is modal, there will never
4636
- // be a focus event until the modal is closed. unfortunately the focus
4637
- // event comes _before_ any input or change event from the file input,
4638
- // so we have to wait.
4639
-
4640
- // tested Cr, FF, IE11
4641
- // update: works in Safari, although oddly not if you call the API
4642
- // function from the console. not sure if that's a browserstack thing.
4643
-
4644
- // eslint-disable-next-line prefer-const
4645
- let finalize: (file?: File) => void;
4646
- let timeout: NodeJS.Timeout|undefined;
4647
-
4648
- // if you get a focus event, allow some reasonable time for the
4649
- // corresponding change event. realistically this should be immediate,
4650
- // but as long as there's not a lot of logic waiting on a cancel, it
4651
- // doesn't really matter.
4652
-
4653
- const window_focus = () => {
4654
-
4655
- // prevent this from accidentally being called more than once
4656
- window.removeEventListener('focus', window_focus);
4657
- timeout = setTimeout(finalize, 250);
4658
- }
4659
-
4660
- const change_handler = () => {
4661
- if (timeout) {
4662
- clearTimeout(timeout);
4663
- timeout = undefined; // necessary?
4664
- }
4665
- finalize(file_chooser.files ? file_chooser.files[0] : undefined);
4666
- }
4667
-
4668
- // our finalize method cleans up and resolves
4669
-
4670
- finalize = (file?: File) => {
4671
- file_chooser.removeEventListener('change', change_handler);
4672
- window.removeEventListener('focus', window_focus);
4673
- resolve(file);
4674
- };
4675
-
4676
- file_chooser.addEventListener('change', change_handler);
4677
- window.addEventListener('focus', window_focus);
4678
-
4679
- file_chooser.click();
4680
-
4681
-
4682
- });
4683
-
4684
- }
4685
- */
4686
-
4687
4643
  /**
4688
4644
  * Insert an image. This method will open a file chooser and (if an image
4689
4645
  * is selected) insert the image into the document.
@@ -4728,7 +4684,7 @@ export class EmbeddedSpreadsheet {
4728
4684
  }
4729
4685
  }
4730
4686
 
4731
- const img = document.createElement('img');
4687
+ const img = DOMUtilities.Create('img');
4732
4688
  img.src = contents;
4733
4689
 
4734
4690
  // this is to let the browser figure out the image size.
@@ -5093,7 +5049,7 @@ export class EmbeddedSpreadsheet {
5093
5049
  const reference = ValidateURI(annotation.data.data.src);
5094
5050
  if (reference) {
5095
5051
 
5096
- const img = document.createElement('img');
5052
+ const img = DOMUtilities.Create('img');
5097
5053
  img.src = reference;
5098
5054
 
5099
5055
  if (annotation.data.data.scale === 'fixed') {
@@ -5142,9 +5098,27 @@ export class EmbeddedSpreadsheet {
5142
5098
 
5143
5099
  // console.info(json);
5144
5100
 
5101
+ // why are we saving this here? and should we not route
5102
+ // through the method? I guess the question should be, is
5103
+ // the visibilitychange handler not sufficient to save the
5104
+ // file? (...)
5105
+
5106
+ // I would prefer to just save once, on visibilitychange -> hidden.
5107
+ // the problem is that this method gets called on "data" events,
5108
+ // which should not trigger saving. we could check the flag?
5109
+
5110
+ // if this does come back, (1) use a method -- maybe split the
5111
+ // SaveLocalStorage method in two, and call the inner half (because
5112
+ // we've already serialized); and (2) gate on dirty.
5113
+
5114
+ // but for the time being we're removing it.
5115
+
5116
+ /*
5145
5117
  if (this.options.local_storage) {
5146
5118
  localStorage.setItem(this.options.local_storage, json);
5147
5119
  }
5120
+ */
5121
+
5148
5122
  if (this.options.undo) {
5149
5123
  this.PushUndo(json, undo_selection, false);
5150
5124
  }
@@ -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
 
@@ -90,6 +90,15 @@
90
90
  white-space: nowrap;
91
91
  position: relative;
92
92
  // -webkit-user-modify: read-write;
93
+
94
+ // this fixes the firefox issue (firefox removing spaces), but
95
+ // I'm not sure if it will cause other problems. for the overlay
96
+ // editor we're trapping CR, so it shouldn't be a typing problem --
97
+ // it might be an issue on paste though. also need to check safari
98
+ // with pre here.
99
+
100
+ white-space: pre;
101
+
93
102
  }
94
103
 
95
104
  /** fix for firefox layout bug */
@@ -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'] = {
@@ -642,6 +642,9 @@ export class Exporter {
642
642
  // is this backwards, vis a vis our rendering? I think it might be...
643
643
  // YES: should be row pattern -> row -> column -> cell [corrected]
644
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
+
645
648
  // if (sheet.row_style && sheet.row_style[row]) {
646
649
  // list.push(sheet.row_style[row]);
647
650
  // }
@@ -1270,6 +1273,7 @@ export class Exporter {
1270
1273
  },
1271
1274
  dataValidations: {},
1272
1275
  hyperlinks: {},
1276
+ conditionalFormatting: {},
1273
1277
  pageMargins: {
1274
1278
  a$: {
1275
1279
  left: 0.7,
@@ -2157,7 +2161,13 @@ export class Exporter {
2157
2161
  if (conditionalFormatting.length) {
2158
2162
  dom.worksheet.conditionalFormatting = (conditionalFormatting.length > 1) ? conditionalFormatting : conditionalFormatting[0];
2159
2163
  }
2160
-
2164
+ else {
2165
+ delete dom.worksheet.conditionalFormatting;
2166
+ }
2167
+
2168
+ }
2169
+ else {
2170
+ delete dom.worksheet.conditionalFormatting;
2161
2171
  }
2162
2172
 
2163
2173
  // --- merges ------------------------------------------------------------
@@ -2333,9 +2343,9 @@ export class Exporter {
2333
2343
 
2334
2344
  // --- move page margins -------------------------------------------------
2335
2345
 
2336
- const margins = dom.worksheet.pageMargins;
2337
- delete dom.worksheet.pageMargins;
2338
- dom.worksheet.pageMargins = margins;
2346
+ // const margins = dom.worksheet.pageMargins;
2347
+ // delete dom.worksheet.pageMargins;
2348
+ // dom.worksheet.pageMargins = margins;
2339
2349
 
2340
2350
  // --- end? --------------------------------------------------------------
2341
2351
 
@@ -424,17 +424,18 @@ export class StyleCache {
424
424
  fill.pattern_type = 'solid';
425
425
  if (composite.fill.text) {
426
426
  fill.fg_color = { argb: composite.fill.text };
427
+ options.fill = fill;
427
428
  }
428
429
  else if (typeof composite.fill.theme === 'number') {
429
430
  fill.fg_color = { theme: composite.fill.theme };
430
431
  if (composite.fill.tint) {
431
432
  fill.fg_color.tint = composite.fill.tint;
432
433
  }
434
+ options.fill = fill;
433
435
  }
434
436
  else {
435
- fill.fg_color = { theme: 1 };
437
+ // fill.fg_color = { theme: 1 };
436
438
  }
437
- options.fill = fill;
438
439
  }
439
440
 
440
441
  if (composite.wrap) {