@trebco/treb 28.11.1 → 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 +11 -11
- package/dist/treb-spreadsheet.mjs +11 -11
- package/dist/treb.d.ts +27 -3
- package/package.json +1 -1
- package/treb-base-types/src/style.ts +3 -0
- package/treb-calculator/src/calculator.ts +235 -68
- package/treb-calculator/src/descriptors.ts +5 -0
- package/treb-calculator/src/expression-calculator.ts +9 -5
- package/treb-calculator/src/functions/base-functions.ts +410 -21
- package/treb-calculator/src/functions/text-functions.ts +45 -55
- package/treb-calculator/src/primitives.ts +11 -0
- package/treb-calculator/src/utilities.ts +55 -0
- 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 +227 -29
- package/treb-embed/src/options.ts +5 -0
- package/treb-embed/style/dark-theme.scss +1 -0
- package/treb-embed/style/formula-bar.scss +20 -7
- package/treb-embed/style/theme-defaults.scss +20 -0
- package/treb-export/src/export-worker/export-worker.ts +1 -0
- package/treb-export/src/export2.ts +6 -1
- package/treb-export/src/import2.ts +76 -6
- package/treb-export/src/shared-strings2.ts +1 -1
- package/treb-export/src/workbook-style2.ts +89 -52
- package/treb-export/src/workbook2.ts +119 -1
- package/treb-grid/src/editors/editor.ts +7 -0
- 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 +28 -9
- package/treb-grid/src/types/grid_base.ts +10 -105
- package/treb-grid/src/types/grid_options.ts +3 -2
- package/treb-grid/src/types/named_range.ts +8 -1
- package/treb-grid/src/types/serialize_options.ts +5 -0
- package/treb-parser/src/parser-types.ts +27 -4
- package/treb-parser/src/parser.ts +74 -36
|
@@ -224,9 +224,10 @@ export class Exporter {
|
|
|
224
224
|
},
|
|
225
225
|
};
|
|
226
226
|
|
|
227
|
-
if (xf.horizontal_alignment || xf.vertical_alignment || xf.wrap_text) {
|
|
227
|
+
if (xf.horizontal_alignment || xf.vertical_alignment || xf.wrap_text || xf.indent) {
|
|
228
228
|
// block.a$.applyAlignment = 1;
|
|
229
229
|
block.alignment = { a$: {}};
|
|
230
|
+
|
|
230
231
|
if (xf.horizontal_alignment) {
|
|
231
232
|
block.alignment.a$.horizontal = xf.horizontal_alignment;
|
|
232
233
|
}
|
|
@@ -236,6 +237,10 @@ export class Exporter {
|
|
|
236
237
|
if (xf.wrap_text) {
|
|
237
238
|
block.alignment.a$.wrapText = 1;
|
|
238
239
|
}
|
|
240
|
+
if (xf.indent && xf.horizontal_alignment !== 'center') {
|
|
241
|
+
block.alignment.a$.indent = xf.indent;
|
|
242
|
+
}
|
|
243
|
+
|
|
239
244
|
}
|
|
240
245
|
|
|
241
246
|
return block;
|
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
import UZip from 'uzip';
|
|
25
25
|
import Base64JS from 'base64-js';
|
|
26
26
|
|
|
27
|
-
import type { AnchoredChartDescription, AnchoredImageDescription} from './workbook2';
|
|
27
|
+
import type { AnchoredChartDescription, AnchoredImageDescription, AnchoredTextBoxDescription} from './workbook2';
|
|
28
28
|
import { ChartType, ConditionalFormatOperators, Workbook } from './workbook2';
|
|
29
29
|
import type { ParseResult } from 'treb-parser';
|
|
30
30
|
import { Parser } from 'treb-parser';
|
|
@@ -81,7 +81,10 @@ export class Importer {
|
|
|
81
81
|
t?: string;
|
|
82
82
|
s?: string;
|
|
83
83
|
};
|
|
84
|
-
v?: string|number
|
|
84
|
+
v?: string|number|{
|
|
85
|
+
t$: string;
|
|
86
|
+
a$?: any;
|
|
87
|
+
};
|
|
85
88
|
f?: string|{
|
|
86
89
|
t$: string;
|
|
87
90
|
a$?: {
|
|
@@ -167,8 +170,13 @@ export class Importer {
|
|
|
167
170
|
const parse_result = this.parser.Parse(formula); // l10n?
|
|
168
171
|
if (parse_result.expression) {
|
|
169
172
|
this.parser.Walk(parse_result.expression, (unit) => {
|
|
170
|
-
if (unit.type === 'call'
|
|
171
|
-
|
|
173
|
+
if (unit.type === 'call') {
|
|
174
|
+
if (/^_xll\./.test(unit.name)) {
|
|
175
|
+
unit.name = unit.name.substring(5);
|
|
176
|
+
}
|
|
177
|
+
else if (/^_xlfn\./.test(unit.name)) {
|
|
178
|
+
unit.name = unit.name.substring(6);
|
|
179
|
+
}
|
|
172
180
|
}
|
|
173
181
|
return true;
|
|
174
182
|
});
|
|
@@ -214,14 +222,17 @@ export class Importer {
|
|
|
214
222
|
}
|
|
215
223
|
|
|
216
224
|
if (typeof element.v !== 'undefined') {
|
|
217
|
-
|
|
225
|
+
|
|
226
|
+
const V = (typeof element.v === 'object') ? element.v?.t$ : element.v;
|
|
227
|
+
|
|
228
|
+
const num = Number(V.toString());
|
|
218
229
|
if (!isNaN(num)) {
|
|
219
230
|
calculated_type = 'number'; // ValueType.number;
|
|
220
231
|
calculated_value = num;
|
|
221
232
|
}
|
|
222
233
|
else {
|
|
223
234
|
calculated_type = 'string'; // ValueType.string;
|
|
224
|
-
calculated_value =
|
|
235
|
+
calculated_value = V.toString();
|
|
225
236
|
}
|
|
226
237
|
}
|
|
227
238
|
|
|
@@ -384,6 +395,20 @@ export class Importer {
|
|
|
384
395
|
|
|
385
396
|
case 'expression':
|
|
386
397
|
if (rule.formula) {
|
|
398
|
+
|
|
399
|
+
if (typeof rule.formula !== 'string') {
|
|
400
|
+
console.info("conditional expression", {rule});
|
|
401
|
+
if (rule.formula.t$) {
|
|
402
|
+
|
|
403
|
+
// the only case (to date) we've seen here is that the attribute
|
|
404
|
+
// is "xml:space=preserve", which we can ignore (are you sure?)
|
|
405
|
+
// (should we check that?)
|
|
406
|
+
|
|
407
|
+
rule.formula = rule.formula.t$;
|
|
408
|
+
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
|
|
387
412
|
let style = {};
|
|
388
413
|
|
|
389
414
|
if (rule.a$.dxfId) {
|
|
@@ -492,6 +517,26 @@ export class Importer {
|
|
|
492
517
|
const conditional_formatting = FindAll('worksheet/conditionalFormatting');
|
|
493
518
|
for (const element of conditional_formatting) {
|
|
494
519
|
if (element.a$?.sqref ){
|
|
520
|
+
|
|
521
|
+
// FIXME: this attribute might include multiple ranges? e.g.:
|
|
522
|
+
//
|
|
523
|
+
// <conditionalFormatting sqref="B31:I31 B10:E30 G10:I30 F14:F15">
|
|
524
|
+
|
|
525
|
+
const parts = element.a$.sqref.split(/\s+/);
|
|
526
|
+
for (const part of parts) {
|
|
527
|
+
const area = sheet.TranslateAddress(part);
|
|
528
|
+
if (element.cfRule) {
|
|
529
|
+
const rules = Array.isArray(element.cfRule) ? element.cfRule : [element.cfRule];
|
|
530
|
+
for (const rule of rules) {
|
|
531
|
+
const format = this.ParseConditionalFormat(area, rule);
|
|
532
|
+
if (format) {
|
|
533
|
+
conditional_formats.push(format);
|
|
534
|
+
}
|
|
535
|
+
}
|
|
536
|
+
}
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
/*
|
|
495
540
|
const area = sheet.TranslateAddress(element.a$.sqref);
|
|
496
541
|
if (element.cfRule) {
|
|
497
542
|
const rules = Array.isArray(element.cfRule) ? element.cfRule : [element.cfRule];
|
|
@@ -502,6 +547,7 @@ export class Importer {
|
|
|
502
547
|
}
|
|
503
548
|
}
|
|
504
549
|
}
|
|
550
|
+
*/
|
|
505
551
|
}
|
|
506
552
|
}
|
|
507
553
|
|
|
@@ -802,6 +848,7 @@ export class Importer {
|
|
|
802
848
|
const drawings = FindAll('worksheet/drawing');
|
|
803
849
|
const chart_descriptors: AnchoredChartDescription[] = [];
|
|
804
850
|
const image_descriptors: AnchoredImageDescription[] = [];
|
|
851
|
+
const textbox_descriptors: AnchoredTextBoxDescription[] = [];
|
|
805
852
|
|
|
806
853
|
for (const child of drawings) {
|
|
807
854
|
|
|
@@ -826,6 +873,9 @@ export class Importer {
|
|
|
826
873
|
case 'image':
|
|
827
874
|
image_descriptors.push(entry);
|
|
828
875
|
break;
|
|
876
|
+
case 'textbox':
|
|
877
|
+
textbox_descriptors.push(entry);
|
|
878
|
+
break;
|
|
829
879
|
}
|
|
830
880
|
}
|
|
831
881
|
}
|
|
@@ -867,6 +917,26 @@ export class Importer {
|
|
|
867
917
|
|
|
868
918
|
};
|
|
869
919
|
|
|
920
|
+
for (const descriptor of textbox_descriptors) {
|
|
921
|
+
|
|
922
|
+
const layout: AnnotationLayout = {
|
|
923
|
+
tl: AnchorToCorner(descriptor.anchor.from),
|
|
924
|
+
br: AnchorToCorner(descriptor.anchor.to),
|
|
925
|
+
};
|
|
926
|
+
|
|
927
|
+
// console.info({descriptor});
|
|
928
|
+
|
|
929
|
+
annotations.push({
|
|
930
|
+
layout,
|
|
931
|
+
type: 'textbox',
|
|
932
|
+
data: {
|
|
933
|
+
style: descriptor.style,
|
|
934
|
+
paragraphs: descriptor.paragraphs,
|
|
935
|
+
}
|
|
936
|
+
});
|
|
937
|
+
|
|
938
|
+
}
|
|
939
|
+
|
|
870
940
|
for (const descriptor of image_descriptors) {
|
|
871
941
|
if (descriptor && descriptor.image) {
|
|
872
942
|
|
|
@@ -44,7 +44,7 @@ export class SharedStrings {
|
|
|
44
44
|
// <t>text here!</t>
|
|
45
45
|
// </si>
|
|
46
46
|
|
|
47
|
-
if (si.t) {
|
|
47
|
+
if (si.t !== undefined) {
|
|
48
48
|
|
|
49
49
|
// seen recently in the wild, text with leading (or trailing) spaces
|
|
50
50
|
// has an attribute xml:space=preserve (which makes sense, but was not
|
|
@@ -29,6 +29,9 @@ import { XMLUtils } from './xml-utils';
|
|
|
29
29
|
|
|
30
30
|
import { Unescape } from './unescape_xml';
|
|
31
31
|
|
|
32
|
+
// what's the default font size? ... 11pt?
|
|
33
|
+
const DEFAULT_FONT_SIZE = 11;
|
|
34
|
+
|
|
32
35
|
export interface Font {
|
|
33
36
|
size?: number;
|
|
34
37
|
name?: string;
|
|
@@ -73,6 +76,7 @@ export interface CellXf {
|
|
|
73
76
|
horizontal_alignment?: string;
|
|
74
77
|
vertical_alignment?: string;
|
|
75
78
|
xfid?: number;
|
|
79
|
+
indent?: number;
|
|
76
80
|
|
|
77
81
|
// FIXME // apply_font?: boolean;
|
|
78
82
|
// FIXME // apply_border?: boolean;
|
|
@@ -154,6 +158,7 @@ export interface StyleOptions {
|
|
|
154
158
|
vertical_alignment?: string;
|
|
155
159
|
wrap?: boolean;
|
|
156
160
|
fill?: Fill;
|
|
161
|
+
indent?: number;
|
|
157
162
|
}
|
|
158
163
|
|
|
159
164
|
|
|
@@ -269,13 +274,24 @@ export class StyleCache {
|
|
|
269
274
|
|
|
270
275
|
}
|
|
271
276
|
|
|
277
|
+
|
|
272
278
|
if (composite.font_size?.unit && composite.font_size.value) {
|
|
273
|
-
if (composite.font_size.unit
|
|
274
|
-
|
|
279
|
+
if (composite.font_size.unit === 'em') {
|
|
280
|
+
font.size = composite.font_size.value * DEFAULT_FONT_SIZE;
|
|
275
281
|
}
|
|
276
|
-
else {
|
|
282
|
+
else if (composite.font_size.unit === '%') {
|
|
283
|
+
font.size = composite.font_size.value * DEFAULT_FONT_SIZE / 100;
|
|
284
|
+
}
|
|
285
|
+
else if (composite.font_size.unit === 'pt' ){
|
|
277
286
|
font.size = composite.font_size.value;
|
|
278
287
|
}
|
|
288
|
+
else if (composite.font_size.unit === 'px' ){
|
|
289
|
+
font.size = composite.font_size.value * .75; // ?
|
|
290
|
+
}
|
|
291
|
+
else {
|
|
292
|
+
console.warn(`Unhandled font size unit`, composite.font_size);
|
|
293
|
+
}
|
|
294
|
+
|
|
279
295
|
}
|
|
280
296
|
|
|
281
297
|
if (composite.bold) font.bold = true;
|
|
@@ -420,6 +436,8 @@ export class StyleCache {
|
|
|
420
436
|
break;
|
|
421
437
|
}
|
|
422
438
|
|
|
439
|
+
options.indent = composite.indent;
|
|
440
|
+
|
|
423
441
|
if (composite.fill) {
|
|
424
442
|
fill.pattern_type = 'solid';
|
|
425
443
|
if (composite.fill.text) {
|
|
@@ -649,6 +667,10 @@ export class StyleCache {
|
|
|
649
667
|
break;
|
|
650
668
|
}
|
|
651
669
|
|
|
670
|
+
// indent
|
|
671
|
+
|
|
672
|
+
props.indent = xf.indent;
|
|
673
|
+
|
|
652
674
|
// wrap
|
|
653
675
|
|
|
654
676
|
if (xf.wrap_text) {
|
|
@@ -657,6 +679,19 @@ export class StyleCache {
|
|
|
657
679
|
|
|
658
680
|
// borders
|
|
659
681
|
|
|
682
|
+
const BorderEdgeToColor = (edge: BorderEdge): Color|undefined => {
|
|
683
|
+
|
|
684
|
+
// TODO: indexed
|
|
685
|
+
|
|
686
|
+
if (typeof edge.theme !== 'undefined') {
|
|
687
|
+
return {
|
|
688
|
+
theme: edge.theme,
|
|
689
|
+
tint: edge.tint,
|
|
690
|
+
};
|
|
691
|
+
}
|
|
692
|
+
|
|
693
|
+
};
|
|
694
|
+
|
|
660
695
|
const border = this.borders[xf.border || 0];
|
|
661
696
|
if (border) {
|
|
662
697
|
if (border.bottom.style) {
|
|
@@ -666,10 +701,20 @@ export class StyleCache {
|
|
|
666
701
|
else {
|
|
667
702
|
props.border_bottom = 1;
|
|
668
703
|
}
|
|
704
|
+
props.border_bottom_fill = BorderEdgeToColor(border.bottom);
|
|
705
|
+
}
|
|
706
|
+
if (border.left.style) {
|
|
707
|
+
props.border_left = 1;
|
|
708
|
+
props.border_left_fill = BorderEdgeToColor(border.left);
|
|
709
|
+
}
|
|
710
|
+
if (border.top.style) {
|
|
711
|
+
props.border_top = 1;
|
|
712
|
+
props.border_top_fill = BorderEdgeToColor(border.top);
|
|
713
|
+
}
|
|
714
|
+
if (border.right.style) {
|
|
715
|
+
props.border_right = 1;
|
|
716
|
+
props.border_right_fill = BorderEdgeToColor(border.right);
|
|
669
717
|
}
|
|
670
|
-
if (border.left.style) props.border_left = 1;
|
|
671
|
-
if (border.top.style) props.border_top = 1;
|
|
672
|
-
if (border.right.style) props.border_right = 1;
|
|
673
718
|
}
|
|
674
719
|
|
|
675
720
|
return props;
|
|
@@ -1054,6 +1099,7 @@ export class StyleCache {
|
|
|
1054
1099
|
xf.border === border_index &&
|
|
1055
1100
|
xf.number_format === number_format_index &&
|
|
1056
1101
|
!!xf.wrap_text === !!options.wrap &&
|
|
1102
|
+
xf.indent === options.indent &&
|
|
1057
1103
|
((!options.horizontal_alignment && !xf.horizontal_alignment) || options.horizontal_alignment === xf.horizontal_alignment) &&
|
|
1058
1104
|
((!options.vertical_alignment && !xf.vertical_alignment) || options.vertical_alignment === xf.vertical_alignment)) {
|
|
1059
1105
|
|
|
@@ -1069,6 +1115,7 @@ export class StyleCache {
|
|
|
1069
1115
|
fill: fill_index,
|
|
1070
1116
|
border: border_index,
|
|
1071
1117
|
number_format: number_format_index,
|
|
1118
|
+
indent: options.indent,
|
|
1072
1119
|
};
|
|
1073
1120
|
|
|
1074
1121
|
if (options.horizontal_alignment) {
|
|
@@ -1083,44 +1130,6 @@ export class StyleCache {
|
|
|
1083
1130
|
|
|
1084
1131
|
this.cell_xfs.push(new_xf);
|
|
1085
1132
|
|
|
1086
|
-
/*
|
|
1087
|
-
|
|
1088
|
-
// add the node structure
|
|
1089
|
-
|
|
1090
|
-
if (!this.dom) throw new Error('missing dom');
|
|
1091
|
-
const xfs = this.dom.find('./cellXfs');
|
|
1092
|
-
|
|
1093
|
-
if (!xfs) throw new Error('xfs not found');
|
|
1094
|
-
xfs.attrib.count = (Number(xfs.attrib.count || 0) + 1).toString();
|
|
1095
|
-
|
|
1096
|
-
const new_element = Element('xf', {
|
|
1097
|
-
borderId: new_xf.border.toString(),
|
|
1098
|
-
fillId: new_xf.fill.toString(),
|
|
1099
|
-
fontId: new_xf.font.toString(),
|
|
1100
|
-
numFmtId: new_xf.number_format.toString(),
|
|
1101
|
-
});
|
|
1102
|
-
|
|
1103
|
-
if (new_xf.horizontal_alignment || new_xf.vertical_alignment) {
|
|
1104
|
-
const attrs: {[index: string]: string} = {};
|
|
1105
|
-
if (new_xf.horizontal_alignment) {
|
|
1106
|
-
attrs.horizontal = new_xf.horizontal_alignment;
|
|
1107
|
-
}
|
|
1108
|
-
if (new_xf.vertical_alignment) {
|
|
1109
|
-
attrs.vertical = new_xf.vertical_alignment;
|
|
1110
|
-
}
|
|
1111
|
-
if (new_xf.wrap_text) {
|
|
1112
|
-
attrs.wrapText = '1';
|
|
1113
|
-
}
|
|
1114
|
-
new_element.append(Element('alignment', attrs));
|
|
1115
|
-
}
|
|
1116
|
-
|
|
1117
|
-
if (typeof new_xf.xfid !== 'undefined') {
|
|
1118
|
-
new_element.attrib.xfId = new_xf.xfid.toString();
|
|
1119
|
-
}
|
|
1120
|
-
|
|
1121
|
-
xfs.append(new_element);
|
|
1122
|
-
*/
|
|
1123
|
-
|
|
1124
1133
|
return this.cell_xfs.length - 1;
|
|
1125
1134
|
|
|
1126
1135
|
}
|
|
@@ -1144,31 +1153,58 @@ export class StyleCache {
|
|
|
1144
1153
|
|
|
1145
1154
|
composite = FindAll('styleSheet/borders/border');
|
|
1146
1155
|
|
|
1156
|
+
const ElementToBorderEdge = (element: any, edge: BorderEdge) => {
|
|
1157
|
+
|
|
1158
|
+
if (element?.a$) {
|
|
1159
|
+
edge.style = element.a$.style;
|
|
1160
|
+
if (typeof element.color === 'object') {
|
|
1161
|
+
if (typeof element.color.a$?.indexed !== 'undefined') {
|
|
1162
|
+
edge.color = Number(element.color.a$.indexed);
|
|
1163
|
+
}
|
|
1164
|
+
if (typeof element.color.a$?.theme !== 'undefined') {
|
|
1165
|
+
edge.theme = Number(element.color.a$.theme);
|
|
1166
|
+
}
|
|
1167
|
+
if (typeof element.color.a$?.tint !== 'undefined') {
|
|
1168
|
+
edge.tint = Number(element.color.a$.tint);
|
|
1169
|
+
}
|
|
1170
|
+
}
|
|
1171
|
+
}
|
|
1172
|
+
|
|
1173
|
+
};
|
|
1174
|
+
|
|
1175
|
+
|
|
1147
1176
|
this.borders = composite.map(element => {
|
|
1148
1177
|
|
|
1149
1178
|
const border: BorderStyle = JSON.parse(JSON.stringify(default_border));
|
|
1150
1179
|
|
|
1180
|
+
/*
|
|
1151
1181
|
// we're relying on these being empty strings -> falsy, not a good look
|
|
1152
1182
|
|
|
1153
1183
|
if (element.left) {
|
|
1154
|
-
border.left.style = element.left.a$.style;
|
|
1155
|
-
border.left.color = Number(element.left.color?.a$?.indexed);
|
|
1184
|
+
// border.left.style = element.left.a$.style;
|
|
1185
|
+
// border.left.color = Number(element.left.color?.a$?.indexed);
|
|
1156
1186
|
}
|
|
1157
1187
|
|
|
1158
1188
|
if (element.right) {
|
|
1159
|
-
border.right.style = element.right.a$.style;
|
|
1160
|
-
border.right.color = Number(element.right.color?.a$?.indexed);
|
|
1189
|
+
// border.right.style = element.right.a$.style;
|
|
1190
|
+
// border.right.color = Number(element.right.color?.a$?.indexed);
|
|
1161
1191
|
}
|
|
1162
1192
|
|
|
1163
1193
|
if (element.top) {
|
|
1164
|
-
border.top.style = element.top.a$.style;
|
|
1165
|
-
border.top.color = Number(element.top.color?.a$?.indexed);
|
|
1194
|
+
// border.top.style = element.top.a$.style;
|
|
1195
|
+
// border.top.color = Number(element.top.color?.a$?.indexed);
|
|
1166
1196
|
}
|
|
1167
1197
|
|
|
1168
1198
|
if (element.bottom) {
|
|
1169
|
-
border.bottom.style = element.bottom.a$.style;
|
|
1170
|
-
border.bottom.color = Number(element.bottom.color?.a$?.indexed);
|
|
1199
|
+
// border.bottom.style = element.bottom.a$.style;
|
|
1200
|
+
// border.bottom.color = Number(element.bottom.color?.a$?.indexed);
|
|
1171
1201
|
}
|
|
1202
|
+
*/
|
|
1203
|
+
|
|
1204
|
+
ElementToBorderEdge(element.left, border.left);
|
|
1205
|
+
ElementToBorderEdge(element.right, border.right);
|
|
1206
|
+
ElementToBorderEdge(element.top, border.top);
|
|
1207
|
+
ElementToBorderEdge(element.bottom, border.bottom);
|
|
1172
1208
|
|
|
1173
1209
|
return border;
|
|
1174
1210
|
|
|
@@ -1191,6 +1227,7 @@ export class StyleCache {
|
|
|
1191
1227
|
xf.horizontal_alignment = element.alignment.a$.horizontal;
|
|
1192
1228
|
xf.vertical_alignment = element.alignment.a$.vertical;
|
|
1193
1229
|
xf.wrap_text = !!element.alignment.a$.wrapText;
|
|
1230
|
+
xf.indent = element.alignment.a$.indent || undefined;
|
|
1194
1231
|
}
|
|
1195
1232
|
|
|
1196
1233
|
return xf;
|
|
@@ -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
|
}
|
|
@@ -789,6 +789,13 @@ export class Editor<E = FormulaEditorEvent> extends EventSource<E|FormulaEditorE
|
|
|
789
789
|
|
|
790
790
|
if (this.active_editor && !this.assume_formula) {
|
|
791
791
|
this.active_editor.node.spellcheck = !(this.text_formula);
|
|
792
|
+
|
|
793
|
+
// if not assuming formula, and it's not a formula, then exit.
|
|
794
|
+
|
|
795
|
+
if (!this.text_formula) {
|
|
796
|
+
return;
|
|
797
|
+
}
|
|
798
|
+
|
|
792
799
|
}
|
|
793
800
|
|
|
794
801
|
// this is a short-circuit so we don't format the same text twice.
|
|
@@ -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';
|