@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
package/src/api/flip.js
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { selectAll } from 'd3-selection';
|
|
2
|
+
|
|
3
|
+
import colorPath from '../util/colorPath';
|
|
4
|
+
import functor from '../util/functor';
|
|
5
|
+
|
|
6
|
+
const pathHighlight = (config, ctx, position) => (d, i) => {
|
|
7
|
+
ctx.highlight.strokeStyle = functor(config.color)(d, i);
|
|
8
|
+
return colorPath(config, position, d, ctx.highlight);
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
// highlight an array of data
|
|
12
|
+
const highlight = (config, pc, canvas, events, ctx, position) =>
|
|
13
|
+
function(data = null) {
|
|
14
|
+
if (data === null) {
|
|
15
|
+
return config.highlighted;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
config.highlighted = data;
|
|
19
|
+
pc.clear('highlight');
|
|
20
|
+
selectAll([canvas.foreground, canvas.brushed]).classed('faded', true);
|
|
21
|
+
data.forEach(pathHighlight(config, ctx, position));
|
|
22
|
+
events.call('highlight', this, data);
|
|
23
|
+
return this;
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
export default highlight;
|
package/src/api/init.js
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { select } from 'd3-selection';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Setup a new parallel coordinates chart.
|
|
5
|
+
*
|
|
6
|
+
* @param config
|
|
7
|
+
* @param canvas
|
|
8
|
+
* @param ctx
|
|
9
|
+
* @returns {pc} a parcoords closure
|
|
10
|
+
*/
|
|
11
|
+
const init = (config, canvas, ctx) => {
|
|
12
|
+
/**
|
|
13
|
+
* Create the chart within a container. The selector can also be a d3 selection.
|
|
14
|
+
*
|
|
15
|
+
* @param selection a d3 selection
|
|
16
|
+
* @returns {pc} instance for chained api
|
|
17
|
+
*/
|
|
18
|
+
const pc = function(selection) {
|
|
19
|
+
selection = pc.selection = select(selection);
|
|
20
|
+
|
|
21
|
+
config.width = selection.node().clientWidth;
|
|
22
|
+
config.height = selection.node().clientHeight;
|
|
23
|
+
// canvas data layers
|
|
24
|
+
['dots', 'foreground', 'brushed', 'marked', 'highlight'].forEach(layer => {
|
|
25
|
+
canvas[layer] = selection
|
|
26
|
+
.append('canvas')
|
|
27
|
+
.attr('class', layer)
|
|
28
|
+
.node();
|
|
29
|
+
ctx[layer] = canvas[layer].getContext('2d');
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
// svg tick and brush layers
|
|
33
|
+
pc.svg = selection
|
|
34
|
+
.append('svg')
|
|
35
|
+
.attr('width', config.width)
|
|
36
|
+
.attr('height', config.height)
|
|
37
|
+
.style('font', '14px sans-serif')
|
|
38
|
+
.style('position', 'absolute')
|
|
39
|
+
|
|
40
|
+
.append('svg:g')
|
|
41
|
+
.attr(
|
|
42
|
+
'transform',
|
|
43
|
+
'translate(' + config.margin.left + ',' + config.margin.top + ')'
|
|
44
|
+
);
|
|
45
|
+
// for chained api
|
|
46
|
+
return pc;
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
// for partial-application style programming
|
|
50
|
+
return pc;
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
export default init;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
// calculate 2d intersection of line a->b with line c->d
|
|
2
|
+
// points are objects with x and y properties
|
|
3
|
+
const intersection = (a, b, c, d) => {
|
|
4
|
+
return {
|
|
5
|
+
x:
|
|
6
|
+
((a.x * b.y - a.y * b.x) * (c.x - d.x) -
|
|
7
|
+
(a.x - b.x) * (c.x * d.y - c.y * d.x)) /
|
|
8
|
+
((a.x - b.x) * (c.y - d.y) - (a.y - b.y) * (c.x - d.x)),
|
|
9
|
+
y:
|
|
10
|
+
((a.x * b.y - a.y * b.x) * (c.y - d.y) -
|
|
11
|
+
(a.y - b.y) * (c.x * d.y - c.y * d.x)) /
|
|
12
|
+
((a.x - b.x) * (c.y - d.y) - (a.y - b.y) * (c.x - d.x)),
|
|
13
|
+
};
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
export default intersection;
|
package/src/api/mark.js
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { selectAll } from 'd3-selection';
|
|
2
|
+
|
|
3
|
+
import { pathMark } from './renderMarked';
|
|
4
|
+
|
|
5
|
+
// mark an array of data
|
|
6
|
+
const mark = (config, pc, canvas, events, ctx, position) =>
|
|
7
|
+
function(data = null) {
|
|
8
|
+
if (data === null) {
|
|
9
|
+
return config.marked;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
// add array to already marked data
|
|
13
|
+
config.marked = config.marked.concat(data);
|
|
14
|
+
selectAll([canvas.foreground, canvas.brushed]).classed('dimmed', true);
|
|
15
|
+
data.forEach(pathMark(config, ctx, position));
|
|
16
|
+
events.call('mark', this, data);
|
|
17
|
+
return this;
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
export default mark;
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { select, selectAll } from 'd3-selection';
|
|
2
|
+
|
|
3
|
+
// Merges the canvases and SVG elements into one canvas element which is then passed into the callback
|
|
4
|
+
// (so you can choose to save it to disk, etc.)
|
|
5
|
+
const mergeParcoords = pc => callback => {
|
|
6
|
+
// Retina display, etc.
|
|
7
|
+
const devicePixelRatio = window.devicePixelRatio || 1;
|
|
8
|
+
|
|
9
|
+
// Create a canvas element to store the merged canvases
|
|
10
|
+
const mergedCanvas = document.createElement('canvas');
|
|
11
|
+
|
|
12
|
+
const foregroundCanvas = pc.canvas.foreground;
|
|
13
|
+
// We will need to adjust for canvas margins to align the svg and canvas
|
|
14
|
+
const canvasMarginLeft = Number(
|
|
15
|
+
foregroundCanvas.style.marginLeft.replace('px', '')
|
|
16
|
+
);
|
|
17
|
+
|
|
18
|
+
const textTopAdjust = 15;
|
|
19
|
+
const canvasMarginTop =
|
|
20
|
+
Number(foregroundCanvas.style.marginTop.replace('px', '')) + textTopAdjust;
|
|
21
|
+
const width =
|
|
22
|
+
(foregroundCanvas.clientWidth + canvasMarginLeft) * devicePixelRatio;
|
|
23
|
+
const height =
|
|
24
|
+
(foregroundCanvas.clientHeight + canvasMarginTop) * devicePixelRatio;
|
|
25
|
+
mergedCanvas.width = width + 50; // pad so that svg labels at right will not get cut off
|
|
26
|
+
mergedCanvas.height = height + 30; // pad so that svg labels at bottom will not get cut off
|
|
27
|
+
mergedCanvas.style.width = mergedCanvas.width / devicePixelRatio + 'px';
|
|
28
|
+
mergedCanvas.style.height = mergedCanvas.height / devicePixelRatio + 'px';
|
|
29
|
+
|
|
30
|
+
// Give the canvas a white background
|
|
31
|
+
const context = mergedCanvas.getContext('2d');
|
|
32
|
+
context.fillStyle = '#ffffff';
|
|
33
|
+
context.fillRect(0, 0, mergedCanvas.width, mergedCanvas.height);
|
|
34
|
+
|
|
35
|
+
// Merge all the canvases
|
|
36
|
+
for (const key in pc.canvas) {
|
|
37
|
+
context.drawImage(
|
|
38
|
+
pc.canvas[key],
|
|
39
|
+
canvasMarginLeft * devicePixelRatio,
|
|
40
|
+
canvasMarginTop * devicePixelRatio,
|
|
41
|
+
width - canvasMarginLeft * devicePixelRatio,
|
|
42
|
+
height - canvasMarginTop * devicePixelRatio
|
|
43
|
+
);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// Add SVG elements to canvas
|
|
47
|
+
const DOMURL = window.URL || window.webkitURL || window;
|
|
48
|
+
const serializer = new XMLSerializer();
|
|
49
|
+
// axis labels are translated (0,-5) so we will clone the svg
|
|
50
|
+
// and translate down so the labels are drawn on the canvas
|
|
51
|
+
const svgNodeCopy = pc.selection
|
|
52
|
+
.select('svg')
|
|
53
|
+
.node()
|
|
54
|
+
.cloneNode(true);
|
|
55
|
+
svgNodeCopy.setAttribute('transform', 'translate(0,' + textTopAdjust + ')');
|
|
56
|
+
svgNodeCopy.setAttribute(
|
|
57
|
+
'height',
|
|
58
|
+
svgNodeCopy.getAttribute('height') + textTopAdjust
|
|
59
|
+
);
|
|
60
|
+
// text will need fill attribute since css styles will not get picked up
|
|
61
|
+
// this is not sophisticated since it doesn't look up css styles
|
|
62
|
+
// if the user changes
|
|
63
|
+
select(svgNodeCopy)
|
|
64
|
+
.selectAll('text')
|
|
65
|
+
.attr('fill', 'black');
|
|
66
|
+
const svgStr = serializer.serializeToString(svgNodeCopy);
|
|
67
|
+
|
|
68
|
+
// Create a Data URI.
|
|
69
|
+
const src = 'data:image/svg+xml;base64,' + window.btoa(svgStr);
|
|
70
|
+
const img = new Image();
|
|
71
|
+
img.onload = () => {
|
|
72
|
+
context.drawImage(
|
|
73
|
+
img,
|
|
74
|
+
0,
|
|
75
|
+
0,
|
|
76
|
+
img.width * devicePixelRatio,
|
|
77
|
+
img.height * devicePixelRatio
|
|
78
|
+
);
|
|
79
|
+
if (typeof callback === 'function') {
|
|
80
|
+
callback(mergedCanvas);
|
|
81
|
+
}
|
|
82
|
+
};
|
|
83
|
+
img.src = src;
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
export default mergeParcoords;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Renders the polylines.
|
|
3
|
+
* If no dimensions have been specified, it will attempt to detect quantitative
|
|
4
|
+
* dimensions based on the first data entry. If scales haven't been set, it will
|
|
5
|
+
* autoscale based on the extent for each dimension.
|
|
6
|
+
*
|
|
7
|
+
* @param config
|
|
8
|
+
* @param pc
|
|
9
|
+
* @param events
|
|
10
|
+
* @returns {Function}
|
|
11
|
+
*/
|
|
12
|
+
const render = (config, pc, events) =>
|
|
13
|
+
function() {
|
|
14
|
+
// try to autodetect dimensions and create scales
|
|
15
|
+
if (!Object.keys(config.dimensions).length) {
|
|
16
|
+
pc.detectDimensions();
|
|
17
|
+
}
|
|
18
|
+
pc.autoscale();
|
|
19
|
+
|
|
20
|
+
pc.render[config.mode]();
|
|
21
|
+
|
|
22
|
+
events.call('render', this);
|
|
23
|
+
return this;
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
export default render;
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import isBrushed from '../util/isBrushed';
|
|
2
|
+
import colorPath from '../util/colorPath';
|
|
3
|
+
import functor from '../util/functor';
|
|
4
|
+
|
|
5
|
+
const pathBrushed = (config, ctx, position) => (d, i) => {
|
|
6
|
+
if (config.brushedColor !== null) {
|
|
7
|
+
ctx.brushed.strokeStyle = functor(config.brushedColor)(d, i);
|
|
8
|
+
} else {
|
|
9
|
+
ctx.brushed.strokeStyle = functor(config.color)(d, i);
|
|
10
|
+
}
|
|
11
|
+
return colorPath(config, position, d, ctx.brushed);
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
const renderBrushedDefault = (config, ctx, position, pc, brushGroup) => () => {
|
|
15
|
+
pc.clear('brushed');
|
|
16
|
+
|
|
17
|
+
if (isBrushed(config, brushGroup) && config.brushed !== false) {
|
|
18
|
+
config.brushed.forEach(pathBrushed(config, ctx, position));
|
|
19
|
+
}
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
const renderBrushedQueue = (config, brushGroup, brushedQueue) => () => {
|
|
23
|
+
if (isBrushed(config, brushGroup)) {
|
|
24
|
+
brushedQueue(config.brushed);
|
|
25
|
+
} else {
|
|
26
|
+
brushedQueue([]); // This is needed to clear the currently brushed items
|
|
27
|
+
}
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
const renderBrushed = (config, pc, events) =>
|
|
31
|
+
function() {
|
|
32
|
+
if (!Object.keys(config.dimensions).length) pc.detectDimensions();
|
|
33
|
+
|
|
34
|
+
pc.renderBrushed[config.mode]();
|
|
35
|
+
events.call('render', this);
|
|
36
|
+
return this;
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
export { pathBrushed, renderBrushed, renderBrushedDefault, renderBrushedQueue };
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import colorPath from '../util/colorPath';
|
|
2
|
+
import functor from '../util/functor';
|
|
3
|
+
|
|
4
|
+
const pathForeground = (config, ctx, position) => (d, i) => {
|
|
5
|
+
ctx.foreground.strokeStyle = functor(config.color)(d, i);
|
|
6
|
+
return colorPath(config, position, d, ctx.foreground);
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
const renderDefault = (config, pc, ctx, position) => () => {
|
|
10
|
+
pc.clear('foreground');
|
|
11
|
+
pc.clear('highlight');
|
|
12
|
+
|
|
13
|
+
pc.renderBrushed.default();
|
|
14
|
+
pc.renderMarked.default();
|
|
15
|
+
|
|
16
|
+
config.data.forEach(pathForeground(config, ctx, position));
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
const renderDefaultQueue = (config, pc, foregroundQueue) => () => {
|
|
20
|
+
pc.renderBrushed.queue();
|
|
21
|
+
pc.renderMarked.queue();
|
|
22
|
+
foregroundQueue(config.data);
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
export default renderDefault;
|
|
26
|
+
|
|
27
|
+
export { pathForeground, renderDefaultQueue };
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import colorPath from '../util/colorPath';
|
|
2
|
+
import functor from '../util/functor';
|
|
3
|
+
|
|
4
|
+
const pathMark = (config, ctx, position) => (d, i) => {
|
|
5
|
+
ctx.marked.strokeStyle = functor(config.color)(d, i);
|
|
6
|
+
return colorPath(config, position, d, ctx.marked);
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
const renderMarkedDefault = (config, pc, ctx, position) => () => {
|
|
10
|
+
pc.clear('marked');
|
|
11
|
+
|
|
12
|
+
if (config.marked.length) {
|
|
13
|
+
config.marked.forEach(pathMark(config, ctx, position));
|
|
14
|
+
}
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
const renderMarkedQueue = (config, markedQueue) => () => {
|
|
18
|
+
if (config.marked) {
|
|
19
|
+
markedQueue(config.marked);
|
|
20
|
+
} else {
|
|
21
|
+
markedQueue([]); // This is needed to clear the currently marked items
|
|
22
|
+
}
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
const renderMarked = (config, pc, events) =>
|
|
26
|
+
function() {
|
|
27
|
+
if (!Object.keys(config.dimensions).length) pc.detectDimensions();
|
|
28
|
+
|
|
29
|
+
pc.renderMarked[config.mode]();
|
|
30
|
+
events.call('render', this);
|
|
31
|
+
return this;
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
export { pathMark, renderMarked, renderMarkedDefault, renderMarkedQueue };
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
// Reorder dimensions, such that the highest value (visually) is on the left and
|
|
2
|
+
// the lowest on the right. Visual values are determined by the data values in
|
|
3
|
+
// the given row.
|
|
4
|
+
const reorder = (config, pc, xscale) => rowdata => {
|
|
5
|
+
const firstDim = pc.getOrderedDimensionKeys()[0];
|
|
6
|
+
|
|
7
|
+
pc.sortDimensionsByRowData(rowdata);
|
|
8
|
+
// NOTE: this is relatively cheap given that:
|
|
9
|
+
// number of dimensions < number of data items
|
|
10
|
+
// Thus we check equality of order to prevent rerendering when this is the case.
|
|
11
|
+
const reordered = firstDim !== pc.getOrderedDimensionKeys()[0];
|
|
12
|
+
|
|
13
|
+
if (reordered) {
|
|
14
|
+
xscale.domain(pc.getOrderedDimensionKeys());
|
|
15
|
+
const highlighted = config.highlighted.slice(0);
|
|
16
|
+
pc.unhighlight();
|
|
17
|
+
|
|
18
|
+
const marked = config.marked.slice(0);
|
|
19
|
+
pc.unmark();
|
|
20
|
+
|
|
21
|
+
const g = pc.g();
|
|
22
|
+
g.transition()
|
|
23
|
+
.duration(1500)
|
|
24
|
+
.attr('transform', d => 'translate(' + xscale(d) + ')');
|
|
25
|
+
pc.render();
|
|
26
|
+
|
|
27
|
+
// pc.highlight() does not check whether highlighted is length zero, so we do that here.
|
|
28
|
+
if (highlighted.length !== 0) {
|
|
29
|
+
pc.highlight(highlighted);
|
|
30
|
+
}
|
|
31
|
+
if (marked.length !== 0) {
|
|
32
|
+
pc.mark(marked);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
export default reorder;
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { drag } from 'd3-drag';
|
|
2
|
+
import { select } from 'd3-selection';
|
|
3
|
+
|
|
4
|
+
import w from '../util/width';
|
|
5
|
+
|
|
6
|
+
// Jason Davies, http://bl.ocks.org/1341281
|
|
7
|
+
const reorderable = (config, pc, xscale, position, dragging, flags) =>
|
|
8
|
+
function() {
|
|
9
|
+
if (pc.g() === undefined) pc.createAxes();
|
|
10
|
+
const g = pc.g();
|
|
11
|
+
|
|
12
|
+
g.style('cursor', 'move').call(
|
|
13
|
+
drag()
|
|
14
|
+
.on('start', function(event, d) {
|
|
15
|
+
dragging[d] = this.__origin__ = xscale(d);
|
|
16
|
+
})
|
|
17
|
+
.on('drag', function(event, d) {
|
|
18
|
+
dragging[d] = Math.min(
|
|
19
|
+
w(config),
|
|
20
|
+
Math.max(0, (this.__origin__ += event.dx))
|
|
21
|
+
);
|
|
22
|
+
pc.sortDimensions();
|
|
23
|
+
xscale.domain(pc.getOrderedDimensionKeys());
|
|
24
|
+
pc.render();
|
|
25
|
+
g.attr('transform', d => 'translate(' + position(d) + ')');
|
|
26
|
+
})
|
|
27
|
+
.on('end', function(event, d) {
|
|
28
|
+
delete this.__origin__;
|
|
29
|
+
delete dragging[d];
|
|
30
|
+
select(this)
|
|
31
|
+
.transition()
|
|
32
|
+
.attr('transform', 'translate(' + xscale(d) + ')');
|
|
33
|
+
pc.render();
|
|
34
|
+
pc.renderMarked();
|
|
35
|
+
})
|
|
36
|
+
);
|
|
37
|
+
flags.reorderable = true;
|
|
38
|
+
return this;
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
export default reorderable;
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
// rescale for height, width and margins
|
|
2
|
+
// TODO currently assumes chart is brushable, and destroys old brushes
|
|
3
|
+
const resize = (config, pc, flags, events) => {
|
|
4
|
+
return function() {
|
|
5
|
+
// selection size
|
|
6
|
+
pc.selection
|
|
7
|
+
.select('svg')
|
|
8
|
+
.attr('width', config.width)
|
|
9
|
+
.attr('height', config.height);
|
|
10
|
+
pc.svg.attr(
|
|
11
|
+
'transform',
|
|
12
|
+
'translate(' + config.margin.left + ',' + config.margin.top + ')'
|
|
13
|
+
);
|
|
14
|
+
|
|
15
|
+
// FIXME: the current brush state should pass through
|
|
16
|
+
if (flags.brushable) pc.brushReset();
|
|
17
|
+
|
|
18
|
+
// scales
|
|
19
|
+
pc.autoscale();
|
|
20
|
+
|
|
21
|
+
// axes, destroys old brushes.
|
|
22
|
+
if (pc.g()) pc.createAxes();
|
|
23
|
+
if (flags.brushable) pc.brushable();
|
|
24
|
+
if (flags.reorderable) pc.reorderable();
|
|
25
|
+
|
|
26
|
+
events.call('resize', this, {
|
|
27
|
+
width: config.width,
|
|
28
|
+
height: config.height,
|
|
29
|
+
margin: config.margin,
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
return this;
|
|
33
|
+
};
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
export default resize;
|