@trebco/treb 29.3.4 → 29.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. package/dist/treb-spreadsheet-light.mjs +12 -12
  2. package/dist/treb-spreadsheet.mjs +12 -12
  3. package/dist/treb.d.ts +36 -41
  4. package/package.json +1 -1
  5. package/treb-base-types/src/area.ts +7 -0
  6. package/treb-base-types/src/cell.ts +2 -46
  7. package/treb-base-types/src/cells.ts +14 -8
  8. package/treb-base-types/src/gradient.ts +2 -2
  9. package/treb-base-types/src/import.ts +2 -2
  10. package/treb-base-types/src/style.ts +79 -6
  11. package/treb-base-types/src/theme.ts +24 -15
  12. package/treb-calculator/src/calculator.ts +22 -12
  13. package/treb-calculator/src/dag/graph.ts +12 -3
  14. package/treb-calculator/src/expression-calculator.ts +66 -74
  15. package/treb-calculator/src/functions/base-functions.ts +2 -2
  16. package/treb-calculator/src/functions/sparkline.ts +2 -2
  17. package/treb-calculator/src/functions/statistics-functions.ts +31 -1
  18. package/treb-data-model/src/data-validation.ts +44 -0
  19. package/treb-data-model/src/data_model.ts +11 -7
  20. package/treb-data-model/src/index.ts +1 -1
  21. package/treb-data-model/src/named.ts +35 -10
  22. package/treb-data-model/src/sheet.ts +75 -15
  23. package/treb-data-model/src/sheet_types.ts +4 -0
  24. package/treb-embed/src/custom-element/spreadsheet-constructor.ts +7 -3
  25. package/treb-embed/src/embedded-spreadsheet.ts +50 -28
  26. package/treb-embed/src/progress-dialog.ts +4 -1
  27. package/treb-embed/src/types.ts +9 -0
  28. package/treb-export/src/drawing2/chart2.ts +20 -38
  29. package/treb-export/src/drawing2/drawing2.ts +2 -107
  30. package/treb-export/src/export-worker/export-worker.ts +1 -1
  31. package/treb-export/src/{export2.ts → export.ts} +439 -628
  32. package/treb-export/src/import2.ts +63 -26
  33. package/treb-export/src/workbook-style2.ts +16 -14
  34. package/treb-export/src/workbook2.ts +2 -18
  35. package/treb-export/src/xml-utils.ts +50 -2
  36. package/treb-export/src/zip-wrapper.ts +1 -1
  37. package/treb-grid/src/editors/overlay_editor.ts +3 -3
  38. package/treb-grid/src/layout/base_layout.ts +5 -14
  39. package/treb-grid/src/render/tile_renderer.ts +49 -48
  40. package/treb-grid/src/types/grid.ts +164 -26
  41. package/treb-grid/src/types/grid_base.ts +93 -17
  42. package/treb-grid/src/types/grid_command.ts +2 -1
  43. package/treb-parser/src/parser-types.ts +10 -0
  44. package/treb-parser/src/parser.ts +55 -17
@@ -30,16 +30,16 @@ import type { ParseResult } from 'treb-parser';
30
30
  import { Parser } from 'treb-parser';
31
31
  import type { RangeType, AddressType, HyperlinkType } from './address-type';
32
32
  import { is_range, ShiftRange, InRange, is_address } from './address-type';
33
- import type { ImportedSheetData, AnchoredAnnotation, CellParseResult, AnnotationLayout, Corner as LayoutCorner, ICellAddress, DataValidation, IArea, GradientStop, Color } from 'treb-base-types/src';
34
- import { ValidationType, type SerializedValueType } from 'treb-base-types/src';
33
+ import type { ImportedSheetData, AnchoredAnnotation, CellParseResult, AnnotationLayout, Corner as LayoutCorner, IArea, GradientStop, Color, HTMLColor, ThemeColor } from 'treb-base-types';
34
+ import type { SerializedValueType } from 'treb-base-types';
35
35
  import type { Sheet} from './workbook-sheet2';
36
36
  import { VisibleState } from './workbook-sheet2';
37
37
  import type { CellAnchor } from './drawing2/drawing2';
38
- import { XMLUtils } from './xml-utils';
38
+ import { type DOMContent, XMLUtils } from './xml-utils';
39
39
 
40
40
  // import { one_hundred_pixels } from './constants';
41
41
  import { ColumnWidthToPixels } from './column-width';
