@trebco/treb 27.12.1 → 28.0.5
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/README.md +6 -0
- package/dist/treb-spreadsheet-light.mjs +16 -0
- package/dist/treb-spreadsheet.mjs +14 -12
- package/dist/treb.d.ts +31 -3
- package/esbuild-custom-element.mjs +3 -1
- package/package.json +4 -4
- package/treb-base-types/src/dom-utilities.ts +157 -19
- package/treb-base-types/src/theme.ts +5 -4
- package/treb-charts/src/renderer.ts +4 -58
- package/treb-embed/markup/layout.html +4 -0
- package/treb-embed/src/custom-element/spreadsheet-constructor.ts +131 -87
- package/treb-embed/src/embedded-spreadsheet.ts +146 -111
- package/treb-embed/src/options.ts +9 -0
- package/treb-embed/src/spinner.ts +5 -3
- package/treb-embed/src/toolbar-message.ts +5 -0
- package/treb-embed/style/layout.scss +65 -1
- package/treb-grid/src/editors/autocomplete.ts +24 -13
- package/treb-grid/src/editors/editor.ts +43 -139
- package/treb-grid/src/editors/external_editor.ts +1 -1
- package/treb-grid/src/editors/formula_bar.ts +24 -24
- package/treb-grid/src/editors/overlay_editor.ts +6 -2
- package/treb-grid/src/layout/base_layout.ts +34 -25
- package/treb-grid/src/layout/grid_layout.ts +20 -20
- package/treb-grid/src/render/selection-renderer.ts +3 -3
- package/treb-grid/src/render/svg_header_overlay.ts +6 -4
- package/treb-grid/src/render/svg_selection_block.ts +10 -7
- package/treb-grid/src/types/grid.ts +80 -81
- package/treb-grid/src/types/scale-control.ts +69 -81
- package/treb-grid/src/types/sheet.ts +3 -52
- package/treb-grid/src/types/tab_bar.ts +27 -13
- package/treb-grid/src/util/fontmetrics2.ts +24 -21
- package/treb-utils/src/event_source.ts +23 -23
- package/treb-utils/src/index.ts +2 -2
- package/treb-utils/src/measurement.ts +24 -24
- package/treb-utils/src/serialize_html.ts +25 -21
- package/treb-utils/src/dispatch.ts +0 -57
- package/treb-utils/src/resizable.ts +0 -159
|
@@ -11,55 +11,7 @@ import { ColorFunctions, type Color } from 'treb-base-types';
|
|
|
11
11
|
import { Measurement } from 'treb-utils';
|
|
12
12
|
import type { ToolbarMessage } from '../toolbar-message';
|
|
13
13
|
|
|
14
|
-
|
|
15
|
-
data: Record<string, string>;
|
|
16
|
-
text: string;
|
|
17
|
-
style: string;
|
|
18
|
-
title: string;
|
|
19
|
-
classes: string|string[];
|
|
20
|
-
}
|
|
21
|
-
|
|
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);
|
|
27
|
-
if (options.classes) {
|
|
28
|
-
|
|
29
|
-
// you can't use an array destructure in a ternary expression? TIL
|
|
30
|
-
|
|
31
|
-
if (Array.isArray(options.classes)) {
|
|
32
|
-
element.classList.add(...options.classes);
|
|
33
|
-
}
|
|
34
|
-
else {
|
|
35
|
-
element.classList.add(options.classes);
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
}
|
|
39
|
-
if (options.title) {
|
|
40
|
-
element.title = options.title;
|
|
41
|
-
}
|
|
42
|
-
if (options.text) {
|
|
43
|
-
element.textContent = options.text;
|
|
44
|
-
}
|
|
45
|
-
if (options.style) {
|
|
46
|
-
element.setAttribute('style', options.style);
|
|
47
|
-
}
|
|
48
|
-
if (options.data) {
|
|
49
|
-
for (const [key, value] of Object.entries(options.data)) {
|
|
50
|
-
element.dataset[key] = value;
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
for (const [key, value] of Object.entries(attrs)) {
|
|
55
|
-
element.setAttribute(key, value);
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
if (parent) {
|
|
59
|
-
parent.appendChild(element);
|
|
60
|
-
}
|
|
61
|
-
return element;
|
|
62
|
-
}
|
|
14
|
+
import { DOMContext } from 'treb-base-types';
|
|
63
15
|
|
|
64
16
|
/** @internal */
|
|
65
17
|
export class SpreadsheetConstructor {
|
|
@@ -82,6 +34,18 @@ export class SpreadsheetConstructor {
|
|
|
82
34
|
/** root layout element */
|
|
83
35
|
protected layout_element?: HTMLElement;
|
|
84
36
|
|
|
37
|
+
/** views container */
|
|
38
|
+
protected views?: HTMLElement;
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* handle to the revert button, so we can adjust it. we can use
|
|
42
|
+
* container classes for the most part but we are updating the title.
|
|
43
|
+
* (FIXME: double-up the button, no reference required)
|
|
44
|
+
*/
|
|
45
|
+
protected revert_button?: HTMLElement;
|
|
46
|
+
|
|
47
|
+
protected revert_state = false;
|
|
48
|
+
|
|
85
49
|
/** cached controls */
|
|
86
50
|
protected toolbar_controls: Record<string, HTMLElement> = {};
|
|
87
51
|
|
|
@@ -91,6 +55,8 @@ export class SpreadsheetConstructor {
|
|
|
91
55
|
other?: HTMLDivElement,
|
|
92
56
|
} = {};
|
|
93
57
|
|
|
58
|
+
protected DOM: DOMContext;
|
|
59
|
+
|
|
94
60
|
//
|
|
95
61
|
|
|
96
62
|
constructor(root?: HTMLElement|string) {
|
|
@@ -99,19 +65,22 @@ export class SpreadsheetConstructor {
|
|
|
99
65
|
root = document.querySelector(root) as HTMLElement;
|
|
100
66
|
}
|
|
101
67
|
|
|
68
|
+
this.DOM = DOMContext.GetInstance(root?.ownerDocument);
|
|
69
|
+
|
|
102
70
|
// there's a possibility this could be running in a node environment.
|
|
103
71
|
// in that case (wihtout a shim) HTMLElement will not exist, so we can't
|
|
104
72
|
// check type.
|
|
105
73
|
|
|
106
|
-
|
|
107
|
-
this.root = root;
|
|
74
|
+
// but in that case what would root be? (...)
|
|
108
75
|
|
|
109
|
-
|
|
76
|
+
if (this.DOM.view && root instanceof this.DOM.view.HTMLElement) {
|
|
77
|
+
|
|
78
|
+
this.root = root;
|
|
79
|
+
|
|
80
|
+
const style_node = this.DOM.doc?.head.querySelector('style[treb-stylesheet]');
|
|
110
81
|
if (!style_node) {
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
style.textContent = css;
|
|
114
|
-
document.head.prepend(style);
|
|
82
|
+
this.DOM.doc?.head.prepend(
|
|
83
|
+
this.DOM.Create('style', undefined, undefined, { text: css, attrs: { 'treb-stylesheet': '' } }));
|
|
115
84
|
}
|
|
116
85
|
|
|
117
86
|
}
|
|
@@ -273,7 +242,7 @@ export class SpreadsheetConstructor {
|
|
|
273
242
|
if (this.root.hasAttribute('inline-document')) {
|
|
274
243
|
const inline_name = this.root.getAttribute('inline-document') || '';
|
|
275
244
|
for (const element of Array.from(this.root.children)) {
|
|
276
|
-
if (element instanceof HTMLScriptElement) {
|
|
245
|
+
if (this.DOM.view && element instanceof this.DOM.view.HTMLScriptElement) {
|
|
277
246
|
if (element.type === 'application/json') {
|
|
278
247
|
const name = element.getAttribute('name') || '';
|
|
279
248
|
|
|
@@ -367,6 +336,22 @@ export class SpreadsheetConstructor {
|
|
|
367
336
|
this.layout_element?.setAttribute('collapsed', '');
|
|
368
337
|
}
|
|
369
338
|
|
|
339
|
+
// --- revert indicator ----------------------------------------------------
|
|
340
|
+
|
|
341
|
+
const revert_indicator = root.querySelector('[data-command=revert-indicator]');
|
|
342
|
+
if (this.DOM.view && revert_indicator instanceof this.DOM.view.HTMLElement) {
|
|
343
|
+
if (sheet.options.revert_indicator) {
|
|
344
|
+
revert_indicator.addEventListener('click', () => {
|
|
345
|
+
sheet.HandleToolbarMessage({
|
|
346
|
+
command: 'revert-indicator',
|
|
347
|
+
});
|
|
348
|
+
});
|
|
349
|
+
}
|
|
350
|
+
else {
|
|
351
|
+
revert_indicator.style.display = 'none';
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
|
|
370
355
|
// --- toolbar/sidebar -----------------------------------------------------
|
|
371
356
|
|
|
372
357
|
const sidebar = root.querySelector('.treb-layout-sidebar');
|
|
@@ -439,6 +424,10 @@ export class SpreadsheetConstructor {
|
|
|
439
424
|
}
|
|
440
425
|
}
|
|
441
426
|
|
|
427
|
+
if (sheet.options.revert_button) {
|
|
428
|
+
this.revert_button = this.layout_element.querySelector('[data-command=revert]') || undefined;
|
|
429
|
+
}
|
|
430
|
+
|
|
442
431
|
// --- resize --------------------------------------------------------------
|
|
443
432
|
|
|
444
433
|
if (sheet.options.resizable) {
|
|
@@ -448,7 +437,7 @@ export class SpreadsheetConstructor {
|
|
|
448
437
|
const delta = { x: 0, y: 0 };
|
|
449
438
|
|
|
450
439
|
// const resize_container = root.querySelector('.treb-layout-resize-container');
|
|
451
|
-
|
|
440
|
+
this.views = root.querySelector('.treb-views') || undefined;
|
|
452
441
|
|
|
453
442
|
let mask: HTMLElement|undefined;
|
|
454
443
|
let resizer: HTMLElement|undefined;
|
|
@@ -456,10 +445,10 @@ export class SpreadsheetConstructor {
|
|
|
456
445
|
const resize_handle = root.querySelector('.treb-layout-resize-handle') as HTMLElement;
|
|
457
446
|
|
|
458
447
|
// mouse up handler added to mask (when created)
|
|
459
|
-
const
|
|
448
|
+
const mouseup = () => finish();
|
|
460
449
|
|
|
461
450
|
// mouse move handler added to mask (when created)
|
|
462
|
-
const
|
|
451
|
+
const mousemove = ((event: MouseEvent) => {
|
|
463
452
|
if (event.buttons === 0) {
|
|
464
453
|
finish();
|
|
465
454
|
}
|
|
@@ -480,13 +469,15 @@ export class SpreadsheetConstructor {
|
|
|
480
469
|
|
|
481
470
|
if (delta.x || delta.y) {
|
|
482
471
|
const rect = root.getBoundingClientRect();
|
|
483
|
-
|
|
472
|
+
if (!sheet.options.constrain_width) {
|
|
473
|
+
root.style.width = (rect.width + delta.x) + 'px';
|
|
474
|
+
}
|
|
484
475
|
root.style.height = (rect.height + delta.y) + 'px';
|
|
485
476
|
}
|
|
486
477
|
|
|
487
478
|
if (mask) {
|
|
488
|
-
mask.removeEventListener('mouseup',
|
|
489
|
-
mask.removeEventListener('mousemove',
|
|
479
|
+
mask.removeEventListener('mouseup', mouseup);
|
|
480
|
+
mask.removeEventListener('mousemove', mousemove);
|
|
490
481
|
mask.parentElement?.removeChild(mask);
|
|
491
482
|
mask = undefined;
|
|
492
483
|
}
|
|
@@ -503,15 +494,17 @@ export class SpreadsheetConstructor {
|
|
|
503
494
|
|
|
504
495
|
const resize_parent = root.querySelector('.treb-main') as HTMLElement; // was document.body
|
|
505
496
|
|
|
506
|
-
resizer =
|
|
497
|
+
resizer = this.DOM.Div('treb-resize-rect', resize_parent);
|
|
507
498
|
|
|
508
|
-
mask =
|
|
509
|
-
|
|
510
|
-
|
|
499
|
+
mask = this.DOM.Div('treb-resize-mask', resize_parent, {
|
|
500
|
+
attrs: {
|
|
501
|
+
style: 'cursor: nw-resize;'
|
|
502
|
+
},
|
|
503
|
+
events: { mouseup, mousemove },
|
|
511
504
|
});
|
|
512
505
|
|
|
513
|
-
mask.addEventListener('mouseup', mouse_up);
|
|
514
|
-
mask.addEventListener('mousemove', mouse_move);
|
|
506
|
+
// mask.addEventListener('mouseup', mouse_up);
|
|
507
|
+
// mask.addEventListener('mousemove', mouse_move);
|
|
515
508
|
|
|
516
509
|
// resize_handle.classList.add('retain-opacity'); // we're not using this anymore
|
|
517
510
|
|
|
@@ -521,7 +514,7 @@ export class SpreadsheetConstructor {
|
|
|
521
514
|
delta.x = 0;
|
|
522
515
|
delta.y = 0;
|
|
523
516
|
|
|
524
|
-
const layouts = views?.querySelectorAll('.treb-spreadsheet-body');
|
|
517
|
+
const layouts = this.views?.querySelectorAll('.treb-spreadsheet-body');
|
|
525
518
|
const rects = Array.from(layouts||[]).map(element => element.getBoundingClientRect());
|
|
526
519
|
if (rects.length) {
|
|
527
520
|
|
|
@@ -688,7 +681,7 @@ export class SpreadsheetConstructor {
|
|
|
688
681
|
|
|
689
682
|
{
|
|
690
683
|
|
|
691
|
-
let fragment =
|
|
684
|
+
let fragment = this.DOM.Fragment();
|
|
692
685
|
|
|
693
686
|
const length = sheet.document_styles.theme_colors.length;
|
|
694
687
|
const themes = ['Background', 'Text', 'Background', 'Text', 'Accent'];
|
|
@@ -718,7 +711,12 @@ export class SpreadsheetConstructor {
|
|
|
718
711
|
}
|
|
719
712
|
|
|
720
713
|
}
|
|
721
|
-
|
|
714
|
+
|
|
715
|
+
this.DOM.Create('button', undefined, fragment, {
|
|
716
|
+
attrs: { style, title },
|
|
717
|
+
data: { command: 'set-color', color: JSON.stringify(entry.color) },
|
|
718
|
+
});
|
|
719
|
+
|
|
722
720
|
}
|
|
723
721
|
}
|
|
724
722
|
|
|
@@ -726,12 +724,11 @@ export class SpreadsheetConstructor {
|
|
|
726
724
|
|
|
727
725
|
this.swatch_lists.theme?.replaceChildren(fragment);
|
|
728
726
|
|
|
729
|
-
fragment =
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
title: 'Default color',
|
|
727
|
+
fragment = this.DOM.Fragment();
|
|
728
|
+
this.DOM.Create('button', 'treb-default-color', fragment, {
|
|
729
|
+
attrs: { title: 'Default color' },
|
|
733
730
|
data: { command: 'set-color', color: JSON.stringify({}) },
|
|
734
|
-
|
|
731
|
+
});
|
|
735
732
|
|
|
736
733
|
const colors = ['Black', 'White', 'Gray', 'Red', 'Orange', 'Yellow', 'Green', 'Blue', 'Violet'];
|
|
737
734
|
|
|
@@ -742,7 +739,12 @@ export class SpreadsheetConstructor {
|
|
|
742
739
|
|
|
743
740
|
for (const text of [...colors, ...additional_colors]) {
|
|
744
741
|
const style = `background: ${text.toLowerCase()};`;
|
|
745
|
-
|
|
742
|
+
this.DOM.Create('button', undefined, fragment, {
|
|
743
|
+
attrs: { style, title: text, },
|
|
744
|
+
data: { command: 'set-color', color: JSON.stringify({text: text.toLowerCase()})},
|
|
745
|
+
});
|
|
746
|
+
|
|
747
|
+
// Element('button', fragment, { style, title: text, data: { command: 'set-color', color: JSON.stringify({text: text.toLowerCase()})}});
|
|
746
748
|
}
|
|
747
749
|
|
|
748
750
|
this.swatch_lists.other?.replaceChildren(fragment);
|
|
@@ -770,16 +772,15 @@ export class SpreadsheetConstructor {
|
|
|
770
772
|
}
|
|
771
773
|
}
|
|
772
774
|
|
|
773
|
-
const Button = (format: string) =>
|
|
774
|
-
|
|
775
|
-
text: format,
|
|
775
|
+
const Button = (format: string) =>
|
|
776
|
+
this.DOM.Create('button', undefined, undefined, {
|
|
777
|
+
text: format,
|
|
778
|
+
data: { format, command: 'number-format' },
|
|
776
779
|
});
|
|
777
|
-
};
|
|
778
780
|
|
|
779
|
-
const fragment =
|
|
781
|
+
const fragment = this.DOM.Fragment();
|
|
780
782
|
fragment.append(...number_formats.map(format => Button(format)));
|
|
781
|
-
|
|
782
|
-
fragment.append(Element('div', undefined, {}, {separator: ''}));
|
|
783
|
+
fragment.append(this.DOM.Div(undefined, undefined, { attrs: { separator: '' }}));
|
|
783
784
|
fragment.append(...date_formats.map(format => Button(format)));
|
|
784
785
|
|
|
785
786
|
format_menu.textContent = '';
|
|
@@ -787,6 +788,48 @@ export class SpreadsheetConstructor {
|
|
|
787
788
|
|
|
788
789
|
}
|
|
789
790
|
|
|
791
|
+
/**
|
|
792
|
+
* setting explicit state on the revert button (if enabled).
|
|
793
|
+
*
|
|
794
|
+
* @param sheet
|
|
795
|
+
*/
|
|
796
|
+
public UpdateRevertState(sheet: EmbeddedSpreadsheet) {
|
|
797
|
+
|
|
798
|
+
const state = sheet.can_revert;
|
|
799
|
+
|
|
800
|
+
if (this.revert_state === state) {
|
|
801
|
+
return; // nothing to do
|
|
802
|
+
}
|
|
803
|
+
|
|
804
|
+
this.revert_state = state;
|
|
805
|
+
|
|
806
|
+
if (this.revert_button || sheet.options.revert_indicator) {
|
|
807
|
+
|
|
808
|
+
if (this.revert_state) {
|
|
809
|
+
this.views?.classList.add('treb-can-revert');
|
|
810
|
+
}
|
|
811
|
+
else {
|
|
812
|
+
this.views?.classList.remove('treb-can-revert');
|
|
813
|
+
}
|
|
814
|
+
|
|
815
|
+
if (this.revert_button) {
|
|
816
|
+
this.revert_button.dataset.canRevert = state ? 'true' : 'false'; // FIXME: remove
|
|
817
|
+
|
|
818
|
+
// FIXME: container classes, double up button
|
|
819
|
+
|
|
820
|
+
if (state) {
|
|
821
|
+
this.revert_button.classList.remove('sidebar-disabled');
|
|
822
|
+
this.revert_button.title = 'Revert to original version'; // FIXME: strings
|
|
823
|
+
}
|
|
824
|
+
else {
|
|
825
|
+
this.revert_button.classList.add('sidebar-disabled');
|
|
826
|
+
this.revert_button.title = 'This is the original version of the document'; // FIXME: strings
|
|
827
|
+
}
|
|
828
|
+
}
|
|
829
|
+
}
|
|
830
|
+
|
|
831
|
+
}
|
|
832
|
+
|
|
790
833
|
/**
|
|
791
834
|
* replace a given template with its contents.
|
|
792
835
|
*/
|
|
@@ -1133,8 +1176,8 @@ export class SpreadsheetConstructor {
|
|
|
1133
1176
|
|
|
1134
1177
|
if (/firefox/i.test(navigator.userAgent)) {
|
|
1135
1178
|
scroller.addEventListener('scroll', () => {
|
|
1136
|
-
if (
|
|
1137
|
-
|
|
1179
|
+
if (this.DOM.view && this.DOM.doc?.activeElement instanceof this.DOM.view.HTMLElement ) {
|
|
1180
|
+
this.DOM.doc.activeElement?.blur();
|
|
1138
1181
|
}
|
|
1139
1182
|
});
|
|
1140
1183
|
}
|
|
@@ -1288,6 +1331,7 @@ export class SpreadsheetConstructor {
|
|
|
1288
1331
|
case 'reset':
|
|
1289
1332
|
this.UpdateDocumentStyles(sheet, format_menu);
|
|
1290
1333
|
this.UpdateSelectionStyle(sheet, toolbar, comment_box);
|
|
1334
|
+
this.UpdateRevertState(sheet);
|
|
1291
1335
|
break;
|
|
1292
1336
|
|
|
1293
1337
|
case 'selection':
|