@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.
Files changed (109) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +160 -0
  3. package/dist/parcoords.css +61 -0
  4. package/dist/parcoords.esm.js +4132 -0
  5. package/dist/parcoords.esm.js.map +1 -0
  6. package/dist/parcoords.js +4130 -0
  7. package/dist/parcoords.js.map +1 -0
  8. package/dist/parcoords.standalone.js +11233 -0
  9. package/dist/parcoords.standalone.js.map +1 -0
  10. package/package.json +89 -0
  11. package/src/api/adjacentPairs.js +10 -0
  12. package/src/api/applyAxisConfig.js +35 -0
  13. package/src/api/applyDimensionDefaults.js +25 -0
  14. package/src/api/autoscale.js +133 -0
  15. package/src/api/axisDots.js +27 -0
  16. package/src/api/brushMode.js +50 -0
  17. package/src/api/brushReset.js +34 -0
  18. package/src/api/brushable.js +81 -0
  19. package/src/api/clear.js +21 -0
  20. package/src/api/commonScale.js +45 -0
  21. package/src/api/computeRealCentroids.js +8 -0
  22. package/src/api/createAxes.js +101 -0
  23. package/src/api/detectDimensionTypes.js +12 -0
  24. package/src/api/detectDimensions.js +7 -0
  25. package/src/api/flip.js +11 -0
  26. package/src/api/getOrderedDimensionKeys.js +8 -0
  27. package/src/api/highlight.js +26 -0
  28. package/src/api/init.js +53 -0
  29. package/src/api/interactive.js +7 -0
  30. package/src/api/intersection.js +16 -0
  31. package/src/api/mark.js +20 -0
  32. package/src/api/mergeParcoords.js +86 -0
  33. package/src/api/removeAxes.js +9 -0
  34. package/src/api/render.js +26 -0
  35. package/src/api/renderBrushed.js +39 -0
  36. package/src/api/renderDefault.js +27 -0
  37. package/src/api/renderMarked.js +34 -0
  38. package/src/api/reorder.js +37 -0
  39. package/src/api/reorderable.js +41 -0
  40. package/src/api/resize.js +36 -0
  41. package/src/api/scale.js +10 -0
  42. package/src/api/selected.js +209 -0
  43. package/src/api/shadows.js +9 -0
  44. package/src/api/sortDimensions.js +13 -0
  45. package/src/api/sortDimensionsByRowData.js +20 -0
  46. package/src/api/toString.js +11 -0
  47. package/src/api/toType.js +9 -0
  48. package/src/api/toTypeCoerceNumbers.js +7 -0
  49. package/src/api/unhighlight.js +12 -0
  50. package/src/api/unmark.js +12 -0
  51. package/src/api/updateAxes.js +103 -0
  52. package/src/bindEvents.js +60 -0
  53. package/src/brush/1d/brushExtents.js +73 -0
  54. package/src/brush/1d/brushFor.js +92 -0
  55. package/src/brush/1d/brushReset.js +36 -0
  56. package/src/brush/1d/index.js +20 -0
  57. package/src/brush/1d/install.js +41 -0
  58. package/src/brush/1d/selected.js +79 -0
  59. package/src/brush/1d/uninstall.js +12 -0
  60. package/src/brush/1d-multi/brushExtents.js +101 -0
  61. package/src/brush/1d-multi/brushFor.js +13 -0
  62. package/src/brush/1d-multi/brushReset.js +53 -0
  63. package/src/brush/1d-multi/drawBrushes.js +36 -0
  64. package/src/brush/1d-multi/index.js +20 -0
  65. package/src/brush/1d-multi/install.js +29 -0
  66. package/src/brush/1d-multi/newBrush.js +102 -0
  67. package/src/brush/1d-multi/selected.js +160 -0
  68. package/src/brush/1d-multi/uninstall.js +12 -0
  69. package/src/brush/angular/brushReset.js +14 -0
  70. package/src/brush/angular/index.js +19 -0
  71. package/src/brush/angular/install.js +141 -0
  72. package/src/brush/angular/onDrag.js +109 -0
  73. package/src/brush/angular/onDragEnd.js +30 -0
  74. package/src/brush/angular/onDragStart.js +38 -0
  75. package/src/brush/angular/removeStrum.js +12 -0
  76. package/src/brush/angular/selected.js +63 -0
  77. package/src/brush/angular/uninstall.js +17 -0
  78. package/src/brush/angular/util/hypothenuse.js +3 -0
  79. package/src/brush/consecutive.js +11 -0
  80. package/src/brush/dimensionsForPoint.js +32 -0
  81. package/src/brush/invertByScale.js +24 -0
  82. package/src/brush/strums/brushReset.js +14 -0
  83. package/src/brush/strums/index.js +19 -0
  84. package/src/brush/strums/install.js +81 -0
  85. package/src/brush/strums/onDrag.js +83 -0
  86. package/src/brush/strums/onDragEnd.js +20 -0
  87. package/src/brush/strums/onDragStart.js +33 -0
  88. package/src/brush/strums/removeStrum.js +10 -0
  89. package/src/brush/strums/selected.js +52 -0
  90. package/src/brush/strums/uninstall.js +16 -0
  91. package/src/index.js +226 -0
  92. package/src/parallel-coordinates.css +61 -0
  93. package/src/state/defaultConfig.js +36 -0
  94. package/src/state/index.js +86 -0
  95. package/src/state/sideEffects.js +89 -0
  96. package/src/util/colorPath.js +71 -0
  97. package/src/util/computeCentroids.js +44 -0
  98. package/src/util/computeClusterCentroids.js +33 -0
  99. package/src/util/computeControlPoints.js +38 -0
  100. package/src/util/dimensionLabels.js +11 -0
  101. package/src/util/flipAxisAndUpdatePCP.js +19 -0
  102. package/src/util/functor.js +3 -0
  103. package/src/util/getRange.js +24 -0
  104. package/src/util/getset.js +23 -0
  105. package/src/util/height.js +3 -0
  106. package/src/util/isBrushed.js +15 -0
  107. package/src/util/renderQueue.js +67 -0
  108. package/src/util/rotateLabels.js +18 -0
  109. 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,9 @@
1
+ const shadows = (flags, pc) =>
2
+ function() {
3
+ flags.shadows = true;
4
+ pc.alphaOnBrushed(0.1);
5
+ pc.render();
6
+ return this;
7
+ };
8
+
9
+ export default shadows;
@@ -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,9 @@
1
+ // a better "typeof" from this post: http://stackoverflow.com/questions/7390426/better-way-to-get-type-of-a-javascript-variable
2
+ const toType = v => {
3
+ return {}.toString
4
+ .call(v)
5
+ .match(/\s([a-zA-Z]+)/)[1]
6
+ .toLowerCase();
7
+ };
8
+
9
+ export default toType;
@@ -0,0 +1,7 @@
1
+ import toType from './toType';
2
+
3
+ // try to coerce to number before returning type
4
+ const toTypeCoerceNumbers = v =>
5
+ parseFloat(v) == v && v !== null ? 'number' : toType(v);
6
+
7
+ export default toTypeCoerceNumbers;
@@ -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;