@trebco/treb 30.16.0 → 31.1.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.
Files changed (58) hide show
  1. package/api-generator/api-generator.ts +3 -1
  2. package/api-generator/package.json +2 -1
  3. package/dist/languages/treb-i18n-da.mjs +1 -1
  4. package/dist/languages/treb-i18n-de.mjs +1 -1
  5. package/dist/languages/treb-i18n-es.mjs +1 -1
  6. package/dist/languages/treb-i18n-fr.mjs +1 -1
  7. package/dist/languages/treb-i18n-it.mjs +1 -1
  8. package/dist/languages/treb-i18n-nl.mjs +1 -1
  9. package/dist/languages/treb-i18n-no.mjs +1 -1
  10. package/dist/languages/treb-i18n-pl.mjs +1 -1
  11. package/dist/languages/treb-i18n-pt.mjs +1 -1
  12. package/dist/languages/treb-i18n-sv.mjs +1 -1
  13. package/dist/treb-export-worker.mjs +2 -2
  14. package/dist/treb-spreadsheet.mjs +13 -13
  15. package/dist/treb.d.ts +19 -2
  16. package/package.json +8 -7
  17. package/treb-base-types/src/font-stack.ts +144 -0
  18. package/treb-base-types/src/style.ts +121 -11
  19. package/treb-base-types/src/theme.ts +53 -8
  20. package/treb-calculator/src/calculator.ts +13 -13
  21. package/treb-calculator/src/descriptors.ts +12 -4
  22. package/treb-calculator/src/expression-calculator.ts +17 -4
  23. package/treb-calculator/src/functions/base-functions.ts +57 -4
  24. package/treb-calculator/src/functions/statistics-functions.ts +9 -6
  25. package/treb-calculator/tsconfig.json +11 -0
  26. package/treb-charts/style/charts.scss +7 -1
  27. package/treb-data-model/src/annotation.ts +6 -0
  28. package/treb-data-model/src/data_model.ts +14 -3
  29. package/treb-data-model/src/language-model.ts +11 -2
  30. package/treb-data-model/src/sheet.ts +57 -56
  31. package/treb-embed/markup/toolbar.html +15 -1
  32. package/treb-embed/src/custom-element/spreadsheet-constructor.ts +38 -0
  33. package/treb-embed/src/embedded-spreadsheet.ts +139 -32
  34. package/treb-embed/src/options.ts +3 -0
  35. package/treb-embed/src/selection-state.ts +1 -0
  36. package/treb-embed/src/toolbar-message.ts +6 -0
  37. package/treb-embed/src/types.ts +9 -0
  38. package/treb-embed/style/defaults.scss +12 -1
  39. package/treb-embed/style/font-stacks.scss +105 -0
  40. package/treb-embed/style/layout.scss +1 -0
  41. package/treb-embed/style/theme-defaults.scss +12 -2
  42. package/treb-embed/style/toolbar.scss +16 -0
  43. package/treb-grid/src/editors/overlay_editor.ts +36 -3
  44. package/treb-grid/src/layout/base_layout.ts +52 -37
  45. package/treb-grid/src/layout/grid_layout.ts +7 -0
  46. package/treb-grid/src/render/tile_renderer.ts +156 -148
  47. package/treb-grid/src/types/grid.ts +188 -54
  48. package/treb-grid/src/types/grid_events.ts +1 -1
  49. package/treb-grid/src/types/grid_options.ts +3 -0
  50. package/treb-grid/src/util/fontmetrics.ts +134 -0
  51. package/treb-parser/src/parser.ts +12 -3
  52. package/treb-utils/src/measurement.ts +2 -3
  53. package/tsproject.json +1 -1
  54. package/treb-calculator/modern.tsconfig.json +0 -11
  55. package/treb-grid/src/util/fontmetrics2.ts +0 -182
  56. package/treb-parser/src/parser.test.ts +0 -298
  57. /package/treb-embed/{modern.tsconfig.json → tsconfig.json} +0 -0
  58. /package/treb-export/{modern.tsconfig.json → tsconfig.json} +0 -0
package/tsproject.json CHANGED
@@ -18,7 +18,7 @@
18
18
  "noEmitHelpers": true,
19
19
  "verbatimModuleSyntax": true,
20
20
  "lib": [
21
- "dom", "esnext"
21
+ "dom", "esnext", "dom.iterable"
22
22
  ],
23
23
  "allowSyntheticDefaultImports": true,
24
24
  "esModuleInterop": true
