@trebco/treb 30.16.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.
Files changed (47) hide show
  1. package/api-generator/api-generator.ts +3 -1
  2. package/api-generator/package.json +2 -1
  3. package/dist/treb-export-worker.mjs +2 -2
  4. package/dist/treb-spreadsheet.mjs +13 -13
  5. package/dist/treb.d.ts +19 -2
  6. package/package.json +8 -7
  7. package/treb-base-types/src/font-stack.ts +144 -0
  8. package/treb-base-types/src/style.ts +121 -11
  9. package/treb-base-types/src/theme.ts +53 -8
  10. package/treb-calculator/src/calculator.ts +13 -13
  11. package/treb-calculator/src/descriptors.ts +12 -4
  12. package/treb-calculator/src/expression-calculator.ts +17 -4
  13. package/treb-calculator/src/functions/base-functions.ts +57 -4
  14. package/treb-calculator/src/functions/statistics-functions.ts +9 -6
  15. package/treb-calculator/tsconfig.json +11 -0
  16. package/treb-charts/style/charts.scss +7 -1
  17. package/treb-data-model/src/annotation.ts +6 -0
  18. package/treb-data-model/src/data_model.ts +14 -3
  19. package/treb-data-model/src/sheet.ts +57 -56
  20. package/treb-embed/markup/toolbar.html +15 -1
  21. package/treb-embed/src/custom-element/spreadsheet-constructor.ts +38 -0
  22. package/treb-embed/src/embedded-spreadsheet.ts +119 -29
  23. package/treb-embed/src/options.ts +3 -0
  24. package/treb-embed/src/selection-state.ts +1 -0
  25. package/treb-embed/src/toolbar-message.ts +6 -0
  26. package/treb-embed/src/types.ts +9 -0
  27. package/treb-embed/style/defaults.scss +12 -1
  28. package/treb-embed/style/font-stacks.scss +105 -0
  29. package/treb-embed/style/layout.scss +1 -0
  30. package/treb-embed/style/theme-defaults.scss +12 -2
  31. package/treb-embed/style/toolbar.scss +16 -0
  32. package/treb-grid/src/editors/overlay_editor.ts +36 -3
  33. package/treb-grid/src/layout/base_layout.ts +52 -37
  34. package/treb-grid/src/layout/grid_layout.ts +7 -0
  35. package/treb-grid/src/render/tile_renderer.ts +154 -148
  36. package/treb-grid/src/types/grid.ts +188 -54
  37. package/treb-grid/src/types/grid_events.ts +1 -1
  38. package/treb-grid/src/types/grid_options.ts +3 -0
  39. package/treb-grid/src/util/fontmetrics.ts +134 -0
  40. package/treb-parser/src/parser.ts +12 -3
  41. package/treb-utils/src/measurement.ts +2 -3
  42. package/tsproject.json +1 -1
  43. package/treb-calculator/modern.tsconfig.json +0 -11
  44. package/treb-grid/src/util/fontmetrics2.ts +0 -182
  45. package/treb-parser/src/parser.test.ts +0 -298
  46. /package/treb-embed/{modern.tsconfig.json → tsconfig.json} +0 -0
  47. /package/treb-export/{modern.tsconfig.json → tsconfig.json} +0 -0
@@ -759,6 +759,8 @@ export class EmbeddedSpreadsheet<USER_DATA_TYPE = unknown> {
759
759
  // delete_tab: this.options.delete_tab,
760
760
  expand: this.options.expand,
761
761
 
762
+ support_font_stacks: !!this.options.font_stack,
763
+
762
764
  };
763
765
 
