import CLASS from './class' import { ChartInternal } from './core' ChartInternal.prototype.initEventRect = function() { var $$ = this, config = $$.config $$.main .select('.' + CLASS.chart) .append('g') .attr('class', CLASS.eventRects) .style('fill-opacity', 0) $$.eventRect = $$.main .select('.' + CLASS.eventRects) .append('rect') .attr('class', CLASS.eventRect) // event rect handle zoom event as well if (config.zoom_enabled && $$.zoom) { $$.eventRect.call($$.zoom).on('dblclick.zoom', null) if (config.zoom_initialRange) { // WORKAROUND: Add transition to apply transform immediately when no subchart $$.eventRect .transition() .duration(0) .call($$.zoom.transform, $$.zoomTransform(config.zoom_initialRange)) } } } ChartInternal.prototype.redrawEventRect = function() { const $$ = this, d3 = $$.d3, config = $$.config function mouseout() { $$.svg.select('.' + CLASS.eventRect).style('cursor', null) $$.hideXGridFocus() $$.hideTooltip() $$.unexpandCircles() $$.unexpandBars() } const isHoveringDataPoint = (mouse, closest) => closest && ($$.isBarType(closest.id) || $$.dist(closest, mouse) < config.point_sensitivity) const withName = d => (d ? $$.addName(Object.assign({}, d)) : null) // rects for mouseover $$.main .select('.' + CLASS.eventRects) .style( 'cursor', config.zoom_enabled ? config.axis_rotated ? 'ns-resize' : 'ew-resize' : null ) $$.eventRect .attr('x', 0) .attr('y', 0) .attr('width', $$.width) .attr('height', $$.height) .on( 'mouseout', config.interaction_enabled ? function() { if (!config) { return } // chart is destroyed if ($$.hasArcType()) { return } if ($$.mouseover) { config.data_onmouseout.call($$.api, $$.mouseover) $$.mouseover = undefined } mouseout() } : null ) .on( 'mousemove', config.interaction_enabled ? function() { // do nothing when dragging if ($$.dragging) { return } const targetsToShow = $$.getTargetsToShow() // do nothing if arc type if ($$.hasArcType(targetsToShow)) { return } const mouse = d3.mouse(this) const closest = withName( $$.findClosestFromTargets(targetsToShow, mouse) ) const isMouseCloseToDataPoint = isHoveringDataPoint(mouse, closest) // ensure onmouseout is always called if mousemove switch between 2 targets if ( $$.mouseover && (!closest || closest.id !== $$.mouseover.id || closest.index !== $$.mouseover.index) ) { config.data_onmouseout.call($$.api, $$.mouseover) $$.mouseover = undefined } if (closest && !$$.mouseover) { config.data_onmouseover.call($$.api, closest) $$.mouseover = closest } // show cursor as pointer if we're hovering a data point close enough $$.svg .select('.' + CLASS.eventRect) .style('cursor', isMouseCloseToDataPoint ? 'pointer' : null) // if tooltip not grouped, we want to display only data from closest data point const showSingleDataPoint = !config.tooltip_grouped || $$.hasType('stanford', targetsToShow) // find data to highlight let selectedData if (showSingleDataPoint) { if (closest) { selectedData = [closest] } } else { let closestByX if (closest) { // reuse closest value closestByX = closest } else { // try to find the closest value by X values from the mouse position const mouseX = config.axis_rotated ? mouse[1] : mouse[0] closestByX = $$.findClosestFromTargetsByX( targetsToShow, $$.x.invert(mouseX) ) } // highlight all data for this 'x' value if (closestByX) { selectedData = $$.filterByX(targetsToShow, closestByX.x) } } // ensure we have data to show if (!selectedData || selectedData.length === 0) { return mouseout() } // inject names for each point selectedData = selectedData.map(withName) // show tooltip $$.showTooltip(selectedData, this) // expand points if (config.point_focus_expand_enabled) { $$.unexpandCircles() selectedData.forEach(function(d) { $$.expandCircles(d.index, d.id, false) }) } // expand bars $$.unexpandBars() selectedData.forEach(function(d) { $$.expandBars(d.index, d.id, false) }) // Show xgrid focus line $$.showXGridFocus(selectedData) } : null ) .on( 'click', config.interaction_enabled ? function() { const targetsToShow = $$.getTargetsToShow() if ($$.hasArcType(targetsToShow)) { return } const mouse = d3.mouse(this) const closest = withName( $$.findClosestFromTargets(targetsToShow, mouse) ) if (!isHoveringDataPoint(mouse, closest)) { return } // select if selection enabled let sameXData if (!config.data_selection_grouped || $$.isStanfordType(closest)) { sameXData = [closest] } else { sameXData = $$.filterByX(targetsToShow, closest.x) } // toggle selected state sameXData.forEach(function(d) { $$.main .selectAll( '.' + CLASS.shapes + $$.getTargetSelectorSuffix(d.id) ) .selectAll('.' + CLASS.shape + '-' + d.index) .each(function() { if ( config.data_selection_grouped || $$.isWithinShape(this, d) ) { $$.toggleShape(this, d, d.index) } }) }) // call data_onclick on the closest data point if (closest) { const shape = $$.main .selectAll( '.' + CLASS.shapes + $$.getTargetSelectorSuffix(closest.id) ) .select('.' + CLASS.shape + '-' + closest.index) config.data_onclick.call($$.api, closest, shape.node()) } } : null ) .call( config.interaction_enabled && config.data_selection_draggable && $$.drag ? d3 .drag() .on('drag', function() { $$.drag(d3.mouse(this)) }) .on('start', function() { $$.dragstart(d3.mouse(this)) }) .on('end', function() { $$.dragend() }) : function() {} ) } ChartInternal.prototype.getMousePosition = function(data) { var $$ = this return [$$.x(data.x), $$.getYScale(data.id)(data.value)] } ChartInternal.prototype.dispatchEvent = function(type, mouse) { var $$ = this, selector = '.' + CLASS.eventRect, eventRect = $$.main.select(selector).node(), box = eventRect.getBoundingClientRect(), x = box.left + (mouse ? mouse[0] : 0), y = box.top + (mouse ? mouse[1] : 0), event = document.createEvent('MouseEvents') event.initMouseEvent( type, true, true, window, 0, x, y, x, y, false, false, false, false, 0, null ) eventRect.dispatchEvent(event) }