@trebco/treb 28.10.0 → 28.11.1
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 +11 -11
- package/dist/treb-spreadsheet.mjs +15 -15
- package/dist/treb.d.ts +3 -1
- package/notes/connected-elements.md +37 -0
- package/package.json +1 -1
- package/treb-calculator/src/calculator.ts +49 -0
- package/treb-calculator/src/dag/graph.ts +1 -0
- package/treb-calculator/src/index.ts +1 -1
- package/treb-charts/src/chart-functions.ts +7 -4
- package/treb-charts/src/chart-types.ts +7 -1
- package/treb-charts/src/chart-utils.ts +130 -10
- package/treb-charts/src/default-chart-renderer.ts +13 -13
- package/treb-charts/src/renderer.ts +149 -26
- package/treb-charts/style/charts.scss +37 -1
- package/treb-embed/src/embedded-spreadsheet.ts +152 -5
- package/treb-export/src/drawing2/bubble-chart-template.ts +553 -0
- package/treb-export/src/drawing2/chart2.ts +84 -1
- package/treb-export/src/export2.ts +49 -10
- package/treb-export/src/import2.ts +21 -0
- package/treb-export/src/workbook2.ts +27 -4
- package/treb-grid/src/index.ts +1 -1
- package/treb-grid/src/types/data_model.ts +32 -0
- package/treb-grid/src/types/grid_base.ts +34 -0
- package/treb-parser/src/parser-types.ts +6 -0
- package/treb-parser/src/parser.ts +48 -1
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
|
|
22
22
|
import type { Size, Point } from './rectangle';
|
|
23
23
|
import { Area } from './rectangle';
|
|
24
|
-
import type { DonutSlice, LegendOptions} from './chart-types';
|
|
24
|
+
import type { DonutSlice, LegendOptions, SeriesType} from './chart-types';
|
|
25
25
|
import { LegendLayout, LegendPosition, LegendStyle } from './chart-types';
|
|
26
26
|
import type { RangeScale } from 'treb-utils';
|
|
27
27
|
|
|
@@ -196,14 +196,22 @@ export class ChartRenderer {
|
|
|
196
196
|
group.appendChild(SVGNode('text', {
|
|
197
197
|
'dominant-baseline': 'middle', x: x + marker_width, y, dy: (trident ? '.3em' : undefined) }, label.label));
|
|
198
198
|
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
199
|
+
switch (options.style) {
|
|
200
|
+
case LegendStyle.marker:
|
|
201
|
+
group.appendChild(SVGNode('rect', {
|
|
202
|
+
class: `series-${color}`, x, y: marker_y - 4, width: 8, height: 8 }));
|
|
203
|
+
break;
|
|
204
|
+
|
|
205
|
+
case LegendStyle.bubble:
|
|
206
|
+
group.appendChild(SVGNode('circle', {
|
|
207
|
+
class: `series-${color}`, cx: x + marker_width - 11, cy: marker_y - 4 + 3, /* r: '0.25em' */ }));
|
|
208
|
+
break;
|
|
209
|
+
|
|
210
|
+
default:
|
|
211
|
+
group.appendChild(SVGNode('rect', {
|
|
212
|
+
class: `series-${color}`, x, y: marker_y - 1, width: marker_width - 3, height: 2}));
|
|
213
|
+
break;
|
|
214
|
+
}
|
|
207
215
|
|
|
208
216
|
h = Math.max(h, text_metrrics.height);
|
|
209
217
|
x += text_metrrics.width + marker_width + padding;
|
|
@@ -960,23 +968,54 @@ export class ChartRenderer {
|
|
|
960
968
|
|
|
961
969
|
}
|
|
962
970
|
|
|
963
|
-
public RenderGrid(area: Area, y_count: number, x_count = 0, classes?: string | string[]): void {
|
|
971
|
+
public RenderGrid(area: Area, y_count: number, x_count = 0, classes?: string | string[], zeros?: number[]): void {
|
|
964
972
|
|
|
965
973
|
const d: string[] = [];
|
|
974
|
+
const d2: string[] = [];
|
|
966
975
|
|
|
967
976
|
let step = area.height / y_count;
|
|
968
977
|
for (let i = 0; i <= y_count; i++) {
|
|
978
|
+
|
|
969
979
|
const y = Math.round(area.top + step * i) - 0.5;
|
|
970
|
-
|
|
980
|
+
|
|
981
|
+
if (zeros && zeros[1] === i) {
|
|
982
|
+
d2.push(`M${area.left} ${y} L${area.right} ${y}`);
|
|
983
|
+
}
|
|
984
|
+
else {
|
|
985
|
+
d.push(`M${area.left} ${y} L${area.right} ${y}`);
|
|
986
|
+
}
|
|
971
987
|
}
|
|
972
988
|
|
|
973
989
|
step = area.width / (x_count - 1);
|
|
974
990
|
for (let i = 0; i < x_count; i++) {
|
|
991
|
+
|
|
975
992
|
const x = Math.round(area.left + step * i) - 0.5;
|
|
976
|
-
|
|
993
|
+
|
|
994
|
+
if (zeros && zeros[0] === i) {
|
|
995
|
+
d2.push(`M${x} ${area.top} L${x} ${area.bottom}`);
|
|
996
|
+
}
|
|
997
|
+
else {
|
|
998
|
+
d.push(`M${x} ${area.top} L${x} ${area.bottom}`);
|
|
999
|
+
}
|
|
977
1000
|
}
|
|
978
1001
|
|
|
979
1002
|
this.group.appendChild(SVGNode('path', {d, class: classes}));
|
|
1003
|
+
if (d2.length) {
|
|
1004
|
+
|
|
1005
|
+
if (classes) {
|
|
1006
|
+
if (!Array.isArray(classes)) {
|
|
1007
|
+
classes = classes + ' zero';
|
|
1008
|
+
}
|
|
1009
|
+
else {
|
|
1010
|
+
classes.push('zero');
|
|
1011
|
+
}
|
|
1012
|
+
}
|
|
1013
|
+
else {
|
|
1014
|
+
classes = 'zero';
|
|
1015
|
+
}
|
|
1016
|
+
|
|
1017
|
+
this.group.appendChild(SVGNode('path', {d: d2, class: classes}));
|
|
1018
|
+
}
|
|
980
1019
|
|
|
981
1020
|
}
|
|
982
1021
|
|
|
@@ -1193,23 +1232,24 @@ export class ChartRenderer {
|
|
|
1193
1232
|
|
|
1194
1233
|
}
|
|
1195
1234
|
|
|
1196
|
-
public RenderBubbleSeries(
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
z: Array<number | undefined>,
|
|
1200
|
-
c: any[] = [],
|
|
1235
|
+
public RenderBubbleSeries(
|
|
1236
|
+
area: Area,
|
|
1237
|
+
series: SeriesType,
|
|
1201
1238
|
x_scale: RangeScale,
|
|
1202
1239
|
y_scale: RangeScale,
|
|
1203
|
-
min = 10,
|
|
1204
|
-
max = 30,
|
|
1205
1240
|
classes?: string | string[]): void {
|
|
1206
1241
|
|
|
1207
|
-
const count = Math.max(x.length, y.length, z.length);
|
|
1208
1242
|
const xrange = (x_scale.max - x_scale.min) || 1;
|
|
1209
1243
|
const yrange = (y_scale.max - y_scale.min) || 1;
|
|
1210
1244
|
|
|
1211
1245
|
// const marker_elements: string[] = [];
|
|
1212
|
-
const points: Array<{x: number, y: number, z: number
|
|
1246
|
+
const points: Array<{x: number, y: number, z: number} | undefined> = [];
|
|
1247
|
+
const labels: Array<{
|
|
1248
|
+
x: number,
|
|
1249
|
+
y: number,
|
|
1250
|
+
text: string,
|
|
1251
|
+
offset: number,
|
|
1252
|
+
}> = [];
|
|
1213
1253
|
|
|
1214
1254
|
const d: string[] = [];
|
|
1215
1255
|
const areas: string[] = [];
|
|
@@ -1219,6 +1259,43 @@ export class ChartRenderer {
|
|
|
1219
1259
|
// if (title) node.setAttribute('title', title);
|
|
1220
1260
|
this.group.appendChild(group);
|
|
1221
1261
|
|
|
1262
|
+
if (series.z) {
|
|
1263
|
+
for (const [index, z] of series.z.data.entries()) {
|
|
1264
|
+
|
|
1265
|
+
const x = series.x.data[index];
|
|
1266
|
+
const y = series.y.data[index];
|
|
1267
|
+
|
|
1268
|
+
if (typeof x !== 'undefined' && typeof y !== 'undefined' && typeof z !== 'undefined' && z > 0) {
|
|
1269
|
+
|
|
1270
|
+
const size_x = z / xrange * area.width;
|
|
1271
|
+
const size_y = z / yrange * area.height;
|
|
1272
|
+
const size = Math.max(size_x, size_y);
|
|
1273
|
+
|
|
1274
|
+
const point: Point & { z: number } = {
|
|
1275
|
+
x: area.left + ((x - x_scale.min) / xrange) * area.width,
|
|
1276
|
+
y: area.bottom - ((y - y_scale.min) / yrange) * area.height,
|
|
1277
|
+
z: size,
|
|
1278
|
+
};
|
|
1279
|
+
|
|
1280
|
+
points.push(point);
|
|
1281
|
+
|
|
1282
|
+
if (series.labels?.[index]) {
|
|
1283
|
+
const r = point.z/2;
|
|
1284
|
+
labels.push({
|
|
1285
|
+
x: point.x, // + Math.cos(Math.PI/4) * r,
|
|
1286
|
+
y: point.y, // + Math.sin(Math.PI/4) * r,
|
|
1287
|
+
text: series.labels?.[index] || '',
|
|
1288
|
+
offset: Math.cos(Math.PI/4) * r,
|
|
1289
|
+
});
|
|
1290
|
+
}
|
|
1291
|
+
|
|
1292
|
+
}
|
|
1293
|
+
|
|
1294
|
+
}
|
|
1295
|
+
}
|
|
1296
|
+
|
|
1297
|
+
/*
|
|
1298
|
+
|
|
1222
1299
|
let z_min = z[0] || 0;
|
|
1223
1300
|
let z_max = z[0] || 0;
|
|
1224
1301
|
|
|
@@ -1262,13 +1339,59 @@ export class ChartRenderer {
|
|
|
1262
1339
|
|
|
1263
1340
|
}
|
|
1264
1341
|
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1342
|
+
*/
|
|
1343
|
+
|
|
1344
|
+
for (const point of points) {
|
|
1345
|
+
if (point) {
|
|
1346
|
+
group.appendChild(SVGNode('circle', {
|
|
1347
|
+
cx: point.x,
|
|
1348
|
+
cy: point.y,
|
|
1349
|
+
r: point.z / 2,
|
|
1350
|
+
class: `point`,
|
|
1351
|
+
}));
|
|
1270
1352
|
}
|
|
1353
|
+
}
|
|
1271
1354
|
|
|
1355
|
+
if (labels.length) {
|
|
1356
|
+
const container = this.label_group.getBoundingClientRect();
|
|
1357
|
+
|
|
1358
|
+
for (const entry of labels) {
|
|
1359
|
+
if (entry.text) {
|
|
1360
|
+
|
|
1361
|
+
const group = this.label_group.appendChild(SVGNode('g', {
|
|
1362
|
+
class: 'bubble-label',
|
|
1363
|
+
}));
|
|
1364
|
+
|
|
1365
|
+
const rect = group.appendChild(SVGNode('rect', {
|
|
1366
|
+
x: entry.x, // + entry.offset,
|
|
1367
|
+
y: entry.y, // + entry.offset,
|
|
1368
|
+
// rx: `3px`,
|
|
1369
|
+
// fill: 'Canvas',
|
|
1370
|
+
// 'fill-opacity': '60%',
|
|
1371
|
+
// stroke: `none`,
|
|
1372
|
+
// 'style': `--translate-offset: ${Math.round(entry.offset)}px`,
|
|
1373
|
+
class: 'label-background'
|
|
1374
|
+
}));
|
|
1375
|
+
|
|
1376
|
+
const label = group.appendChild(SVGNode('text', {
|
|
1377
|
+
x: entry.x, // + entry.offset,
|
|
1378
|
+
y: entry.y, // + entry.offset,
|
|
1379
|
+
offset: entry.offset,
|
|
1380
|
+
class: 'label-text',
|
|
1381
|
+
'text-anchor': 'middle',
|
|
1382
|
+
'alignment-baseline': 'middle',
|
|
1383
|
+
'style': `--translate-offset: ${Math.round(entry.offset)}px`,
|
|
1384
|
+
}, entry.text));
|
|
1385
|
+
|
|
1386
|
+
const bounds = label.getBoundingClientRect();
|
|
1387
|
+
|
|
1388
|
+
rect.setAttribute('x', (bounds.left - container.left - 2).toString());
|
|
1389
|
+
rect.setAttribute('y', (bounds.top - container.top - 1).toString());
|
|
1390
|
+
rect.style.height = (bounds.height + 2) + `px`;
|
|
1391
|
+
rect.style.width = (bounds.width + 4) + `px`;
|
|
1392
|
+
|
|
1393
|
+
}
|
|
1394
|
+
}
|
|
1272
1395
|
}
|
|
1273
1396
|
|
|
1274
1397
|
|
|
@@ -92,11 +92,23 @@
|
|
|
92
92
|
rect {
|
|
93
93
|
fill: currentColor;
|
|
94
94
|
}
|
|
95
|
+
circle {
|
|
96
|
+
fill: currentColor;
|
|
97
|
+
fill-opacity: .5;
|
|
98
|
+
stroke: currentColor;
|
|
99
|
+
stroke-width: 2px;
|
|
100
|
+
r: .25em;
|
|
101
|
+
}
|
|
95
102
|
}
|
|
96
103
|
|
|
97
104
|
/* grid */
|
|
98
105
|
.chart-grid, .chart-ticks {
|
|
99
106
|
stroke: var(--treb-chart-grid-color, #ddd);
|
|
107
|
+
|
|
108
|
+
&.zero {
|
|
109
|
+
stroke: var(--treb-chart-grid-zero-color, var(--treb-chart-grid-color, #999));
|
|
110
|
+
}
|
|
111
|
+
|
|
100
112
|
}
|
|
101
113
|
|
|
102
114
|
/* mouse elements */
|
|
@@ -166,7 +178,31 @@
|
|
|
166
178
|
stroke-width: 3;
|
|
167
179
|
fill: color-mix(in srgb, currentColor 75%, transparent);
|
|
168
180
|
stroke: currentColor;
|
|
169
|
-
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
.bubble-label {
|
|
184
|
+
|
|
185
|
+
.label-background {
|
|
186
|
+
stroke: none;
|
|
187
|
+
fill: none;
|
|
188
|
+
|
|
189
|
+
/*
|
|
190
|
+
fill: Canvas;
|
|
191
|
+
fill-opacity: .5;
|
|
192
|
+
rx: 2px;
|
|
193
|
+
*/
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
.label-text {
|
|
197
|
+
|
|
198
|
+
/**
|
|
199
|
+
* default translate to lower-right. you can calc() to switch
|
|
200
|
+
* to a different position.
|
|
201
|
+
*/
|
|
202
|
+
transform: translate(var(--translate-offset), var(--translate-offset));
|
|
203
|
+
|
|
204
|
+
}
|
|
205
|
+
|
|
170
206
|
}
|
|
171
207
|
|
|
172
208
|
/* scatter plot line (and marker -- change that class name) */
|
|
@@ -87,6 +87,10 @@ import type { BorderToolbarMessage, ToolbarMessage } from './toolbar-message';
|
|
|
87
87
|
import { Chart, ChartFunctions } from 'treb-charts';
|
|
88
88
|
import type { SetRangeOptions } from 'treb-grid';
|
|
89
89
|
|
|
90
|
+
import type { StateLeafVertex } from 'treb-calculator';
|
|
91
|
+
import type { ConnectedElementType } from 'treb-grid';
|
|
92
|
+
|
|
93
|
+
|
|
90
94
|
// --- worker ------------------------------------------------------------------
|
|
91
95
|
|
|
92
96
|
/**
|
|
@@ -95,7 +99,6 @@ import type { SetRangeOptions } from 'treb-grid';
|
|
|
95
99
|
* the script so we can run it as a worker.
|
|
96
100
|
*/
|
|
97
101
|
import * as export_worker_script from 'worker:../../treb-export/src/export-worker/index.worker';
|
|
98
|
-
import { StateLeafVertex } from 'treb-calculator/src/dag/state_leaf_vertex';
|
|
99
102
|
|
|
100
103
|
// --- types -------------------------------------------------------------------
|
|
101
104
|
|
|
@@ -2197,6 +2200,114 @@ export class EmbeddedSpreadsheet<USER_DATA_TYPE = unknown> {
|
|
|
2197
2200
|
|
|
2198
2201
|
}
|
|
2199
2202
|
|
|
2203
|
+
public RemoveConnectedChart(id: number) {
|
|
2204
|
+
const element = this.model.RemoveConnectedElement(id);
|
|
2205
|
+
if (element) {
|
|
2206
|
+
const removed = this.calculator.RemoveConnectedELement(element);
|
|
2207
|
+
if (removed) {
|
|
2208
|
+
// ... actually don't need to update here
|
|
2209
|
+
}
|
|
2210
|
+
}
|
|
2211
|
+
}
|
|
2212
|
+
|
|
2213
|
+
public UpdateConnectedChart(id: number, formula: string) {
|
|
2214
|
+
const element = this.model.connected_elements.get(id);
|
|
2215
|
+
if (element) {
|
|
2216
|
+
element.formula = formula;
|
|
2217
|
+
const internal = (element.internal) as { vertex: StateLeafVertex, state: any };
|
|
2218
|
+
|
|
2219
|
+
if (internal?.state) {
|
|
2220
|
+
internal.state = undefined;
|
|
2221
|
+
}
|
|
2222
|
+
this.calculator.UpdateConnectedElements(this.grid.active_sheet, element);
|
|
2223
|
+
|
|
2224
|
+
if (element.update) {
|
|
2225
|
+
element.update.call(0, element);
|
|
2226
|
+
}
|
|
2227
|
+
|
|
2228
|
+
}
|
|
2229
|
+
}
|
|
2230
|
+
|
|
2231
|
+
/**
|
|
2232
|
+
* @internal
|
|
2233
|
+
*
|
|
2234
|
+
* @returns an id that can be used to manage the reference
|
|
2235
|
+
*/
|
|
2236
|
+
public CreateConnectedChart(formula: string, target: HTMLElement, options: EvaluateOptions): number {
|
|
2237
|
+
|
|
2238
|
+
// FIXME: merge w/ insert annotation?
|
|
2239
|
+
|
|
2240
|
+
let r1c1 = options?.r1c1 || false;
|
|
2241
|
+
let argument_separator = options?.argument_separator || this.parser.argument_separator; // default to current
|
|
2242
|
+
|
|
2243
|
+
this.parser.Save();
|
|
2244
|
+
this.parser.flags.r1c1 = r1c1;
|
|
2245
|
+
|
|
2246
|
+
if (argument_separator === ',') {
|
|
2247
|
+
this.parser.argument_separator = ArgumentSeparatorType.Comma;
|
|
2248
|
+
this.parser.decimal_mark = DecimalMarkType.Period;
|
|
2249
|
+
}
|
|
2250
|
+
else {
|
|
2251
|
+
this.parser.argument_separator = ArgumentSeparatorType.Semicolon;
|
|
2252
|
+
this.parser.decimal_mark = DecimalMarkType.Comma;
|
|
2253
|
+
}
|
|
2254
|
+
|
|
2255
|
+
const result = this.parser.Parse(formula);
|
|
2256
|
+
|
|
2257
|
+
this.parser.Restore();
|
|
2258
|
+
|
|
2259
|
+
if (result.expression) {
|
|
2260
|
+
formula = '=' + this.parser.Render(result.expression, { missing: '' });
|
|
2261
|
+
}
|
|
2262
|
+
else {
|
|
2263
|
+
console.warn("invalid formula", result.error);
|
|
2264
|
+
}
|
|
2265
|
+
|
|
2266
|
+
const chart = this.CreateChart();
|
|
2267
|
+
chart.Initialize(target);
|
|
2268
|
+
|
|
2269
|
+
const id = this.model.AddConnectedElement({
|
|
2270
|
+
formula,
|
|
2271
|
+
|
|
2272
|
+
// this is circular, but I want to leave `this` bound to the sheet
|
|
2273
|
+
// instance in case we need it -- so what's a better approach? pass
|
|
2274
|
+
// in the formula explicitly, and update if we need to make changes?
|
|
2275
|
+
|
|
2276
|
+
update: (instance: ConnectedElementType) => {
|
|
2277
|
+
|
|
2278
|
+
const parse_result = this.parser.Parse(instance.formula);
|
|
2279
|
+
|
|
2280
|
+
if (parse_result &&
|
|
2281
|
+
parse_result.expression &&
|
|
2282
|
+
parse_result.expression.type === 'call') {
|
|
2283
|
+
|
|
2284
|
+
// FIXME: make a method for doing this
|
|
2285
|
+
|
|
2286
|
+
this.parser.Walk(parse_result.expression, (unit) => {
|
|
2287
|
+
if (unit.type === 'address' || unit.type === 'range') {
|
|
2288
|
+
this.model.ResolveSheetID(unit, undefined, this.grid.active_sheet);
|
|
2289
|
+
}
|
|
2290
|
+
return true;
|
|
2291
|
+
});
|
|
2292
|
+
|
|
2293
|
+
const expr_name = parse_result.expression.name.toLowerCase();
|
|
2294
|
+
const result = this.calculator.CalculateExpression(parse_result.expression);
|
|
2295
|
+
chart.Exec(expr_name, result as ExtendedUnion); // FIXME: type?
|
|
2296
|
+
}
|
|
2297
|
+
|
|
2298
|
+
chart.Update();
|
|
2299
|
+
|
|
2300
|
+
},
|
|
2301
|
+
|
|
2302
|
+
});
|
|
2303
|
+
|
|
2304
|
+
this.calculator.UpdateConnectedElements(this.grid.active_sheet);
|
|
2305
|
+
this.UpdateConnectedElements();
|
|
2306
|
+
|
|
2307
|
+
return id;
|
|
2308
|
+
|
|
2309
|
+
}
|
|
2310
|
+
|
|
2200
2311
|
/**
|
|
2201
2312
|
* Insert an annotation node. Usually this means inserting a chart. Regarding
|
|
2202
2313
|
* the argument separator, see the Evaluate function.
|
|
@@ -2226,11 +2337,21 @@ export class EmbeddedSpreadsheet<USER_DATA_TYPE = unknown> {
|
|
|
2226
2337
|
target = Rectangle.IsRectangle(rect) ? rect : this.model.ResolveArea(rect, this.grid.active_sheet);
|
|
2227
2338
|
}
|
|
2228
2339
|
|
|
2340
|
+
// FIXME: with the new parser save/restore semantics we should
|
|
2341
|
+
// just always do this. also I don't think the r1c1 logic works
|
|
2342
|
+
// properly here... unless we're assuming that the default state
|
|
2343
|
+
// is always off
|
|
2344
|
+
|
|
2229
2345
|
if (argument_separator && argument_separator !== this.parser.argument_separator || r1c1) {
|
|
2346
|
+
|
|
2347
|
+
this.parser.Save();
|
|
2348
|
+
|
|
2349
|
+
/*
|
|
2230
2350
|
const current = {
|
|
2231
2351
|
argument_separator: this.parser.argument_separator,
|
|
2232
2352
|
decimal_mark: this.parser.decimal_mark,
|
|
2233
2353
|
};
|
|
2354
|
+
*/
|
|
2234
2355
|
|
|
2235
2356
|
if (argument_separator === ',') {
|
|
2236
2357
|
this.parser.argument_separator = ArgumentSeparatorType.Comma;
|
|
@@ -2241,17 +2362,21 @@ export class EmbeddedSpreadsheet<USER_DATA_TYPE = unknown> {
|
|
|
2241
2362
|
this.parser.decimal_mark = DecimalMarkType.Comma;
|
|
2242
2363
|
}
|
|
2243
2364
|
|
|
2244
|
-
const r1c1_state = this.parser.flags.r1c1;
|
|
2245
|
-
if (r1c1)
|
|
2246
|
-
|
|
2247
|
-
|
|
2365
|
+
// const r1c1_state = this.parser.flags.r1c1;
|
|
2366
|
+
// if (r1c1)
|
|
2367
|
+
// {
|
|
2368
|
+
this.parser.flags.r1c1 = !!r1c1;
|
|
2369
|
+
// }
|
|
2248
2370
|
|
|
2249
2371
|
const result = this.parser.Parse(formula);
|
|
2250
2372
|
|
|
2373
|
+
/*
|
|
2251
2374
|
// reset
|
|
2252
2375
|
this.parser.argument_separator = current.argument_separator;
|
|
2253
2376
|
this.parser.decimal_mark = current.decimal_mark;
|
|
2254
2377
|
this.parser.flags.r1c1 = r1c1_state;
|
|
2378
|
+
*/
|
|
2379
|
+
this.parser.Restore();
|
|
2255
2380
|
|
|
2256
2381
|
if (result.expression) {
|
|
2257
2382
|
formula = '=' + this.parser.Render(result.expression, { missing: '' });
|
|
@@ -4856,6 +4981,7 @@ export class EmbeddedSpreadsheet<USER_DATA_TYPE = unknown> {
|
|
|
4856
4981
|
* (just sparklines atm) and update if necessary.
|
|
4857
4982
|
*/
|
|
4858
4983
|
protected UpdateAnnotations(): void {
|
|
4984
|
+
|
|
4859
4985
|
for (const annotation of this.grid.active_sheet.annotations) {
|
|
4860
4986
|
if (annotation.temp.vertex) {
|
|
4861
4987
|
const vertex = annotation.temp.vertex as StateLeafVertex;
|
|
@@ -4893,6 +5019,27 @@ export class EmbeddedSpreadsheet<USER_DATA_TYPE = unknown> {
|
|
|
4893
5019
|
}
|
|
4894
5020
|
|
|
4895
5021
|
}
|
|
5022
|
+
|
|
5023
|
+
this.UpdateConnectedElements();
|
|
5024
|
+
|
|
5025
|
+
}
|
|
5026
|
+
|
|
5027
|
+
protected UpdateConnectedElements() {
|
|
5028
|
+
for (const element of this.model.connected_elements.values()) {
|
|
5029
|
+
const internal = (element.internal) as { vertex: StateLeafVertex, state: any };
|
|
5030
|
+
if (internal?.vertex && internal.vertex.state_id !== internal.state ) {
|
|
5031
|
+
internal.state = internal.vertex.state_id;
|
|
5032
|
+
const fn = element.update;
|
|
5033
|
+
if (fn) {
|
|
5034
|
+
|
|
5035
|
+
// FIXME: what if there are multiple calls pending? some
|
|
5036
|
+
// kind of a dispatch system might be useful
|
|
5037
|
+
|
|
5038
|
+
Promise.resolve().then(() => fn.call(0, element));
|
|
5039
|
+
|
|
5040
|
+
}
|
|
5041
|
+
}
|
|
5042
|
+
}
|
|
4896
5043
|
}
|
|
4897
5044
|
|
|
4898
5045
|
/*
|