@trebco/treb 30.1.8 → 30.2.10
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/dist/languages/treb-i18n-da.mjs +1 -0
- package/dist/languages/treb-i18n-de.mjs +1 -0
- package/dist/languages/treb-i18n-es.mjs +1 -0
- package/dist/languages/treb-i18n-fr.mjs +1 -0
- package/dist/languages/treb-i18n-it.mjs +1 -0
- package/dist/languages/treb-i18n-nl.mjs +1 -0
- package/dist/languages/treb-i18n-no.mjs +1 -0
- package/dist/languages/treb-i18n-pl.mjs +1 -0
- package/dist/languages/treb-i18n-pt.mjs +1 -0
- package/dist/languages/treb-i18n-sv.mjs +1 -0
- package/dist/treb-spreadsheet-light.mjs +11 -11
- package/dist/treb-spreadsheet.mjs +11 -11
- package/dist/treb.d.ts +15 -1
- package/esbuild-custom-element.mjs +23 -1
- package/esbuild-utils.mjs +1 -0
- package/package.json +1 -1
- package/treb-calculator/src/calculator.ts +41 -0
- package/treb-calculator/src/expression-calculator.ts +40 -32
- package/treb-calculator/src/functions/base-functions.ts +164 -1
- package/treb-calculator/src/functions/statistics-functions.ts +134 -0
- package/treb-calculator/src/functions/text-functions.ts +45 -0
- package/treb-calculator/src/utilities.ts +5 -1
- package/treb-data-model/src/data_model.ts +227 -2
- package/treb-data-model/src/index.ts +1 -0
- package/{treb-embed → treb-data-model}/src/language-model.ts +3 -0
- package/treb-data-model/src/sheet.ts +0 -13
- package/treb-embed/src/embedded-spreadsheet.ts +84 -25
- package/treb-embed/src/options.ts +18 -0
- package/treb-embed/src/plugin.ts +31 -0
- package/treb-grid/src/editors/autocomplete_matcher.ts +8 -1
- package/treb-grid/src/render/tile_renderer.ts +14 -0
- package/treb-grid/src/types/grid.ts +25 -139
- package/treb-parser/src/parser-types.ts +12 -0
- package/treb-parser/src/parser.ts +43 -2
- package/tsproject.json +1 -1
|
@@ -22,12 +22,13 @@
|
|
|
22
22
|
import type { Sheet } from './sheet';
|
|
23
23
|
import { SheetCollection } from './sheet_collection';
|
|
24
24
|
import { type UnitAddress, type UnitStructuredReference, type UnitRange, Parser, QuotedSheetNameRegex, DecimalMarkType, ArgumentSeparatorType } from 'treb-parser';
|
|
25
|
-
import type { IArea, ICellAddress, Table, CellStyle } from 'treb-base-types';
|
|
25
|
+
import type { IArea, ICellAddress, Table, CellStyle, CellValue } from 'treb-base-types';
|
|
26
|
+
import { Is2DArray } from 'treb-base-types';
|
|
26
27
|
import { Area, IsCellAddress, Style } from 'treb-base-types';
|
|
27
28
|
import type { SerializedNamed } from './named';
|
|
28
29
|
import { NamedRangeManager } from './named';
|
|
29
30
|
import type { ConnectedElementType, MacroFunction } from './types';
|
|
30
|
-
|
|
31
|
+
import type { LanguageModel } from './language-model';
|
|
31
32
|
|
|
32
33
|
/**
|
|
33
34
|
*
|
|
@@ -36,6 +37,9 @@ export class DataModel {
|
|
|
36
37
|
|
|
37
38
|
public readonly parser: Parser = new Parser();
|
|
38
39
|
|
|
40
|
+
/** moved from embedded spreadsheet */
|
|
41
|
+
public language_model?: LanguageModel;
|
|
42
|
+
|
|
39
43
|
/** document metadata */
|
|
40
44
|
public document_name?: string;
|
|
41
45
|
|
|
@@ -523,5 +527,226 @@ export class DataModel {
|
|
|
523
527
|
*/
|
|
524
528
|
public connected_elements: Map<number, ConnectedElementType> = new Map();
|
|
525
529
|
|
|
530
|
+
|
|
531
|
+
|
|
532
|
+
// --- moving translation here ----------------------------------------------
|
|
533
|
+
|
|
534
|
+
|
|
535
|
+
/**
|
|
536
|
+
* maps common language (english) -> local language. this should
|
|
537
|
+
* be passed in (actually set via a function).
|
|
538
|
+
*/
|
|
539
|
+
private language_map?: Record<string, string>;
|
|
540
|
+
|
|
541
|
+
/**
|
|
542
|
+
* maps local language -> common (english). this should be constructed
|
|
543
|
+
* when the forward function is passed in, so there's a 1-1 correspondence.
|
|
544
|
+
*/
|
|
545
|
+
private reverse_language_map?: Record<string, string>;
|
|
546
|
+
|
|
547
|
+
|
|
548
|
+
/**
|
|
549
|
+
* set the language translation map. this is a set of function names
|
|
550
|
+
* (in english) -> the local equivalent. both should be in canonical form,
|
|
551
|
+
* as that will be used when we translate one way or the other.
|
|
552
|
+
*/
|
|
553
|
+
public SetLanguageMap(language_map?: Record<string, string>) {
|
|
554
|
+
|
|
555
|
+
if (!language_map) {
|
|
556
|
+
this.language_map = this.reverse_language_map = undefined;
|
|
557
|
+
}
|
|
558
|
+
else {
|
|
559
|
+
|
|
560
|
+
const keys = Object.keys(language_map);
|
|
561
|
+
|
|
562
|
+
// normalize forward
|
|
563
|
+
this.language_map = {};
|
|
564
|
+
for (const key of keys) {
|
|
565
|
+
this.language_map[key.toUpperCase()] = language_map[key];
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
// normalize backward
|
|
569
|
+
this.reverse_language_map = {};
|
|
570
|
+
for (const key of keys) {
|
|
571
|
+
const value = language_map[key];
|
|
572
|
+
this.reverse_language_map[value.toUpperCase()] = key;
|
|
573
|
+
}
|
|
574
|
+
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
/*
|
|
578
|
+
// we might need to update the current displayed selection. depends
|
|
579
|
+
// on when we expect languages to be set.
|
|
580
|
+
|
|
581
|
+
if (!this.primary_selection.empty) {
|
|
582
|
+
this.Select(this.primary_selection, this.primary_selection.area, this.primary_selection.target);
|
|
583
|
+
}
|
|
584
|
+
*/
|
|
585
|
+
|
|
586
|
+
}
|
|
587
|
+
|
|
588
|
+
/**
|
|
589
|
+
* translate function from common (english) -> local language. this could
|
|
590
|
+
* be inlined (assuming it's only called in one place), but we are breaking
|
|
591
|
+
* it out so we can develop/test/manage it.
|
|
592
|
+
*/
|
|
593
|
+
public TranslateFunction(value: string): string {
|
|
594
|
+
if (this.language_map) {
|
|
595
|
+
return this.TranslateInternal(value, this.language_map, this.language_model?.boolean_true, this.language_model?.boolean_false);
|
|
596
|
+
}
|
|
597
|
+
return value;
|
|
598
|
+
}
|
|
599
|
+
|
|
600
|
+
/**
|
|
601
|
+
* translate from local language -> common (english).
|
|
602
|
+
* @see TranslateFunction
|
|
603
|
+
*/
|
|
604
|
+
public UntranslateFunction(value: string): string {
|
|
605
|
+
if (this.reverse_language_map) {
|
|
606
|
+
return this.TranslateInternal(value, this.reverse_language_map, 'TRUE', 'FALSE');
|
|
607
|
+
}
|
|
608
|
+
return value;
|
|
609
|
+
}
|
|
610
|
+
|
|
611
|
+
public UntranslateData(value: CellValue|CellValue[]|CellValue[][]): CellValue|CellValue[]|CellValue[][] {
|
|
612
|
+
|
|
613
|
+
if (Array.isArray(value)) {
|
|
614
|
+
|
|
615
|
+
// could be 1d or 2d. typescript is complaining, not sure why...
|
|
616
|
+
|
|
617
|
+
if (Is2DArray(value)) {
|
|
618
|
+
return value.map(row => row.map(entry => {
|
|
619
|
+
if (entry && typeof entry === 'string' && entry[0] === '=') {
|
|
620
|
+
return this.UntranslateFunction(entry);
|
|
621
|
+
}
|
|
622
|
+
return entry;
|
|
623
|
+
}));
|
|
624
|
+
}
|
|
625
|
+
else {
|
|
626
|
+
return value.map(entry => {
|
|
627
|
+
if (entry && typeof entry === 'string' && entry[0] === '=') {
|
|
628
|
+
return this.UntranslateFunction(entry);
|
|
629
|
+
}
|
|
630
|
+
return entry;
|
|
631
|
+
});
|
|
632
|
+
}
|
|
633
|
+
|
|
634
|
+
}
|
|
635
|
+
else if (value && typeof value === 'string' && value[0] === '=') {
|
|
636
|
+
|
|
637
|
+
// single value
|
|
638
|
+
value = this.UntranslateFunction(value);
|
|
639
|
+
|
|
640
|
+
}
|
|
641
|
+
|
|
642
|
+
return value;
|
|
643
|
+
|
|
644
|
+
}
|
|
645
|
+
|
|
646
|
+
|
|
647
|
+
/**
|
|
648
|
+
* translation back and forth is the same operation, with a different
|
|
649
|
+
* (inverted) map. although it still might be worth inlining depending
|
|
650
|
+
* on cost.
|
|
651
|
+
*
|
|
652
|
+
* FIXME: it's about time we started using proper maps, we dropped
|
|
653
|
+
* support for IE11 some time ago.
|
|
654
|
+
*/
|
|
655
|
+
private TranslateInternal(value: string, map: Record<string, string>, boolean_true?: string, boolean_false?: string): string {
|
|
656
|
+
|
|
657
|
+
const parse_result = this.parser.Parse(value);
|
|
658
|
+
|
|
659
|
+
if (parse_result.expression) {
|
|
660
|
+
|
|
661
|
+
let modified = false;
|
|
662
|
+
this.parser.Walk(parse_result.expression, unit => {
|
|
663
|
+
if (unit.type === 'call') {
|
|
664
|
+
const replacement = map[unit.name.toUpperCase()];
|
|
665
|
+
if (replacement) {
|
|
666
|
+
modified = true;
|
|
667
|
+
unit.name = replacement;
|
|
668
|
+
}
|
|
669
|
+
}
|
|
670
|
+
else if (unit.type === 'literal' && typeof unit.value === 'boolean') {
|
|
671
|
+
|
|
672
|
+
// to/from english/locale depends on the direction, but we're not
|
|
673
|
+
// passing that in? FIXME, pass it as a parameter. it doesn't matter
|
|
674
|
+
// here, but later when we render.
|
|
675
|
+
|
|
676
|
+
modified = true;
|
|
677
|
+
|
|
678
|
+
}
|
|
679
|
+
|
|
680
|
+
return true;
|
|
681
|
+
});
|
|
682
|
+
|
|
683
|
+
if (modified) {
|
|
684
|
+
return '=' + this.parser.Render(parse_result.expression, {
|
|
685
|
+
missing: '', boolean_true, boolean_false,
|
|
686
|
+
});
|
|
687
|
+
}
|
|
688
|
+
}
|
|
689
|
+
|
|
690
|
+
return value;
|
|
691
|
+
|
|
692
|
+
}
|
|
693
|
+
|
|
694
|
+
|
|
695
|
+
/**
|
|
696
|
+
* this is not public _yet_
|
|
697
|
+
*
|
|
698
|
+
* @internal
|
|
699
|
+
*/
|
|
700
|
+
public SetLanguage(model?: LanguageModel): void {
|
|
701
|
+
|
|
702
|
+
this.language_model = model;
|
|
703
|
+
|
|
704
|
+
if (!model) {
|
|
705
|
+
this.SetLanguageMap(); // clear
|
|
706
|
+
|
|
707
|
+
// set defaults for parsing.
|
|
708
|
+
|
|
709
|
+
this.parser.flags.boolean_true = 'TRUE';
|
|
710
|
+
this.parser.flags.boolean_false = 'FALSE';
|
|
711
|
+
|
|
712
|
+
}
|
|
713
|
+
else {
|
|
714
|
+
|
|
715
|
+
// create a name map for grid
|
|
716
|
+
|
|
717
|
+
const map: Record< string, string > = {};
|
|
718
|
+
|
|
719
|
+
if (model.functions) {
|
|
720
|
+
for (const entry of model.functions || []) {
|
|
721
|
+
map[entry.base.toUpperCase()] = entry.name; // toUpperCase because of a data error -- fix at the source
|
|
722
|
+
}
|
|
723
|
+
}
|
|
724
|
+
|
|
725
|
+
this.SetLanguageMap(map);
|
|
726
|
+
|
|
727
|
+
// console.info({map});
|
|
728
|
+
|
|
729
|
+
if (!model.boolean_false) {
|
|
730
|
+
model.boolean_false = map['FALSE'];
|
|
731
|
+
}
|
|
732
|
+
if (!model.boolean_true) {
|
|
733
|
+
model.boolean_true = map['TRUE'];
|
|
734
|
+
}
|
|
735
|
+
|
|
736
|
+
// set defaults for parsing.
|
|
737
|
+
|
|
738
|
+
this.parser.flags.boolean_true = model.boolean_true || 'TRUE';
|
|
739
|
+
this.parser.flags.boolean_false = model.boolean_false || 'FALSE';
|
|
740
|
+
|
|
741
|
+
// console.info("booleans:", this.model.parser.flags.boolean_true, ",", this.model.parser.flags.boolean_false)
|
|
742
|
+
|
|
743
|
+
}
|
|
744
|
+
|
|
745
|
+
for (const sheet of this.sheets.list) {
|
|
746
|
+
sheet.FlushCellStyles();
|
|
747
|
+
}
|
|
748
|
+
|
|
749
|
+
}
|
|
750
|
+
|
|
526
751
|
}
|
|
527
752
|
|
|
@@ -807,19 +807,6 @@ export class Sheet {
|
|
|
807
807
|
}
|
|
808
808
|
}
|
|
809
809
|
|
|
810
|
-
/* *
|
|
811
|
-
* factory method creates a sheet from a 2D array.
|
|
812
|
-
*
|
|
813
|
-
* /
|
|
814
|
-
public static FromArray(data: any[] = [], transpose = false): Sheet {
|
|
815
|
-
const sheet = new Sheet();
|
|
816
|
-
sheet.cells.FromArray(data, transpose);
|
|
817
|
-
|
|
818
|
-
return sheet;
|
|
819
|
-
}
|
|
820
|
-
*/
|
|
821
|
-
|
|
822
|
-
|
|
823
810
|
// --- public methods -------------------------------------------------------
|
|
824
811
|
|
|
825
812
|
public MergeCells(area: Area): void {
|
|
@@ -49,7 +49,10 @@ import type {
|
|
|
49
49
|
CondifionalFormatExpressionOptions,
|
|
50
50
|
ConditionalFormatCellMatchOptions,
|
|
51
51
|
ConditionalFormatCellMatch,
|
|
52
|
-
|
|
52
|
+
|
|
53
|
+
LanguageModel,
|
|
54
|
+
TranslatedFunctionDescriptor,
|
|
55
|
+
|
|
53
56
|
} from 'treb-data-model';
|
|
54
57
|
|
|
55
58
|
|
|
@@ -88,7 +91,6 @@ import { Spinner } from './spinner';
|
|
|
88
91
|
import { type EmbeddedSpreadsheetOptions, DefaultOptions, type ExportOptions } from './options';
|
|
89
92
|
import { type TREBDocument, SaveFileType, LoadSource, type EmbeddedSheetEvent, type InsertTableOptions } from './types';
|
|
90
93
|
|
|
91
|
-
import type { LanguageModel, TranslatedFunctionDescriptor } from './language-model';
|
|
92
94
|
import type { SelectionState } from './selection-state';
|
|
93
95
|
import type { BorderToolbarMessage, ToolbarMessage } from './toolbar-message';
|
|
94
96
|
|
|
@@ -170,7 +172,6 @@ export interface LoadDocumentOptions {
|
|
|
170
172
|
source?: LoadSource,
|
|
171
173
|
}
|
|
172
174
|
|
|
173
|
-
|
|
174
175
|
/**
|
|
175
176
|
* options for the GetRange method
|
|
176
177
|
*/
|
|
@@ -255,6 +256,15 @@ export class EmbeddedSpreadsheet<USER_DATA_TYPE = unknown> {
|
|
|
255
256
|
/** @internal */
|
|
256
257
|
public static treb_embedded_script_path = '';
|
|
257
258
|
|
|
259
|
+
/**
|
|
260
|
+
* @internal
|
|
261
|
+
*
|
|
262
|
+
* keep track of modules we've tried to load dynamically, so we don't
|
|
263
|
+
* do it again. not sure if this is strictly necessary but especially if
|
|
264
|
+
* we're logging I don't want to see it again
|
|
265
|
+
*/
|
|
266
|
+
protected static failed_dynamic_modules: string[] = [];
|
|
267
|
+
|
|
258
268
|
/* * @internal */
|
|
259
269
|
// public static enable_engine = false;
|
|
260
270
|
|
|
@@ -333,8 +343,9 @@ export class EmbeddedSpreadsheet<USER_DATA_TYPE = unknown> {
|
|
|
333
343
|
return Localization;
|
|
334
344
|
}
|
|
335
345
|
|
|
336
|
-
|
|
346
|
+
/* * loaded language model, if any (moved to data model) * /
|
|
337
347
|
protected language_model?: LanguageModel;
|
|
348
|
+
*/
|
|
338
349
|
|
|
339
350
|
/** FIXME: fix type (needs to be extensible) */
|
|
340
351
|
protected events = new EventSource<{ type: string }>();
|
|
@@ -646,8 +657,6 @@ export class EmbeddedSpreadsheet<USER_DATA_TYPE = unknown> {
|
|
|
646
657
|
*/
|
|
647
658
|
constructor(options: EmbeddedSpreadsheetOptions & { model?: EmbeddedSpreadsheet }) {
|
|
648
659
|
|
|
649
|
-
// super();
|
|
650
|
-
|
|
651
660
|
// we renamed this option, default to the new name
|
|
652
661
|
|
|
653
662
|
if (options.storage_key && !options.local_storage) {
|
|
@@ -842,6 +851,24 @@ export class EmbeddedSpreadsheet<USER_DATA_TYPE = unknown> {
|
|
|
842
851
|
this.grid.headless = true; // FIXME: move into grid options
|
|
843
852
|
}
|
|
844
853
|
|
|
854
|
+
// --- testing dynamic loading ---------------------------------------------
|
|
855
|
+
|
|
856
|
+
this.LoadLanguage(options.language);
|
|
857
|
+
|
|
858
|
+
// --- testing plugins -----------------------------------------------------
|
|
859
|
+
|
|
860
|
+
/*
|
|
861
|
+
// FIXME: when to do this? could it wait? should it be async? (...)
|
|
862
|
+
|
|
863
|
+
if (options.plugins) {
|
|
864
|
+
for (const plugin of options.plugins) {
|
|
865
|
+
plugin.Attach(this);
|
|
866
|
+
}
|
|
867
|
+
}
|
|
868
|
+
*/
|
|
869
|
+
|
|
870
|
+
// -------------------------------------------------------------------------
|
|
871
|
+
|
|
845
872
|
// we're now gating this on container to support fully headless operation
|
|
846
873
|
|
|
847
874
|
if (container) {
|
|
@@ -1180,15 +1207,28 @@ export class EmbeddedSpreadsheet<USER_DATA_TYPE = unknown> {
|
|
|
1180
1207
|
|
|
1181
1208
|
let list: FunctionDescriptor[] = this.calculator.SupportedFunctions();
|
|
1182
1209
|
|
|
1183
|
-
if (this.language_model) {
|
|
1210
|
+
if (this.model.language_model?.functions) {
|
|
1184
1211
|
|
|
1185
1212
|
const map: Record<string, TranslatedFunctionDescriptor> = {};
|
|
1186
|
-
for (const entry of this.language_model.functions || []) {
|
|
1213
|
+
for (const entry of this.model.language_model.functions || []) {
|
|
1187
1214
|
map[entry.base.toUpperCase()] = entry;
|
|
1188
1215
|
}
|
|
1189
1216
|
|
|
1190
1217
|
list = list.map(descriptor => {
|
|
1191
|
-
|
|
1218
|
+
const partial = map[descriptor.name.toUpperCase()];
|
|
1219
|
+
|
|
1220
|
+
// FIXME: this is not deep enough if we are going to modify
|
|
1221
|
+
// argument names. we will need to keep other elements of the
|
|
1222
|
+
// argument entries. this is sufficient for now if we're just
|
|
1223
|
+
// setting function names / descriptions.
|
|
1224
|
+
|
|
1225
|
+
if (partial) {
|
|
1226
|
+
return {
|
|
1227
|
+
...descriptor,
|
|
1228
|
+
...partial,
|
|
1229
|
+
};
|
|
1230
|
+
}
|
|
1231
|
+
return descriptor;
|
|
1192
1232
|
});
|
|
1193
1233
|
|
|
1194
1234
|
}
|
|
@@ -1956,29 +1996,48 @@ export class EmbeddedSpreadsheet<USER_DATA_TYPE = unknown> {
|
|
|
1956
1996
|
|
|
1957
1997
|
// --- public API methods ----------------------------------------------------
|
|
1958
1998
|
|
|
1959
|
-
/**
|
|
1960
|
-
|
|
1961
|
-
*
|
|
1962
|
-
* @internal
|
|
1963
|
-
*/
|
|
1964
|
-
public SetLanguage(model?: LanguageModel): void {
|
|
1999
|
+
/** dynamically load language module */
|
|
2000
|
+
public async LoadLanguage(language = '') {
|
|
1965
2001
|
|
|
1966
|
-
|
|
1967
|
-
|
|
1968
|
-
|
|
1969
|
-
|
|
2002
|
+
if (!language || language === 'locale') {
|
|
2003
|
+
const locale = Localization.locale || '';
|
|
2004
|
+
const parts = locale.split(/-/).map(part => part.toLowerCase());
|
|
2005
|
+
language = parts[0];
|
|
1970
2006
|
}
|
|
1971
|
-
else {
|
|
1972
2007
|
|
|
1973
|
-
|
|
2008
|
+
language = language.toLowerCase();
|
|
2009
|
+
|
|
2010
|
+
let mod: { LanguageMap: LanguageModel } | undefined;
|
|
2011
|
+
|
|
2012
|
+
if (language && language !== 'en') {
|
|
2013
|
+
|
|
2014
|
+
// FIXME: even though we now have a dynamic import
|
|
2015
|
+
// working, we still probably want to use a filter
|
|
2016
|
+
// list to avoid unnecessary 404s.
|
|
1974
2017
|
|
|
1975
|
-
|
|
1976
|
-
|
|
1977
|
-
|
|
2018
|
+
// regarding the import, this is specially crafted to
|
|
2019
|
+
// work in both esbuild and vite. probably will break
|
|
2020
|
+
// some other bundlers though -- might need some magic
|
|
2021
|
+
// comments
|
|
2022
|
+
|
|
2023
|
+
try {
|
|
2024
|
+
mod = await import(`esbuild-ignore-import:./languages/treb-i18n-${language}.mjs`);
|
|
2025
|
+
}
|
|
2026
|
+
catch (err) {
|
|
2027
|
+
console.error(err);
|
|
1978
2028
|
}
|
|
1979
|
-
|
|
2029
|
+
|
|
1980
2030
|
}
|
|
1981
2031
|
|
|
2032
|
+
if (mod) {
|
|
2033
|
+
this.model.SetLanguage(mod.LanguageMap);
|
|
2034
|
+
}
|
|
2035
|
+
else {
|
|
2036
|
+
this.model.SetLanguage();
|
|
2037
|
+
}
|
|
2038
|
+
|
|
2039
|
+
this.grid.Reselect();
|
|
2040
|
+
this.grid.Update(true);
|
|
1982
2041
|
this.UpdateAC();
|
|
1983
2042
|
|
|
1984
2043
|
}
|
|
@@ -22,6 +22,7 @@
|
|
|
22
22
|
import type { ICellAddress } from 'treb-base-types';
|
|
23
23
|
import type { TREBDocument } from './types';
|
|
24
24
|
import type { ChartRenderer } from 'treb-charts';
|
|
25
|
+
// import type { TREBPlugin } from './plugin';
|
|
25
26
|
|
|
26
27
|
/**
|
|
27
28
|
* factory type for chart renderer, if you want instances (pass a constructor)
|
|
@@ -327,6 +328,23 @@ export interface EmbeddedSpreadsheetOptions {
|
|
|
327
328
|
*/
|
|
328
329
|
spill?: boolean;
|
|
329
330
|
|
|
331
|
+
/**
|
|
332
|
+
* language. at the moment this controls spreadsheet function names
|
|
333
|
+
* only; the plan is to expand to the rest of the interface over time.
|
|
334
|
+
* should be an ISO 639-1 language code, like "en", "fr" or "sv" (case
|
|
335
|
+
* insensitive). we only support a limited subset of languages at the
|
|
336
|
+
* moment.
|
|
337
|
+
*
|
|
338
|
+
* leave blank or set to "locale" to use the current locale.
|
|
339
|
+
*/
|
|
340
|
+
language?: string;
|
|
341
|
+
|
|
342
|
+
/* *
|
|
343
|
+
* @internal
|
|
344
|
+
* testing plugins
|
|
345
|
+
*/
|
|
346
|
+
// plugins?: TREBPlugin[];
|
|
347
|
+
|
|
330
348
|
}
|
|
331
349
|
|
|
332
350
|
/**
|
|
@@ -0,0 +1,31 @@
|
|
|
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 type { EmbeddedSpreadsheet } from './embedded-spreadsheet';
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* @internal
|
|
26
|
+
*
|
|
27
|
+
* testing plugins
|
|
28
|
+
*/
|
|
29
|
+
export interface TREBPlugin {
|
|
30
|
+
Attach: (instance: EmbeddedSpreadsheet) => void;
|
|
31
|
+
}
|
|
@@ -151,6 +151,8 @@ export class AutocompleteMatcher {
|
|
|
151
151
|
return {};
|
|
152
152
|
}
|
|
153
153
|
|
|
154
|
+
// console.info(data);
|
|
155
|
+
|
|
154
156
|
let match;
|
|
155
157
|
let result: AutocompleteExecResult = {};
|
|
156
158
|
|
|
@@ -162,7 +164,8 @@ export class AutocompleteMatcher {
|
|
|
162
164
|
// if it's a token, and ends with a legal character
|
|
163
165
|
// UPDATE: adding the negative leading \d to fix entering complex numbers
|
|
164
166
|
|
|
165
|
-
match = data.text.match(/(?:^|[^A-Za-z_\d])([A-Za-z_][\w\d_.]*)\s*$/);
|
|
167
|
+
// match = data.text.match(/(?:^|[^A-Za-z_\d])([A-Za-z_][\w\d_.]*)\s*$/);
|
|
168
|
+
match = data.text.match(/(?:^|[^a-zA-Z\u00C0-\u024F_\d])([a-zA-Z\u00C0-\u024F_][\w\d\u00C0-\u024F_.]*)\s*$/);
|
|
166
169
|
|
|
167
170
|
if (match) {
|
|
168
171
|
const token = match[1];
|
|
@@ -178,6 +181,9 @@ export class AutocompleteMatcher {
|
|
|
178
181
|
};
|
|
179
182
|
|
|
180
183
|
}
|
|
184
|
+
else {
|
|
185
|
+
// console.info("NOP");
|
|
186
|
+
}
|
|
181
187
|
|
|
182
188
|
}
|
|
183
189
|
|
|
@@ -261,6 +267,7 @@ export class AutocompleteMatcher {
|
|
|
261
267
|
if ( (char >= 0x61 && char <= 0x7a) // a-z
|
|
262
268
|
|| (char >= 0x41 && char <= 0x5a) // A-Z
|
|
263
269
|
|| (char >= 0x30 && char <= 0x39) // 0-9
|
|
270
|
+
|| (char >= 0x00C0 && char <= 0x024F) // accented characters
|
|
264
271
|
|| (char === 0x5f) // _
|
|
265
272
|
|| (char === 0x2e)) { // .
|
|
266
273
|
|
|
@@ -727,6 +727,20 @@ export class TileRenderer {
|
|
|
727
727
|
let pad_entry: RenderTextPart | undefined;
|
|
728
728
|
let composite_width = 0;
|
|
729
729
|
|
|
730
|
+
// -------------------------------------------------------------------------
|
|
731
|
+
|
|
732
|
+
// moved translated boolean formatting here. I don't like this. it
|
|
733
|
+
// should be in sheet (at least that's where the rest of formatting
|
|
734
|
+
// is), but sheet doesn't have a reference to data model (and because
|
|
735
|
+
// sheets are owned by data model, I don't want to add a circular ref).
|
|
736
|
+
|
|
737
|
+
if (cell.rendered_type === ValueType.boolean) {
|
|
738
|
+
const value = cell.calculated_type ? cell.calculated : cell.value;
|
|
739
|
+
cell.formatted = value ? (this.model.language_model?.boolean_true || 'TRUE') : (this.model.language_model?.boolean_false || 'FALSE');
|
|
740
|
+
}
|
|
741
|
+
|
|
742
|
+
// -------------------------------------------------------------------------
|
|
743
|
+
|
|
730
744
|
let override_formatting: string | undefined;
|
|
731
745
|
let formatted = cell.editing ? '' : cell.formatted; // <-- empty on editing, to remove overflows
|
|
732
746
|
|