@jrkasprzyk/parcoord-es 3.0.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/LICENSE +21 -0
- package/README.md +160 -0
- package/dist/parcoords.css +61 -0
- package/dist/parcoords.esm.js +4132 -0
- package/dist/parcoords.esm.js.map +1 -0
- package/dist/parcoords.js +4130 -0
- package/dist/parcoords.js.map +1 -0
- package/dist/parcoords.standalone.js +11233 -0
- package/dist/parcoords.standalone.js.map +1 -0
- package/package.json +89 -0
- package/src/api/adjacentPairs.js +10 -0
- package/src/api/applyAxisConfig.js +35 -0
- package/src/api/applyDimensionDefaults.js +25 -0
- package/src/api/autoscale.js +133 -0
- package/src/api/axisDots.js +27 -0
- package/src/api/brushMode.js +50 -0
- package/src/api/brushReset.js +34 -0
- package/src/api/brushable.js +81 -0
- package/src/api/clear.js +21 -0
- package/src/api/commonScale.js +45 -0
- package/src/api/computeRealCentroids.js +8 -0
- package/src/api/createAxes.js +101 -0
- package/src/api/detectDimensionTypes.js +12 -0
- package/src/api/detectDimensions.js +7 -0
- package/src/api/flip.js +11 -0
- package/src/api/getOrderedDimensionKeys.js +8 -0
- package/src/api/highlight.js +26 -0
- package/src/api/init.js +53 -0
- package/src/api/interactive.js +7 -0
- package/src/api/intersection.js +16 -0
- package/src/api/mark.js +20 -0
- package/src/api/mergeParcoords.js +86 -0
- package/src/api/removeAxes.js +9 -0
- package/src/api/render.js +26 -0
- package/src/api/renderBrushed.js +39 -0
- package/src/api/renderDefault.js +27 -0
- package/src/api/renderMarked.js +34 -0
- package/src/api/reorder.js +37 -0
- package/src/api/reorderable.js +41 -0
- package/src/api/resize.js +36 -0
- package/src/api/scale.js +10 -0
- package/src/api/selected.js +209 -0
- package/src/api/shadows.js +9 -0
- package/src/api/sortDimensions.js +13 -0
- package/src/api/sortDimensionsByRowData.js +20 -0
- package/src/api/toString.js +11 -0
- package/src/api/toType.js +9 -0
- package/src/api/toTypeCoerceNumbers.js +7 -0
- package/src/api/unhighlight.js +12 -0
- package/src/api/unmark.js +12 -0
- package/src/api/updateAxes.js +103 -0
- package/src/bindEvents.js +60 -0
- package/src/brush/1d/brushExtents.js +73 -0
- package/src/brush/1d/brushFor.js +92 -0
- package/src/brush/1d/brushReset.js +36 -0
- package/src/brush/1d/index.js +20 -0
- package/src/brush/1d/install.js +41 -0
- package/src/brush/1d/selected.js +79 -0
- package/src/brush/1d/uninstall.js +12 -0
- package/src/brush/1d-multi/brushExtents.js +101 -0
- package/src/brush/1d-multi/brushFor.js +13 -0
- package/src/brush/1d-multi/brushReset.js +53 -0
- package/src/brush/1d-multi/drawBrushes.js +36 -0
- package/src/brush/1d-multi/index.js +20 -0
- package/src/brush/1d-multi/install.js +29 -0
- package/src/brush/1d-multi/newBrush.js +102 -0
- package/src/brush/1d-multi/selected.js +160 -0
- package/src/brush/1d-multi/uninstall.js +12 -0
- package/src/brush/angular/brushReset.js +14 -0
- package/src/brush/angular/index.js +19 -0
- package/src/brush/angular/install.js +141 -0
- package/src/brush/angular/onDrag.js +109 -0
- package/src/brush/angular/onDragEnd.js +30 -0
- package/src/brush/angular/onDragStart.js +38 -0
- package/src/brush/angular/removeStrum.js +12 -0
- package/src/brush/angular/selected.js +63 -0
- package/src/brush/angular/uninstall.js +17 -0
- package/src/brush/angular/util/hypothenuse.js +3 -0
- package/src/brush/consecutive.js +11 -0
- package/src/brush/dimensionsForPoint.js +32 -0
- package/src/brush/invertByScale.js +24 -0
- package/src/brush/strums/brushReset.js +14 -0
- package/src/brush/strums/index.js +19 -0
- package/src/brush/strums/install.js +81 -0
- package/src/brush/strums/onDrag.js +83 -0
- package/src/brush/strums/onDragEnd.js +20 -0
- package/src/brush/strums/onDragStart.js +33 -0
- package/src/brush/strums/removeStrum.js +10 -0
- package/src/brush/strums/selected.js +52 -0
- package/src/brush/strums/uninstall.js +16 -0
- package/src/index.js +226 -0
- package/src/parallel-coordinates.css +61 -0
- package/src/state/defaultConfig.js +36 -0
- package/src/state/index.js +86 -0
- package/src/state/sideEffects.js +89 -0
- package/src/util/colorPath.js +71 -0
- package/src/util/computeCentroids.js +44 -0
- package/src/util/computeClusterCentroids.js +33 -0
- package/src/util/computeControlPoints.js +38 -0
- package/src/util/dimensionLabels.js +11 -0
- package/src/util/flipAxisAndUpdatePCP.js +19 -0
- package/src/util/functor.js +3 -0
- package/src/util/getRange.js +24 -0
- package/src/util/getset.js +23 -0
- package/src/util/height.js +3 -0
- package/src/util/isBrushed.js +15 -0
- package/src/util/renderQueue.js +67 -0
- package/src/util/rotateLabels.js +18 -0
- package/src/util/width.js +3 -0
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
import { brushSelection } from 'd3-brush';
|
|
2
|
+
|
|
3
|
+
const selected = (config, pc) => () => {
|
|
4
|
+
let actives = [];
|
|
5
|
+
let extents = [];
|
|
6
|
+
let ranges = {};
|
|
7
|
+
//get brush selections from each node, convert to actual values
|
|
8
|
+
//invert order of values in array to comply with the parcoords architecture
|
|
9
|
+
if (config.brushes.length === 0) {
|
|
10
|
+
let nodes = pc
|
|
11
|
+
.g()
|
|
12
|
+
.selectAll('.brush')
|
|
13
|
+
.nodes();
|
|
14
|
+
for (let k = 0; k < nodes.length; k++) {
|
|
15
|
+
if (brushSelection(nodes[k]) !== null) {
|
|
16
|
+
actives.push(nodes[k].__data__);
|
|
17
|
+
let values = [];
|
|
18
|
+
let ranger = brushSelection(nodes[k]);
|
|
19
|
+
if (
|
|
20
|
+
typeof config.dimensions[nodes[k].__data__].yscale.domain()[0] ===
|
|
21
|
+
'number'
|
|
22
|
+
) {
|
|
23
|
+
for (let i = 0; i < ranger.length; i++) {
|
|
24
|
+
if (
|
|
25
|
+
actives.includes(nodes[k].__data__) &&
|
|
26
|
+
config.flipAxes.includes(nodes[k].__data__)
|
|
27
|
+
) {
|
|
28
|
+
values.push(
|
|
29
|
+
config.dimensions[nodes[k].__data__].yscale.invert(ranger[i])
|
|
30
|
+
);
|
|
31
|
+
} else if (config.dimensions[nodes[k].__data__].yscale() !== 1) {
|
|
32
|
+
values.unshift(
|
|
33
|
+
config.dimensions[nodes[k].__data__].yscale.invert(ranger[i])
|
|
34
|
+
);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
extents.push(values);
|
|
38
|
+
for (let ii = 0; ii < extents.length; ii++) {
|
|
39
|
+
if (extents[ii].length === 0) {
|
|
40
|
+
extents[ii] = [1, 1];
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
} else {
|
|
44
|
+
ranges[nodes[k].__data__] = brushSelection(nodes[k]);
|
|
45
|
+
let dimRange = config.dimensions[nodes[k].__data__].yscale.range();
|
|
46
|
+
let dimDomain = config.dimensions[nodes[k].__data__].yscale.domain();
|
|
47
|
+
for (let j = 0; j < dimRange.length; j++) {
|
|
48
|
+
if (
|
|
49
|
+
dimRange[j] >= ranger[0] &&
|
|
50
|
+
dimRange[j] <= ranger[1] &&
|
|
51
|
+
actives.includes(nodes[k].__data__) &&
|
|
52
|
+
config.flipAxes.includes(nodes[k].__data__)
|
|
53
|
+
) {
|
|
54
|
+
values.push(dimRange[j]);
|
|
55
|
+
} else if (dimRange[j] >= ranger[0] && dimRange[j] <= ranger[1]) {
|
|
56
|
+
values.unshift(dimRange[j]);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
extents.push(values);
|
|
60
|
+
for (let ii = 0; ii < extents.length; ii++) {
|
|
61
|
+
if (extents[ii].length === 0) {
|
|
62
|
+
extents[ii] = [1, 1];
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
// test if within range
|
|
69
|
+
const within = {
|
|
70
|
+
date: function(d, p, dimension) {
|
|
71
|
+
let category = d[p];
|
|
72
|
+
let categoryIndex = config.dimensions[p].yscale
|
|
73
|
+
.domain()
|
|
74
|
+
.indexOf(category);
|
|
75
|
+
let categoryRangeValue = config.dimensions[p].yscale.range()[
|
|
76
|
+
categoryIndex
|
|
77
|
+
];
|
|
78
|
+
return (
|
|
79
|
+
categoryRangeValue >= ranges[p][0] &&
|
|
80
|
+
categoryRangeValue <= ranges[p][1]
|
|
81
|
+
);
|
|
82
|
+
},
|
|
83
|
+
number: function(d, p, dimension) {
|
|
84
|
+
return extents[dimension][0] <= d[p] && d[p] <= extents[dimension][1];
|
|
85
|
+
},
|
|
86
|
+
string: function(d, p, dimension) {
|
|
87
|
+
let category = d[p];
|
|
88
|
+
let categoryIndex = config.dimensions[p].yscale
|
|
89
|
+
.domain()
|
|
90
|
+
.indexOf(category);
|
|
91
|
+
let categoryRangeValue = config.dimensions[p].yscale.range()[
|
|
92
|
+
categoryIndex
|
|
93
|
+
];
|
|
94
|
+
return (
|
|
95
|
+
categoryRangeValue >= ranges[p][0] &&
|
|
96
|
+
categoryRangeValue <= ranges[p][1]
|
|
97
|
+
);
|
|
98
|
+
},
|
|
99
|
+
};
|
|
100
|
+
return config.data.filter(d =>
|
|
101
|
+
actives.every((p, dimension) =>
|
|
102
|
+
within[config.dimensions[p].type](d, p, dimension)
|
|
103
|
+
)
|
|
104
|
+
);
|
|
105
|
+
} else {
|
|
106
|
+
// need to get data from each brush instead of each axis
|
|
107
|
+
// first must find active axes by iterating through all brushes
|
|
108
|
+
// then go through similiar process as above.
|
|
109
|
+
let multiBrushData = [];
|
|
110
|
+
for (let idx = 0; idx < config.brushes.length; idx++) {
|
|
111
|
+
let brush = config.brushes[idx];
|
|
112
|
+
let values = [];
|
|
113
|
+
let ranger = brush.extent;
|
|
114
|
+
let actives = [brush.data];
|
|
115
|
+
if (
|
|
116
|
+
typeof config.dimensions[brush.data].yscale.domain()[0] === 'number'
|
|
117
|
+
) {
|
|
118
|
+
for (let i = 0; i < ranger.length; i++) {
|
|
119
|
+
if (
|
|
120
|
+
actives.includes(brush.data) &&
|
|
121
|
+
config.flipAxes.includes(brush.data)
|
|
122
|
+
) {
|
|
123
|
+
values.push(config.dimensions[brush.data].yscale.invert(ranger[i]));
|
|
124
|
+
} else if (config.dimensions[brush.data].yscale() !== 1) {
|
|
125
|
+
values.unshift(
|
|
126
|
+
config.dimensions[brush.data].yscale.invert(ranger[i])
|
|
127
|
+
);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
extents.push(values);
|
|
131
|
+
for (let ii = 0; ii < extents.length; ii++) {
|
|
132
|
+
if (extents[ii].length === 0) {
|
|
133
|
+
extents[ii] = [1, 1];
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
} else {
|
|
137
|
+
ranges[brush.data] = brush.extent;
|
|
138
|
+
let dimRange = config.dimensions[brush.data].yscale.range();
|
|
139
|
+
let dimDomain = config.dimensions[brush.data].yscale.domain();
|
|
140
|
+
for (let j = 0; j < dimRange.length; j++) {
|
|
141
|
+
if (
|
|
142
|
+
dimRange[j] >= ranger[0] &&
|
|
143
|
+
dimRange[j] <= ranger[1] &&
|
|
144
|
+
actives.includes(brush.data) &&
|
|
145
|
+
config.flipAxes.includes(brush.data)
|
|
146
|
+
) {
|
|
147
|
+
values.push(dimRange[j]);
|
|
148
|
+
} else if (dimRange[j] >= ranger[0] && dimRange[j] <= ranger[1]) {
|
|
149
|
+
values.unshift(dimRange[j]);
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
extents.push(values);
|
|
153
|
+
for (let ii = 0; ii < extents.length; ii++) {
|
|
154
|
+
if (extents[ii].length === 0) {
|
|
155
|
+
extents[ii] = [1, 1];
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
let within = {
|
|
160
|
+
date: function(d, p, dimension) {
|
|
161
|
+
let category = d[p];
|
|
162
|
+
let categoryIndex = config.dimensions[p].yscale
|
|
163
|
+
.domain()
|
|
164
|
+
.indexOf(category);
|
|
165
|
+
let categoryRangeValue = config.dimensions[p].yscale.range()[
|
|
166
|
+
categoryIndex
|
|
167
|
+
];
|
|
168
|
+
return (
|
|
169
|
+
categoryRangeValue >= ranges[p][0] &&
|
|
170
|
+
categoryRangeValue <= ranges[p][1]
|
|
171
|
+
);
|
|
172
|
+
},
|
|
173
|
+
number: function(d, p, dimension) {
|
|
174
|
+
return extents[idx][0] <= d[p] && d[p] <= extents[idx][1];
|
|
175
|
+
},
|
|
176
|
+
string: function(d, p, dimension) {
|
|
177
|
+
let category = d[p];
|
|
178
|
+
let categoryIndex = config.dimensions[p].yscale
|
|
179
|
+
.domain()
|
|
180
|
+
.indexOf(category);
|
|
181
|
+
let categoryRangeValue = config.dimensions[p].yscale.range()[
|
|
182
|
+
categoryIndex
|
|
183
|
+
];
|
|
184
|
+
return (
|
|
185
|
+
categoryRangeValue >= ranges[p][0] &&
|
|
186
|
+
categoryRangeValue <= ranges[p][1]
|
|
187
|
+
);
|
|
188
|
+
},
|
|
189
|
+
};
|
|
190
|
+
|
|
191
|
+
// filter data, but instead of returning it now,
|
|
192
|
+
// put it into multiBrush data which is returned after
|
|
193
|
+
// all brushes are iterated through.
|
|
194
|
+
let filtered = config.data.filter(d =>
|
|
195
|
+
actives.every((p, dimension) =>
|
|
196
|
+
within[config.dimensions[p].type](d, p, dimension)
|
|
197
|
+
)
|
|
198
|
+
);
|
|
199
|
+
for (let z = 0; z < filtered.length; z++) {
|
|
200
|
+
multiBrushData.push(filtered[z]);
|
|
201
|
+
}
|
|
202
|
+
actives = [];
|
|
203
|
+
ranges = {};
|
|
204
|
+
}
|
|
205
|
+
return multiBrushData;
|
|
206
|
+
}
|
|
207
|
+
};
|
|
208
|
+
|
|
209
|
+
export default selected;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
const sortDimensions = (config, position) => () => {
|
|
2
|
+
const copy = Object.assign({}, config.dimensions);
|
|
3
|
+
const positionSortedKeys = Object.keys(config.dimensions).sort(
|
|
4
|
+
(a, b) => (position(a) - position(b) === 0 ? 1 : position(a) - position(b))
|
|
5
|
+
);
|
|
6
|
+
config.dimensions = {};
|
|
7
|
+
positionSortedKeys.forEach((p, i) => {
|
|
8
|
+
config.dimensions[p] = copy[p];
|
|
9
|
+
config.dimensions[p].index = i;
|
|
10
|
+
});
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
export default sortDimensions;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
const sortDimensionsByRowData = config => rowdata => {
|
|
2
|
+
const copy = Object.assign({}, config.dimensions);
|
|
3
|
+
const positionSortedKeys = Object.keys(config.dimensions).sort((a, b) => {
|
|
4
|
+
const pixelDifference =
|
|
5
|
+
config.dimensions[a].yscale(rowdata[a]) -
|
|
6
|
+
config.dimensions[b].yscale(rowdata[b]);
|
|
7
|
+
|
|
8
|
+
// Array.sort is not necessarily stable, this means that if pixelDifference is zero
|
|
9
|
+
// the ordering of dimensions might change unexpectedly. This is solved by sorting on
|
|
10
|
+
// variable name in that case.
|
|
11
|
+
return pixelDifference === 0 ? a.localeCompare(b) : pixelDifference;
|
|
12
|
+
});
|
|
13
|
+
config.dimensions = {};
|
|
14
|
+
positionSortedKeys.forEach((p, i) => {
|
|
15
|
+
config.dimensions[p] = copy[p];
|
|
16
|
+
config.dimensions[p].index = i;
|
|
17
|
+
});
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
export default sortDimensionsByRowData;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
// this descriptive text should live with other introspective methods
|
|
2
|
+
const toString = config => () =>
|
|
3
|
+
'Parallel Coordinates: ' +
|
|
4
|
+
Object.keys(config.dimensions).length +
|
|
5
|
+
' dimensions (' +
|
|
6
|
+
Object.keys(config.data[0]).length +
|
|
7
|
+
' total) , ' +
|
|
8
|
+
config.data.length +
|
|
9
|
+
' rows';
|
|
10
|
+
|
|
11
|
+
export default toString;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { selectAll } from 'd3-selection';
|
|
2
|
+
|
|
3
|
+
// clear highlighting
|
|
4
|
+
const unhighlight = (config, pc, canvas) =>
|
|
5
|
+
function() {
|
|
6
|
+
config.highlighted = [];
|
|
7
|
+
pc.clear('highlight');
|
|
8
|
+
selectAll([canvas.foreground, canvas.brushed]).classed('faded', false);
|
|
9
|
+
return this;
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
export default unhighlight;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { selectAll } from 'd3-selection';
|
|
2
|
+
|
|
3
|
+
// clear marked data arrays
|
|
4
|
+
const unmark = (config, pc, canvas) =>
|
|
5
|
+
function() {
|
|
6
|
+
config.marked = [];
|
|
7
|
+
pc.clear('marked');
|
|
8
|
+
selectAll([canvas.foreground, canvas.brushed]).classed('dimmed', false);
|
|
9
|
+
return this;
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
export default unmark;
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import { select } from 'd3-selection';
|
|
2
|
+
|
|
3
|
+
import dimensionLabels from '../util/dimensionLabels';
|
|
4
|
+
import flipAxisAndUpdatePCP from '../util/flipAxisAndUpdatePCP';
|
|
5
|
+
import rotateLabels from '../util/rotateLabels';
|
|
6
|
+
|
|
7
|
+
const updateAxes = (config, pc, position, axis, flags) => (
|
|
8
|
+
animationTime = null
|
|
9
|
+
) => {
|
|
10
|
+
if (animationTime === null) {
|
|
11
|
+
animationTime = config.animationTime;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const g_data = pc.svg
|
|
15
|
+
.selectAll('.dimension')
|
|
16
|
+
.data(pc.getOrderedDimensionKeys());
|
|
17
|
+
// Enter
|
|
18
|
+
g_data
|
|
19
|
+
.enter()
|
|
20
|
+
.append('svg:g')
|
|
21
|
+
.attr('class', 'dimension')
|
|
22
|
+
.attr('transform', p => 'translate(' + position(p) + ')')
|
|
23
|
+
.style('opacity', 0)
|
|
24
|
+
.append('svg:g')
|
|
25
|
+
.attr('class', 'axis')
|
|
26
|
+
.attr('transform', 'translate(0,0)')
|
|
27
|
+
.each(function(d) {
|
|
28
|
+
const axisElement = select(this).call(
|
|
29
|
+
pc.applyAxisConfig(axis, config.dimensions[d])
|
|
30
|
+
);
|
|
31
|
+
|
|
32
|
+
axisElement
|
|
33
|
+
.selectAll('path')
|
|
34
|
+
.style('fill', 'none')
|
|
35
|
+
.style('stroke', '#222')
|
|
36
|
+
.style('shape-rendering', 'crispEdges');
|
|
37
|
+
|
|
38
|
+
axisElement
|
|
39
|
+
.selectAll('line')
|
|
40
|
+
.style('fill', 'none')
|
|
41
|
+
.style('stroke', '#222')
|
|
42
|
+
.style('shape-rendering', 'crispEdges');
|
|
43
|
+
})
|
|
44
|
+
.append('svg:text')
|
|
45
|
+
.attr('text-anchor', 'middle')
|
|
46
|
+
.attr('class', 'label')
|
|
47
|
+
.attr('x', 0)
|
|
48
|
+
.attr('y', 0)
|
|
49
|
+
.attr(
|
|
50
|
+
'transform',
|
|
51
|
+
'translate(0,-5) rotate(' + config.dimensionTitleRotation + ')'
|
|
52
|
+
)
|
|
53
|
+
.text(dimensionLabels(config))
|
|
54
|
+
.on('dblclick', flipAxisAndUpdatePCP(config, pc, axis))
|
|
55
|
+
.on('wheel', rotateLabels(config, pc));
|
|
56
|
+
|
|
57
|
+
// Update
|
|
58
|
+
g_data.attr('opacity', 0);
|
|
59
|
+
g_data
|
|
60
|
+
.select('.axis')
|
|
61
|
+
.transition()
|
|
62
|
+
.duration(animationTime)
|
|
63
|
+
.each(function(d) {
|
|
64
|
+
select(this).call(pc.applyAxisConfig(axis, config.dimensions[d]));
|
|
65
|
+
});
|
|
66
|
+
g_data
|
|
67
|
+
.select('.label')
|
|
68
|
+
.transition()
|
|
69
|
+
.duration(animationTime)
|
|
70
|
+
.text(dimensionLabels(config))
|
|
71
|
+
.attr(
|
|
72
|
+
'transform',
|
|
73
|
+
'translate(0,-5) rotate(' + config.dimensionTitleRotation + ')'
|
|
74
|
+
);
|
|
75
|
+
|
|
76
|
+
// Exit
|
|
77
|
+
g_data.exit().remove();
|
|
78
|
+
|
|
79
|
+
const g = pc.svg.selectAll('.dimension');
|
|
80
|
+
g.transition()
|
|
81
|
+
.duration(animationTime)
|
|
82
|
+
.attr('transform', p => 'translate(' + position(p) + ')')
|
|
83
|
+
.style('opacity', 1);
|
|
84
|
+
|
|
85
|
+
pc.svg
|
|
86
|
+
.selectAll('.axis')
|
|
87
|
+
.transition()
|
|
88
|
+
.duration(animationTime)
|
|
89
|
+
.each(function(d) {
|
|
90
|
+
select(this).call(pc.applyAxisConfig(axis, config.dimensions[d]));
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
if (flags.brushable) pc.brushable();
|
|
94
|
+
if (flags.reorderable) pc.reorderable();
|
|
95
|
+
if (pc.brushMode() !== 'None') {
|
|
96
|
+
const mode = pc.brushMode();
|
|
97
|
+
pc.brushMode('None');
|
|
98
|
+
pc.brushMode(mode);
|
|
99
|
+
}
|
|
100
|
+
return this;
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
export default updateAxes;
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
// side effects for setters
|
|
2
|
+
import sideEffects from './state/sideEffects';
|
|
3
|
+
import getset from './util/getset';
|
|
4
|
+
|
|
5
|
+
const d3_rebind = (target, source, method) =>
|
|
6
|
+
function() {
|
|
7
|
+
const value = method.apply(source, arguments);
|
|
8
|
+
return value === source ? target : value;
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
const _rebind = (target, source, method) => {
|
|
12
|
+
target[method] = d3_rebind(target, source, source[method]);
|
|
13
|
+
return target;
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
const bindEvents = (
|
|
17
|
+
__,
|
|
18
|
+
ctx,
|
|
19
|
+
pc,
|
|
20
|
+
xscale,
|
|
21
|
+
flags,
|
|
22
|
+
brushedQueue,
|
|
23
|
+
markedQueue,
|
|
24
|
+
foregroundQueue,
|
|
25
|
+
events,
|
|
26
|
+
axis
|
|
27
|
+
) => {
|
|
28
|
+
const side_effects = sideEffects(
|
|
29
|
+
__,
|
|
30
|
+
ctx,
|
|
31
|
+
pc,
|
|
32
|
+
xscale,
|
|
33
|
+
axis,
|
|
34
|
+
flags,
|
|
35
|
+
brushedQueue,
|
|
36
|
+
markedQueue,
|
|
37
|
+
foregroundQueue
|
|
38
|
+
);
|
|
39
|
+
|
|
40
|
+
// create getter/setters
|
|
41
|
+
getset(pc, __, events, side_effects);
|
|
42
|
+
|
|
43
|
+
// expose events
|
|
44
|
+
// getter/setter with event firing
|
|
45
|
+
_rebind(pc, events, 'on');
|
|
46
|
+
|
|
47
|
+
_rebind(
|
|
48
|
+
pc,
|
|
49
|
+
axis,
|
|
50
|
+
'ticks',
|
|
51
|
+
'orient',
|
|
52
|
+
'tickValues',
|
|
53
|
+
'tickSubdivide',
|
|
54
|
+
'tickSize',
|
|
55
|
+
'tickPadding',
|
|
56
|
+
'tickFormat'
|
|
57
|
+
);
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
export default bindEvents;
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { select } from 'd3-selection';
|
|
2
|
+
import { brushSelection } from 'd3-brush';
|
|
3
|
+
|
|
4
|
+
import invertByScale from '../invertByScale';
|
|
5
|
+
|
|
6
|
+
const brushExtents = (state, config, pc) => extents => {
|
|
7
|
+
const { brushes, brushNodes } = state;
|
|
8
|
+
|
|
9
|
+
if (typeof extents === 'undefined') {
|
|
10
|
+
return Object.keys(config.dimensions).reduce((acc, cur) => {
|
|
11
|
+
const brush = brushes[cur];
|
|
12
|
+
//todo: brush check
|
|
13
|
+
if (brush !== undefined && brushSelection(brushNodes[cur]) !== null) {
|
|
14
|
+
const raw = brushSelection(brushNodes[cur]);
|
|
15
|
+
const yScale = config.dimensions[cur].yscale;
|
|
16
|
+
const scaled = invertByScale(raw, yScale);
|
|
17
|
+
|
|
18
|
+
acc[cur] = {
|
|
19
|
+
extent: brush.extent(),
|
|
20
|
+
selection: {
|
|
21
|
+
raw,
|
|
22
|
+
scaled,
|
|
23
|
+
},
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
return acc;
|
|
28
|
+
}, {});
|
|
29
|
+
} else {
|
|
30
|
+
//first get all the brush selections
|
|
31
|
+
const brushSelections = {};
|
|
32
|
+
pc.g()
|
|
33
|
+
.selectAll('.brush')
|
|
34
|
+
.each(function(d) {
|
|
35
|
+
brushSelections[d] = select(this);
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
// loop over each dimension and update appropriately (if it was passed in through extents)
|
|
39
|
+
Object.keys(config.dimensions).forEach(d => {
|
|
40
|
+
if (extents[d] === undefined) {
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const brush = brushes[d];
|
|
45
|
+
if (brush !== undefined) {
|
|
46
|
+
const dim = config.dimensions[d];
|
|
47
|
+
const yExtent = extents[d].map(dim.yscale);
|
|
48
|
+
|
|
49
|
+
//update the extent
|
|
50
|
+
//sets the brushable extent to the specified array of points [[x0, y0], [x1, y1]]
|
|
51
|
+
//we actually don't need this since we are using brush.move below
|
|
52
|
+
//extents set the limits of the brush which means a user will not be able
|
|
53
|
+
//to move or drag the brush beyond the limits set by brush.extent
|
|
54
|
+
//brush.extent([[-15, yExtent[1]], [15, yExtent[0]]]);
|
|
55
|
+
|
|
56
|
+
//redraw the brush
|
|
57
|
+
//https://github.com/d3/d3-brush#brush_move
|
|
58
|
+
// For an x-brush, it must be defined as [x0, x1]; for a y-brush, it must be defined as [y0, y1].
|
|
59
|
+
brushSelections[d].call(brush).call(brush.move, yExtent.reverse());
|
|
60
|
+
|
|
61
|
+
//fire some events
|
|
62
|
+
// brush.event(brushSelections[d]);
|
|
63
|
+
}
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
//redraw the chart
|
|
67
|
+
pc.renderBrushed();
|
|
68
|
+
|
|
69
|
+
return pc;
|
|
70
|
+
}
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
export default brushExtents;
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import { brushY, brushSelection } from 'd3-brush';
|
|
2
|
+
import invertByScale from '../invertByScale';
|
|
3
|
+
import selected from './selected';
|
|
4
|
+
|
|
5
|
+
const brushUpdated = (config, pc, events, args) => newSelection => {
|
|
6
|
+
config.brushed = newSelection;
|
|
7
|
+
events.call('brush', pc, config.brushed, args);
|
|
8
|
+
pc.renderBrushed();
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
const brushFor = (state, config, pc, events, brushGroup) => (
|
|
12
|
+
axis,
|
|
13
|
+
_selector
|
|
14
|
+
) => {
|
|
15
|
+
// handle hidden axes which will not be a property of dimensions
|
|
16
|
+
if (!config.dimensions.hasOwnProperty(axis)) {
|
|
17
|
+
return () => {};
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const brushRangeMax =
|
|
21
|
+
config.dimensions[axis].type === 'string'
|
|
22
|
+
? config.dimensions[axis].yscale.range()[
|
|
23
|
+
config.dimensions[axis].yscale.range().length - 1
|
|
24
|
+
]
|
|
25
|
+
: config.dimensions[axis].yscale.range()[0];
|
|
26
|
+
|
|
27
|
+
const _brush = brushY(_selector).extent([[-15, 0], [15, brushRangeMax]]);
|
|
28
|
+
|
|
29
|
+
const convertBrushArguments = args => {
|
|
30
|
+
const args_array = Array.prototype.slice.call(args);
|
|
31
|
+
const axis = args_array[0];
|
|
32
|
+
|
|
33
|
+
const raw = brushSelection(args_array[2][0]) || [];
|
|
34
|
+
|
|
35
|
+
// handle hidden axes which will not have a yscale
|
|
36
|
+
let yscale = null;
|
|
37
|
+
if (config.dimensions.hasOwnProperty(axis)) {
|
|
38
|
+
yscale = config.dimensions[axis].yscale;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// ordinal scales do not have invert
|
|
42
|
+
const scaled = invertByScale(raw, yscale);
|
|
43
|
+
|
|
44
|
+
return {
|
|
45
|
+
axis: args_array[0],
|
|
46
|
+
node: args_array[2][0],
|
|
47
|
+
selection: {
|
|
48
|
+
raw,
|
|
49
|
+
scaled,
|
|
50
|
+
},
|
|
51
|
+
};
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
_brush
|
|
55
|
+
.on('start', function(event) {
|
|
56
|
+
if (event.sourceEvent !== null) {
|
|
57
|
+
events.call(
|
|
58
|
+
'brushstart',
|
|
59
|
+
pc,
|
|
60
|
+
config.brushed,
|
|
61
|
+
convertBrushArguments(arguments)
|
|
62
|
+
);
|
|
63
|
+
if (typeof event.sourceEvent.stopPropagation === 'function') {
|
|
64
|
+
event.sourceEvent.stopPropagation();
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
})
|
|
68
|
+
.on('brush', function() {
|
|
69
|
+
brushUpdated(
|
|
70
|
+
config,
|
|
71
|
+
pc,
|
|
72
|
+
events,
|
|
73
|
+
convertBrushArguments(arguments)
|
|
74
|
+
)(selected(state, config, brushGroup)());
|
|
75
|
+
})
|
|
76
|
+
.on('end', function() {
|
|
77
|
+
brushUpdated(config, pc, events)(selected(state, config, brushGroup)());
|
|
78
|
+
events.call(
|
|
79
|
+
'brushend',
|
|
80
|
+
pc,
|
|
81
|
+
config.brushed,
|
|
82
|
+
convertBrushArguments(arguments)
|
|
83
|
+
);
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
state.brushes[axis] = _brush;
|
|
87
|
+
state.brushNodes[axis] = _selector.node();
|
|
88
|
+
|
|
89
|
+
return _brush;
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
export default brushFor;
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { select } from 'd3-selection';
|
|
2
|
+
|
|
3
|
+
const brushReset = (state, config, pc) => dimension => {
|
|
4
|
+
const { brushes } = state;
|
|
5
|
+
|
|
6
|
+
if (dimension === undefined) {
|
|
7
|
+
config.brushed = false;
|
|
8
|
+
if (pc.g() !== undefined && pc.g() !== null) {
|
|
9
|
+
pc.g()
|
|
10
|
+
.selectAll('.brush')
|
|
11
|
+
.each(function(d) {
|
|
12
|
+
if (brushes[d] !== undefined) {
|
|
13
|
+
select(this).call(brushes[d].move, null);
|
|
14
|
+
}
|
|
15
|
+
});
|
|
16
|
+
pc.renderBrushed();
|
|
17
|
+
}
|
|
18
|
+
} else {
|
|
19
|
+
config.brushed = false;
|
|
20
|
+
if (pc.g() !== undefined && pc.g() !== null) {
|
|
21
|
+
pc.g()
|
|
22
|
+
.selectAll('.brush')
|
|
23
|
+
.each(function(d) {
|
|
24
|
+
if (d !== dimension) return;
|
|
25
|
+
select(this).call(brushes[d].move, null);
|
|
26
|
+
if (typeof brushes[d].type === 'function') {
|
|
27
|
+
brushes[d].event(select(this));
|
|
28
|
+
}
|
|
29
|
+
});
|
|
30
|
+
pc.renderBrushed();
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
return this;
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
export default brushReset;
|