@trebco/treb 32.4.1 → 32.5.0

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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@trebco/treb",
3
- "version": "32.4.1",
3
+ "version": "32.5.0",
4
4
  "license": "LGPL-3.0-or-later",
5
5
  "homepage": "https://treb.app",
6
6
  "repository": {
@@ -16,7 +16,7 @@
16
16
  "@types/uzip": "^0.20201231.0",
17
17
  "base64-js": "^1.5.1",
18
18
  "cssnano": "^7.0.4",
19
- "esbuild": "^0.24.0",
19
+ "esbuild": "^0.25.0",
20
20
  "eslint": "^9.9.1",
21
21
  "fast-xml-parser": "^4.0.7",
22
22
  "html-minifier-terser": "^7.2.0",
@@ -1532,9 +1532,9 @@ export class EmbeddedSpreadsheet<USER_DATA_TYPE = unknown> {
1532
1532
  }
1533
1533
 
1534
1534
  /**
1535
- * @internal
1535
+ *
1536
1536
  */
1537
- public ConditionalFormatDataBar(range: RangeReference|undefined, options?: ConditionalFormatDataBarOptions): ConditionalFormat {
1537
+ public ConditionalFormatDataBars(range: RangeReference|undefined, options?: ConditionalFormatDataBarOptions): ConditionalFormat {
1538
1538
 
1539
1539
  const area = this.RangeOrSelection(range, 'invalid range (no selection)');
1540
1540
 
@@ -68,6 +68,7 @@
68
68
  & .treb-spreadsheet-tabs>li {
69
69
  display: inline-block;
70
70
  position: relative;
71
+ margin: 0;
71
72
  }
72
73
 
73
74
  & .treb-spreadsheet-tabs>li,
@@ -21,8 +21,8 @@
21
21
 
22
22
  import type { ImageOptions } from './embedded-image';
23
23
  import { EmbeddedImage } from './embedded-image';
24
- import type { ChartOptions} from './chart2';
25
- import { Chart} from './chart2';
24
+ import type { ChartOptions} from './chart';
25
+ import { Chart} from './chart';
26
26
  import type { RelationshipMap} from '../relationship';
27
27
  import { AddRel } from '../relationship';
28
28
  import type { DOMContent } from '../xml-utils';
@@ -44,10 +44,10 @@ import { Area, Cells, ValueType, Style, IsHTMLColor, IsThemeColor, ThemeColorInd
44
44
  import type { XmlBuilderOptions} from 'fast-xml-parser';
45
45
  import { XMLParser } from 'fast-xml-parser';
46
46
 
47
- import { SharedStrings } from './shared-strings2';
48
- import type { XlColor, BorderEdge } from './workbook-style2';
49
- import { StyleCache } from './workbook-style2';
50
- import { Theme } from './workbook-theme2';
47
+ import { SharedStrings } from './shared-strings';
48
+ import type { XlColor, BorderEdge } from './workbook-style';
49
+ import { StyleCache } from './workbook-style';
50
+ import { Theme } from './workbook-theme';
51
51
 
52
52
  import type { RelationshipMap} from './relationship';
53
53
  import { AddRel } from './relationship';
@@ -57,12 +57,12 @@ import type { UnitAddress, UnitRange, ExpressionUnit} from 'treb-parser';
57
57
  import { Parser } from 'treb-parser';
58
58
 
59
59
  // FIXME: move
60
- import type { ChartOptions } from './drawing2/chart2';
61
- import { Chart } from './drawing2/chart2';
62
- import type { ImageOptions } from './drawing2/embedded-image';
63
- import type { TwoCellAnchor } from './drawing2/drawing2';
64
- import { Drawing } from './drawing2/drawing2';
65
- import { ConditionalFormatOperators, type TableDescription, type TableFooterType } from './workbook2';
60
+ import type { ChartOptions } from './drawing/chart';
61
+ import { Chart } from './drawing/chart';
62
+ import type { ImageOptions } from './drawing/embedded-image';
63
+ import type { TwoCellAnchor } from './drawing/drawing';
64
+ import { Drawing } from './drawing/drawing';
65
+ import { ConditionalFormatOperators, type TableDescription, type TableFooterType } from './workbook';
66
66
  import type { AnnotationData } from 'treb-data-model/src/annotation';
67
67
  import { ZipWrapper } from './zip-wrapper';
68
68
 
@@ -179,7 +179,7 @@ export class Exporter {
179
179
  }
180
180
 
181
181
  const parsed = Base64JS.toByteArray(template);
182
- this.zip = new ZipWrapper(parsed);
182
+ this.zip = new ZipWrapper(parsed.buffer as ArrayBuffer);
183
183
 
184
184
  }
185
185
 
@@ -24,17 +24,17 @@
24
24
  // import UZip from 'uzip';
25
25
  import Base64JS from 'base64-js';
26
26
 
27
- import type { AnchoredChartDescription, AnchoredImageDescription, AnchoredTextBoxDescription} from './workbook2';
28
- import { ChartType, ConditionalFormatOperators, Workbook } from './workbook2';
27
+ import type { AnchoredChartDescription, AnchoredImageDescription, AnchoredTextBoxDescription} from './workbook';
28
+ import { ChartType, ConditionalFormatOperators, Workbook } from './workbook';
29
29
  import type { ParseResult } from 'treb-parser';
30
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
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
- import type { Sheet} from './workbook-sheet2';
36
- import { VisibleState } from './workbook-sheet2';
37
- import type { CellAnchor } from './drawing2/drawing2';
35
+ import type { Sheet} from './workbook-sheet';
36
+ import { VisibleState } from './workbook-sheet';
37
+ import type { CellAnchor } from './drawing/drawing';
38
38
  import { type GenericDOMElement, XMLUtils } from './xml-utils';
39
39
 
40
40
  // import { one_hundred_pixels } from './constants';
@@ -79,7 +79,26 @@ interface ConditionalFormatRule {
79
79
  priority?: string;
80
80
  operator?: string;
81
81
  };
82
+
82
83
  formula?: string|[number,number]|{t$: string};
84
+
85
+ dataBar?: {
86
+ a$?: {
87
+ showValue?: string;
88
+ },
89
+ color?: {
90
+ a$: {
91
+ rgb?: string;
92
+ }
93
+ }
94
+ }
95
+
96
+ extLst?: {
97
+ ext?: {
98
+ 'x14:id'?: string;
99
+ }
100
+ }
101
+
83
102
  colorScale?: {
84
103
  cfvo?: {
85
104
  a$: {
@@ -383,7 +402,7 @@ export class Importer {
383
402
 
384
403
  }
385
404
 
386
- public ParseConditionalFormat(address: RangeType|AddressType, rule: ConditionalFormatRule): ConditionalFormat|ConditionalFormat[]|undefined {
405
+ public ParseConditionalFormat(address: RangeType|AddressType, rule: ConditionalFormatRule, extensions?: any[]): ConditionalFormat|ConditionalFormat[]|undefined {
387
406
 
388
407
  const area = this.AddressToArea(address);
389
408
  const operators = ConditionalFormatOperators;
@@ -543,6 +562,44 @@ export class Importer {
543
562
  }
544
563
  break;
545
564
 
565
+ case 'dataBar':
566
+ {
567
+ const hide_values = (rule.dataBar?.a$?.showValue === '0');
568
+ let extension: any = undefined;
569
+
570
+ if (rule.extLst?.ext?.['x14:id']) {
571
+ for (const test of (extensions || [])) {
572
+ if (test['x14:cfRule']?.a$?.id === rule.extLst.ext['x14:id']) {
573
+ extension = test;
574
+ break;
575
+ }
576
+ }
577
+ if (!extension) {
578
+ console.info("conditional format extension not found");
579
+ }
580
+ }
581
+
582
+ if (rule.dataBar?.color?.a$?.rgb) {
583
+
584
+ let negative: Color|undefined = undefined;
585
+
586
+ if (extension?.['x14:cfRule']?.['x14:dataBar']?.['x14:negativeFillColor']?.a$?.rgb) {
587
+ const rgb = extension['x14:cfRule']['x14:dataBar']['x14:negativeFillColor'].a$.rgb;
588
+ negative = { text: '#' + rgb.toString().substring(2) };
589
+ }
590
+
591
+ const fill: Color = { text: '#' + rule.dataBar.color.a$.rgb.substring(2) };
592
+ return {
593
+ type: 'data-bar',
594
+ area,
595
+ fill,
596
+ hide_values,
597
+ negative,
598
+ };
599
+ }
600
+ }
601
+ break;
602
+
546
603
  case 'colorScale':
547
604
  if (rule.colorScale && Array.isArray(rule.colorScale.cfvo) && Array.isArray(rule.colorScale.color)) {
548
605
 
@@ -665,6 +722,11 @@ export class Importer {
665
722
  // conditionals
666
723
 
667
724
  const conditional_formatting = FindAll('worksheet/conditionalFormatting');
725
+
726
+ // we might need extensions as well? TODO
727
+
728
+ const conditional_formattings = FindAll('worksheet/extLst/ext/x14:conditionalFormattings/x14:conditionalFormatting');
729
+
668
730
  for (const element of conditional_formatting) {
669
731
  if (element.a$?.sqref ){
670
732
 
@@ -678,7 +740,7 @@ export class Importer {
678
740
  if (element.cfRule) {
679
741
  const rules = Array.isArray(element.cfRule) ? element.cfRule : [element.cfRule];
680
742
  for (const rule of rules) {
681
- const format = this.ParseConditionalFormat(area, rule as unknown as ConditionalFormatRule);
743
+ const format = this.ParseConditionalFormat(area, rule as unknown as ConditionalFormatRule, conditional_formattings);
682
744
  if (format) {
683
745
  if (Array.isArray(format)) {
684
746
  conditional_formats.push(...format);
@@ -43,8 +43,8 @@
43
43
 
44
44
  import type { AddressType, RangeType} from './address-type';
45
45
  import { is_range } from './address-type';
46
- import type { SharedStrings } from './shared-strings2';
47
- import type { Drawing } from './drawing2/drawing2';
46
+ import type { SharedStrings } from './shared-strings';
47
+ import type { Drawing } from './drawing/drawing';
48
48
  import type { RelationshipMap } from './relationship';
49
49
 
50
50
  export interface SheetOptions {
@@ -23,7 +23,7 @@
23
23
  // import { Element, ElementTree as Tree } from 'elementtree';
24
24
 
25
25
  import { type CompositeBorderEdge, Style, type CellStyle, type PropertyKeys, type Color, IsHTMLColor, IsThemeColor, type ThemeColor, type HTMLColor, ThemeColorIndex } from 'treb-base-types';
26
- import { Theme } from './workbook-theme2';
26
+ import { Theme } from './workbook-theme';
27
27
  import { NumberFormatCache } from 'treb-format';
28
28
  import { XMLUtils } from './xml-utils';
29
29
 
@@ -19,8 +19,6 @@
19
19
  *
20
20
  */
21
21
 
22
- import { XMLUtils } from './xml-utils';
23
-
24
22
  export interface ColorSchemeElement {
25
23
  name?: string;
26
24
  value?: string;
@@ -28,15 +28,15 @@ const xmlparser2 = new XMLParser(XMLOptions2);
28
28
 
29
29
  // import * as he from 'he';
30
30
 
31
- import type { TwoCellAnchor, CellAnchor } from './drawing2/drawing2';
31
+ import type { TwoCellAnchor, CellAnchor } from './drawing/drawing';
32
32
 
33
- import { SharedStrings } from './shared-strings2';
34
- import { StyleCache } from './workbook-style2';
35
- import { Theme } from './workbook-theme2';
36
- import { Sheet, VisibleState } from './workbook-sheet2';
33
+ import { SharedStrings } from './shared-strings';
34
+ import { StyleCache } from './workbook-style';
35
+ import { Theme } from './workbook-theme';
36
+ import { Sheet, VisibleState } from './workbook-sheet';
37
37
  import type { RelationshipMap } from './relationship';
38
38
  import { ZipWrapper } from './zip-wrapper';
39
- import type { CellStyle, ICellAddress, ThemeColor } from 'treb-base-types';
39
+ import type { CellStyle, ThemeColor } from 'treb-base-types';
40
40
  import type { SerializedNamed } from 'treb-data-model';
41
41
 
42
42
  /**
@@ -1311,7 +1311,7 @@ export class Grid extends GridBase {
1311
1311
  }
1312
1312
 
1313
1313
  if (this.tab_bar) {
1314
- this.tab_bar.Update();
1314
+ this.tab_bar.Update(false); // !user
1315
1315
  }
1316
1316
 
1317
1317
  }
@@ -1320,7 +1320,7 @@ export class Grid extends GridBase {
1320
1320
  * This function is called via Shift+PageUp/PageDown. We need
1321
1321
  * to update to account for hidden sheets, which can't be activated.
1322
1322
  */
1323
- public NextSheet(step = 1): void {
1323
+ public NextSheet(step = 1, user = false): void {
1324
1324
 
1325
1325
  if (this.model.sheets.length === 1) {
1326
1326
  return;
@@ -1344,7 +1344,7 @@ export class Grid extends GridBase {
1344
1344
  if (visible[i].sheet === this.active_sheet) {
1345
1345
  let index = (i + step) % visible.length;
1346
1346
  while (index < 0) { index += visible.length; }
1347
- this.ActivateSheet(visible[index].index);
1347
+ this.ActivateSheet(visible[index].index, user);
1348
1348
  return;
1349
1349
  }
1350
1350
  }
@@ -1430,7 +1430,7 @@ export class Grid extends GridBase {
1430
1430
  }
1431
1431
 
1432
1432
  if (this.tab_bar) {
1433
- this.tab_bar.Update();
1433
+ this.tab_bar.Update(false); // !user
1434
1434
  }
1435
1435
 
1436
1436
  }
@@ -1453,7 +1453,7 @@ export class Grid extends GridBase {
1453
1453
  */
1454
1454
  public UpdateTabBar(): void {
1455
1455
  if (this.tab_bar) {
1456
- this.tab_bar.Update();
1456
+ this.tab_bar.Update(true); // user? ...
1457
1457
  }
1458
1458
  }
1459
1459
 
@@ -1730,7 +1730,7 @@ export class Grid extends GridBase {
1730
1730
  // this.tab_bar.Update();
1731
1731
  //}
1732
1732
 
1733
- this.tab_bar?.Update();
1733
+ this.tab_bar?.Update(false);
1734
1734
 
1735
1735
  this.Repaint(true);
1736
1736
  }
@@ -2673,7 +2673,7 @@ export class Grid extends GridBase {
2673
2673
 
2674
2674
  protected RenameSheetInternal(target: Sheet, name: string) {
2675
2675
  super.RenameSheetInternal(target, name);
2676
- this.tab_bar?.Update();
2676
+ this.tab_bar?.Update(false);
2677
2677
 
2678
2678
  }
2679
2679
 
@@ -2898,7 +2898,7 @@ export class Grid extends GridBase {
2898
2898
  activate: this.active_sheet,
2899
2899
  });
2900
2900
 
2901
- if (this.tab_bar) { this.tab_bar.Update(); }
2901
+ if (this.tab_bar) { this.tab_bar.Update(!!command.user); }
2902
2902
 
2903
2903
  this.layout.scroll_offset = this.active_sheet.scroll_offset;
2904
2904
 
@@ -5237,7 +5237,7 @@ export class Grid extends GridBase {
5237
5237
  case 'PageUp':
5238
5238
  case 'PageDown':
5239
5239
  if (event.shiftKey) {
5240
- this.NextSheet(event.key === 'PageUp' ? -1 : 1);
5240
+ this.NextSheet(event.key === 'PageUp' ? -1 : 1, true);
5241
5241
  break;
5242
5242
  }
5243
5243
 
@@ -254,7 +254,7 @@ export class GridBase {
254
254
  * activate sheet, by name or index number
255
255
  * @param sheet number (index into the array) or string (name)
256
256
  */
257
- public ActivateSheet(sheet: number | string): void {
257
+ public ActivateSheet(sheet: number | string, user?: boolean): void {
258
258
 
259
259
  const index = (typeof sheet === 'number') ? sheet : undefined;
260
260
  const name = (typeof sheet === 'string') ? sheet : undefined;
@@ -263,6 +263,7 @@ export class GridBase {
263
263
  key: CommandKey.ActivateSheet,
264
264
  index,
265
265
  name,
266
+ user,
266
267
  });
267
268
 
268
269
  }
@@ -431,6 +431,9 @@ export interface ActivateSheetCommand extends SheetSelection {
431
431
  tab_bar_event?: boolean;
432
432
 
433
433
  force?: boolean;
434
+
435
+ /** user action; use smooth scrolling */
436
+ user?: boolean;
434
437
  }
435
438
 
436
439
  /**
@@ -235,7 +235,8 @@ export class TabBar extends EventSource<TabEvent> {
235
235
 
236
236
  }
237
237
 
238
- public SetActive(tab: HTMLElement, active: boolean): void {
238
+ public SetActive(tab: HTMLElement, active: boolean, user = false): void {
239
+
239
240
  if (active) {
240
241
  // tab.classList.add('treb-selected');
241
242
  tab.setAttribute('selected', '');
@@ -245,8 +246,33 @@ export class TabBar extends EventSource<TabEvent> {
245
246
  tab.style.color = '';
246
247
  }
247
248
 
249
+ // this is forcing the page to scroll if the sheet is below
250
+ // the fold. this is not useful behavior. at the same time,
251
+ // we do need this to work... we probably have to do it manually
252
+ // instead of using scrollIntoView. would be nice if we could
253
+ // toggle this on manual/auto, so user clicks would still be
254
+ // smooth. call that a TODO
255
+
248
256
  requestAnimationFrame(() => {
249
- tab.scrollIntoView({ behavior: 'smooth', block: 'nearest', inline: 'nearest'});
257
+ if (user) {
258
+ tab.scrollIntoView({ behavior: 'smooth', block: 'nearest', inline: 'nearest'});
259
+ }
260
+ else {
261
+ if (tab.parentElement) {
262
+ const left = tab.offsetLeft;
263
+ const width = tab.offsetWidth;
264
+ const container_width = tab.parentElement.clientWidth || 0;
265
+ const scroll_left = tab.parentElement.scrollLeft || 0;
266
+
267
+ if (left > container_width) {
268
+ tab.parentElement.scrollLeft = left - width;
269
+ }
270
+ else if (scroll_left > left) {
271
+ tab.parentElement.scrollLeft = left;
272
+ }
273
+
274
+ }
275
+ }
250
276
  });
251
277
 
252
278
  }
@@ -298,7 +324,7 @@ export class TabBar extends EventSource<TabEvent> {
298
324
  case 'Escape':
299
325
  tab.innerText = sheet.name;
300
326
  this.Publish({ type: 'cancel' });
301
- this.Update();
327
+ this.Update(true);
302
328
  break;
303
329
 
304
330
  default:
@@ -314,7 +340,7 @@ export class TabBar extends EventSource<TabEvent> {
314
340
  this.Publish({ type: 'rename-sheet', name, sheet });
315
341
  }
316
342
  else {
317
- this.Update();
343
+ this.Update(true);
318
344
  }
319
345
  });
320
346
 
@@ -350,7 +376,7 @@ export class TabBar extends EventSource<TabEvent> {
350
376
  // then the classes won't change.
351
377
 
352
378
  for (const candidate of tabs) {
353
- this.SetActive(candidate, candidate === tab);
379
+ this.SetActive(candidate, candidate === tab, true);
354
380
  }
355
381
 
356
382
  this.dragging = true;
@@ -445,8 +471,11 @@ export class TabBar extends EventSource<TabEvent> {
445
471
 
446
472
  /**
447
473
  * update tabs from model.
474
+ *
475
+ * @param user - this is a user action, so use smooth scrolling
476
+ * when activating the tab. otherwise it's automatic so jump.
448
477
  */
449
- public Update(): void {
478
+ public Update(user = false): void {
450
479
 
451
480
  this.tab_color_cache.clear(); // we're setting tab color but it's not getting updated otherwise
452
481
 
@@ -512,7 +541,7 @@ export class TabBar extends EventSource<TabEvent> {
512
541
  tab.style.order = (index * 2).toString();
513
542
  tab.role = 'tab';
514
543
 
515
- this.SetActive(tab, sheet === this.view.active_sheet);
544
+ this.SetActive(tab, sheet === this.view.active_sheet, user);
516
545
 
517
546
  const mousedown = (event: MouseEvent) => this.MouseDownTab(event, tab, sheet, index, tabs);
518
547