@trebco/treb 28.13.2 → 28.15.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.
- package/dist/treb-spreadsheet-light.mjs +9 -9
- package/dist/treb-spreadsheet.mjs +9 -9
- package/dist/treb.d.ts +19 -3
- package/package.json +1 -1
- package/treb-base-types/src/style.ts +3 -0
- package/treb-calculator/src/expression-calculator.ts +9 -5
- package/treb-calculator/src/functions/base-functions.ts +334 -57
- package/treb-embed/markup/layout.html +15 -10
- package/treb-embed/markup/toolbar.html +5 -5
- package/treb-embed/src/custom-element/spreadsheet-constructor.ts +38 -2
- package/treb-embed/src/embedded-spreadsheet.ts +66 -2
- package/treb-embed/src/options.ts +5 -0
- package/treb-embed/style/formula-bar.scss +20 -7
- package/treb-embed/style/theme-defaults.scss +20 -0
- package/treb-export/src/export2.ts +6 -1
- package/treb-export/src/import2.ts +67 -3
- package/treb-export/src/shared-strings2.ts +1 -1
- package/treb-export/src/workbook-style2.ts +11 -38
- package/treb-export/src/workbook2.ts +119 -1
- package/treb-grid/src/editors/formula_bar.ts +23 -1
- package/treb-grid/src/render/tile_renderer.ts +46 -3
- package/treb-grid/src/types/annotation.ts +17 -3
- package/treb-grid/src/types/grid.ts +8 -0
- package/treb-grid/src/types/grid_options.ts +3 -2
- package/treb-grid/src/types/named_range.ts +8 -1
package/dist/treb.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/*! API v28.
|
|
1
|
+
/*! API v28.15. Copyright 2018-2024 trebco, llc. All rights reserved. LGPL: https://treb.app/license */
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* add our tag to the map
|
|
@@ -1077,7 +1077,7 @@ export interface FreezePane {
|
|
|
1077
1077
|
rows: number;
|
|
1078
1078
|
columns: number;
|
|
1079
1079
|
}
|
|
1080
|
-
export type AnnotationType = 'treb-chart' | 'image' | 'external';
|
|
1080
|
+
export type AnnotationType = 'treb-chart' | 'image' | 'textbox' | 'external';
|
|
1081
1081
|
export declare type BorderConstants = "none" | "all" | "outside" | "top" | "bottom" | "left" | "right";
|
|
1082
1082
|
|
|
1083
1083
|
/**
|
|
@@ -1274,6 +1274,9 @@ export interface CellStyle {
|
|
|
1274
1274
|
/** border color */
|
|
1275
1275
|
border_bottom_fill?: Color;
|
|
1276
1276
|
|
|
1277
|
+
/** text indent */
|
|
1278
|
+
indent?: number;
|
|
1279
|
+
|
|
1277
1280
|
/**
|
|
1278
1281
|
* cell is locked for editing
|
|
1279
1282
|
*/
|
|
@@ -1805,7 +1808,7 @@ export interface SerializedGridSelection {
|
|
|
1805
1808
|
/** for cacheing addtional selections. optimally don't serialize */
|
|
1806
1809
|
rendered?: boolean;
|
|
1807
1810
|
}
|
|
1808
|
-
export type AnnotationData = AnnotationChartData | AnnotationImageData | AnnotationExternalData;
|
|
1811
|
+
export type AnnotationData = AnnotationChartData | AnnotationImageData | AnnotationExternalData | AnnotationTextBoxData;
|
|
1809
1812
|
export interface ImageSize {
|
|
1810
1813
|
width: number;
|
|
1811
1814
|
height: number;
|
|
@@ -1888,6 +1891,19 @@ export interface AnnotationImageData extends AnnotationDataBase {
|
|
|
1888
1891
|
export interface AnnotationChartData extends AnnotationDataBase {
|
|
1889
1892
|
type: 'treb-chart';
|
|
1890
1893
|
}
|
|
1894
|
+
export interface AnnotationTextBoxData extends AnnotationDataBase {
|
|
1895
|
+
type: 'textbox';
|
|
1896
|
+
data: {
|
|
1897
|
+
style?: CellStyle;
|
|
1898
|
+
paragraphs: {
|
|
1899
|
+
style?: CellStyle;
|
|
1900
|
+
content: {
|
|
1901
|
+
text: string;
|
|
1902
|
+
style?: CellStyle;
|
|
1903
|
+
}[];
|
|
1904
|
+
}[];
|
|
1905
|
+
};
|
|
1906
|
+
}
|
|
1891
1907
|
export interface AnnotationExternalData extends AnnotationDataBase {
|
|
1892
1908
|
type: 'external';
|
|
1893
1909
|
data: Record<string, string>;
|
package/package.json
CHANGED
|
@@ -806,15 +806,11 @@ export class ExpressionCalculator {
|
|
|
806
806
|
|
|
807
807
|
}
|
|
808
808
|
|
|
809
|
-
//protected ElementwiseBinaryExpression(fn: Primitives.PrimitiveBinaryExpression, left: UnionValue[][], right: UnionValue[][]): UnionValue[][] {
|
|
810
809
|
protected ElementwiseBinaryExpression(fn: Primitives.PrimitiveBinaryExpression, left: ArrayUnion, right: ArrayUnion): ArrayUnion {
|
|
811
810
|
|
|
812
811
|
const columns = Math.max(left.value.length, right.value.length);
|
|
813
812
|
const rows = Math.max(left.value[0].length, right.value[0].length);
|
|
814
813
|
|
|
815
|
-
// const columns = Math.max(left.length, right.length);
|
|
816
|
-
// const rows = Math.max(left[0].length, right[0].length);
|
|
817
|
-
|
|
818
814
|
const left_values = this.RecycleArray(left.value, columns, rows);
|
|
819
815
|
const right_values = this.RecycleArray(right.value, columns, rows);
|
|
820
816
|
|
|
@@ -822,8 +818,16 @@ export class ExpressionCalculator {
|
|
|
822
818
|
|
|
823
819
|
for (let c = 0; c < columns; c++) {
|
|
824
820
|
const col: UnionValue[] = [];
|
|
821
|
+
|
|
825
822
|
for (let r = 0; r < rows; r++ ) {
|
|
826
|
-
|
|
823
|
+
|
|
824
|
+
// handle undefineds. this is unfortunate. shouldn't the recycle
|
|
825
|
+
// function do that? ...CHECK/TODO/FIXME
|
|
826
|
+
|
|
827
|
+
col[r] = fn(
|
|
828
|
+
left_values[c][r] || { type: ValueType.undefined },
|
|
829
|
+
right_values[c][r] || { type: ValueType.undefined });
|
|
830
|
+
|
|
827
831
|
}
|
|
828
832
|
value.push(col);
|
|
829
833
|
}
|
|
@@ -88,6 +88,98 @@ const inverse_normal = (q: number): number => {
|
|
|
88
88
|
|
|
89
89
|
};
|
|
90
90
|
|
|
91
|
+
const zlookup_arguments = [
|
|
92
|
+
{
|
|
93
|
+
name: "Lookup value",
|
|
94
|
+
},
|
|
95
|
+
{
|
|
96
|
+
name: "Table",
|
|
97
|
+
},
|
|
98
|
+
{
|
|
99
|
+
name: "Result index",
|
|
100
|
+
},
|
|
101
|
+
{
|
|
102
|
+
name: "Inexact",
|
|
103
|
+
default: true,
|
|
104
|
+
},
|
|
105
|
+
];
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* unified VLOOKUP/HLOOKUP. ordinarily we'd call it XLOOKUP but that's taken.
|
|
109
|
+
* FIXME: can't use use that function for this?
|
|
110
|
+
*/
|
|
111
|
+
const ZLookup = (value: any, table: any[][], col: number, inexact = true, transpose = false): UnionValue => {
|
|
112
|
+
|
|
113
|
+
if (transpose) {
|
|
114
|
+
table = Utils.TransposeArray(table);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
col = Math.max(0, col - 1);
|
|
118
|
+
|
|
119
|
+
// inexact is the default. this assumes that the data is sorted,
|
|
120
|
+
// either numerically or alphabetically. it returns the closest
|
|
121
|
+
// value without going over -- meaning walk the list, and when
|
|
122
|
+
// you're over return the _previous_ item. except if there's an
|
|
123
|
+
// exact match, I guess, in that case return the exact match.
|
|
124
|
+
|
|
125
|
+
// FIXME: there's a hint in the docs for XLOOKUP that this might
|
|
126
|
+
// be using a binary search. not sure why, but that might be
|
|
127
|
+
// correct.
|
|
128
|
+
|
|
129
|
+
if (inexact) {
|
|
130
|
+
|
|
131
|
+
let result: any = table[col][0];
|
|
132
|
+
|
|
133
|
+
if (typeof value === 'number') {
|
|
134
|
+
|
|
135
|
+
let compare = Number(table[0][0]);
|
|
136
|
+
if (isNaN(compare) || compare > value) {
|
|
137
|
+
return NAError();
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
for (let i = 1; i < table[0].length; i++) {
|
|
141
|
+
compare = Number(table[0][i]);
|
|
142
|
+
if (isNaN(compare) || compare > value) {
|
|
143
|
+
break;
|
|
144
|
+
}
|
|
145
|
+
result = table[col][i];
|
|
146
|
+
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
}
|
|
150
|
+
else {
|
|
151
|
+
|
|
152
|
+
value = value.toLowerCase(); // ?
|
|
153
|
+
let compare: string = (table[0][0] || '').toString().toLowerCase();
|
|
154
|
+
if (compare.localeCompare(value) > 0) {
|
|
155
|
+
return NAError();
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
for (let i = 1; i < table[0].length; i++) {
|
|
159
|
+
compare = (table[0][i] || '').toString().toLowerCase();
|
|
160
|
+
if (compare.localeCompare(value) > 0) {
|
|
161
|
+
break;
|
|
162
|
+
}
|
|
163
|
+
result = table[col][i];
|
|
164
|
+
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
return Box(result);
|
|
170
|
+
|
|
171
|
+
}
|
|
172
|
+
else {
|
|
173
|
+
for (let i = 0; i < table[0].length; i++) {
|
|
174
|
+
if (table[0][i] == value) { // ==
|
|
175
|
+
return Box(table[col][i]);
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
return NAError();
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
};
|
|
182
|
+
|
|
91
183
|
/**
|
|
92
184
|
* alternate functions. these are used (atm) only for changing complex
|
|
93
185
|
* behavior.
|
|
@@ -803,90 +895,275 @@ export const BaseFunctionLibrary: FunctionMap = {
|
|
|
803
895
|
},
|
|
804
896
|
*/
|
|
805
897
|
|
|
806
|
-
|
|
807
|
-
*
|
|
808
|
-
*
|
|
898
|
+
/*
|
|
899
|
+
* unsaid anywhere (that I can locate) aboud XLOOKUP is that lookup
|
|
900
|
+
* array must be one-dimensional. it can be either a row or a column,
|
|
901
|
+
* but one dimension must be one. that simplifies things quite a bit.
|
|
902
|
+
*
|
|
903
|
+
* there's a note in the docs about binary search over the data --
|
|
904
|
+
* that might explain how inexact VLOOKUP works as well. seems an odd
|
|
905
|
+
* choice but maybe back in the day it made sense
|
|
809
906
|
*/
|
|
810
|
-
|
|
811
|
-
|
|
907
|
+
XLOOKUP: {
|
|
812
908
|
arguments: [
|
|
813
|
-
{
|
|
814
|
-
|
|
815
|
-
},
|
|
816
|
-
{
|
|
817
|
-
|
|
818
|
-
},
|
|
819
|
-
{
|
|
820
|
-
name: "Result index",
|
|
821
|
-
},
|
|
822
|
-
{
|
|
823
|
-
name: "Inexact",
|
|
824
|
-
default: true,
|
|
825
|
-
},
|
|
909
|
+
{ name: 'Lookup value', },
|
|
910
|
+
{ name: 'Lookup array', },
|
|
911
|
+
{ name: 'Return array', },
|
|
912
|
+
{ name: 'Not found', boxed: true },
|
|
913
|
+
{ name: 'Match mode', default: 0, },
|
|
914
|
+
{ name: 'Search mode', default: 1, },
|
|
826
915
|
],
|
|
916
|
+
xlfn: true,
|
|
917
|
+
fn: (
|
|
918
|
+
lookup_value: any,
|
|
919
|
+
lookup_array: any[][],
|
|
920
|
+
return_array: any[][],
|
|
921
|
+
not_found?: UnionValue,
|
|
922
|
+
match_mode = 0,
|
|
923
|
+
search_mode = 1,
|
|
924
|
+
): UnionValue => {
|
|
925
|
+
|
|
926
|
+
// FIXME: we could I suppose be more graceful about single values
|
|
927
|
+
// if passed instead of arrays
|
|
928
|
+
|
|
929
|
+
if (!Array.isArray(lookup_array)) {
|
|
930
|
+
console.info("lookup is not an array");
|
|
931
|
+
return ValueError();
|
|
932
|
+
}
|
|
827
933
|
|
|
828
|
-
|
|
934
|
+
const first = lookup_array[0];
|
|
935
|
+
if (!Array.isArray(first)) {
|
|
936
|
+
console.info("lookip is not a 2d array");
|
|
937
|
+
return ValueError();
|
|
938
|
+
}
|
|
829
939
|
|
|
830
|
-
|
|
940
|
+
if (lookup_array.length !== 1 && first.length !== 1) {
|
|
941
|
+
console.info("lookup array has invalid dimensions");
|
|
942
|
+
return ValueError();
|
|
943
|
+
}
|
|
831
944
|
|
|
832
|
-
//
|
|
833
|
-
//
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
945
|
+
// FIXME: is it required that the return array be (at least) the
|
|
946
|
+
// same size? we can return undefineds, but maybe we should error
|
|
947
|
+
|
|
948
|
+
if (!Array.isArray(return_array)) {
|
|
949
|
+
console.info("return array is not an array");
|
|
950
|
+
return ValueError();
|
|
951
|
+
}
|
|
839
952
|
|
|
840
|
-
|
|
953
|
+
let transpose = (lookup_array.length === 1);
|
|
954
|
+
if (transpose) {
|
|
955
|
+
lookup_array = Utils.TransposeArray(lookup_array);
|
|
956
|
+
return_array = Utils.TransposeArray(return_array);
|
|
957
|
+
}
|
|
841
958
|
|
|
842
|
-
|
|
959
|
+
// maybe reverse...
|
|
843
960
|
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
961
|
+
if (search_mode < 0) {
|
|
962
|
+
lookup_array.reverse();
|
|
963
|
+
return_array.reverse();
|
|
964
|
+
}
|
|
965
|
+
|
|
966
|
+
//
|
|
967
|
+
// return value at index, transpose if necessary, and return
|
|
968
|
+
// an array. we might prefer to return a scalar if there's only
|
|
969
|
+
// one value, not sure what's the intended behavior
|
|
970
|
+
//
|
|
971
|
+
const ReturnIndex = (index: number): UnionValue => {
|
|
848
972
|
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
973
|
+
const values = return_array[index];
|
|
974
|
+
|
|
975
|
+
if (!values) {
|
|
976
|
+
return { type: ValueType.undefined };
|
|
977
|
+
}
|
|
978
|
+
|
|
979
|
+
if (!Array.isArray(values)) {
|
|
980
|
+
return Box(values);
|
|
981
|
+
}
|
|
982
|
+
|
|
983
|
+
let boxes = [values.map(value => Box(value))];
|
|
984
|
+
|
|
985
|
+
if (transpose) {
|
|
986
|
+
boxes = Utils.TransposeArray(boxes);
|
|
987
|
+
}
|
|
988
|
+
|
|
989
|
+
return {
|
|
990
|
+
type: ValueType.array,
|
|
991
|
+
value: boxes,
|
|
992
|
+
}
|
|
993
|
+
|
|
994
|
+
};
|
|
995
|
+
|
|
996
|
+
// if value is not a string, then we can ignore wildcards.
|
|
997
|
+
// in that case convert to exact match.
|
|
998
|
+
|
|
999
|
+
if (match_mode === 2 && typeof lookup_value !== 'string') {
|
|
1000
|
+
match_mode = 0;
|
|
1001
|
+
}
|
|
1002
|
+
|
|
1003
|
+
// what does inexact matching mean in this case if the lookup
|
|
1004
|
+
// value is a string or boolean? (...)
|
|
1005
|
+
|
|
1006
|
+
if ((match_mode === 1 || match_mode === -1) && typeof lookup_value === 'number') {
|
|
1007
|
+
|
|
1008
|
+
let min_delta = 0;
|
|
1009
|
+
let index = -1;
|
|
1010
|
+
|
|
1011
|
+
for (let i = 0; i < lookup_array.length; i++) {
|
|
1012
|
+
const value = lookup_array[i][0];
|
|
1013
|
+
|
|
1014
|
+
|
|
1015
|
+
if (typeof value === 'number') {
|
|
1016
|
+
|
|
1017
|
+
// check for exact match first, just in case
|
|
1018
|
+
if (value === lookup_value) {
|
|
1019
|
+
return ReturnIndex(i);
|
|
1020
|
+
}
|
|
1021
|
+
|
|
1022
|
+
const delta = Math.abs(value - lookup_value);
|
|
1023
|
+
|
|
1024
|
+
if ((match_mode === 1 && value > lookup_value) || (match_mode === -1 && value < lookup_value)){
|
|
1025
|
+
if (index < 0 || delta < min_delta) {
|
|
1026
|
+
min_delta = delta;
|
|
1027
|
+
index = i;
|
|
1028
|
+
}
|
|
853
1029
|
}
|
|
854
|
-
result = table[col][i];
|
|
855
1030
|
|
|
856
1031
|
}
|
|
1032
|
+
}
|
|
857
1033
|
|
|
1034
|
+
if (index >= 0) {
|
|
1035
|
+
return ReturnIndex(index);
|
|
858
1036
|
}
|
|
859
|
-
else {
|
|
860
1037
|
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
1038
|
+
}
|
|
1039
|
+
|
|
1040
|
+
switch (match_mode) {
|
|
1041
|
+
|
|
1042
|
+
case 2:
|
|
1043
|
+
{
|
|
1044
|
+
// wildcard string match. we only handle strings for
|
|
1045
|
+
// this case (see above).
|
|
1046
|
+
|
|
1047
|
+
const pattern = Utils.ParseWildcards(lookup_value);
|
|
1048
|
+
const regex = new RegExp('^' + pattern + '$', 'i'); //.exec(lookup_value);
|
|
866
1049
|
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
1050
|
+
for (let i = 0; i < lookup_array.length; i++) {
|
|
1051
|
+
let value = lookup_array[i][0];
|
|
1052
|
+
if (typeof value === 'string' && regex.exec(value)) {
|
|
1053
|
+
return ReturnIndex(i);
|
|
1054
|
+
}
|
|
871
1055
|
}
|
|
872
|
-
result = table[col][i];
|
|
873
1056
|
|
|
874
1057
|
}
|
|
1058
|
+
break;
|
|
875
1059
|
|
|
876
|
-
|
|
1060
|
+
case 0:
|
|
1061
|
+
if (typeof lookup_value === 'string') {
|
|
1062
|
+
lookup_value = lookup_value.toLowerCase();
|
|
1063
|
+
}
|
|
1064
|
+
for (let i = 0; i < lookup_array.length; i++) {
|
|
1065
|
+
let value = lookup_array[i][0];
|
|
1066
|
+
|
|
1067
|
+
if (typeof value === 'string') {
|
|
1068
|
+
value = value.toLowerCase();
|
|
1069
|
+
}
|
|
1070
|
+
if (value === lookup_value) {
|
|
1071
|
+
return ReturnIndex(i);
|
|
1072
|
+
}
|
|
1073
|
+
}
|
|
877
1074
|
|
|
878
|
-
|
|
1075
|
+
break;
|
|
1076
|
+
}
|
|
1077
|
+
|
|
1078
|
+
/*
|
|
1079
|
+
const flat_lookup = Utils.FlattenUnboxed(lookup_array);
|
|
1080
|
+
const flat_return = Utils.FlattenUnboxed(return_array);
|
|
879
1081
|
|
|
1082
|
+
// maybe reverse...
|
|
1083
|
+
|
|
1084
|
+
if (search_mode < 0) {
|
|
1085
|
+
flat_lookup.reverse();
|
|
1086
|
+
flat_return.reverse();
|
|
880
1087
|
}
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
1088
|
+
|
|
1089
|
+
// if value is not a string, then we can ignore wildcards.
|
|
1090
|
+
// in that case convert to exact match.
|
|
1091
|
+
|
|
1092
|
+
if (match_mode === 2 && typeof lookup_value !== 'string') {
|
|
1093
|
+
match_mode = 0;
|
|
1094
|
+
}
|
|
1095
|
+
|
|
1096
|
+
switch (match_mode) {
|
|
1097
|
+
case 2:
|
|
1098
|
+
|
|
1099
|
+
{
|
|
1100
|
+
|
|
1101
|
+
// wildcard string match. we only handle strings
|
|
1102
|
+
// for wildcard matching (handled above).
|
|
1103
|
+
|
|
1104
|
+
const pattern = Utils.ParseWildcards(lookup_value);
|
|
1105
|
+
const regex = new RegExp('^' + pattern + '$', 'i'); //.exec(lookup_value);
|
|
1106
|
+
|
|
1107
|
+
for (let i = 0; i < flat_lookup.length; i++) {
|
|
1108
|
+
let value = flat_lookup[i];
|
|
1109
|
+
if (typeof value === 'string' && regex.exec(value)) {
|
|
1110
|
+
return Box(flat_return[i]);
|
|
1111
|
+
}
|
|
1112
|
+
}
|
|
1113
|
+
|
|
1114
|
+
|
|
885
1115
|
}
|
|
886
|
-
|
|
887
|
-
|
|
1116
|
+
|
|
1117
|
+
break;
|
|
1118
|
+
|
|
1119
|
+
case 0:
|
|
1120
|
+
|
|
1121
|
+
// return exact match or NA/default. in this case
|
|
1122
|
+
// "exact" means icase (but not wildcard)
|
|
1123
|
+
|
|
1124
|
+
if (typeof lookup_value === 'string') {
|
|
1125
|
+
lookup_value = lookup_value.toLowerCase();
|
|
1126
|
+
}
|
|
1127
|
+
for (let i = 0; i < flat_lookup.length; i++) {
|
|
1128
|
+
let value = flat_lookup[i];
|
|
1129
|
+
if (typeof value === 'string') {
|
|
1130
|
+
value = value.toLowerCase();
|
|
1131
|
+
}
|
|
1132
|
+
if (value === lookup_value) {
|
|
1133
|
+
return Box(flat_return[i]);
|
|
1134
|
+
}
|
|
1135
|
+
}
|
|
1136
|
+
|
|
1137
|
+
break;
|
|
888
1138
|
}
|
|
1139
|
+
*/
|
|
1140
|
+
|
|
1141
|
+
// FIXME: if we're expecting to return an array maybe we should
|
|
1142
|
+
// pack it up as an array? if it's not already an array? (...)
|
|
1143
|
+
|
|
1144
|
+
return (not_found && not_found.type !== ValueType.undefined) ? not_found : NAError();
|
|
1145
|
+
|
|
1146
|
+
},
|
|
1147
|
+
},
|
|
889
1148
|
|
|
1149
|
+
/**
|
|
1150
|
+
* copied from HLOOKUP, fix that one first
|
|
1151
|
+
*/
|
|
1152
|
+
HLookup: {
|
|
1153
|
+
arguments: [...zlookup_arguments],
|
|
1154
|
+
fn: (value: any, table: any[][], col: number, inexact = true): UnionValue => {
|
|
1155
|
+
return ZLookup(value, table, col, inexact, true);
|
|
1156
|
+
},
|
|
1157
|
+
},
|
|
1158
|
+
|
|
1159
|
+
/**
|
|
1160
|
+
* FIXME: does not implement inexact matching (what's the algo for
|
|
1161
|
+
* that, anyway? nearest? price is right style? what about ties?)
|
|
1162
|
+
*/
|
|
1163
|
+
VLookup: {
|
|
1164
|
+
arguments: [...zlookup_arguments],
|
|
1165
|
+
fn: (value: any, table: any[][], col: number, inexact = true): UnionValue => {
|
|
1166
|
+
return ZLookup(value, table, col, inexact, false);
|
|
890
1167
|
},
|
|
891
1168
|
},
|
|
892
1169
|
|
|
@@ -38,7 +38,7 @@
|
|
|
38
38
|
<div data-bind='message' class='treb-embed-dialog-message'></div>
|
|
39
39
|
<div data-bind='about' class='treb-embed-dialog-body'></div>
|
|
40
40
|
</div>
|
|
41
|
-
<button type='button' title='
|
|
41
|
+
<button type='button' data-title='close_dialog' data-bind='close' class='treb-close-box'>
|
|
42
42
|
<svg viewBox='0 0 16 16'>
|
|
43
43
|
<path d='M11.854 4.146a.5.5 0 0 1 0 .708l-7 7a.5.5 0 0 1-.708-.708l7-7a.5.5 0 0 1 .708 0z'/>
|
|
44
44
|
<path d='M4.146 4.146a.5.5 0 0 0 0 .708l7 7a.5.5 0 0 0 .708-.708l-7-7a.5.5 0 0 0-.708 0z'/>
|
|
@@ -62,6 +62,9 @@
|
|
|
62
62
|
|
|
63
63
|
<div class="treb-formula-bar notranslate" hidden>
|
|
64
64
|
<div class="treb-address-label"><div></div></div>
|
|
65
|
+
<button class="treb-insert-function-button"
|
|
66
|
+
data-title="insert_function"
|
|
67
|
+
data-conditional="insert-function">𝑓<small>(x)</small></button>
|
|
65
68
|
<div class="treb-editor-container">
|
|
66
69
|
<div contenteditable="true"></div>
|
|
67
70
|
</div>
|
|
@@ -95,7 +98,7 @@
|
|
|
95
98
|
-->
|
|
96
99
|
|
|
97
100
|
<!-- converted to button, more appropriate -->
|
|
98
|
-
<button class="treb-delete-tab" title="
|
|
101
|
+
<button class="treb-delete-tab" data-title="delete_sheet" data-command="delete-tab" data-conditional="delete-tab">
|
|
99
102
|
<svg tabindex="-1" viewbox='0 0 16 16'><path d='M4,4 L12,12 M12,4 L4,12'/></svg>
|
|
100
103
|
</button>
|
|
101
104
|
|
|
@@ -105,7 +108,8 @@
|
|
|
105
108
|
</div>
|
|
106
109
|
|
|
107
110
|
<!-- converted to button, more appropriate -->
|
|
108
|
-
<button class="treb-add-tab" data-command="add-tab" data-conditional="add-tab"
|
|
111
|
+
<button class="treb-add-tab" data-command="add-tab" data-conditional="add-tab"
|
|
112
|
+
data-title="add_sheet">+</button>
|
|
109
113
|
|
|
110
114
|
<!--
|
|
111
115
|
we removed the junk node with "flex grow" to split the layout, in
|
|
@@ -125,7 +129,8 @@
|
|
|
125
129
|
|
|
126
130
|
<div class="treb-revert-indicator"
|
|
127
131
|
data-command="revert-indicator"
|
|
128
|
-
title="
|
|
132
|
+
data-title="document_modified"
|
|
133
|
+
></div>
|
|
129
134
|
|
|
130
135
|
</div> <!-- /treb-view -->
|
|
131
136
|
</template>
|
|
@@ -133,13 +138,13 @@
|
|
|
133
138
|
</div>
|
|
134
139
|
|
|
135
140
|
<div class="treb-layout-sidebar treb-animate">
|
|
136
|
-
<button data-command="recalculate" title="
|
|
137
|
-
<button data-command="toggle-toolbar" data-conditional="toolbar" title="
|
|
138
|
-
<button data-command="export-xlsx" data-conditional="export" title="
|
|
139
|
-
<button data-command="revert" data-conditional="revert" title="
|
|
140
|
-
<button data-command="about" title="
|
|
141
|
+
<button data-command="recalculate" data-title="recalculate"></button>
|
|
142
|
+
<button data-command="toggle-toolbar" data-conditional="toolbar" data-title="toggle_toolbar"></button>
|
|
143
|
+
<button data-command="export-xlsx" data-conditional="export" data-title="export"></button>
|
|
144
|
+
<button data-command="revert" data-conditional="revert" data-title="revert"></button>
|
|
145
|
+
<button data-command="about" data-title="about"></button>
|
|
141
146
|
</div>
|
|
142
147
|
|
|
143
|
-
<button class="treb-toggle-sidebar-button" title="
|
|
148
|
+
<button class="treb-toggle-sidebar-button" data-title="toggle_sidebar"></button>
|
|
144
149
|
|
|
145
150
|
</div>
|
|
@@ -49,12 +49,12 @@
|
|
|
49
49
|
|
|
50
50
|
<div class="group">
|
|
51
51
|
<button data-command="wrap-text" title="Wrap text"></button>
|
|
52
|
-
<button data-command="merge-cells" data-id="merge" data-title="Merge cells" data-active-title="Unmerge cells"></button>
|
|
53
|
-
<button data-command="lock-cells" data-title="Lock cells" data-active-title="Unlock cells"></button>
|
|
54
|
-
<button data-command="freeze-panes" data-title="Freeze panes" data-active-title="Unfreeze panes" freeze-button></button>
|
|
55
|
-
<button data-command="insert-table" data-icon="table" data-title="Insert table" data-active-title="Remove table" table-button></button>
|
|
52
|
+
<button data-command="merge-cells" data-id="merge" data-inactive-title="Merge cells" data-active-title="Unmerge cells"></button>
|
|
53
|
+
<button data-command="lock-cells" data-inactive-title="Lock cells" data-active-title="Unlock cells"></button>
|
|
54
|
+
<button data-command="freeze-panes" data-inactive-title="Freeze panes" data-active-title="Unfreeze panes" freeze-button></button>
|
|
55
|
+
<button data-command="insert-table" data-icon="table" data-inactive-title="Insert table" data-active-title="Remove table" table-button></button>
|
|
56
56
|
<div class="treb-menu">
|
|
57
|
-
<button data-icon="comment" data-title="Comment" data-active-title="Update comment"></button>
|
|
57
|
+
<button data-icon="comment" data-inactive-title="Comment" data-active-title="Update comment"></button>
|
|
58
58
|
<div class="treb-comment-box">
|
|
59
59
|
<textarea></textarea>
|
|
60
60
|
<div>
|