@trebco/treb 30.15.0 → 31.0.2
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/api-generator/api-generator.ts +3 -1
- package/api-generator/package.json +2 -1
- package/dist/treb-export-worker.mjs +2 -2
- package/dist/treb-spreadsheet.mjs +13 -13
- package/dist/treb.d.ts +19 -2
- package/package.json +8 -7
- package/treb-base-types/src/font-stack.ts +144 -0
- package/treb-base-types/src/style.ts +121 -11
- package/treb-base-types/src/theme.ts +53 -8
- package/treb-calculator/src/calculator.ts +13 -13
- package/treb-calculator/src/descriptors.ts +12 -4
- package/treb-calculator/src/expression-calculator.ts +17 -4
- package/treb-calculator/src/functions/base-functions.ts +57 -4
- package/treb-calculator/src/functions/statistics-functions.ts +9 -6
- package/treb-calculator/tsconfig.json +11 -0
- package/treb-charts/src/chart-functions.ts +41 -4
- package/treb-charts/src/chart-types.ts +20 -1
- package/treb-charts/src/chart-utils.ts +86 -9
- package/treb-charts/src/default-chart-renderer.ts +40 -1
- package/treb-charts/src/renderer.ts +3 -3
- package/treb-charts/style/charts.scss +7 -1
- package/treb-data-model/src/annotation.ts +6 -0
- package/treb-data-model/src/data_model.ts +14 -3
- package/treb-data-model/src/sheet.ts +57 -56
- package/treb-embed/markup/toolbar.html +15 -1
- package/treb-embed/src/custom-element/spreadsheet-constructor.ts +38 -0
- package/treb-embed/src/embedded-spreadsheet.ts +119 -29
- package/treb-embed/src/options.ts +3 -0
- package/treb-embed/src/selection-state.ts +1 -0
- package/treb-embed/src/toolbar-message.ts +6 -0
- package/treb-embed/src/types.ts +9 -0
- package/treb-embed/style/defaults.scss +12 -1
- package/treb-embed/style/font-stacks.scss +105 -0
- package/treb-embed/style/layout.scss +1 -0
- package/treb-embed/style/theme-defaults.scss +12 -2
- package/treb-embed/style/toolbar.scss +16 -0
- package/treb-grid/src/editors/overlay_editor.ts +36 -3
- package/treb-grid/src/layout/base_layout.ts +52 -37
- package/treb-grid/src/layout/grid_layout.ts +7 -0
- package/treb-grid/src/render/tile_renderer.ts +154 -148
- package/treb-grid/src/types/grid.ts +188 -54
- package/treb-grid/src/types/grid_events.ts +1 -1
- package/treb-grid/src/types/grid_options.ts +3 -0
- package/treb-grid/src/util/fontmetrics.ts +134 -0
- package/treb-parser/src/parser.ts +12 -3
- package/treb-utils/src/measurement.ts +2 -3
- package/tsproject.json +1 -1
- package/treb-calculator/modern.tsconfig.json +0 -11
- package/treb-grid/src/util/fontmetrics2.ts +0 -182
- package/treb-parser/src/parser.test.ts +0 -298
- /package/treb-embed/{modern.tsconfig.json → tsconfig.json} +0 -0
- /package/treb-export/{modern.tsconfig.json → tsconfig.json} +0 -0
package/dist/treb.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/*! API
|
|
1
|
+
/*! API v31.0. Copyright 2018-2024 trebco, llc. All rights reserved. LGPL: https://treb.app/license */
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* add our tag to the map
|
|
@@ -170,6 +170,9 @@ export interface EmbeddedSpreadsheetOptions {
|
|
|
170
170
|
/** include the font scale control in the toolbar */
|
|
171
171
|
font_scale?: boolean;
|
|
172
172
|
|
|
173
|
+
/** include the font stack control in the toolbar */
|
|
174
|
+
font_stack?: boolean;
|
|
175
|
+
|
|
173
176
|
/** include the insert/remove table button in the toolbar */
|
|
174
177
|
table_button?: boolean;
|
|
175
178
|
|
|
@@ -1601,7 +1604,7 @@ export declare type LoadSource = "drag-and-drop" | "local-file" | "network-file"
|
|
|
1601
1604
|
* EmbeddedSheetEvent is a discriminated union. Switch on the `type` field
|
|
1602
1605
|
* of the event.
|
|
1603
1606
|
*/
|
|
1604
|
-
export type EmbeddedSheetEvent = DocumentChangeEvent | DocumentResetEvent | DocumentLoadEvent | ThemeChangeEvent | ViewChangeEvent | DataChangeEvent | FocusViewEvent | SelectionEvent | ResizeEvent;
|
|
1607
|
+
export type EmbeddedSheetEvent = DocumentChangeEvent | DocumentResetEvent | DocumentLoadEvent | ThemeChangeEvent | ViewChangeEvent | DataChangeEvent | FocusViewEvent | SelectionEvent | ResizeEvent | AnnotationSelectionEvent;
|
|
1605
1608
|
|
|
1606
1609
|
/**
|
|
1607
1610
|
* options when inserting a table into a sheet
|
|
@@ -1691,6 +1694,14 @@ export interface SelectionEvent {
|
|
|
1691
1694
|
type: 'selection';
|
|
1692
1695
|
}
|
|
1693
1696
|
|
|
1697
|
+
/**
|
|
1698
|
+
* this event is used when an annotation is selected. we're not changing
|
|
1699
|
+
* the original selection event, because I don't want to break anything.
|
|
1700
|
+
*/
|
|
1701
|
+
export interface AnnotationSelectionEvent {
|
|
1702
|
+
type: 'annotation-selection';
|
|
1703
|
+
}
|
|
1704
|
+
|
|
1694
1705
|
/**
|
|
1695
1706
|
* This event is sent when the focused view changes, if you have more
|
|
1696
1707
|
* than one view.
|
|
@@ -1973,6 +1984,12 @@ export interface AnnotationDataBase {
|
|
|
1973
1984
|
/** the new layout, persisted and takes preference over the old one */
|
|
1974
1985
|
layout?: AnnotationLayout;
|
|
1975
1986
|
|
|
1987
|
+
/**
|
|
1988
|
+
* adding cell style as a convenient store for font stack; atm we are
|
|
1989
|
+
* ignoring everything but the font_face attribute
|
|
1990
|
+
*/
|
|
1991
|
+
style?: CellStyle;
|
|
1992
|
+
|
|
1976
1993
|
/**
|
|
1977
1994
|
* the old layout used rectangles, and we need to keep support for
|
|
1978
1995
|
* that. this is not the layout rectangle. this rectangle is just
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@trebco/treb",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "31.0.2",
|
|
4
4
|
"license": "LGPL-3.0-or-later",
|
|
5
5
|
"homepage": "https://treb.app",
|
|
6
6
|
"repository": {
|
|
@@ -30,8 +30,9 @@
|
|
|
30
30
|
"treb-grid": "file:treb-grid",
|
|
31
31
|
"treb-parser": "file:treb-parser",
|
|
32
32
|
"treb-utils": "file:treb-utils",
|
|
33
|
-
"ts-node
|
|
33
|
+
"ts-node": "^10.9.2",
|
|
34
34
|
"tslib": "^2.7.0",
|
|
35
|
+
"tsx": "^4.19.1",
|
|
35
36
|
"typescript": "^5.3.3",
|
|
36
37
|
"typescript-eslint": "^8.3.0",
|
|
37
38
|
"uzip": "^0.20201231.0"
|
|
@@ -42,11 +43,11 @@
|
|
|
42
43
|
"clean": "rm -fr build dist declaration",
|
|
43
44
|
"watch": "node --watch esbuild-composite.mjs --watch --dev",
|
|
44
45
|
"watch-production": "node --watch esbuild-composite.mjs --watch",
|
|
45
|
-
"tsc": "tsc -b treb-embed/
|
|
46
|
-
"rebuild-tsc": "tsc -b --force treb-embed/
|
|
47
|
-
"watch-tsc": "tsc -b treb-embed/
|
|
48
|
-
"watch-api": "
|
|
49
|
-
"generate-api": "
|
|
46
|
+
"tsc": "tsc -b treb-embed/tsconfig.json",
|
|
47
|
+
"rebuild-tsc": "tsc -b --force treb-embed/tsconfig.json",
|
|
48
|
+
"watch-tsc": "tsc -b treb-embed/tsconfig.json -w",
|
|
49
|
+
"watch-api": "tsx --watch api-generator/api-generator.ts --config api-config.json",
|
|
50
|
+
"generate-api": "tsx api-generator/api-generator.ts --config api-config.json",
|
|
50
51
|
"release": "npm run rebuild-tsc && npm run build && npm run generate-api"
|
|
51
52
|
}
|
|
52
53
|
}
|
|
@@ -0,0 +1,144 @@
|
|
|
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 { UA } from 'treb-grid';
|
|
23
|
+
import { Measurement } from 'treb-utils';
|
|
24
|
+
import { type FontSize, Style } from './style';
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* these are the stacks we're currently supporting.
|
|
28
|
+
*/
|
|
29
|
+
export const font_stack_names = [
|
|
30
|
+
'default',
|
|
31
|
+
'old-style',
|
|
32
|
+
'transitional',
|
|
33
|
+
'handwritten',
|
|
34
|
+
'monospace',
|
|
35
|
+
'industrial',
|
|
36
|
+
'ui',
|
|
37
|
+
] as const;
|
|
38
|
+
|
|
39
|
+
export type FontStackType = typeof font_stack_names[number];
|
|
40
|
+
|
|
41
|
+
export const font_stack_labels: Record<FontStackType, string> = {
|
|
42
|
+
'default': 'Sans-serif',
|
|
43
|
+
'old-style': 'Old style',
|
|
44
|
+
'transitional': 'Serif',
|
|
45
|
+
'handwritten': 'Handwritten',
|
|
46
|
+
'monospace': 'Monospace',
|
|
47
|
+
'industrial': 'Industrial sans',
|
|
48
|
+
'ui': 'System UI',
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* representation of a font stack as an object we can manage in themes.
|
|
53
|
+
* the impetus for this is having some font "sets" we can use in a sheet
|
|
54
|
+
* that we know will render in a reasonable cross-platform way.
|
|
55
|
+
*
|
|
56
|
+
* we're also taking advantage of chrome supporting font features in
|
|
57
|
+
* canvas. we have to do a little bit of carve-out for ffx browsers on
|
|
58
|
+
* windows but that should be it, and the fallback is OK.
|
|
59
|
+
*/
|
|
60
|
+
|
|
61
|
+
export interface FontStack {
|
|
62
|
+
|
|
63
|
+
/** the font family from css. this will usually be a list. */
|
|
64
|
+
family: string;
|
|
65
|
+
|
|
66
|
+
/** the actual font used */
|
|
67
|
+
font?: string;
|
|
68
|
+
|
|
69
|
+
/** default size for grid cells. may be different for different fonts. */
|
|
70
|
+
size: FontSize;
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* font variants. we used this for sitka text to apply lining-nums and
|
|
74
|
+
* tabular-nums, although we're not using sitka anymore. atm only chrome
|
|
75
|
+
* supports font variants in canvas (boo).
|
|
76
|
+
*/
|
|
77
|
+
variants?: string;
|
|
78
|
+
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
const font_cache: Map<string, boolean> = new Map();
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
*
|
|
85
|
+
* @param name - for reporting purposes only
|
|
86
|
+
* @param computed - computed css to resolve variables
|
|
87
|
+
* @returns
|
|
88
|
+
*/
|
|
89
|
+
export const GenerateFontStack = (name: string, computed: CSSStyleDeclaration): FontStack => {
|
|
90
|
+
|
|
91
|
+
const family = computed.fontFamily;
|
|
92
|
+
let font = '';
|
|
93
|
+
|
|
94
|
+
const elements = family.split(/,/);
|
|
95
|
+
|
|
96
|
+
for (let element of elements) {
|
|
97
|
+
element = element.replace(/'"/g, '').trim();
|
|
98
|
+
const lc = element.toLowerCase();
|
|
99
|
+
|
|
100
|
+
//
|
|
101
|
+
// platform-specific hacks. this is kind of unfortunate.
|
|
102
|
+
//
|
|
103
|
+
|
|
104
|
+
if (UA.is_firefox && /sitka text/i.test(lc)) {
|
|
105
|
+
continue;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
let check = font_cache.get(lc);
|
|
109
|
+
if (typeof check === 'undefined') {
|
|
110
|
+
check = Measurement.FontLoaded(element);
|
|
111
|
+
font_cache.set(lc, check);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
if (check) {
|
|
115
|
+
font = element;
|
|
116
|
+
break;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
if (!font) {
|
|
121
|
+
console.warn('no font found for font stack', name);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// check the base size, and any adjustments for this font
|
|
125
|
+
|
|
126
|
+
const variable_name = font.toLowerCase().replace(/['"]/g, '').replace(/\W+/g, '-');
|
|
127
|
+
|
|
128
|
+
const base = computed.getPropertyValue('--treb-font-stack-default-size');
|
|
129
|
+
const adjusted = computed.getPropertyValue(`--treb-font-stack-${variable_name}-size`);
|
|
130
|
+
const variants = computed.getPropertyValue(`--treb-font-stack-${variable_name}-variant`);
|
|
131
|
+
const size = Style.ParseFontSize(adjusted || base || '10pt').font_size || { unit: 'pt', value: 10 };
|
|
132
|
+
|
|
133
|
+
// console.info({stack: name, family, font, base, adjusted, size, variants});
|
|
134
|
+
|
|
135
|
+
const stack = {
|
|
136
|
+
family,
|
|
137
|
+
font,
|
|
138
|
+
variants,
|
|
139
|
+
size,
|
|
140
|
+
};
|
|
141
|
+
|
|
142
|
+
return stack;
|
|
143
|
+
|
|
144
|
+
};
|
|
@@ -21,6 +21,8 @@
|
|
|
21
21
|
|
|
22
22
|
const empty_json = JSON.stringify({}); // we could probably hard-code this
|
|
23
23
|
|
|
24
|
+
import type { Theme } from './theme';
|
|
25
|
+
|
|
24
26
|
/** horizontal align constants for cell style */
|
|
25
27
|
export type HorizontalAlign = '' | 'left' | 'center' | 'right';
|
|
26
28
|
|
|
@@ -271,8 +273,9 @@ export const Style = {
|
|
|
271
273
|
number_format: 'General', // '0.######', // use symbolic, e.g. "general"
|
|
272
274
|
nan: 'NaN',
|
|
273
275
|
|
|
274
|
-
font_size: { unit: '
|
|
275
|
-
|
|
276
|
+
font_size: { unit: 'em', value: 1 },
|
|
277
|
+
// font_size: { unit: 'pt', value: 10.5 },
|
|
278
|
+
// font_face: 'sans-serif',
|
|
276
279
|
|
|
277
280
|
bold: false, // drop "font_"
|
|
278
281
|
italic: false, // ...
|
|
@@ -439,7 +442,9 @@ export const Style = {
|
|
|
439
442
|
|
|
440
443
|
},
|
|
441
444
|
|
|
442
|
-
/**
|
|
445
|
+
/**
|
|
446
|
+
* @internal
|
|
447
|
+
*/
|
|
443
448
|
FontSize: (properties: CellStyle, prefer_points = true): string => {
|
|
444
449
|
|
|
445
450
|
const value = properties.font_size?.value;
|
|
@@ -466,10 +471,49 @@ export const Style = {
|
|
|
466
471
|
return '';
|
|
467
472
|
},
|
|
468
473
|
|
|
469
|
-
/**
|
|
470
|
-
*
|
|
474
|
+
/**
|
|
475
|
+
* @internal
|
|
476
|
+
*
|
|
477
|
+
* generate a font size based on a base size (hopefully in actual units)
|
|
478
|
+
* and a relative size (em, %, or possibly a static unit). also optionally
|
|
479
|
+
* apply a scale.
|
|
480
|
+
*
|
|
471
481
|
*/
|
|
472
|
-
|
|
482
|
+
CompositeFontSize: (base: FontSize, relative: FontSize, scale = 1, prefer_points = false) => {
|
|
483
|
+
|
|
484
|
+
let composite: FontSize = { ...base };
|
|
485
|
+
|
|
486
|
+
// maybe it's actually not relative
|
|
487
|
+
|
|
488
|
+
if (relative.unit === 'pt' || relative.unit === 'px') {
|
|
489
|
+
composite = { ...relative };
|
|
490
|
+
}
|
|
491
|
+
else {
|
|
492
|
+
composite.value = relative.value * base.value;
|
|
493
|
+
if (relative.unit === '%') {
|
|
494
|
+
composite.value /= 100;
|
|
495
|
+
}
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
if (composite.unit === 'px' && prefer_points) {
|
|
499
|
+
composite.value = Math.round((composite.value||16) * 300 / 4) / 100;
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
composite.value *= scale;
|
|
503
|
+
|
|
504
|
+
return composite;
|
|
505
|
+
|
|
506
|
+
},
|
|
507
|
+
|
|
508
|
+
/**
|
|
509
|
+
* return a font string suitable for canvas. because our font sizes are
|
|
510
|
+
* (probably) in ems, we need a base size to bounce off.
|
|
511
|
+
*/
|
|
512
|
+
CompositeFont: (base: FontSize, properties: CellStyle, scale: number, theme: Theme) => {
|
|
513
|
+
|
|
514
|
+
let variants: string|undefined;
|
|
515
|
+
let stack_size: FontSize|undefined; // for reporting only
|
|
516
|
+
let font_size: FontSize|undefined;
|
|
473
517
|
|
|
474
518
|
const parts: string[] = [];
|
|
475
519
|
|
|
@@ -481,13 +525,79 @@ export const Style = {
|
|
|
481
525
|
parts.push('italic');
|
|
482
526
|
}
|
|
483
527
|
|
|
484
|
-
parts.push(((properties.font_size?.value || 0) * scale).toFixed(2) +
|
|
485
|
-
(properties.font_size?.unit || 'pt'));
|
|
486
528
|
|
|
487
|
-
|
|
529
|
+
const font_face = properties.font_face || 'stack:default';
|
|
530
|
+
// let stack_scale = 1;
|
|
531
|
+
|
|
532
|
+
// check if this is a stack
|
|
533
|
+
if (font_face.startsWith('stack:')) {
|
|
534
|
+
let stack = theme.font_stacks[font_face.substring(6) || 'default'];
|
|
535
|
+
|
|
536
|
+
// default to default (not just a clever name). the rationale is we
|
|
537
|
+
// want to support environments that don't have fonts turned on. in
|
|
538
|
+
// that case, we just don't create the mappings, so everything shows
|
|
539
|
+
// as the default font.
|
|
540
|
+
|
|
541
|
+
if (!stack) {
|
|
542
|
+
stack = theme.font_stacks.default;
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
if (stack) {
|
|
546
|
+
stack_size = properties.font_size;
|
|
547
|
+
font_size = Style.CompositeFontSize(stack.size, properties.font_size || { unit: 'pt', value: 10 }, scale);
|
|
548
|
+
parts.push(font_size.value.toFixed(2) + font_size.unit);
|
|
549
|
+
parts.push(stack.font || '');
|
|
550
|
+
variants = stack.variants;
|
|
551
|
+
}
|
|
552
|
+
}
|
|
553
|
+
else {
|
|
554
|
+
font_size = Style.CompositeFontSize(base, properties.font_size || { unit: 'pt', value: 10 }, scale);
|
|
555
|
+
parts.push(font_size.value.toFixed(2) + font_size.unit);
|
|
556
|
+
parts.push(font_face || '');
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
return { font: parts.join(' '), variants, base, size: properties.font_size, scale, stack_size, font_size };
|
|
560
|
+
|
|
561
|
+
},
|
|
562
|
+
|
|
563
|
+
/*
|
|
564
|
+
Font2: (properties: CellStyle, scale: number, theme: Theme) => {
|
|
565
|
+
|
|
566
|
+
let features = false;
|
|
488
567
|
|
|
489
|
-
|
|
490
|
-
|
|
568
|
+
const parts: string[] = [];
|
|
569
|
+
|
|
570
|
+
if (properties.bold) {
|
|
571
|
+
parts.push('bold');
|
|
572
|
+
}
|
|
573
|
+
|
|
574
|
+
if (properties.italic) {
|
|
575
|
+
parts.push('italic');
|
|
576
|
+
}
|
|
577
|
+
|
|
578
|
+
const font_face = properties.font_face || 'stack:default';
|
|
579
|
+
let stack_scale = 1;
|
|
580
|
+
|
|
581
|
+
// check if this is a stack
|
|
582
|
+
if (font_face.startsWith('stack:')) {
|
|
583
|
+
const stack = theme.font_stacks[font_face.substring(6) || 'default'];
|
|
584
|
+
if (stack) {
|
|
585
|
+
stack_scale = stack.scale;
|
|
586
|
+
parts.push(((properties.font_size?.value || 0) * (scale || 1) * (stack.scale || 1)).toFixed(2) +
|
|
587
|
+
(properties.font_size?.unit || 'pt'));
|
|
588
|
+
parts.push(stack.font || '');
|
|
589
|
+
features = !!stack.apply_num_features;
|
|
590
|
+
}
|
|
591
|
+
}
|
|
592
|
+
else {
|
|
593
|
+
parts.push(((properties.font_size?.value || 0) * (scale || 1)).toFixed(2) +
|
|
594
|
+
(properties.font_size?.unit || 'pt'));
|
|
595
|
+
parts.push(font_face || '');
|
|
596
|
+
}
|
|
597
|
+
|
|
598
|
+
return { font: parts.join(' '), features, base_size: properties.font_size, scale, stack_scale };
|
|
599
|
+
|
|
491
600
|
},
|
|
601
|
+
*/
|
|
492
602
|
|
|
493
603
|
};
|
|
@@ -19,12 +19,13 @@
|
|
|
19
19
|
*
|
|
20
20
|
*/
|
|
21
21
|
|
|
22
|
-
import { type Color, type CellStyle, IsHTMLColor, IsThemeColor, ThemeColorIndex, type ThemeColor } from './style';
|
|
22
|
+
import { type Color, type CellStyle, IsHTMLColor, IsThemeColor, ThemeColorIndex, type ThemeColor, type FontSize } from './style';
|
|
23
23
|
import { ColorFunctions } from './color';
|
|
24
24
|
// import * as LCHColorFunctions from './color2';
|
|
25
25
|
|
|
26
26
|
import { DOMContext } from './dom-utilities';
|
|
27
27
|
import { Measurement } from 'treb-utils';
|
|
28
|
+
import { font_stack_names, type FontStack, GenerateFontStack } from './font-stack';
|
|
28
29
|
|
|
29
30
|
/*
|
|
30
31
|
* so this is a little strange. we use CSS to populate a theme object,
|
|
@@ -90,6 +91,12 @@ export interface Theme {
|
|
|
90
91
|
/** grid cell defaults (composite: size, font face, color, background) */
|
|
91
92
|
grid_cell?: CellStyle;
|
|
92
93
|
|
|
94
|
+
/**
|
|
95
|
+
* base font size for grid cell. we try to specify things in ems, but
|
|
96
|
+
* we do need to know this in order to measure
|
|
97
|
+
*/
|
|
98
|
+
grid_cell_font_size: FontSize;
|
|
99
|
+
|
|
93
100
|
/** gridlines color */
|
|
94
101
|
grid_color: string;
|
|
95
102
|
|
|
@@ -150,6 +157,9 @@ export interface Theme {
|
|
|
150
157
|
/** dark color for offset (against light background) */
|
|
151
158
|
offset_dark: string;
|
|
152
159
|
|
|
160
|
+
/** precalculated font stacks */
|
|
161
|
+
font_stacks: Record<string, FontStack>;
|
|
162
|
+
|
|
153
163
|
}
|
|
154
164
|
|
|
155
165
|
/**
|
|
@@ -162,6 +172,8 @@ export const DefaultTheme: Theme = {
|
|
|
162
172
|
offset_cache: {},
|
|
163
173
|
offset_light: '#fff',
|
|
164
174
|
offset_dark: '#000',
|
|
175
|
+
font_stacks: {},
|
|
176
|
+
grid_cell_font_size: { value: 10, unit: 'pt' },
|
|
165
177
|
};
|
|
166
178
|
|
|
167
179
|
/* *
|
|
@@ -428,19 +440,32 @@ const TableStyleFromCSS = (base: CSSStyleDeclaration, style: CSSStyleDeclaration
|
|
|
428
440
|
*/
|
|
429
441
|
|
|
430
442
|
// testing
|
|
431
|
-
const StyleFromCSS = (css: CSSStyleDeclaration): CellStyle => {
|
|
443
|
+
const StyleFromCSS = (css: CSSStyleDeclaration, include_font_face = false): CellStyle => {
|
|
432
444
|
|
|
433
|
-
const { value, unit } = ParseFontSize(css.fontSize||'');
|
|
445
|
+
// const { value, unit } = ParseFontSize(css.fontSize||'');
|
|
434
446
|
|
|
435
447
|
const style: CellStyle = {
|
|
436
448
|
fill: { text: css.backgroundColor }, // || 'none',
|
|
437
449
|
text: { text: css.color },
|
|
450
|
+
|
|
451
|
+
/*
|
|
438
452
|
font_size: {
|
|
439
453
|
unit, value,
|
|
440
454
|
},
|
|
441
|
-
|
|
455
|
+
*/
|
|
456
|
+
|
|
457
|
+
// use container size unless we scale. the reason we do this is
|
|
458
|
+
// because if we set scale, we always wind up with em units.
|
|
459
|
+
|
|
460
|
+
font_size: { unit: 'em', value: 1 },
|
|
461
|
+
|
|
462
|
+
// font_face: css.fontFamily,
|
|
442
463
|
};
|
|
443
464
|
|
|
465
|
+
if (include_font_face) {
|
|
466
|
+
style.font_face = css.fontFamily;
|
|
467
|
+
}
|
|
468
|
+
|
|
444
469
|
// the default border comes from the "theme colors", not from
|
|
445
470
|
// the CSS property (it used to come from the CSS property, which
|
|
446
471
|
// is why we have the CSS property set).
|
|
@@ -544,7 +569,7 @@ export const ThemeColorTable = (theme_color: number, tint = .7): TableTheme => {
|
|
|
544
569
|
*
|
|
545
570
|
* @internal
|
|
546
571
|
*/
|
|
547
|
-
export const LoadThemeProperties = (container: HTMLElement): Theme => {
|
|
572
|
+
export const LoadThemeProperties = (container: HTMLElement, use_font_stacks = false): Theme => {
|
|
548
573
|
|
|
549
574
|
const theme: Theme = JSON.parse(JSON.stringify(DefaultTheme));
|
|
550
575
|
const DOM = DOMContext.GetInstance(container.ownerDocument);
|
|
@@ -558,14 +583,34 @@ export const LoadThemeProperties = (container: HTMLElement): Theme => {
|
|
|
558
583
|
}
|
|
559
584
|
|
|
560
585
|
const node = Append(container, '');
|
|
561
|
-
const CSS = ElementCSS.bind(0, node);
|
|
586
|
+
const CSS: (classes: string) => CSSStyleDeclaration = ElementCSS.bind(0, node);
|
|
562
587
|
|
|
563
588
|
let css = CSS('grid-cells');
|
|
564
|
-
theme.grid_cell = StyleFromCSS(css);
|
|
589
|
+
theme.grid_cell = StyleFromCSS(css, false);
|
|
565
590
|
theme.grid_color = css.stroke || '';
|
|
591
|
+
theme.grid_cell_font_size = ParseFontSize(css.fontSize||'');
|
|
592
|
+
|
|
593
|
+
// console.info({theme});
|
|
594
|
+
|
|
595
|
+
if (use_font_stacks) {
|
|
596
|
+
for (const key of font_stack_names) {
|
|
597
|
+
css = CSS(`treb-font-stack-${key}`);
|
|
598
|
+
theme.font_stacks[key] = GenerateFontStack(key, css);
|
|
599
|
+
}
|
|
600
|
+
}
|
|
601
|
+
else {
|
|
566
602
|
|
|
603
|
+
// default only
|
|
604
|
+
|
|
605
|
+
css = CSS(`treb-font-stack-default`);
|
|
606
|
+
theme.font_stacks.default = GenerateFontStack('default', css);
|
|
607
|
+
|
|
608
|
+
}
|
|
609
|
+
// console.info(theme.font_stacks);
|
|
610
|
+
|
|
611
|
+
|
|
567
612
|
css = CSS('grid-headers');
|
|
568
|
-
theme.headers = StyleFromCSS(css);
|
|
613
|
+
theme.headers = StyleFromCSS(css, true);
|
|
569
614
|
theme.headers_grid_color = css.stroke;
|
|
570
615
|
if (!theme.headers_grid_color || theme.headers_grid_color === 'none') {
|
|
571
616
|
theme.headers_grid_color = theme.grid_color;
|
|
@@ -1569,7 +1569,7 @@ export class Calculator extends Graph {
|
|
|
1569
1569
|
};
|
|
1570
1570
|
}
|
|
1571
1571
|
|
|
1572
|
-
return this.expression_calculator.Calculate(vertex.expression, vertex.address); // <- this one
|
|
1572
|
+
return this.expression_calculator.Calculate(vertex.expression, vertex.address, vertex.reference?.area); // <- this one
|
|
1573
1573
|
}
|
|
1574
1574
|
|
|
1575
1575
|
|
|
@@ -1837,6 +1837,11 @@ export class Calculator extends Graph {
|
|
|
1837
1837
|
for (const name of Object.keys(map)) {
|
|
1838
1838
|
|
|
1839
1839
|
const descriptor = map[name];
|
|
1840
|
+
|
|
1841
|
+
// the way we call this now, this is unecessary
|
|
1842
|
+
// @see `CallExpression` in `expression-calculator.ts`.
|
|
1843
|
+
|
|
1844
|
+
/*
|
|
1840
1845
|
const original_function = descriptor.fn;
|
|
1841
1846
|
|
|
1842
1847
|
// we don't bind to the actual context because that would allow
|
|
@@ -1844,28 +1849,23 @@ export class Calculator extends Graph {
|
|
|
1844
1849
|
// that rely on it. which is a pretty far-fetched scenario, but we might
|
|
1845
1850
|
// as well protect against it.
|
|
1846
1851
|
|
|
1852
|
+
console.info('wrapping...');
|
|
1853
|
+
|
|
1847
1854
|
descriptor.fn = (...args: unknown[]) => {
|
|
1855
|
+
|
|
1856
|
+
console.info("wrapped?");
|
|
1857
|
+
|
|
1848
1858
|
return original_function.apply({
|
|
1849
1859
|
address: { ...this.expression_calculator.context.address},
|
|
1850
1860
|
}, args);
|
|
1851
1861
|
};
|
|
1862
|
+
*/
|
|
1852
1863
|
|
|
1853
1864
|
this.library.Register({[name]: descriptor});
|
|
1854
1865
|
}
|
|
1855
1866
|
|
|
1856
1867
|
}
|
|
1857
1868
|
|
|
1858
|
-
/* *
|
|
1859
|
-
* wrap the attachdata function so we can update the expression calculator
|
|
1860
|
-
* at the same time (we should unwind this a little bit, it's an artifact
|
|
1861
|
-
* of graph being a separate class)
|
|
1862
|
-
* /
|
|
1863
|
-
public AttachModel(): void {
|
|
1864
|
-
// this.RebuildMap();
|
|
1865
|
-
// this.expression_calculator.SetModel(this.model);
|
|
1866
|
-
}
|
|
1867
|
-
*/
|
|
1868
|
-
|
|
1869
1869
|
/**
|
|
1870
1870
|
* wrapper method for calculation
|
|
1871
1871
|
*/
|
|
@@ -2076,7 +2076,7 @@ export class Calculator extends Graph {
|
|
|
2076
2076
|
address: ICellAddress = {row: -1, column: -1},
|
|
2077
2077
|
preserve_flags = false): UnionValue {
|
|
2078
2078
|
|
|
2079
|
-
return this.expression_calculator.Calculate(expression, address, preserve_flags).value; // dropping volatile flag
|
|
2079
|
+
return this.expression_calculator.Calculate(expression, address, undefined, preserve_flags).value; // dropping volatile flag
|
|
2080
2080
|
}
|
|
2081
2081
|
|
|
2082
2082
|
/**
|
|
@@ -19,7 +19,15 @@
|
|
|
19
19
|
*
|
|
20
20
|
*/
|
|
21
21
|
|
|
22
|
-
import type { RenderFunction, ClickFunction, UnionValue } from 'treb-base-types';
|
|
22
|
+
import type { RenderFunction, ClickFunction, UnionValue, ICellAddress, IArea } from 'treb-base-types';
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* FIXME: possible to add stuff in here if we need it
|
|
26
|
+
*/
|
|
27
|
+
export interface FunctionContext {
|
|
28
|
+
address: ICellAddress;
|
|
29
|
+
area?: IArea;
|
|
30
|
+
}
|
|
23
31
|
|
|
24
32
|
// FIXME: at least some of this could move to base types
|
|
25
33
|
|
|
@@ -122,12 +130,12 @@ export interface CompositeFunctionDescriptor {
|
|
|
122
130
|
* the actual function. if this is an object member and needs access
|
|
123
131
|
* to the containing instance, make sure to bind it to that instance.
|
|
124
132
|
*
|
|
125
|
-
*
|
|
126
|
-
* a
|
|
133
|
+
* otherwise, `this` will be bound to a `CalculationContext` object
|
|
134
|
+
* if your function is defined as a function (i.e. not an arrow function).
|
|
127
135
|
*
|
|
128
136
|
*/
|
|
129
137
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
130
|
-
fn: (...args: any[]) => UnionValue;
|
|
138
|
+
fn: (this: FunctionContext|undefined, ...args: any[]) => UnionValue;
|
|
131
139
|
|
|
132
140
|
/**
|
|
133
141
|
* limited visibility
|
|
@@ -26,7 +26,8 @@ import type { Cell, ICellAddress,
|
|
|
26
26
|
NumberUnion,
|
|
27
27
|
UndefinedUnion,
|
|
28
28
|
ComplexUnion,
|
|
29
|
-
DimensionedQuantityUnion
|
|
29
|
+
DimensionedQuantityUnion,
|
|
30
|
+
IArea} from 'treb-base-types';
|
|
30
31
|
import { ValueType, GetValueType, Area } from 'treb-base-types';
|
|
31
32
|
import type { Parser, ExpressionUnit, UnitBinary, UnitIdentifier,
|
|
32
33
|
UnitGroup, UnitUnary, UnitAddress, UnitRange, UnitCall, UnitDimensionedQuantity, UnitStructuredReference } from 'treb-parser';
|
|
@@ -83,6 +84,7 @@ export interface ReferenceMetadata {
|
|
|
83
84
|
|
|
84
85
|
export interface CalculationContext {
|
|
85
86
|
address: ICellAddress;
|
|
87
|
+
area?: IArea;
|
|
86
88
|
volatile: boolean;
|
|
87
89
|
}
|
|
88
90
|
|
|
@@ -104,12 +106,13 @@ export class ExpressionCalculator {
|
|
|
104
106
|
* there's a case where we are calling this from within a function
|
|
105
107
|
* (which is weird, but hey) and to do that we need to preserve flags.
|
|
106
108
|
*/
|
|
107
|
-
public Calculate(expr: ExpressionUnit, addr: ICellAddress, preserve_flags = false): {
|
|
109
|
+
public Calculate(expr: ExpressionUnit, addr: ICellAddress, area?: IArea, preserve_flags = false): {
|
|
108
110
|
value: UnionValue /*UnionOrArray*/, volatile: boolean }{
|
|
109
111
|
|
|
110
112
|
if (!preserve_flags) {
|
|
111
113
|
this.context.address = addr;
|
|
112
114
|
this.context.volatile = false;
|
|
115
|
+
this.context.area = area;
|
|
113
116
|
}
|
|
114
117
|
|
|
115
118
|
return {
|
|
@@ -560,9 +563,19 @@ export class ExpressionCalculator {
|
|
|
560
563
|
return argument_error;
|
|
561
564
|
}
|
|
562
565
|
|
|
566
|
+
// cloning, out of an abundance of caution
|
|
567
|
+
// const ctx = JSON.parse(JSON.stringify({ address: this.context.address, area: this.context.area }));
|
|
568
|
+
const ctx = {
|
|
569
|
+
address: { ...this.context.address },
|
|
570
|
+
area: this.context.area ? {
|
|
571
|
+
start: { ...this.context.area.start, },
|
|
572
|
+
end: { ...this.context.area.end, },
|
|
573
|
+
} : undefined,
|
|
574
|
+
};
|
|
575
|
+
|
|
563
576
|
if (func.return_type === 'reference') {
|
|
564
577
|
|
|
565
|
-
const result = func.fn.apply(
|
|
578
|
+
const result = func.fn.apply(ctx, mapped_args);
|
|
566
579
|
|
|
567
580
|
if (return_reference) {
|
|
568
581
|
return result;
|
|
@@ -581,7 +594,7 @@ export class ExpressionCalculator {
|
|
|
581
594
|
|
|
582
595
|
}
|
|
583
596
|
|
|
584
|
-
return func.fn.apply(
|
|
597
|
+
return func.fn.apply(ctx, mapped_args);
|
|
585
598
|
|
|
586
599
|
};
|
|
587
600
|
|