@qtsurfer/sveltecharts 0.3.5 → 0.4.3

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.
@@ -0,0 +1,699 @@
1
+ import {} from 'echarts';
2
+ import {} from './';
3
+ export class TimeSeriesChartBuilder {
4
+ ECharts;
5
+ builderConfig = {
6
+ externalManagerLegend: false
7
+ };
8
+ option = {};
9
+ yDimensions;
10
+ yDimensionNames;
11
+ _tsColumn = '_ts';
12
+ constructor(instance, builderConfig) {
13
+ this.ECharts = instance;
14
+ this.builderConfig = { ...this.builderConfig, ...builderConfig };
15
+ this.option.animation = false;
16
+ this.option.legend = this.builderConfig.externalManagerLegend
17
+ ? {
18
+ show: false,
19
+ selected: {}
20
+ }
21
+ : {
22
+ top: '5%',
23
+ selected: {}
24
+ };
25
+ this.option.grid = {
26
+ top: '10%',
27
+ left: '3%',
28
+ right: '4%',
29
+ bottom: '15%',
30
+ containLabel: true
31
+ };
32
+ this.option.dataZoom = [
33
+ {
34
+ type: 'inside',
35
+ filterMode: 'filter',
36
+ zoomOnMouseWheel: true,
37
+ moveOnMouseMove: true,
38
+ realtime: true,
39
+ start: 45,
40
+ end: 55
41
+ },
42
+ {
43
+ top: '86%',
44
+ left: '8%',
45
+ right: '8%',
46
+ bottom: '5%',
47
+ type: 'slider',
48
+ show: true,
49
+ filterMode: 'filter',
50
+ realtime: false
51
+ }
52
+ ];
53
+ this.option.tooltip = {
54
+ trigger: 'axis',
55
+ axisPointer: { type: 'cross' }
56
+ };
57
+ this.option.xAxis = {
58
+ type: 'time',
59
+ axisLine: { show: true }
60
+ };
61
+ this.option.yAxis = [
62
+ {
63
+ type: 'value',
64
+ scale: true,
65
+ splitLine: { show: false },
66
+ axisLine: { show: true, lineStyle: { type: 'dashed' } }
67
+ },
68
+ {
69
+ type: 'value',
70
+ scale: true,
71
+ splitLine: { show: false },
72
+ axisLine: { show: true, lineStyle: { type: 'dashed' } },
73
+ axisLabel: {
74
+ formatter: (value) => `${value.toFixed(2)}%`
75
+ },
76
+ name: '%'
77
+ }
78
+ ];
79
+ this.option.dataset = {
80
+ dimensions: [],
81
+ source: []
82
+ };
83
+ this.option.series = [];
84
+ }
85
+ /**
86
+ * Accepts data as rows: [timestamp, v1, v2, ...]
87
+ * Automatically generates line series for each value column (>=1).
88
+ */
89
+ setDataset(data, yDimensionsNames) {
90
+ if (!Array.isArray(data)) {
91
+ this.setDataByObjectSimple(data, yDimensionsNames);
92
+ }
93
+ else {
94
+ if (data.length < 2) {
95
+ throw new Error('Minimum data length is 2.');
96
+ }
97
+ if (this.isNumberArray(data)) {
98
+ if (!yDimensionsNames?.length) {
99
+ throw new Error('Requires yDimensionsNames. e.g. ["v1", "v2", "v3"]');
100
+ }
101
+ this.setDatasetByArray(data, yDimensionsNames);
102
+ }
103
+ else if (this.isRecordArray(data)) {
104
+ this.setDataByObject(data, yDimensionsNames);
105
+ }
106
+ else {
107
+ throw new Error('Data must be an array');
108
+ }
109
+ }
110
+ return this.build();
111
+ }
112
+ toggleLegend(column) {
113
+ if (!column || !this.ECharts)
114
+ return this;
115
+ const selected = this.getColumnsSelected();
116
+ selected[column] = !selected[column];
117
+ this.ECharts.dispatchAction({
118
+ type: 'legendToggleSelect',
119
+ name: column
120
+ });
121
+ return this;
122
+ }
123
+ goToZoom(start, end) {
124
+ this.ECharts.dispatchAction({
125
+ type: 'dataZoom',
126
+ dataZoomIndex: 0,
127
+ start,
128
+ end
129
+ });
130
+ return this;
131
+ }
132
+ /**
133
+ * data: [1658870400, 823, 95.8, ...]
134
+ * dimensionsNames: ['_ts', 'price', 'otherColumn', ...]
135
+ */
136
+ setDatasetByArray(data, dimensionsNames, xAxisName) {
137
+ // Build series based on number of columns (minus the time column).
138
+ const columns = Array.isArray(data) && data.length > 0 ? data[0].length : 0;
139
+ const totalCol = Math.max(0, columns);
140
+ if (totalCol !== dimensionsNames?.length) {
141
+ throw new Error(`Dimensions length ${dimensionsNames?.length} does not match total columns ${totalCol}.`);
142
+ }
143
+ /**
144
+ * First column is the time dimension.
145
+ * ------
146
+ * _ts |
147
+ * ------
148
+ */
149
+ const timeDimensionKey = dimensionsNames.shift();
150
+ if (timeDimensionKey === undefined) {
151
+ throw new Error('No time dimension found.');
152
+ }
153
+ this._tsColumn = timeDimensionKey;
154
+ /**
155
+ * TimeDimensionName is the name of the time dimension.
156
+ */
157
+ const timeDimensionName = xAxisName || this._tsColumn;
158
+ /**
159
+ * YDimensions are the column names.
160
+ * --------------------------------------------------------
161
+ * Column 1 | Column 2 | Column 3 | Column 4 | Column 5
162
+ * --------------------------------------------------------
163
+ */
164
+ this.yDimensions = dimensionsNames;
165
+ this.yDimensionNames = dimensionsNames;
166
+ /**
167
+ * Dataset is an array of rows.
168
+ * --------------------------------------------------------------------------------
169
+ * Dimensions | TIME | Column 1 | Column 2 | Column 3 | Column 4 | Column 5 |
170
+ * --------------------------------------------------------------------------------
171
+ * Source | 1658870400 | 32.4 | 32.7 | 32.8 | 32.9 | 32.5 |
172
+ * --------------------------------------------------------------------------------
173
+ */
174
+ if (this.option.dataset && !Array.isArray(this.option.dataset)) {
175
+ this.option.dataset.dimensions = [timeDimensionKey, ...this.yDimensions];
176
+ this.option.dataset.source = data;
177
+ }
178
+ this.createSeriesData(this._tsColumn, timeDimensionName);
179
+ }
180
+ /**
181
+ * Data is an array of objects.
182
+ * [
183
+ * {_ts: 1658870400, price: 823, otherColumn: 95.8},
184
+ * {...}
185
+ * ]
186
+ */
187
+ setDataByObject(data, dimensionsNames) {
188
+ if (data.length < 2) {
189
+ throw new Error('Minimum data length is 2.');
190
+ }
191
+ // All dimensions are obtained based on the keys of the first element in the array.
192
+ // The first dimension, corresponding to time, is separated.
193
+ const dimensionKeys = Object.keys(data[0]);
194
+ const timeDimensionKey = dimensionKeys.shift();
195
+ if (timeDimensionKey === undefined) {
196
+ throw new Error('No time dimension found.');
197
+ }
198
+ this._tsColumn = timeDimensionKey;
199
+ // If custom dimension names are specified, those values will be used.
200
+ // By default, the dimensions will keep the same names as the original keys.
201
+ const timeDimensionName = dimensionsNames ? dimensionsNames.shift() : this._tsColumn;
202
+ /**
203
+ * `yDimensions` represents all data keys except the time dimension.
204
+ * -------------------
205
+ * price | otherColumn
206
+ * -------------------
207
+ */
208
+ this.yDimensions = dimensionKeys;
209
+ // If custom dimension names are specified, those values will be used.
210
+ // By default, the dimensions will keep the same names as the original keys.
211
+ this.yDimensionNames = dimensionsNames || dimensionKeys;
212
+ if (this.option.dataset && !Array.isArray(this.option.dataset)) {
213
+ this.option.dataset.dimensions = [this._tsColumn, ...this.yDimensions];
214
+ this.option.dataset.source = data;
215
+ }
216
+ this.createSeriesData(this._tsColumn, timeDimensionName);
217
+ }
218
+ setDataByObjectSimple(data, dimensionsNames) {
219
+ // All dimensions are obtained based on the keys of the first element in the array.
220
+ // The first dimension, corresponding to time, is separated.
221
+ const dimensionKeys = Object.keys(data);
222
+ const timeDimensionKey = dimensionKeys.shift();
223
+ if (timeDimensionKey === undefined) {
224
+ throw new Error('No time dimension found.');
225
+ }
226
+ this._tsColumn = timeDimensionKey;
227
+ // If custom dimension names are specified, those values will be used.
228
+ // By default, the dimensions will keep the same names as the original keys.
229
+ const timeDimensionName = dimensionsNames ? dimensionsNames.shift() : this._tsColumn;
230
+ /**
231
+ * `yDimensions` represents all data keys except the time dimension.
232
+ * -------------------
233
+ * price | otherColumn
234
+ * -------------------
235
+ */
236
+ this.yDimensions = dimensionKeys;
237
+ // If custom dimension names are specified, those values will be used.
238
+ // By default, the dimensions will keep the same names as the original keys.
239
+ this.yDimensionNames = dimensionsNames || dimensionKeys;
240
+ if (this.option.dataset && !Array.isArray(this.option.dataset)) {
241
+ this.option.dataset.dimensions = [this._tsColumn, ...this.yDimensions];
242
+ this.option.dataset.source = data;
243
+ }
244
+ this.createSeriesData(this._tsColumn, timeDimensionName);
245
+ }
246
+ addDimension(data, dimName) {
247
+ this.yDimensions.push(dimName);
248
+ this.yDimensionNames?.push(dimName);
249
+ if (this.option.dataset && !Array.isArray(this.option.dataset) && this.option.dataset.source) {
250
+ this.option.dataset.dimensions?.push(dimName);
251
+ Object.assign(this.option.dataset.source, data);
252
+ }
253
+ Object.keys(data).forEach((key) => this.addSeries(key, dimName, true));
254
+ this.build();
255
+ return this;
256
+ }
257
+ getColumnsSelected() {
258
+ const selected = this.option.legend.selected;
259
+ return selected;
260
+ }
261
+ addSeries(dim, dimName, isSelected) {
262
+ const percentageFields = this.detectPercentageFields();
263
+ const isPercentage = percentageFields.includes(dim);
264
+ const selected = this.getColumnsSelected();
265
+ Object.assign(selected, { [dimName]: isSelected });
266
+ const series = this.option.series;
267
+ series.push({
268
+ type: 'line',
269
+ animation: false,
270
+ id: dim,
271
+ name: dimName,
272
+ encode: { x: this._tsColumn, y: dim },
273
+ emphasis: {
274
+ focus: 'none',
275
+ disabled: true
276
+ },
277
+ connectNulls: false,
278
+ smooth: false,
279
+ sampling: 'lttb',
280
+ showSymbol: false,
281
+ progressive: 4000,
282
+ progressiveThreshold: 3000,
283
+ progressiveChunkMode: 'mod',
284
+ silent: true,
285
+ clip: true,
286
+ lineStyle: { width: 1 },
287
+ yAxisIndex: isPercentage ? 1 : 0,
288
+ label: {
289
+ show: true,
290
+ backgroundColor: '#000000ff',
291
+ color: '#fff',
292
+ fontSize: 10,
293
+ fontWeight: 'bold',
294
+ borderRadius: 3,
295
+ padding: [5, 5, 5, 5],
296
+ position: 'inside',
297
+ formatter(params) {
298
+ if (!params.seriesId || !params.data)
299
+ return '';
300
+ const value = params.data;
301
+ if (value[params.seriesId]) {
302
+ return `${value[params.seriesId].toFixed(2)}${isPercentage ? '%' : ''}`;
303
+ }
304
+ const idx = params.componentIndex + 1;
305
+ if (value[idx]) {
306
+ return `${value[idx].toFixed(2)}${isPercentage ? '%' : ''}`;
307
+ }
308
+ return '-';
309
+ }
310
+ }
311
+ });
312
+ }
313
+ /**
314
+ * Tooltip bound to axis with a crosshair pointer.
315
+ */
316
+ setAxisTooltip() {
317
+ this.option.tooltip = {
318
+ ...this.option.tooltip
319
+ };
320
+ return this;
321
+ }
322
+ /**
323
+ * Legend with a custom icon (e.g., 'circle', 'rect').
324
+ */
325
+ setLegendIcon(icon) {
326
+ this.option.legend = {
327
+ ...this.option.legend,
328
+ icon
329
+ };
330
+ return this;
331
+ }
332
+ /**
333
+ * Adds both inside and slider dataZoom.
334
+ */
335
+ setDataZoom(zoomOptions) {
336
+ this.option.dataZoom = zoomOptions;
337
+ return this;
338
+ }
339
+ setGrid(gridOption) {
340
+ this.option.grid = {
341
+ ...this.option.grid,
342
+ ...gridOption
343
+ };
344
+ return this;
345
+ }
346
+ /**
347
+ * Sets chart title and optional subtitle, centered.
348
+ */
349
+ setTitle(text, subtext) {
350
+ this.option.title = {
351
+ ...this.option.title,
352
+ text,
353
+ subtext
354
+ };
355
+ return this;
356
+ }
357
+ /**
358
+ * Applies a partial style to all existing series (e.g., { smooth: true, symbol: 'none' }).
359
+ */
360
+ setSeriesStyle(style) {
361
+ if (Array.isArray(this.option.series)) {
362
+ this.option.series = this.option.series.map((s) => ({
363
+ ...s,
364
+ ...style
365
+ }));
366
+ }
367
+ return this;
368
+ }
369
+ /**
370
+ * Adds a marker event to the chart.
371
+ */
372
+ addMarkerEvents(data, widthLine = 1) {
373
+ if (!Array.isArray(this.option.series)) {
374
+ throw new Error('Series must be an array');
375
+ }
376
+ for (const event of data) {
377
+ const position = event.position || 'aboveBar';
378
+ this.option.series.push({
379
+ type: 'line',
380
+ data: [],
381
+ markLine: {
382
+ symbol: this.getIcon(event.icon || 'none'),
383
+ symbolSize: [15, 15],
384
+ symbolOffset: [
385
+ [0, 15],
386
+ [0, 15]
387
+ ],
388
+ label: {
389
+ position: position === 'aboveBar' ? 'insideEnd' : 'insideStart',
390
+ offset: position === 'aboveBar' ? [-35, 0] : [35, 0],
391
+ distance: 0,
392
+ color: 'white',
393
+ formatter: event.name || '',
394
+ fontSize: 12,
395
+ fontFamily: 'Arial',
396
+ fontStyle: 'normal',
397
+ padding: 8,
398
+ backgroundColor: event.name ? (event.color?.toString() ?? 'white') : undefined,
399
+ borderRadius: 4
400
+ },
401
+ emphasis: {
402
+ disabled: true
403
+ },
404
+ lineStyle: {
405
+ color: event.color,
406
+ width: widthLine,
407
+ type: 'dashed'
408
+ },
409
+ data: event.xAxis.map((x) => ({ xAxis: x }))
410
+ }
411
+ });
412
+ }
413
+ return this.build();
414
+ }
415
+ /**
416
+ * Adds a marker area event to the chart.
417
+ */
418
+ addMarkArea(data) {
419
+ if (!Array.isArray(this.option.series)) {
420
+ throw new Error('Series must be an array');
421
+ }
422
+ for (const event of data) {
423
+ this.option.series.push({
424
+ type: 'line',
425
+ data: [],
426
+ markArea: {
427
+ itemStyle: {
428
+ color: event.color || 'rgba(0, 17, 255, 0.1)'
429
+ },
430
+ label: {
431
+ position: 'top',
432
+ formatter: event.name || '',
433
+ fontWeight: 'bold',
434
+ fontSize: 11
435
+ },
436
+ data: [
437
+ [
438
+ {
439
+ xAxis: event.xAxis[0]
440
+ },
441
+ {
442
+ xAxis: event.xAxis[1]
443
+ }
444
+ ]
445
+ ]
446
+ }
447
+ });
448
+ }
449
+ this.addMarkerEvents(data.map((e) => ({ ...e, name: undefined })), 1);
450
+ return this.build();
451
+ }
452
+ getIcon(icon) {
453
+ const arrowUpPath = 'path://M7.414 27.414l16.586-16.586v7.172c0 1.105 0.895 2 2 2s2-0.895 2-2v-12c0-0.809-0.487-1.538-1.235-1.848-0.248-0.103-0.508-0.151-0.765-0.151v-0.001h-12c-1.105 0-2 0.895-2 2s0.895 2 2 2h7.172l-16.586 16.586c-0.391 0.39-0.586 0.902-0.586 1.414s0.195 1.024 0.586 1.414c0.781 0.781 2.047 0.781 2.828 0z';
454
+ const arrowDownPath = 'path://M4.586 7.414l16.586 16.586h-7.171c-1.105 0-2 0.895-2 2s0.895 2 2 2h12c0.809 0 1.538-0.487 1.848-1.235 0.103-0.248 0.151-0.508 0.151-0.765h0.001v-12c0-1.105-0.895-2-2-2s-2 0.895-2 2v7.172l-16.586-16.586c-0.391-0.391-0.902-0.586-1.414-0.586s-1.024 0.195-1.414 0.586c-0.781 0.781-0.781 2.047 0 2.828z';
455
+ const circlePath = 'path://M16 0c-8.837 0-16 7.163-16 16s7.163 16 16 16 16-7.163 16-16-7.163-16-16-16zM16 28c-6.627 0-12-5.373-12-12s5.373-12 12-12c6.627 0 12 5.373 12 12s-5.373 12-12 12z';
456
+ if (icon === 'arrowDown') {
457
+ return arrowDownPath;
458
+ }
459
+ if (icon === 'arrowUp') {
460
+ return arrowUpPath;
461
+ }
462
+ if (icon === 'circle') {
463
+ return circlePath;
464
+ }
465
+ return icon;
466
+ }
467
+ addMarkerPoint(id, data, options) {
468
+ try {
469
+ const opt = {
470
+ icon: 'none',
471
+ position: 'inside',
472
+ symbolSize: 18,
473
+ color: 'black',
474
+ ...options
475
+ };
476
+ if (!Array.isArray(this.option.series)) {
477
+ throw new Error('Series must be an array');
478
+ }
479
+ if (Array.isArray(this.option.dataset)) {
480
+ throw new Error('Series must be an array');
481
+ }
482
+ // Search for the dimension
483
+ const seriesDimension = this.option.series
484
+ .filter((s) => s.encode && s.encode.y)
485
+ .find((s) => {
486
+ return s.encode.y === data.dimName;
487
+ });
488
+ if (!seriesDimension)
489
+ throw new Error(`Dimension ${data.dimName} not found`);
490
+ let value = this.searchValueByDimensionKeyAndTimestamp(data.dimName, data.timestamp);
491
+ /**
492
+ * Creates a data point for the marker
493
+ */
494
+ const dataPoint = () => {
495
+ return {
496
+ name: `markerpoint-${id}`,
497
+ coord: [data.timestamp, value],
498
+ symbol: this.getIcon(opt.icon),
499
+ symbolSize: opt.symbolSize,
500
+ symbolOffset: [0, -1 * (opt.symbolSize * 3)],
501
+ itemStyle: {
502
+ color: opt.color,
503
+ borderColor: opt.color,
504
+ borderWidth: 2
505
+ },
506
+ label: {
507
+ show: true,
508
+ offset: [0, 30],
509
+ formatter: data.name && Number(data.name)
510
+ ? Number(data.name).toFixed(2)
511
+ : (data.name ?? value.toFixed(2)),
512
+ fontSize: 12,
513
+ fontWeight: 'bold',
514
+ color: 'white',
515
+ backgroundColor: opt.color,
516
+ padding: 4,
517
+ borderRadius: 4
518
+ },
519
+ z: 11
520
+ };
521
+ };
522
+ // Create markPoint if it doesn't exist
523
+ if (!seriesDimension.markPoint) {
524
+ seriesDimension.markPoint = {
525
+ data: [dataPoint()]
526
+ };
527
+ }
528
+ else {
529
+ seriesDimension.markPoint.data.push(dataPoint());
530
+ }
531
+ }
532
+ catch (error) {
533
+ console.error(error.message);
534
+ }
535
+ return this;
536
+ }
537
+ isNumberArray(arr) {
538
+ return Array.isArray(arr[0]);
539
+ }
540
+ /**
541
+ * Creates the series data
542
+ */
543
+ createSeriesData(timeDimensionKey, timeDimensionName) {
544
+ if (!this.yDimensions?.length || !this.yDimensionNames?.length) {
545
+ throw new Error('No dimensions found.');
546
+ }
547
+ if (this.yDimensions.length !== this.yDimensionNames.length) {
548
+ throw new Error(`Dimensions length ${this.yDimensionNames.length} does not match total columns ${this.yDimensions.length}.`);
549
+ }
550
+ this.option.xAxis = { type: 'time', name: timeDimensionName };
551
+ this.yDimensions.map((dim, inx) => this.addSeries(dim, this.yDimensionNames[inx], (this.yDimensions.length > 1 && dim === 'price') || this.yDimensions.length === 1));
552
+ }
553
+ /**
554
+ * Search for the dimension key and timestamp
555
+ */
556
+ searchValueByDimensionKeyAndTimestamp(yDimKey, timestamp) {
557
+ const dataset = this.option.dataset;
558
+ if (!dataset.dimensions.find((d) => d === yDimKey)) {
559
+ throw new Error('No source data or dimensions found. Before loading data');
560
+ }
561
+ if (Array.isArray(dataset.source)) {
562
+ if (this.isNumberArray(dataset.source)) {
563
+ const dataFind = dataset.source.find((row) => {
564
+ return row[0] === timestamp;
565
+ });
566
+ if (!dataFind) {
567
+ throw new Error(`No data found in timestamp ${timestamp}`);
568
+ }
569
+ const yDimensionKey = dataset.dimensions.findIndex((d) => d === yDimKey);
570
+ return dataFind[yDimensionKey];
571
+ }
572
+ else if (this.isRecordArray(dataset.source)) {
573
+ const dataFind = dataset.source.find((row) => {
574
+ return row[this._tsColumn] === timestamp;
575
+ });
576
+ if (!dataFind) {
577
+ throw new Error(`No data found in timestamp ${timestamp}`);
578
+ }
579
+ return dataFind[yDimKey];
580
+ }
581
+ }
582
+ else {
583
+ const dataFind = dataset.source[this._tsColumn].indexOf(timestamp);
584
+ if (dataFind === -1) {
585
+ throw new Error(`No data found in timestamp ${timestamp}`);
586
+ }
587
+ return dataset.source[yDimKey][dataFind];
588
+ }
589
+ }
590
+ /**
591
+ * Return the percentage fields in the dataset
592
+ */
593
+ detectPercentageFields() {
594
+ if (!this.yDimensions?.length) {
595
+ throw new Error('No dimensions found.');
596
+ }
597
+ const percentFields = this.yDimensions.filter((key) => !key.startsWith('_') && key.endsWith('%'));
598
+ return percentFields;
599
+ }
600
+ build() {
601
+ const option = this.ECharts.getOption();
602
+ if (option &&
603
+ option.dataZoom &&
604
+ Array.isArray(option.dataZoom) &&
605
+ Array.isArray(this.option.dataZoom)) {
606
+ this.option.dataZoom[0].start = option.dataZoom[0].start;
607
+ this.option.dataZoom[0].end = option.dataZoom[0].end;
608
+ }
609
+ this.ECharts.setOption(this.option, {
610
+ lazyUpdate: true,
611
+ notMerge: false,
612
+ replaceMerge: ['dataset']
613
+ });
614
+ return this;
615
+ }
616
+ getDimensionKeys() {
617
+ return {
618
+ y: this.yDimensionNames,
619
+ x: this._tsColumn
620
+ };
621
+ }
622
+ getLegendStatus() {
623
+ return this.getColumnsSelected();
624
+ }
625
+ getTotalRows() {
626
+ const dataset = this.option.dataset;
627
+ if (Array.isArray(dataset.source)) {
628
+ return dataset.source.length;
629
+ }
630
+ else {
631
+ return dataset.source[this._tsColumn].length;
632
+ }
633
+ }
634
+ isSimpleObject(s) {
635
+ return !Array.isArray(s) && typeof s === 'object' && s !== null;
636
+ }
637
+ isRecordArray(source) {
638
+ return Array.isArray(source) && (source.length === 0 || !Array.isArray(source[0]));
639
+ }
640
+ isNumberMatrix(source) {
641
+ return Array.isArray(source) && (source.length === 0 || Array.isArray(source[0]));
642
+ }
643
+ getRangeValues() {
644
+ const dataset = this.option.dataset;
645
+ const source = dataset.source;
646
+ // ---- Record<string, any>[] ----
647
+ if (this.isRecordArray(source)) {
648
+ if (!source.length)
649
+ return [0, 0];
650
+ const objSource = source;
651
+ const firstRow = objSource[0];
652
+ const lastRow = objSource[objSource.length - 1];
653
+ const first = firstRow[this._tsColumn];
654
+ const last = lastRow[this._tsColumn];
655
+ return [first, last];
656
+ }
657
+ // ---- number[][] ----
658
+ if (this.isNumberMatrix(source)) {
659
+ if (!source.length)
660
+ return [0, 0];
661
+ const matrixSource = source;
662
+ const tsIndex = dataset.dimensions.indexOf(this._tsColumn);
663
+ const idx = tsIndex === -1 ? 0 : tsIndex;
664
+ const firstRow = matrixSource[0];
665
+ const lastRow = matrixSource[matrixSource.length - 1];
666
+ const first = firstRow[idx];
667
+ const last = lastRow[idx];
668
+ return [first, last];
669
+ }
670
+ // ---- Record<string, number[]> ----
671
+ if (this.isSimpleObject(source)) {
672
+ const col = source[this._tsColumn];
673
+ if (!col?.length)
674
+ return [0, 0];
675
+ return [col[0], col[col.length - 1]];
676
+ }
677
+ return [0, 0];
678
+ }
679
+ toggleMarkers(id, dimName, shape) {
680
+ if (!Array.isArray(this.option.series)) {
681
+ throw new Error('Series must be an array');
682
+ }
683
+ if (Array.isArray(this.option.dataset)) {
684
+ throw new Error('Series must be an array');
685
+ }
686
+ // Search for the dimension
687
+ const seriesDimension = this.option.series.find((s) => {
688
+ return s.encode && s.encode.y && s.encode.y === dimName;
689
+ });
690
+ const markerPoints = seriesDimension?.markPoint.data;
691
+ const point = markerPoints.find((mp) => mp.name === `markerpoint-${id}`);
692
+ if (!point) {
693
+ return;
694
+ }
695
+ point.symbol = point.symbol === 'none' ? this.getIcon(shape) : 'none';
696
+ this.build();
697
+ return this;
698
+ }
699
+ }
package/dist/index.d.ts CHANGED
@@ -1,3 +1,4 @@
1
1
  export { default as SVECharts } from './SVECharts.svelte';
2
+ export { TimeSeriesChartBuilder } from './TimeSeriesChartBuilder';
2
3
  export * from './types';
3
4
  //# sourceMappingURL=index.d.ts.map