@trebco/treb 28.13.2 → 28.15.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/dist/treb-spreadsheet-light.mjs +9 -9
- package/dist/treb-spreadsheet.mjs +9 -9
- package/dist/treb.d.ts +19 -3
- package/package.json +1 -1
- package/treb-base-types/src/style.ts +3 -0
- package/treb-calculator/src/expression-calculator.ts +9 -5
- package/treb-calculator/src/functions/base-functions.ts +334 -57
- package/treb-embed/markup/layout.html +15 -10
- package/treb-embed/markup/toolbar.html +5 -5
- package/treb-embed/src/custom-element/spreadsheet-constructor.ts +38 -2
- package/treb-embed/src/embedded-spreadsheet.ts +66 -2
- package/treb-embed/src/options.ts +5 -0
- package/treb-embed/style/formula-bar.scss +20 -7
- package/treb-embed/style/theme-defaults.scss +20 -0
- package/treb-export/src/export2.ts +6 -1
- package/treb-export/src/import2.ts +67 -3
- package/treb-export/src/shared-strings2.ts +1 -1
- package/treb-export/src/workbook-style2.ts +11 -38
- package/treb-export/src/workbook2.ts +119 -1
- package/treb-grid/src/editors/formula_bar.ts +23 -1
- package/treb-grid/src/render/tile_renderer.ts +46 -3
- package/treb-grid/src/types/annotation.ts +17 -3
- package/treb-grid/src/types/grid.ts +8 -0
- package/treb-grid/src/types/grid_options.ts +3 -2
- package/treb-grid/src/types/named_range.ts +8 -1
|
@@ -29,9 +29,11 @@ import { DOMContext } from 'treb-base-types';
|
|
|
29
29
|
|
|
30
30
|
// --- from formula_bar ---
|
|
31
31
|
|
|
32
|
+
/*
|
|
32
33
|
export interface FormulaBarResizeEvent {
|
|
33
34
|
type: 'formula-bar-resize';
|
|
34
35
|
}
|
|
36
|
+
*/
|
|
35
37
|
|
|
36
38
|
export interface FormulaButtonEvent {
|
|
37
39
|
type: 'formula-button';
|
|
@@ -46,7 +48,7 @@ export interface AddressLabelEvent {
|
|
|
46
48
|
|
|
47
49
|
export type FormulaBar2Event
|
|
48
50
|
= FormulaButtonEvent
|
|
49
|
-
| FormulaBarResizeEvent
|
|
51
|
+
// | FormulaBarResizeEvent
|
|
50
52
|
| AddressLabelEvent
|
|
51
53
|
;
|
|
52
54
|
|
|
@@ -172,6 +174,7 @@ export class FormulaBar extends Editor<FormulaBar2Event|FormulaEditorEvent> {
|
|
|
172
174
|
|
|
173
175
|
this.InitAddressLabel();
|
|
174
176
|
|
|
177
|
+
/*
|
|
175
178
|
if (this.options.insert_function_button) {
|
|
176
179
|
this.button = DOM.Create('button', 'formula-button', inner_node);
|
|
177
180
|
this.button.addEventListener('click', () => {
|
|
@@ -179,6 +182,7 @@ export class FormulaBar extends Editor<FormulaBar2Event|FormulaEditorEvent> {
|
|
|
179
182
|
this.Publish({ type: 'formula-button', formula });
|
|
180
183
|
});
|
|
181
184
|
}
|
|
185
|
+
*/
|
|
182
186
|
|
|
183
187
|
this.container_node = container.querySelector('.treb-editor-container') as HTMLDivElement;
|
|
184
188
|
const target = this.container_node.firstElementChild as HTMLDivElement;
|
|
@@ -270,9 +274,18 @@ export class FormulaBar extends Editor<FormulaBar2Event|FormulaEditorEvent> {
|
|
|
270
274
|
this.RegisterListener(descriptor, 'keydown', this.FormulaKeyDown.bind(this));
|
|
271
275
|
this.RegisterListener(descriptor, 'keyup', this.FormulaKeyUp.bind(this));
|
|
272
276
|
|
|
277
|
+
// why is this here, instead of in markup? just an oversight?
|
|
278
|
+
|
|
273
279
|
if (this.options.expand_formula_button) {
|
|
280
|
+
|
|
281
|
+
let focus_related_target: HTMLElement|undefined;
|
|
282
|
+
|
|
274
283
|
this.expand_button = DOM.Create('button', 'expand-button', inner_node, {
|
|
275
284
|
events: {
|
|
285
|
+
focus: (event: FocusEvent) => {
|
|
286
|
+
focus_related_target = event.relatedTarget instanceof HTMLElement ? event.relatedTarget : undefined;
|
|
287
|
+
},
|
|
288
|
+
|
|
276
289
|
click: (event: MouseEvent) => {
|
|
277
290
|
event.stopPropagation();
|
|
278
291
|
event.preventDefault();
|
|
@@ -285,6 +298,11 @@ export class FormulaBar extends Editor<FormulaBar2Event|FormulaEditorEvent> {
|
|
|
285
298
|
else {
|
|
286
299
|
inner_node.setAttribute('expanded', '');
|
|
287
300
|
}
|
|
301
|
+
|
|
302
|
+
if (focus_related_target) {
|
|
303
|
+
focus_related_target.focus();
|
|
304
|
+
}
|
|
305
|
+
|
|
288
306
|
},
|
|
289
307
|
},
|
|
290
308
|
});
|
|
@@ -297,6 +315,10 @@ export class FormulaBar extends Editor<FormulaBar2Event|FormulaEditorEvent> {
|
|
|
297
315
|
return element === this.active_editor?.node;
|
|
298
316
|
}
|
|
299
317
|
|
|
318
|
+
public IsExpandButton(element: HTMLElement): boolean {
|
|
319
|
+
return this.expand_button && (element === this.expand_button);
|
|
320
|
+
}
|
|
321
|
+
|
|
300
322
|
public InitAddressLabel() {
|
|
301
323
|
|
|
302
324
|
this.address_label.contentEditable = 'true';
|
|
@@ -34,6 +34,7 @@ import type { BaseLayout, TileRange } from '../layout/base_layout';
|
|
|
34
34
|
import type { DataModel, ViewModel } from '../types/data_model';
|
|
35
35
|
import type { GridOptions } from '../types/grid_options';
|
|
36
36
|
|
|
37
|
+
const DEFAULT_INDENT = ' '; // two spaces in the current font
|
|
37
38
|
const BASELINE = 'bottom';
|
|
38
39
|
const WK = /webkit/i.test(typeof navigator === 'undefined' ? '' : navigator?.userAgent || '') ? 1 : 0;
|
|
39
40
|
|
|
@@ -723,6 +724,15 @@ export class TileRenderer {
|
|
|
723
724
|
let override_formatting: string | undefined;
|
|
724
725
|
let formatted = cell.editing ? '' : cell.formatted; // <-- empty on editing, to remove overflows
|
|
725
726
|
|
|
727
|
+
// precalculate indent as string so we can use layout
|
|
728
|
+
|
|
729
|
+
let indent = '';
|
|
730
|
+
if (style.indent) {
|
|
731
|
+
for (let i = 0; i < style.indent; i++) {
|
|
732
|
+
indent += DEFAULT_INDENT;
|
|
733
|
+
}
|
|
734
|
+
}
|
|
735
|
+
|
|
726
736
|
if (Array.isArray(formatted)) {
|
|
727
737
|
|
|
728
738
|
// type 1 is a multi-part formatted string; used for number formats.
|
|
@@ -733,6 +743,15 @@ export class TileRenderer {
|
|
|
733
743
|
|
|
734
744
|
// this is a single line, with number formatting
|
|
735
745
|
|
|
746
|
+
if (indent) {
|
|
747
|
+
if (style.horizontal_align === 'left') {
|
|
748
|
+
formatted.unshift({ text: indent });
|
|
749
|
+
}
|
|
750
|
+
else if (style.horizontal_align === 'right') {
|
|
751
|
+
formatted.push({ text: indent });
|
|
752
|
+
}
|
|
753
|
+
}
|
|
754
|
+
|
|
736
755
|
for (const part of formatted) {
|
|
737
756
|
if (part.flag === TextPartFlag.formatting) {
|
|
738
757
|
override_formatting = part.text;
|
|
@@ -810,11 +829,14 @@ export class TileRenderer {
|
|
|
810
829
|
|
|
811
830
|
// for wrapping
|
|
812
831
|
|
|
813
|
-
|
|
832
|
+
let bound = cell_width - (2 * this.cell_edge_buffer);
|
|
814
833
|
const strings: RenderTextPart[][] = [];
|
|
815
834
|
|
|
816
835
|
if (style.wrap) {
|
|
817
836
|
|
|
837
|
+
const indent_width = (indent && style.horizontal_align !== 'center') ? context.measureText(indent).width : 0;
|
|
838
|
+
bound -= indent_width;
|
|
839
|
+
|
|
818
840
|
for (const line of md) {
|
|
819
841
|
|
|
820
842
|
// we should probably normalize whitespace -- because formatting
|
|
@@ -921,14 +943,25 @@ export class TileRenderer {
|
|
|
921
943
|
|
|
922
944
|
max_width = Math.max(max_width, last.width);
|
|
923
945
|
|
|
924
|
-
|
|
946
|
+
const line_string = line2.map((metric) => {
|
|
925
947
|
return {
|
|
926
948
|
...metric.part,
|
|
927
949
|
hidden: false,
|
|
928
950
|
width: metric.width,
|
|
929
951
|
text: metric.text,
|
|
930
952
|
};
|
|
931
|
-
})
|
|
953
|
+
});
|
|
954
|
+
|
|
955
|
+
if (style.indent) {
|
|
956
|
+
if (style.horizontal_align === 'left') {
|
|
957
|
+
line_string.unshift({ text: indent, hidden: false, width: indent_width });
|
|
958
|
+
}
|
|
959
|
+
else if (style.horizontal_align === 'right') {
|
|
960
|
+
line_string.push({ text: indent, hidden: false, width: indent_width });
|
|
961
|
+
}
|
|
962
|
+
}
|
|
963
|
+
|
|
964
|
+
strings.push(line_string);
|
|
932
965
|
|
|
933
966
|
}
|
|
934
967
|
|
|
@@ -939,9 +972,19 @@ export class TileRenderer {
|
|
|
939
972
|
|
|
940
973
|
// simple case
|
|
941
974
|
|
|
975
|
+
|
|
942
976
|
for (const line of md) {
|
|
943
977
|
const parts: RenderTextPart[] = [];
|
|
944
978
|
|
|
979
|
+
if (style.indent) {
|
|
980
|
+
if (style.horizontal_align === 'left') {
|
|
981
|
+
line.unshift({ text: indent });
|
|
982
|
+
}
|
|
983
|
+
else if (style.horizontal_align === 'right') {
|
|
984
|
+
line.push({ text: indent });
|
|
985
|
+
}
|
|
986
|
+
}
|
|
987
|
+
|
|
945
988
|
let line_width = 0;
|
|
946
989
|
|
|
947
990
|
for (const element of line) {
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
*
|
|
20
20
|
*/
|
|
21
21
|
|
|
22
|
-
import type { ICellAddress, AnnotationLayout, IRectangle } from 'treb-base-types';
|
|
22
|
+
import type { ICellAddress, AnnotationLayout, IRectangle, CellStyle } from 'treb-base-types';
|
|
23
23
|
import { Rectangle } from 'treb-base-types';
|
|
24
24
|
|
|
25
25
|
/**
|
|
@@ -97,7 +97,7 @@ export interface ImageAnnotationData {
|
|
|
97
97
|
|
|
98
98
|
}
|
|
99
99
|
|
|
100
|
-
export type AnnotationType = 'treb-chart'|'image'|'external';
|
|
100
|
+
export type AnnotationType = 'treb-chart'|'image'|'textbox'|'external';
|
|
101
101
|
|
|
102
102
|
/**
|
|
103
103
|
* splitting persisted data from the annotation class. that class might
|
|
@@ -182,12 +182,26 @@ export interface AnnotationChartData extends AnnotationDataBase {
|
|
|
182
182
|
type: 'treb-chart';
|
|
183
183
|
}
|
|
184
184
|
|
|
185
|
+
export interface AnnotationTextBoxData extends AnnotationDataBase {
|
|
186
|
+
type: 'textbox';
|
|
187
|
+
data: {
|
|
188
|
+
style?: CellStyle;
|
|
189
|
+
paragraphs: {
|
|
190
|
+
style?: CellStyle,
|
|
191
|
+
content: {
|
|
192
|
+
text: string,
|
|
193
|
+
style?: CellStyle
|
|
194
|
+
}[],
|
|
195
|
+
}[];
|
|
196
|
+
};
|
|
197
|
+
};
|
|
198
|
+
|
|
185
199
|
export interface AnnotationExternalData extends AnnotationDataBase {
|
|
186
200
|
type: 'external';
|
|
187
201
|
data: Record<string, string>;
|
|
188
202
|
}
|
|
189
203
|
|
|
190
|
-
export type AnnotationData = AnnotationChartData | AnnotationImageData | AnnotationExternalData;
|
|
204
|
+
export type AnnotationData = AnnotationChartData | AnnotationImageData | AnnotationExternalData | AnnotationTextBoxData;
|
|
191
205
|
|
|
192
206
|
/**
|
|
193
207
|
* why is this a class? it doesn't do anything.
|
|
@@ -634,6 +634,14 @@ export class Grid extends GridBase {
|
|
|
634
634
|
this.editing_annotation = annotation;
|
|
635
635
|
this.layout.ShowSelections(true);
|
|
636
636
|
}
|
|
637
|
+
else if (this.formula_bar?.IsExpandButton(event.relatedTarget as HTMLElement)) {
|
|
638
|
+
|
|
639
|
+
// for this particular case, do nothing. basically you are
|
|
640
|
+
// expanding/contracting the formula bar. we want to preserve
|
|
641
|
+
// the selected annotation, if any. after the operation we'll
|
|
642
|
+
// restore focus.
|
|
643
|
+
|
|
644
|
+
}
|
|
637
645
|
else {
|
|
638
646
|
if (this.selected_annotation === annotation) {
|
|
639
647
|
this.selected_annotation = undefined;
|
|
@@ -68,8 +68,9 @@ export interface GridOptions {
|
|
|
68
68
|
/* * show delete tab in the tab bar */
|
|
69
69
|
// delete_tab?: boolean;
|
|
70
70
|
|
|
71
|
-
|
|
71
|
+
/* * show the "insert function" button. requires formula bar. * /
|
|
72
72
|
insert_function_button?: boolean;
|
|
73
|
+
*/
|
|
73
74
|
|
|
74
75
|
/** button to increase/reduce size of formula editor */
|
|
75
76
|
expand_formula_button?: boolean;
|
|
@@ -97,7 +98,7 @@ export const DefaultGridOptions: GridOptions = {
|
|
|
97
98
|
formula_bar: true,
|
|
98
99
|
add_tab: false,
|
|
99
100
|
tab_bar: 'auto',
|
|
100
|
-
insert_function_button: false,
|
|
101
|
+
// insert_function_button: false,
|
|
101
102
|
expand_formula_button: false,
|
|
102
103
|
expand: true,
|
|
103
104
|
repaint_on_cell_change: true,
|
|
@@ -92,10 +92,17 @@ export class NamedRangeCollection {
|
|
|
92
92
|
console.warn('invalid name');
|
|
93
93
|
return false;
|
|
94
94
|
}
|
|
95
|
+
|
|
96
|
+
// why is this considered invalid here? I've seen it done.
|
|
97
|
+
// maybe something we're doing with these ranges doesn't
|
|
98
|
+
// collapse them? (...)
|
|
99
|
+
|
|
95
100
|
if (range.entire_column || range.entire_row) {
|
|
96
|
-
console.
|
|
101
|
+
console.info({range});
|
|
102
|
+
console.warn(`invalid range`);
|
|
97
103
|
return false;
|
|
98
104
|
}
|
|
105
|
+
|
|
99
106
|
this.forward[validated] = range;
|
|
100
107
|
if (apply) {
|
|
101
108
|
this.RebuildList();
|