Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 14 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,12 @@ This project supports dynamic data and configuration updates and is very simple

Vue2 Example: https://codepen.io/arik-test/pen/qBGYjyG

## Build
```
> yarn
> yarn build
```

## Installation

```
Expand Down Expand Up @@ -92,6 +98,8 @@ graph.draw();
* **details** should be displayed [ true | false ]
* **tooltip** should be displayed [ true | false ]
**Note:** The tooltip display depends on the details display so it can calculate its range according to the dividers.
* **responsive** when true the SVG's width and height will be set to 100%. And the configured width and height will be set in the viewBox.
Make sure to set the parent DIVs elements to be 100% as well for resized graph

## Updatable FunnelGraph configuration
* width
Expand Down Expand Up @@ -126,4 +134,9 @@ CSS:
JS:
```html
<script src="../dist/js/funnel-graph.min.js"></script>
```
```

## Responsive Graph
* In order to have a responsive graph the wrapper DIVs should have width/height of 100%
* The "responsive" flag should be set to true (the SVG width/height will be set to 100%/100%)
* The graph width/height should be at a ratio that fits your page - that will set the viewBox. (e.g. 800/200 100/100 etc...)
93 changes: 66 additions & 27 deletions dist/js/funnel-graph.js
Original file line number Diff line number Diff line change
Expand Up @@ -5375,13 +5375,14 @@ var getTooltipElement = function getTooltipElement() {
var createRootSVG = exports.createRootSVG = function createRootSVG(_ref) {
var context = _ref.context;
var id = context.getId();
var responsive = context.getResponsive();
var width = context.getWidth();
var height = context.getHeight();
var margin = context.getMargin();
var containerSelector = context.getContainerSelector();
var container = (0, _d3Selection.select)(containerSelector);
container.append('div').attr('id', "d3-funnel-js-tooltip").attr('class', 'd3-funnel-js-tooltip');
var d3Svg = container.append('svg').attr('class', 'd3-funnel-js').attr('id', id).attr('width', width).attr('height', height).attr('viewBox', "0 0 ".concat(width, " ").concat(height)).attr('preserveAspectRatio', 'xMidYMin meet');
var d3Svg = container.append('svg').attr('class', 'd3-funnel-js').attr('id', id).attr('width', responsive ? "100%" : width).attr('height', responsive ? "100%" : height).attr('viewBox', "0 0 ".concat(width, " ").concat(height)).attr('preserveAspectRatio', 'xMidYMin meet');
getRootSvgGroup(id, margin);
return d3Svg;
};
Expand All @@ -5394,17 +5395,24 @@ var updateSVGGroup = function updateSVGGroup(id, margin) {
* Update the root SVG [demnsions, transform]
*/
var updateRootSVG = exports.updateRootSVG = function updateRootSVG(_ref2) {
var id = _ref2.id,
width = _ref2.width,
height = _ref2.height,
var context = _ref2.context,
rotateFrom = _ref2.rotateFrom,
rotateTo = _ref2.rotateTo;
var id = context.getId();
var responsive = context.getResponsive();
var width = context.getWidth();
var height = context.getHeight();
var d3Svg = id ? getRootSvg(id) : undefined;
if (d3Svg) {
var root = d3Svg.transition().delay(500).duration(1000);
if (!isNaN(width) && !isNaN(height)) {
d3Svg.attr("width", width);
d3Svg.attr("height", height);
if (!responsive) {
d3Svg.attr("width", width);
d3Svg.attr("height", height);
} else {
d3Svg.attr("width", "100%");
d3Svg.attr("height", "100%");
}
d3Svg.attr('viewBox', "0 0 ".concat(width, " ").concat(height));
}
if (!isNaN(rotateTo) && !isNaN(rotateTo)) {
Expand Down Expand Up @@ -5514,7 +5522,7 @@ var addMouseEventIfNotExists = function addMouseEventIfNotExists(_ref6) {
var label = handlerMetadata.label || "Value";
label = is2d ? handlerMetadata.subLabel || label : label;
var tooltipText = "".concat(label, ": ").concat(handlerMetadata.value);
tooltipElement.style("left", event.clientX + 10 + "px").style("top", event.clientY + 10 + "px").text(tooltipText).style("opacity", "1").style("display", "flex");
tooltipElement.style("left", event.offsetX + 10 + "px").style("top", event.offsetY + 10 + "px").text(tooltipText).style("opacity", "1").style("display", "flex");
}, 500);
}
};
Expand Down Expand Up @@ -5548,7 +5556,7 @@ var onEachPathHandler = function onEachPathHandler(_ref7) {
var callbacks = context.getCallBacks();
var d3Path = (0, _d3Selection.select)(nodes[i]);
var color = is2d ? colors[i] : colors;
var fillMode = typeof color === 'string' || color.length === 1 ? 'solid' : 'gradient';
var fillMode = typeof color === 'string' || (color === null || color === void 0 ? void 0 : color.length) === 1 ? 'solid' : 'gradient';
if (fillMode === 'solid') {
d3Path.attr('fill', color).attr('stroke', color);
} else if (fillMode === 'gradient') {
Expand Down Expand Up @@ -5595,9 +5603,7 @@ var drawPaths = exports.drawPaths = function drawPaths(_ref9) {
var id = context.getId();
var rootSvg = getRootSvgGroup(id);
updateRootSVG({
id: id,
width: context.getWidth(),
height: context.getHeight()
context: context
});
if (definitions && rootSvg) {
var paths = rootSvg.selectAll('path').data(definitions.paths);
Expand Down Expand Up @@ -5801,7 +5807,7 @@ var applyGradient = function applyGradient(id, d3Path, colors, index, gradientDi
}

// Set color stops
var numberOfColors = colors.length;
var numberOfColors = (colors === null || colors === void 0 ? void 0 : colors.length) || 0;
for (var i = 0; i < numberOfColors; i++) {
d3Gradient.append('stop').attr('offset', "".concat(Math.round(100 * i / (numberOfColors - 1)), "%")).attr('stop-color', colors[i]);
}
Expand Down Expand Up @@ -5876,7 +5882,8 @@ function _toPrimitive(t, r) { if ("object" != _typeof(t) || !t) return t; var e
* 'click': () => {}
* }
* details: false
* tooltip: true
* tooltip: true,
* responsive: false
* }
* TODO: outlines: for two dimensions graph display
*/
Expand All @@ -5886,6 +5893,7 @@ var FunnelGraph = /*#__PURE__*/function () {
_classCallCheck(this, FunnelGraph);
this.id = this.generateId(), this.containerSelector = options.container;
this.gradientDirection = options.gradientDirection && options.gradientDirection === 'vertical' ? 'vertical' : 'horizontal';
this.setResponsive(options.hasOwnProperty("responsive") ? options.responsive : false);
this.setDetails(options.hasOwnProperty('details') ? options.details : true);
this.setTooltip(options.hasOwnProperty('tooltip') ? options.tooltip : true);
this.getDirection(options === null || options === void 0 ? void 0 : options.direction);
Expand Down Expand Up @@ -6016,6 +6024,7 @@ var FunnelGraph = /*#__PURE__*/function () {
value: function setDetails(bool) {
this.details = bool;
}

/**
* Get the graph width
*
Expand Down Expand Up @@ -6101,6 +6110,16 @@ var FunnelGraph = /*#__PURE__*/function () {
value: function getLinePositions() {
return this.linePositions;
}
}, {
key: "getResponsive",
value: function getResponsive() {
return this.responsive;
}
}, {
key: "setResponsive",
value: function setResponsive(value) {
this.responsive = value;
}
}, {
key: "getValues2d",
value: function getValues2d() {
Expand Down Expand Up @@ -6183,9 +6202,7 @@ var FunnelGraph = /*#__PURE__*/function () {
this.setWidth(this.origHeight);
this.setHeight(this.origWidth);
(0, _d.updateRootSVG)({
id: this.id,
width: this.getWidth(),
height: this.getHeight()
context: this.getContext()
});
this.updateData();
}
Expand All @@ -6198,9 +6215,7 @@ var FunnelGraph = /*#__PURE__*/function () {
this.setWidth(this.origWidth);
this.setHeight(this.origHeight);
(0, _d.updateRootSVG)({
id: this.id,
width: this.getWidth(),
height: this.getHeight()
context: this.getContext()
});
this.updateData();
}
Expand Down Expand Up @@ -6364,6 +6379,9 @@ var FunnelGraph = /*#__PURE__*/function () {
key: "updateData",
value: function updateData(d) {
if (d) {
if (typeof d.responsive !== 'undefined') {
this.setResponsive(d.responsive);
}
if (typeof d.width !== 'undefined') {
this.setWidth(d.width);
}
Expand Down Expand Up @@ -6449,6 +6467,13 @@ var getPathDefinitions = exports.getPathDefinitions = function getPathDefinition
var height = context.getHeight(false);
var valuesNum = crossAxisPoints.length - 1;
for (var i = 0; i < valuesNum; i++) {
var _crossAxisPoints$i;
var allZeros = crossAxisPoints === null || crossAxisPoints === void 0 || (_crossAxisPoints$i = crossAxisPoints[i]) === null || _crossAxisPoints$i === void 0 ? void 0 : _crossAxisPoints$i.every(function (value) {
return value === 0;
});
if (allZeros) {
continue;
}
if (isVertical) {
var X = crossAxisPoints[i];
var XNext = crossAxisPoints[i + 1];
Expand Down Expand Up @@ -6505,36 +6530,50 @@ var getCrossAxisPoints = exports.getCrossAxisPoints = function getCrossAxisPoint
// we use this when calculating the "A" shape
var dimension = fullDimension / 2;
if (is2d) {
var _pointsOfFirstPath;
var totalValues = values2d;
var max = Math.max.apply(Math, _toConsumableArray(totalValues));

// duplicate last value
totalValues.push(_toConsumableArray(totalValues).pop());
// get points for path "A"
points.push(totalValues.map(function (value) {
return (0, _number.roundPoint)((max - value) / max * dimension);
var point = (0, _number.roundPoint)((max - value) / max * dimension);
return isNaN(point) ? 0 : point;
}));
// percentages with duplicated last value
var percentagesFull = percentages2d;
var pointsOfFirstPath = points[0];
for (var i = 1; i < subDataSize; i++) {
var _newPoints;
var p = points[i - 1];
var newPoints = [];
for (var j = 0; j < dataSize; j++) {
newPoints.push((0, _number.roundPoint)(
// eslint-disable-next-line comma-dangle
p[j] + (fullDimension - pointsOfFirstPath[j] * 2) * (percentagesFull[j][i - 1] / 100)));
}

newPoints = (_newPoints = newPoints) === null || _newPoints === void 0 ? void 0 : _newPoints.map(function (value) {
return isNaN(value) ? 0 : value;
});
// duplicate the last value as points #2 and #3 have the same value on the cross axis
newPoints.push([].concat(newPoints).pop());
newPoints.push(_toConsumableArray(newPoints).pop());
points.push(newPoints);
}

// add points for path "D", that is simply the "inverted" path "A"
points.push(pointsOfFirstPath.map(function (point) {
return fullDimension - point;
}));
pointsOfFirstPath = (_pointsOfFirstPath = pointsOfFirstPath) === null || _pointsOfFirstPath === void 0 ? void 0 : _pointsOfFirstPath.map(function (value) {
return isNaN(value) ? 0 : value;
});
var allZeros = pointsOfFirstPath.every(function (value) {
return value === 0;
});
if (allZeros) {
points.push(pointsOfFirstPath);
} else {
// add points for path "D", that is simply the "inverted" path "A"
points.push(pointsOfFirstPath.map(function (point) {
return fullDimension - point;
}));
}
} else {
// As you can see on the visualization above points #2 and #3 have the same cross axis coordinate
// so we duplicate the last value
Expand Down
2 changes: 1 addition & 1 deletion dist/js/funnel-graph.min.js

Large diffs are not rendered by default.

31 changes: 25 additions & 6 deletions examples/example.html
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<!DOCTYPE html>
<html lang="en">

<!-- TODO: Refactor Just to make it Clean -->
<head>
<meta charset="UTF-8">
<title>SVG Funnel</title>
Expand All @@ -19,7 +19,8 @@
}

.funnel {
height: auto;
height: 100%;
height: 100%;
}

.flex {
Expand Down Expand Up @@ -106,15 +107,16 @@
<button id="useData1">DataSet 1</button>
<button id="useData2">DataSet 2</button>
<button id="useData3">DataSet 3</button>
<button id="useData4">DataSet 4 (empty)</button>
<button id="useData4">DataSet 4 (empty array [])</button>
<button id="useData5">DataSet 5 (empty zeros [0, 0...])</button>
<button id="hideTooltip">Hide Tooltip</button>
<button id="hideDetails">Hide Details</button>
<button id="showTooltip">Show Tooltip</button>
<button id="showDetails">Show Details</button>
<button id="setResponsive">Toggle Responsive</button>
</div>
<div class="flex funnel-wrapper col">
<div class="funnel">
</div>
<div class="funnel"></div>
</div>
</div>

Expand All @@ -125,6 +127,14 @@
values: []
};

const emptyZerosExample = {
values: [
[0, 0, 0],
[0, 0, 0],
[0, 0, 0]
],
};

const dataExample1 = {
colors: ['#FFB178', '#FF3C8E'],
values: [11000, 3000, 240]
Expand All @@ -151,6 +161,7 @@
],
};

let responsive = false;
const graph = new FunnelGraph({
container: '.funnel',
gradientDirection: 'horizontal',
Expand All @@ -165,7 +176,8 @@
console.log(metadata);
}
},
margin: { top: 120, right: 60, bottom: 60, left: 60, text: 10 }
margin: { top: 120, right: 60, bottom: 60, left: 60, text: 10 },
responsive
});

graph.draw();
Expand Down Expand Up @@ -219,6 +231,9 @@
document.querySelector('#useData4').addEventListener('click', function () {
graph.updateData(emptyExample);
});
document.querySelector('#useData5').addEventListener('click', function () {
graph.updateData(emptyZerosExample);
});
document.querySelector('#hideTooltip').addEventListener('click', function () {
graph.updateData({ tooltip: false });
});
Expand All @@ -231,6 +246,10 @@
document.querySelector('#showDetails').addEventListener('click', function () {
graph.updateData({ width: 600, height: 400, details: true, margin: { top: 100, right: 80, bottom: 80, left: 80, text: 20 } });
});
document.querySelector('#setResponsive').addEventListener('click', function () {
responsive = !responsive;
graph.updateData({ responsive });
});

</script>
</body>
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "d3-funnel-graph",
"version": "1.0.5",
"version": "1.0.6",
"description": "SVG Funnel Graph Javascript Library",
"main": "dist/js/funnel-graph.min.js",
"style": "dist/funnel-graph.min.css",
Expand Down
Loading