@trebco/treb 28.13.2 → 28.15.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.
- package/dist/treb-spreadsheet-light.mjs +9 -9
- package/dist/treb-spreadsheet.mjs +15 -15
- 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 +405 -57
- package/treb-calculator/src/utilities.ts +37 -24
- 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 +66 -13
- package/treb-export/src/shared-strings2.ts +1 -1
- package/treb-export/src/workbook-style2.ts +25 -64
- 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 +48 -3
- package/treb-grid/src/types/annotation.ts +17 -3
- package/treb-grid/src/types/grid.ts +9 -1
- package/treb-grid/src/types/grid_options.ts +3 -2
- package/treb-grid/src/types/named_range.ts +8 -1
|
@@ -76,6 +76,7 @@ export interface CellXf {
|
|
|
76
76
|
horizontal_alignment?: string;
|
|
77
77
|
vertical_alignment?: string;
|
|
78
78
|
xfid?: number;
|
|
79
|
+
indent?: number;
|
|
79
80
|
|
|
80
81
|
// FIXME // apply_font?: boolean;
|
|
81
82
|
// FIXME // apply_border?: boolean;
|
|
@@ -157,6 +158,7 @@ export interface StyleOptions {
|
|
|
157
158
|
vertical_alignment?: string;
|
|
158
159
|
wrap?: boolean;
|
|
159
160
|
fill?: Fill;
|
|
161
|
+
indent?: number;
|
|
160
162
|
}
|
|
161
163
|
|
|
162
164
|
|
|
@@ -434,6 +436,8 @@ export class StyleCache {
|
|
|
434
436
|
break;
|
|
435
437
|
}
|
|
436
438
|
|
|
439
|
+
options.indent = composite.indent;
|
|
440
|
+
|
|
437
441
|
if (composite.fill) {
|
|
438
442
|
fill.pattern_type = 'solid';
|
|
439
443
|
if (composite.fill.text) {
|
|
@@ -663,6 +667,10 @@ export class StyleCache {
|
|
|
663
667
|
break;
|
|
664
668
|
}
|
|
665
669
|
|
|
670
|
+
// indent
|
|
671
|
+
|
|
672
|
+
props.indent = (typeof xf.indent === 'string') ? Number(xf.indent) : xf.indent;
|
|
673
|
+
|
|
666
674
|
// wrap
|
|
667
675
|
|
|
668
676
|
if (xf.wrap_text) {
|
|
@@ -714,6 +722,19 @@ export class StyleCache {
|
|
|
714
722
|
|
|
715
723
|
/** map all cell xfs to styles; retain order */
|
|
716
724
|
public CellXfToStyles(): CellStyle[] {
|
|
725
|
+
|
|
726
|
+
/*
|
|
727
|
+
const mapped = this.cell_xfs.map((xf, index) => {
|
|
728
|
+
const style = this.CellXfToStyle(xf);
|
|
729
|
+
if (style.font_size?.value && style.font_size.value > 200) {
|
|
730
|
+
console.info({index, fs: JSON.stringify(style.font_size), style, xf});
|
|
731
|
+
console.info(this);
|
|
732
|
+
}
|
|
733
|
+
return style;
|
|
734
|
+
});
|
|
735
|
+
return mapped;
|
|
736
|
+
*/
|
|
737
|
+
|
|
717
738
|
return this.cell_xfs.map((xf) => this.CellXfToStyle(xf));
|
|
718
739
|
}
|
|
719
740
|
|
|
@@ -1091,6 +1112,7 @@ export class StyleCache {
|
|
|
1091
1112
|
xf.border === border_index &&
|
|
1092
1113
|
xf.number_format === number_format_index &&
|
|
1093
1114
|
!!xf.wrap_text === !!options.wrap &&
|
|
1115
|
+
xf.indent === options.indent &&
|
|
1094
1116
|
((!options.horizontal_alignment && !xf.horizontal_alignment) || options.horizontal_alignment === xf.horizontal_alignment) &&
|
|
1095
1117
|
((!options.vertical_alignment && !xf.vertical_alignment) || options.vertical_alignment === xf.vertical_alignment)) {
|
|
1096
1118
|
|
|
@@ -1106,6 +1128,7 @@ export class StyleCache {
|
|
|
1106
1128
|
fill: fill_index,
|
|
1107
1129
|
border: border_index,
|
|
1108
1130
|
number_format: number_format_index,
|
|
1131
|
+
indent: options.indent,
|
|
1109
1132
|
};
|
|
1110
1133
|
|
|
1111
1134
|
if (options.horizontal_alignment) {
|
|
@@ -1120,44 +1143,6 @@ export class StyleCache {
|
|
|
1120
1143
|
|
|
1121
1144
|
this.cell_xfs.push(new_xf);
|
|
1122
1145
|
|
|
1123
|
-
/*
|
|
1124
|
-
|
|
1125
|
-
// add the node structure
|
|
1126
|
-
|
|
1127
|
-
if (!this.dom) throw new Error('missing dom');
|
|
1128
|
-
const xfs = this.dom.find('./cellXfs');
|
|
1129
|
-
|
|
1130
|
-
if (!xfs) throw new Error('xfs not found');
|
|
1131
|
-
xfs.attrib.count = (Number(xfs.attrib.count || 0) + 1).toString();
|
|
1132
|
-
|
|
1133
|
-
const new_element = Element('xf', {
|
|
1134
|
-
borderId: new_xf.border.toString(),
|
|
1135
|
-
fillId: new_xf.fill.toString(),
|
|
1136
|
-
fontId: new_xf.font.toString(),
|
|
1137
|
-
numFmtId: new_xf.number_format.toString(),
|
|
1138
|
-
});
|
|
1139
|
-
|
|
1140
|
-
if (new_xf.horizontal_alignment || new_xf.vertical_alignment) {
|
|
1141
|
-
const attrs: {[index: string]: string} = {};
|
|
1142
|
-
if (new_xf.horizontal_alignment) {
|
|
1143
|
-
attrs.horizontal = new_xf.horizontal_alignment;
|
|
1144
|
-
}
|
|
1145
|
-
if (new_xf.vertical_alignment) {
|
|
1146
|
-
attrs.vertical = new_xf.vertical_alignment;
|
|
1147
|
-
}
|
|
1148
|
-
if (new_xf.wrap_text) {
|
|
1149
|
-
attrs.wrapText = '1';
|
|
1150
|
-
}
|
|
1151
|
-
new_element.append(Element('alignment', attrs));
|
|
1152
|
-
}
|
|
1153
|
-
|
|
1154
|
-
if (typeof new_xf.xfid !== 'undefined') {
|
|
1155
|
-
new_element.attrib.xfId = new_xf.xfid.toString();
|
|
1156
|
-
}
|
|
1157
|
-
|
|
1158
|
-
xfs.append(new_element);
|
|
1159
|
-
*/
|
|
1160
|
-
|
|
1161
1146
|
return this.cell_xfs.length - 1;
|
|
1162
1147
|
|
|
1163
1148
|
}
|
|
@@ -1202,40 +1187,14 @@ export class StyleCache {
|
|
|
1202
1187
|
|
|
1203
1188
|
|
|
1204
1189
|
this.borders = composite.map(element => {
|
|
1205
|
-
|
|
1206
1190
|
const border: BorderStyle = JSON.parse(JSON.stringify(default_border));
|
|
1207
1191
|
|
|
1208
|
-
/*
|
|
1209
|
-
// we're relying on these being empty strings -> falsy, not a good look
|
|
1210
|
-
|
|
1211
|
-
if (element.left) {
|
|
1212
|
-
// border.left.style = element.left.a$.style;
|
|
1213
|
-
// border.left.color = Number(element.left.color?.a$?.indexed);
|
|
1214
|
-
}
|
|
1215
|
-
|
|
1216
|
-
if (element.right) {
|
|
1217
|
-
// border.right.style = element.right.a$.style;
|
|
1218
|
-
// border.right.color = Number(element.right.color?.a$?.indexed);
|
|
1219
|
-
}
|
|
1220
|
-
|
|
1221
|
-
if (element.top) {
|
|
1222
|
-
// border.top.style = element.top.a$.style;
|
|
1223
|
-
// border.top.color = Number(element.top.color?.a$?.indexed);
|
|
1224
|
-
}
|
|
1225
|
-
|
|
1226
|
-
if (element.bottom) {
|
|
1227
|
-
// border.bottom.style = element.bottom.a$.style;
|
|
1228
|
-
// border.bottom.color = Number(element.bottom.color?.a$?.indexed);
|
|
1229
|
-
}
|
|
1230
|
-
*/
|
|
1231
|
-
|
|
1232
1192
|
ElementToBorderEdge(element.left, border.left);
|
|
1233
1193
|
ElementToBorderEdge(element.right, border.right);
|
|
1234
1194
|
ElementToBorderEdge(element.top, border.top);
|
|
1235
1195
|
ElementToBorderEdge(element.bottom, border.bottom);
|
|
1236
1196
|
|
|
1237
1197
|
return border;
|
|
1238
|
-
|
|
1239
1198
|
});
|
|
1240
1199
|
|
|
1241
1200
|
// ---
|
|
@@ -1255,6 +1214,7 @@ export class StyleCache {
|
|
|
1255
1214
|
xf.horizontal_alignment = element.alignment.a$.horizontal;
|
|
1256
1215
|
xf.vertical_alignment = element.alignment.a$.vertical;
|
|
1257
1216
|
xf.wrap_text = !!element.alignment.a$.wrapText;
|
|
1217
|
+
xf.indent = element.alignment.a$.indent || undefined;
|
|
1258
1218
|
}
|
|
1259
1219
|
|
|
1260
1220
|
return xf;
|
|
@@ -1340,6 +1300,7 @@ export class StyleCache {
|
|
|
1340
1300
|
if (element.color.a$?.rgb) {
|
|
1341
1301
|
font.color_argb = element.color.a$.rgb;
|
|
1342
1302
|
}
|
|
1303
|
+
|
|
1343
1304
|
}
|
|
1344
1305
|
|
|
1345
1306
|
return font;
|
|
@@ -44,6 +44,7 @@ import { Theme } from './workbook-theme2';
|
|
|
44
44
|
import { Sheet, VisibleState } from './workbook-sheet2';
|
|
45
45
|
import type { RelationshipMap } from './relationship';
|
|
46
46
|
import { ZipWrapper } from './zip-wrapper';
|
|
47
|
+
import type { CellStyle } from 'treb-base-types';
|
|
47
48
|
|
|
48
49
|
|
|
49
50
|
/*
|
|
@@ -98,7 +99,23 @@ export interface AnchoredChartDescription {
|
|
|
98
99
|
anchor: TwoCellAnchor,
|
|
99
100
|
}
|
|
100
101
|
|
|
101
|
-
export
|
|
102
|
+
export interface AnchoredTextBoxDescription {
|
|
103
|
+
type: 'textbox';
|
|
104
|
+
style?: CellStyle;
|
|
105
|
+
paragraphs: {
|
|
106
|
+
style?: CellStyle,
|
|
107
|
+
content: {
|
|
108
|
+
text: string,
|
|
109
|
+
style?: CellStyle
|
|
110
|
+
}[],
|
|
111
|
+
}[];
|
|
112
|
+
anchor: TwoCellAnchor,
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
export type AnchoredDrawingPart =
|
|
116
|
+
AnchoredChartDescription |
|
|
117
|
+
AnchoredTextBoxDescription |
|
|
118
|
+
AnchoredImageDescription ;
|
|
102
119
|
|
|
103
120
|
export interface TableFooterType {
|
|
104
121
|
type: 'label'|'formula';
|
|
@@ -363,6 +380,107 @@ export class Workbook {
|
|
|
363
380
|
}
|
|
364
381
|
|
|
365
382
|
}
|
|
383
|
+
else {
|
|
384
|
+
|
|
385
|
+
let style: CellStyle|undefined;
|
|
386
|
+
|
|
387
|
+
const sppr = XMLUtils.FindAll(anchor_node, 'xdr:sp/xdr:spPr')[0];
|
|
388
|
+
if (sppr) {
|
|
389
|
+
style = {};
|
|
390
|
+
const fill = sppr['a:solidFill'];
|
|
391
|
+
if (fill) {
|
|
392
|
+
if (fill['a:schemeClr']?.a$?.val) {
|
|
393
|
+
let m = (fill['a:schemeClr'].a$.val).match(/accent(\d+)/);
|
|
394
|
+
if (m) {
|
|
395
|
+
style.fill = { theme: Number(m[1]) + 3 }
|
|
396
|
+
if (fill['a:schemeClr']['a:lumOff']?.a$?.val) {
|
|
397
|
+
const num = Number(fill['a:schemeClr']['a:lumOff'].a$.val);
|
|
398
|
+
if (!isNaN(num)) {
|
|
399
|
+
style.fill.tint = num / 1e5;
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
const tx = XMLUtils.FindAll(anchor_node, 'xdr:sp/xdr:txBody')[0];
|
|
409
|
+
if (tx) {
|
|
410
|
+
|
|
411
|
+
const paragraphs: {
|
|
412
|
+
style?: CellStyle,
|
|
413
|
+
content: {
|
|
414
|
+
text: string,
|
|
415
|
+
style?: CellStyle
|
|
416
|
+
}[],
|
|
417
|
+
}[] = [];
|
|
418
|
+
|
|
419
|
+
/*
|
|
420
|
+
const content: {
|
|
421
|
+
text: string,
|
|
422
|
+
style?: CellStyle,
|
|
423
|
+
}[][] = [];
|
|
424
|
+
*/
|
|
425
|
+
|
|
426
|
+
const p_list = XMLUtils.FindAll(tx, 'a:p');
|
|
427
|
+
for (const paragraph of p_list) {
|
|
428
|
+
const para: { text: string, style?: CellStyle }[] = [];
|
|
429
|
+
let style: CellStyle|undefined;
|
|
430
|
+
|
|
431
|
+
let appr = paragraph['a:pPr'];
|
|
432
|
+
if (appr) {
|
|
433
|
+
style = {};
|
|
434
|
+
if (appr.a$?.algn === 'r') {
|
|
435
|
+
style.horizontal_align = 'right';
|
|
436
|
+
}
|
|
437
|
+
else if (appr.a$?.algn === 'ctr') {
|
|
438
|
+
style.horizontal_align = 'center';
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
let ar = paragraph['a:r'];
|
|
443
|
+
if (ar) {
|
|
444
|
+
if (!Array.isArray(ar)) {
|
|
445
|
+
ar = [ar];
|
|
446
|
+
}
|
|
447
|
+
for (const line of ar) {
|
|
448
|
+
|
|
449
|
+
const entry: { text: string, style?: CellStyle } = {
|
|
450
|
+
text: line['a:t'] || '',
|
|
451
|
+
};
|
|
452
|
+
|
|
453
|
+
// format
|
|
454
|
+
const fmt = line['a:rPr'];
|
|
455
|
+
if (fmt) {
|
|
456
|
+
entry.style = {};
|
|
457
|
+
if (fmt.a$?.b === '1') {
|
|
458
|
+
entry.style.bold = true;
|
|
459
|
+
}
|
|
460
|
+
if (fmt.a$?.i === '1') {
|
|
461
|
+
entry.style.italic = true;
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
para.push(entry);
|
|
466
|
+
|
|
467
|
+
}
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
paragraphs.push({ content: para, style });
|
|
471
|
+
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
results.push({
|
|
475
|
+
type: 'textbox',
|
|
476
|
+
style,
|
|
477
|
+
paragraphs,
|
|
478
|
+
anchor,
|
|
479
|
+
});
|
|
480
|
+
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
}
|
|
366
484
|
|
|
367
485
|
|
|
368
486
|
}
|
|
@@ -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 === 'right') {
|
|
748
|
+
formatted.push({ text: indent });
|
|
749
|
+
}
|
|
750
|
+
else if (style.horizontal_align !== 'center') {
|
|
751
|
+
formatted.unshift({ 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,26 @@ 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 === 'right') {
|
|
957
|
+
line_string.push({ text: indent, hidden: false, width: indent_width });
|
|
958
|
+
}
|
|
959
|
+
else if (style.horizontal_align !== 'center') {
|
|
960
|
+
line_string.unshift({ text: indent, hidden: false, width: indent_width });
|
|
961
|
+
}
|
|
962
|
+
|
|
963
|
+
}
|
|
964
|
+
|
|
965
|
+
strings.push(line_string);
|
|
932
966
|
|
|
933
967
|
}
|
|
934
968
|
|
|
@@ -939,9 +973,20 @@ export class TileRenderer {
|
|
|
939
973
|
|
|
940
974
|
// simple case
|
|
941
975
|
|
|
976
|
+
|
|
942
977
|
for (const line of md) {
|
|
943
978
|
const parts: RenderTextPart[] = [];
|
|
944
979
|
|
|
980
|
+
if (style.indent) {
|
|
981
|
+
if (style.horizontal_align === 'right') {
|
|
982
|
+
line.push({ text: indent });
|
|
983
|
+
}
|
|
984
|
+
else if (style.horizontal_align !== 'center') {
|
|
985
|
+
line.unshift({ text: indent });
|
|
986
|
+
}
|
|
987
|
+
|
|
988
|
+
}
|
|
989
|
+
|
|
945
990
|
let line_width = 0;
|
|
946
991
|
|
|
947
992
|
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;
|
|
@@ -970,7 +978,7 @@ export class Grid extends GridBase {
|
|
|
970
978
|
|
|
971
979
|
const validated = this.model.named_ranges.ValidateNamed(name);
|
|
972
980
|
if (!validated) {
|
|
973
|
-
console.warn(`invalid name: ${name}
|
|
981
|
+
console.warn(`invalid name: ${name}`, import_data.names[name]);
|
|
974
982
|
continue;
|
|
975
983
|
}
|
|
976
984
|
|
|
@@ -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();
|