@trebco/treb 32.10.0 → 32.13.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.
- package/dist/treb-export-worker.mjs +1 -1
- package/dist/treb-spreadsheet.mjs +11 -11
- package/dist/treb.d.ts +1 -1
- package/package.json +1 -1
- package/treb-calculator/src/functions/base-functions.ts +263 -13
- package/treb-calculator/src/functions/fp.ts +192 -2
- package/treb-grid/src/types/scale-control.ts +5 -1
package/dist/treb.d.ts
CHANGED
package/package.json
CHANGED
|
@@ -234,6 +234,79 @@ const TrigFunction = (real: (value: number) => number, complex: (value: Complex)
|
|
|
234
234
|
};
|
|
235
235
|
}
|
|
236
236
|
|
|
237
|
+
/**
|
|
238
|
+
* helper for sorting. kind of weird rules
|
|
239
|
+
*/
|
|
240
|
+
const SortHelper = (order: number, av: CellValue, bv: CellValue) => {
|
|
241
|
+
|
|
242
|
+
// OK from what I can tell the rules are
|
|
243
|
+
//
|
|
244
|
+
// (1) no type coercion (but see below for blanks)
|
|
245
|
+
// (2) strings are sorted case-insensitive
|
|
246
|
+
// (3) blank values are coerced -> 0 but these are not sorted as numbers
|
|
247
|
+
// (4) order is numbers, strings, booleans, then blanks
|
|
248
|
+
//
|
|
249
|
+
// some of which makes sense, I guess...
|
|
250
|
+
//
|
|
251
|
+
// blanks are always sorted last, irrespective of sort order. which
|
|
252
|
+
// means we can't sort and then reverse, because reverse sort is not
|
|
253
|
+
// the inverse of forward sort
|
|
254
|
+
|
|
255
|
+
// special case
|
|
256
|
+
|
|
257
|
+
if (av === bv) {
|
|
258
|
+
return 0;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
// special case
|
|
262
|
+
|
|
263
|
+
if (av === undefined) {
|
|
264
|
+
return 1;
|
|
265
|
+
}
|
|
266
|
+
if (bv === undefined) {
|
|
267
|
+
return -1;
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
// actual comparisons
|
|
271
|
+
|
|
272
|
+
if (typeof av === 'number' && typeof bv === 'number') {
|
|
273
|
+
return (av - bv) * order;
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
if (typeof av === 'string' && typeof bv === 'string') {
|
|
277
|
+
return av.toLocaleLowerCase().localeCompare(bv.toLowerCase()) * order;
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
if (typeof av === 'boolean' && typeof bv === 'boolean') {
|
|
281
|
+
return av ? order : -order;
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
if (IsComplex(av) && IsComplex(bv)) {
|
|
285
|
+
return 0; // no sort order
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
// type rules
|
|
289
|
+
|
|
290
|
+
const types = [av, bv].map(x => {
|
|
291
|
+
switch (typeof x) {
|
|
292
|
+
case 'number':
|
|
293
|
+
return 0;
|
|
294
|
+
case 'string':
|
|
295
|
+
return 2;
|
|
296
|
+
case 'boolean':
|
|
297
|
+
return 3;
|
|
298
|
+
default:
|
|
299
|
+
if (IsComplex(x)) {
|
|
300
|
+
return 1;
|
|
301
|
+
}
|
|
302
|
+
return 4;
|
|
303
|
+
}
|
|
304
|
+
});
|
|
305
|
+
|
|
306
|
+
return (types[0] - types[1]) * order;
|
|
307
|
+
|
|
308
|
+
};
|
|
309
|
+
|
|
237
310
|
/**
|
|
238
311
|
* alternate functions. these are used (atm) only for changing complex
|
|
239
312
|
* behavior.
|
|
@@ -1176,29 +1249,149 @@ export const BaseFunctionLibrary: FunctionMap = {
|
|
|
1176
1249
|
|
|
1177
1250
|
},
|
|
1178
1251
|
|
|
1252
|
+
/**
|
|
1253
|
+
* sortby allows multiple sort indexes, but no column sorting
|
|
1254
|
+
*/
|
|
1255
|
+
SortBy: {
|
|
1256
|
+
arguments: [
|
|
1257
|
+
{ name: 'array', },
|
|
1258
|
+
{ name: 'index', },
|
|
1259
|
+
{ name: 'order', description: 'Set to -1 to sort in descending order', default: 1 }
|
|
1260
|
+
],
|
|
1261
|
+
fn: (ref: CellValue|CellValue[][], ...args: (CellValue|CellValue[][])[]): UnionValue => {
|
|
1262
|
+
|
|
1263
|
+
if (!Array.isArray(ref)) {
|
|
1264
|
+
ref = [[ref]];
|
|
1265
|
+
}
|
|
1266
|
+
|
|
1267
|
+
// must have at least one sort order?
|
|
1268
|
+
|
|
1269
|
+
if (args.length < 1) {
|
|
1270
|
+
return ArgumentError();
|
|
1271
|
+
}
|
|
1272
|
+
|
|
1273
|
+
// ensure any sort argument pairs are valid... I guess they
|
|
1274
|
+
// need to be the same length? what happens if not? [A: error]
|
|
1275
|
+
|
|
1276
|
+
const rows = ref[0]?.length || 0;
|
|
1277
|
+
const orders: number[] = [];
|
|
1278
|
+
const values: CellValue[][] = [];
|
|
1279
|
+
|
|
1280
|
+
for (let i = 0; i < args.length; i += 2) {
|
|
1281
|
+
|
|
1282
|
+
const target = i/2;
|
|
1283
|
+
|
|
1284
|
+
let sort_range = args[i];
|
|
1285
|
+
if (!Array.isArray(sort_range)) {
|
|
1286
|
+
sort_range = [[sort_range]];
|
|
1287
|
+
}
|
|
1288
|
+
|
|
1289
|
+
const check = sort_range[0]?.length || 0;
|
|
1290
|
+
if (check !== rows) {
|
|
1291
|
+
return ArgumentError();
|
|
1292
|
+
}
|
|
1293
|
+
|
|
1294
|
+
let order = 1;
|
|
1295
|
+
const arg = args[i+1];
|
|
1296
|
+
if (typeof arg === 'number' && arg < 0) {
|
|
1297
|
+
order = -1;
|
|
1298
|
+
}
|
|
1299
|
+
|
|
1300
|
+
orders[target] = order;
|
|
1301
|
+
values[target] = sort_range[0]; // (sort_range[0]).slice(0);
|
|
1302
|
+
|
|
1303
|
+
}
|
|
1304
|
+
|
|
1305
|
+
const mapped = ref[0]?.map((value, index) => (index));
|
|
1306
|
+
mapped.sort((a, b) => {
|
|
1307
|
+
|
|
1308
|
+
for (let i = 0; i < orders.length; i++) {
|
|
1309
|
+
const order = orders[i];
|
|
1310
|
+
const value_set = values[i];
|
|
1311
|
+
const result = SortHelper(order, value_set[a], value_set[b]);
|
|
1312
|
+
if (result) {
|
|
1313
|
+
return result;
|
|
1314
|
+
}
|
|
1315
|
+
}
|
|
1316
|
+
|
|
1317
|
+
return 0;
|
|
1318
|
+
|
|
1319
|
+
});
|
|
1320
|
+
|
|
1321
|
+
// output is same shape
|
|
1322
|
+
|
|
1323
|
+
const columns = ref.length;
|
|
1324
|
+
const result: UnionValue[][] = [];
|
|
1325
|
+
|
|
1326
|
+
for (let c = 0; c < columns; c++) {
|
|
1327
|
+
const column: UnionValue[] = [];
|
|
1328
|
+
for (const index of mapped) {
|
|
1329
|
+
column.push(Box(ref[c][index]));
|
|
1330
|
+
}
|
|
1331
|
+
result.push(column);
|
|
1332
|
+
}
|
|
1333
|
+
|
|
1334
|
+
return { type: ValueType.array, value: result };
|
|
1335
|
+
|
|
1336
|
+
|
|
1337
|
+
},
|
|
1338
|
+
},
|
|
1339
|
+
|
|
1179
1340
|
/**
|
|
1180
1341
|
* sort arguments, but ensure we return empty strings to
|
|
1181
1342
|
* fill up the result array
|
|
1182
1343
|
*
|
|
1183
1344
|
* FIXME: instead of boxing all the values, why not pass them in boxed?
|
|
1184
1345
|
* was this function just written at the wrong time?
|
|
1346
|
+
*
|
|
1347
|
+
* UPDATE: rewriting to match Excel args
|
|
1348
|
+
*
|
|
1185
1349
|
*/
|
|
1186
1350
|
Sort: {
|
|
1187
1351
|
arguments: [
|
|
1188
|
-
{ name: '
|
|
1352
|
+
{ name: 'array', },
|
|
1353
|
+
{ name: 'index', },
|
|
1354
|
+
{ name: 'order', description: 'Set to -1 to sort in descending order', default: 1 }
|
|
1189
1355
|
],
|
|
1190
|
-
fn: (
|
|
1356
|
+
fn: (ref: CellValue|CellValue[][], index = 1, order = 1): UnionValue => {
|
|
1191
1357
|
|
|
1192
|
-
|
|
1358
|
+
if (!Array.isArray(ref)) {
|
|
1359
|
+
ref = [[ref]];
|
|
1360
|
+
}
|
|
1361
|
+
|
|
1362
|
+
// FIXME: transpose for column sort
|
|
1363
|
+
|
|
1364
|
+
const sort_column = ref[index - 1];
|
|
1365
|
+
if (!sort_column) {
|
|
1366
|
+
return ArgumentError();
|
|
1367
|
+
}
|
|
1193
1368
|
|
|
1194
|
-
|
|
1195
|
-
|
|
1369
|
+
// clean (and be lenient)
|
|
1370
|
+
|
|
1371
|
+
if (order < 0) {
|
|
1372
|
+
order = -1;
|
|
1196
1373
|
}
|
|
1197
1374
|
else {
|
|
1198
|
-
|
|
1375
|
+
order = 1;
|
|
1376
|
+
}
|
|
1377
|
+
|
|
1378
|
+
const mapped = sort_column.map((value, index) => ({value, index}));
|
|
1379
|
+
mapped.sort((a, b) => SortHelper(order, a.value, b.value));
|
|
1380
|
+
|
|
1381
|
+
// output is same shape
|
|
1382
|
+
|
|
1383
|
+
const columns = ref.length;
|
|
1384
|
+
const result: UnionValue[][] = [];
|
|
1385
|
+
|
|
1386
|
+
for (let c = 0; c < columns; c++) {
|
|
1387
|
+
const column: UnionValue[] = [];
|
|
1388
|
+
for (const { index } of mapped) {
|
|
1389
|
+
column.push(Box(ref[c][index]));
|
|
1390
|
+
}
|
|
1391
|
+
result.push(column);
|
|
1199
1392
|
}
|
|
1200
1393
|
|
|
1201
|
-
return { type: ValueType.array, value:
|
|
1394
|
+
return { type: ValueType.array, value: result };
|
|
1202
1395
|
|
|
1203
1396
|
},
|
|
1204
1397
|
},
|
|
@@ -1564,8 +1757,10 @@ export const BaseFunctionLibrary: FunctionMap = {
|
|
|
1564
1757
|
//}
|
|
1565
1758
|
|
|
1566
1759
|
let rng: Area|undefined;
|
|
1760
|
+
let return_from_array = false;
|
|
1567
1761
|
|
|
1568
1762
|
if (return_array.type === ValueType.array) {
|
|
1763
|
+
|
|
1569
1764
|
// console.info({return_array});
|
|
1570
1765
|
|
|
1571
1766
|
const arr = return_array.value;
|
|
@@ -1579,9 +1774,16 @@ export const BaseFunctionLibrary: FunctionMap = {
|
|
|
1579
1774
|
rng = new Area(start.value.address, end.value.address);
|
|
1580
1775
|
}
|
|
1581
1776
|
|
|
1777
|
+
// we can allow a regular array here... perhaps we should
|
|
1778
|
+
// check the dimensions to ensure they match? TODO/FIXME
|
|
1779
|
+
|
|
1780
|
+
else {
|
|
1781
|
+
return_from_array = true;
|
|
1782
|
+
}
|
|
1783
|
+
|
|
1582
1784
|
}
|
|
1583
1785
|
|
|
1584
|
-
if (!rng) {
|
|
1786
|
+
if (!rng && !return_from_array) {
|
|
1585
1787
|
console.info('invalid range');
|
|
1586
1788
|
return ReferenceError();
|
|
1587
1789
|
}
|
|
@@ -1655,7 +1857,55 @@ export const BaseFunctionLibrary: FunctionMap = {
|
|
|
1655
1857
|
// an array. we might prefer to return a scalar if there's only
|
|
1656
1858
|
// one value, not sure what's the intended behavior
|
|
1657
1859
|
//
|
|
1658
|
-
const ReturnIndex = (
|
|
1860
|
+
const ReturnIndex = (index: number): UnionValue => {
|
|
1861
|
+
|
|
1862
|
+
// FIXME: we could almost certainly merge these two paths
|
|
1863
|
+
|
|
1864
|
+
if (return_from_array && return_array.type === ValueType.array) {
|
|
1865
|
+
|
|
1866
|
+
// instead of a range, we're returning values from a static array
|
|
1867
|
+
|
|
1868
|
+
const src_columns = return_array.value.length;
|
|
1869
|
+
const src_rows = return_array.value[0]?.length || 0;
|
|
1870
|
+
const result: UnionValue[][] = [];
|
|
1871
|
+
|
|
1872
|
+
let start_row = 0;
|
|
1873
|
+
let end_row = src_rows - 1;
|
|
1874
|
+
|
|
1875
|
+
let start_column = 0;
|
|
1876
|
+
let end_column = src_columns - 1;
|
|
1877
|
+
|
|
1878
|
+
if (transpose) {
|
|
1879
|
+
if (search_mode < 0) {
|
|
1880
|
+
index = src_rows - 1 - index; // invert FIXME: test
|
|
1881
|
+
}
|
|
1882
|
+
start_row = end_row = index;
|
|
1883
|
+
}
|
|
1884
|
+
else {
|
|
1885
|
+
if (search_mode < 0) {
|
|
1886
|
+
index = src_columns - 1 - index; // invert FIXME: test
|
|
1887
|
+
}
|
|
1888
|
+
start_column = end_column = index;
|
|
1889
|
+
}
|
|
1890
|
+
|
|
1891
|
+
for (let c = start_column; c <= end_column; c++) {
|
|
1892
|
+
const column: UnionValue[] = [];
|
|
1893
|
+
for (let r = start_row; r <= end_row; r++) {
|
|
1894
|
+
column.push(return_array.value[c][r]);
|
|
1895
|
+
}
|
|
1896
|
+
result.push(column);
|
|
1897
|
+
}
|
|
1898
|
+
|
|
1899
|
+
return {
|
|
1900
|
+
type: ValueType.array,
|
|
1901
|
+
value: result,
|
|
1902
|
+
};
|
|
1903
|
+
|
|
1904
|
+
}
|
|
1905
|
+
|
|
1906
|
+
if (!rng) {
|
|
1907
|
+
throw new Error('invalid range');
|
|
1908
|
+
}
|
|
1659
1909
|
|
|
1660
1910
|
// console.info("transpose?", transpose, {rng}, 'shape', rng.rows, rng.columns);
|
|
1661
1911
|
|
|
@@ -1760,7 +2010,7 @@ export const BaseFunctionLibrary: FunctionMap = {
|
|
|
1760
2010
|
|
|
1761
2011
|
// check for exact match first, just in case
|
|
1762
2012
|
if (value === lookup_value) {
|
|
1763
|
-
return ReturnIndex(
|
|
2013
|
+
return ReturnIndex(i);
|
|
1764
2014
|
}
|
|
1765
2015
|
|
|
1766
2016
|
const delta = Math.abs(value - lookup_value);
|
|
@@ -1776,7 +2026,7 @@ export const BaseFunctionLibrary: FunctionMap = {
|
|
|
1776
2026
|
}
|
|
1777
2027
|
|
|
1778
2028
|
if (index >= 0) {
|
|
1779
|
-
return ReturnIndex(
|
|
2029
|
+
return ReturnIndex(index);
|
|
1780
2030
|
}
|
|
1781
2031
|
|
|
1782
2032
|
}
|
|
@@ -1794,7 +2044,7 @@ export const BaseFunctionLibrary: FunctionMap = {
|
|
|
1794
2044
|
for (let i = 0; i < lookup_array.length; i++) {
|
|
1795
2045
|
const value = lookup_array[i][0];
|
|
1796
2046
|
if (typeof value === 'string' && regex.exec(value)) {
|
|
1797
|
-
return ReturnIndex(
|
|
2047
|
+
return ReturnIndex(i);
|
|
1798
2048
|
}
|
|
1799
2049
|
}
|
|
1800
2050
|
|
|
@@ -1812,7 +2062,7 @@ export const BaseFunctionLibrary: FunctionMap = {
|
|
|
1812
2062
|
value = value.toLowerCase();
|
|
1813
2063
|
}
|
|
1814
2064
|
if (value === lookup_value) {
|
|
1815
|
-
return ReturnIndex(
|
|
2065
|
+
return ReturnIndex(i);
|
|
1816
2066
|
}
|
|
1817
2067
|
}
|
|
1818
2068
|
|
|
@@ -20,9 +20,10 @@
|
|
|
20
20
|
*/
|
|
21
21
|
|
|
22
22
|
import type { FunctionMap } from '../descriptors';
|
|
23
|
-
import type { FunctionUnion, UnionValue} from 'treb-base-types';
|
|
24
|
-
import { ValueType } from 'treb-base-types';
|
|
23
|
+
import type { CellValue, FunctionUnion, UnionValue} from 'treb-base-types';
|
|
24
|
+
import { Box, ValueType } from 'treb-base-types';
|
|
25
25
|
import { ArgumentError, ValueError } from '../function-error';
|
|
26
|
+
import { FlattenCellValues } from '../utilities';
|
|
26
27
|
|
|
27
28
|
export const FPFunctionLibrary: FunctionMap = {
|
|
28
29
|
|
|
@@ -67,6 +68,195 @@ export const FPFunctionLibrary: FunctionMap = {
|
|
|
67
68
|
},
|
|
68
69
|
},
|
|
69
70
|
|
|
71
|
+
ChooseCols: {
|
|
72
|
+
description: 'Returns one or more columns from an array, by column index',
|
|
73
|
+
arguments: [{
|
|
74
|
+
name: 'array',
|
|
75
|
+
}, {
|
|
76
|
+
name: 'column',
|
|
77
|
+
repeat: true,
|
|
78
|
+
}],
|
|
79
|
+
fn: function(data: CellValue|CellValue[][], ...args: number[]) {
|
|
80
|
+
|
|
81
|
+
if (!Array.isArray(data)) {
|
|
82
|
+
data = [[data]];
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
const flat = FlattenCellValues(args);
|
|
86
|
+
// console.info({flat});
|
|
87
|
+
|
|
88
|
+
const columns = data.length;
|
|
89
|
+
const result: UnionValue[][] = [];
|
|
90
|
+
|
|
91
|
+
for (let arg of flat) {
|
|
92
|
+
if (typeof arg === 'number') {
|
|
93
|
+
|
|
94
|
+
if (arg < 0) {
|
|
95
|
+
arg = columns + arg;
|
|
96
|
+
}
|
|
97
|
+
else {
|
|
98
|
+
arg--;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
const col = data[arg];
|
|
102
|
+
if (col) {
|
|
103
|
+
const column: UnionValue[] = [];
|
|
104
|
+
for (const value of col) {
|
|
105
|
+
column.push(Box(value));
|
|
106
|
+
}
|
|
107
|
+
result.push(column);
|
|
108
|
+
}
|
|
109
|
+
else {
|
|
110
|
+
return ArgumentError(); // invalid column
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
if (!result.length) {
|
|
116
|
+
result.push([]);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
return {
|
|
120
|
+
type: ValueType.array,
|
|
121
|
+
value: result,
|
|
122
|
+
};
|
|
123
|
+
|
|
124
|
+
},
|
|
125
|
+
},
|
|
126
|
+
|
|
127
|
+
ChooseRows: {
|
|
128
|
+
description: 'Returns one or more rows from an array, by row index',
|
|
129
|
+
arguments: [{
|
|
130
|
+
name: 'array',
|
|
131
|
+
}, {
|
|
132
|
+
name: 'row',
|
|
133
|
+
repeat: true,
|
|
134
|
+
}],
|
|
135
|
+
fn: function(data: CellValue|CellValue[][], ...args: number[]) {
|
|
136
|
+
|
|
137
|
+
if (!Array.isArray(data)) {
|
|
138
|
+
data = [[data]];
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
const flat = FlattenCellValues(args);
|
|
142
|
+
// console.info({flat});
|
|
143
|
+
|
|
144
|
+
const rows = data[0]?.length || 0;
|
|
145
|
+
const result: UnionValue[][] = [];
|
|
146
|
+
|
|
147
|
+
for (let arg of flat) {
|
|
148
|
+
if (typeof arg === 'number') {
|
|
149
|
+
|
|
150
|
+
if (arg < 0) {
|
|
151
|
+
arg = rows + arg;
|
|
152
|
+
}
|
|
153
|
+
else {
|
|
154
|
+
arg--;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
for (const [column_index, col] of data.entries()) {
|
|
158
|
+
const column = result[column_index] || [];
|
|
159
|
+
if (arg < 0 || arg >= col.length) {
|
|
160
|
+
return ArgumentError();
|
|
161
|
+
}
|
|
162
|
+
column.push(Box(col[arg]));
|
|
163
|
+
result[column_index] = column;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
if (!result.length) {
|
|
170
|
+
result.push([]);
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
return {
|
|
174
|
+
type: ValueType.array,
|
|
175
|
+
value: result,
|
|
176
|
+
};
|
|
177
|
+
|
|
178
|
+
},
|
|
179
|
+
},
|
|
180
|
+
|
|
181
|
+
Take: {
|
|
182
|
+
description: 'Returns some number of rows/columns from the start or end of an array',
|
|
183
|
+
arguments: [{
|
|
184
|
+
name: 'array',
|
|
185
|
+
}, {
|
|
186
|
+
name: 'rows',
|
|
187
|
+
}, {
|
|
188
|
+
name: 'columns',
|
|
189
|
+
}],
|
|
190
|
+
fn: function(data: CellValue|CellValue[][], rows?: number, columns?: number) {
|
|
191
|
+
|
|
192
|
+
// we need one of rows, columns to be defined. neither can === 0.
|
|
193
|
+
|
|
194
|
+
if ((!rows && !columns) || rows === 0 || columns === 0 || (typeof rows !== 'number' && typeof rows !== 'undefined') || (typeof columns !== 'number' && typeof columns !== 'undefined')) {
|
|
195
|
+
return ArgumentError();
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
if (!Array.isArray(data)) {
|
|
199
|
+
data = [[data]];
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
const data_columns = data.length;
|
|
203
|
+
const data_rows = data[0].length;
|
|
204
|
+
const result: UnionValue[][] = [];
|
|
205
|
+
|
|
206
|
+
// I guess we can compose?
|
|
207
|
+
|
|
208
|
+
// data is column-first
|
|
209
|
+
|
|
210
|
+
let start_column = 0;
|
|
211
|
+
let end_column = data_columns - 1;
|
|
212
|
+
|
|
213
|
+
let start_row = 0;
|
|
214
|
+
let end_row = data_rows - 1;
|
|
215
|
+
|
|
216
|
+
if (typeof columns === 'number') {
|
|
217
|
+
|
|
218
|
+
// clip data so it has the first (or last) X columns
|
|
219
|
+
|
|
220
|
+
if (columns > 0) {
|
|
221
|
+
end_column = Math.min(columns, data_columns) - 1;
|
|
222
|
+
}
|
|
223
|
+
else if (columns < 0) {
|
|
224
|
+
end_column = data_columns - 1;
|
|
225
|
+
start_column = Math.max(0, end_column + columns + 1);
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
if (typeof rows === 'number') {
|
|
231
|
+
|
|
232
|
+
// clip data so it has the first (or last) X columns
|
|
233
|
+
|
|
234
|
+
if (rows > 0) {
|
|
235
|
+
end_row = Math.min(rows, data_rows) - 1;
|
|
236
|
+
}
|
|
237
|
+
else if (rows < 0) {
|
|
238
|
+
end_row = data_rows - 1;
|
|
239
|
+
start_row = Math.max(0, end_row + rows + 1);
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
for (let c = start_column; c <= end_column; c++) {
|
|
245
|
+
const column: UnionValue[] = [];
|
|
246
|
+
for (let r = start_row; r <= end_row; r++) {
|
|
247
|
+
column.push(Box(data[c][r]));
|
|
248
|
+
}
|
|
249
|
+
result.push(column);
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
return {
|
|
253
|
+
type: ValueType.array,
|
|
254
|
+
value: result,
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
},
|
|
258
|
+
},
|
|
259
|
+
|
|
70
260
|
Reduce: {
|
|
71
261
|
description: 'Accumulates a value by applying a function to a set of values',
|
|
72
262
|
arguments: [
|
|
@@ -140,11 +140,15 @@ export class ScaleControl extends EventSource<ScaleEvent> {
|
|
|
140
140
|
}
|
|
141
141
|
});
|
|
142
142
|
|
|
143
|
+
// we're overriding scroll behavior here, but it's a tiny panel.
|
|
144
|
+
// if we explicitly mark passive:false we can avoid the console
|
|
145
|
+
// warning (I think)
|
|
146
|
+
|
|
143
147
|
container.addEventListener('wheel', (event: WheelEvent) => {
|
|
144
148
|
event.stopPropagation();
|
|
145
149
|
event.preventDefault();
|
|
146
150
|
this.Tick(event.deltaY)
|
|
147
|
-
});
|
|
151
|
+
}, { passive: false });
|
|
148
152
|
|
|
149
153
|
}
|
|
150
154
|
|