@trebco/treb 32.5.0 → 32.6.6

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@trebco/treb",
3
- "version": "32.5.0",
3
+ "version": "32.6.6",
4
4
  "license": "LGPL-3.0-or-later",
5
5
  "homepage": "https://treb.app",
6
6
  "repository": {
@@ -27,18 +27,22 @@ import type { Table } from './table';
27
27
  import type { DataValidation, AnnotationType, ConditionalFormat } from 'treb-data-model';
28
28
 
29
29
  export interface CellParseResult {
30
- row: number,
31
- column: number,
30
+ row: number;
31
+ column: number;
32
32
  type: SerializedValueType; // ValueType,
33
- value: number|string|undefined|boolean,
34
- calculated?: number|string|undefined|boolean,
33
+ value: number|string|undefined|boolean;
34
+ calculated?: number|string|undefined|boolean;
35
35
  calculated_type?: SerializedValueType; // ValueType,
36
- style_ref?: number,
37
- hyperlink?: string,
38
- validation?: DataValidation,
39
- merge_area?: IArea,
40
- area?: IArea,
41
- table?: Table,
36
+ style_ref?: number;
37
+ hyperlink?: string;
38
+ validation?: DataValidation;
39
+ merge_area?: IArea;
40
+ area?: IArea;
41
+
42
+ /** dynamic arrays */
43
+ spill?: IArea;
44
+
45
+ table?: Table;
42
46
  }
43
47
 
44
48
  export interface AnchoredAnnotation {
@@ -166,23 +166,22 @@ export enum ValueType {
166
166
  // OK we use it all the time now
167
167
  object = 5,
168
168
 
169
+ function = 6, // why was this inserted in the middle?
170
+
169
171
  // error is a STRING VALUE... object errors are layered on top? is that
170
172
  // correct? (...) it sort of makes sense... since we have separate typing
171
- error = 6,
173
+ error = 7,
172
174
 
173
175
  // complex is pretty stable by now
174
- complex = 7,
176
+ complex = 8,
175
177
 
176
178
  // this is new though. this is not a cell value, it's
177
179
  // only for union types. perhaps we should move or rename
178
180
  // this array, and then cells could have a subset?
179
- array = 8,
181
+ array = 9,
180
182
 
181
183
  // adding DQ to union
182
- dimensioned_quantity = 9,
183
-
184
- // new for lambdas
185
- function = 10,
184
+ dimensioned_quantity = 10,
186
185
 
187
186
  }
188
187
 
@@ -1221,11 +1221,16 @@ export class Calculator extends Graph {
1221
1221
  public AttachSpillData(area: Area, cells?: Cells) {
1222
1222
 
1223
1223
  if (!cells) {
1224
+
1225
+ // can we assume active sheet here? actually I guess not, we'll
1226
+ // need to set that...
1227
+
1224
1228
  const sheet = area.start.sheet_id ? this.model.sheets.Find(area.start.sheet_id) : undefined;
1225
1229
  cells = sheet?.cells;
1226
1230
  }
1227
1231
 
1228
1232
  if (!cells) {
1233
+ console.info({area, cells});
1229
1234
  throw new Error('invalid sheet ID in attach spill data');
1230
1235
  }
1231
1236
 
@@ -366,9 +366,9 @@ export const BaseFunctionLibrary: FunctionMap = {
366
366
 
367
367
  if (integer) {
368
368
  const range = max - min + 1;
369
- for (let i = 0; i < rows; i++) {
369
+ for (let i = 0; i < columns; i++) {
370
370
  const row: UnionValue[] = [];
371
- for (let j = 0; j < columns; j++) {
371
+ for (let j = 0; j < rows; j++) {
372
372
  row.push({
373
373
  type: ValueType.number,
374
374
  value: Math.floor(Math.random() * range + min),
@@ -380,9 +380,9 @@ export const BaseFunctionLibrary: FunctionMap = {
380
380
  else {
381
381
  const range = max - min;
382
382
 
383
- for (let i = 0; i < rows; i++) {
383
+ for (let i = 0; i < columns; i++) {
384
384
  const row: UnionValue[] = [];
385
- for (let j = 0; j < columns; j++) {
385
+ for (let j = 0; j < rows; j++) {
386
386
  row.push({
387
387
  type: ValueType.number,
388
388
  value: Math.random() * range + min,
@@ -391,7 +391,7 @@ export const BaseFunctionLibrary: FunctionMap = {
391
391
  value.push(row);
392
392
  }
393
393
  }
394
-
394
+
395
395
  return {
396
396
  type: ValueType.array,
397
397
  value,
@@ -486,7 +486,7 @@ export const StatisticsFunctionLibrary: FunctionMap = {
486
486
  'Norm.S.Inv': {
487
487
  description: 'Inverse of the standard normal cumulative distribution',
488
488
  arguments: [
489
- {name: 'probability'},
489
+ {name: 'probability', unroll: true },
490
490
  ],
491
491
  xlfn: true,
492
492
  fn: (q: number): UnionValue => {
@@ -207,6 +207,7 @@ export interface HistogramData extends ColumnDataBaseType {
207
207
  export interface LineBaseData extends ChartDataBaseType {
208
208
  series?: NumberOrUndefinedArray[];
209
209
  series2?: SeriesType[];
210
+ stacked_series?: SeriesType[];
210
211
  scale: RangeScale;
211
212
  x_scale?: RangeScale;
212
213
  titles?: string[];
@@ -262,6 +263,7 @@ export interface BarData extends LineBaseData {
262
263
  type: 'bar';
263
264
  round?: boolean;
264
265
  space?: number;
266
+ stacked?: boolean;
265
267
  }
266
268
 
267
269
  export interface DonutDataBaseType extends ChartDataBaseType {
@@ -1001,8 +1001,11 @@ export const CreateColumnChart = (
1001
1001
 
1002
1002
  const [data, labels, args_title, args_options] = args;
1003
1003
 
1004
- const series: SeriesType[] = TransformSeriesData(data);
1005
- const common = CommonData(series);
1004
+ const options = args_options?.toString() || undefined;
1005
+ const stacked = /stacked/i.test(options || '');
1006
+
1007
+ let series: SeriesType[] = TransformSeriesData(data);
1008
+ let common = CommonData(series);
1006
1009
 
1007
1010
  let category_labels: string[] | undefined;
1008
1011
 
@@ -1041,15 +1044,50 @@ export const CreateColumnChart = (
1041
1044
 
1042
1045
  }
1043
1046
 
1047
+ let stacked_series: SeriesType[]|undefined = undefined;
1048
+ const legend = common.legend; // in case we munge it
1049
+
1050
+ if (stacked) {
1051
+ stacked_series = series;
1052
+
1053
+ const map: Map<number, number> = new Map();
1054
+ // const label_map: Map<number, string> = new Map();
1055
+
1056
+ for (const entry of series) {
1057
+ for (const [index, key] of entry.x.data.entries()) {
1058
+ if (key !== undefined) {
1059
+ const value = entry.y.data[index] || 0;
1060
+ map.set(key, value + (map.get(key) || 0));
1061
+ }
1062
+ }
1063
+ }
1064
+
1065
+ const x_data = Array.from(map.keys()).sort((a, b) => a - b);
1066
+ const y_data = x_data.map(key => map.get(key) || 0);
1067
+
1068
+ // console.info({stacked_series, map, x_data, y_data});
1069
+
1070
+ series = [{
1071
+ x: { data: x_data, format: stacked_series[0]?.x?.format },
1072
+ y: { data: y_data, format: stacked_series[0]?.y?.format },
1073
+ }];
1074
+
1075
+ series[0].x.range = ArrayMinMax(x_data);
1076
+ series[0].y.range = ArrayMinMax(y_data);
1077
+
1078
+ common = CommonData(series, Math.min(0, ...y_data));
1079
+
1080
+ }
1081
+
1044
1082
  const title = args_title?.toString() || undefined;
1045
- const options = args_options?.toString() || undefined;
1046
1083
 
1047
1084
  const chart_data = {
1048
1085
  type,
1049
- legend: common.legend,
1086
+ legend, // legend: common.legend,
1050
1087
  // legend_position: LegendPosition.right,
1051
1088
  legend_style: LegendStyle.marker,
1052
1089
  series2: series,
1090
+ stacked_series,
1053
1091
  scale: common.y.scale,
1054
1092
  title,
1055
1093
  y_labels: type === 'bar' ? category_labels : common.y.labels, // swapped
@@ -1057,6 +1095,9 @@ export const CreateColumnChart = (
1057
1095
  };
1058
1096
 
1059
1097
  if (options) {
1098
+
1099
+ (chart_data as BarData).stacked = stacked;
1100
+
1060
1101
  (chart_data as BarData).round = /round/i.test(options);
1061
1102
  (chart_data as ChartDataBaseType).data_labels = /labels/i.test(options);
1062
1103
 
@@ -599,23 +599,33 @@ export class DefaultChartRenderer implements ChartRendererType {
599
599
  corners = [half_width, half_width, 0, 0];
600
600
  }
601
601
 
602
- for (let s = 0; s < series_count; s++) {
603
- const series = chart_data.series2[s];
604
- const color_index = typeof series.index === 'number' ? series.index : s + 1;
602
+ if (chart_data.stacked_series) {
605
603
 
606
- for (let i = 0; i < series.y.data.length; i++ ){
607
- const value = series.y.data[i];
608
- // const format = NumberFormatCache.Get(series.y.format || '0.00');
604
+ // step by group (stacked series)
605
+ for (let s = 0; s < chart_data.stacked_series[0].y.data.length; s++ ){
609
606
 
610
- if (typeof value === 'number') {
607
+ const x = (area.left + s * column_width + space) ; // + s * width;
608
+ let y = 0;
609
+
610
+ // now check each series, grab s-th value
611
+ for (let t = 0; t < chart_data.stacked_series.length; t++) {
612
+
613
+ const series = chart_data.stacked_series[t];
614
+ const color_index = typeof series.index === 'number' ? series.index : t + 1;
611
615
 
612
- // const x = Math.round(area.left + i * column_width + space) + s * width;
613
- const x = (area.left + i * column_width + space) + s * width;
614
-
615
616
  let height = 0;
616
- let y = 0;
617
617
  // let negative = false;
618
618
 
619
+ const value = (series.y.data[s] || 0);
620
+
621
+ if (t === 0) {
622
+ y = Math.min(chart_data.scale.min || 0, value);
623
+ }
624
+
625
+ height = Util.ApplyScale(value, area.height, chart_data.scale);
626
+ const base = area.bottom - height - Util.ApplyScale(y, area.height, chart_data.scale);
627
+
628
+ /*
619
629
  if (zero) {
620
630
  if (value > 0) {
621
631
  height = Util.ApplyScale(value + chart_data.scale.min, area.height, chart_data.scale);
@@ -631,27 +641,77 @@ export class DefaultChartRenderer implements ChartRendererType {
631
641
  height = Util.ApplyScale(value, area.height, chart_data.scale);
632
642
  y = area.bottom - height;
633
643
  }
644
+ */
634
645
 
635
- // const bar_title = chart_data.titles ? chart_data.titles[i] : undefined;
636
- const bar_title = undefined;
646
+ let bar_title = undefined;
647
+ let label = undefined;
648
+ let label_point = undefined;
637
649
 
638
- if (height) {
650
+ this.renderer.RenderRectangle(new Area(
651
+ x, base, x + width, base + height,
652
+ ), corners, ['chart-column', `series-${color_index}`], bar_title || undefined, label, label_point);
639
653
 
640
- const label = (chart_data.data_labels && !!series.y.labels) ? series.y.labels[i] : '';
641
- const label_point = {
642
- x: Math.round(x + width / 2),
643
- y: Math.round(y - 10),
644
- };
654
+ y += value;
645
655
 
646
- this.renderer.RenderRectangle(new Area(
647
- x, y, x + width, y + height,
648
- ), corners, ['chart-column', `series-${color_index}`], bar_title || undefined, label, label_point);
649
- }
650
656
  }
657
+
658
+
651
659
  }
660
+ }
661
+ else {
662
+ for (let s = 0; s < series_count; s++) {
663
+ const series = chart_data.series2[s];
664
+ const color_index = typeof series.index === 'number' ? series.index : s + 1;
665
+
666
+ for (let i = 0; i < series.y.data.length; i++ ){
667
+ const value = series.y.data[i];
668
+
669
+ if (typeof value === 'number') {
670
+
671
+ // const x = Math.round(area.left + i * column_width + space) + s * width;
672
+ const x = (area.left + i * column_width + space) + s * width;
673
+
674
+ let height = 0;
675
+ let y = 0;
676
+ // let negative = false;
677
+
678
+ if (zero) {
679
+ if (value > 0) {
680
+ height = Util.ApplyScale(value + chart_data.scale.min, area.height, chart_data.scale);
681
+ y = area.bottom - height - zero;
682
+ }
683
+ else {
684
+ height = Util.ApplyScale(chart_data.scale.min - value, area.height, chart_data.scale);
685
+ y = area.bottom - zero; // // area.bottom - height - zero;
686
+ // negative = true;
687
+ }
688
+ }
689
+ else {
690
+ height = Util.ApplyScale(value, area.height, chart_data.scale);
691
+ y = area.bottom - height;
692
+ }
693
+
694
+ // const bar_title = chart_data.titles ? chart_data.titles[i] : undefined;
695
+ const bar_title = undefined;
696
+
697
+ if (height) {
652
698
 
699
+ const label = (chart_data.data_labels && !!series.y.labels) ? series.y.labels[i] : '';
700
+ const label_point = {
701
+ x: Math.round(x + width / 2),
702
+ y: Math.round(y - 10),
703
+ };
704
+
705
+ this.renderer.RenderRectangle(new Area(
706
+ x, y, x + width, y + height,
707
+ ), corners, ['chart-column', `series-${color_index}`], bar_title || undefined, label, label_point);
708
+ }
709
+ }
710
+ }
711
+
712
+ }
653
713
  }
654
-
714
+
655
715
  }
656
716
 
657
717
  }
@@ -105,7 +105,14 @@ export class DataModel {
105
105
  }
106
106
 
107
107
  const sheet = this.sheets.ID(sheet_name);
108
- return this.named.Get_(parts[1], sheet || 0, true); // require scope in this case
108
+
109
+ // Q: why require scope in this case? if there _is_ a global name,
110
+ // and no scoped name, why not return that? that seems to be the
111
+ // behavior in excel, although I can't be sure
112
+
113
+ // test: default false
114
+
115
+ return this.named.Get_(parts[1], sheet || 0, false); // true); // require scope in this case
109
116
 
110
117
 
111
118
  }
@@ -201,14 +201,17 @@ export class NamedRangeManager {
201
201
  * that implies that if there are both, we'll prefer the scoped name.
202
202
  *
203
203
  * now possible to require scope, for qualified scoped names
204
+ *
205
+ * Q: why require scope? what's the benefit of that? (...)
206
+ *
204
207
  */
205
208
  public Get_(name: string, scope: number, require_scope = false) {
206
-
209
+
207
210
  if (require_scope) {
208
211
  return this.named.get(this.ScopedName(name, scope));
209
212
  }
210
213
 
211
- return this.named.get(this.ScopedName(name, scope)) || this.named.get(name.toLowerCase());
214
+ return this.named.get(this.ScopedName(name, scope)) ?? this.named.get(name.toLowerCase());
212
215
  }
213
216
 
214
217
  /**
@@ -3011,6 +3011,15 @@ export class Sheet {
3011
3011
  this.name = data.name || ''; // wtf is this?
3012
3012
  }
3013
3013
 
3014
+ // patching from import
3015
+ for (const cell of this.cells.Iterate()) {
3016
+ if (cell.spill) {
3017
+ if (!cell.spill.start.sheet_id) {
3018
+ cell.spill.SetSheetID(this.id);
3019
+ }
3020
+ }
3021
+ }
3022
+
3014
3023
  if (data.tab_color) {
3015
3024
  this.tab_color = data.tab_color;
3016
3025
  }
@@ -59,6 +59,12 @@
59
59
  margin: 0;
60
60
  overflow-x: scroll;
61
61
  overflow-y: hidden;
62
+
63
+ scrollbar-width: none; /* For Firefox */
64
+ &::-webkit-scrollbar {
65
+ display: none;
66
+ }
67
+
62
68
  }
63
69
 
64
70
  &[hidden] {