@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.
Files changed (52) 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/src/chart-functions.ts +41 -4
  17. package/treb-charts/src/chart-types.ts +20 -1
  18. package/treb-charts/src/chart-utils.ts +86 -9
  19. package/treb-charts/src/default-chart-renderer.ts +40 -1
  20. package/treb-charts/src/renderer.ts +3 -3
  21. package/treb-charts/style/charts.scss +7 -1
  22. package/treb-data-model/src/annotation.ts +6 -0
  23. package/treb-data-model/src/data_model.ts +14 -3
  24. package/treb-data-model/src/sheet.ts +57 -56
  25. package/treb-embed/markup/toolbar.html +15 -1
  26. package/treb-embed/src/custom-element/spreadsheet-constructor.ts +38 -0
  27. package/treb-embed/src/embedded-spreadsheet.ts +119 -29
  28. package/treb-embed/src/options.ts +3 -0
  29. package/treb-embed/src/selection-state.ts +1 -0
  30. package/treb-embed/src/toolbar-message.ts +6 -0
  31. package/treb-embed/src/types.ts +9 -0
  32. package/treb-embed/style/defaults.scss +12 -1
  33. package/treb-embed/style/font-stacks.scss +105 -0
  34. package/treb-embed/style/layout.scss +1 -0
  35. package/treb-embed/style/theme-defaults.scss +12 -2
  36. package/treb-embed/style/toolbar.scss +16 -0
  37. package/treb-grid/src/editors/overlay_editor.ts +36 -3
  38. package/treb-grid/src/layout/base_layout.ts +52 -37
  39. package/treb-grid/src/layout/grid_layout.ts +7 -0
  40. package/treb-grid/src/render/tile_renderer.ts +154 -148
  41. package/treb-grid/src/types/grid.ts +188 -54
  42. package/treb-grid/src/types/grid_events.ts +1 -1
  43. package/treb-grid/src/types/grid_options.ts +3 -0
  44. package/treb-grid/src/util/fontmetrics.ts +134 -0
  45. package/treb-parser/src/parser.ts +12 -3
  46. package/treb-utils/src/measurement.ts +2 -3
  47. package/tsproject.json +1 -1
  48. package/treb-calculator/modern.tsconfig.json +0 -11
  49. package/treb-grid/src/util/fontmetrics2.ts +0 -182
  50. package/treb-parser/src/parser.test.ts +0 -298
  51. /package/treb-embed/{modern.tsconfig.json → tsconfig.json} +0 -0
  52. /package/treb-export/{modern.tsconfig.json → tsconfig.json} +0 -0
@@ -33,7 +33,10 @@ import { Measurement, ValidateURI } from 'treb-utils';
33
33
 
34
34
  import type { TextPart ,
35
35
  Cell, ICellAddress, CellSerializationOptions, CellValue, ImportedSheetData, Complex,
36
- DimensionedQuantity, IArea, Table, TableTheme, HorizontalAlign, VerticalAlign} from 'treb-base-types';
36
+ DimensionedQuantity, IArea, Table, TableTheme, HorizontalAlign, VerticalAlign,
37
+ Theme} from 'treb-base-types';
38
+
39
+ import { Get as GetFonrMetrics } from 'treb-grid/src/util/fontmetrics';
37
40
 
38
41
  // --- local imports ----------------------------------------------------------
39
42
 