764
766
  if (this.options.scale) {
@@ -838,7 +840,12 @@ export class EmbeddedSpreadsheet<USER_DATA_TYPE = unknown> {
838
840
  // if we don't have a container. that's distinct (at the moment)
839
841
  // from headless, which is a state that can change.
840
842
 
841
- this.grid = new Grid(grid_options, this.model, undefined, !!container, this.DOM);
843
+ this.grid = new Grid(
844
+ grid_options,
845
+ this.model,
846
+ undefined,
847
+ !!container,
848
+ this.DOM);
842
849
 
843
850
  if (this.options.headless) {
844
851
  this.grid.headless = true; // FIXME: move into grid options
@@ -1003,7 +1010,7 @@ export class EmbeddedSpreadsheet<USER_DATA_TYPE = unknown> {
1003
1010
 
1004
1011
  case 'scale':
1005
1012
  this.RebuildAllAnnotations();
1006
- this.events.Publish({ type: 'view-change' });
1013
+ this.Publish({ type: 'view-change' });
1007
1014
  break;
1008
1015
 
1009
1016
  case 'annotation':
@@ -1013,32 +1020,48 @@ export class EmbeddedSpreadsheet<USER_DATA_TYPE = unknown> {
1013
1020
  if (event.annotation) {
1014
1021
  const view: AnnotationViewData = event.annotation.view[this.grid.view_index] || {};
1015
1022
 
1016
- this.DocumentChange();
1017
- switch (event.event) {
1018
- case 'create':
1019
- this.InflateAnnotation(event.annotation);
1020
- this.calculator.UpdateAnnotations(event.annotation, this.grid.active_sheet);
1021
- this.grid.AnnotationUpdated(event.annotation);
1022
- break;
1023
- case 'delete':
1024
- this.calculator.RemoveAnnotation(event.annotation); // clean up vertex
1025
- break;
1026
- case 'update':
1027
- if (view.update_callback) {
1028
- view.update_callback();
1029
- this.grid.AnnotationUpdated(event.annotation);
1030
- }
1031
- else {
1032
- console.info('annotation update event without update callback');
1033
- }
1034
- this.calculator.UpdateAnnotations(event.annotation, this.grid.active_sheet);
1035
- break;
1036
- case 'resize':
1037
- if (view.resize_callback) {
1038
- view.resize_callback();
1023
+ let update_toolbar = false;
1024
+
1025
+ if (event.event === 'select') {
1026
+ update_toolbar = true;
1027
+ }
1028
+ else {
1029
+ this.DocumentChange();
1030
+ switch (event.event) {
1031
+ case 'create':
1032
+ this.InflateAnnotation(event.annotation);
1033
+ this.calculator.UpdateAnnotations(event.annotation, this.grid.active_sheet);
1039
1034
  this.grid.AnnotationUpdated(event.annotation);
1040
- }
1041
- break;
1035
+ update_toolbar = (event.annotation === this.grid.selected_annotation);
1036
+ break;
1037
+ case 'delete':
1038
+ this.calculator.RemoveAnnotation(event.annotation); // clean up vertex
1039
+ break;
1040
+ case 'update':
1041
+ if (view.update_callback) {
1042
+ view.update_callback();
1043
+ this.grid.AnnotationUpdated(event.annotation);
1044
+ }
1045
+ else {
1046
+ console.info('annotation update event without update callback');
1047
+ }
1048
+ this.calculator.UpdateAnnotations(event.annotation, this.grid.active_sheet);
1049
+ update_toolbar = (event.annotation === this.grid.selected_annotation);
1050
+ break;
1051
+ case 'resize':
1052
+ if (view.resize_callback) {
1053
+ view.resize_callback();
1054
+ this.grid.AnnotationUpdated(event.annotation);
1055
+ }
1056
+ break;
1057
+ }
1058
+
1059
+ }
1060
+
1061
+ if (update_toolbar) {
1062
+ this.UpdateSelectionStyleAnnotation(event.annotation);
1063
+ this.Publish({ type: 'annotation-selection' });
1064
+ break;
1042
1065
  }
1043
1066
 
1044
1067
  }
@@ -1557,6 +1580,34 @@ export class EmbeddedSpreadsheet<USER_DATA_TYPE = unknown> {
1557
1580
 
1558
1581
  let updated_style: CellStyle = {};
1559
1582
 
1583
+ if (this.grid.AnnotationSelected()) {
1584
+
1585
+ // only handle a limited set of commands, then
1586
+
1587
+ switch (event.command) {
1588
+ case 'font-scale':
1589
+ {
1590
+ const scale = Number(event.scale || 1);
1591
+ if (scale && !isNaN(scale)) {
1592
+ updated_style.font_size = { unit: 'em', value: scale };
1593
+ this.grid.ApplyAnnotationStyle(updated_style);
1594
+ }
1595
+ }
1596
+ break;
1597
+
1598
+ case 'font-stack':
1599
+ if (event.font_stack) {
1600
+ updated_style.font_face = 'stack:' + event.font_stack;
1601
+ }
1602
+ this.grid.ApplyAnnotationStyle(updated_style);
1603
+ break;
1604
+ }
1605
+
1606
+ this.Focus();
1607
+ return;
1608
+
1609
+ }
1610
+
1560
1611
  const insert_annotation = (func: string) => {
1561
1612
  const selection = this.grid.GetSelection();
1562
1613
  if (selection && !selection.empty) {
@@ -1608,6 +1659,12 @@ export class EmbeddedSpreadsheet<USER_DATA_TYPE = unknown> {
1608
1659
  updated_style.number_format = event.format || 'General';
1609
1660
  break;
1610
1661
 
1662
+ case 'font-stack':
1663
+ if (event.font_stack) {
1664
+ updated_style.font_face = 'stack:' + event.font_stack;
1665
+ }
1666
+ break;
1667
+
1611
1668
  case 'font-scale':
1612
1669
 
1613
1670
  // above we handle 'font-size' events; this comes from a dropdown,
@@ -1615,6 +1672,7 @@ export class EmbeddedSpreadsheet<USER_DATA_TYPE = unknown> {
1615
1672
  // FIXME: break out
1616
1673
 
1617
1674
  {
1675
+
1618
1676
  const selection = this.grid.GetSelection();
1619
1677
  const area = this.grid.active_sheet.RealArea(selection.area);
1620
1678
  const scale = Number(event.scale || 1);
@@ -1899,7 +1957,7 @@ export class EmbeddedSpreadsheet<USER_DATA_TYPE = unknown> {
1899
1957
  if (Object.keys(updated_style).length) {
1900
1958
  this.grid.ApplyStyle(undefined, updated_style, true);
1901
1959
  }
1902
-
1960
+
1903
1961
  this.Focus();
1904
1962
 
1905
1963
  }
@@ -5813,6 +5871,26 @@ export class EmbeddedSpreadsheet<USER_DATA_TYPE = unknown> {
5813
5871
  this.Publish({ type: 'selection' });
5814
5872
  }
5815
5873
 
5874
+ /** update selection style for the toolbar, when an annotation is selected. */
5875
+ protected UpdateSelectionStyleAnnotation(annotation: Annotation) {
5876
+
5877
+ const state: SelectionState = {
5878
+ style: JSON.parse(JSON.stringify(annotation.data.style || {})),
5879
+ };
5880
+
5881
+ if (state.style) {
5882
+ if (!state.style.font_face) {
5883
+ state.style.font_face = 'stack:ui';
5884
+ }
5885
+ if (!state.style.font_size) {
5886
+ state.style.font_size = { unit: 'em', value: 1 };
5887
+ }
5888
+ }
5889
+
5890
+ state.relative_font_size = Style.RelativeFontSize(state.style || {}, this.grid.theme.grid_cell || {});
5891
+ this.selection_state = state;
5892
+
5893
+ }
5816
5894
 
5817
5895
  /** update selection style for the toolbar */
5818
5896
  protected UpdateSelectionStyle(selection?: GridSelection): void {
@@ -5845,10 +5923,22 @@ export class EmbeddedSpreadsheet<USER_DATA_TYPE = unknown> {
5845
5923
  state.style = data.style ? { ...data.style } : undefined;
5846
5924
  state.relative_font_size = Style.RelativeFontSize(state.style || {}, this.grid.theme.grid_cell || {});
5847
5925
 
5926
+ state.empty = typeof data.value === 'undefined' && !data.style?.font_face;
5927
+
5928
+ // let's not do this
5929
+ /*
5930
+ if (state.empty && state.style) {
5931
+ state.style.font_face = this.selection_state.style?.font_face || undefined;
5932
+ }
5933
+ */
5934
+
5935
+ }
5936
+
5937
+ if (this.grid?.edit_state) {
5938
+ this.grid.edit_state.font_face = state.style?.font_face || undefined;
5848
5939
  }
5849
5940
 
5850
5941
  this.selection_state = state;
5851
- // this.toolbar?.UpdateState(state);
5852
5942
 
5853
5943
  }
5854
5944
 
@@ -187,6 +187,9 @@ export interface EmbeddedSpreadsheetOptions {
187
187
  /** include the font scale control in the toolbar */
188
188
  font_scale?: boolean;
189
189
 
190
+ /** include the font stack control in the toolbar */
191
+ font_stack?: boolean;
192
+
190
193
  /** include the insert/remove table button in the toolbar */
191
194
  table_button?: boolean;
192
195
 
@@ -10,6 +10,7 @@ export interface SelectionState {
10
10
  merge?: boolean;
11
11
  table?: boolean;
12
12
  frozen?: boolean;
13
+ empty?: boolean;
13
14
  comment?: string;
14
15
  selection?: GridSelection;
15
16
  relative_font_size?: number;
@@ -55,6 +55,11 @@ export interface FontScaleToolbarMessage {
55
55
  scale?: number;
56
56
  }
57
57
 
58
+ export interface FontStackToolbarMessage {
59
+ command: 'font-stack';
60
+ font_stack?: string;
61
+ }
62
+
58
63
  export interface NumberFormatToolbarMessage {
59
64
  command: 'number-format';
60
65
  format?: string;
@@ -103,4 +108,5 @@ export type ToolbarMessage
103
108
  | IndentMessage
104
109
  | NumberFormatToolbarMessage
105
110
  | FontScaleToolbarMessage
111
+ | FontStackToolbarMessage
106
112
  ;
@@ -198,6 +198,14 @@ export interface SelectionEvent {
198
198
  type: 'selection';
199
199
  }
200
200
 
201
+ /**
202
+ * this event is used when an annotation is selected. we're not changing
203
+ * the original selection event, because I don't want to break anything.
204
+ */
205
+ export interface AnnotationSelectionEvent {
206
+ type: 'annotation-selection';
207
+ }
208
+
201
209
  /**
202
210
  * This event is sent when the focused view changes, if you have more
203
211
  * than one view.
@@ -220,6 +228,7 @@ export type EmbeddedSheetEvent
220
228
  | FocusViewEvent
221
229
  | SelectionEvent
222
230
  | ResizeEvent
231
+ | AnnotationSelectionEvent
223
232
  ;
224
233
 
225
234
  /**
@@ -25,8 +25,19 @@ $default-box-shadow: 0px 1px 4px 2px rgba(109,109,109,0.2);
25
25
 
26
26
  $tooltip-arrow-size: 4px;
27
27
 
28
+ //
29
+ // -apple-system came out because it would break safari when rendering
30
+ // certain characters. if you render an acute angle character ("⦟", U+299F)
31
+ // in a canvas in safari with -apple-system, javascript will stop. no
32
+ // exception, no error, it just stops.
33
+ //
34
+ // that was ca 2023? haven't tested this lately so it may be resolved by now.
35
+ //
36
+
37
+ // "BlinkMacSystemFont" magically turns into "system-ui"? that's incredibly annoying.
38
+
28
39
  $font-stack: // "-apple-system",
29
- "BlinkMacSystemFont",
40
+ "BlinkMacSystemFont", // this turns into "system-ui"? wuwt? is that a sass thing?
30
41
  "Segoe UI",
31
42
  "Roboto",
32
43
  "Oxygen-Sans",
@@ -0,0 +1,105 @@
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
+ //
23
+ // thanks to
24
+ // https://modernfontstacks.com/
25
+ //
26
+ // for the stacks. we make some minor changes, mostly because of browser issues.
27
+ //
28
+
29
+ .treb-main.treb-main {
30
+
31
+ /* default size for fonts in grid cells */
32
+ --treb-font-stack-default-size: 14px;
33
+
34
+ /* size adjustment for some fonts */
35
+ --treb-font-stack-calibri-size: 16px;
36
+ --treb-font-stack-tscu-comic-size: 16px;
37
+ --treb-font-stack-helvetica-neue-size: 13px;
38
+ --treb-font-stack-cambria-size: 16px;
39
+
40
+ /* flag that we need to use font variants (chrome only) */
41
+ --treb-font-stack-sitka-text-variant: lining-nums tabular-nums;
42
+
43
+ /*
44
+ * os-specific size adjustment. it's not that important but for consistency
45
+ * with the old layout this should be 10pt, ~13.33px. I notice that the
46
+ * editor is slightly off from the canvas rendering, I assume that's related
47
+ * to the subpixel size? maybe we should break from the old version. (or
48
+ * maybe we can fix the editor?)
49
+ */
50
+ &.treb-ua-osx {
51
+ --treb-font-stack-system-ui-size: 10pt;
52
+ }
53
+
54
+ /**
55
+ * this is the default font. it's also used for other font stacks
56
+ * if stacks are turned off (the default, for now).
57
+ */
58
+ .treb-font-stack-default {
59
+
60
+ // I love avenir but the bold is too bold. can we adjust it?
61
+
62
+ // font-family: calibri, avenir, $font-stack;
63
+ font-family: calibri, $font-stack;
64
+
65
+ }
66
+
67
+ .treb-font-stack-transitional {
68
+
69
+ // we drop sitka text because the bounding box isn't calculating properly
70
+ // for some reason -- variants? also, firefox does not support numeric
71
+ // variants in canvas and the sitka default is old-style non-tabular nums.
72
+
73
+ // font-family: Charter, 'Bitstream Charter', 'Sitka Text', Cambria, serif;
74
+ font-family: Charter, 'Bitstream Charter', Cambria, serif;
75
+
76
+ }
77
+
78
+ /* not using this atm, we just have one serif (the transitional) */
79
+ .treb-font-stack-old-style {
80
+ font-family: 'Iowan Old Style', 'Palatino Linotype', 'URW Palladio L', P052, serif;
81
+ }
82
+
83
+ .treb-font-stack-handwritten {
84
+
85
+ // not sure I believe that that tscu comic font is widely installed...
86
+ // there might be a better alternative.
87
+
88
+ font-family: 'Segoe Print', 'Bradley Hand', Chilanka, TSCu_Comic, casual, cursive;
89
+ }
90
+
91
+ /* not using this atm */
92
+ .treb-font-stack-industrial {
93
+ font-family: Bahnschrift, 'DIN Alternate', 'Franklin Gothic Medium', 'Nimbus Sans Narrow', sans-serif-condensed, sans-serif;
94
+ }
95
+
96
+ /* not a slab */
97
+ .treb-font-stack-monospace {
98
+ font-family: ui-monospace, 'Cascadia Code', 'Source Code Pro', Menlo, Consolas, 'DejaVu Sans Mono', monospace;
99
+ }
100
+
101
+ .treb-font-stack-ui {
102
+ font-family: system-ui, sans-serif;
103
+ }
104
+
105
+ }
@@ -7,6 +7,7 @@
7
7
  @import './spinner.scss';
8
8
  @import './treb-icons.scss';
9
9
  @import './toolbar.scss';
10
+ @import './font-stacks.scss';
10
11
 
11
12
  /*
12
13
  * switching to a double-selector to increase specificity. the particular
@@ -192,7 +192,16 @@ $text-reference-color-5: rgb(254, 47, 1);
192
192
  */
193
193
  .grid-cells {
194
194
  font-family: var(--treb-grid-font-family, inherit);
195
+
196
+ // it seems like the old default was 14px / 16px for calibri on windows?
197
+ // we should get back to that, then we can adjust calibri (in the stacks)
198
+ // to scale.
199
+
200
+ // font-size: var(--treb-grid-font-size, 10px);
201
+ // font-size: var(--treb-grid-font-size, 10pt);
202
+
195
203
  font-size: var(--treb-grid-font-size, 14px);
204
+
196
205
  color: var(--treb-grid-default-color, inherit);
197
206
  stroke: var(--treb-grid-grid-color, rgb(204, 204, 212));
198
207
  background: var(--treb-grid-background, #fff);
@@ -520,11 +529,11 @@ $text-reference-color-5: rgb(254, 47, 1);
520
529
 
521
530
 
522
531
 
523
- /**
532
+ /* *
524
533
  * special override for windows, using calibri (which is too small).
525
534
  * you should still be able to override with a regular theme style,
526
535
  * or (potentially) also override for windows specifically. fancy!
527
- */
536
+ * /
528
537
  &.treb-ua-windows .grid-cells {
529
538
  font-size: var(--treb-grid-font-size-windows, var(--treb-grid-font-size, 12pt));
530
539
  font-family: var(--treb-grid-font-family-windows, var(--treb-grid-font-family, "Calibri", $font-stack)); // does this actually work? is this included? (...)
@@ -534,6 +543,7 @@ $text-reference-color-5: rgb(254, 47, 1);
534
543
  font-size: var(--treb-grid-font-size-osx, var(--treb-grid-font-size, 10pt)); // slightly smaller default
535
544
  font-family: var(--treb-grid-font-family-osx, var(--treb-grid-font-family, inherit));
536
545
  }
546
+ */
537
547
 
538
548
  /**
539
549
  *
@@ -157,6 +157,13 @@ $swatch-size: 18px;
157
157
  mask-size: var(--icon-size, 16px 16px);
158
158
  -webkit-mask-size: var(--icon-size, 16px 16px);
159
159
  }
160
+
161
+ &.treb-font-stack {
162
+ &::before {
163
+ display: none;
164
+ }
165
+ }
166
+
160
167
  }
161
168
 
162
169
  }
@@ -193,6 +200,15 @@ $swatch-size: 18px;
193
200
 
194
201
  }
195
202
 
203
+ .treb-font-stack {
204
+ width: 8em;
205
+ align-items: flex-start;
206
+ overflow: hidden;
207
+ &::before {
208
+ display: none;
209
+ }
210
+ }
211
+
196
212
  .treb-menu {
197
213
 
198
214
  // on Safari, we need to make these menus focus targets, so we can
@@ -67,6 +67,9 @@ export class OverlayEditor extends Editor<ResetSelectionEvent> {
67
67
 
68
68
  // ---------------------------------------------------------------------------
69
69
 
70
+ /** possibly carrying over a font face (+size?) */
71
+ public edit_style?: CellStyle;
72
+
70
73
  /**
71
74
  * this is a flag used to indicate when we need to reset the selection.
72
75
  * the issue has to do with selecting cells via arrow keys; if you do
@@ -273,7 +276,7 @@ export class OverlayEditor extends Editor<ResetSelectionEvent> {
273
276
  *
274
277
  * something to do with keyboard selection? (which needs to be fixed)?
275
278
  */
276
- public Edit(gridselection: GridSelection, rect: Rectangle, cell: Cell, value?: CellValue, event?: Event): void {
279
+ public Edit(gridselection: GridSelection, rect: Rectangle, cell: Cell, value?: CellValue, event?: Event, edit_style?: CellStyle): void {
277
280
 
278
281
  this.Publish({
279
282
  type: 'start-editing',
@@ -284,9 +287,33 @@ export class OverlayEditor extends Editor<ResetSelectionEvent> {
284
287
  this.target_address = {...gridselection.target};
285
288
  this.reset_selection = false;
286
289
 
287
- const style: CellStyle = cell.style || {};
290
+ const style: CellStyle = JSON.parse(JSON.stringify(cell.style || {})); // clone
291
+
292
+ //
293
+ // CURRENT ISSUE: this works, but the style is not actually applied to
294
+ // the cell. we need to modify the commit routine to apply the style.
295
+ // I think?
296
+ //
297
+ // WAIT, that might not be right... might have something to do with
298
+ // default?
299
+ //
300
+
301
+ this.edit_style = edit_style; // set (or unset)
302
+
303
+ if (edit_style?.font_face) {
304
+ style.font_face = edit_style.font_face;
305
+ }
306
+
307
+ const font_info = Style.CompositeFont(this.theme.grid_cell_font_size, style, this.scale, this.theme);
308
+
309
+ this.edit_node.style.font = font_info.font;
310
+ if (font_info.variants) {
311
+ this.edit_node.style.fontVariant = font_info.variants;
312
+ }
313
+ else {
314
+ this.edit_node.style.fontVariant = '';
315
+ }
288
316
 
289
- this.edit_node.style.font = Style.Font(style, this.scale);
290
317
  this.edit_node.style.color = ResolveThemeColor(this.theme, style.text, 1);
291
318
  this.edit_inset.style.backgroundColor = ResolveThemeColor(this.theme, style.fill, 0);
292
319
 
@@ -332,6 +359,12 @@ export class OverlayEditor extends Editor<ResetSelectionEvent> {
332
359
 
333
360
  rect.ApplyStyle(this.container_node);
334
361
 
362
+ // trial and error. still a little wonky at scales, but reasonable.
363
+
364
+ const offset = UA.is_mac ? 0 : 0.5;
365
+
366
+ this.edit_node.style.bottom = offset.toFixed(2) + 'px'; // does this need to scale for dpr? not sure
367
+
335
368
  this.autocomplete?.ResetBlock();
336
369
  this.selection = gridselection;
337
370
 
@@ -377,6 +377,11 @@ export abstract class BaseLayout {
377
377
 
378
378
  }
379
379
 
380
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
381
+ public FocusInLayout(target?: EventTarget): boolean {
382
+ return false;
383
+ }
384
+
380
385
  /**
381
386
  * if the DPR has changed, update it and return true. otherwise return
382
387
  * false. this is used on resize events: if the scale has changed, we
@@ -672,7 +677,7 @@ export abstract class BaseLayout {
672
677
 
673
678
  }
674
679
 
675
- public UpdateAnnotation(elements: Annotation | Annotation[]): void {
680
+ public UpdateAnnotation(elements: Annotation | Annotation[], theme: Theme): void {
676
681
  if (!Array.isArray(elements)) elements = [elements];
677
682
  for (const annotation of elements) {
678
683
  const view = annotation.view[this.view.view_index] || {};
@@ -684,8 +689,48 @@ export abstract class BaseLayout {
684
689
  }
685
690
  */
686
691
 
692
+ let font_size_pt = 10 * this.scale;
693
+
694
+ // console.info(annotation.data.style);
695
+
696
+ if (annotation.data.style?.font_size?.unit === 'em') {
697
+
698
+ font_size_pt *= annotation.data.style.font_size.value;
699
+
700
+ // console.info("scaling up...", font_size_pt)
701
+
702
+ }
703
+
687
704
  view.node.dataset.scale = this.scale.toString();
688
- view.node.style.fontSize = `${10 * this.scale}pt`;
705
+
706
+ // maybe we should just reset and then apply?
707
+
708
+ if (annotation.data.style?.font_face && annotation.data.style.font_face.startsWith('stack:')) {
709
+ const name = annotation.data.style.font_face.substring(6);
710
+ const stack = theme.font_stacks[name];
711
+ if(stack) {
712
+ view.node.classList.add('treb-inherit-font');
713
+ view.node.style.fontFamily = stack.font || '';
714
+
715
+ // font_size_pt *= stack.scale;
716
+
717
+ if (stack.variants) {
718
+ view.node.style.fontVariant = stack.variants;
719
+ }
720
+ }
721
+ else {
722
+ view.node.classList.remove('treb-inherit-font');
723
+ view.node.style.fontFamily = '';
724
+ view.node.style.fontVariant = '';
725
+ }
726
+ }
727
+ else {
728
+ view.node.classList.remove('treb-inherit-font');
729
+ view.node.style.fontFamily = '';
730
+ view.node.style.fontVariant = '';
731
+ }
732
+
733
+ view.node.style.fontSize = `${font_size_pt}pt`;
689
734
 
690
735
  // update the layout here if necessary. after that it should
691
736
  // be persisted (assuming it's saved). eventually this should
@@ -848,13 +893,13 @@ export abstract class BaseLayout {
848
893
 
849
894
  }
850
895
 
851
- public AddAnnotation(annotation: Annotation): void {
896
+ public AddAnnotation(annotation: Annotation, theme: Theme): void {
852
897
  const view = annotation.view[this.view.view_index] || {};
853
898
  if (!view.node) {
854
899
  throw new Error('annotation view/node missing');
855
900
  }
856
901
  this.annotation_container.appendChild(view.node);
857
- this.UpdateAnnotation(annotation);
902
+ this.UpdateAnnotation(annotation, theme);
858
903
  }
859
904
 
860
905
  // testing moving this here...
@@ -1246,38 +1291,6 @@ export abstract class BaseLayout {
1246
1291
 
1247
1292
  parent.appendChild(tile);
1248
1293
 
1249
- // NOTE re: text rendering. you can't use baseline = top, because that's
1250
- // inconsistent among browsers. in fact of all baselines, the only ones that
1251
- // are even close are alphabetic and bottom -- bottom is slightly different
1252
- // in ffx compared to chrome and edge, but that could be because of different
1253
- // font rendering schemes. alphabetic is the closest, but requires offset for
1254
- // ascender (or descender).
1255
-
1256
- // actually it looks like there's a 1px difference in bottom baseline...
1257
- // alphabetic is the only one that's consistent.
1258
-
1259
- // FIXME: why not just offset on a per-browser basis? it might be ugly
1260
- // but it's simpler.
1261
-
1262
- // for the time being we will use bottom.
1263
-
1264
- // why were we prepainting (because firefox, below?) and why was this so
1265
- // slow? do we need to preset the context text parameters?
1266
-
1267
- /*
1268
- const context = tile.getContext('2d', {alpha: false});
1269
-
1270
- if (context) {
1271
- context.textAlign = 'left';
1272
- context.textBaseline = 'alphabetic';
1273
-
1274
- // prepaint -- firefox is a little slow so flashes empty tiles sometimes
1275
-
1276
- // context.fillStyle = '#fff'; // FIXME: use theme color
1277
- // context.fillRect(0, 0, tile.width, tile.height);
1278
- }
1279
- */
1280
-
1281
1294
  return tile;
1282
1295
 
1283
1296
  }
@@ -1319,7 +1332,9 @@ export abstract class BaseLayout {
1319
1332
 
1320
1333
  // TODO: dropdown caret
1321
1334
 
1322
- this.dropdown_list.style.font = Style.Font(theme.grid_cell || {});
1335
+ this.dropdown_list.style.font = Style.CompositeFont(theme.grid_cell_font_size, {
1336
+ font_face: 'stack:default',
1337
+ }, this.scale, theme).font;
1323
1338
 
1324
1339
  // testing
1325
1340