@trebco/treb 28.5.2 → 28.10.0
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 +15 -15
- package/dist/treb-spreadsheet.mjs +15 -15
- package/dist/treb.d.ts +37 -6
- package/package.json +1 -1
- package/treb-base-types/src/api_types.ts +1 -1
- package/treb-base-types/src/area.ts +1 -1
- package/treb-base-types/src/basic_types.ts +21 -21
- package/treb-base-types/src/cell.ts +1 -1
- package/treb-base-types/src/cells.ts +1 -1
- package/treb-base-types/src/color.ts +1 -1
- package/treb-base-types/src/dom-utilities.ts +1 -1
- package/treb-base-types/src/gradient.ts +1 -1
- package/treb-base-types/src/import.ts +1 -1
- package/treb-base-types/src/index-standalone.ts +9 -9
- package/treb-base-types/src/index.ts +1 -1
- package/treb-base-types/src/layout.ts +21 -21
- package/treb-base-types/src/localization.ts +27 -21
- package/treb-base-types/src/rectangle.ts +1 -1
- package/treb-base-types/src/render_text.ts +1 -1
- package/treb-base-types/src/style.ts +1 -1
- package/treb-base-types/src/table.ts +1 -1
- package/treb-base-types/src/text_part.ts +21 -21
- package/treb-base-types/src/theme.ts +1 -1
- package/treb-base-types/src/union.ts +1 -1
- package/treb-base-types/src/value-type.ts +1 -1
- package/treb-base-types/style/resizable.css +21 -21
- package/treb-calculator/src/calculator.ts +24 -31
- package/treb-calculator/src/complex-math.ts +1 -1
- package/treb-calculator/src/dag/array-vertex.ts +1 -1
- package/treb-calculator/src/dag/calculation_leaf_vertex.ts +8 -1
- package/treb-calculator/src/dag/graph.ts +8 -1
- package/treb-calculator/src/dag/spreadsheet_vertex.ts +1 -1
- package/treb-calculator/src/dag/spreadsheet_vertex_base.ts +21 -21
- package/treb-calculator/src/dag/state_leaf_vertex.ts +1 -1
- package/treb-calculator/src/dag/vertex.ts +21 -21
- package/treb-calculator/src/descriptors.ts +1 -1
- package/treb-calculator/src/expression-calculator.ts +1 -1
- package/treb-calculator/src/function-error.ts +1 -1
- package/treb-calculator/src/function-library.ts +1 -1
- package/treb-calculator/src/functions/base-functions.ts +31 -2
- package/treb-calculator/src/functions/checkbox.ts +1 -1
- package/treb-calculator/src/functions/complex-functions.ts +1 -1
- package/treb-calculator/src/functions/finance-functions.ts +1 -1
- package/treb-calculator/src/functions/information-functions.ts +1 -1
- package/treb-calculator/src/functions/matrix-functions.ts +1 -1
- package/treb-calculator/src/functions/sparkline.ts +1 -1
- package/treb-calculator/src/functions/statistics-functions.ts +1 -1
- package/treb-calculator/src/functions/text-functions.ts +1 -1
- package/treb-calculator/src/index.ts +1 -1
- package/treb-calculator/src/notifier-types.ts +1 -1
- package/treb-calculator/src/primitives.ts +1 -1
- package/treb-calculator/src/utilities.ts +1 -1
- package/treb-charts/src/chart-functions.ts +12 -1
- package/treb-charts/src/chart-types.ts +39 -21
- package/treb-charts/src/chart-utils.ts +783 -0
- package/treb-charts/src/chart.ts +96 -1291
- package/treb-charts/src/default-chart-renderer.ts +560 -0
- package/treb-charts/src/index.ts +5 -4
- package/treb-charts/src/main.ts +17 -17
- package/treb-charts/src/rectangle.ts +21 -21
- package/treb-charts/src/renderer-type.ts +32 -0
- package/treb-charts/src/renderer.ts +82 -1
- package/treb-charts/src/util.ts +1 -1
- package/treb-charts/style/charts.scss +9 -1
- package/treb-charts/style/old-charts.scss +21 -21
- package/treb-embed/markup/toolbar.html +35 -34
- package/treb-embed/src/custom-element/treb-global.ts +10 -2
- package/treb-embed/src/embedded-spreadsheet.ts +72 -113
- package/treb-embed/src/language-model.ts +1 -1
- package/treb-embed/src/options.ts +37 -1
- package/treb-embed/src/progress-dialog.ts +1 -1
- package/treb-embed/src/spinner.ts +1 -1
- package/treb-embed/src/types.ts +1 -1
- package/treb-embed/style/autocomplete.scss +1 -1
- package/treb-embed/style/dark-theme.scss +1 -1
- package/treb-embed/style/defaults.scss +1 -1
- package/treb-embed/style/dialog.scss +1 -1
- package/treb-embed/style/dropdown-select.scss +1 -1
- package/treb-embed/style/formula-bar.scss +1 -1
- package/treb-embed/style/grid.scss +1 -1
- package/treb-embed/style/layout.scss +4 -0
- package/treb-embed/style/mouse-mask.scss +1 -1
- package/treb-embed/style/note.scss +1 -1
- package/treb-embed/style/overlay-editor.scss +1 -1
- package/treb-embed/style/spinner.scss +1 -1
- package/treb-embed/style/tab-bar.scss +1 -1
- package/treb-embed/style/table.scss +1 -1
- package/treb-embed/style/theme-defaults.scss +1 -1
- package/treb-embed/style/toolbar.scss +37 -0
- package/treb-embed/style/tooltip.scss +1 -1
- package/treb-embed/style/z-index.scss +1 -1
- package/treb-export/src/address-type.ts +21 -21
- package/treb-export/src/base-template.ts +1 -1
- package/treb-export/src/column-width.ts +1 -1
- package/treb-export/src/drawing2/chart-template-components2.ts +1 -1
- package/treb-export/src/drawing2/chart2.ts +1 -1
- package/treb-export/src/drawing2/column-chart-template2.ts +1 -1
- package/treb-export/src/drawing2/donut-chart-template2.ts +1 -1
- package/treb-export/src/drawing2/drawing2.ts +1 -1
- package/treb-export/src/drawing2/embedded-image.ts +1 -1
- package/treb-export/src/drawing2/scatter-chart-template2.ts +1 -1
- package/treb-export/src/export-worker/export-worker.ts +1 -1
- package/treb-export/src/export-worker/index.worker.ts +1 -1
- package/treb-export/src/export2.ts +1 -1
- package/treb-export/src/import2.ts +1 -1
- package/treb-export/src/relationship.ts +1 -1
- package/treb-export/src/shared-strings2.ts +1 -1
- package/treb-export/src/template-2.ts +2 -2
- package/treb-export/src/workbook-sheet2.ts +1 -1
- package/treb-export/src/workbook-style2.ts +1 -1
- package/treb-export/src/workbook-theme2.ts +1 -1
- package/treb-export/src/workbook2.ts +1 -1
- package/treb-export/src/xml-utils.ts +1 -1
- package/treb-format/src/format.test.ts +21 -21
- package/treb-format/src/format.ts +1 -1
- package/treb-format/src/format_cache.ts +21 -21
- package/treb-format/src/format_parser.ts +1 -1
- package/treb-format/src/index.ts +4 -4
- package/treb-format/src/number_format_section.ts +21 -21
- package/treb-format/src/value_parser.ts +1 -1
- package/treb-grid/src/editors/autocomplete.ts +1 -1
- package/treb-grid/src/editors/autocomplete_matcher.ts +21 -21
- package/treb-grid/src/editors/editor.ts +1 -1
- package/treb-grid/src/editors/formula_bar.ts +1 -1
- package/treb-grid/src/editors/overlay_editor.ts +1 -1
- package/treb-grid/src/index.ts +1 -1
- package/treb-grid/src/layout/base_layout.ts +1 -1
- package/treb-grid/src/layout/grid_layout.ts +1 -1
- package/treb-grid/src/layout/rectangle_cache.ts +21 -21
- package/treb-grid/src/render/selection-renderer.ts +1 -1
- package/treb-grid/src/render/svg_header_overlay.ts +1 -1
- package/treb-grid/src/render/svg_selection_block.ts +1 -1
- package/treb-grid/src/render/tile_renderer.ts +1 -1
- package/treb-grid/src/types/annotation.ts +1 -1
- package/treb-grid/src/types/border_constants.ts +1 -1
- package/treb-grid/src/types/clipboard_data.ts +1 -1
- package/treb-grid/src/types/conditional_format.ts +1 -1
- package/treb-grid/src/types/data_model.ts +1 -1
- package/treb-grid/src/types/drag_mask.ts +21 -21
- package/treb-grid/src/types/grid.ts +12 -2
- package/treb-grid/src/types/grid_base.ts +128 -6
- package/treb-grid/src/types/grid_command.ts +33 -1
- package/treb-grid/src/types/grid_events.ts +8 -1
- package/treb-grid/src/types/grid_options.ts +9 -1
- package/treb-grid/src/types/grid_selection.ts +1 -1
- package/treb-grid/src/types/named_range.ts +1 -1
- package/treb-grid/src/types/scale-control.ts +1 -1
- package/treb-grid/src/types/serialize_options.ts +1 -1
- package/treb-grid/src/types/set_range_options.ts +1 -1
- package/treb-grid/src/types/sheet.ts +1 -57
- package/treb-grid/src/types/sheet_types.ts +1 -1
- package/treb-grid/src/types/tab_bar.ts +1 -1
- package/treb-grid/src/types/tile.ts +21 -21
- package/treb-grid/src/types/update_flags.ts +2 -1
- package/treb-grid/src/util/fontmetrics2.ts +1 -1
- package/treb-grid/src/util/ua.ts +21 -21
- package/treb-parser/src/csv-parser.ts +21 -21
- package/treb-parser/src/index.ts +5 -5
- package/treb-parser/src/md-parser.ts +1 -1
- package/treb-parser/src/parser-types.ts +1 -1
- package/treb-parser/src/parser.test.ts +21 -21
- package/treb-parser/src/parser.ts +1 -1
- package/treb-utils/src/event_source.ts +1 -1
- package/treb-utils/src/ievent_source.ts +13 -13
- package/treb-utils/src/index.ts +1 -1
- package/treb-utils/src/measurement.ts +1 -1
- package/treb-utils/src/scale.ts +21 -21
- package/treb-utils/src/serialize_html.ts +1 -1
|
@@ -0,0 +1,560 @@
|
|
|
1
|
+
|
|
2
|
+
import type { ChartRenderer as ChartRendererType } from './renderer-type';
|
|
3
|
+
|
|
4
|
+
import type { Metrics } from './renderer';
|
|
5
|
+
import { ChartRenderer } from './renderer';
|
|
6
|
+
import { Area } from './rectangle';
|
|
7
|
+
import { Util } from './util';
|
|
8
|
+
import type { ChartData } from './chart-types';
|
|
9
|
+
import { LegendLayout, LegendPosition } from './chart-types';
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* this class contains the original chart rendering functions, mapped
|
|
13
|
+
* to the new interface.
|
|
14
|
+
*/
|
|
15
|
+
export class DefaultChartRenderer implements ChartRendererType {
|
|
16
|
+
|
|
17
|
+
private renderer = new ChartRenderer();
|
|
18
|
+
|
|
19
|
+
// not chart-specific, so leave outside (FIXME: layout options?)
|
|
20
|
+
|
|
21
|
+
// FIXME: change depending on whether there are y-axis labels
|
|
22
|
+
// FIXME: different for donut charts...
|
|
23
|
+
|
|
24
|
+
private margin = { top: 0.025, left: 0.05, bottom: 0.025, right: 0.075 };
|
|
25
|
+
|
|
26
|
+
public Resize(target: HTMLElement, data: ChartData) {
|
|
27
|
+
this.renderer.Resize();
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
public Initialize(target: HTMLElement) {
|
|
31
|
+
this.renderer.Initialize(target);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* redraw
|
|
36
|
+
*/
|
|
37
|
+
public Update(target: HTMLElement, chart_data: ChartData) {
|
|
38
|
+
|
|
39
|
+
// reset
|
|
40
|
+
this.renderer.Resize(); // just too many problems
|
|
41
|
+
this.renderer.Prerender();
|
|
42
|
+
this.renderer.Clear(chart_data.class_name);
|
|
43
|
+
|
|
44
|
+
// get usable area [FIXME: method]
|
|
45
|
+
const area = new Area(0, 0, this.renderer.size.width, this.renderer.size.height);
|
|
46
|
+
|
|
47
|
+
// chart margin
|
|
48
|
+
const chart_margin = {
|
|
49
|
+
top: Math.round(area.height) * this.margin.top,
|
|
50
|
+
bottom: Math.round(area.height) * this.margin.bottom,
|
|
51
|
+
left: Math.round(area.width) * this.margin.left,
|
|
52
|
+
right: Math.round(area.width) * this.margin.right,
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
// title, top or bottom
|
|
56
|
+
const title = chart_data.title;
|
|
57
|
+
|
|
58
|
+
if (title) {
|
|
59
|
+
this.renderer.RenderTitle(title, area, chart_margin.top,
|
|
60
|
+
chart_data.title_layout||'top');
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// pad
|
|
64
|
+
area.top += chart_margin.top;
|
|
65
|
+
area.left += chart_margin.left;
|
|
66
|
+
area.bottom -= chart_margin.bottom;
|
|
67
|
+
area.right -= chart_margin.right;
|
|
68
|
+
|
|
69
|
+
if (chart_data.legend && chart_data.legend.length) {
|
|
70
|
+
|
|
71
|
+
let default_position = LegendPosition.top;
|
|
72
|
+
if (chart_data.title) {
|
|
73
|
+
if (!chart_data.title_layout || chart_data.title_layout === 'top') {
|
|
74
|
+
default_position = LegendPosition.bottom;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
const position = chart_data.legend_position || default_position;
|
|
79
|
+
|
|
80
|
+
this.renderer.Legend({
|
|
81
|
+
labels: chart_data.legend,
|
|
82
|
+
position,
|
|
83
|
+
style: chart_data.legend_style,
|
|
84
|
+
layout: (position === LegendPosition.top || position === LegendPosition.bottom) ?
|
|
85
|
+
LegendLayout.horizontal : LegendLayout.vertical,
|
|
86
|
+
area,
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
if (chart_data.type === 'histogram'
|
|
92
|
+
|| chart_data.type === 'line'
|
|
93
|
+
|| chart_data.type === 'area'
|
|
94
|
+
|| chart_data.type === 'column'
|
|
95
|
+
|| chart_data.type === 'histogram2'
|
|
96
|
+
|| chart_data.type === 'bar'
|
|
97
|
+
|| chart_data.type === 'scatter2'
|
|
98
|
+
|| chart_data.type === 'bubble'
|
|
99
|
+
) {
|
|
100
|
+
|
|
101
|
+
// we need to measure first, then lay out the other axis, then we
|
|
102
|
+
// can come back and render. it doesn't really matter which one you
|
|
103
|
+
// do first.
|
|
104
|
+
|
|
105
|
+
// measure x axis (height)
|
|
106
|
+
|
|
107
|
+
let x_metrics: Metrics[] = [];
|
|
108
|
+
let max_x_height = 0;
|
|
109
|
+
|
|
110
|
+
if (chart_data.x_labels && chart_data.x_labels.length) {
|
|
111
|
+
x_metrics = chart_data.x_labels.map((text) => {
|
|
112
|
+
const metrics = this.renderer.MeasureText(text, ['axis-label', 'x-axis-label'], true);
|
|
113
|
+
max_x_height = Math.max(max_x_height, metrics.height);
|
|
114
|
+
return metrics;
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// measure & render y axis
|
|
119
|
+
|
|
120
|
+
if (chart_data.y_labels && chart_data.y_labels.length) {
|
|
121
|
+
|
|
122
|
+
const y_labels: Array<{label: string; metrics: Metrics}> = [];
|
|
123
|
+
let max_width = 0;
|
|
124
|
+
let max_height = 0;
|
|
125
|
+
|
|
126
|
+
const scale = (chart_data.type === 'scatter2' || chart_data.type === 'bubble') ? chart_data.y_scale : chart_data.scale;
|
|
127
|
+
|
|
128
|
+
const count = (chart_data.type === 'bar') ?
|
|
129
|
+
chart_data.y_labels.length :
|
|
130
|
+
/* chart_data. */
|
|
131
|
+
scale.count + 1;
|
|
132
|
+
|
|
133
|
+
for (let i = 0; i < count; i++ ){
|
|
134
|
+
const metrics = this.renderer.MeasureText(chart_data.y_labels[i], ['axis-label', 'y-axis-label']);
|
|
135
|
+
y_labels.push({ label: chart_data.y_labels[i], metrics });
|
|
136
|
+
max_width = Math.max(max_width, metrics.width);
|
|
137
|
+
max_height = Math.max(max_height, metrics.height);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
area.bottom = Math.round(area.bottom - max_height / 2);
|
|
141
|
+
area.top = Math.round(area.top + max_height / 2);
|
|
142
|
+
|
|
143
|
+
if (x_metrics.length) {
|
|
144
|
+
area.bottom -= (max_x_height + chart_margin.bottom);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
if (chart_data.type === 'bar') {
|
|
148
|
+
this.renderer.RenderYAxisBar(area, area.left + max_width, y_labels, ['axis-label', 'y-axis-label']);
|
|
149
|
+
}
|
|
150
|
+
else {
|
|
151
|
+
this.renderer.RenderYAxis(area, area.left + max_width, y_labels, ['axis-label', 'y-axis-label']);
|
|
152
|
+
}
|
|
153
|
+
area.left += (max_width + chart_margin.left);
|
|
154
|
+
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// now render x axis
|
|
158
|
+
|
|
159
|
+
if (x_metrics.length && chart_data.x_labels && chart_data.x_labels.length) {
|
|
160
|
+
|
|
161
|
+
const tick = (chart_data.type === 'histogram2');
|
|
162
|
+
const offset_tick = (
|
|
163
|
+
chart_data.type !== 'line' &&
|
|
164
|
+
chart_data.type !== 'area' &&
|
|
165
|
+
chart_data.type !== 'bar' &&
|
|
166
|
+
chart_data.type !== 'scatter2' &&
|
|
167
|
+
chart_data.type !== 'bubble' &&
|
|
168
|
+
chart_data.type !== 'histogram2'
|
|
169
|
+
);
|
|
170
|
+
|
|
171
|
+
// do this before you fix the offset
|
|
172
|
+
|
|
173
|
+
if (tick) {
|
|
174
|
+
this.renderer.RenderXAxisTicks(area, offset_tick, chart_data.x_labels.length);
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
if (chart_data.y_labels) {
|
|
179
|
+
// undo, temp
|
|
180
|
+
area.bottom += (max_x_height + chart_margin.bottom);
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
// render
|
|
184
|
+
this.renderer.RenderXAxis(area,
|
|
185
|
+
offset_tick,
|
|
186
|
+
chart_data.x_labels,
|
|
187
|
+
x_metrics,
|
|
188
|
+
['axis-label', 'x-axis-label']);
|
|
189
|
+
|
|
190
|
+
// update bottom (either we unwound for labels, or we need to do it the first time)
|
|
191
|
+
area.bottom -= (max_x_height + chart_margin.bottom);
|
|
192
|
+
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
// now do type-specific rendering
|
|
198
|
+
|
|
199
|
+
switch (chart_data.type) {
|
|
200
|
+
case 'scatter':
|
|
201
|
+
this.renderer.RenderPoints(area, chart_data.x, chart_data.y, 'mc mc-correlation series-1');
|
|
202
|
+
break;
|
|
203
|
+
|
|
204
|
+
case 'bubble':
|
|
205
|
+
|
|
206
|
+
this.renderer.RenderGrid(area,
|
|
207
|
+
chart_data.y_scale.count,
|
|
208
|
+
chart_data.x_scale.count + 1, // (sigh)
|
|
209
|
+
'chart-grid');
|
|
210
|
+
|
|
211
|
+
if (chart_data.x && chart_data.y && chart_data.z) {
|
|
212
|
+
this.renderer.RenderBubbleSeries(area,
|
|
213
|
+
chart_data.x.data,
|
|
214
|
+
chart_data.y.data,
|
|
215
|
+
chart_data.z.data,
|
|
216
|
+
chart_data.c || [],
|
|
217
|
+
chart_data.x_scale,
|
|
218
|
+
chart_data.y_scale,
|
|
219
|
+
undefined,
|
|
220
|
+
undefined,
|
|
221
|
+
'bubble-chart',
|
|
222
|
+
);
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
break;
|
|
226
|
+
|
|
227
|
+
case 'scatter2':
|
|
228
|
+
|
|
229
|
+
this.renderer.RenderGrid(area,
|
|
230
|
+
chart_data.y_scale.count,
|
|
231
|
+
chart_data.x_scale.count + 1, // (sigh)
|
|
232
|
+
'chart-grid');
|
|
233
|
+
|
|
234
|
+
if (chart_data.series) {
|
|
235
|
+
for (let i = 0; i < chart_data.series.length; i++) {
|
|
236
|
+
const series = chart_data.series[i];
|
|
237
|
+
|
|
238
|
+
let lines = !!chart_data.lines;
|
|
239
|
+
let points = !!chart_data.points;
|
|
240
|
+
|
|
241
|
+
if (series.subtype === 'plot') {
|
|
242
|
+
points = true;
|
|
243
|
+
lines = false;
|
|
244
|
+
}
|
|
245
|
+
else if (series.subtype === 'line') {
|
|
246
|
+
points = false;
|
|
247
|
+
lines = true;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
const index = typeof series.index === 'number' ? series.index : i + 1;
|
|
251
|
+
this.renderer.RenderScatterSeries(area,
|
|
252
|
+
series.x.data,
|
|
253
|
+
series.y.data,
|
|
254
|
+
chart_data.x_scale,
|
|
255
|
+
chart_data.y_scale,
|
|
256
|
+
lines,
|
|
257
|
+
points,
|
|
258
|
+
!!chart_data.filled,
|
|
259
|
+
!!chart_data.markers,
|
|
260
|
+
!!chart_data.smooth,
|
|
261
|
+
`scatter-plot series-${index}`);
|
|
262
|
+
}
|
|
263
|
+
if (chart_data.data_labels) {
|
|
264
|
+
for (let i = 0; i < chart_data.series.length; i++) {
|
|
265
|
+
const series = chart_data.series[i];
|
|
266
|
+
if (series.y.labels) {
|
|
267
|
+
this.renderer.RenderDataLabels(
|
|
268
|
+
area,
|
|
269
|
+
series.x.data,
|
|
270
|
+
series.y.data,
|
|
271
|
+
chart_data.x_scale,
|
|
272
|
+
chart_data.y_scale,
|
|
273
|
+
series.y.labels,
|
|
274
|
+
i + 1);
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
break;
|
|
280
|
+
|
|
281
|
+
case 'pie':
|
|
282
|
+
case 'donut':
|
|
283
|
+
{
|
|
284
|
+
const outer = (Math.min(area.height, area.width) / 2) * .9;
|
|
285
|
+
const inner = chart_data.type === 'pie' ? 0 : outer * .8;
|
|
286
|
+
this.renderer.RenderDonut(chart_data.slices, area.center, outer, inner, area,
|
|
287
|
+
true, 'donut');
|
|
288
|
+
}
|
|
289
|
+
break;
|
|
290
|
+
|
|
291
|
+
case 'line':
|
|
292
|
+
case 'area':
|
|
293
|
+
{
|
|
294
|
+
const scale = chart_data.scale;
|
|
295
|
+
if (chart_data.series) {
|
|
296
|
+
|
|
297
|
+
const points = chart_data.x_scale ?
|
|
298
|
+
chart_data.x_scale.max :
|
|
299
|
+
Math.max.apply(0, chart_data.series.map(x => x.length));
|
|
300
|
+
|
|
301
|
+
const func = chart_data.smooth ?
|
|
302
|
+
this.renderer.RenderSmoothLine : this.renderer.RenderLine;
|
|
303
|
+
|
|
304
|
+
// gridlines
|
|
305
|
+
this.renderer.RenderGrid(area,
|
|
306
|
+
chart_data.scale.count,
|
|
307
|
+
chart_data.x_scale ? chart_data.x_scale.count + 1 : points,
|
|
308
|
+
'chart-grid');
|
|
309
|
+
|
|
310
|
+
// series
|
|
311
|
+
let series_index = 0;
|
|
312
|
+
for (const series of chart_data.series) {
|
|
313
|
+
|
|
314
|
+
|
|
315
|
+
|
|
316
|
+
const y = series.map((point) => {
|
|
317
|
+
if (typeof point === 'undefined') { return undefined; }
|
|
318
|
+
return Util.ApplyScale(point, area.height, scale);
|
|
319
|
+
});
|
|
320
|
+
|
|
321
|
+
if (y.length < points) {
|
|
322
|
+
for (let i = y.length; i < points; i++) {
|
|
323
|
+
y.push(undefined);
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
const styles = [
|
|
328
|
+
chart_data.type === 'area' ? 'chart-area' : 'chart-line',
|
|
329
|
+
`series-${series_index + 1}`]
|
|
330
|
+
|
|
331
|
+
func.call(this.renderer, area, y, (chart_data.type === 'area'), chart_data.titles, styles);
|
|
332
|
+
series_index++;
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
// TODO: callouts
|
|
337
|
+
|
|
338
|
+
}
|
|
339
|
+
break;
|
|
340
|
+
|
|
341
|
+
case 'bar':
|
|
342
|
+
{
|
|
343
|
+
let corners: number[]|undefined;
|
|
344
|
+
|
|
345
|
+
// gridlines
|
|
346
|
+
this.renderer.RenderBarGrid(area, chart_data.scale.count, 'chart-grid');
|
|
347
|
+
if (chart_data.series2) {
|
|
348
|
+
|
|
349
|
+
let count = 0;
|
|
350
|
+
const series_count = chart_data.series2.length;
|
|
351
|
+
|
|
352
|
+
for (const series of chart_data.series2) {
|
|
353
|
+
count = Math.max(count, series.y.data.length);
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
const row_height = area.height / count;
|
|
357
|
+
let row_pct = .7;
|
|
358
|
+
if (typeof chart_data.space === 'number') {
|
|
359
|
+
row_pct = Math.max(0, Math.min(1, 1 - (chart_data.space)));
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
const space = row_height * (1 - row_pct) / 2;
|
|
363
|
+
const height = (row_height - space * 2) / series_count;
|
|
364
|
+
|
|
365
|
+
let zero = 0;
|
|
366
|
+
if (chart_data.scale.min < 0) { // && chart_data.scale.max >= 0) {
|
|
367
|
+
zero = Util.ApplyScale(0, area.width, chart_data.scale);
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
if (chart_data.round) {
|
|
371
|
+
const half_height = Math.floor(height / 2);
|
|
372
|
+
corners = [0, half_height, half_height, 0];
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
for (let s = 0; s < series_count; s++) {
|
|
376
|
+
const series = chart_data.series2[s];
|
|
377
|
+
const color_index = typeof series.index === 'number' ? series.index : s + 1;
|
|
378
|
+
|
|
379
|
+
for (let i = 0; i < series.y.data.length; i++ ){
|
|
380
|
+
const value = series.y.data[i];
|
|
381
|
+
if (typeof value === 'number') {
|
|
382
|
+
|
|
383
|
+
const y = Math.round(area.top + i * row_height + space) + s * height;
|
|
384
|
+
|
|
385
|
+
let x = 0;
|
|
386
|
+
let width = 0;
|
|
387
|
+
let negative = false;
|
|
388
|
+
|
|
389
|
+
if (zero) {
|
|
390
|
+
if (value > 0) {
|
|
391
|
+
width = Util.ApplyScale(value + chart_data.scale.min, area.width, chart_data.scale);
|
|
392
|
+
x = area.left + zero;
|
|
393
|
+
}
|
|
394
|
+
else {
|
|
395
|
+
width = Util.ApplyScale(chart_data.scale.min - value, area.width, chart_data.scale);
|
|
396
|
+
x = area.left + zero - width;
|
|
397
|
+
negative = true;
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
else {
|
|
401
|
+
width = Util.ApplyScale(value, area.width, chart_data.scale);
|
|
402
|
+
x = area.left;
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
// const bar_title = chart_data.titles ? chart_data.titles[i] : undefined;
|
|
406
|
+
const bar_title = undefined;
|
|
407
|
+
|
|
408
|
+
if (width) {
|
|
409
|
+
this.renderer.RenderRectangle(new Area(
|
|
410
|
+
x, y, x + width, y + height,
|
|
411
|
+
), corners, ['chart-column', `series-${color_index}`], bar_title || undefined);
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
}
|
|
420
|
+
break;
|
|
421
|
+
|
|
422
|
+
case 'column':
|
|
423
|
+
case 'histogram2':
|
|
424
|
+
{
|
|
425
|
+
|
|
426
|
+
// gridlines
|
|
427
|
+
this.renderer.RenderGrid(area, chart_data.scale.count, 0, 'chart-grid');
|
|
428
|
+
|
|
429
|
+
if (chart_data.series2) {
|
|
430
|
+
|
|
431
|
+
let count = 0;
|
|
432
|
+
const series_count = chart_data.series2.length;
|
|
433
|
+
|
|
434
|
+
for (const series of chart_data.series2) {
|
|
435
|
+
count = Math.max(count, series.y.data.length);
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
// columns
|
|
439
|
+
const column_width = area.width / count;
|
|
440
|
+
let column_pct = .7;
|
|
441
|
+
if (typeof chart_data.space === 'number') {
|
|
442
|
+
column_pct = Math.max(0, Math.min(1, 1 - (chart_data.space)));
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
const space = column_width * (1 - column_pct) / 2;
|
|
446
|
+
const width = (column_width - space * 2) / series_count;
|
|
447
|
+
|
|
448
|
+
let zero = 0;
|
|
449
|
+
if (chart_data.scale.min < 0) { // && chart_data.scale.max >= 0) {
|
|
450
|
+
zero = Util.ApplyScale(0, area.height, chart_data.scale);
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
if (chart_data.callouts && chart_data.x_scale) {
|
|
454
|
+
const scale = chart_data.x_scale;
|
|
455
|
+
const lines = chart_data.callouts.map((callout, index) => {
|
|
456
|
+
const x = Math.round(area.left + Util.ApplyScale(callout.value, area.width, scale)) + .5;
|
|
457
|
+
return {
|
|
458
|
+
x1: x, y1: area.bottom - area.height, x2: x, y2: area.bottom,
|
|
459
|
+
classes: `callout-${index + 1}`,
|
|
460
|
+
}
|
|
461
|
+
});
|
|
462
|
+
this.renderer.RenderCalloutLines(lines);
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
let corners: number[]|undefined;
|
|
466
|
+
|
|
467
|
+
if (chart_data.round) {
|
|
468
|
+
const half_width = Math.floor(width/2);
|
|
469
|
+
corners = [half_width, half_width, 0, 0];
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
for (let s = 0; s < series_count; s++) {
|
|
473
|
+
const series = chart_data.series2[s];
|
|
474
|
+
const color_index = typeof series.index === 'number' ? series.index : s + 1;
|
|
475
|
+
|
|
476
|
+
for (let i = 0; i < series.y.data.length; i++ ){
|
|
477
|
+
const value = series.y.data[i];
|
|
478
|
+
// const format = NumberFormatCache.Get(series.y.format || '0.00');
|
|
479
|
+
|
|
480
|
+
if (typeof value === 'number') {
|
|
481
|
+
|
|
482
|
+
// const x = Math.round(area.left + i * column_width + space) + s * width;
|
|
483
|
+
const x = (area.left + i * column_width + space) + s * width;
|
|
484
|
+
|
|
485
|
+
let height = 0;
|
|
486
|
+
let y = 0;
|
|
487
|
+
let negative = false;
|
|
488
|
+
|
|
489
|
+
if (zero) {
|
|
490
|
+
if (value > 0) {
|
|
491
|
+
height = Util.ApplyScale(value + chart_data.scale.min, area.height, chart_data.scale);
|
|
492
|
+
y = area.bottom - height - zero;
|
|
493
|
+
}
|
|
494
|
+
else {
|
|
495
|
+
height = Util.ApplyScale(chart_data.scale.min - value, area.height, chart_data.scale);
|
|
496
|
+
y = area.bottom - zero; // // area.bottom - height - zero;
|
|
497
|
+
negative = true;
|
|
498
|
+
}
|
|
499
|
+
}
|
|
500
|
+
else {
|
|
501
|
+
height = Util.ApplyScale(value, area.height, chart_data.scale);
|
|
502
|
+
y = area.bottom - height;
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
// const bar_title = chart_data.titles ? chart_data.titles[i] : undefined;
|
|
506
|
+
const bar_title = undefined;
|
|
507
|
+
|
|
508
|
+
if (height) {
|
|
509
|
+
|
|
510
|
+
const label = (chart_data.data_labels && !!series.y.labels) ? series.y.labels[i] : '';
|
|
511
|
+
const label_point = {
|
|
512
|
+
x: Math.round(x + width / 2),
|
|
513
|
+
y: Math.round(y - 10),
|
|
514
|
+
};
|
|
515
|
+
|
|
516
|
+
this.renderer.RenderRectangle(new Area(
|
|
517
|
+
x, y, x + width, y + height,
|
|
518
|
+
), corners, ['chart-column', `series-${color_index}`], bar_title || undefined, label, label_point);
|
|
519
|
+
}
|
|
520
|
+
}
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
}
|
|
528
|
+
break;
|
|
529
|
+
|
|
530
|
+
case 'histogram':
|
|
531
|
+
{
|
|
532
|
+
// gridlines
|
|
533
|
+
this.renderer.RenderGrid(area, chart_data.scale.count, 0, 'chart-grid');
|
|
534
|
+
|
|
535
|
+
// columns
|
|
536
|
+
const column_width = area.width / chart_data.count;
|
|
537
|
+
const column_pct = chart_data.column_width;
|
|
538
|
+
|
|
539
|
+
const space = column_width * (1 - column_pct) / 2;
|
|
540
|
+
|
|
541
|
+
for (let i = 0; i < chart_data.count; i++ ){
|
|
542
|
+
const x = Math.round(area.left + i * column_width + space);
|
|
543
|
+
const width = column_width - space * 2;
|
|
544
|
+
const height = Util.ApplyScale(chart_data.bins[i], area.height, chart_data.scale);
|
|
545
|
+
const y = area.bottom - height;
|
|
546
|
+
const bar_title = chart_data.titles ? chart_data.titles[i] : undefined;
|
|
547
|
+
|
|
548
|
+
this.renderer.RenderRectangle(new Area(
|
|
549
|
+
x, y, x + width, y + height,
|
|
550
|
+
), undefined, 'chart-column series-1', bar_title || undefined);
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
}
|
|
554
|
+
break;
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
|
|
560
|
+
}
|
package/treb-charts/src/index.ts
CHANGED
|
@@ -14,11 +14,12 @@
|
|
|
14
14
|
* You should have received a copy of the GNU General Public License along
|
|
15
15
|
* with TREB. If not, see <https://www.gnu.org/licenses/>.
|
|
16
16
|
*
|
|
17
|
-
* Copyright 2022-
|
|
17
|
+
* Copyright 2022-2024 trebco, llc.
|
|
18
18
|
* info@treb.app
|
|
19
19
|
*
|
|
20
20
|
*/
|
|
21
21
|
|
|
22
|
-
export { Chart } from './chart';
|
|
23
|
-
export { ChartFunctions } from './chart-functions';
|
|
24
|
-
export { Util } from './util';
|
|
22
|
+
export { Chart } from './chart';
|
|
23
|
+
export { ChartFunctions } from './chart-functions';
|
|
24
|
+
export { Util } from './util';
|
|
25
|
+
export type { ChartRenderer } from './renderer-type';
|
package/treb-charts/src/main.ts
CHANGED
|
@@ -14,24 +14,24 @@
|
|
|
14
14
|
* You should have received a copy of the GNU General Public License along
|
|
15
15
|
* with TREB. If not, see <https://www.gnu.org/licenses/>.
|
|
16
16
|
*
|
|
17
|
-
* Copyright 2022-
|
|
17
|
+
* Copyright 2022-2024 trebco, llc.
|
|
18
18
|
* info@treb.app
|
|
19
19
|
*
|
|
20
20
|
*/
|
|
21
21
|
|
|
22
|
-
import { Chart } from './';
|
|
23
|
-
|
|
24
|
-
// tslint:disable-next-line: no-var-requires
|
|
25
|
-
require('../style/charts.pcss');
|
|
26
|
-
|
|
27
|
-
if (!(self as any).TREB) {
|
|
28
|
-
(self as any).TREB = {};
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
// (temporarily, atm) switching name to prevent overlap
|
|
32
|
-
|
|
33
|
-
(self as any).TREB.CreateChart2 = (node?: HTMLElement) => {
|
|
34
|
-
const chart = new Chart();
|
|
35
|
-
if (node) chart.Initialize(node);
|
|
36
|
-
return chart;
|
|
37
|
-
};
|
|
22
|
+
import { Chart } from './';
|
|
23
|
+
|
|
24
|
+
// tslint:disable-next-line: no-var-requires
|
|
25
|
+
require('../style/charts.pcss');
|
|
26
|
+
|
|
27
|
+
if (!(self as any).TREB) {
|
|
28
|
+
(self as any).TREB = {};
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// (temporarily, atm) switching name to prevent overlap
|
|
32
|
+
|
|
33
|
+
(self as any).TREB.CreateChart2 = (node?: HTMLElement) => {
|
|
34
|
+
const chart = new Chart();
|
|
35
|
+
if (node) chart.Initialize(node);
|
|
36
|
+
return chart;
|
|
37
|
+
};
|
|
@@ -1,24 +1,24 @@
|
|
|
1
|
-
/*
|
|
2
|
-
* This file is part of TREB.
|
|
3
|
-
*
|
|
4
|
-
* TREB is free software: you can redistribute it and/or modify it under the
|
|
5
|
-
* terms of the GNU General Public License as published by the Free Software
|
|
6
|
-
* Foundation, either version 3 of the License, or (at your option) any
|
|
7
|
-
* later version.
|
|
8
|
-
*
|
|
9
|
-
* TREB is distributed in the hope that it will be useful, but WITHOUT ANY
|
|
10
|
-
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
|
11
|
-
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
|
12
|
-
* details.
|
|
13
|
-
*
|
|
14
|
-
* You should have received a copy of the GNU General Public License along
|
|
15
|
-
* with TREB. If not, see <https://www.gnu.org/licenses/>.
|
|
16
|
-
*
|
|
17
|
-
* Copyright 2022-
|
|
18
|
-
* info@treb.app
|
|
19
|
-
*
|
|
20
|
-
*/
|
|
21
|
-
|
|
1
|
+
/*
|
|
2
|
+
* This file is part of TREB.
|
|
3
|
+
*
|
|
4
|
+
* TREB is free software: you can redistribute it and/or modify it under the
|
|
5
|
+
* terms of the GNU General Public License as published by the Free Software
|
|
6
|
+
* Foundation, either version 3 of the License, or (at your option) any
|
|
7
|
+
* later version.
|
|
8
|
+
*
|
|
9
|
+
* TREB is distributed in the hope that it will be useful, but WITHOUT ANY
|
|
10
|
+
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
|
11
|
+
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
|
12
|
+
* details.
|
|
13
|
+
*
|
|
14
|
+
* You should have received a copy of the GNU General Public License along
|
|
15
|
+
* with TREB. If not, see <https://www.gnu.org/licenses/>.
|
|
16
|
+
*
|
|
17
|
+
* Copyright 2022-2024 trebco, llc.
|
|
18
|
+
* info@treb.app
|
|
19
|
+
*
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
22
|
export interface Point {
|
|
23
23
|
x: number;
|
|
24
24
|
y: number;
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
|
|
2
|
+
import type { ChartData } from './chart-types'
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* interface type for renderer
|
|
6
|
+
*/
|
|
7
|
+
export interface ChartRenderer {
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* set the target node. this is separate from rendering in the
|
|
11
|
+
* event you want to cache or precalculate anything. it will be
|
|
12
|
+
* called any time the node changes.
|
|
13
|
+
*/
|
|
14
|
+
Initialize: (target: HTMLElement) => void;
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* called when the data has updated, you need to repaint the chart.
|
|
18
|
+
* the node is passed as a conveniece, but you can cache the node
|
|
19
|
+
* from Initialize().
|
|
20
|
+
*/
|
|
21
|
+
Update: (target: HTMLElement, data: ChartData) => void;
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* called when the chart is resized. data has not changed. if you cached
|
|
25
|
+
* the data previously, then you can reuse that. the node is passed as a
|
|
26
|
+
* conveniece, but you can cache the node from Initialize().
|
|
27
|
+
*/
|
|
28
|
+
Resize: (target: HTMLElement, data: ChartData) => void;
|
|
29
|
+
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
|