@trebco/treb 28.15.0 → 28.17.4
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/api-generator/api-generator.ts +5 -1
- package/dist/treb-spreadsheet-light.mjs +10 -10
- package/dist/treb-spreadsheet.mjs +15 -15
- package/dist/treb.d.ts +6 -1
- package/package.json +1 -1
- package/treb-base-types/src/area.ts +80 -1
- package/treb-base-types/src/cells.ts +187 -41
- package/treb-calculator/src/calculator.ts +13 -1
- package/treb-calculator/src/functions/base-functions.ts +71 -0
- package/treb-calculator/src/utilities.ts +37 -24
- package/treb-embed/markup/toolbar.html +5 -0
- package/treb-embed/src/custom-element/spreadsheet-constructor.ts +5 -2
- package/treb-embed/src/embedded-spreadsheet.ts +46 -19
- package/treb-embed/src/options.ts +5 -0
- package/treb-embed/src/toolbar-message.ts +5 -0
- package/treb-embed/style/toolbar.scss +3 -0
- package/treb-embed/style/treb-icons.scss +6 -0
- package/treb-export/src/import2.ts +5 -16
- package/treb-export/src/workbook-style2.ts +15 -27
- package/treb-grid/src/render/tile_renderer.ts +37 -13
- package/treb-grid/src/types/grid.ts +49 -3
- package/treb-grid/src/types/grid_base.ts +156 -8
- package/treb-grid/src/types/grid_command.ts +8 -0
- package/treb-grid/src/types/sheet.ts +127 -7
package/dist/treb.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/*! API v28.
|
|
1
|
+
/*! API v28.17. 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
|
|
@@ -271,6 +271,11 @@ export interface EmbeddedSpreadsheetOptions {
|
|
|
271
271
|
* change in the future. key modifiers have no effect.
|
|
272
272
|
*/
|
|
273
273
|
recalculate_on_f9?: boolean;
|
|
274
|
+
|
|
275
|
+
/**
|
|
276
|
+
* indent/outdent buttons; default false
|
|
277
|
+
*/
|
|
278
|
+
indent_buttons?: boolean;
|
|
274
279
|
}
|
|
275
280
|
|
|
276
281
|
/**
|
package/package.json
CHANGED
|
@@ -101,7 +101,7 @@ export interface Dimensions {
|
|
|
101
101
|
* sheet class has a method for reducing infinite ranges to actual
|
|
102
102
|
* populated ranges.
|
|
103
103
|
*/
|
|
104
|
-
export class Area implements IArea {
|
|
104
|
+
export class Area implements IArea {
|
|
105
105
|
|
|
106
106
|
// tslint:disable-next-line:variable-name
|
|
107
107
|
private start_: ICellAddress;
|
|
@@ -562,6 +562,7 @@ export class Area implements IArea { // }, IterableIterator<ICellAddress> {
|
|
|
562
562
|
return new Area(this.start, this.end); // ensure copies
|
|
563
563
|
}
|
|
564
564
|
|
|
565
|
+
/* removed, use iterator
|
|
565
566
|
public Array(): ICellAddress[] {
|
|
566
567
|
if (this.entire_column || this.entire_row) throw new Error('can\'t convert infinite area to array');
|
|
567
568
|
const array: ICellAddress[] = new Array<ICellAddress>(this.rows * this.columns);
|
|
@@ -578,6 +579,7 @@ export class Area implements IArea { // }, IterableIterator<ICellAddress> {
|
|
|
578
579
|
}
|
|
579
580
|
return array;
|
|
580
581
|
}
|
|
582
|
+
*/
|
|
581
583
|
|
|
582
584
|
get left(): Area{
|
|
583
585
|
const area = new Area(this.start_, this.end_);
|
|
@@ -637,6 +639,82 @@ export class Area implements IArea { // }, IterableIterator<ICellAddress> {
|
|
|
637
639
|
return this; // fluent
|
|
638
640
|
}
|
|
639
641
|
|
|
642
|
+
/* *
|
|
643
|
+
* preferred to straight iterator. actually in this class iterator
|
|
644
|
+
* is OK but in some other cases we'll want to generate like this
|
|
645
|
+
*
|
|
646
|
+
* eh I don't know about this inline function, is that going to be
|
|
647
|
+
* optimized out? ...
|
|
648
|
+
* /
|
|
649
|
+
public get contents(): Generator<{ row: number, column: number, sheet_id?: number }> {
|
|
650
|
+
|
|
651
|
+
if (this.entire_row || this.entire_column) {
|
|
652
|
+
throw new Error(`don't iterate infinite area`);
|
|
653
|
+
}
|
|
654
|
+
|
|
655
|
+
const sheet_id = this.start_.sheet_id;
|
|
656
|
+
const start_column = this.start_.column;
|
|
657
|
+
const end_column = this.end_.column;
|
|
658
|
+
const start_row = this.start_.row;
|
|
659
|
+
const end_row = this.end_.row;
|
|
660
|
+
|
|
661
|
+
|
|
662
|
+
function *generator() {
|
|
663
|
+
for (let column = start_column; column <= end_column; column++){
|
|
664
|
+
for (let row = start_row; row <= end_row; row++){
|
|
665
|
+
yield {column, row, sheet_id};
|
|
666
|
+
}
|
|
667
|
+
}
|
|
668
|
+
}
|
|
669
|
+
|
|
670
|
+
return generator();
|
|
671
|
+
|
|
672
|
+
}
|
|
673
|
+
*/
|
|
674
|
+
|
|
675
|
+
/**
|
|
676
|
+
* modernizing. this is a proper iterator. generators are prettier
|
|
677
|
+
* but there's at least some performance cost -- I'm not sure how
|
|
678
|
+
* much, but it's non-zero.
|
|
679
|
+
*/
|
|
680
|
+
public [Symbol.iterator](): Iterator<ICellAddress> {
|
|
681
|
+
|
|
682
|
+
if (this.entire_row || this.entire_column) {
|
|
683
|
+
throw new Error(`don't iterate infinite area`);
|
|
684
|
+
}
|
|
685
|
+
|
|
686
|
+
let row = this.start_.row;
|
|
687
|
+
let column = this.start_.column;
|
|
688
|
+
|
|
689
|
+
// this now uses "live" references, so if the object were mutated
|
|
690
|
+
// during iteration the iterator would reflect those changes. which
|
|
691
|
+
// seems bad, but also correct.
|
|
692
|
+
|
|
693
|
+
return {
|
|
694
|
+
next: () => {
|
|
695
|
+
|
|
696
|
+
const value = { column, row, sheet_id: this.start_.sheet_id };
|
|
697
|
+
|
|
698
|
+
if (column > this.end_.column) {
|
|
699
|
+
return {
|
|
700
|
+
done: true,
|
|
701
|
+
value: undefined,
|
|
702
|
+
};
|
|
703
|
+
}
|
|
704
|
+
|
|
705
|
+
if (++row > this.end_.row) {
|
|
706
|
+
row = this.start_.row;
|
|
707
|
+
column++;
|
|
708
|
+
}
|
|
709
|
+
|
|
710
|
+
return { value };
|
|
711
|
+
|
|
712
|
+
},
|
|
713
|
+
};
|
|
714
|
+
|
|
715
|
+
};
|
|
716
|
+
|
|
717
|
+
/* * @deprecated * /
|
|
640
718
|
public Iterate(f: (...args: any[]) => any): void {
|
|
641
719
|
if (this.entire_column || this.entire_row) {
|
|
642
720
|
console.warn(`don't iterate infinite area`);
|
|
@@ -648,6 +726,7 @@ export class Area implements IArea { // }, IterableIterator<ICellAddress> {
|
|
|
648
726
|
}
|
|
649
727
|
}
|
|
650
728
|
}
|
|
729
|
+
*/
|
|
651
730
|
|
|
652
731
|
/* *
|
|
653
732
|
* testing: we may have to polyfill for IE11, or just not use it at
|
|
@@ -796,40 +796,33 @@ export class Cells {
|
|
|
796
796
|
return this.GetRange({row: 0, column: 0}, {row: this.rows_ - 1, column: this.columns_ - 1}, transpose);
|
|
797
797
|
}
|
|
798
798
|
|
|
799
|
-
/**
|
|
800
|
-
public
|
|
799
|
+
/** overload */
|
|
800
|
+
public Normalize(from: ICellAddress, to: ICellAddress): { from: ICellAddress, to: ICellAddress };
|
|
801
801
|
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
}
|
|
805
|
-
|
|
806
|
-
if (from.row === Infinity) {
|
|
807
|
-
from = { ...from, row: 0, };
|
|
808
|
-
}
|
|
809
|
-
|
|
810
|
-
if (to.column === Infinity) {
|
|
811
|
-
to = { ...to, column: this.columns_ - 1};
|
|
812
|
-
}
|
|
813
|
-
|
|
814
|
-
if (to.row === Infinity) {
|
|
815
|
-
to = { ...to, row: this.rows_ - 1};
|
|
816
|
-
}
|
|
802
|
+
/** overload */
|
|
803
|
+
public Normalize(from: ICellAddress): { from: ICellAddress };
|
|
817
804
|
|
|
818
|
-
|
|
819
|
-
}
|
|
805
|
+
/** overload */
|
|
806
|
+
public Normalize<K extends ICellAddress|undefined>(from: ICellAddress, to: K): { from: ICellAddress, to: K };
|
|
820
807
|
|
|
821
|
-
/**
|
|
822
|
-
public
|
|
808
|
+
/** base */
|
|
809
|
+
public Normalize(from: ICellAddress, to?: ICellAddress): { from: ICellAddress, to?: ICellAddress } {
|
|
823
810
|
|
|
824
|
-
|
|
825
|
-
|
|
811
|
+
from = {
|
|
812
|
+
...from,
|
|
813
|
+
row: from.row == Infinity ? 0 : from.row,
|
|
814
|
+
column: from.column == Infinity ? 0 : from.column,
|
|
826
815
|
}
|
|
827
816
|
|
|
828
|
-
if (
|
|
829
|
-
|
|
817
|
+
if (to) {
|
|
818
|
+
to = {
|
|
819
|
+
...to,
|
|
820
|
+
row: to.row == Infinity ? this.rows_ - 1 : to.row,
|
|
821
|
+
column: to.column == Infinity ? this.columns_ - 1 : to.column,
|
|
822
|
+
}
|
|
830
823
|
}
|
|
831
824
|
|
|
832
|
-
return from;
|
|
825
|
+
return { from, to };
|
|
833
826
|
|
|
834
827
|
}
|
|
835
828
|
|
|
@@ -851,7 +844,7 @@ export class Cells {
|
|
|
851
844
|
*/
|
|
852
845
|
public RawValue(from: ICellAddress, to: ICellAddress = from): CellValue | CellValue[][] | undefined {
|
|
853
846
|
|
|
854
|
-
({from, to} = this.
|
|
847
|
+
({from, to} = this.Normalize(from, to));
|
|
855
848
|
|
|
856
849
|
if (from.row === to.row && from.column === to.column) {
|
|
857
850
|
if (this.data[from.row] && this.data[from.row][from.column]) {
|
|
@@ -885,12 +878,7 @@ export class Cells {
|
|
|
885
878
|
/** gets range as values */
|
|
886
879
|
public GetRange(from: ICellAddress, to?: ICellAddress, transpose = false){
|
|
887
880
|
|
|
888
|
-
|
|
889
|
-
({from, to} = this.Normalize2(from, to));
|
|
890
|
-
}
|
|
891
|
-
else {
|
|
892
|
-
from = this.Normalize1(from);
|
|
893
|
-
}
|
|
881
|
+
({from, to} = this.Normalize(from, to));
|
|
894
882
|
|
|
895
883
|
// console.info("getrange", from, to, transpose);
|
|
896
884
|
|
|
@@ -972,7 +960,7 @@ export class Cells {
|
|
|
972
960
|
|
|
973
961
|
public GetRange4(from: ICellAddress, to: ICellAddress = from, transpose = false): UnionValue {
|
|
974
962
|
|
|
975
|
-
({from, to} = this.
|
|
963
|
+
({from, to} = this.Normalize(from, to));
|
|
976
964
|
|
|
977
965
|
if (from.row === to.row && from.column === to.column) {
|
|
978
966
|
if (this.data[from.row] && this.data[from.row][from.column]){
|
|
@@ -1008,10 +996,10 @@ export class Cells {
|
|
|
1008
996
|
|
|
1009
997
|
}
|
|
1010
998
|
|
|
1011
|
-
|
|
999
|
+
/* *
|
|
1012
1000
|
* apply function to address/area
|
|
1013
1001
|
* @deprecated - use Apply2
|
|
1014
|
-
|
|
1002
|
+
* /
|
|
1015
1003
|
public Apply(area: Area|ICellAddress, f: (cell: Cell, c?: number, r?: number) => void, create_missing_cells = false): void {
|
|
1016
1004
|
|
|
1017
1005
|
// allow single address
|
|
@@ -1050,12 +1038,71 @@ export class Cells {
|
|
|
1050
1038
|
}
|
|
1051
1039
|
}
|
|
1052
1040
|
}
|
|
1041
|
+
*/
|
|
1053
1042
|
|
|
1054
|
-
|
|
1043
|
+
/* *
|
|
1044
|
+
* as a replacement for the Apply functions. testing
|
|
1045
|
+
*
|
|
1046
|
+
* (1) it seems like almost no one uses the cell address, so returning
|
|
1047
|
+
* it just adds a wasteful destructuring to every loop iteration.
|
|
1048
|
+
*
|
|
1049
|
+
* actually there's exactly one, and we could work around it. but
|
|
1050
|
+
* it seems like it might be useful in some cases, so... ?
|
|
1051
|
+
*
|
|
1052
|
+
* (2) can we consolidate with the IterateAll method (named Iterate)?
|
|
1053
|
+
* that would only work if we had the same return type, meaning either
|
|
1054
|
+
* drop the address from this one or add it to the other one
|
|
1055
|
+
*
|
|
1056
|
+
* @deprecated use Iterate, if possible
|
|
1057
|
+
*
|
|
1058
|
+
* /
|
|
1059
|
+
public *IterateArea(area: Area|ICellAddress, create_missing_cells = false) {
|
|
1060
|
+
|
|
1061
|
+
// allow single address
|
|
1062
|
+
if (IsCellAddress(area)) {
|
|
1063
|
+
area = new Area(area);
|
|
1064
|
+
}
|
|
1065
|
+
|
|
1066
|
+
// why not just cap? (...)
|
|
1067
|
+
if (area.entire_column || area.entire_row) {
|
|
1068
|
+
throw new Error(`don't iterate infinite cells`);
|
|
1069
|
+
}
|
|
1070
|
+
|
|
1071
|
+
const start = area.start;
|
|
1072
|
+
const end = area.end;
|
|
1073
|
+
|
|
1074
|
+
if (create_missing_cells){
|
|
1075
|
+
for ( let r = start.row; r <= end.row; r++ ){
|
|
1076
|
+
if (!this.data[r]) this.data[r] = [];
|
|
1077
|
+
const row = this.data[r];
|
|
1078
|
+
for ( let c = start.column; c <= end.column; c++ ){
|
|
1079
|
+
if (!row[c]) row[c] = new Cell();
|
|
1080
|
+
yield { row: r, column: c, cell: row[c] };
|
|
1081
|
+
}
|
|
1082
|
+
}
|
|
1083
|
+
}
|
|
1084
|
+
else {
|
|
1085
|
+
// we can loop over indexes that don't exist, just check for existence
|
|
1086
|
+
for ( let r = start.row; r <= end.row; r++ ){
|
|
1087
|
+
if (this.data[r]){
|
|
1088
|
+
const row = this.data[r];
|
|
1089
|
+
for ( let c = start.column; c <= end.column; c++ ){
|
|
1090
|
+
if (row[c]) {
|
|
1091
|
+
yield { row: r, column: c, cell: row[c] };
|
|
1092
|
+
}
|
|
1093
|
+
}
|
|
1094
|
+
}
|
|
1095
|
+
}
|
|
1096
|
+
}
|
|
1097
|
+
|
|
1098
|
+
}
|
|
1099
|
+
*/
|
|
1100
|
+
|
|
1101
|
+
/* *
|
|
1055
1102
|
* apply function to address/area
|
|
1056
1103
|
*
|
|
1057
1104
|
* this version lets you abort by returning false from the callback function
|
|
1058
|
-
|
|
1105
|
+
* /
|
|
1059
1106
|
public Apply2(area: Area|ICellAddress, func: (cell: Cell, c?: number, r?: number) => boolean, create_missing_cells = false): void {
|
|
1060
1107
|
|
|
1061
1108
|
// allow single address
|
|
@@ -1100,6 +1147,7 @@ export class Cells {
|
|
|
1100
1147
|
}
|
|
1101
1148
|
}
|
|
1102
1149
|
}
|
|
1150
|
+
*/
|
|
1103
1151
|
|
|
1104
1152
|
/**
|
|
1105
1153
|
* set area. shortcut to reduce overhead. consolidates single value
|
|
@@ -1148,11 +1196,108 @@ export class Cells {
|
|
|
1148
1196
|
}
|
|
1149
1197
|
|
|
1150
1198
|
/**
|
|
1199
|
+
* replacement for old callback iterator. this is a composite
|
|
1200
|
+
* of iterating all and iterating an area. I want to remove the
|
|
1201
|
+
* other method, but there are still some calls that use the
|
|
1202
|
+
* cell address.
|
|
1203
|
+
*
|
|
1204
|
+
* Q: is it possible to use Symbol.iterator with arguments?
|
|
1205
|
+
* A: apparently it is, but how would you call it? (...)
|
|
1206
|
+
*/
|
|
1207
|
+
public *Iterate(area?: Area|ICellAddress, create_missing_cells = false) {
|
|
1208
|
+
|
|
1209
|
+
// special case. normally iterating over all cells
|
|
1210
|
+
// doesn't create missing, so we use a simpler loop.
|
|
1211
|
+
|
|
1212
|
+
if (!area && create_missing_cells) {
|
|
1213
|
+
area = new Area({
|
|
1214
|
+
row: 0,
|
|
1215
|
+
column: 0,
|
|
1216
|
+
}, {
|
|
1217
|
+
row: this.rows_ - 1,
|
|
1218
|
+
column: this.columns - 1,
|
|
1219
|
+
});
|
|
1220
|
+
}
|
|
1221
|
+
|
|
1222
|
+
// if we have an area, iterate over the area. we need indexes.
|
|
1223
|
+
|
|
1224
|
+
if (area) {
|
|
1225
|
+
|
|
1226
|
+
// allow single address
|
|
1227
|
+
if (IsCellAddress(area)) {
|
|
1228
|
+
area = new Area(area);
|
|
1229
|
+
}
|
|
1230
|
+
|
|
1231
|
+
// why not just cap? (...)
|
|
1232
|
+
// if (area.entire_column || area.entire_row) {
|
|
1233
|
+
// throw new Error(`don't iterate infinite cells`);
|
|
1234
|
+
//}
|
|
1235
|
+
|
|
1236
|
+
if (area.entire_column || area.entire_row) {
|
|
1237
|
+
area = new Area(area.start, area.end);
|
|
1238
|
+
if (area.start.column === Infinity) {
|
|
1239
|
+
area.start.column = 0;
|
|
1240
|
+
area.end.column = this.columns_ - 1;
|
|
1241
|
+
}
|
|
1242
|
+
if (area.start.row === Infinity) {
|
|
1243
|
+
area.start.row = 0;
|
|
1244
|
+
area.end.row = this.rows_ - 1;
|
|
1245
|
+
}
|
|
1246
|
+
}
|
|
1247
|
+
|
|
1248
|
+
const start = area.start;
|
|
1249
|
+
const end = area.end;
|
|
1250
|
+
|
|
1251
|
+
if (create_missing_cells){
|
|
1252
|
+
for ( let r = start.row; r <= end.row; r++ ){
|
|
1253
|
+
if (!this.data[r]) this.data[r] = [];
|
|
1254
|
+
const row = this.data[r];
|
|
1255
|
+
for ( let c = start.column; c <= end.column; c++ ){
|
|
1256
|
+
if (!row[c]) row[c] = new Cell();
|
|
1257
|
+
yield row[c]; // { row: r, column: c, cell: row[c] };
|
|
1258
|
+
}
|
|
1259
|
+
}
|
|
1260
|
+
}
|
|
1261
|
+
else {
|
|
1262
|
+
// we can loop over indexes that don't exist, just check for existence
|
|
1263
|
+
for ( let r = start.row; r <= end.row; r++ ){
|
|
1264
|
+
if (this.data[r]){
|
|
1265
|
+
const row = this.data[r];
|
|
1266
|
+
for ( let c = start.column; c <= end.column; c++ ){
|
|
1267
|
+
if (row[c]) {
|
|
1268
|
+
yield row[c]; // { row: r, column: c, cell: row[c] };
|
|
1269
|
+
}
|
|
1270
|
+
}
|
|
1271
|
+
}
|
|
1272
|
+
}
|
|
1273
|
+
}
|
|
1274
|
+
|
|
1275
|
+
}
|
|
1276
|
+
else {
|
|
1277
|
+
|
|
1278
|
+
// no area; just iterate all cells. implicitly skip undefined cells.
|
|
1279
|
+
|
|
1280
|
+
for (const row of this.data) {
|
|
1281
|
+
if (row) {
|
|
1282
|
+
for (const cell of row) {
|
|
1283
|
+
if (cell) {
|
|
1284
|
+
yield cell;
|
|
1285
|
+
}
|
|
1286
|
+
}
|
|
1287
|
+
}
|
|
1288
|
+
}
|
|
1289
|
+
}
|
|
1290
|
+
}
|
|
1291
|
+
|
|
1292
|
+
/* *
|
|
1151
1293
|
* iterates over all cells (using loops) and runs function per-cell.
|
|
1152
1294
|
* FIXME: switch to indexing on empty indexes? (...)
|
|
1153
|
-
|
|
1295
|
+
*
|
|
1296
|
+
* removed in favor of generator-based function
|
|
1297
|
+
*
|
|
1298
|
+
* /
|
|
1154
1299
|
public IterateAll(func: (cell: Cell) => void){
|
|
1155
|
-
|
|
1300
|
+
/ *
|
|
1156
1301
|
const row_keys = Object.keys(this.data);
|
|
1157
1302
|
for (const row of row_keys){
|
|
1158
1303
|
const n_row = Number(row) || 0;
|
|
@@ -1161,7 +1306,7 @@ export class Cells {
|
|
|
1161
1306
|
f(this.data[n_row][Number(column_key)]);
|
|
1162
1307
|
}
|
|
1163
1308
|
}
|
|
1164
|
-
|
|
1309
|
+
* /
|
|
1165
1310
|
for (const row of this.data) {
|
|
1166
1311
|
if (row) {
|
|
1167
1312
|
for (const cell of row) {
|
|
@@ -1173,6 +1318,7 @@ export class Cells {
|
|
|
1173
1318
|
}
|
|
1174
1319
|
|
|
1175
1320
|
}
|
|
1321
|
+
*/
|
|
1176
1322
|
|
|
1177
1323
|
/** moved from sheet, so we can do it non-functional style (for perf) */
|
|
1178
1324
|
public FlushCellStyles() {
|
|
@@ -2475,9 +2475,15 @@ export class Calculator extends Graph {
|
|
|
2475
2475
|
const unit = dependencies.ranges[key];
|
|
2476
2476
|
const range = new Area(unit.start, unit.end);
|
|
2477
2477
|
|
|
2478
|
+
for (const address of range) {
|
|
2479
|
+
this.AddLeafVertexEdge(address, vertex);
|
|
2480
|
+
}
|
|
2481
|
+
|
|
2482
|
+
/*
|
|
2478
2483
|
range.Iterate((address: ICellAddress) => {
|
|
2479
2484
|
this.AddLeafVertexEdge(address, vertex);
|
|
2480
2485
|
});
|
|
2486
|
+
*/
|
|
2481
2487
|
|
|
2482
2488
|
/*
|
|
2483
2489
|
for (const address of range) {
|
|
@@ -2709,7 +2715,13 @@ export class Calculator extends Graph {
|
|
|
2709
2715
|
this.AddArrayEdge(range, address);
|
|
2710
2716
|
}
|
|
2711
2717
|
else {
|
|
2712
|
-
|
|
2718
|
+
|
|
2719
|
+
for (const target of range) {
|
|
2720
|
+
this.AddEdge(target, address);
|
|
2721
|
+
}
|
|
2722
|
+
|
|
2723
|
+
// range.Iterate((target: ICellAddress) => this.AddEdge(target, address));
|
|
2724
|
+
|
|
2713
2725
|
}
|
|
2714
2726
|
|
|
2715
2727
|
|
|
@@ -729,6 +729,77 @@ export const BaseFunctionLibrary: FunctionMap = {
|
|
|
729
729
|
})
|
|
730
730
|
},
|
|
731
731
|
|
|
732
|
+
Large: {
|
|
733
|
+
description: 'Returns the nth numeric value from the data, in descending order',
|
|
734
|
+
arguments: [
|
|
735
|
+
{
|
|
736
|
+
name: 'values',
|
|
737
|
+
},
|
|
738
|
+
{
|
|
739
|
+
name: 'index',
|
|
740
|
+
}
|
|
741
|
+
],
|
|
742
|
+
|
|
743
|
+
// OK a little tricky here -- we're going to reverse the arguments
|
|
744
|
+
// so we can call apply as array, but applying against the trailing
|
|
745
|
+
// arguments.
|
|
746
|
+
|
|
747
|
+
fn: Utils.ApplyAsArraySwap((data: any, index: number) => {
|
|
748
|
+
|
|
749
|
+
if (index <= 0) {
|
|
750
|
+
return ArgumentError();
|
|
751
|
+
}
|
|
752
|
+
|
|
753
|
+
const flat = Utils.FlattenUnboxed(data);
|
|
754
|
+
const numeric: number[] = flat.filter(test => typeof test === 'number');
|
|
755
|
+
numeric.sort((a, b) => b - a);
|
|
756
|
+
|
|
757
|
+
if (index <= numeric.length) {
|
|
758
|
+
return {
|
|
759
|
+
type: ValueType.number,
|
|
760
|
+
value: numeric[index - 1],
|
|
761
|
+
};
|
|
762
|
+
}
|
|
763
|
+
|
|
764
|
+
return ArgumentError();
|
|
765
|
+
}),
|
|
766
|
+
},
|
|
767
|
+
|
|
768
|
+
Small: {
|
|
769
|
+
description: 'Returns the nth numeric value from the data, in ascending order',
|
|
770
|
+
arguments: [
|
|
771
|
+
{
|
|
772
|
+
name: 'values',
|
|
773
|
+
},
|
|
774
|
+
{
|
|
775
|
+
name: 'index',
|
|
776
|
+
}
|
|
777
|
+
],
|
|
778
|
+
|
|
779
|
+
// see large, above
|
|
780
|
+
|
|
781
|
+
fn: Utils.ApplyAsArraySwap((data: any, index: number) => {
|
|
782
|
+
|
|
783
|
+
if (index <= 0) {
|
|
784
|
+
return ArgumentError();
|
|
785
|
+
}
|
|
786
|
+
|
|
787
|
+
const flat = Utils.FlattenUnboxed(data);
|
|
788
|
+
const numeric: number[] = flat.filter(test => typeof test === 'number');
|
|
789
|
+
numeric.sort((a, b) => a - b);
|
|
790
|
+
|
|
791
|
+
if (index <= numeric.length) {
|
|
792
|
+
return {
|
|
793
|
+
type: ValueType.number,
|
|
794
|
+
value: numeric[index - 1],
|
|
795
|
+
};
|
|
796
|
+
}
|
|
797
|
+
|
|
798
|
+
return ArgumentError();
|
|
799
|
+
|
|
800
|
+
}),
|
|
801
|
+
},
|
|
802
|
+
|
|
732
803
|
/**
|
|
733
804
|
* sort arguments, but ensure we return empty strings to
|
|
734
805
|
* fill up the result array
|
|
@@ -193,22 +193,52 @@ export const ApplyArrayFunc = (base: (...args: any[]) => any) => {
|
|
|
193
193
|
};
|
|
194
194
|
};
|
|
195
195
|
|
|
196
|
+
export const ApplyAsArraySwap = (base: (...args: any[]) => UnionValue) => {
|
|
197
|
+
return (...args: any[]): UnionValue => {
|
|
198
|
+
|
|
199
|
+
// swap here
|
|
200
|
+
args.reverse();
|
|
201
|
+
const [a, ...rest] = args;
|
|
202
|
+
|
|
203
|
+
if (Array.isArray(a)) {
|
|
204
|
+
return {
|
|
205
|
+
type: ValueType.array,
|
|
206
|
+
value: a.map(row => row.map((element: any) => {
|
|
207
|
+
|
|
208
|
+
// swap back
|
|
209
|
+
const swapped = [...rest, element];
|
|
210
|
+
return base(...swapped);
|
|
211
|
+
|
|
212
|
+
})),
|
|
213
|
+
};
|
|
214
|
+
}
|
|
215
|
+
else if (typeof a === 'object' && !!a && a.type === ValueType.array ) {
|
|
216
|
+
return {
|
|
217
|
+
type: ValueType.array,
|
|
218
|
+
value: (a as ArrayUnion).value.map(row => row.map((element: any) => {
|
|
219
|
+
|
|
220
|
+
const swapped = [...rest, element];
|
|
221
|
+
return base(...swapped);
|
|
222
|
+
|
|
223
|
+
})),
|
|
224
|
+
};
|
|
225
|
+
|
|
226
|
+
}
|
|
227
|
+
else {
|
|
228
|
+
return base(...rest, a);
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
};
|
|
232
|
+
|
|
196
233
|
export const ApplyAsArray = (base: (a: any, ...rest: any[]) => UnionValue) => {
|
|
197
234
|
return (a: any, ...rest: any[]): UnionValue => {
|
|
198
235
|
if (Array.isArray(a)) {
|
|
199
|
-
|
|
200
236
|
return {
|
|
201
237
|
type: ValueType.array,
|
|
202
238
|
value: a.map(row => row.map((element: any) => {
|
|
203
239
|
return base(element, ...rest);
|
|
204
240
|
})),
|
|
205
241
|
};
|
|
206
|
-
|
|
207
|
-
/*
|
|
208
|
-
return a.map(row => row.map((element: any) => {
|
|
209
|
-
return base(element, ...rest);
|
|
210
|
-
}));
|
|
211
|
-
*/
|
|
212
242
|
}
|
|
213
243
|
else if (typeof a === 'object' && !!a && a.type === ValueType.array ) {
|
|
214
244
|
return {
|
|
@@ -280,23 +310,6 @@ export const ApplyAsArray2 = (base: (a: any, b: any, ...rest: any[]) => UnionVal
|
|
|
280
310
|
};
|
|
281
311
|
}
|
|
282
312
|
|
|
283
|
-
/*
|
|
284
|
-
if (Array.isArray(a)) {
|
|
285
|
-
if (Array.isArray(b)) {
|
|
286
|
-
return a.map((row, i: number) => row.map((element: any, j: number) => {
|
|
287
|
-
return base(element, b[i][j], ...rest);
|
|
288
|
-
}));
|
|
289
|
-
}
|
|
290
|
-
else {
|
|
291
|
-
return a.map(row => row.map((element: any) => {
|
|
292
|
-
return base(element, b, ...rest);
|
|
293
|
-
}));
|
|
294
|
-
}
|
|
295
|
-
}
|
|
296
|
-
else {
|
|
297
|
-
return base(a, b, ...rest);
|
|
298
|
-
}
|
|
299
|
-
*/
|
|
300
313
|
return base(a, b, ...rest);
|
|
301
314
|
|
|
302
315
|
}
|
|
@@ -47,6 +47,11 @@
|
|
|
47
47
|
<button data-command="align-bottom" title="Align to bottom"></button>
|
|
48
48
|
</div>
|
|
49
49
|
|
|
50
|
+
<div class="group" wide indent-group>
|
|
51
|
+
<button data-command="outdent" title="Decrease indent"></button>
|
|
52
|
+
<button data-command="indent" title="Incrase indent"></button>
|
|
53
|
+
</div>
|
|
54
|
+
|
|
50
55
|
<div class="group">
|
|
51
56
|
<button data-command="wrap-text" title="Wrap text"></button>
|
|
52
57
|
<button data-command="merge-cells" data-id="merge" data-inactive-title="Merge cells" data-active-title="Unmerge cells"></button>
|
|
@@ -146,6 +146,7 @@ export class SpreadsheetConstructor<USER_DATA_TYPE = unknown> {
|
|
|
146
146
|
if (this.root) {
|
|
147
147
|
|
|
148
148
|
const names = this.root.getAttributeNames();
|
|
149
|
+
console.info({names});
|
|
149
150
|
|
|
150
151
|
for (let name of names) {
|
|
151
152
|
|
|
@@ -202,8 +203,7 @@ export class SpreadsheetConstructor<USER_DATA_TYPE = unknown> {
|
|
|
202
203
|
// attrtibute options are in kebab-case while our internal
|
|
203
204
|
// options are still in snake_case.
|
|
204
205
|
|
|
205
|
-
name
|
|
206
|
-
attribute_options[name] = this.CoerceAttributeValue(this.root.getAttribute(name));
|
|
206
|
+
attribute_options[name.replace(/-/g, '_')] = this.CoerceAttributeValue(this.root.getAttribute(name));
|
|
207
207
|
|
|
208
208
|
}
|
|
209
209
|
}
|
|
@@ -910,6 +910,9 @@ export class SpreadsheetConstructor<USER_DATA_TYPE = unknown> {
|
|
|
910
910
|
if (!sheet.options.file_menu) {
|
|
911
911
|
remove.push(toolbar.querySelector('[file-menu]'));
|
|
912
912
|
}
|
|
913
|
+
if (!sheet.options.indent_buttons) {
|
|
914
|
+
remove.push(toolbar.querySelector('[indent-group]'));
|
|
915
|
+
}
|
|
913
916
|
if (!sheet.options.font_scale) {
|
|
914
917
|
remove.push(toolbar.querySelector('[font-scale]'));
|
|
915
918
|
}
|