import { Selector } from 'reselect' ; import { ChartType } from '../../..' ; import { Scale } from '../../../../scales' ; import { GroupBrushExtent , SeriesSpecs , XYBrushEvent } from '../../../../specs' ; import { BrushAxis } from '../../../../specs/constants' ; import { DragState , GlobalChartState } from '../../../../state/chart_state' ; import { createCustomCachedSelector } from '../../../../state/create_selector' ; import { getSettingsSpecSelector } from '../../../../state/selectors/get_settings_specs' ; import { clamp , Rotation } from '../../../../utils/common' ; import { Dimensions } from '../../../../utils/dimensions' ; import { hasDragged , DragCheckProps } from '../../../../utils/events' ; import { GroupId } from '../../../../utils/ids' ; import { isHistogramEnabled } from '../../domains/y_domain' ; import { isVerticalRotation } from '../utils/common' ; import { computeChartDimensionsSelector } from './compute_chart_dimensions' ; import { computeSmallMultipleScalesSelector , SmallMultipleScales } from './compute_small_multiple_scales' ; import { getPlotAreaRestrictedPoint , getPointsConstraintToSinglePanel , PanelPoints } from './get_brush_area' ; import { getComputedScalesSelector } from './get_computed_scales' ; import { getSeriesSpecsSelector } from './get_specs' ; import { isBrushAvailableSelector } from './is_brush_available' ; import { isHistogramModeEnabledSelector } from './is_histogram_mode_enabled' ; const getLastDragSelector = ( state ) => state . interactions . pointer . lastDrag ; internal export function createOnBrushEndCaller ( ) { let prevProps = null ; let selector = null ; return ( state ) => { if ( selector === null && state . chartType === ChartType . XYAxis ) { if ( ! isBrushAvailableSelector ( state ) ) { selector = null ; prevProps = null ; return ; } selector = createCustomCachedSelector ( [ getLastDragSelector , getSettingsSpecSelector , getComputedScalesSelector , computeChartDimensionsSelector , isHistogramModeEnabledSelector , computeSmallMultipleScalesSelector , getSeriesSpecsSelector , ] , ( lastDrag , { onBrushEnd , rotation , brushAxis , minBrushDelta , roundHistogramBrushValues , allowBrushingLastHistogramBin } , computedScales , { chartDimensions } , histogramMode , smallMultipleScales , seriesSpec ) : void => { const nextProps = { lastDrag , onBrushEnd , } ; if ( lastDrag !== null && hasDragged ( prevProps , nextProps ) && onBrushEnd ) { const brushAreaEvent = { } ; const { yScales , xScale } = computedScales ; if ( brushAxis === BrushAxis . X || brushAxis === BrushAxis . Both ) { brushAreaEvent . x = getXBrushExtent ( chartDimensions , lastDrag , rotation , histogramMode , xScale as Scale < number > , smallMultipleScales , allowBrushingLastHistogramBin , seriesSpec , minBrushDelta , roundHistogramBrushValues ) ; } if ( brushAxis === BrushAxis . Y || brushAxis === BrushAxis . Both ) { brushAreaEvent . y = getYBrushExtents ( chartDimensions , lastDrag , rotation , yScales , smallMultipleScales , minBrushDelta ) ; } if ( brushAreaEvent . x !== undefined || brushAreaEvent . y !== undefined ) { onBrushEnd ( brushAreaEvent ) ; } } prevProps = nextProps ; } ) ; } if ( selector ) { selector ( state ) ; } } ; } function scalePanelPointsToPanelCoordinates ( scaleXPoint , { start , end , vPanelStart , hPanelStart , vPanelHeight , hPanelWidth } ) { const startPos = scaleXPoint ? start . x - hPanelStart : start . y - vPanelStart ; const endPos = scaleXPoint ? end . x - hPanelStart : end . y - vPanelStart ; const panelMax = scaleXPoint ? hPanelWidth : vPanelHeight ; return { minPos : Math . min ( startPos , endPos ) , maxPos : Math . max ( startPos , endPos ) , panelMax , } ; } function getXBrushExtent ( chartDimensions , lastDrag , rotation , histogramMode , xScale , smallMultipleScales , allowBrushingLastHistogramBin , seriesSpecs , minBrushDelta ? , roundHistogramBrushValues ? ) { const isXHorizontal = ! isVerticalRotation ( rotation ) ; const scaledPanelPoints = getMinMaxPos ( chartDimensions , lastDrag , smallMultipleScales , isXHorizontal ) ; let { minPos , maxPos } = scaledPanelPoints ; if ( rotation === - 90 || rotation === 180 ) { minPos = scaledPanelPoints . panelMax - minPos ; maxPos = scaledPanelPoints . panelMax - maxPos ; } if ( minBrushDelta !== undefined ? Math . abs ( maxPos - minPos ) < minBrushDelta : maxPos === minPos ) { return ; } const offset = histogramMode ? 0 : - ( xScale . bandwidth + xScale . bandwidthPadding ) / 2 ; const histogramEnabled = isHistogramEnabled ( seriesSpecs ) ; const invertValue = histogramEnabled && roundHistogramBrushValues ? ( value ) => xScale . invertWithStep ( value , xScale . domain ) . value : ( value ) => xScale . invert ( value ) ; const minPosScaled = invertValue ( minPos + offset ) ; const maxPosScaled = invertValue ( maxPos + offset ) ; const maxDomainValue = xScale . domain [ 1 ] + ( histogramEnabled && allowBrushingLastHistogramBin ? xScale . minInterval : 0 ) ; const minValue = clamp ( minPosScaled , xScale . domain [ 0 ] , maxPosScaled ) ; const maxValue = clamp ( minPosScaled , maxPosScaled , maxDomainValue ) ; return [ minValue , maxValue ] ; } function getMinMaxPos ( chartDimensions , lastDrag , smallMultipleScales , scaleXPoint ) { const panelPoints = getPanelPoints ( chartDimensions , lastDrag , smallMultipleScales ) ; return scalePanelPointsToPanelCoordinates ( scaleXPoint , panelPoints ) ; } function getPanelPoints ( chartDimensions , lastDrag , smallMultipleScales ) { const plotStartPointPx = getPlotAreaRestrictedPoint ( lastDrag . start . position , chartDimensions ) ; const plotEndPointPx = getPlotAreaRestrictedPoint ( lastDrag . end . position , chartDimensions ) ; return getPointsConstraintToSinglePanel ( plotStartPointPx , plotEndPointPx , smallMultipleScales ) ; } function getYBrushExtents ( chartDimensions , lastDrag , rotation , yScales , smallMultipleScales , minBrushDelta ? ) { const yValues = [ ] ; yScales . forEach ( ( yScale , groupId ) => { const isXVertical = isVerticalRotation ( rotation ) ; const scaledPanelPoints = getMinMaxPos ( chartDimensions , lastDrag , smallMultipleScales , isXVertical ) ; let { minPos , maxPos } = scaledPanelPoints ; if ( rotation === 90 || rotation === 180 ) { minPos = scaledPanelPoints . panelMax - minPos ; maxPos = scaledPanelPoints . panelMax - maxPos ; } if ( minBrushDelta !== undefined ? Math . abs ( maxPos - minPos ) < minBrushDelta : maxPos === minPos ) { return ; } const minPosScaled = yScale . invert ( minPos ) ; const maxPosScaled = yScale . invert ( maxPos ) ; const minValue = clamp ( minPosScaled , yScale . domain [ 0 ] , maxPosScaled ) ; const maxValue = clamp ( minPosScaled , maxPosScaled , yScale . domain [ 1 ] ) ; yValues . push ( { extent : [ minValue , maxValue ] , groupId } ) ; } ) ; return yValues . length === 0 ? undefined : yValues ; }