@progress/kendo-charts 2.0.0 → 2.1.0-dev.202401221445

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.
Files changed (39) hide show
  1. package/dist/cdn/js/kendo-charts.js +1 -1
  2. package/dist/cdn/main.js +1 -1
  3. package/dist/es/{map/scroller → common}/observable.js +1 -1
  4. package/dist/es/main.js +1 -0
  5. package/dist/es/map/attribution.js +1 -1
  6. package/dist/es/map/map.js +1 -1
  7. package/dist/es/map/navigator.js +1 -1
  8. package/dist/es/map/scroller/draggable.js +1 -1
  9. package/dist/es/map/scroller/scroller.js +1 -1
  10. package/dist/es/map/scroller/user-events.js +1 -1
  11. package/dist/es/map/zoom.js +1 -1
  12. package/dist/es/sankey/calculation.js +176 -0
  13. package/dist/es/sankey/element.js +49 -0
  14. package/dist/es/sankey/label.js +83 -0
  15. package/dist/es/sankey/link.js +62 -0
  16. package/dist/es/sankey/node.js +50 -0
  17. package/dist/es/sankey/sankey.js +345 -0
  18. package/dist/es/sankey.js +1 -0
  19. package/dist/es2015/{map/scroller → common}/observable.js +1 -1
  20. package/dist/es2015/main.js +1 -0
  21. package/dist/es2015/map/attribution.js +1 -1
  22. package/dist/es2015/map/map.js +1 -1
  23. package/dist/es2015/map/navigator.js +1 -1
  24. package/dist/es2015/map/scroller/draggable.js +1 -1
  25. package/dist/es2015/map/scroller/scroller.js +1 -1
  26. package/dist/es2015/map/scroller/user-events.js +1 -1
  27. package/dist/es2015/map/zoom.js +1 -1
  28. package/dist/es2015/sankey/calculation.js +170 -0
  29. package/dist/es2015/sankey/element.js +41 -0
  30. package/dist/es2015/sankey/label.js +69 -0
  31. package/dist/es2015/sankey/link.js +49 -0
  32. package/dist/es2015/sankey/node.js +40 -0
  33. package/dist/es2015/sankey/sankey.js +315 -0
  34. package/dist/es2015/sankey.js +1 -0
  35. package/dist/npm/main.d.ts +1 -0
  36. package/dist/npm/main.js +743 -1
  37. package/dist/npm/sankey.d.ts +275 -0
  38. package/dist/systemjs/kendo-charts.js +1 -1
  39. package/package.json +1 -1
