UNPKG

lucid-ui

Version:

A UI component library from AppNexus.

464 lines (377 loc) 18.8 kB
import _min from "lodash/min"; import _max from "lodash/max"; import _some from "lodash/some"; import _isUndefined from "lodash/isUndefined"; import _reduce from "lodash/reduce"; function _toConsumableArray(arr) { return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _unsupportedIterableToArray(arr) || _nonIterableSpread(); } function _nonIterableSpread() { throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } function _iterableToArray(iter) { if (typeof Symbol !== "undefined" && Symbol.iterator in Object(iter)) return Array.from(iter); } function _arrayWithoutHoles(arr) { if (Array.isArray(arr)) return _arrayLikeToArray(arr); } function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); if (enumerableOnly) symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; }); keys.push.apply(keys, symbols); } return keys; } function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; if (i % 2) { ownKeys(Object(source), true).forEach(function (key) { _defineProperty(target, key, source[key]); }); } else if (Object.getOwnPropertyDescriptors) { Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); } else { ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } } return target; } function _slicedToArray(arr, i) { return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _unsupportedIterableToArray(arr, i) || _nonIterableRest(); } function _nonIterableRest() { throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); } function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) { arr2[i] = arr[i]; } return arr2; } function _iterableToArrayLimit(arr, i) { if (typeof Symbol === "undefined" || !(Symbol.iterator in Object(arr))) return; var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"] != null) _i["return"](); } finally { if (_d) throw _e; } } return _arr; } function _arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; } function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } import * as d3Axis from 'd3-axis'; import * as d3Shape from 'd3-shape'; import * as d3Drag from 'd3-drag'; import * as d3Selection from 'd3-selection'; import * as d3Transition from 'd3-transition'; import ReactDOM from 'react-dom'; import { d3Scale } from '../../index'; import * as d3Array from 'd3-array'; import { lucidClassNames } from '../../util/style-helpers'; import { getGroup, lucidXAxis } from './d3-helpers'; var cx = lucidClassNames.bind('&-DraggableLineChart'); var getAttributes = function getAttributes(selection, obj) { return _reduce(obj, function (acc, value) { // @ts-ignore acc[value] = selection.attr(value); return acc; }, {}); }; var DraggableLineChartD3 = function DraggableLineChartD3(_selection, params) { var _this = this; _classCallCheck(this, DraggableLineChartD3); _defineProperty(this, "selection", void 0); _defineProperty(this, "params", void 0); _defineProperty(this, "xScale", void 0); _defineProperty(this, "yScale", void 0); _defineProperty(this, "setMouseDown", function (isMouseDown, mouseDownStep) { _this.params.isMouseDown = isMouseDown; _this.params.mouseDownStep = mouseDownStep; }); _defineProperty(this, "getIsMouseDown", function () { return _this.params.isMouseDown; }); _defineProperty(this, "getMouseDownStep", function () { return _this.params.mouseDownStep || 0; }); _defineProperty(this, "getHasRenderedPoint", function () { return !!_this.params.hasRenderedPoint; }); _defineProperty(this, "getHasRenderedLine", function () { return !!_this.params.hasRenderedLine; }); _defineProperty(this, "setHasRenderedPoint", function () { _this.params.hasRenderedPoint = true; }); _defineProperty(this, "setHasRenderedLine", function () { _this.params.hasRenderedLine = true; }); _defineProperty(this, "shouldShowPreselect", function () { var hasUserValues = _some(_this.params.data, function (_ref) { var y = _ref.y; return y > 0; }); return !!_this.params.onPreselect && !hasUserValues; }); _defineProperty(this, "drag", function () { var xScale = _this.xScale, yScale = _this.yScale, renderLine = _this.renderLine, renderPoints = _this.renderPoints, selection = _this.selection; var _this$params = _this.params, cx = _this$params.cx, onDragEnd = _this$params.onDragEnd; var initialPosition; return d3Drag.drag().on('start', function () { var activeDot = d3Selection.select(this); initialPosition = Number(activeDot.attr('cy')); }).on('drag', function (pointData) { var _yScale$range = yScale.range(), _yScale$range2 = _slicedToArray(_yScale$range, 2), max = _yScale$range2[0], min = _yScale$range2[1]; var activeDot = d3Selection.select(this); var adjMouseY = initialPosition + d3Selection.event.y; var newPointY = adjMouseY < min ? min : adjMouseY > max ? max : adjMouseY; var lines = selection.selectAll("path.".concat(cx('&-Line'))); pointData.y = Number(yScale.invert(newPointY)); activeDot.attr('cy', newPointY); var line = d3Shape.line().x(function (chartData) { return xScale(chartData.x) || 0; }).y(function (chartData) { return yScale(chartData.y); }); lines.attr('d', line); }).on('end', function (d) { if (onDragEnd) onDragEnd(d.y, d.x); renderLine(); renderPoints(); }); }); _defineProperty(this, "renderXAxis", function () { var _this$params2 = _this.params, margin = _this$params2.margin, height = _this$params2.height, xAxisTicksVertical = _this$params2.xAxisTicksVertical, dataIsCentered = _this$params2.dataIsCentered, cx = _this$params2.cx, xAxisRenderProp = _this$params2.xAxisRenderProp, data = _this$params2.data; var xGroup = getGroup(_this.selection, "".concat(cx('&-Axis'))); xGroup.call(function (xAxis) { xAxis.attr('transform', "translate(".concat(0, ",", margin.top, ")")).call(lucidXAxis, { xScale: _this.xScale, tickSize: margin.top + margin.bottom - height, xAxisRenderProp: xAxisRenderProp, dataIsCentered: dataIsCentered, data: data }); if (xAxisTicksVertical) { xAxis.classed('Vert', true); } else { xAxis.classed('NoVert', true); } if (dataIsCentered) { xAxis.classed('Center', true); } }).call(function () { return xGroup; }); }); _defineProperty(this, "renderYAxis", function () { var yGroup = getGroup(_this.selection, 'yAxisGroup'); yGroup.call(function (yAxis) { var _this$params3 = _this.params, margin = _this$params3.margin, cx = _this$params3.cx; yAxis.attr('transform', "translate(".concat(margin.left, ",", 0, ")")).classed("".concat(cx('&-Axis')), true).transition().duration(500).call(d3Axis.axisLeft(_this.yScale).ticks(10)); }).call(function () { return yGroup; }); }); _defineProperty(this, "renderLine", function () { if (_this.shouldShowPreselect()) { return; } var _this$params4 = _this.params, dataIsCentered = _this$params4.dataIsCentered, cx = _this$params4.cx; if (!_this.getHasRenderedLine()) { if (dataIsCentered) { var innerXTickWidth = _this.xScale.step(); _this.selection.append('g').append('path').attr('class', "".concat(cx('&-Line'))).attr('transform', "translate(".concat(innerXTickWidth / 2, ", 0)")); } else { _this.selection.append('g').append('path').attr('class', "".concat(cx('&-Line'))); } _this.setHasRenderedLine(); } var lines = _this.selection.selectAll("path.".concat(cx('&-Line'))); lines.datum(_this.params.data).enter(); lines.transition(d3Transition.transition().duration(100)).attr('fill', 'none').attr('d', d3Shape.line().x(function (d) { return _this.xScale(d.x) || 0; }).y(function (d) { return _this.yScale(d.y); })); }); _defineProperty(this, "renderEmptyRenderProp", function (height, width) { var emptyRenderProp = _this.params.emptyRenderProp; if (!emptyRenderProp || !_this.shouldShowPreselect()) { return; } var emptyDataObject = _this.selection.selectAll('.emptyRender'); if (emptyDataObject.empty()) { var emptyRender = _this.selection.selectAll('.overlayContainer').append('foreignObject').attr('height', height).attr('width', width).attr('x', _this.params.margin.left).classed('emptyRender', true); emptyRender.html(function (value, num, node) { ReactDOM.render(emptyRenderProp(), node[0]); }); } }); _defineProperty(this, "renderPoints", function () { if (_this.shouldShowPreselect()) { return; } var _this$params5 = _this.params, data = _this$params5.data, dataIsCentered = _this$params5.dataIsCentered; var circle = _this.getHasRenderedPoint() ? _this.selection.selectAll('circle').data(data).join('circle') : _this.selection.append('g').selectAll('circle').data(data).join('circle'); if (dataIsCentered) { var innerXTickWidth = _this.xScale.step(); circle.transition().duration(100).attr('cx', function (d) { return _this.xScale(d.x) || 0; }).attr('cy', function (d) { return _this.yScale(d.y); }).attr('r', 5).attr('transform', "translate(".concat(innerXTickWidth / 2, ", 0)")).style('fill', '#587EBA').style('stroke', 'white').style('stroke-width', 1); } else { circle.transition().duration(100).attr('cx', function (d) { return _this.xScale(d.x) || 0; }).attr('cy', function (d) { return _this.yScale(d.y); }).attr('r', 5).style('fill', '#587EBA').style('stroke', 'white').style('stroke-width', 1); } if (!_this.getHasRenderedPoint()) circle.call(_this.drag()); _this.setHasRenderedPoint(); }); _defineProperty(this, "reRenderDragBox", function (_ref2) { var dragBox = _ref2.dragBox, mouseX = _ref2.mouseX, xLeft = _ref2.xLeft, xRight = _ref2.xRight, stepWidth = _ref2.stepWidth, stepCount = _ref2.stepCount; var isLeft = xLeft >= mouseX; var isRight = xRight <= mouseX; var mouseDownStep = _this.getMouseDownStep(); if (isLeft) { var difference = _max([xLeft - mouseX, 0]) || 0; var rawStepsSelected = Math.floor(difference / stepWidth) + 2; var maxStepsAvailable = mouseDownStep + 1; var stepsSelected = _min([rawStepsSelected, maxStepsAvailable]) || 1; var activeBoxWidth = stepsSelected * stepWidth; var nextXLeft = xRight - activeBoxWidth; dragBox.attr('x', nextXLeft); dragBox.attr('width', activeBoxWidth); } else if (isRight) { var _difference = _max([mouseX - xRight, 0]) || 0; var _rawStepsSelected = Math.floor(_difference / stepWidth) + 2; var _maxStepsAvailable = stepCount - mouseDownStep; var _stepsSelected = _min([_rawStepsSelected, _maxStepsAvailable]) || 1; var _activeBoxWidth = _stepsSelected * stepWidth; dragBox.attr('x', xLeft); dragBox.attr('width', _activeBoxWidth); } else { dragBox.attr('x', xLeft); dragBox.attr('width', stepWidth); } }); _defineProperty(this, "renderHoverTracker", function () { var _this$params6 = _this.params, height = _this$params6.height, _this$params6$margin = _this$params6.margin, top = _this$params6$margin.top, bottom = _this$params6$margin.bottom, data = _this$params6.data, onPreselect = _this$params6.onPreselect; var shouldShowPreselect = _this.shouldShowPreselect, setMouseDown = _this.setMouseDown, getIsMouseDown = _this.getIsMouseDown, getMouseDownStep = _this.getMouseDownStep, reRenderDragBox = _this.reRenderDragBox, xScale = _this.xScale, selection = _this.selection; if (!shouldShowPreselect()) { selection.selectAll('.overlayContainer').remove(); return; } var innerHeight = height - top - bottom; var stepWidth = xScale.step(); var stepCount = data.length; var overlayContainer = selection.append('g').classed('overlayContainer', true).attr('transform', "translate(".concat(0, ",", top, ")")); _this.renderEmptyRenderProp(innerHeight, stepCount * stepWidth); var overlayTrack = overlayContainer.selectAll('rect').data(data).enter(); overlayTrack.append('rect').attr('x', function (chartData) { return _this.xScale(chartData.x) || 0; }).attr('y', 0).attr('width', function (chartData) { return _this.xScale.step(); }).attr('height', innerHeight).classed(cx('&-overlayTrack'), true).on('mouseenter', function (d, i, nodes) { if (!getIsMouseDown()) { d3Selection.select(nodes[i]).classed('active', true); } }).on('mouseout', function (d, i, nodes) { if (!getIsMouseDown()) { d3Selection.select(nodes[i]).classed('active', false); } }).on('mousedown', function (d, i) { d3Selection.selectAll('.active').classed('active', false); var currentTarget = d3Selection.select(this); var _getAttributes = getAttributes(currentTarget, ['x', 'y', 'width', 'height']), x = _getAttributes.x, y = _getAttributes.y, width = _getAttributes.width, height = _getAttributes.height; setMouseDown(true, i); var xLeft = +x; var xRight = +x + +width; // @ts-ignore var container = d3Selection.select(this.parentNode); container.append('rect').attr('x', x).attr('y', y).attr('width', width).attr('height', height).classed(cx('&-overlayTrack'), true).classed('active', true).classed('dragBox', true).on('mouseout', function () { // @ts-ignore var _d3Selection$mouse = d3Selection.mouse(this), _d3Selection$mouse2 = _slicedToArray(_d3Selection$mouse, 1), mouseX = _d3Selection$mouse2[0]; var dragBox = selection.selectAll('.dragBox'); reRenderDragBox({ dragBox: dragBox, mouseX: mouseX, xLeft: xLeft, xRight: xRight, stepWidth: stepWidth, stepCount: stepCount }); }).on('mousemove', function () { // @ts-ignore var _d3Selection$mouse3 = d3Selection.mouse(this), _d3Selection$mouse4 = _slicedToArray(_d3Selection$mouse3, 1), mouseX = _d3Selection$mouse4[0]; var dragBox = selection.selectAll('.dragBox'); reRenderDragBox({ dragBox: dragBox, mouseX: mouseX, xLeft: xLeft, xRight: xRight, stepWidth: stepWidth, stepCount: stepCount }); }).on('mouseup', function () { var clickStep = getMouseDownStep(); var activeBox = selection.selectAll('.dragBox'); var _getAttributes2 = getAttributes(activeBox, ['x', 'width']), x = _getAttributes2.x, width = _getAttributes2.width; var isRight = xLeft === +x; var steps = Math.round(+width / stepWidth); var startingIndex = isRight ? clickStep : clickStep - steps + 1; var endingIndex = startingIndex + steps - 1; var updatedData = data.map(function (step, i) { return _objectSpread(_objectSpread({}, step), {}, { isSelected: i >= startingIndex && i <= endingIndex }); }); !!onPreselect && onPreselect(updatedData); setMouseDown(false); selection.selectAll('.dragBox').remove(); selection.selectAll('.overlayContainer').remove(); }); }); }); _defineProperty(this, "renderLineChart", function () { _this.renderXAxis(); _this.renderYAxis(); _this.renderHoverTracker(); _this.renderLine(); _this.renderPoints(); }); _defineProperty(this, "updateLineChart", function (data) { _this.params.data = data; _this.yScale.domain([_isUndefined(_this.params.yAxisMin) ? d3Array.min(_this.params.data, function (d) { return d.y; }) : _this.params.yAxisMin, d3Array.max(_this.params.data, function (d) { return d.y; }) || 10]); _this.renderLineChart(); }); this.selection = _selection; this.params = params; if (params.dataIsCentered) { this.xScale = d3Scale.scalePoint().domain([].concat(_toConsumableArray(this.params.data.map(function (d) { return d.x; })), [''])).range([this.params.margin.left, this.params.width - this.params.margin.right - this.params.margin.left]); } else { this.xScale = d3Scale.scalePoint().domain(this.params.data.map(function (d) { return d.x; })).range([this.params.margin.left, this.params.width - this.params.margin.right - this.params.margin.left]); } this.yScale = d3Scale.scaleLinear().domain([_isUndefined(this.params.yAxisMin) ? d3Array.min(this.params.data, function (d) { return d.y; }) : this.params.yAxisMin, d3Array.max(this.params.data, function (d) { return d.y; }) || 10]).nice().range([this.params.height - this.params.margin.bottom, this.params.margin.top]); }; export default DraggableLineChartD3;