@@ -299,7 +302,8 @@ export class Sheet {
299
302
 
300
303
  this.default_column_width = DEFAULT_COLUMN_WIDTH;
301
304
  this.row_header_width = DEFAULT_ROW_HEADER_WIDTH;
302
- this.UpdateDefaultRowHeight();
305
+
306
+ // this.UpdateDefaultRowHeight();
303
307
 
304
308
  this.id_ = Sheet.base_id++;
305
309
 
@@ -314,10 +318,14 @@ export class Sheet {
314
318
  /**
315
319
  * factory method creates a new sheet
316
320
  */
317
- public static Blank(style_defaults: CellStyle, name?: string, rows = 30, columns = 20): Sheet {
321
+ public static Blank(style_defaults: CellStyle, name?: string, rows = 30, columns = 20, theme?: Theme): Sheet {
318
322
 
319
323
  const sheet = new Sheet(style_defaults);
320
324
 
325
+ if (theme) {
326
+ sheet.UpdateDefaultRowHeight(theme);
327
+ }
328
+
321
329
  if (name) {
322
330
  sheet.name = name;
323
331
  }
@@ -905,71 +913,45 @@ export class Sheet {
905
913
 
906
914
  }
907
915
 
908
- /**
909
- * FIXME: measure the font.
910
- *
911
- * Can we use the same metrics as renderer? That uses a canvas. Obviously
912
- * canvas won't work if there's no DOM but it's OK if this method fails in
913
- * that case; the only question is will it break if it's running headless?
914
- */
915
- public StyleFontSize(style: CellStyle, default_properties: CellStyle = {}): number {
916
-
917
- let font_height = (style.font_size?.value || 0);
918
-
919
- let scale = 0;
920
-
921
- switch (style.font_size?.unit) {
922
- case 'px':
923
- font_height *= (75 / 100);
924
- break;
925
-
926
- case 'em':
927
- scale = style.font_size.value || 1;
928
- break;
929
-
930
- case '%':
931
- scale = (style.font_size.value || 100) / 100;
932
- break;
933
- }
934
-
935
- if (scale) {
936
- font_height = scale * (default_properties.font_size?.value || 10);
937
- if (default_properties.font_size?.unit === 'px') {
938
- font_height *= (75 / 100);
939
- }
940
- }
941
-
942
- return font_height || 10;
943
-
944
- }
945
-
946
916
  /**
947
917
  * FIXME: this is called in the ctor, which made sense when sheets
948
918
  * were more ephemeral. now that we update a single instance, rather
949
919
  * than create new instances, we lose this behavior. we should call
950
920
  * this when we change sheet style.
951
921
  *
952
- * removing parameter, event
922
+ * actually this should just move to theme, no? as long as sheet has
923
+ * a reference to theme. for headless instances that would just use
924
+ * theme defaults, which should be appropriate.
925
+ *
926
+ * I guess the original idea was that sheet style might be used to
927
+ * base row height on sheet font size? not sure if that's how it actually
928
+ * plays out, since this is only called in the ctor (or equivalent) or when
929
+ * theme is updated (but not when sheet style is updated). might need some
930
+ * thought.
931
+ *
953
932
  */
954
- public UpdateDefaultRowHeight(): void {
933
+ public UpdateDefaultRowHeight(theme: Theme, scale = 1): void {
955
934
 
956
- const composite = Style.Composite([this.default_style_properties, this.sheet_style]);
935
+ // this guard is here because this is called by sheet directly, so maybe
936
+ // in headless context? would make more sense to just not call it
957
937
 
958
938
  if (typeof window !== 'undefined') {
959
939
 
960
- const measurement = Measurement.MeasureText(Style.Font(composite), 'M');
961
- const height = Math.round(measurement.height * 1.4);
940
+ const composite = Style.Composite([this.default_style_properties, this.sheet_style]);
941
+ const font_info = Style.CompositeFont(theme.grid_cell_font_size, composite, scale, theme);
942
+ const metrics = GetFonrMetrics(font_info.font, font_info.variants);
943
+ const height = metrics.height * 1.25; // ??
944
+
945
+ // const measurement = Measurement.MeasureText(Style.Font2(composite, 1, theme).font, 'M');
946
+ // const height = Math.round(measurement.height * 1.4);
947
+
948
+ // console.info({height, default: this.default_row_height});
962
949
 
963
950
  if (this.default_row_height < height) {
964
951
  this.default_row_height = height;
965
952
  }
966
953
 
967
954
  }
968
- /*
969
- else {
970
- // console.info('worker?');
971
- }
972
- */
973
955
 
974
956
  }
975
957
 
@@ -1658,14 +1640,14 @@ export class Sheet {
1658
1640
  this.column_header_height = column_header_height;
1659
1641
  }
1660
1642
 
1661
- /**
1643
+ /* *
1662
1644
  * resize row to match character hight, taking into
1663
1645
  * account multi-line values.
1664
1646
  *
1665
1647
  * UPDATE: since the only caller calls with inline = true, removing
1666
1648
  * parameter, test, and extra behavior.
1667
- */
1668
- public AutoSizeRow(row: number, default_properties: CellStyle = {}, allow_shrink = true): void {
1649
+ * /
1650
+ public AutoSizeRow(row: number, theme?: Theme, allow_shrink = true, scale = 1): void {
1669
1651
 
1670
1652
  let height = this.default_row_height;
1671
1653
  const padding = 9; // 9?
@@ -1673,7 +1655,7 @@ export class Sheet {
1673
1655
  for (let column = 0; column < this.cells.columns; column++) {
1674
1656
 
1675
1657
  const cell = this.CellData({ row, column });
1676
- const style = cell.style;
1658
+ const style = JSON.parse(JSON.stringify(cell.style));
1677
1659
  let text = cell.formatted || '';
1678
1660
 
1679
1661
  if (typeof text !== 'string') {
@@ -1682,7 +1664,25 @@ export class Sheet {
1682
1664
 
1683
1665
  if (style && text && text.length) {
1684
1666
  const lines = text.split(/\n/);
1685
- const font_height = Math.round(this.StyleFontSize(style, default_properties) * 1.5); // it's a start, we still need to measure properly
1667
+
1668
+ if (style.font_size) {
1669
+ if (style.font_size.unit === 'em') {
1670
+ const base = theme?.grid_cell_font_size || { unit: 'pt', value: 10 };
1671
+ style.font_size.unit = base.unit;
1672
+ style.font_size.value *= base.value;
1673
+ }
1674
+ }
1675
+ else {
1676
+ style.font_size = theme?.grid_cell_font_size || { unit: 'pt', value: 10 };
1677
+ }
1678
+
1679
+ let target = style.font_size.value;
1680
+ if (style.font_size.unit === 'px') {
1681
+ target = Math.round((style.font_size.value||16) * 300 / 4) / 100;
1682
+ }
1683
+
1684
+ const font_height = // Math.round(this.StyleFontSize(style, default_properties) * 1.5); // it's a start, we still need to measure properly
1685
+ Math.round(target * 1.5 * scale);
1686
1686
  height = Math.max(height, ((font_height || 10) + padding) * lines.length);
1687
1687
  }
1688
1688
  }
@@ -1695,6 +1695,7 @@ export class Sheet {
1695
1695
  this.SetRowHeight(row, height);
1696
1696
 
1697
1697
  }
1698
+ */
1698
1699
 
1699
1700
  /** returns the style properties for a given style index */
1700
1701
  public GetStyle(index: number): CellStyle {
@@ -130,13 +130,27 @@
130
130
  <button data-command="font-scale" data-scale="0.9">0.90</button>
131
131
  <button data-command="font-scale" data-scale="1.0">1.00</button>
132
132
  <button data-command="font-scale" data-scale="1.1">1.10</button>
133
- <button data-command="font-scale" data-scale="1.2">1.20</button>
133
+ <button data-command="font-scale" data-scale="1.25">1.25</button>
134
134
  <button data-command="font-scale" data-scale="1.5">1.50</button>
135
135
  <button data-command="font-scale" data-scale="2.0">2.00</button>
136
136
  </div>
137
137
  </div>
138
138
  </div>
139
139
 
140
+ <div composite font-stack>
141
+ <button class="treb-font-stack" data-command="font-stack" data-font-stack="" title="Font stack"></button>
142
+ <div class="treb-menu">
143
+ <button dropdown title="Font stack options"></button>
144
+ <div>
145
+ <button data-command="font-stack" data-font-stack="default"></button>
146
+ <button data-command="font-stack" data-font-stack="transitional"></button>
147
+ <button data-command="font-stack" data-font-stack="monospace"></button>
148
+ <button data-command="font-stack" data-font-stack="handwritten"></button>
149
+ <button data-command="font-stack" data-font-stack="ui"></button>
150
+ </div>
151
+ </div>
152
+ </div>
153
+
140
154
  <div composite>
141
155
  <input class="treb-number-format" title="Number format">
142
156
  <div class="treb-menu">
@@ -12,6 +12,7 @@ import { Measurement } from 'treb-utils';
12
12
  import type { ToolbarMessage } from '../toolbar-message';
13
13
 
14
14
  import { DOMContext } from 'treb-base-types';
15
+ import { font_stack_labels, type FontStackType } from 'treb-base-types/src/font-stack';
15
16
 
16
17
  /** with a view towards i18n */
17
18
  const default_titles: Record<string, string> = {
@@ -668,6 +669,24 @@ export class SpreadsheetConstructor<USER_DATA_TYPE = unknown> {
668
669
  }
669
670
  }
670
671
 
672
+ if (this.toolbar_controls.stack) {
673
+ if (state.style?.font_face) {
674
+ if (state.style.font_face.startsWith('stack:')) {
675
+ const stack_name = state.style.font_face.substring(6) as FontStackType;
676
+ this.toolbar_controls.stack.textContent = font_stack_labels[stack_name] || '';
677
+ this.toolbar_controls.stack.dataset.fontStack = stack_name;
678
+ }
679
+ else {
680
+ this.toolbar_controls.stack.textContent = '';
681
+ this.toolbar_controls.stack.dataset.fontStack = '';
682
+ }
683
+ }
684
+ else { // } if (!state.empty) {
685
+ this.toolbar_controls.stack.textContent = font_stack_labels.default || '';
686
+ this.toolbar_controls.stack.dataset.fontStack = 'default';
687
+ }
688
+ }
689
+
671
690
  const format = this.toolbar_controls.format as HTMLInputElement;
672
691
  if (format) {
673
692
  if (state.style?.number_format) {
@@ -914,6 +933,19 @@ export class SpreadsheetConstructor<USER_DATA_TYPE = unknown> {
914
933
  if (!sheet.options.font_scale) {
915
934
  remove.push(toolbar.querySelector('[font-scale]'));
916
935
  }
936
+ if (!sheet.options.font_stack) {
937
+ remove.push(toolbar.querySelector('[font-stack]'));
938
+ }
939
+ else {
940
+ const buttons = toolbar.querySelectorAll(`[font-stack] button[data-font-stack]`) as NodeListOf<HTMLElement>;
941
+ for (const button of buttons) {
942
+ if (button.dataset.fontStack) {
943
+ const label = font_stack_labels[button.dataset.fontStack as FontStackType];
944
+ button.textContent = label || '';
945
+ }
946
+ }
947
+ }
948
+
917
949
  if (!sheet.options.chart_menu) {
918
950
  remove.push(toolbar.querySelector('[chart-menu]'));
919
951
  }
@@ -967,6 +999,8 @@ export class SpreadsheetConstructor<USER_DATA_TYPE = unknown> {
967
999
  'format': 'input.treb-number-format',
968
1000
  'scale': 'input.treb-font-scale',
969
1001
 
1002
+ 'stack': 'button.treb-font-stack',
1003
+
970
1004
  })) {
971
1005
 
972
1006
  const element = toolbar.querySelector(value) as HTMLElement;
@@ -1029,9 +1063,11 @@ export class SpreadsheetConstructor<USER_DATA_TYPE = unknown> {
1029
1063
  color?: Color;
1030
1064
  format?: string;
1031
1065
  scale?: string;
1066
+ font_stack?: string;
1032
1067
  } = {
1033
1068
  format: target.dataset.format,
1034
1069
  scale: target.dataset.scale,
1070
+ font_stack: target.dataset.fontStack,
1035
1071
  };
1036
1072
 
1037
1073
  let command = target?.dataset.command;
@@ -1376,9 +1412,11 @@ export class SpreadsheetConstructor<USER_DATA_TYPE = unknown> {
1376
1412
  this.UpdateSelectionStyle(sheet, toolbar, comment_box);
1377
1413
  break;
1378
1414
 
1415
+ case 'annotation-selection':
1379
1416
  case 'selection':
1380
1417
  this.UpdateSelectionStyle(sheet, toolbar, comment_box);
1381
1418
  break;
1419
+
1382
1420
  }
1383
1421
  });
1384
1422
 
@@ -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",