@@ -0,0 +1,69 @@
1
+ import { setDefaultOptions, deepExtend } from '../common';
2
+ import { SankeyElement } from './element';
3
+ import { Box, TextBox } from '../core';
4
+ const INSIDE = 'inside';
5
+ const BEFORE = 'before';
6
+ const AFTER = 'after';
7
+
8
+ export class Label extends SankeyElement {
9
+ getElement() {
10
+ const options = deepExtend({}, this.options, this.options.node.label);
11
+ const { node, totalWidth, position, text, offset } = options;
12
+
13
+ if (!options.visible || !text) {
14
+ return null;
15
+ }
16
+
17
+ const nodeBox = new Box(node.x0, node.y0, node.x1, node.y1);
18
+ const visualOptions = this.visualOptions();
19
+ const textbox = new TextBox(text, visualOptions);
20
+ textbox.reflow(new Box());
21
+ const textSizeBox = textbox.box;
22
+
23
+ const goesOutside = node.x1 + textSizeBox.width() > totalWidth;
24
+ const textY = nodeBox.center().y - (textSizeBox.height() / 2);
25
+ const side = position === BEFORE || (position === INSIDE && goesOutside) ? BEFORE : AFTER;
26
+ const textOrigin = [side === BEFORE ? node.x0 - textSizeBox.width() : node.x1, textY];
27
+
28
+ const textRect = new Box(textOrigin[0], textOrigin[1], textOrigin[0] + textSizeBox.width(), textOrigin[1] + textSizeBox.height());
29
+ textRect.translate(offset.left || 0, offset.top || 0);
30
+ textbox.reflow(textRect);
31
+
32
+ textbox.renderVisual();
33
+
34
+ return textbox.visual;
35
+ }
36
+
37
+ visualOptions() {
38
+ const options = deepExtend({}, this.options, this.options.node.label);
39
+ return {
40
+ color: options.color,
41
+ opacity: options.opacity,
42
+ font: options.font,
43
+ border: options.border,
44
+ margin: options.margin,
45
+ padding: options.padding,
46
+ align: options.align,
47
+ };
48
+ }
49
+ }
50
+
51
+ setDefaultOptions(Label, {
52
+ position: INSIDE, // inside, before, after
53
+ });
54
+
55
+ export const resolveLabelOptions = (node, options, totalWidth) => deepExtend({},
56
+ options,
57
+ {
58
+ node,
59
+ totalWidth,
60
+ visual: node.label.visual,
61
+ visible: node.label.visible,
62
+ margin: node.label.margin,
63
+ padding: node.label.padding,
64
+ border: node.label.border,
65
+ align: node.label.align,
66
+ opacity: node.label.opacity,
67
+ offset: node.label.offset
68
+ }
69
+ );
@@ -0,0 +1,49 @@
1
+ import { drawing } from '@progress/kendo-drawing';
2
+ import { SankeyElement } from './element';
3
+ import { deepExtend } from '../common';
4
+ import { defined } from '../drawing-utils';
5
+
6
+ export class Link extends SankeyElement {
7
+ getElement() {
8
+ const link = this.options.link;
9
+ const { source, target, y0, y1 } = link;
10
+ const xC = (source.x0 + target.x1) / 2;
11
+
12
+ return new drawing.Path(this.visualOptions())
13
+ .moveTo(source.x1, y0).curveTo([xC, y0], [xC, y1], [target.x0, y1]);
14
+ }
15
+
16
+ visualOptions() {
17
+ const options = this.options;
18
+ const link = this.options.link;
19
+ return {
20
+ stroke: {
21
+ width: options.link.width,
22
+ color: link.color || options.color,
23
+ opacity: defined(link.opacity) ? link.opacity : options.opacity
24
+ }
25
+ };
26
+ }
27
+ }
28
+
29
+ export const resolveLinkOptions = (link, options, sourceNode, targetNode) => {
30
+ const linkOptions = deepExtend({},
31
+ options,
32
+ {
33
+ link,
34
+ opacity: link.opacity,
35
+ color: link.color,
36
+ colorType: link.colorType,
37
+ visual: link.visual,
38
+ highlight: link.highlight
39
+ }
40
+ );
41
+
42
+ if (linkOptions.colorType === 'source') {
43
+ linkOptions.color = sourceNode.options.fill.color;
44
+ } else if (linkOptions.colorType === 'target') {
45
+ linkOptions.color = targetNode.options.fill.color;
46
+ }
47
+
48
+ return linkOptions;
49
+ };
@@ -0,0 +1,40 @@
1
+ import { geometry, drawing } from '@progress/kendo-drawing';
2
+ import { SankeyElement } from './element';
3
+ import { deepExtend } from '../common';
4
+
5
+ export class Node extends SankeyElement {
6
+ getElement() {
7
+ const options = this.options;
8
+ const node = options.node;
9
+ const rect = new geometry.Rect([node.x0, node.y0], [node.x1 - node.x0, node.y1 - node.y0]);
10
+
11
+ return drawing.Path.fromRect(rect, this.visualOptions());
12
+ }
13
+
14
+ visualOptions() {
15
+ const options = deepExtend({}, this.options, this.options.node);
16
+
17
+ return {
18
+ fill: {
19
+ color: options.color,
20
+ opacity: options.opacity
21
+ },
22
+ stroke: { width: 0 },
23
+ };
24
+ }
25
+ }
26
+
27
+ export const resolveNodeOptions = (node, options, nodesColors, index) => {
28
+ const nodeOptions = deepExtend({}, options, options.node);
29
+ return deepExtend({},
30
+ { color: nodesColors[index % nodesColors.length] },
31
+ nodeOptions,
32
+ { node },
33
+ {
34
+ visual: node.visual,
35
+ opacity: node.opacity,
36
+ offset: node.offset,
37
+ color: node.color
38
+ }
39
+ );
40
+ };
@@ -0,0 +1,315 @@
1
+ import { drawing } from '@progress/kendo-drawing';
2
+ import { deepExtend, addClass, setDefaultOptions } from '../common';
3
+ import { calculateSankey } from './calculation';
4
+ import { Node, resolveNodeOptions } from './node';
5
+ import { Link, resolveLinkOptions } from './link';
6
+ import { Label, resolveLabelOptions } from './label';
7
+ import { LEFT } from '../common/constants';
8
+ import Box from '../core/box';
9
+ import rectToBox from '../core/utils/rect-to-box';
10
+ import { Observable } from '../common/observable';
11
+
12
+ const LINK = 'link';
13
+ const NODE = 'node';
14
+
15
+ export class Sankey extends Observable {
16
+ constructor(element, options, theme) {
17
+ super();
18
+
19
+ this._initElement(element);
20
+ this._initTheme(theme);
21
+ this._setOptions(options);
22
+ this._initSurface();
23
+
24
+ if (options && options.data) {
25
+ this._redraw();
26
+ }
27
+
28
+ this._initResizeObserver();
29
+ }
30
+
31
+ destroy() {
32
+ this.unbind();
33
+ this._destroySurface();
34
+ this._destroyResizeObserver();
35
+ }
36
+
37
+ _initElement(element) {
38
+ this.element = element;
39
+ addClass(this.element, [ "k-chart", "k-sankey" ]);
40
+ }
41
+
42
+ _initSurface() {
43
+ if (!this.surface) {
44
+ this._destroySurface();
45
+ this._initSurfaceElement();
46
+ this.surface = this._createSurface();
47
+ }
48
+ }
49
+
50
+ _initResizeObserver() {
51
+ const observer = new ResizeObserver((entries) => {
52
+ entries.forEach(entry => {
53
+ const { width, height } = entry.contentRect;
54
+ if (entry.target !== this.element ||
55
+ (this.size.width === width && this.size.height === height)) {
56
+ return;
57
+ }
58
+ this.size = { width, height };
59
+ this.surface.setSize(this.size);
60
+ this._redraw();
61
+ });
62
+ });
63
+ this._resizeObserver = observer;
64
+ observer.observe(this.element);
65
+ }
66
+
67
+ _createSurface() {
68
+ return drawing.Surface.create(this.surfaceElement, {
69
+ mouseenter: this._mouseenter.bind(this),
70
+ mouseleave: this._mouseleave.bind(this)
71
+ });
72
+ }
73
+
74
+ _initTheme(theme) {
75
+ let currentTheme = theme || this.theme || {};
76
+ this.theme = currentTheme;
77
+ this.options = deepExtend({}, currentTheme, this.options);
78
+ }
79
+
80
+ setLinksOpacity(opacity) {
81
+ this.linksVisuals.forEach(link => {
82
+ this.setOpacity(link, opacity);
83
+ });
84
+ }
85
+
86
+ setOpacity(link, opacity) {
87
+ link.options.set('stroke', Object.assign({}, link.options.stroke, {opacity}));
88
+ }
89
+
90
+ trigger(name, ev) {
91
+ ev.type = name;
92
+ return super.trigger(name, ev);
93
+ }
94
+
95
+ _mouseenter(ev) {
96
+ const element = ev.element;
97
+ const isLink = element.type === LINK;
98
+ const isNode = element.type === NODE;
99
+
100
+ if ((isLink && this.trigger('linkEnter', ev)) ||
101
+ (isNode && this.trigger('nodeEnter', ev))) {
102
+ return;
103
+ }
104
+
105
+ const { highlight } = this.options.links;
106
+ if (isLink) {
107
+ this.setLinksOpacity(highlight.inactiveOpacity);
108
+ this.setOpacity(element, highlight.opacity);
109
+ } else if (isNode) {
110
+ this.setLinksOpacity(highlight.inactiveOpacity);
111
+ element.links.forEach(link => {
112
+ this.setOpacity(link, highlight.opacity);
113
+ });
114
+ }
115
+ }
116
+
117
+ _mouseleave(ev) {
118
+ const element = ev.element;
119
+ const isLink = element.type === LINK;
120
+ const isNode = element.type === NODE;
121
+ const target = ev.originalEvent.relatedTarget;
122
+
123
+ if (isLink && target && target.nodeName === 'text') {
124
+ return;
125
+ }
126
+
127
+ if ((isLink && this.trigger('linkLeave', ev)) ||
128
+ (isNode && this.trigger('nodeLeave', ev))) {
129
+ return;
130
+ }
131
+
132
+ if (isLink || isNode) {
133
+ this.setLinksOpacity(this.options.links.opacity);
134
+ }
135
+ }
136
+
137
+ _destroySurface() {
138
+ if (this.surface) {
139
+ this.surface.destroy();
140
+ this.surface = null;
141
+ this._destroySurfaceElement();
142
+ }
143
+ }
144
+
145
+ _destroyResizeObserver() {
146
+ if (this._resizeObserver) {
147
+ this._resizeObserver.disconnect();
148
+ this._resizeObserver = null;
149
+ }
150
+ }
151
+
152
+ _initSurfaceElement() {
153
+ if (!this.surfaceElement) {
154
+ this.surfaceElement = document.createElement('div');
155
+ this.element.appendChild(this.surfaceElement);
156
+ }
157
+ }
158
+
159
+ _destroySurfaceElement() {
160
+ if (this.surfaceElement && this.surfaceElement.parentNode) {
161
+ this.surfaceElement.parentNode.removeChild(this.surfaceElement);
162
+ this.surfaceElement = null;
163
+ }
164
+ }
165
+
166
+ setOptions(options, theme) {
167
+ this._setOptions(options);
168
+ this._initTheme(theme);
169
+ this._initSurface();
170
+ this._redraw();
171
+ }
172
+
173
+ _redraw() {
174
+ this.surface.clear();
175
+
176
+ const { width, height } = this._getSize();
177
+ this.size = { width, height };
178
+ this.surface.setSize(this.size);
179
+
180
+ this.createVisual();
181
+
182
+ this.surface.draw(this.visual);
183
+ }
184
+
185
+ _getSize() {
186
+ return this.element.getBoundingClientRect();
187
+ }
188
+
189
+ createVisual() {
190
+ this.visual = this._render();
191
+ }
192
+
193
+ calculateSankey(options) {
194
+ const { nodes, labels, nodesColors } = this.options;
195
+ const calculatedNodes = calculateSankey(options).nodes;
196
+ const box = new Box();
197
+
198
+ calculatedNodes.forEach((nodeEl, i) => {
199
+ const nodeOps = resolveNodeOptions(nodeEl, nodes, nodesColors, i);
200
+ const nodeInstance = new Node(nodeOps);
201
+ box.wrap(rectToBox(nodeInstance.exportVisual().rawBBox()));
202
+
203
+ const labelInstance = new Label(deepExtend({ node: nodeEl, totalWidth: options.width }, labels));
204
+ const labelVisual = labelInstance.exportVisual();
205
+ if (labelVisual) {
206
+ box.wrap(rectToBox(labelVisual.rawBBox()));
207
+ }
208
+ });
209
+
210
+ const offsetX = box.x1 < 0 ? -box.x1 : 0;
211
+ const offsetY = box.y1 < 0 ? -box.y1 : 0;
212
+
213
+ const width = box.width() > options.width ? offsetX + options.width - (box.width() - options.width) : options.width;
214
+ const height = box.height() > options.height ? offsetY + options.height - (box.height() - options.height) : options.height;
215
+
216
+ return calculateSankey(Object.assign({}, options, {width, height, offsetX, offsetY}));
217
+ }
218
+
219
+ _render() {
220
+ const { data, labels: labelOptions, nodes: nodesOptions, links: linkOptions, nodesColors } = this.options;
221
+ const { width, height } = this.size;
222
+ const calcOptions = Object.assign({}, data, {width, height, nodesOptions});
223
+ let { nodes, links } = this.calculateSankey(calcOptions);
224
+
225
+ const visual = new drawing.Group();
226
+
227
+ const visualNodes = new Map();
228
+
229
+ nodes.forEach((node, i) => {
230
+ const nodeOps = resolveNodeOptions(node, nodesOptions, nodesColors, i);
231
+
232
+ const nodeInstance = new Node(nodeOps);
233
+ const nodeVisual = nodeInstance.exportVisual();
234
+ nodeVisual.links = [];
235
+ nodeVisual.type = NODE;
236
+ visualNodes.set(node.id, nodeVisual);
237
+
238
+ visual.append(nodeVisual);
239
+ });
240
+
241
+ links = links.slice().sort((a, b) => b.value - a.value);
242
+
243
+ const linksVisuals = [];
244
+
245
+ links.forEach(link => {
246
+ const { source, target } = link;
247
+ const sourceNode = visualNodes.get(source.id);
248
+ const targetNode = visualNodes.get(target.id);
249
+ const linkOps = resolveLinkOptions(link, linkOptions, sourceNode, targetNode);
250
+ const linkInstance = new Link(linkOps);
251
+ const linkVisual = linkInstance.exportVisual();
252
+
253
+ linkVisual.type = LINK;
254
+ linksVisuals.push(linkVisual);
255
+
256
+ sourceNode.links.push(linkVisual);
257
+ targetNode.links.push(linkVisual);
258
+
259
+ visual.append(linkVisual);
260
+ });
261
+
262
+ this.linksVisuals = linksVisuals;
263
+
264
+ nodes.forEach((node) => {
265
+ const textOps = resolveLabelOptions(node, labelOptions, width);
266
+ const labelInstance = new Label(textOps);
267
+ const labelVisual = labelInstance.exportVisual();
268
+
269
+ if (labelVisual) {
270
+ visual.append(labelVisual);
271
+ }
272
+ });
273
+
274
+ return visual;
275
+ }
276
+
277
+ exportVisual() {
278
+ return this._render();
279
+ }
280
+
281
+ _setOptions(options) {
282
+ this.options = deepExtend({}, this.options, options);
283
+ }
284
+ }
285
+
286
+ setDefaultOptions(Sankey, {
287
+ labels: {
288
+ visible: true,
289
+ margin: {
290
+ left: 8,
291
+ right: 8
292
+ },
293
+ padding: 0,
294
+ border: {
295
+ width: 0
296
+ },
297
+ align: LEFT,
298
+ opacity: 1,
299
+ offset: { left: 0, top: 0 }
300
+ },
301
+ nodes: {
302
+ width: 24,
303
+ padding: 16,
304
+ opacity: 1,
305
+ offset: { left: 0, top: 0 }
306
+ },
307
+ links: {
308
+ colorType: 'static', // 'source', 'target', 'static'
309
+ opacity: 0.4,
310
+ highlight: {
311
+ opacity: 0.8,
312
+ inactiveOpacity: 0.2
313
+ }
314
+ }
315
+ });
@@ -0,0 +1 @@
1
+ export { Sankey } from './sankey/sankey';
@@ -7,6 +7,7 @@ export * from './gauges';
7
7
  export * from './barcode';
8
8
  export * from './qrcode';
9
9
  export * from './map';
10
+ export * from './sankey';
10
11
  export * from './common';
11
12
  export * from './field-types'
12
13
  export * from './validation';