42
- import type { AnnotationType } from 'treb-data-model';
42
+ import type { DataValidation, AnnotationType } from 'treb-data-model';
43
43
  import { ZipWrapper } from './zip-wrapper';
44
44
  import type { ConditionalFormat } from 'treb-data-model';
45
45
 
@@ -84,7 +84,7 @@ export class Importer {
84
84
  };
85
85
  v?: string|number|{
86
86
  t$: string;
87
- a$?: any;
87
+ a$?: DOMContent;
88
88
  };
89
89
  f?: string|{
90
90
  t$: string;
@@ -99,7 +99,7 @@ export class Importer {
99
99
  arrays: RangeType[],
100
100
  merges: RangeType[],
101
101
  links: HyperlinkType[],
102
- validations: Array<{ address: ICellAddress, validation: DataValidation }>,
102
+ // validations: Array<{ address: ICellAddress, validation: DataValidation }>,
103
103
  ): CellParseResult | undefined {
104
104
 
105
105
  // must have, at minimum, an address (must be a single cell? FIXME)
@@ -285,12 +285,14 @@ export class Importer {
285
285
  }
286
286
  }
287
287
 
288
+ /*
288
289
  for (const validation of validations) {
289
290
  if (validation.address.row === shifted.row && validation.address.column === shifted.col) {
290
291
  result.validation = validation.validation;
291
292
  break;
292
293
  }
293
294
  }
295
+ */
294
296
 
295
297
  for (const range of merges) {
296
298
  if (InRange(range, shifted)) {
@@ -445,12 +447,12 @@ export class Importer {
445
447
 
446
448
  const color_element = rule.colorScale.color[index];
447
449
  if (color_element.a$.rgb) {
448
- color.text = '#' + color_element.a$.rgb.substring(2);
450
+ (color as HTMLColor).text = '#' + color_element.a$.rgb.substring(2);
449
451
  }
450
452
  else if (color_element.a$.theme) {
451
- color.theme = Number(color_element.a$.theme) || 0;
453
+ (color as ThemeColor).theme = Number(color_element.a$.theme) || 0;
452
454
  if (color_element.a$.tint) {
453
- color.tint = Math.round(color_element.a$.tint * 1000) / 1000;
455
+ (color as ThemeColor).tint = Math.round(color_element.a$.tint * 1000) / 1000;
454
456
  }
455
457
  }
456
458
 
@@ -512,10 +514,15 @@ export class Importer {
512
514
  const conditional_formats: ConditionalFormat[] = [];
513
515
  const links: HyperlinkType[] = [];
514
516
  const row_styles: number[] = []; // may be sparse
517
+
518
+ /*
515
519
  const validations: Array<{
516
520
  address: ICellAddress,
517
521
  validation: DataValidation,
518
522
  }> = [];
523
+ */
524
+ const validations: DataValidation[] = [];
525
+
519
526
  const annotations: AnchoredAnnotation[] = [];
520
527
 
521
528
  const FindAll: (path: string) => any[] = XMLUtils.FindAll.bind(XMLUtils, sheet.sheet_data);
@@ -569,33 +576,68 @@ export class Importer {
569
576
  const formula = entry.formula1;
570
577
 
571
578
  if (ref && formula && type === 'list') {
572
- let address: ICellAddress|undefined;
579
+ // let address: ICellAddress|undefined;
573
580
  let validation: DataValidation|undefined;
574
581
  let parse_result = this.parser.Parse(ref);
582
+ const target: IArea[] = [];
575
583
 
576
584
  // apparently these are encoded as ranges for merged cells...
577
585
 
586
+ // NOTE: actually you can have a range, then validation applies
587
+ // to every cell in the range. also you can have multiple ranges,
588
+ // apparently separated by spaces.
589
+
578
590
  if (parse_result.expression) {
579
591
  if (parse_result.expression.type === 'address') {
580
- address = parse_result.expression;
592
+ // address = parse_result.expression;
593
+ target.push({start: parse_result.expression, end: parse_result.expression});
581
594
  }
582
595
  else if (parse_result.expression.type === 'range') {
583
- address = parse_result.expression.start;
596
+ // address = parse_result.expression.start;
597
+ target.push(parse_result.expression);
584
598
  }
585
599
  }
586
600
 
587
601
  parse_result = this.parser.Parse(formula);
602
+
588
603
  if (parse_result.expression) {
589
604
  if (parse_result.expression.type === 'range') {
590
605
  validation = {
591
- type: ValidationType.Range,
606
+ type: 'range',
592
607
  area: parse_result.expression,
608
+ target,
593
609
  };
594
610
  }
595
611
  else if (parse_result.expression.type === 'literal') {
596
612
  validation = {
597
- type: ValidationType.List,
613
+ type: 'list',
614
+ target,
598
615
  list: parse_result.expression.value.toString().split(/,/).map(value => {
616
+
617
+ // there are no formulas here. value is a string, separated
618
+ // by commas. there is no way to escape a comma (AFAICT; not
619
+ // official, but search). if you did want a comma, you'd need
620
+ // to use a range.
621
+
622
+ // but the uptake is split on commas. after that you can try
623
+ // to check for numbers or bools, but they will be in the string.
624
+
625
+ // I think excel might sort the entries? not sure. don't do it
626
+ // for now.
627
+
628
+ const num = Number(value);
629
+ if (!isNaN(num)) {
630
+ return num;
631
+ }
632
+ if (value.toLowerCase() === 'true') {
633
+ return true;
634
+ }
635
+ if (value.toLowerCase() === 'false') {
636
+ return false;
637
+ }
638
+ return value; // string
639
+
640
+ /*
599
641
  const tmp = this.parser.Parse(value);
600
642
 
601
643
  // if type is "group", that means we saw some spaces. this
@@ -612,13 +654,16 @@ export class Importer {
612
654
  return tmp.expression.name;
613
655
  }
614
656
  return undefined;
657
+ */
658
+
615
659
  }),
616
660
  };
617
661
  }
618
662
  }
619
663
 
620
- if (address && validation) {
621
- validations.push({address, validation});
664
+ if (target.length && validation) {
665
+ // validations.push({address, validation});
666
+ validations.push(validation);
622
667
  }
623
668
 
624
669
  }
@@ -717,20 +762,11 @@ export class Importer {
717
762
  row_heights[row_index - 1] = height;
718
763
  }
719
764
 
720
- /*
721
- if (row.a$?.ht && row.a$?.customHeight) {
722
- const num = Number(row.a$.ht);
723
- if (!isNaN(num)) {
724
- row_heights[row_index - 1] = Math.round(num * 4 / 3); // seems to be the excel unit -> pixel ratio
725
- }
726
- }
727
- */
728
-
729
765
  let cells = row.c || [];
730
766
  if (!Array.isArray(cells)) { cells = [cells]; }
731
767
 
732
768
  for (const element of cells) {
733
- const cell = this.ParseCell(sheet, element, shared_formulae, arrays, merges, links, validations);
769
+ const cell = this.ParseCell(sheet, element, shared_formulae, arrays, merges, links); // , validations);
734
770
  if (cell) {
735
771
  data.push(cell);
736
772
  }
@@ -1178,6 +1214,7 @@ export class Importer {
1178
1214
  row_styles,
1179
1215
  annotations,
1180
1216
  conditional_formats,
1217
+ data_validations: validations,
1181
1218
  styles: this.workbook?.style_cache?.CellXfToStyles() || [],
1182
1219
  };
1183
1220
 
@@ -22,7 +22,7 @@
22
22
  // import * as ElementTree from 'elementtree';
23
23
  // import { Element, ElementTree as Tree } from 'elementtree';
24
24
 
25
- import { type CompositeBorderEdge, Style, type CellStyle, type PropertyKeys, type Color } from 'treb-base-types';
25
+ import { type CompositeBorderEdge, Style, type CellStyle, type PropertyKeys, type Color, IsHTMLColor, IsThemeColor, type ThemeColor, type HTMLColor, ThemeColorIndex } from 'treb-base-types';
26
26
  import { Theme } from './workbook-theme2';
27
27
  import { NumberFormatCache } from 'treb-format';
28
28
  import { XMLUtils } from './xml-utils';
@@ -85,12 +85,14 @@ export interface CellXf {
85
85
 
86
86
  }
87
87
 
88
+ /*
88
89
  interface ColorAttributes {
89
90
  indexed?: string;
90
91
  rgb?: string;
91
92
  theme?: string;
92
93
  tint?: string;
93
94
  }
95
+ */
94
96
 
95
97
  export interface BorderEdge {
96
98
  style?: string;
@@ -303,11 +305,11 @@ export class StyleCache {
303
305
  //}
304
306
 
305
307
  if (composite.text) {
306
- if (composite.text.text) {
308
+ if (IsHTMLColor(composite.text)) {
307
309
  font.color_argb = composite.text.text;
308
310
  }
309
- else if (typeof composite.text.theme === 'number') {
310
- font.color_theme = composite.text.theme;
311
+ else if (IsThemeColor(composite.text)) {
312
+ font.color_theme = ThemeColorIndex(composite.text);
311
313
  if (composite.text.tint) {
312
314
  font.color_tint = composite.text.tint;
313
315
  }
@@ -317,11 +319,11 @@ export class StyleCache {
317
319
  const TranslateBorder = (src: CompositeBorderEdge, dest: BorderEdge) => {
318
320
  if (src.width) {
319
321
  dest.style = 'thin';
320
- if (src.color.text) {
322
+ if (IsHTMLColor(src.color)) {
321
323
  dest.rgba =src.color.text;
322
324
  }
323
- else if (typeof src.color.theme === 'number') {
324
- dest.theme = src.color.theme;
325
+ else if (IsThemeColor(src.color)) {
326
+ dest.theme = ThemeColorIndex(src.color);
325
327
  if (src.color.tint) {
326
328
  dest.tint = src.color.tint;
327
329
  }
@@ -440,12 +442,12 @@ export class StyleCache {
440
442
 
441
443
  if (composite.fill) {
442
444
  fill.pattern_type = 'solid';
443
- if (composite.fill.text) {
445
+ if (IsHTMLColor(composite.fill)) {
444
446
  fill.fg_color = { argb: composite.fill.text };
445
447
  options.fill = fill;
446
448
  }
447
- else if (typeof composite.fill.theme === 'number') {
448
- fill.fg_color = { theme: composite.fill.theme };
449
+ else if (IsThemeColor(composite.fill)) {
450
+ fill.fg_color = { theme: ThemeColorIndex(composite.fill) };
449
451
  if (composite.fill.tint) {
450
452
  fill.fg_color.tint = composite.fill.tint;
451
453
  }
@@ -619,7 +621,7 @@ export class StyleCache {
619
621
  };
620
622
 
621
623
  if (fill.fg_color.tint) {
622
- props.fill.tint = Math.round(fill.fg_color.tint * 1000) / 1000;
624
+ (props.fill as ThemeColor).tint = Math.round(fill.fg_color.tint * 1000) / 1000;
623
625
  }
624
626
 
625
627
  /*
@@ -1317,12 +1319,12 @@ export class StyleCache {
1317
1319
  const ParseDXFColor = (element: any) => {
1318
1320
  const color: Color = {};
1319
1321
  if (element.a$.rgb) {
1320
- color.text = '#' + element.a$.rgb.substring(2);
1322
+ (color as HTMLColor).text = '#' + element.a$.rgb.substring(2);
1321
1323
  }
1322
1324
  else if (element.a$.theme) {
1323
- color.theme = Number(element.a$.theme) || 0;
1325
+ (color as ThemeColor).theme = Number(element.a$.theme) || 0;
1324
1326
  if (element.a$.tint) {
1325
- color.tint = Math.round(element.a$.tint * 1000) / 1000;
1327
+ (color as ThemeColor).tint = Math.round(element.a$.tint * 1000) / 1000;
1326
1328
  }
1327
1329
  }
1328
1330
  return color;
@@ -28,33 +28,17 @@ const xmlparser2 = new XMLParser(XMLOptions2);
28
28
 
29
29
  // import * as he from 'he';
30
30
 
31
- //import { Drawing, TwoCellAnchor, CellAnchor } from './drawing/drawing';
32
31
  import type { TwoCellAnchor, CellAnchor } from './drawing2/drawing2';
33
32
 
34
- // import { ImportedSheetData, IArea } from 'treb-base-types/src';
35
33
  import { SharedStrings } from './shared-strings2';
36
34
  import { StyleCache } from './workbook-style2';
37
35
  import { Theme } from './workbook-theme2';
38
36
  import { Sheet, VisibleState } from './workbook-sheet2';
39
37
  import type { RelationshipMap } from './relationship';
40
38
  import { ZipWrapper } from './zip-wrapper';
41
- import type { CellStyle } from 'treb-base-types';
39
+ import type { CellStyle, ThemeColor } from 'treb-base-types';
42
40
  import type { SerializedNamed } from 'treb-data-model';
43
41
 
44
-
45
- /*
46
- const XMLTypeMap = {
47
- 'sheet': 'application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml',
48
- 'theme': 'application/vnd.openxmlformats-officedocument.theme+xml',
49
- 'drawing': 'application/vnd.openxmlformats-officedocument.drawing+xml',
50
- 'chart': 'application/vnd.openxmlformats-officedocument.drawingml.chart+xml',
51
- 'themeOverride': 'application/vnd.openxmlformats-officedocument.themeOverride+xml',
52
- 'ctrlProp': 'application/vnd.ms-excel.controlproperties+xml',
53
- 'style': 'application/vnd.ms-office.chartstyle+xml',
54
- 'colors': 'application/vnd.ms-office.chartcolorstyle+xml',
55
- };
56
- */
57
-
58
42
  export const ConditionalFormatOperators: Record<string, string> = {
59
43
  greaterThan: '>',
60
44
  greaterThanOrEquals: '>=',
@@ -413,7 +397,7 @@ export class Workbook {
413
397
  if (fill['a:schemeClr']['a:lumOff']?.a$?.val) {
414
398
  const num = Number(fill['a:schemeClr']['a:lumOff'].a$.val);
415
399
  if (!isNaN(num)) {
416
- style.fill.tint = num / 1e5;
400
+ (style.fill as ThemeColor).tint = num / 1e5;
417
401
  }
418
402
  }
419
403
  }
@@ -20,14 +20,62 @@
20
20
  */
21
21
 
22
22
  import { Unescape } from './unescape_xml';
23
- import type { X2jOptions } from 'fast-xml-parser';
23
+ import { type X2jOptions, XMLBuilder, type XmlBuilderOptions } from 'fast-xml-parser';
24
24
 
25
25
  export const XMLTagProcessor = (name: string, value: string): string => Unescape(value);
26
26
 
27
27
  export interface DOMContent {
28
- [index: string]: string|DOMContent|string[]|DOMContent[]|number|number[];
28
+ [index: string]: string|DOMContent|string[]|DOMContent[]|number|number[]|undefined;
29
29
  }
30
30
 
31
+ /**
32
+ * not sure why we have to do this, but filter attributes that
33
+ * have value === undefined
34
+ */
35
+ export const ScrubXML = (dom: DOMContent) => {
36
+ if (dom) {
37
+ if (Array.isArray(dom)) {
38
+ for (const entry of dom) {
39
+ ScrubXML(entry);
40
+ }
41
+ }
42
+ else if (typeof dom === 'object') {
43
+ for (const [key, value] of Object.entries(dom)) {
44
+ if (key === 'a$') {
45
+ if (typeof value === 'object') {
46
+ const replacement: DOMContent = {};
47
+ for (const [attr_name, attr_value] of Object.entries(value)) {
48
+ if (attr_value !== undefined) {
49
+ replacement[attr_name] = attr_value;
50
+ }
51
+ }
52
+ dom[key] = replacement;
53
+ }
54
+ }
55
+ else {
56
+ ScrubXML(value as DOMContent);
57
+ }
58
+ }
59
+ }
60
+ }
61
+ return dom;
62
+ };
63
+
64
+ export const PatchXMLBuilder = (options: Partial<XmlBuilderOptions>) => {
65
+
66
+ const builder = new XMLBuilder(options);
67
+ const build = builder.build;
68
+
69
+ builder.build = (arg: DOMContent) => {
70
+ return build.call(builder, ScrubXML(arg)); // get the "this" value right
71
+ }
72
+
73
+ return builder;
74
+
75
+ };
76
+
77
+ //////////////////
78
+
31
79
  export const XMLOptions: Partial<X2jOptions> = {
32
80
  ignoreAttributes: false,
33
81
  attributeNamePrefix: '__',
@@ -58,7 +58,7 @@ export class ZipWrapper {
58
58
  public Set(path: string, text: string) {
59
59
 
60
60
  this.text.set(path, text);
61
- if (!!this.records[path]) {
61
+ if (this.records[path]) {
62
62
  delete this.records[path];
63
63
  }
64
64
  }
@@ -20,7 +20,7 @@
20
20
  */
21
21
 
22
22
  import { Editor, type NodeDescriptor } from './editor';
23
- import { Area, Cell, type CellStyle, type CellValue, Rectangle, Style, type Theme, ThemeColor2 } from 'treb-base-types';
23
+ import { Area, Cell, type CellStyle, type CellValue, Rectangle, Style, type Theme, ResolveThemeColor } from 'treb-base-types';
24
24
  import { DataModel, type ViewModel, type GridSelection } from 'treb-data-model';
25
25
  import { Autocomplete } from './autocomplete';
26
26
  import { UA } from '../util/ua';
@@ -287,8 +287,8 @@ export class OverlayEditor extends Editor<ResetSelectionEvent> {
287
287
  const style: CellStyle = cell.style || {};
288
288
 
289
289
  this.edit_node.style.font = Style.Font(style, this.scale);
290
- this.edit_node.style.color = ThemeColor2(this.theme, style.text, 1);
291
- this.edit_inset.style.backgroundColor = ThemeColor2(this.theme, style.fill, 0);
290
+ this.edit_node.style.color = ResolveThemeColor(this.theme, style.text, 1);
291
+ this.edit_inset.style.backgroundColor = ResolveThemeColor(this.theme, style.fill, 0);
292
292
 
293
293
  // NOTE: now that we dropped support for IE11, we can probably
294
294
  // remove more than one class at the same time.
@@ -24,7 +24,7 @@ import type { DataModel, ViewModel, Annotation } from 'treb-data-model';
24
24
 
25
25
  import type { Tile } from '../types/tile';
26
26
  import type { Theme, Point, Extent, Size, Position, ICellAddress, Table, IArea } from 'treb-base-types';
27
- import { Style, Area, Rectangle, ThemeColor } from 'treb-base-types';
27
+ import { Style, Area, Rectangle, ResolveThemeColor } from 'treb-base-types';
28
28
 
29
29
  import { MouseDrag } from '../types/drag_mask';
30
30
  import type { GridEvent } from '../types/grid_events';
@@ -1279,7 +1279,7 @@ export abstract class BaseLayout {
1279
1279
  this.row_header.style.backgroundColor =
1280
1280
  this.column_header.style.backgroundColor =
1281
1281
  this.corner.style.backgroundColor =
1282
- theme.headers?.fill ? ThemeColor(theme, theme.headers.fill) : '';
1282
+ theme.headers?.fill ? ResolveThemeColor(theme, theme.headers.fill, 0) : '';
1283
1283
 
1284
1284
  // theme.headers?.background || '';
1285
1285
  // theme.header_background_color || ''; // this.theme.header_background;
@@ -1291,23 +1291,12 @@ export abstract class BaseLayout {
1291
1291
 
1292
1292
  for (const row of this.grid_tiles) {
1293
1293
  for (const tile of row) {
1294
- tile.style.backgroundColor = ThemeColor(theme, theme.grid_cell?.fill) || '#fff';
1294
+ tile.style.backgroundColor = ResolveThemeColor(theme, theme.grid_cell?.fill, 0) || '#fff';
1295
1295
  }
1296
1296
  }
1297
1297
 
1298
- /*
1299
- this.tooltip.style.fontFamily = theme.tooltip_font_face || '';
1300
- this.tooltip.style.fontSize = theme.tooltip_font_size ? `${theme.tooltip_font_size}pt` : '';
1301
- this.tooltip.style.backgroundColor = theme.tooltip_background || '';
1302
- this.tooltip.style.borderColor = theme.tooltip_background || ''; // for arrow
1303
- this.tooltip.style.color = theme.tooltip_color || '';
1304
- */
1305
-
1306
1298
  // TODO: dropdown caret
1307
1299
 
1308
- // this.dropdown_list.style.fontFamily = theme.cell_font || '';
1309
- // const font_size = (theme.cell_font_size_value || 10) * this.scale;
1310
- // this.dropdown_list.style.fontSize = (font_size) + (theme.cell_font_size_unit || 'pt');
1311
1300
  this.dropdown_list.style.font = Style.Font(theme.grid_cell || {});
1312
1301
 
1313
1302
  }
@@ -1415,6 +1404,8 @@ export abstract class BaseLayout {
1415
1404
  this.dropdown_list.style.left = `${target_rect.left + 2}px`;
1416
1405
  this.dropdown_list.style.minWidth = `${target_rect.width}px`;
1417
1406
 
1407
+ this.dropdown_list.style.fontSize = (this.scale.toFixed(2) + 'em');
1408
+
1418
1409
  this.dropdown_list.textContent = '';
1419
1410
  for (const value of list) {
1420
1411
  const entry = this.DOM.Div(undefined, this.dropdown_list);