@hpcc-js/tree 3.2.13 → 3.2.15

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/src/Dendrogram.ts CHANGED
@@ -1,296 +1,296 @@
1
- import { ITree } from "@hpcc-js/api";
2
- import { PropertyExt, SVGZoomWidget, Utility } from "@hpcc-js/common";
3
- import { cluster as d3Cluster, hierarchy as d3Hierarchy, tree as d3Tree } from "d3-hierarchy";
4
- import { select as d3Select } from "d3-selection";
5
-
6
- import "../src/Dendrogram.css";
7
-
8
- export class DendrogramColumn extends PropertyExt {
9
- _owner: Dendrogram;
10
-
11
- constructor() {
12
- super();
13
- }
14
-
15
- owner(): Dendrogram;
16
- owner(_: Dendrogram): this;
17
- owner(_?: Dendrogram): Dendrogram | this {
18
- if (!arguments.length) return this._owner;
19
- this._owner = _;
20
- return this;
21
- }
22
- valid(): boolean {
23
- return !!this.column();
24
- }
25
-
26
- column: { (): string; (_: string): Dendrogram; };
27
- }
28
- DendrogramColumn.prototype._class += " tree_Dendrogram.DendrogramColumn";
29
-
30
- DendrogramColumn.prototype.publish("column", null, "set", "Field", function (this: DendrogramColumn) { return this._owner ? this._owner.columns() : []; }, { optional: true });
31
-
32
- // ===
33
- export class Dendrogram extends SVGZoomWidget {
34
- Column;
35
- _d3LayoutCluster;
36
- _d3LayoutTree;
37
- _d3Layout;
38
-
39
- constructor() {
40
- super();
41
- ITree.call(this);
42
- Utility.SimpleSelectionMixin.call(this);
43
-
44
- this._drawStartPos = "origin";
45
-
46
- this._d3LayoutCluster = d3Cluster();
47
- this._d3LayoutTree = d3Tree();
48
- }
49
-
50
- dendrogramData() {
51
- if (this.data().length === 0) return [];
52
- if (!this.mappings().filter(mapping => mapping.valid()).length) {
53
- return this.data();
54
- }
55
- const view = this._db.rollupView(this.mappings().map(function (mapping) { return mapping.column(); }));
56
- const retVal = {
57
- key: "root",
58
- values: view.entries()
59
- };
60
- return formatData(retVal);
61
-
62
- function formatData(node) {
63
- return {
64
- label: node.key,
65
- children: node.values.filter(function (value) { return !(value instanceof Array); }).map(function (value) { return formatData(value); }),
66
- origRows: node.values
67
- };
68
- }
69
- }
70
-
71
- enter(domNode, element) {
72
- super.enter(domNode, element);
73
- this._renderElement
74
- .attr("opacity", 0)
75
- .transition().duration(500)
76
- .attr("opacity", 1)
77
- ;
78
- this._selection.widgetElement(this._renderElement);
79
- }
80
-
81
- update(domNode, element) {
82
- super.update(domNode, element);
83
- const context = this;
84
- const isVertical = this.orientation() === "vertical";
85
-
86
- this._palette = this._palette.switch(this.paletteID());
87
- if (this.useClonedPalette()) {
88
- this._palette = this._palette.cloneNotExists(this.paletteID() + "_" + this.id());
89
- }
90
-
91
- this._d3Layout = this.dendrogram() ? this._d3LayoutCluster : this._d3LayoutTree;
92
-
93
- if (this.radial()) {
94
- this._d3Layout
95
- .size([360, this.separation() * 2])
96
- ;
97
- this._d3Layout.separation(function separation(a, b) {
98
- return (a.parent === b.parent ? 1 : 2) / a.depth;
99
- });
100
- } else {
101
- this._d3Layout.nodeSize([14, this.separation()]);
102
- this._d3Layout.separation(function separation(a, b) {
103
- return a.parent === b.parent ? 1 : 2;
104
- });
105
- }
106
-
107
- const data = this.dendrogramData();
108
- const root = d3Hierarchy(data);
109
- this._d3Layout(root);
110
-
111
- const dataNodes = root.descendants();
112
- const links = root.descendants().slice(1);
113
-
114
- // Lines ---
115
- function linkVertical(d) {
116
- return "M" + d.parent.x + "," + d.parent.y
117
- + "C" + d.parent.x + "," + (d.parent.y + d.y) / 2
118
- + " " + d.x + "," + (d.parent.y + d.y) / 2
119
- + " " + d.x + "," + d.y;
120
- }
121
-
122
- function linkHorizontal(d) {
123
- return "M" + d.y + "," + d.x
124
- + "C" + (d.y + d.parent.y) / 2 + "," + d.x
125
- + " " + (d.y + d.parent.y) / 2 + "," + d.parent.x
126
- + " " + d.parent.y + "," + d.parent.x;
127
- }
128
- function diagonal(d) {
129
- return isVertical ? linkVertical(d) : linkHorizontal(d);
130
- }
131
-
132
- function project(x, y) {
133
- const angle = (x - 90) / 180 * Math.PI;
134
- const radius = y;
135
- return [radius * Math.cos(angle), radius * Math.sin(angle)];
136
- }
137
-
138
- function radialDiagonal(d) {
139
- return "M" + project(d.x, d.y)
140
- + "C" + project(d.x, (d.y + d.parent.y) / 2)
141
- + " " + project(d.parent.x, (d.y + d.parent.y) / 2)
142
- + " " + project(d.parent.x, d.parent.y);
143
- }
144
-
145
- const transitionDuration = this._renderCount ? 500 : 0;
146
- const lines = this._renderElement.selectAll(".link").data(links);
147
- lines.enter().append("path")
148
- .attr("class", "link")
149
- .attr("d", this.radial() ? radialDiagonal : diagonal)
150
- ;
151
- lines.transition().duration(transitionDuration)
152
- .attr("d", this.radial() ? radialDiagonal : diagonal)
153
- ;
154
- lines.exit().remove();
155
-
156
- // Nodes ---
157
- const textOffsetX = this.circleRadius() + 2;
158
- function nodeTransform(d) {
159
- if (context.radial()) {
160
- return "rotate(" + (d.x - 90) + ")translate(" + d.y + ")";
161
- }
162
- return context.orientation() === "horizontal" ? "translate(" + d.y + "," + d.x + ")" : "translate(" + d.x + "," + d.y + ")";
163
- }
164
- const nodes = this._renderElement.selectAll(".node").data(dataNodes);
165
- nodes.transition().duration(transitionDuration)
166
- .attr("transform", nodeTransform)
167
- ;
168
- const enterNodes = nodes.enter().append("g")
169
- .attr("class", "node")
170
- .attr("transform", nodeTransform)
171
- .call(this._selection.enter.bind(this._selection))
172
- .on("click", function (d) {
173
- let tmp = d;
174
- while (tmp.children) {
175
- tmp = tmp.children[0];
176
- }
177
- if (d.depth > 0) {
178
- if (tmp.origRows) {
179
- context.click(context.rowToObj(tmp.origRows[0]), context.mappings()[d.depth - 1].column(), true);
180
- } else {
181
- context.click(tmp.data, context.mappings()[d.depth - 1].column(), true);
182
- }
183
- }
184
- })
185
- .on("dblclick", function (d) {
186
- let tmp = d;
187
- while (tmp.children) {
188
- tmp = tmp.children[0];
189
- }
190
- if (d.depth > 0) {
191
- if (tmp.origRows) {
192
- context.dblclick(context.rowToObj(tmp.origRows[0]), context.mappings()[d.depth - 1].column(), true);
193
- } else {
194
- context.dblclick(tmp.data, context.mappings()[d.depth - 1].column(), true);
195
- }
196
- }
197
- })
198
- .each(function () {
199
- const e = d3Select(this);
200
- e.append("circle");
201
- e.append("text");
202
- })
203
- ;
204
- enterNodes.merge(nodes).select("circle")
205
- .attr("r", this.circleRadius())
206
- .style("fill", function (d) { return context._palette(d.data.label); })
207
- .append("title")
208
- .text(function (d) { return d.data.label; })
209
- ;
210
- enterNodes.merge(nodes).select("text")
211
- .attr("dx", function (d) {
212
- if (context.radial()) {
213
- if (d.children) {
214
- return d.x < 180 ? -textOffsetX : textOffsetX;
215
- } else {
216
- return d.x < 180 ? textOffsetX : -textOffsetX;
217
- }
218
- } else if (isVertical) {
219
- return d.children ? textOffsetX : -textOffsetX;
220
- }
221
- return d.children ? -textOffsetX : textOffsetX;
222
- })
223
- .attr("dy", "0.25em")
224
- .style("text-anchor", function (d) {
225
- if (context.radial()) {
226
- if (d.children) {
227
- return d.x < 180 ? "end" : "start";
228
- } else {
229
- return d.x < 180 ? "start" : "end";
230
- }
231
- } else if (isVertical) {
232
- return d.children ? "start" : "end";
233
- }
234
- return d.children ? "end" : "start";
235
- })
236
- .attr("transform", function (d) {
237
- if (context.radial()) {
238
- return d.x < 180 ? null : "rotate(180)";
239
- } else if (isVertical) {
240
- return "rotate(-66)";
241
- }
242
- return null;
243
- })
244
- .text(function (d) { return d.data.label; })
245
- ;
246
- nodes.exit().remove();
247
-
248
- if (!this._renderCount) {
249
- context.zoomToFit();
250
- }
251
- }
252
- }
253
- Dendrogram.prototype._class += " tree_Dendrogram";
254
- Dendrogram.prototype.implements(ITree.prototype);
255
- Dendrogram.prototype.mixin(Utility.SimpleSelectionMixin);
256
- Dendrogram.prototype.Column = DendrogramColumn;
257
-
258
- export interface Dendrogram {
259
- _palette;
260
-
261
- // ITree ---
262
- click(row, column, selected): void;
263
- dblclick(row, column, selected): void;
264
-
265
- // SimpleSelectionMixin ---
266
- _selection;
267
-
268
- // Properties ---
269
- paletteID(): string;
270
- paletteID(_: string): this;
271
- useClonedPalette(): boolean;
272
- useClonedPalette(_: boolean): this;
273
- mappings(): DendrogramColumn[];
274
- mappings(_: DendrogramColumn[]): this;
275
-
276
- circleRadius(): number;
277
- circleRadius(_: number): this;
278
- separation(): number;
279
- separation(_: number): this;
280
- dendrogram(): boolean;
281
- dendrogram(_: boolean): this;
282
- radial(): boolean;
283
- radial(_: boolean): this;
284
- orientation(): "horizontal" | "vertical";
285
- orientation(_: "horizontal" | "vertical"): this;
286
- }
287
-
288
- Dendrogram.prototype.publish("paletteID", "default", "set", "Color palette for this widget", Dendrogram.prototype._palette.switch(), { tags: ["Basic", "Shared"] });
289
- Dendrogram.prototype.publish("useClonedPalette", false, "boolean", "Enable or disable using a cloned palette", null, { tags: ["Intermediate", "Shared"] });
290
- Dendrogram.prototype.publish("mappings", [], "propertyArray", "Source Columns", null, { autoExpand: DendrogramColumn });
291
-
292
- Dendrogram.prototype.publish("circleRadius", 4.5, "number", "Text offset from circle");
293
- Dendrogram.prototype.publish("separation", 240, "number", "Leaf Separation");
294
- Dendrogram.prototype.publish("dendrogram", true, "boolean", "Dendrogram");
295
- Dendrogram.prototype.publish("radial", false, "boolean", "Radial");
296
- Dendrogram.prototype.publish("orientation", "horizontal", "set", "Orientation", ["horizontal", "vertical"], { tags: ["Private"], disable: w => w.radial() });
1
+ import { ITree } from "@hpcc-js/api";
2
+ import { PropertyExt, SVGZoomWidget, Utility } from "@hpcc-js/common";
3
+ import { cluster as d3Cluster, hierarchy as d3Hierarchy, tree as d3Tree } from "d3-hierarchy";
4
+ import { select as d3Select } from "d3-selection";
5
+
6
+ import "../src/Dendrogram.css";
7
+
8
+ export class DendrogramColumn extends PropertyExt {
9
+ _owner: Dendrogram;
10
+
11
+ constructor() {
12
+ super();
13
+ }
14
+
15
+ owner(): Dendrogram;
16
+ owner(_: Dendrogram): this;
17
+ owner(_?: Dendrogram): Dendrogram | this {
18
+ if (!arguments.length) return this._owner;
19
+ this._owner = _;
20
+ return this;
21
+ }
22
+ valid(): boolean {
23
+ return !!this.column();
24
+ }
25
+
26
+ column: { (): string; (_: string): Dendrogram; };
27
+ }
28
+ DendrogramColumn.prototype._class += " tree_Dendrogram.DendrogramColumn";
29
+
30
+ DendrogramColumn.prototype.publish("column", null, "set", "Field", function (this: DendrogramColumn) { return this._owner ? this._owner.columns() : []; }, { optional: true });
31
+
32
+ // ===
33
+ export class Dendrogram extends SVGZoomWidget {
34
+ Column;
35
+ _d3LayoutCluster;
36
+ _d3LayoutTree;
37
+ _d3Layout;
38
+
39
+ constructor() {
40
+ super();
41
+ ITree.call(this);
42
+ Utility.SimpleSelectionMixin.call(this);
43
+
44
+ this._drawStartPos = "origin";
45
+
46
+ this._d3LayoutCluster = d3Cluster();
47
+ this._d3LayoutTree = d3Tree();
48
+ }
49
+
50
+ dendrogramData() {
51
+ if (this.data().length === 0) return [];
52
+ if (!this.mappings().filter(mapping => mapping.valid()).length) {
53
+ return this.data();
54
+ }
55
+ const view = this._db.rollupView(this.mappings().map(function (mapping) { return mapping.column(); }));
56
+ const retVal = {
57
+ key: "root",
58
+ values: view.entries()
59
+ };
60
+ return formatData(retVal);
61
+
62
+ function formatData(node) {
63
+ return {
64
+ label: node.key,
65
+ children: node.values.filter(function (value) { return !(value instanceof Array); }).map(function (value) { return formatData(value); }),
66
+ origRows: node.values
67
+ };
68
+ }
69
+ }
70
+
71
+ enter(domNode, element) {
72
+ super.enter(domNode, element);
73
+ this._renderElement
74
+ .attr("opacity", 0)
75
+ .transition().duration(500)
76
+ .attr("opacity", 1)
77
+ ;
78
+ this._selection.widgetElement(this._renderElement);
79
+ }
80
+
81
+ update(domNode, element) {
82
+ super.update(domNode, element);
83
+ const context = this;
84
+ const isVertical = this.orientation() === "vertical";
85
+
86
+ this._palette = this._palette.switch(this.paletteID());
87
+ if (this.useClonedPalette()) {
88
+ this._palette = this._palette.cloneNotExists(this.paletteID() + "_" + this.id());
89
+ }
90
+
91
+ this._d3Layout = this.dendrogram() ? this._d3LayoutCluster : this._d3LayoutTree;
92
+
93
+ if (this.radial()) {
94
+ this._d3Layout
95
+ .size([360, this.separation() * 2])
96
+ ;
97
+ this._d3Layout.separation(function separation(a, b) {
98
+ return (a.parent === b.parent ? 1 : 2) / a.depth;
99
+ });
100
+ } else {
101
+ this._d3Layout.nodeSize([14, this.separation()]);
102
+ this._d3Layout.separation(function separation(a, b) {
103
+ return a.parent === b.parent ? 1 : 2;
104
+ });
105
+ }
106
+
107
+ const data = this.dendrogramData();
108
+ const root = d3Hierarchy(data);
109
+ this._d3Layout(root);
110
+
111
+ const dataNodes = root.descendants();
112
+ const links = root.descendants().slice(1);
113
+
114
+ // Lines ---
115
+ function linkVertical(d) {
116
+ return "M" + d.parent.x + "," + d.parent.y
117
+ + "C" + d.parent.x + "," + (d.parent.y + d.y) / 2
118
+ + " " + d.x + "," + (d.parent.y + d.y) / 2
119
+ + " " + d.x + "," + d.y;
120
+ }
121
+
122
+ function linkHorizontal(d) {
123
+ return "M" + d.y + "," + d.x
124
+ + "C" + (d.y + d.parent.y) / 2 + "," + d.x
125
+ + " " + (d.y + d.parent.y) / 2 + "," + d.parent.x
126
+ + " " + d.parent.y + "," + d.parent.x;
127
+ }
128
+ function diagonal(d) {
129
+ return isVertical ? linkVertical(d) : linkHorizontal(d);
130
+ }
131
+
132
+ function project(x, y) {
133
+ const angle = (x - 90) / 180 * Math.PI;
134
+ const radius = y;
135
+ return [radius * Math.cos(angle), radius * Math.sin(angle)];
136
+ }
137
+
138
+ function radialDiagonal(d) {
139
+ return "M" + project(d.x, d.y)
140
+ + "C" + project(d.x, (d.y + d.parent.y) / 2)
141
+ + " " + project(d.parent.x, (d.y + d.parent.y) / 2)
142
+ + " " + project(d.parent.x, d.parent.y);
143
+ }
144
+
145
+ const transitionDuration = this._renderCount ? 500 : 0;
146
+ const lines = this._renderElement.selectAll(".link").data(links);
147
+ lines.enter().append("path")
148
+ .attr("class", "link")
149
+ .attr("d", this.radial() ? radialDiagonal : diagonal)
150
+ ;
151
+ lines.transition().duration(transitionDuration)
152
+ .attr("d", this.radial() ? radialDiagonal : diagonal)
153
+ ;
154
+ lines.exit().remove();
155
+
156
+ // Nodes ---
157
+ const textOffsetX = this.circleRadius() + 2;
158
+ function nodeTransform(d) {
159
+ if (context.radial()) {
160
+ return "rotate(" + (d.x - 90) + ")translate(" + d.y + ")";
161
+ }
162
+ return context.orientation() === "horizontal" ? "translate(" + d.y + "," + d.x + ")" : "translate(" + d.x + "," + d.y + ")";
163
+ }
164
+ const nodes = this._renderElement.selectAll(".node").data(dataNodes);
165
+ nodes.transition().duration(transitionDuration)
166
+ .attr("transform", nodeTransform)
167
+ ;
168
+ const enterNodes = nodes.enter().append("g")
169
+ .attr("class", "node")
170
+ .attr("transform", nodeTransform)
171
+ .call(this._selection.enter.bind(this._selection))
172
+ .on("click", function (d) {
173
+ let tmp = d;
174
+ while (tmp.children) {
175
+ tmp = tmp.children[0];
176
+ }
177
+ if (d.depth > 0) {
178
+ if (tmp.origRows) {
179
+ context.click(context.rowToObj(tmp.origRows[0]), context.mappings()[d.depth - 1].column(), true);
180
+ } else {
181
+ context.click(tmp.data, context.mappings()[d.depth - 1].column(), true);
182
+ }
183
+ }
184
+ })
185
+ .on("dblclick", function (d) {
186
+ let tmp = d;
187
+ while (tmp.children) {
188
+ tmp = tmp.children[0];
189
+ }
190
+ if (d.depth > 0) {
191
+ if (tmp.origRows) {
192
+ context.dblclick(context.rowToObj(tmp.origRows[0]), context.mappings()[d.depth - 1].column(), true);
193
+ } else {
194
+ context.dblclick(tmp.data, context.mappings()[d.depth - 1].column(), true);
195
+ }
196
+ }
197
+ })
198
+ .each(function () {
199
+ const e = d3Select(this);
200
+ e.append("circle");
201
+ e.append("text");
202
+ })
203
+ ;
204
+ enterNodes.merge(nodes).select("circle")
205
+ .attr("r", this.circleRadius())
206
+ .style("fill", function (d) { return context._palette(d.data.label); })
207
+ .append("title")
208
+ .text(function (d) { return d.data.label; })
209
+ ;
210
+ enterNodes.merge(nodes).select("text")
211
+ .attr("dx", function (d) {
212
+ if (context.radial()) {
213
+ if (d.children) {
214
+ return d.x < 180 ? -textOffsetX : textOffsetX;
215
+ } else {
216
+ return d.x < 180 ? textOffsetX : -textOffsetX;
217
+ }
218
+ } else if (isVertical) {
219
+ return d.children ? textOffsetX : -textOffsetX;
220
+ }
221
+ return d.children ? -textOffsetX : textOffsetX;
222
+ })
223
+ .attr("dy", "0.25em")
224
+ .style("text-anchor", function (d) {
225
+ if (context.radial()) {
226
+ if (d.children) {
227
+ return d.x < 180 ? "end" : "start";
228
+ } else {
229
+ return d.x < 180 ? "start" : "end";
230
+ }
231
+ } else if (isVertical) {
232
+ return d.children ? "start" : "end";
233
+ }
234
+ return d.children ? "end" : "start";
235
+ })
236
+ .attr("transform", function (d) {
237
+ if (context.radial()) {
238
+ return d.x < 180 ? null : "rotate(180)";
239
+ } else if (isVertical) {
240
+ return "rotate(-66)";
241
+ }
242
+ return null;
243
+ })
244
+ .text(function (d) { return d.data.label; })
245
+ ;
246
+ nodes.exit().remove();
247
+
248
+ if (!this._renderCount) {
249
+ context.zoomToFit();
250
+ }
251
+ }
252
+ }
253
+ Dendrogram.prototype._class += " tree_Dendrogram";
254
+ Dendrogram.prototype.implements(ITree.prototype);
255
+ Dendrogram.prototype.mixin(Utility.SimpleSelectionMixin);
256
+ Dendrogram.prototype.Column = DendrogramColumn;
257
+
258
+ export interface Dendrogram {
259
+ _palette;
260
+
261
+ // ITree ---
262
+ click(row, column, selected): void;
263
+ dblclick(row, column, selected): void;
264
+
265
+ // SimpleSelectionMixin ---
266
+ _selection;
267
+
268
+ // Properties ---
269
+ paletteID(): string;
270
+ paletteID(_: string): this;
271
+ useClonedPalette(): boolean;
272
+ useClonedPalette(_: boolean): this;
273
+ mappings(): DendrogramColumn[];
274
+ mappings(_: DendrogramColumn[]): this;
275
+
276
+ circleRadius(): number;
277
+ circleRadius(_: number): this;
278
+ separation(): number;
279
+ separation(_: number): this;
280
+ dendrogram(): boolean;
281
+ dendrogram(_: boolean): this;
282
+ radial(): boolean;
283
+ radial(_: boolean): this;
284
+ orientation(): "horizontal" | "vertical";
285
+ orientation(_: "horizontal" | "vertical"): this;
286
+ }
287
+
288
+ Dendrogram.prototype.publish("paletteID", "default", "set", "Color palette for this widget", Dendrogram.prototype._palette.switch(), { tags: ["Basic", "Shared"] });
289
+ Dendrogram.prototype.publish("useClonedPalette", false, "boolean", "Enable or disable using a cloned palette", null, { tags: ["Intermediate", "Shared"] });
290
+ Dendrogram.prototype.publish("mappings", [], "propertyArray", "Source Columns", null, { autoExpand: DendrogramColumn });
291
+
292
+ Dendrogram.prototype.publish("circleRadius", 4.5, "number", "Text offset from circle");
293
+ Dendrogram.prototype.publish("separation", 240, "number", "Leaf Separation");
294
+ Dendrogram.prototype.publish("dendrogram", true, "boolean", "Dendrogram");
295
+ Dendrogram.prototype.publish("radial", false, "boolean", "Radial");
296
+ Dendrogram.prototype.publish("orientation", "horizontal", "set", "Orientation", ["horizontal", "vertical"], { tags: ["Private"], disable: w => w.radial() });