@@ -1,11 +0,0 @@
1
- {
2
- "extends": "../tsproject.json",
3
- "references": [
4
- { "path": "../treb-grid/modern.tsconfig.json" },
5
- { "path": "../treb-utils/modern.tsconfig.json" }
6
- ],
7
- "include": [
8
- "../treb-base-types/**/*.ts"
9
- ]
10
- }
11
-
@@ -1,182 +0,0 @@
1
- /*
2
- * This file is part of TREB.
3
- *
4
- * TREB is free software: you can redistribute it and/or modify it under the
5
- * terms of the GNU General Public License as published by the Free Software
6
- * Foundation, either version 3 of the License, or (at your option) any
7
- * later version.
8
- *
9
- * TREB is distributed in the hope that it will be useful, but WITHOUT ANY
10
- * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
11
- * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
12
- * details.
13
- *
14
- * You should have received a copy of the GNU General Public License along
15
- * with TREB. If not, see <https://www.gnu.org/licenses/>.
16
- *
17
- * Copyright 2022-2024 trebco, llc.
18
- * info@treb.app
19
- *
20
- */
21
-
22
- export interface FontMetrics2 {
23
- ascender: number;
24
- descender: number;
25
- block: number;
26
- paren: number;
27
- hash: number;
28
- }
29
-
30
- export class FontMetricsFactory {
31
-
32
- private canvas!: HTMLCanvasElement;
33
-
34
- private cache: Record<string, FontMetrics2> = {};
35
-
36
- // public base_size_px = 10;
37
-
38
- constructor() {
39
-
40
- // you don't have to attach this canvas to the document
41
- // in order to use it to measure. however, if the font size
42
- // is relative it needs to be relative to the canvas size;
43
- // and if the canvas is not attached to the document, size
44
- // doesn't work.
45
-
46
- // FIXME: test with a containing node?
47
- // (NOTE: that doesn't help)
48
-
49
- if (typeof document !== 'undefined') {
50
- this.canvas = document.createElement('canvas');
51
- }
52
-
53
- // FIXME: since this is prelude to drawing, couldn't we use
54
- // our drawing canvas to render? then we don't need to attach
55
- // garbage to the DOM
56
-
57
- // alternatively, we could munge the font description...
58
-
59
- // this.canvas.style.position = 'absolute';
60
- // this.canvas.style.top = '-1000px';
61
- // document.body.appendChild(this.canvas);
62
-
63
- // what we're doing now is calculating -- we get the base size
64
- // from theme and if we see em or % we scale manually.
65
-
66
- // based on the above, we don't need to worry about which
67
- // document we're using. but we probably should just to be consistent.
68
-
69
- }
70
-
71
- /* *
72
- * set base font size. the idea here is to have a base in case font sizes
73
- * are relative (% or em), they need to be relative to something. HOWEVER,
74
- * canvas doesn't inherit --
75
- *
76
- * (moved to base_size_points)
77
- *
78
- * /
79
- public BaseSize(size: string): void {
80
- this.canvas.style.fontSize = size || '';
81
- }
82
- */
83
-
84
- public Flush(): void {
85
- this.cache = {};
86
- }
87
-
88
- public Get(font: string, base = 10): FontMetrics2 {
89
- const key = font + ';' + base;
90
- let metrics = this.cache[key];
91
- if (metrics) {
92
- return metrics;
93
- }
94
- metrics = this.Measure(font, base);
95
- this.cache[key] = metrics;
96
- return metrics;
97
- }
98
-
99
- public Measure(font: string, base: number): FontMetrics2 {
100
-
101
- const match = font.match(/([\d.]+)((?:%|em))/);
102
-
103
- if (match) {
104
- const target = match[1] + match[2];
105
- let value = Number(match[1]) * base;
106
- if (match[2] === '%') { value /= 100; }
107
- font = font.replace(target, value + 'px');
108
- }
109
-
110
- // what's up with the double-access on context?
111
-
112
- let context = this.canvas.getContext('2d', {
113
- willReadFrequently: true,
114
- });
115
-
116
- if (!context) {
117
- throw new Error('invalid context');
118
- }
119
-
120
- context.font = font;
121
- const metrics = context.measureText('MMM');
122
-
123
- const size = Math.ceil(metrics.width);
124
-
125
- this.canvas.setAttribute('width', size.toString());
126
- this.canvas.setAttribute('height', size.toString());
127
-
128
- context = this.canvas.getContext('2d', {
129
- willReadFrequently: true,
130
- });
131
-
132
- if (!context) {
133
- throw new Error('invalid context');
134
- }
135
-
136
- context.font = font;
137
-
138
- context.textAlign = 'center';
139
- context.textBaseline = 'alphabetic';
140
- context.fillStyle = '#000';
141
-
142
- const y = Math.round(size * 2 / 3);
143
- const x = Math.round(size / 2);
144
-
145
- context.clearRect(0, 0, size, size);
146
- for (let i = 0x20; i <= 0x7e; i++) {
147
- const s = String.fromCharCode(i);
148
- context.fillText(s, x, y);
149
- }
150
-
151
- const data = context.getImageData(0, 0, this.canvas.width, this.canvas.height).data;
152
-
153
- const top = Math.floor(this.GetFirstIndex(data)/size);
154
- const bottom = Math.floor(this.GetLastIndex(data)/size);
155
-
156
- return {
157
- ascender: y - top,
158
- descender: bottom - y,
159
- block: bottom - top + 1,
160
- paren: context.measureText('(').width,
161
- hash: context.measureText('##').width - context.measureText('#').width,
162
- };
163
-
164
- }
165
-
166
- private GetFirstIndex(pixels: Uint8ClampedArray) {
167
- for (let i = 3, n = pixels.length; i < n; i += 4) {
168
- if (pixels[i] > 0) return (i - 3) / 4;
169
- }
170
- return pixels.length;
171
- }
172
-
173
- private GetLastIndex(pixels: Uint8ClampedArray) {
174
- for (let i = pixels.length - 1; i >= 3; i -= 4) {
175
- if (pixels[i] > 0) return i / 4;
176
- }
177
- return 0;
178
- }
179
-
180
- }
181
-
182
- export const FontMetricsCache = new FontMetricsFactory();
@@ -1,298 +0,0 @@
1
- /*
2
- * This file is part of TREB.
3
- *
4
- * TREB is free software: you can redistribute it and/or modify it under the
5
- * terms of the GNU General Public License as published by the Free Software
6
- * Foundation, either version 3 of the License, or (at your option) any
7
- * later version.
8
- *
9
- * TREB is distributed in the hope that it will be useful, but WITHOUT ANY
10
- * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
11
- * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
12
- * details.
13
- *
14
- * You should have received a copy of the GNU General Public License along
15
- * with TREB. If not, see <https://www.gnu.org/licenses/>.
16
- *
17
- * Copyright 2022-2024 trebco, llc.
18
- * info@treb.app
19
- *
20
- */
21
-
22
- // import { Parser, UnitLiteral, UnitBinary, UnitUnary, DecimalMarkType, UnitCall, ArgumentSeparatorType } from '../';
23
- import { Parser } from './parser';
24
- import { ArgumentSeparatorType,
25
- UnitLiteral, UnitBinary, UnitUnary, DecimalMarkType, UnitCall } from './parser-types';
26
-
27
- const parser = new Parser();
28
-
29
- test('constructor', () => {
30
- expect(typeof parser).toBe('object');
31
- });
32
-
33
- describe('basic parsing', () => {
34
-
35
- test('3 + 4', () => {
36
- const result = parser.Parse('3 + 4');
37
- expect(result).toBeDefined();
38
- expect(result.valid).toBeTruthy();
39
- });
40
-
41
- test('/ 2', () => {
42
- const result = parser.Parse('/ 2');
43
- expect(result).toBeDefined();
44
- expect(result.valid).toBeFalsy();
45
- });
46
-
47
- });
48
-
49
- describe('parsing/rendering', () => {
50
-
51
- const expression = '2.2 + (3 / foo(bar("1"), 8))';
52
- test(expression, () => {
53
- const result = parser.Parse(expression);
54
- expect(result.valid).toBeTruthy();
55
- expect(result.expression).toBeDefined();
56
- if (result.expression){
57
- const rendered = parser.Render(result.expression);
58
- expect(rendered).toEqual(expression);
59
- }
60
- });
61
-
62
- test('converting separators', () => {
63
- const result = parser.Parse(expression);
64
- expect(result.valid).toBeTruthy();
65
- expect(result.expression).toBeDefined();
66
- if (result.expression){
67
- const rendered = parser.Render(result.expression,
68
- undefined, undefined, DecimalMarkType.Comma, ArgumentSeparatorType.Semicolon);
69
- expect(rendered).toEqual('2,2 + (3 / foo(bar("1"); 8))');
70
- }
71
- });
72
-
73
- });
74
-
75
- describe('number parsing', () => {
76
-
77
- const decimals = [1, 1.11, 2.2343, 123819238, -6, -7.77, -0.00012];
78
-
79
- decimals.forEach((decimal) => {
80
- const as_string = decimal.toString();
81
- test(as_string, () => {
82
- const result = parser.Parse(as_string);
83
- expect(result).toBeDefined();
84
- expect(result.expression).toBeDefined();
85
- if (result.expression) {
86
- expect(result.expression.type).toBe('literal');
87
- expect((result.expression as UnitLiteral).value).toBeCloseTo(decimal);
88
- }
89
- });
90
- });
91
-
92
- test('2.2e-7', () => {
93
- const result = parser.Parse('2.2e-7');
94
- expect(result).toBeDefined();
95
- expect(result.expression).toBeDefined();
96
- if (result.expression) {
97
- expect(result.expression.type).toBe('literal');
98
- expect((result.expression as UnitLiteral).value).toBeCloseTo(2.2e-7);
99
- }
100
- });
101
-
102
- test('-1.123e8', () => {
103
- const result = parser.Parse('-1.123e8');
104
- expect(result).toBeDefined();
105
- expect(result.expression).toBeDefined();
106
- if (result.expression) {
107
- expect(result.expression.type).toBe('literal');
108
- expect((result.expression as UnitLiteral).value).toBeCloseTo(-1.123e8);
109
- }
110
- });
111
-
112
- test('33.33%', () => {
113
- const result = parser.Parse('33.33%');
114
- expect(result).toBeDefined();
115
- expect(result.expression).toBeDefined();
116
- if (result.expression) {
117
- expect(result.expression.type).toBe('literal');
118
- expect((result.expression as UnitLiteral).value).toBeCloseTo(.3333);
119
- }
120
- });
121
-
122
- });
123
-
124
- describe('comma decimal parsing', () => {
125
-
126
- test('1,23', () => {
127
- parser.decimal_mark = DecimalMarkType.Comma;
128
- const result = parser.Parse('1,23');
129
- parser.decimal_mark = DecimalMarkType.Period;
130
- expect(result).toBeDefined();
131
- expect(result.expression).toBeDefined();
132
- if (result.expression) {
133
- expect(result.expression.type).toBe('literal');
134
- expect((result.expression as UnitLiteral).value).toBeCloseTo(1.23);
135
- }
136
- });
137
-
138
- test('-2231,909', () => {
139
- parser.decimal_mark = DecimalMarkType.Comma;
140
- const result = parser.Parse('-2231,909');
141
- parser.decimal_mark = DecimalMarkType.Period;
142
- expect(result).toBeDefined();
143
- expect(result.expression).toBeDefined();
144
- if (result.expression) {
145
- expect(result.expression.type).toBe('literal');
146
- expect((result.expression as UnitLiteral).value).toBeCloseTo(-2231.909);
147
- }
148
- });
149
- });
150
-
151
- describe('unary operators', () => {
152
-
153
- test('2 + -3', () => {
154
- const result = parser.Parse('2 + -3');
155
- expect(result).toBeDefined();
156
- expect(result.expression).toBeDefined();
157
- if (result.expression) {
158
- expect(result.expression.type).toBe('binary');
159
- const right = (result.expression as UnitBinary).right;
160
- expect(right).toBeDefined();
161
- if (right){
162
- expect(right.type).toBe('unary');
163
- const unary = (right as UnitUnary);
164
- expect(unary.operator).toBe('-');
165
- expect(unary.operand.type).toBe('literal');
166
- expect((unary.operand as UnitLiteral).value).toBeCloseTo(3);
167
- }
168
- }
169
- });
170
-
171
- test('op() / +3', () => {
172
- const result = parser.Parse('op() / +3');
173
- expect(result).toBeDefined();
174
- expect(result.expression).toBeDefined();
175
- if (result.expression) {
176
- expect(result.expression.type).toBe('binary');
177
- const right = (result.expression as UnitBinary).right;
178
- expect(right).toBeDefined();
179
- if (right){
180
- expect(right.type).toBe('unary');
181
- const unary = (right as UnitUnary);
182
- expect(unary.operator).toBe('+');
183
- expect(unary.operand.type).toBe('literal');
184
- expect((unary.operand as UnitLiteral).value).toBeCloseTo(3);
185
- }
186
- }
187
- });
188
- });
189
-
190
- describe('binary operators', () => {
191
-
192
- test('10 * 8', () => {
193
- const result = parser.Parse('10 * 8');
194
- expect(result).toBeDefined();
195
- expect(result.expression).toBeDefined();
196
- if (result.expression) {
197
- expect(result.expression.type).toBe('binary');
198
- const binary = result.expression as UnitBinary;
199
- expect(binary.operator).toBe('*');
200
- expect((binary.left as UnitLiteral).value).toBe(10);
201
- expect((binary.right as UnitLiteral).value).toBe(8);
202
- }
203
- });
204
-
205
- });
206
-
207
- describe('grouping/ordering', () => {
208
- test('(2 / (1 + (2 * 3))) * 4', () => {
209
- const result = parser.Parse('(2 / (1 + (2 * 3))) * 4');
210
- expect(result).toBeDefined();
211
- expect(result.expression).toBeDefined();
212
- if (result.expression){
213
- expect(result.expression.type).toBe('binary');
214
- const binary = result.expression as UnitBinary;
215
- expect(binary.right.type).toBe('literal');
216
- expect((binary.right as UnitLiteral).value).toBe(4);
217
- expect(binary.left.type).toBe('group');
218
- }
219
- });
220
- });
221
-
222
- describe('function calls', () => {
223
-
224
- test('foo()', () => {
225
- const result = parser.Parse('foo()');
226
- expect(result).toBeDefined();
227
- expect(result.expression).toBeDefined();
228
- if (result.expression) {
229
- expect(result.expression.type).toBe('call');
230
- const call = result.expression as UnitCall;
231
- expect(call.name).toBe('foo');
232
- expect(call.args.length).toBe(0);
233
- }
234
- });
235
-
236
- test('oof(1, "bar", 3.3)', () => {
237
- const result = parser.Parse('oof(1, "bar", 3.3)');
238
- expect(result).toBeDefined();
239
- expect(result.expression).toBeDefined();
240
- if (result.expression) {
241
- expect(result.expression.type).toBe('call');
242
- const call = result.expression as UnitCall;
243
- expect(call.name).toBe('oof');
244
- expect(call.args.length).toBe(3);
245
- if (call.args.length === 3){
246
- expect(call.args[0].type).toBe('literal');
247
- expect((call.args[0] as UnitLiteral).value).toBe(1);
248
- expect(call.args[1].type).toBe('literal');
249
- expect((call.args[1] as UnitLiteral).value).toBe('bar');
250
- expect(call.args[2].type).toBe('literal');
251
- expect((call.args[2] as UnitLiteral).value).toBe(3.3);
252
- }
253
- }
254
- });
255
-
256
- });
257
-
258
- describe('addresses', () => {
259
- test('=A1 + $B2 - C$3 - $ZZ$40', () => {
260
- const result = parser.Parse('=A1 + $B2 - C$3 - $ZZ$40');
261
- expect(result).toBeDefined();
262
- expect(result.expression).toBeDefined();
263
- if (result.expression) {
264
- expect(parser.Render(result.expression)).toBe('A1 + $B2 - C$3 - $ZZ$40');
265
- }
266
- });
267
- });
268
-
269
- describe('semicolon-separated arguments', () => {
270
-
271
- test('xfoo(1; "xbar"; 3,33)', () => {
272
- parser.decimal_mark = DecimalMarkType.Comma;
273
- parser.argument_separator = ArgumentSeparatorType.Semicolon;
274
- const result = parser.Parse('xfoo(1; "xbar"; 3,33)');
275
- parser.decimal_mark = DecimalMarkType.Period;
276
- parser.argument_separator = ArgumentSeparatorType.Comma;
277
-
278
- expect(result).toBeDefined();
279
- expect(result.expression).toBeDefined();
280
- if (result.expression) {
281
- expect(result.expression.type).toBe('call');
282
- const call = result.expression as UnitCall;
283
- expect(call.name).toBe('xfoo');
284
- expect(call.args.length).toBe(3);
285
- if (call.args.length === 3){
286
- expect(call.args[0].type).toBe('literal');
287
- expect((call.args[0] as UnitLiteral).value).toBe(1);
288
- expect(call.args[1].type).toBe('literal');
289
- expect((call.args[1] as UnitLiteral).value).toBe('xbar');
290
- expect(call.args[2].type).toBe('literal');
291
- expect((call.args[2] as UnitLiteral).value).toBeCloseTo(3.33);
292
- }
293
- }
294
- });
295
-
296
- });
297
-
298
-