@trebco/treb 30.15.0 → 31.0.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/api-generator/api-generator.ts +3 -1
- package/api-generator/package.json +2 -1
- package/dist/treb-export-worker.mjs +2 -2
- package/dist/treb-spreadsheet.mjs +13 -13
- package/dist/treb.d.ts +19 -2
- package/package.json +8 -7
- package/treb-base-types/src/font-stack.ts +144 -0
- package/treb-base-types/src/style.ts +121 -11
- package/treb-base-types/src/theme.ts +53 -8
- package/treb-calculator/src/calculator.ts +13 -13
- package/treb-calculator/src/descriptors.ts +12 -4
- package/treb-calculator/src/expression-calculator.ts +17 -4
- package/treb-calculator/src/functions/base-functions.ts +57 -4
- package/treb-calculator/src/functions/statistics-functions.ts +9 -6
- package/treb-calculator/tsconfig.json +11 -0
- package/treb-charts/src/chart-functions.ts +41 -4
- package/treb-charts/src/chart-types.ts +20 -1
- package/treb-charts/src/chart-utils.ts +86 -9
- package/treb-charts/src/default-chart-renderer.ts +40 -1
- package/treb-charts/src/renderer.ts +3 -3
- package/treb-charts/style/charts.scss +7 -1
- package/treb-data-model/src/annotation.ts +6 -0
- package/treb-data-model/src/data_model.ts +14 -3
- package/treb-data-model/src/sheet.ts +57 -56
- package/treb-embed/markup/toolbar.html +15 -1
- package/treb-embed/src/custom-element/spreadsheet-constructor.ts +38 -0
- package/treb-embed/src/embedded-spreadsheet.ts +119 -29
- package/treb-embed/src/options.ts +3 -0
- package/treb-embed/src/selection-state.ts +1 -0
- package/treb-embed/src/toolbar-message.ts +6 -0
- package/treb-embed/src/types.ts +9 -0
- package/treb-embed/style/defaults.scss +12 -1
- package/treb-embed/style/font-stacks.scss +105 -0
- package/treb-embed/style/layout.scss +1 -0
- package/treb-embed/style/theme-defaults.scss +12 -2
- package/treb-embed/style/toolbar.scss +16 -0
- package/treb-grid/src/editors/overlay_editor.ts +36 -3
- package/treb-grid/src/layout/base_layout.ts +52 -37
- package/treb-grid/src/layout/grid_layout.ts +7 -0
- package/treb-grid/src/render/tile_renderer.ts +154 -148
- package/treb-grid/src/types/grid.ts +188 -54
- package/treb-grid/src/types/grid_events.ts +1 -1
- package/treb-grid/src/types/grid_options.ts +3 -0
- package/treb-grid/src/util/fontmetrics.ts +134 -0
- package/treb-parser/src/parser.ts +12 -3
- package/treb-utils/src/measurement.ts +2 -3
- package/tsproject.json +1 -1
- package/treb-calculator/modern.tsconfig.json +0 -11
- package/treb-grid/src/util/fontmetrics2.ts +0 -182
- package/treb-parser/src/parser.test.ts +0 -298
- /package/treb-embed/{modern.tsconfig.json → tsconfig.json} +0 -0
- /package/treb-export/{modern.tsconfig.json → tsconfig.json} +0 -0
|
@@ -41,6 +41,8 @@ import { CoerceComplex } from './function-utilities';
|
|
|
41
41
|
import type { UnitAddress, UnitRange } from 'treb-parser';
|
|
42
42
|
import { ConstructDate } from './date-utils';
|
|
43
43
|
|
|
44
|
+
// import type { CalculationContext } from '../descriptors';
|
|
45
|
+
|
|
44
46
|
/**
|
|
45
47
|
* BaseFunctionLibrary is a static object that has basic spreadsheet
|
|
46
48
|
* functions and associated metadata (there's also a list of aliases).
|
|
@@ -1253,10 +1255,35 @@ export const BaseFunctionLibrary: FunctionMap = {
|
|
|
1253
1255
|
},
|
|
1254
1256
|
*/
|
|
1255
1257
|
|
|
1256
|
-
|
|
1257
1258
|
Row: {
|
|
1258
1259
|
arguments: [{ name: 'reference', metadata: true }],
|
|
1259
|
-
fn: (ref
|
|
1260
|
+
fn: function(ref?: UnionValue): UnionValue {
|
|
1261
|
+
|
|
1262
|
+
if (!ref) {
|
|
1263
|
+
if (this?.area) {
|
|
1264
|
+
const value: UnionValue[][] = [];
|
|
1265
|
+
for (let c = this.area.start.column; c <= this.area.end.column; c++) {
|
|
1266
|
+
const col: UnionValue[] = [];
|
|
1267
|
+
for (let r = this.area.start.row; r <= this.area.end.row; r++) {
|
|
1268
|
+
col.push({
|
|
1269
|
+
type: ValueType.number,
|
|
1270
|
+
value: r + 1,
|
|
1271
|
+
});
|
|
1272
|
+
}
|
|
1273
|
+
value.push(col);
|
|
1274
|
+
}
|
|
1275
|
+
return {
|
|
1276
|
+
type: ValueType.array, value,
|
|
1277
|
+
}
|
|
1278
|
+
}
|
|
1279
|
+
else {
|
|
1280
|
+
return {
|
|
1281
|
+
type: ValueType.number,
|
|
1282
|
+
value: this ? this.address.row + 1 : -1,
|
|
1283
|
+
};
|
|
1284
|
+
}
|
|
1285
|
+
}
|
|
1286
|
+
|
|
1260
1287
|
if (ref.type === ValueType.array) {
|
|
1261
1288
|
const arr = ref.value;
|
|
1262
1289
|
const first = arr[0][0];
|
|
@@ -1283,7 +1310,33 @@ export const BaseFunctionLibrary: FunctionMap = {
|
|
|
1283
1310
|
|
|
1284
1311
|
Column: {
|
|
1285
1312
|
arguments: [{ name: 'reference', metadata: true }],
|
|
1286
|
-
fn: (ref
|
|
1313
|
+
fn: function(ref?: UnionValue): UnionValue {
|
|
1314
|
+
|
|
1315
|
+
if (!ref) {
|
|
1316
|
+
if (this?.area) {
|
|
1317
|
+
const value: UnionValue[][] = [];
|
|
1318
|
+
for (let c = this.area.start.column; c <= this.area.end.column; c++) {
|
|
1319
|
+
const col: UnionValue[] = [];
|
|
1320
|
+
for (let r = this.area.start.row; r <= this.area.end.row; r++) {
|
|
1321
|
+
col.push({
|
|
1322
|
+
type: ValueType.number,
|
|
1323
|
+
value: c + 1,
|
|
1324
|
+
});
|
|
1325
|
+
}
|
|
1326
|
+
value.push(col);
|
|
1327
|
+
}
|
|
1328
|
+
return {
|
|
1329
|
+
type: ValueType.array, value,
|
|
1330
|
+
}
|
|
1331
|
+
}
|
|
1332
|
+
else {
|
|
1333
|
+
return {
|
|
1334
|
+
type: ValueType.number,
|
|
1335
|
+
value: this ? this.address.column + 1 : -1,
|
|
1336
|
+
};
|
|
1337
|
+
}
|
|
1338
|
+
}
|
|
1339
|
+
|
|
1287
1340
|
if (ref.type === ValueType.array) {
|
|
1288
1341
|
const arr = ref.value;
|
|
1289
1342
|
const first = arr[0][0];
|
|
@@ -1307,7 +1360,7 @@ export const BaseFunctionLibrary: FunctionMap = {
|
|
|
1307
1360
|
return ArgumentError();
|
|
1308
1361
|
},
|
|
1309
1362
|
},
|
|
1310
|
-
|
|
1363
|
+
|
|
1311
1364
|
Choose: {
|
|
1312
1365
|
arguments: [
|
|
1313
1366
|
{ name: 'Selected index', },
|
|
@@ -409,6 +409,7 @@ export const StatisticsFunctionLibrary: FunctionMap = {
|
|
|
409
409
|
},
|
|
410
410
|
},
|
|
411
411
|
|
|
412
|
+
/* use alias instead
|
|
412
413
|
'NormsInv': {
|
|
413
414
|
|
|
414
415
|
description: 'Inverse of the normal cumulative distribution',
|
|
@@ -423,7 +424,8 @@ export const StatisticsFunctionLibrary: FunctionMap = {
|
|
|
423
424
|
}
|
|
424
425
|
}
|
|
425
426
|
},
|
|
426
|
-
|
|
427
|
+
*/
|
|
428
|
+
|
|
427
429
|
'Norm.Inv': {
|
|
428
430
|
description: 'Inverse of the normal cumulative distribution',
|
|
429
431
|
arguments: [
|
|
@@ -441,17 +443,15 @@ export const StatisticsFunctionLibrary: FunctionMap = {
|
|
|
441
443
|
},
|
|
442
444
|
|
|
443
445
|
'Norm.S.Inv': {
|
|
444
|
-
description: 'Inverse of the normal cumulative distribution',
|
|
446
|
+
description: 'Inverse of the standard normal cumulative distribution',
|
|
445
447
|
arguments: [
|
|
446
448
|
{name: 'probability'},
|
|
447
|
-
{name: 'mean', default: 0},
|
|
448
|
-
{name: 'standard deviation', default: 1},
|
|
449
449
|
],
|
|
450
450
|
xlfn: true,
|
|
451
|
-
fn: (q: number
|
|
451
|
+
fn: (q: number): UnionValue => {
|
|
452
452
|
return {
|
|
453
453
|
type: ValueType.number,
|
|
454
|
-
value: inverse_normal(q)
|
|
454
|
+
value: inverse_normal(q),
|
|
455
455
|
}
|
|
456
456
|
}
|
|
457
457
|
},
|
|
@@ -1073,4 +1073,7 @@ export const StatisticsFunctionAliases: {[index: string]: string} = {
|
|
|
1073
1073
|
'StDevPA': 'StDev.P',
|
|
1074
1074
|
'Var': 'Var.S',
|
|
1075
1075
|
'Quartile': 'Quartile.Inc',
|
|
1076
|
+
'NormSInv': 'Norm.S.Inv',
|
|
1077
|
+
'NormSDist': 'Norm.S.Dist',
|
|
1078
|
+
|
|
1076
1079
|
};
|
|
@@ -48,7 +48,7 @@ export type ChartFunction
|
|
|
48
48
|
| 'Box.Plot'
|
|
49
49
|
;
|
|
50
50
|
|
|
51
|
-
type SupportFunction = 'Group'|'Series';
|
|
51
|
+
type SupportFunction = 'Group'|'Series' ; // |'Scatter.Series';
|
|
52
52
|
|
|
53
53
|
/**
|
|
54
54
|
* chart functions for registration
|
|
@@ -87,20 +87,57 @@ export const ChartFunctions: Record<ChartFunction|SupportFunction, CompositeFunc
|
|
|
87
87
|
{ name: 'X', metadata: true, },
|
|
88
88
|
{ name: 'Y', metadata: true, },
|
|
89
89
|
{ name: 'Z', metadata: true, },
|
|
90
|
-
{ name: '
|
|
91
|
-
{ name: '
|
|
90
|
+
{ name: 'Index', },
|
|
91
|
+
{ name: 'Subtype', },
|
|
92
92
|
{ name: 'Labels', description: 'Labels for bubble charts only (atm)' },
|
|
93
|
+
{ name: 'Axis', description: `Series axis (scatter plot only)` },
|
|
93
94
|
],
|
|
94
95
|
fn: (...args: unknown[]) => {
|
|
95
96
|
return {
|
|
96
97
|
type: ValueType.object,
|
|
97
|
-
value:
|
|
98
|
+
value: {
|
|
99
|
+
label: args[0],
|
|
100
|
+
x: args[1],
|
|
101
|
+
y: args[2],
|
|
102
|
+
z: args[3],
|
|
103
|
+
index: args[4],
|
|
104
|
+
subtype: args[5],
|
|
105
|
+
data_labels: args[6],
|
|
106
|
+
axis: args[7],
|
|
107
|
+
},
|
|
98
108
|
key: 'series',
|
|
99
109
|
};
|
|
100
110
|
},
|
|
101
111
|
category: ['chart functions'],
|
|
102
112
|
},
|
|
103
113
|
|
|
114
|
+
/*
|
|
115
|
+
'Scatter.Series': {
|
|
116
|
+
arguments: [
|
|
117
|
+
{ name: 'Label' }, // , metadata: true, },
|
|
118
|
+
{ name: 'X', metadata: true, },
|
|
119
|
+
{ name: 'Y', metadata: true, },
|
|
120
|
+
{ name: 'index', },
|
|
121
|
+
{ name: 'subtype', },
|
|
122
|
+
{ name: 'axis', },
|
|
123
|
+
],
|
|
124
|
+
fn: (...args: unknown[]) => {
|
|
125
|
+
return {
|
|
126
|
+
type: ValueType.object,
|
|
127
|
+
value: {
|
|
128
|
+
label: args[0],
|
|
129
|
+
x: args[1],
|
|
130
|
+
y: args[2],
|
|
131
|
+
index: args[3],
|
|
132
|
+
subtype: args[4],
|
|
133
|
+
axis: args[5],
|
|
134
|
+
},
|
|
135
|
+
key: 'series',
|
|
136
|
+
};
|
|
137
|
+
},
|
|
138
|
+
},
|
|
139
|
+
*/
|
|
140
|
+
|
|
104
141
|
'Bar.Chart': {
|
|
105
142
|
arguments: [
|
|
106
143
|
{ name: 'Data', metadata: true, },
|
|
@@ -33,6 +33,18 @@ export interface ReferenceMetadata {
|
|
|
33
33
|
|
|
34
34
|
export interface ReferenceSeries extends ExtendedUnion {
|
|
35
35
|
key: 'series',
|
|
36
|
+
value: {
|
|
37
|
+
label?: string;
|
|
38
|
+
x?: UnionValue;
|
|
39
|
+
y?: UnionValue;
|
|
40
|
+
z?: UnionValue;
|
|
41
|
+
index?: number;
|
|
42
|
+
subtype?: string;
|
|
43
|
+
data_labels?: string[];
|
|
44
|
+
axis?: number;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/*
|
|
36
48
|
value: [
|
|
37
49
|
CellValue?, // { name: 'Label' }, // , metadata: true, },
|
|
38
50
|
UnionValue?, // { name: 'X', metadata: true, },
|
|
@@ -42,6 +54,8 @@ export interface ReferenceSeries extends ExtendedUnion {
|
|
|
42
54
|
CellValue?, // { name: 'subtype', },
|
|
43
55
|
CellValue?, // { name: 'Labels', description: 'Labels for bubble charts only (atm)' },
|
|
44
56
|
];
|
|
57
|
+
*/
|
|
58
|
+
|
|
45
59
|
}
|
|
46
60
|
|
|
47
61
|
export const IsMetadata = (value?: unknown): value is ExtendedUnion & { value: ReferenceMetadata } => {
|
|
@@ -54,7 +68,8 @@ export const IsMetadata = (value?: unknown): value is ExtendedUnion & { value: R
|
|
|
54
68
|
export const IsSeries = (value?: unknown): value is ReferenceSeries => {
|
|
55
69
|
return (!!value && (typeof value === 'object')
|
|
56
70
|
&& (value as ReferenceSeries).key === 'series'
|
|
57
|
-
&& Array.isArray((value as ReferenceSeries).value));
|
|
71
|
+
// && Array.isArray((value as ReferenceSeries).value));
|
|
72
|
+
&& (typeof (value as ReferenceSeries).value === 'object'));
|
|
58
73
|
};
|
|
59
74
|
|
|
60
75
|
export const IsArrayUnion = (value?: unknown): value is ArrayUnion => {
|
|
@@ -140,9 +155,11 @@ export interface ScatterData2 extends ChartDataBaseType {
|
|
|
140
155
|
|
|
141
156
|
x_scale: RangeScale;
|
|
142
157
|
y_scale: RangeScale;
|
|
158
|
+
y2_scale?: RangeScale;
|
|
143
159
|
|
|
144
160
|
x_labels?: string[];
|
|
145
161
|
y_labels?: string[];
|
|
162
|
+
y2_labels?: string[];
|
|
146
163
|
|
|
147
164
|
style?: 'plot'|'line'|'area';
|
|
148
165
|
|
|
@@ -311,8 +328,10 @@ export interface SeriesType {
|
|
|
311
328
|
subtype?: string;
|
|
312
329
|
x: SubSeries;
|
|
313
330
|
y: SubSeries;
|
|
331
|
+
y2?: SubSeries;
|
|
314
332
|
z?: SubSeries;
|
|
315
333
|
index?: number;
|
|
316
334
|
labels?: string[];
|
|
335
|
+
axis?: number;
|
|
317
336
|
}
|
|
318
337
|
|
|
@@ -61,8 +61,10 @@ export const ArrayMinMax = (data: number[]) => {
|
|
|
61
61
|
|
|
62
62
|
export const ReadSeries = (data: ReferenceSeries['value']): SeriesType => {
|
|
63
63
|
|
|
64
|
-
const
|
|
64
|
+
// const label, x, y, z, index, subtype, data_labels] = data;
|
|
65
65
|
|
|
66
|
+
const { label, x, y, z, index, subtype, data_labels, axis } = data;
|
|
67
|
+
|
|
66
68
|
// series type is (now)
|
|
67
69
|
//
|
|
68
70
|
// [0] label, string
|
|
@@ -79,6 +81,10 @@ export const ReadSeries = (data: ReferenceSeries['value']): SeriesType => {
|
|
|
79
81
|
y: { data: [] },
|
|
80
82
|
};
|
|
81
83
|
|
|
84
|
+
if (typeof axis === 'number') {
|
|
85
|
+
series.axis = axis;
|
|
86
|
+
}
|
|
87
|
+
|
|
82
88
|
if (typeof index === 'number') {
|
|
83
89
|
series.index = index;
|
|
84
90
|
}
|
|
@@ -431,10 +437,22 @@ export const CommonData = (series: SeriesType[], y_floor?: number, y_ceiling?: n
|
|
|
431
437
|
|
|
432
438
|
let x_format = '';
|
|
433
439
|
let y_format = '';
|
|
440
|
+
let y2_format = '';
|
|
434
441
|
|
|
435
442
|
for (const entry of series) {
|
|
436
|
-
if (entry.
|
|
437
|
-
|
|
443
|
+
if (entry.axis) {
|
|
444
|
+
if (entry.y.format && !y2_format) {
|
|
445
|
+
y2_format = entry.y.format;
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
else {
|
|
449
|
+
if (entry.y.format && !y_format) {
|
|
450
|
+
y_format = entry.y.format;
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
if (entry.x.format && !x_format) {
|
|
454
|
+
x_format = entry.x.format;
|
|
455
|
+
}
|
|
438
456
|
}
|
|
439
457
|
|
|
440
458
|
let legend: Array<{ label: string, index?: number }> | undefined; // string[]|undefined;
|
|
@@ -449,13 +467,25 @@ export const CommonData = (series: SeriesType[], y_floor?: number, y_ceiling?: n
|
|
|
449
467
|
let x_min = Math.min.apply(0, x.map(test => test.x.range?.min || 0));
|
|
450
468
|
let x_max = Math.max.apply(0, x.map(test => test.x.range?.max || 0));
|
|
451
469
|
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
470
|
+
const x1 = x.filter(test => !test.axis);
|
|
471
|
+
const x2 = x.filter(test => test.axis);
|
|
472
|
+
|
|
473
|
+
let y_min = Math.min.apply(0, x1.map(test => test.y.range?.min || 0));
|
|
474
|
+
let y_max = Math.max.apply(0, x1.map(test => test.y.range?.max || 0));
|
|
475
|
+
|
|
476
|
+
let y2_min = -1;
|
|
477
|
+
let y2_max = -1;
|
|
478
|
+
|
|
479
|
+
if (x2.length) {
|
|
480
|
+
y2_min = Math.min.apply(0, x2.map(test => test.y.range?.min || 0));
|
|
481
|
+
y2_max = Math.max.apply(0, x2.map(test => test.y.range?.max || 0));
|
|
482
|
+
}
|
|
455
483
|
|
|
456
484
|
// if there's z data (used for bubble size), adjust x/y min/max to
|
|
457
485
|
// account for the z size so bubbles are contained within the grid
|
|
458
486
|
|
|
487
|
+
// this can't be used with axis (atm)
|
|
488
|
+
|
|
459
489
|
for (const subseries of series) {
|
|
460
490
|
if (subseries.z) {
|
|
461
491
|
for (const [index, z] of subseries.z.data.entries()) {
|
|
@@ -496,9 +526,29 @@ export const CommonData = (series: SeriesType[], y_floor?: number, y_ceiling?: n
|
|
|
496
526
|
|
|
497
527
|
const x_scale = Util.Scale(x_min, x_max, 7);
|
|
498
528
|
const y_scale = Util.Scale(y_min, y_max, 7);
|
|
529
|
+
const y2_scale = Util.Scale(y2_min, y2_max, 7);
|
|
530
|
+
|
|
531
|
+
if (y2_min !== y2_max && y_scale && y2_scale) {
|
|
532
|
+
if (y_scale.count !== y2_scale.count) {
|
|
533
|
+
const max = Math.max(y_scale.count, y2_scale.count);
|
|
534
|
+
const target = y_scale.count < max ? y_scale : y2_scale;
|
|
535
|
+
for (let i = target.count; i < max; i += 2) {
|
|
536
|
+
// add high
|
|
537
|
+
target.max += target.step;
|
|
538
|
+
target.count++;
|
|
539
|
+
|
|
540
|
+
if (target.count < max) {
|
|
541
|
+
// add low
|
|
542
|
+
target.min -= target.step;
|
|
543
|
+
target.count++;
|
|
544
|
+
}
|
|
545
|
+
}
|
|
546
|
+
}
|
|
547
|
+
}
|
|
499
548
|
|
|
500
549
|
let x_labels: string[] | undefined;
|
|
501
550
|
let y_labels: string[] | undefined;
|
|
551
|
+
let y2_labels: string[] | undefined;
|
|
502
552
|
|
|
503
553
|
if (x_format) {
|
|
504
554
|
x_labels = [];
|
|
@@ -509,10 +559,13 @@ export const CommonData = (series: SeriesType[], y_floor?: number, y_ceiling?: n
|
|
|
509
559
|
}
|
|
510
560
|
|
|
511
561
|
if (!y_format && auto_number_format) {
|
|
512
|
-
// y_format = default_number_format;
|
|
513
562
|
y_format = AutoFormat(y_scale);
|
|
514
563
|
}
|
|
515
564
|
|
|
565
|
+
if (!y2_format && auto_number_format) {
|
|
566
|
+
y2_format = AutoFormat(y2_scale);
|
|
567
|
+
}
|
|
568
|
+
|
|
516
569
|
if (y_format) {
|
|
517
570
|
y_labels = [];
|
|
518
571
|
const format = NumberFormatCache.Get(y_format);
|
|
@@ -520,8 +573,20 @@ export const CommonData = (series: SeriesType[], y_floor?: number, y_ceiling?: n
|
|
|
520
573
|
y_labels.push(format.Format(y_scale.min + i * y_scale.step));
|
|
521
574
|
}
|
|
522
575
|
}
|
|
576
|
+
if (y2_format) {
|
|
577
|
+
y2_labels = [];
|
|
578
|
+
const format = NumberFormatCache.Get(y2_format);
|
|
579
|
+
for (let i = 0; i <= y2_scale.count; i++) {
|
|
580
|
+
y2_labels.push(format.Format(y2_scale.min + i * y2_scale.step));
|
|
581
|
+
}
|
|
582
|
+
}
|
|
523
583
|
|
|
524
|
-
|
|
584
|
+
const result: {
|
|
585
|
+
legend?: { label: string, index?: number }[],
|
|
586
|
+
x: { format: string, scale: RangeScale, labels?: string[] },
|
|
587
|
+
y: { format: string, scale: RangeScale, labels?: string[] },
|
|
588
|
+
y2?: { format: string, scale: RangeScale, labels?: string[] },
|
|
589
|
+
} = {
|
|
525
590
|
x: {
|
|
526
591
|
format: x_format,
|
|
527
592
|
scale: x_scale,
|
|
@@ -535,6 +600,16 @@ export const CommonData = (series: SeriesType[], y_floor?: number, y_ceiling?: n
|
|
|
535
600
|
legend,
|
|
536
601
|
};
|
|
537
602
|
|
|
603
|
+
if (y2_min !== y2_max) {
|
|
604
|
+
result.y2 = {
|
|
605
|
+
format: y2_format,
|
|
606
|
+
scale: y2_scale,
|
|
607
|
+
labels: y2_labels,
|
|
608
|
+
};
|
|
609
|
+
}
|
|
610
|
+
|
|
611
|
+
return result;
|
|
612
|
+
|
|
538
613
|
};
|
|
539
614
|
|
|
540
615
|
const ApplyLabels = (series_list: SeriesType[], pattern: string, category_labels?: string[]): void => {
|
|
@@ -859,7 +934,6 @@ export const CreateScatterChart = (args: [UnionValue, string, string], style: 'p
|
|
|
859
934
|
// transform).
|
|
860
935
|
|
|
861
936
|
const series: SeriesType[] = TransformSeriesData(args[0]);
|
|
862
|
-
|
|
863
937
|
const common = CommonData(series);
|
|
864
938
|
|
|
865
939
|
const title = args[1]?.toString() || undefined;
|
|
@@ -878,6 +952,9 @@ export const CreateScatterChart = (args: [UnionValue, string, string], style: 'p
|
|
|
878
952
|
y_scale: common.y.scale,
|
|
879
953
|
y_labels: common.y.labels,
|
|
880
954
|
|
|
955
|
+
y2_scale: common.y2?.scale,
|
|
956
|
+
y2_labels: common.y2?.labels,
|
|
957
|
+
|
|
881
958
|
lines: style === 'line', // true,
|
|
882
959
|
points: style === 'plot',
|
|
883
960
|
|
|
@@ -175,6 +175,44 @@ export class DefaultChartRenderer implements ChartRendererType {
|
|
|
175
175
|
|
|
176
176
|
}
|
|
177
177
|
|
|
178
|
+
if (chart_data.type === 'scatter2' && chart_data.y2_labels && chart_data.y2_labels.length && chart_data.y2_scale) {
|
|
179
|
+
|
|
180
|
+
const y2_labels: Array<{label: string; metrics: Metrics}> = [];
|
|
181
|
+
let max_width = 0;
|
|
182
|
+
let max_height = 0;
|
|
183
|
+
|
|
184
|
+
const scale = chart_data.y2_scale;
|
|
185
|
+
|
|
186
|
+
const count = scale.count + 1;
|
|
187
|
+
|
|
188
|
+
for (let i = 0; i < count; i++ ){
|
|
189
|
+
const metrics = this.renderer.MeasureText(chart_data.y2_labels[i], ['axis-label', 'y-axis-label']);
|
|
190
|
+
y2_labels.push({ label: chart_data.y2_labels[i], metrics });
|
|
191
|
+
max_width = Math.max(max_width, metrics.width);
|
|
192
|
+
max_height = Math.max(max_height, metrics.height);
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
/*
|
|
196
|
+
area.bottom = Math.round(area.bottom - max_height / 2);
|
|
197
|
+
area.top = Math.round(area.top + max_height / 2);
|
|
198
|
+
|
|
199
|
+
if (x_metrics.length) {
|
|
200
|
+
area.bottom -= (max_x_height + chart_margin.bottom);
|
|
201
|
+
}
|
|
202
|
+
if (x_metrics2.length) {
|
|
203
|
+
area.bottom -= (max_x_height2 + chart_margin.bottom);
|
|
204
|
+
}
|
|
205
|
+
if (extra_padding) {
|
|
206
|
+
area.bottom -= extra_padding;
|
|
207
|
+
}
|
|
208
|
+
*/
|
|
209
|
+
|
|
210
|
+
this.renderer.RenderYAxis(area, area.right - max_width, y2_labels, ['axis-label', 'y-axis-label'], 'left');
|
|
211
|
+
area.right -= (max_width + chart_margin.left);
|
|
212
|
+
// area.left += (max_width + chart_margin.left);
|
|
213
|
+
|
|
214
|
+
}
|
|
215
|
+
|
|
178
216
|
// now render x axis
|
|
179
217
|
|
|
180
218
|
if (x_metrics.length && chart_data.x_labels?.length) {
|
|
@@ -318,11 +356,12 @@ export class DefaultChartRenderer implements ChartRendererType {
|
|
|
318
356
|
}
|
|
319
357
|
|
|
320
358
|
const index = typeof series.index === 'number' ? series.index : i + 1;
|
|
359
|
+
|
|
321
360
|
this.renderer.RenderScatterSeries(area,
|
|
322
361
|
series.x.data,
|
|
323
362
|
series.y.data,
|
|
324
363
|
chart_data.x_scale,
|
|
325
|
-
chart_data.y_scale,
|
|
364
|
+
(series.axis && chart_data.y2_scale) ? chart_data.y2_scale : chart_data.y_scale,
|
|
326
365
|
lines,
|
|
327
366
|
points,
|
|
328
367
|
!!chart_data.filled,
|
|
@@ -577,11 +577,11 @@ export class ChartRenderer {
|
|
|
577
577
|
/**
|
|
578
578
|
* render y axis labels; skips over labels to prevent overlap
|
|
579
579
|
*/
|
|
580
|
-
public RenderYAxis(area: Area,
|
|
580
|
+
public RenderYAxis(area: Area, x: number,
|
|
581
581
|
labels: Array<{
|
|
582
582
|
label: string;
|
|
583
583
|
metrics: Metrics;
|
|
584
|
-
}>, classes?: string | string[]) {
|
|
584
|
+
}>, classes?: string | string[], align:'left'|'right' = 'right') {
|
|
585
585
|
|
|
586
586
|
const count = labels.length;
|
|
587
587
|
if (!count) return;
|
|
@@ -612,7 +612,7 @@ export class ChartRenderer {
|
|
|
612
612
|
for (let i = 0; i < count; i += increment) {
|
|
613
613
|
const label = labels[i];
|
|
614
614
|
const y = Math.round(area.bottom - step * i + label.metrics.height / 4);
|
|
615
|
-
this.RenderText(this.axis_group, label.label,
|
|
615
|
+
this.RenderText(this.axis_group, label.label, align, { x, y }, classes);
|
|
616
616
|
}
|
|
617
617
|
|
|
618
618
|
}
|
|
@@ -43,8 +43,14 @@
|
|
|
43
43
|
// this was getting lost by the new higher-specificity reset
|
|
44
44
|
background: var(--treb-chart-background, #fff);
|
|
45
45
|
|
|
46
|
-
}
|
|
46
|
+
}
|
|
47
47
|
|
|
48
|
+
/**
|
|
49
|
+
* new override allows inheriting fonts in annotations (at least for charts)
|
|
50
|
+
*/
|
|
51
|
+
.treb-main.treb-main .treb-inherit-font .treb-chart {
|
|
52
|
+
font-family: inherit;
|
|
53
|
+
}
|
|
48
54
|
|
|
49
55
|
/* container style */
|
|
50
56
|
.treb-chart {
|
|
@@ -117,6 +117,12 @@ export interface AnnotationDataBase {
|
|
|
117
117
|
/** the new layout, persisted and takes preference over the old one */
|
|
118
118
|
layout?: AnnotationLayout;
|
|
119
119
|
|
|
120
|
+
/**
|
|
121
|
+
* adding cell style as a convenient store for font stack; atm we are
|
|
122
|
+
* ignoring everything but the font_face attribute
|
|
123
|
+
*/
|
|
124
|
+
style?: CellStyle;
|
|
125
|
+
|
|
120
126
|
/**
|
|
121
127
|
* the old layout used rectangles, and we need to keep support for
|
|
122
128
|
* that. this is not the layout rectangle. this rectangle is just
|
|
@@ -435,8 +435,8 @@ export class DataModel {
|
|
|
435
435
|
}
|
|
436
436
|
|
|
437
437
|
/** wrapper method ensures it always returns an Area (instance, not interface) */
|
|
438
|
-
public ResolveArea(address: string|ICellAddress|IArea, active_sheet: Sheet): Area {
|
|
439
|
-
const resolved = this.ResolveAddress(address, active_sheet);
|
|
438
|
+
public ResolveArea(address: string|ICellAddress|IArea, active_sheet: Sheet, options?: { r1c1?: boolean }): Area {
|
|
439
|
+
const resolved = this.ResolveAddress(address, active_sheet, options);
|
|
440
440
|
return IsCellAddress(resolved) ? new Area(resolved) : new Area(resolved.start, resolved.end);
|
|
441
441
|
}
|
|
442
442
|
|
|
@@ -449,10 +449,21 @@ export class DataModel {
|
|
|
449
449
|
* Q: why are we not preserving absoute/relative? (...)
|
|
450
450
|
*
|
|
451
451
|
*/
|
|
452
|
-
public ResolveAddress(address: string|ICellAddress|IArea, active_sheet: Sheet): ICellAddress|IArea {
|
|
452
|
+
public ResolveAddress(address: string|ICellAddress|IArea, active_sheet: Sheet, options? : { r1c1?: boolean }): ICellAddress|IArea {
|
|
453
453
|
|
|
454
454
|
if (typeof address === 'string') {
|
|
455
|
+
|
|
456
|
+
if (options?.r1c1) {
|
|
457
|
+
this.parser.Save();
|
|
458
|
+
this.parser.flags.r1c1 = true;
|
|
459
|
+
}
|
|
460
|
+
|
|
455
461
|
const parse_result = this.parser.Parse(address);
|
|
462
|
+
|
|
463
|
+
if (options?.r1c1) {
|
|
464
|
+
this.parser.Restore();
|
|
465
|
+
}
|
|
466
|
+
|
|
456
467
|
if (parse_result.expression && parse_result.expression.type === 'address') {
|
|
457
468
|
this.ResolveSheetID(parse_result.expression, undefined, active_sheet);
|
|
458
469
|
return {
|