From 8a98899bfd1cbe3e08f4ad6563fdd28b33c70859 Mon Sep 17 00:00:00 2001 From: Stanislav Germanovskii Date: Wed, 14 Apr 2021 22:45:59 +0300 Subject: [PATCH 01/15] feat(brush): add using widows move events with isUseWindowMoveEvents props --- packages/visx-brush/src/BaseBrush.tsx | 185 ++++++++++++++++++++- packages/visx-brush/src/Brush.tsx | 5 + packages/visx-brush/src/BrushHandle.tsx | 30 +++- packages/visx-brush/src/BrushSelection.tsx | 35 +++- packages/visx-drag/src/Drag.tsx | 5 +- packages/visx-drag/src/useDrag.ts | 9 + 6 files changed, 255 insertions(+), 14 deletions(-) diff --git a/packages/visx-brush/src/BaseBrush.tsx b/packages/visx-brush/src/BaseBrush.tsx index d7d33dff3..46845fc1b 100644 --- a/packages/visx-brush/src/BaseBrush.tsx +++ b/packages/visx-brush/src/BaseBrush.tsx @@ -7,6 +7,7 @@ import BrushHandle from './BrushHandle'; import BrushCorner from './BrushCorner'; import BrushSelection from './BrushSelection'; import { MarginShape, Point, BrushShape, ResizeTriggerAreas, PartialBrushStartEnd } from './types'; +import {localPoint} from '@visx/event'; const BRUSH_OVERLAY_STYLES = { cursor: 'crosshair' }; @@ -33,11 +34,18 @@ export type BaseBrushProps = { clickSensitivity: number; disableDraggingSelection: boolean; resetOnEnd?: boolean; + isUseWindowMoveEvents?: boolean; + isBrushResizeInProgress?: boolean; }; export type BaseBrushState = BrushShape & { activeHandle: ResizeTriggerAreas | null; isBrushing: boolean; + isDragInProgress: boolean; + isMovingBrushSelection: boolean; + brushHandleChange: { + [key in ResizeTriggerAreas]?: boolean + }; }; export type UpdateBrush = @@ -68,6 +76,14 @@ export default class BaseBrush extends React.Component { + const {isUseWindowMoveEvents} = this.props; + const { isDragInProgress, isMovingBrushSelection, brushHandleChange } = this.state; + const isBrushHandleChange = brushHandleChange.top || + brushHandleChange.bottom || + brushHandleChange.left || + brushHandleChange.right; + + if (isUseWindowMoveEvents && (isDragInProgress || isMovingBrushSelection || isBrushHandleChange)) { + this.updateBrush((prevBrush: BaseBrushState) => { + const {start, end, extent} = prevBrush; + + start.x = Math.min(extent.x0, extent.x1); + start.y = Math.min(extent.y0, extent.y0); + end.x = Math.max(extent.x0, extent.x1); + end.y = Math.max(extent.y0, extent.y1); + + return { + ...prevBrush, + activeHandle: null, + isBrushing: false, + isDragInProgress: false, + isMovingBrushSelection: false, + brushHandleChange: Object.keys(brushHandleChange).reduce((res, key) => ({ + ...res, + [key]: false + }), {}) + }; + }); + } + } + + handleMouseMove = (event: MouseEvent) => { + const {isUseWindowMoveEvents, left, top, inheritedMargin} = this.props; + const { isDragInProgress, isMovingBrushSelection, brushHandleChange, isBrushing, bounds, start, end } = this.state; + + if (!isUseWindowMoveEvents || !isBrushing) return; + + const marginLeft = (inheritedMargin && inheritedMargin.left) || 0; + const marginTop = (inheritedMargin && inheritedMargin.top) || 0; + const point = localPoint(event); + const calculatedX = Math.min( + Math.max((point?.x || 0) - left - marginLeft, bounds.x0), + bounds.x1 + ); + const calculatedY = Math.min( + Math.max((point?.y || 0) - top - marginTop, bounds.y0), + bounds.y1 + ); + + if (brushHandleChange.left || brushHandleChange.top) { + let newStart = {x: calculatedX, y: calculatedY}; + this.updateBrush((prevBrush: BaseBrushState) => { + const extent = this.getExtent(newStart, end); + return { + ...prevBrush, + start: newStart, + end: end, + extent, + }; + }); + return; + } + + if (isDragInProgress || brushHandleChange.right || brushHandleChange.bottom) { + let newEnd = {x: calculatedX, y: calculatedY} + this.updateBrush((prevBrush: BaseBrushState) => { + const extent = this.getExtent(start, newEnd); + return { + ...prevBrush, + end: newEnd, + extent, + }; + }); + return; + } + + if (isMovingBrushSelection) { + let newStart = start; + let newEnd = {x: calculatedX, y: calculatedY} + + const xDiff = calculatedX - end.x; + const yDiff = calculatedY - end.y; + + newStart = { + x: newStart.x + xDiff, + y: newStart.y + yDiff + } + + if (newStart.x < bounds.x0) { + newEnd.x = newEnd.x + (bounds.x0 - newStart.x); + newStart.x = bounds.x0; + } + if (newStart.y < bounds.y0) { + newEnd.y = newEnd.y + (bounds.y0 - newStart.y); + newStart.y = bounds.y0; + } + if (newEnd.x > bounds.x1) { + newStart.x = newStart.x - (newEnd.x - bounds.x1); + newEnd.x = bounds.x1; + } + if (newEnd.y > bounds.y1) { + newStart.y = newStart.y - (newEnd.y - bounds.y1); + newEnd.y = bounds.y1; + } + this.updateBrush((prevBrush: BaseBrushState) => { + const extent = this.getExtent(newStart, newEnd); + return { + ...prevBrush, + start: newStart, + end: newEnd, + extent, + }; + }); + return; + } + } + getExtent = (start: Partial, end: Partial) => { const { brushDirection, width, height } = this.props; const x0 = brushDirection === 'vertical' ? 0 : Math.min(start.x || 0, end.x || 0); @@ -151,6 +296,7 @@ export default class BaseBrush extends React.Component { + this.updateBrush((prevBrush: BaseBrushState) => { + return { + ...prevBrush, + isMovingBrushSelection: value, + isBrushing: true + }; + }); + } + + onBrushHandleChange = (value: boolean, type: ResizeTriggerAreas) => { + this.updateBrush((prevBrush: BaseBrushState) => { + return { + ...prevBrush, + brushHandleChange: { + ...prevBrush.brushHandleChange, + [type]: value + }, + isBrushing: true + }; + }); + } + render() { const { start, end } = this.state; const { @@ -355,8 +528,11 @@ export default class BaseBrush extends React.Component {({ dragStart, isDragging, dragMove, dragEnd }) => ( )} {/* handles */} @@ -442,8 +622,11 @@ export default class BaseBrush extends React.Component ) ); diff --git a/packages/visx-brush/src/Brush.tsx b/packages/visx-brush/src/Brush.tsx index a1bcf0a03..7d2678355 100644 --- a/packages/visx-brush/src/Brush.tsx +++ b/packages/visx-brush/src/Brush.tsx @@ -58,6 +58,8 @@ export type BrushProps = { handleSize: number; /** Reference to the BaseBrush component. */ innerRef?: React.MutableRefObject; + /** Prevent drag end on mouse leave from brush */ + isUseWindowMoveEvents?: boolean; }; class Brush extends Component { @@ -94,6 +96,7 @@ class Brush extends Component { onMouseMove: null, onMouseLeave: null, onClick: null, + isUseWindowMoveEvents: false }; handleChange = (brush: BaseBrushState) => { @@ -174,6 +177,7 @@ class Brush extends Component { onMouseMove, onClick, handleSize, + isUseWindowMoveEvents } = this.props; if (!xScale || !yScale) return null; @@ -234,6 +238,7 @@ class Brush extends Component { onClick={onClick} onMouseLeave={onMouseLeave} onMouseMove={onMouseMove} + isUseWindowMoveEvents={isUseWindowMoveEvents} /> ); } diff --git a/packages/visx-brush/src/BrushHandle.tsx b/packages/visx-brush/src/BrushHandle.tsx index 3a859ba45..5d3190254 100644 --- a/packages/visx-brush/src/BrushHandle.tsx +++ b/packages/visx-brush/src/BrushHandle.tsx @@ -12,12 +12,23 @@ export type BrushHandleProps = { onBrushEnd?: (brush: BrushState) => void; type: ResizeTriggerAreas; handle: { x: number; y: number; width: number; height: number }; + isUseWindowMoveEvents?: boolean; + isDragInProgress?: boolean; + onBrushHandleChange?: (value: boolean, type: ResizeTriggerAreas) => void; }; /** BrushHandle's are placed along the bounds of the brush and handle Drag events which update the passed brush. */ export default class BrushHandle extends React.Component { + handleDragStart = () => { + const {onBrushHandleChange, type} = this.props; + + if (onBrushHandleChange) { + onBrushHandleChange(true, type); + } + } + handleDragMove = (drag: DragArgs) => { - const { updateBrush, type } = this.props; + const { updateBrush, type, onBrushHandleChange} = this.props; if (!drag.isDragging) return; updateBrush((prevBrush: BrushState) => { @@ -76,10 +87,13 @@ export default class BrushHandle extends React.Component { return prevBrush; } }); + if (onBrushHandleChange) { + onBrushHandleChange(true, type); + } }; handleDragEnd = () => { - const { updateBrush, onBrushEnd } = this.props; + const { updateBrush, onBrushEnd, onBrushHandleChange, type } = this.props; updateBrush((prevBrush: BrushState) => { const { start, end, extent } = prevBrush; start.x = Math.min(extent.x0, extent.x1); @@ -105,10 +119,13 @@ export default class BrushHandle extends React.Component { return nextBrush; }); + if (onBrushHandleChange) { + onBrushHandleChange(false, type); + } }; render() { - const { stageWidth, stageHeight, brush, type, handle } = this.props; + const { stageWidth, stageHeight, brush, type, handle, isUseWindowMoveEvents, isDragInProgress } = this.props; const { x, y, width, height } = handle; const cursor = type === 'right' || type === 'left' ? 'ew-resize' : 'ns-resize'; @@ -119,6 +136,7 @@ export default class BrushHandle extends React.Component { onDragMove={this.handleDragMove} onDragEnd={this.handleDragEnd} resetOnStart + isDragging={isUseWindowMoveEvents ? isDragInProgress : undefined} > {({ dragStart, dragEnd, dragMove, isDragging }) => ( @@ -130,8 +148,8 @@ export default class BrushHandle extends React.Component { height={stageHeight} style={{ cursor }} onMouseMove={dragMove} - onMouseUp={dragEnd} - onMouseLeave={dragEnd} + onMouseUp={isUseWindowMoveEvents ? undefined : dragEnd} + onMouseLeave={isUseWindowMoveEvents ? undefined : dragEnd} /> )} { className={`visx-brush-handle-${type}`} onPointerDown={dragStart} onPointerMove={dragMove} - onPointerUp={dragEnd} + onPointerUp={isUseWindowMoveEvents ? undefined : dragEnd} style={{ cursor, pointerEvents: !!brush.activeHandle || !!brush.isBrushing ? 'none' : 'all', diff --git a/packages/visx-brush/src/BrushSelection.tsx b/packages/visx-brush/src/BrushSelection.tsx index fd0ab708e..174bdcc2c 100644 --- a/packages/visx-brush/src/BrushSelection.tsx +++ b/packages/visx-brush/src/BrushSelection.tsx @@ -14,6 +14,7 @@ export type BrushSelectionProps = { stageHeight: number; brush: BrushState; updateBrush: (update: UpdateBrush) => void; + onMoveSelectionChange?: (value: boolean) => void; onBrushEnd?: (brush: BrushState) => void; disableDraggingSelection: boolean; onMouseLeave: PointerHandler; @@ -21,6 +22,8 @@ export type BrushSelectionProps = { onMouseUp: PointerHandler; onClick: PointerHandler; selectedBoxStyle: React.SVGProps; + isUseWindowMoveEvents?: boolean; + isDragInProgress?: boolean; }; export default class BrushSelection extends React.Component< @@ -33,8 +36,16 @@ export default class BrushSelection extends React.Component< onClick: null, }; + selectionDragStart = () => { + const {onMoveSelectionChange} = this.props; + + if (onMoveSelectionChange) { + onMoveSelectionChange(true) + } + } + selectionDragMove = (drag: DragArgs) => { - const { updateBrush } = this.props; + const { updateBrush, onMoveSelectionChange } = this.props; updateBrush((prevBrush: BrushState) => { const { x: x0, y: y0 } = prevBrush.start; const { x: x1, y: y1 } = prevBrush.end; @@ -60,10 +71,13 @@ export default class BrushSelection extends React.Component< }, }; }); + if (onMoveSelectionChange) { + onMoveSelectionChange(true) + } }; selectionDragEnd = () => { - const { updateBrush, onBrushEnd } = this.props; + const { updateBrush, onBrushEnd, onMoveSelectionChange } = this.props; updateBrush((prevBrush: BrushState) => { const nextBrush = { ...prevBrush, @@ -82,9 +96,12 @@ export default class BrushSelection extends React.Component< if (onBrushEnd) { onBrushEnd(nextBrush); } - return nextBrush; }); + if (onMoveSelectionChange) { + onMoveSelectionChange(false) + } + }; render() { @@ -100,6 +117,8 @@ export default class BrushSelection extends React.Component< onMouseUp, onClick, selectedBoxStyle, + isUseWindowMoveEvents, + isDragInProgress } = this.props; return ( @@ -107,8 +126,10 @@ export default class BrushSelection extends React.Component< width={width} height={height} resetOnStart + onDragStart={this.selectionDragStart} onDragMove={this.selectionDragMove} onDragEnd={this.selectionDragEnd} + isDragging={isUseWindowMoveEvents ? isDragInProgress : undefined} > {({ isDragging, dragStart, dragEnd, dragMove }) => ( @@ -117,9 +138,9 @@ export default class BrushSelection extends React.Component< width={stageWidth} height={stageHeight} fill="transparent" - onPointerUp={dragEnd} + onPointerUp={isUseWindowMoveEvents ? undefined : dragEnd} onPointerMove={dragMove} - onPointerLeave={dragEnd} + onPointerLeave={isUseWindowMoveEvents ? undefined : dragEnd} style={DRAGGING_OVERLAY_STYLES} /> )} @@ -138,7 +159,9 @@ export default class BrushSelection extends React.Component< if (onMouseMove) onMouseMove(event); }} onPointerUp={event => { - dragEnd(event); + if (!isUseWindowMoveEvents) { + dragEnd(event) + } if (onMouseUp) onMouseUp(event); }} onClick={event => { diff --git a/packages/visx-drag/src/Drag.tsx b/packages/visx-drag/src/Drag.tsx index 2e6257287..82d2879ed 100644 --- a/packages/visx-drag/src/Drag.tsx +++ b/packages/visx-drag/src/Drag.tsx @@ -13,6 +13,8 @@ export type DragProps = UseDragOptions & { height: number; /** Whether to render an invisible rect below children to capture the drag area as defined by width and height. */ captureDragArea?: boolean; + /** move drag state control to parent */ + isDragging?: boolean; }; export default function Drag({ @@ -28,8 +30,9 @@ export default function Drag({ width, x, y, + isDragging, }: DragProps) { - const drag = useDrag({ resetOnStart, onDragEnd, onDragMove, onDragStart, x, y, dx, dy }); + const drag = useDrag({ resetOnStart, onDragEnd, onDragMove, onDragStart, x, y, dx, dy, isDragging }); return ( <> diff --git a/packages/visx-drag/src/useDrag.ts b/packages/visx-drag/src/useDrag.ts index ff904c18e..58512cee2 100644 --- a/packages/visx-drag/src/useDrag.ts +++ b/packages/visx-drag/src/useDrag.ts @@ -26,6 +26,8 @@ export type UseDragOptions = { dx?: number; /** Optionally set the initial drag dy, or override the current drag dy. */ dy?: number; + /** If defined - parent controls dragging state */ + isDragging?: boolean }; export type DragState = { @@ -60,6 +62,7 @@ export default function useDrag({ y, dx, dy, + isDragging, }: UseDragOptions | undefined = {}): UseDrag { // use ref to detect prop changes const positionPropsRef = useRef({ x, y, dx, dy }); @@ -85,6 +88,12 @@ export default function useDrag({ } }); + useEffect(() => { + if (isDragging !== undefined && dragState.isDragging !== isDragging) { + setDragStateWithCallback(currState => ({ ...currState, isDragging: isDragging })); + } + }, [isDragging]) + const handleDragStart = useCallback( (event: MouseTouchOrPointerEvent) => { event.persist(); From bf17e3f1eafda44a4966b2d086a1b9ef80f63bb4 Mon Sep 17 00:00:00 2001 From: Stanislav Germanovskii Date: Wed, 14 Apr 2021 23:09:28 +0300 Subject: [PATCH 02/15] fix(brush): lint fixes --- packages/visx-brush/package.json | 3 +- packages/visx-brush/src/BaseBrush.tsx | 123 ++++++++++++--------- packages/visx-brush/src/BrushHandle.tsx | 16 ++- packages/visx-brush/src/BrushSelection.tsx | 15 ++- packages/visx-drag/src/Drag.tsx | 12 +- packages/visx-drag/src/useDrag.ts | 10 +- 6 files changed, 105 insertions(+), 74 deletions(-) diff --git a/packages/visx-brush/package.json b/packages/visx-brush/package.json index 0be169e97..e15211f91 100644 --- a/packages/visx-brush/package.json +++ b/packages/visx-brush/package.json @@ -37,7 +37,8 @@ "@visx/group": "1.7.0", "@visx/shape": "1.7.0", "classnames": "^2.2.5", - "prop-types": "^15.6.1" + "prop-types": "^15.6.1", + "@visx/event": "1.7.0" }, "devDependencies": { "@visx/scale": "1.7.0" diff --git a/packages/visx-brush/src/BaseBrush.tsx b/packages/visx-brush/src/BaseBrush.tsx index 46845fc1b..7fcbef252 100644 --- a/packages/visx-brush/src/BaseBrush.tsx +++ b/packages/visx-brush/src/BaseBrush.tsx @@ -1,13 +1,13 @@ import React from 'react'; import { Group } from '@visx/group'; import { Bar } from '@visx/shape'; +import { localPoint } from '@visx/event'; import Drag, { HandlerArgs as DragArgs } from '@visx/drag/lib/Drag'; import BrushHandle from './BrushHandle'; import BrushCorner from './BrushCorner'; import BrushSelection from './BrushSelection'; import { MarginShape, Point, BrushShape, ResizeTriggerAreas, PartialBrushStartEnd } from './types'; -import {localPoint} from '@visx/event'; const BRUSH_OVERLAY_STYLES = { cursor: 'crosshair' }; @@ -44,7 +44,7 @@ export type BaseBrushState = BrushShape & { isDragInProgress: boolean; isMovingBrushSelection: boolean; brushHandleChange: { - [key in ResizeTriggerAreas]?: boolean + [key in ResizeTriggerAreas]?: boolean; }; }; @@ -82,7 +82,7 @@ export default class BaseBrush extends React.Component { - const {isUseWindowMoveEvents} = this.props; + const { isUseWindowMoveEvents } = this.props; const { isDragInProgress, isMovingBrushSelection, brushHandleChange } = this.state; - const isBrushHandleChange = brushHandleChange.top || - brushHandleChange.bottom || - brushHandleChange.left || - brushHandleChange.right; - - if (isUseWindowMoveEvents && (isDragInProgress || isMovingBrushSelection || isBrushHandleChange)) { + const isBrushHandleChange = + brushHandleChange.top || + brushHandleChange.bottom || + brushHandleChange.left || + brushHandleChange.right; + + if ( + isUseWindowMoveEvents && + (isDragInProgress || isMovingBrushSelection || isBrushHandleChange) + ) { this.updateBrush((prevBrush: BaseBrushState) => { - const {start, end, extent} = prevBrush; + const { start, end, extent } = prevBrush; start.x = Math.min(extent.x0, extent.x1); start.y = Math.min(extent.y0, extent.y0); @@ -161,41 +165,49 @@ export default class BaseBrush extends React.Component ({ - ...res, - [key]: false - }), {}) + brushHandleChange: Object.keys(brushHandleChange).reduce( + (res, key) => ({ + ...res, + [key]: false, + }), + {}, + ), }; }); } - } + }; handleMouseMove = (event: MouseEvent) => { - const {isUseWindowMoveEvents, left, top, inheritedMargin} = this.props; - const { isDragInProgress, isMovingBrushSelection, brushHandleChange, isBrushing, bounds, start, end } = this.state; + const { isUseWindowMoveEvents, left, top, inheritedMargin } = this.props; + const { + isDragInProgress, + isMovingBrushSelection, + brushHandleChange, + isBrushing, + bounds, + start, + end, + } = this.state; if (!isUseWindowMoveEvents || !isBrushing) return; - const marginLeft = (inheritedMargin && inheritedMargin.left) || 0; - const marginTop = (inheritedMargin && inheritedMargin.top) || 0; + const marginLeft = inheritedMargin?.left || 0; + const marginTop = inheritedMargin?.top || 0; const point = localPoint(event); const calculatedX = Math.min( - Math.max((point?.x || 0) - left - marginLeft, bounds.x0), - bounds.x1 - ); - const calculatedY = Math.min( - Math.max((point?.y || 0) - top - marginTop, bounds.y0), - bounds.y1 + Math.max((point?.x || 0) - left - marginLeft, bounds.x0), + bounds.x1, ); + const calculatedY = Math.min(Math.max((point?.y || 0) - top - marginTop, bounds.y0), bounds.y1); if (brushHandleChange.left || brushHandleChange.top) { - let newStart = {x: calculatedX, y: calculatedY}; + const newStart = { x: calculatedX, y: calculatedY }; this.updateBrush((prevBrush: BaseBrushState) => { const extent = this.getExtent(newStart, end); return { ...prevBrush, start: newStart, - end: end, + end, extent, }; }); @@ -203,7 +215,7 @@ export default class BaseBrush extends React.Component { const extent = this.getExtent(start, newEnd); return { @@ -217,30 +229,30 @@ export default class BaseBrush extends React.Component bounds.x1) { - newStart.x = newStart.x - (newEnd.x - bounds.x1); + newStart.x -= newEnd.x - bounds.x1; newEnd.x = bounds.x1; } if (newEnd.y > bounds.y1) { - newStart.y = newStart.y - (newEnd.y - bounds.y1); + newStart.y -= newEnd.y - bounds.y1; newEnd.y = bounds.y1; } this.updateBrush((prevBrush: BaseBrushState) => { @@ -252,9 +264,8 @@ export default class BaseBrush extends React.Component, end: Partial) => { const { brushDirection, width, height } = this.props; @@ -273,8 +284,8 @@ export default class BaseBrush extends React.Component { const { onBrushStart, left, top, inheritedMargin } = this.props; - const marginLeft = inheritedMargin && inheritedMargin.left ? inheritedMargin.left : 0; - const marginTop = inheritedMargin && inheritedMargin.top ? inheritedMargin.top : 0; + const marginLeft = inheritedMargin?.left ? inheritedMargin.left : 0; + const marginTop = inheritedMargin?.top ? inheritedMargin.top : 0; const start = { x: (draw.x || 0) + draw.dx - left - marginLeft, y: (draw.y || 0) + draw.dy - top - marginTop, @@ -303,8 +314,8 @@ export default class BaseBrush extends React.Component { const { left, top, inheritedMargin } = this.props; if (!drag.isDragging) return; - const marginLeft = (inheritedMargin && inheritedMargin.left) || 0; - const marginTop = (inheritedMargin && inheritedMargin.top) || 0; + const marginLeft = inheritedMargin?.left || 0; + const marginTop = inheritedMargin?.top || 0; const end = { x: (drag.x || 0) + drag.dx - left - marginLeft, y: (drag.y || 0) + drag.dy - top - marginTop, @@ -489,28 +500,28 @@ export default class BaseBrush extends React.Component { + handleMovingBrushSelectionChange = (value: boolean) => { this.updateBrush((prevBrush: BaseBrushState) => { return { ...prevBrush, isMovingBrushSelection: value, - isBrushing: true + isBrushing: true, }; }); - } + }; - onBrushHandleChange = (value: boolean, type: ResizeTriggerAreas) => { - this.updateBrush((prevBrush: BaseBrushState) => { + handleBrushHandleChange = (value: boolean, type: ResizeTriggerAreas) => { + this.updateBrush((prevBrush: BaseBrushState) => { return { ...prevBrush, brushHandleChange: { ...prevBrush.brushHandleChange, - [type]: value + [type]: value, }, - isBrushing: true + isBrushing: true, }; }); - } + }; render() { const { start, end } = this.state; @@ -528,7 +539,7 @@ export default class BaseBrush extends React.Component ) ); diff --git a/packages/visx-brush/src/BrushHandle.tsx b/packages/visx-brush/src/BrushHandle.tsx index 5d3190254..5f79a0d06 100644 --- a/packages/visx-brush/src/BrushHandle.tsx +++ b/packages/visx-brush/src/BrushHandle.tsx @@ -20,15 +20,15 @@ export type BrushHandleProps = { /** BrushHandle's are placed along the bounds of the brush and handle Drag events which update the passed brush. */ export default class BrushHandle extends React.Component { handleDragStart = () => { - const {onBrushHandleChange, type} = this.props; + const { onBrushHandleChange, type } = this.props; if (onBrushHandleChange) { onBrushHandleChange(true, type); } - } + }; handleDragMove = (drag: DragArgs) => { - const { updateBrush, type, onBrushHandleChange} = this.props; + const { updateBrush, type, onBrushHandleChange } = this.props; if (!drag.isDragging) return; updateBrush((prevBrush: BrushState) => { @@ -125,7 +125,15 @@ export default class BrushHandle extends React.Component { }; render() { - const { stageWidth, stageHeight, brush, type, handle, isUseWindowMoveEvents, isDragInProgress } = this.props; + const { + stageWidth, + stageHeight, + brush, + type, + handle, + isUseWindowMoveEvents, + isDragInProgress, + } = this.props; const { x, y, width, height } = handle; const cursor = type === 'right' || type === 'left' ? 'ew-resize' : 'ns-resize'; diff --git a/packages/visx-brush/src/BrushSelection.tsx b/packages/visx-brush/src/BrushSelection.tsx index 174bdcc2c..f41930530 100644 --- a/packages/visx-brush/src/BrushSelection.tsx +++ b/packages/visx-brush/src/BrushSelection.tsx @@ -37,12 +37,12 @@ export default class BrushSelection extends React.Component< }; selectionDragStart = () => { - const {onMoveSelectionChange} = this.props; + const { onMoveSelectionChange } = this.props; if (onMoveSelectionChange) { - onMoveSelectionChange(true) + onMoveSelectionChange(true); } - } + }; selectionDragMove = (drag: DragArgs) => { const { updateBrush, onMoveSelectionChange } = this.props; @@ -72,7 +72,7 @@ export default class BrushSelection extends React.Component< }; }); if (onMoveSelectionChange) { - onMoveSelectionChange(true) + onMoveSelectionChange(true); } }; @@ -99,9 +99,8 @@ export default class BrushSelection extends React.Component< return nextBrush; }); if (onMoveSelectionChange) { - onMoveSelectionChange(false) + onMoveSelectionChange(false); } - }; render() { @@ -118,7 +117,7 @@ export default class BrushSelection extends React.Component< onClick, selectedBoxStyle, isUseWindowMoveEvents, - isDragInProgress + isDragInProgress, } = this.props; return ( @@ -160,7 +159,7 @@ export default class BrushSelection extends React.Component< }} onPointerUp={event => { if (!isUseWindowMoveEvents) { - dragEnd(event) + dragEnd(event); } if (onMouseUp) onMouseUp(event); }} diff --git a/packages/visx-drag/src/Drag.tsx b/packages/visx-drag/src/Drag.tsx index 82d2879ed..95b8b9e0a 100644 --- a/packages/visx-drag/src/Drag.tsx +++ b/packages/visx-drag/src/Drag.tsx @@ -32,7 +32,17 @@ export default function Drag({ y, isDragging, }: DragProps) { - const drag = useDrag({ resetOnStart, onDragEnd, onDragMove, onDragStart, x, y, dx, dy, isDragging }); + const drag = useDrag({ + resetOnStart, + onDragEnd, + onDragMove, + onDragStart, + x, + y, + dx, + dy, + isDragging, + }); return ( <> diff --git a/packages/visx-drag/src/useDrag.ts b/packages/visx-drag/src/useDrag.ts index 58512cee2..52ad1c015 100644 --- a/packages/visx-drag/src/useDrag.ts +++ b/packages/visx-drag/src/useDrag.ts @@ -27,7 +27,7 @@ export type UseDragOptions = { /** Optionally set the initial drag dy, or override the current drag dy. */ dy?: number; /** If defined - parent controls dragging state */ - isDragging?: boolean + isDragging?: boolean; }; export type DragState = { @@ -89,10 +89,10 @@ export default function useDrag({ }); useEffect(() => { - if (isDragging !== undefined && dragState.isDragging !== isDragging) { - setDragStateWithCallback(currState => ({ ...currState, isDragging: isDragging })); - } - }, [isDragging]) + if (isDragging !== undefined && dragState.isDragging !== isDragging) { + setDragStateWithCallback(currState => ({ ...currState, isDragging })); + } + }, [dragState.isDragging, isDragging, setDragStateWithCallback]); const handleDragStart = useCallback( (event: MouseTouchOrPointerEvent) => { From 6e99ad2dc0650ed5a70c77fa1707bc4bd032d662 Mon Sep 17 00:00:00 2001 From: Stanislav Germanovskii Date: Thu, 15 Apr 2021 08:26:05 +0300 Subject: [PATCH 03/15] fix(brush): lint fixes 2 --- packages/visx-brush/src/BaseBrush.tsx | 1 - packages/visx-brush/src/Brush.tsx | 4 ++-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/packages/visx-brush/src/BaseBrush.tsx b/packages/visx-brush/src/BaseBrush.tsx index 7fcbef252..56871651a 100644 --- a/packages/visx-brush/src/BaseBrush.tsx +++ b/packages/visx-brush/src/BaseBrush.tsx @@ -35,7 +35,6 @@ export type BaseBrushProps = { disableDraggingSelection: boolean; resetOnEnd?: boolean; isUseWindowMoveEvents?: boolean; - isBrushResizeInProgress?: boolean; }; export type BaseBrushState = BrushShape & { diff --git a/packages/visx-brush/src/Brush.tsx b/packages/visx-brush/src/Brush.tsx index 7d2678355..62d35a108 100644 --- a/packages/visx-brush/src/Brush.tsx +++ b/packages/visx-brush/src/Brush.tsx @@ -96,7 +96,7 @@ class Brush extends Component { onMouseMove: null, onMouseLeave: null, onClick: null, - isUseWindowMoveEvents: false + isUseWindowMoveEvents: false, }; handleChange = (brush: BaseBrushState) => { @@ -177,7 +177,7 @@ class Brush extends Component { onMouseMove, onClick, handleSize, - isUseWindowMoveEvents + isUseWindowMoveEvents, } = this.props; if (!xScale || !yScale) return null; From 556083647bdb3788ea2e978af14b92ac8c1c4bb5 Mon Sep 17 00:00:00 2001 From: Stanislav Germanovskii Date: Mon, 19 Apr 2021 11:01:54 +0300 Subject: [PATCH 04/15] fix(brush): PR fixes --- packages/visx-brush/package.json | 4 +- packages/visx-brush/src/BaseBrush.tsx | 132 ++++++++------------- packages/visx-brush/src/Brush.tsx | 8 +- packages/visx-brush/src/BrushHandle.tsx | 24 ++-- packages/visx-brush/src/BrushSelection.tsx | 21 ++-- packages/visx-brush/src/types.ts | 2 + packages/visx-drag/src/Drag.tsx | 2 +- packages/visx-drag/src/useDrag.ts | 2 +- 8 files changed, 80 insertions(+), 115 deletions(-) diff --git a/packages/visx-brush/package.json b/packages/visx-brush/package.json index e15211f91..fd9bef070 100644 --- a/packages/visx-brush/package.json +++ b/packages/visx-brush/package.json @@ -34,11 +34,11 @@ }, "dependencies": { "@visx/drag": "1.7.4", + "@visx/event": "1.7.0", "@visx/group": "1.7.0", "@visx/shape": "1.7.0", "classnames": "^2.2.5", - "prop-types": "^15.6.1", - "@visx/event": "1.7.0" + "prop-types": "^15.6.1" }, "devDependencies": { "@visx/scale": "1.7.0" diff --git a/packages/visx-brush/src/BaseBrush.tsx b/packages/visx-brush/src/BaseBrush.tsx index 56871651a..f1a9e5cc8 100644 --- a/packages/visx-brush/src/BaseBrush.tsx +++ b/packages/visx-brush/src/BaseBrush.tsx @@ -7,7 +7,14 @@ import Drag, { HandlerArgs as DragArgs } from '@visx/drag/lib/Drag'; import BrushHandle from './BrushHandle'; import BrushCorner from './BrushCorner'; import BrushSelection from './BrushSelection'; -import { MarginShape, Point, BrushShape, ResizeTriggerAreas, PartialBrushStartEnd } from './types'; +import { + MarginShape, + Point, + BrushShape, + ResizeTriggerAreas, + PartialBrushStartEnd, + BrushingType, +} from './types'; const BRUSH_OVERLAY_STYLES = { cursor: 'crosshair' }; @@ -34,17 +41,13 @@ export type BaseBrushProps = { clickSensitivity: number; disableDraggingSelection: boolean; resetOnEnd?: boolean; - isUseWindowMoveEvents?: boolean; + useWindowMoveEvents?: boolean; }; export type BaseBrushState = BrushShape & { activeHandle: ResizeTriggerAreas | null; isBrushing: boolean; - isDragInProgress: boolean; - isMovingBrushSelection: boolean; - brushHandleChange: { - [key in ResizeTriggerAreas]?: boolean; - }; + brushingType?: BrushingType; }; export type UpdateBrush = @@ -74,15 +77,8 @@ export default class BaseBrush extends React.Component { - const { isUseWindowMoveEvents } = this.props; - const { isDragInProgress, isMovingBrushSelection, brushHandleChange } = this.state; - const isBrushHandleChange = - brushHandleChange.top || - brushHandleChange.bottom || - brushHandleChange.left || - brushHandleChange.right; + const { useWindowMoveEvents } = this.props; + const { brushingType } = this.state; if ( - isUseWindowMoveEvents && - (isDragInProgress || isMovingBrushSelection || isBrushHandleChange) + useWindowMoveEvents && + ['top', 'bottom', 'left', 'right', 'select', 'move'].includes(brushingType ?? '') ) { this.updateBrush((prevBrush: BaseBrushState) => { const { start, end, extent } = prevBrush; @@ -162,33 +157,17 @@ export default class BaseBrush extends React.Component ({ - ...res, - [key]: false, - }), - {}, - ), + brushingType: undefined, }; }); } }; handleMouseMove = (event: MouseEvent) => { - const { isUseWindowMoveEvents, left, top, inheritedMargin } = this.props; - const { - isDragInProgress, - isMovingBrushSelection, - brushHandleChange, - isBrushing, - bounds, - start, - end, - } = this.state; + const { useWindowMoveEvents, left, top, inheritedMargin } = this.props; + const { brushingType, isBrushing, bounds, start, end } = this.state; - if (!isUseWindowMoveEvents || !isBrushing) return; + if (!useWindowMoveEvents || !isBrushing) return; const marginLeft = inheritedMargin?.left || 0; const marginTop = inheritedMargin?.top || 0; @@ -199,7 +178,7 @@ export default class BaseBrush extends React.Component { const extent = this.getExtent(newStart, end); @@ -213,7 +192,7 @@ export default class BaseBrush extends React.Component { const extent = this.getExtent(start, newEnd); @@ -226,7 +205,7 @@ export default class BaseBrush extends React.Component { - this.updateBrush((prevBrush: BaseBrushState) => { - return { - ...prevBrush, - isMovingBrushSelection: value, - isBrushing: true, - }; - }); - }; - - handleBrushHandleChange = (value: boolean, type: ResizeTriggerAreas) => { + handleBrushingTypeChange = (type?: BrushingType) => { this.updateBrush((prevBrush: BaseBrushState) => { return { ...prevBrush, - brushHandleChange: { - ...prevBrush.brushHandleChange, - [type]: value, - }, - isBrushing: true, + brushingType: type, + isBrushing: type !== undefined, }; }); }; @@ -538,10 +502,10 @@ export default class BaseBrush extends React.Component {({ dragStart, isDragging, dragMove, dragEnd }) => ( )} {/* handles */} @@ -632,13 +596,11 @@ export default class BaseBrush extends React.Component ) ); diff --git a/packages/visx-brush/src/Brush.tsx b/packages/visx-brush/src/Brush.tsx index 62d35a108..813e6abe2 100644 --- a/packages/visx-brush/src/Brush.tsx +++ b/packages/visx-brush/src/Brush.tsx @@ -59,7 +59,7 @@ export type BrushProps = { /** Reference to the BaseBrush component. */ innerRef?: React.MutableRefObject; /** Prevent drag end on mouse leave from brush */ - isUseWindowMoveEvents?: boolean; + useWindowMoveEvents?: boolean; }; class Brush extends Component { @@ -96,7 +96,7 @@ class Brush extends Component { onMouseMove: null, onMouseLeave: null, onClick: null, - isUseWindowMoveEvents: false, + useWindowMoveEvents: false, }; handleChange = (brush: BaseBrushState) => { @@ -177,7 +177,7 @@ class Brush extends Component { onMouseMove, onClick, handleSize, - isUseWindowMoveEvents, + useWindowMoveEvents, } = this.props; if (!xScale || !yScale) return null; @@ -238,7 +238,7 @@ class Brush extends Component { onClick={onClick} onMouseLeave={onMouseLeave} onMouseMove={onMouseMove} - isUseWindowMoveEvents={isUseWindowMoveEvents} + useWindowMoveEvents={useWindowMoveEvents} /> ); } diff --git a/packages/visx-brush/src/BrushHandle.tsx b/packages/visx-brush/src/BrushHandle.tsx index 5f79a0d06..6ad3a3771 100644 --- a/packages/visx-brush/src/BrushHandle.tsx +++ b/packages/visx-brush/src/BrushHandle.tsx @@ -2,7 +2,7 @@ import React from 'react'; import Drag, { HandlerArgs as DragArgs } from '@visx/drag/lib/Drag'; import { BaseBrushState as BrushState, UpdateBrush } from './BaseBrush'; -import { ResizeTriggerAreas } from './types'; +import { BrushingType, ResizeTriggerAreas } from './types'; export type BrushHandleProps = { stageWidth: number; @@ -12,9 +12,9 @@ export type BrushHandleProps = { onBrushEnd?: (brush: BrushState) => void; type: ResizeTriggerAreas; handle: { x: number; y: number; width: number; height: number }; - isUseWindowMoveEvents?: boolean; + useWindowMoveEvents?: boolean; isDragInProgress?: boolean; - onBrushHandleChange?: (value: boolean, type: ResizeTriggerAreas) => void; + onBrushHandleChange?: (type?: BrushingType) => void; }; /** BrushHandle's are placed along the bounds of the brush and handle Drag events which update the passed brush. */ @@ -23,7 +23,7 @@ export default class BrushHandle extends React.Component { const { onBrushHandleChange, type } = this.props; if (onBrushHandleChange) { - onBrushHandleChange(true, type); + onBrushHandleChange(type); } }; @@ -88,12 +88,12 @@ export default class BrushHandle extends React.Component { } }); if (onBrushHandleChange) { - onBrushHandleChange(true, type); + onBrushHandleChange(type); } }; handleDragEnd = () => { - const { updateBrush, onBrushEnd, onBrushHandleChange, type } = this.props; + const { updateBrush, onBrushEnd, onBrushHandleChange } = this.props; updateBrush((prevBrush: BrushState) => { const { start, end, extent } = prevBrush; start.x = Math.min(extent.x0, extent.x1); @@ -120,7 +120,7 @@ export default class BrushHandle extends React.Component { return nextBrush; }); if (onBrushHandleChange) { - onBrushHandleChange(false, type); + onBrushHandleChange(); } }; @@ -131,7 +131,7 @@ export default class BrushHandle extends React.Component { brush, type, handle, - isUseWindowMoveEvents, + useWindowMoveEvents, isDragInProgress, } = this.props; const { x, y, width, height } = handle; @@ -144,7 +144,7 @@ export default class BrushHandle extends React.Component { onDragMove={this.handleDragMove} onDragEnd={this.handleDragEnd} resetOnStart - isDragging={isUseWindowMoveEvents ? isDragInProgress : undefined} + isDragging={useWindowMoveEvents ? isDragInProgress : undefined} > {({ dragStart, dragEnd, dragMove, isDragging }) => ( @@ -156,8 +156,8 @@ export default class BrushHandle extends React.Component { height={stageHeight} style={{ cursor }} onMouseMove={dragMove} - onMouseUp={isUseWindowMoveEvents ? undefined : dragEnd} - onMouseLeave={isUseWindowMoveEvents ? undefined : dragEnd} + onMouseUp={useWindowMoveEvents ? undefined : dragEnd} + onMouseLeave={useWindowMoveEvents ? undefined : dragEnd} /> )} { className={`visx-brush-handle-${type}`} onPointerDown={dragStart} onPointerMove={dragMove} - onPointerUp={isUseWindowMoveEvents ? undefined : dragEnd} + onPointerUp={useWindowMoveEvents ? undefined : dragEnd} style={{ cursor, pointerEvents: !!brush.activeHandle || !!brush.isBrushing ? 'none' : 'all', diff --git a/packages/visx-brush/src/BrushSelection.tsx b/packages/visx-brush/src/BrushSelection.tsx index f41930530..9e27dab06 100644 --- a/packages/visx-brush/src/BrushSelection.tsx +++ b/packages/visx-brush/src/BrushSelection.tsx @@ -2,6 +2,7 @@ import React from 'react'; import Drag, { HandlerArgs as DragArgs } from '@visx/drag/lib/Drag'; import { BaseBrushState as BrushState, UpdateBrush } from './BaseBrush'; +import { BrushingType } from './types'; const DRAGGING_OVERLAY_STYLES = { cursor: 'move' }; @@ -14,7 +15,7 @@ export type BrushSelectionProps = { stageHeight: number; brush: BrushState; updateBrush: (update: UpdateBrush) => void; - onMoveSelectionChange?: (value: boolean) => void; + onMoveSelectionChange?: (type?: BrushingType) => void; onBrushEnd?: (brush: BrushState) => void; disableDraggingSelection: boolean; onMouseLeave: PointerHandler; @@ -22,7 +23,7 @@ export type BrushSelectionProps = { onMouseUp: PointerHandler; onClick: PointerHandler; selectedBoxStyle: React.SVGProps; - isUseWindowMoveEvents?: boolean; + useWindowMoveEvents?: boolean; isDragInProgress?: boolean; }; @@ -40,7 +41,7 @@ export default class BrushSelection extends React.Component< const { onMoveSelectionChange } = this.props; if (onMoveSelectionChange) { - onMoveSelectionChange(true); + onMoveSelectionChange('move'); } }; @@ -72,7 +73,7 @@ export default class BrushSelection extends React.Component< }; }); if (onMoveSelectionChange) { - onMoveSelectionChange(true); + onMoveSelectionChange('move'); } }; @@ -99,7 +100,7 @@ export default class BrushSelection extends React.Component< return nextBrush; }); if (onMoveSelectionChange) { - onMoveSelectionChange(false); + onMoveSelectionChange(); } }; @@ -116,7 +117,7 @@ export default class BrushSelection extends React.Component< onMouseUp, onClick, selectedBoxStyle, - isUseWindowMoveEvents, + useWindowMoveEvents, isDragInProgress, } = this.props; @@ -128,7 +129,7 @@ export default class BrushSelection extends React.Component< onDragStart={this.selectionDragStart} onDragMove={this.selectionDragMove} onDragEnd={this.selectionDragEnd} - isDragging={isUseWindowMoveEvents ? isDragInProgress : undefined} + isDragging={useWindowMoveEvents ? isDragInProgress : undefined} > {({ isDragging, dragStart, dragEnd, dragMove }) => ( @@ -137,9 +138,9 @@ export default class BrushSelection extends React.Component< width={stageWidth} height={stageHeight} fill="transparent" - onPointerUp={isUseWindowMoveEvents ? undefined : dragEnd} + onPointerUp={useWindowMoveEvents ? undefined : dragEnd} onPointerMove={dragMove} - onPointerLeave={isUseWindowMoveEvents ? undefined : dragEnd} + onPointerLeave={useWindowMoveEvents ? undefined : dragEnd} style={DRAGGING_OVERLAY_STYLES} /> )} @@ -158,7 +159,7 @@ export default class BrushSelection extends React.Component< if (onMouseMove) onMouseMove(event); }} onPointerUp={event => { - if (!isUseWindowMoveEvents) { + if (!useWindowMoveEvents) { dragEnd(event); } if (onMouseUp) onMouseUp(event); diff --git a/packages/visx-brush/src/types.ts b/packages/visx-brush/src/types.ts index a67ef8c02..6295a81d1 100644 --- a/packages/visx-brush/src/types.ts +++ b/packages/visx-brush/src/types.ts @@ -44,6 +44,8 @@ export type ResizeTriggerAreas = | 'bottomLeft' | 'bottomRight'; +export type BrushingType = 'move' | 'select' | ResizeTriggerAreas; + export interface Scale { (value: Input): Output; ticks?: (count: number) => Input[]; diff --git a/packages/visx-drag/src/Drag.tsx b/packages/visx-drag/src/Drag.tsx index 95b8b9e0a..ee643c4a1 100644 --- a/packages/visx-drag/src/Drag.tsx +++ b/packages/visx-drag/src/Drag.tsx @@ -13,7 +13,7 @@ export type DragProps = UseDragOptions & { height: number; /** Whether to render an invisible rect below children to capture the drag area as defined by width and height. */ captureDragArea?: boolean; - /** move drag state control to parent */ + /** If defined, parent controls dragging state. */ isDragging?: boolean; }; diff --git a/packages/visx-drag/src/useDrag.ts b/packages/visx-drag/src/useDrag.ts index 52ad1c015..1e19abff0 100644 --- a/packages/visx-drag/src/useDrag.ts +++ b/packages/visx-drag/src/useDrag.ts @@ -26,7 +26,7 @@ export type UseDragOptions = { dx?: number; /** Optionally set the initial drag dy, or override the current drag dy. */ dy?: number; - /** If defined - parent controls dragging state */ + /** If defined, parent controls dragging state. */ isDragging?: boolean; }; From 7c47e7351f7b12600532ab9c94e2c72a94d490a4 Mon Sep 17 00:00:00 2001 From: Stanislav Germanovskii Date: Thu, 13 May 2021 07:49:38 +0300 Subject: [PATCH 05/15] fix(brush): fix jumps on moving --- packages/visx-brush/src/BaseBrush.tsx | 85 +++++++++++----------- packages/visx-brush/src/BrushSelection.tsx | 23 +++++- packages/visx-brush/src/types.ts | 4 + 3 files changed, 65 insertions(+), 47 deletions(-) diff --git a/packages/visx-brush/src/BaseBrush.tsx b/packages/visx-brush/src/BaseBrush.tsx index f1a9e5cc8..eabf29fb7 100644 --- a/packages/visx-brush/src/BaseBrush.tsx +++ b/packages/visx-brush/src/BaseBrush.tsx @@ -14,6 +14,7 @@ import { ResizeTriggerAreas, PartialBrushStartEnd, BrushingType, + BrushingOptions, } from './types'; const BRUSH_OVERLAY_STYLES = { cursor: 'crosshair' }; @@ -47,6 +48,7 @@ export type BaseBrushProps = { export type BaseBrushState = BrushShape & { activeHandle: ResizeTriggerAreas | null; isBrushing: boolean; + brushingOptions?: BrushingOptions; brushingType?: BrushingType; }; @@ -165,7 +167,7 @@ export default class BaseBrush extends React.Component { const { useWindowMoveEvents, left, top, inheritedMargin } = this.props; - const { brushingType, isBrushing, bounds, start, end } = this.state; + const { brushingType, isBrushing, brushingOptions, bounds, start, end } = this.state; if (!useWindowMoveEvents || !isBrushing) return; @@ -178,66 +180,56 @@ export default class BaseBrush extends React.Component { - const extent = this.getExtent(newStart, end); + const offsetX = event.pageX - (brushingOptions?.pageX || 0); + const offsetY = event.pageX - (brushingOptions?.pageY || 0); + const { x: x0, y: y0 } = prevBrush.start; + const { x: x1, y: y1 } = prevBrush.end; + const validDx = + offsetX > 0 + ? Math.min(offsetX, prevBrush.bounds.x1 - x1) + : Math.max(offsetX, prevBrush.bounds.x0 - x0); + + const validDy = + offsetY > 0 + ? Math.min(offsetY, prevBrush.bounds.y1 - y1) + : Math.max(offsetY, prevBrush.bounds.y0 - y0); + return { ...prevBrush, - start: newStart, - end, - extent, + isBrushing: true, + extent: { + ...prevBrush.extent, + x0: x0 + validDx, + x1: x1 + validDx, + y0: y0 + validDy, + y1: y1 + validDy, + }, }; }); - return; } - if (['select', 'right', 'bottom'].includes(brushingType ?? '')) { - const newEnd = { x: calculatedX, y: calculatedY }; + if (['left', 'top'].includes(brushingType ?? '')) { + const newStart = { x: calculatedX, y: calculatedY }; this.updateBrush((prevBrush: BaseBrushState) => { - const extent = this.getExtent(start, newEnd); + const extent = this.getExtent(newStart, end); return { ...prevBrush, - end: newEnd, + start: newStart, + end, extent, }; }); return; } - if (brushingType === 'move') { - let newStart = start; + if (['select', 'right', 'bottom'].includes(brushingType ?? '')) { const newEnd = { x: calculatedX, y: calculatedY }; - - const xDiff = calculatedX - end.x; - const yDiff = calculatedY - end.y; - - newStart = { - x: newStart.x + xDiff, - y: newStart.y + yDiff, - }; - - if (newStart.x < bounds.x0) { - newEnd.x += bounds.x0 - newStart.x; - newStart.x = bounds.x0; - } - if (newStart.y < bounds.y0) { - newEnd.y += bounds.y0 - newStart.y; - newStart.y = bounds.y0; - } - if (newEnd.x > bounds.x1) { - newStart.x -= newEnd.x - bounds.x1; - newEnd.x = bounds.x1; - } - if (newEnd.y > bounds.y1) { - newStart.y -= newEnd.y - bounds.y1; - newEnd.y = bounds.y1; - } this.updateBrush((prevBrush: BaseBrushState) => { - const extent = this.getExtent(newStart, newEnd); + const extent = this.getExtent(start, newEnd); return { ...prevBrush, - start: newStart, end: newEnd, extent, }; @@ -471,18 +463,25 @@ export default class BaseBrush extends React.Component { + handleBrushingTypeChange = (type?: BrushingType, options?: BrushingOptions) => { this.updateBrush((prevBrush: BaseBrushState) => { - return { + const next = { ...prevBrush, brushingType: type, isBrushing: type !== undefined, }; + + if (options || type === undefined) { + next.brushingOptions = options; + } + + return next; }); }; diff --git a/packages/visx-brush/src/BrushSelection.tsx b/packages/visx-brush/src/BrushSelection.tsx index 9e27dab06..1a6730b50 100644 --- a/packages/visx-brush/src/BrushSelection.tsx +++ b/packages/visx-brush/src/BrushSelection.tsx @@ -1,8 +1,9 @@ /* eslint react/jsx-handler-names: 0 */ import React from 'react'; import Drag, { HandlerArgs as DragArgs } from '@visx/drag/lib/Drag'; + import { BaseBrushState as BrushState, UpdateBrush } from './BaseBrush'; -import { BrushingType } from './types'; +import { BrushingOptions, BrushingType } from './types'; const DRAGGING_OVERLAY_STYLES = { cursor: 'move' }; @@ -15,7 +16,7 @@ export type BrushSelectionProps = { stageHeight: number; brush: BrushState; updateBrush: (update: UpdateBrush) => void; - onMoveSelectionChange?: (type?: BrushingType) => void; + onMoveSelectionChange?: (type?: BrushingType, options?: BrushingOptions) => void; onBrushEnd?: (brush: BrushState) => void; disableDraggingSelection: boolean; onMouseLeave: PointerHandler; @@ -37,11 +38,25 @@ export default class BrushSelection extends React.Component< onClick: null, }; - selectionDragStart = () => { + selectionDragStart = (drag: DragArgs) => { const { onMoveSelectionChange } = this.props; if (onMoveSelectionChange) { - onMoveSelectionChange('move'); + let pageX; + let pageY; + if (drag.event instanceof TouchEvent) { + const touchEvent = drag.event as React.TouchEvent; + pageX = touchEvent.touches[0].pageX; + pageY = touchEvent.touches[0].pageY; + } else { + const pointerEvent = drag.event as React.PointerEvent; + pageX = pointerEvent.pageX; + pageY = pointerEvent.pageY; + } + onMoveSelectionChange('move', { + pageX, + pageY, + }); } }; diff --git a/packages/visx-brush/src/types.ts b/packages/visx-brush/src/types.ts index 6295a81d1..4d1ba6d50 100644 --- a/packages/visx-brush/src/types.ts +++ b/packages/visx-brush/src/types.ts @@ -45,6 +45,10 @@ export type ResizeTriggerAreas = | 'bottomRight'; export type BrushingType = 'move' | 'select' | ResizeTriggerAreas; +export type BrushingOptions = { + pageX?: number; + pageY?: number; +}; export interface Scale { (value: Input): Output; From 4428ecd0936477519447976b5dbdb3eba51c6c59 Mon Sep 17 00:00:00 2001 From: Stanislav Germanovskii Date: Thu, 13 May 2021 08:02:03 +0300 Subject: [PATCH 06/15] fix(brush): touch event check fix --- packages/visx-brush/src/BrushSelection.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/visx-brush/src/BrushSelection.tsx b/packages/visx-brush/src/BrushSelection.tsx index 1a6730b50..506118c51 100644 --- a/packages/visx-brush/src/BrushSelection.tsx +++ b/packages/visx-brush/src/BrushSelection.tsx @@ -44,7 +44,7 @@ export default class BrushSelection extends React.Component< if (onMoveSelectionChange) { let pageX; let pageY; - if (drag.event instanceof TouchEvent) { + if (window.TouchEvent && drag.event instanceof TouchEvent) { const touchEvent = drag.event as React.TouchEvent; pageX = touchEvent.touches[0].pageX; pageY = touchEvent.touches[0].pageY; From 50f3f43b08dc33c1477c9f0186773c4407345cdb Mon Sep 17 00:00:00 2001 From: Stanislav Germanovskii Date: Fri, 14 May 2021 08:20:45 +0300 Subject: [PATCH 07/15] fix(brush): fix resize --- packages/visx-brush/src/BrushHandle.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/visx-brush/src/BrushHandle.tsx b/packages/visx-brush/src/BrushHandle.tsx index 6ad3a3771..12e2b8b96 100644 --- a/packages/visx-brush/src/BrushHandle.tsx +++ b/packages/visx-brush/src/BrushHandle.tsx @@ -141,6 +141,7 @@ export default class BrushHandle extends React.Component { Date: Wed, 19 May 2021 09:07:36 +0300 Subject: [PATCH 08/15] fix(brush): fix jumping during transition between svg/window --- packages/visx-brush/src/BaseBrush.tsx | 152 +++++++++++++-------- packages/visx-brush/src/BrushHandle.tsx | 70 +++++----- packages/visx-brush/src/BrushSelection.tsx | 71 +++++----- packages/visx-brush/src/utils.ts | 16 +++ packages/visx-drag/src/useDrag.ts | 2 +- 5 files changed, 177 insertions(+), 134 deletions(-) diff --git a/packages/visx-brush/src/BaseBrush.tsx b/packages/visx-brush/src/BaseBrush.tsx index eabf29fb7..d40a5e405 100644 --- a/packages/visx-brush/src/BaseBrush.tsx +++ b/packages/visx-brush/src/BaseBrush.tsx @@ -1,7 +1,6 @@ import React from 'react'; import { Group } from '@visx/group'; import { Bar } from '@visx/shape'; -import { localPoint } from '@visx/event'; import Drag, { HandlerArgs as DragArgs } from '@visx/drag/lib/Drag'; import BrushHandle from './BrushHandle'; @@ -16,6 +15,7 @@ import { BrushingType, BrushingOptions, } from './types'; +import { getPageCoordinates } from './utils'; const BRUSH_OVERLAY_STYLES = { cursor: 'crosshair' }; @@ -166,24 +166,53 @@ export default class BaseBrush extends React.Component { - const { useWindowMoveEvents, left, top, inheritedMargin } = this.props; - const { brushingType, isBrushing, brushingOptions, bounds, start, end } = this.state; + const { useWindowMoveEvents, onBrushEnd, resetOnEnd } = this.props; + const { brushingType, isBrushing, brushingOptions, start } = this.state; if (!useWindowMoveEvents || !isBrushing) return; - const marginLeft = inheritedMargin?.left || 0; - const marginTop = inheritedMargin?.top || 0; - const point = localPoint(event); - const calculatedX = Math.min( - Math.max((point?.x || 0) - left - marginLeft, bounds.x0), - bounds.x1, - ); - const calculatedY = Math.min(Math.max((point?.y || 0) - top - marginTop, bounds.y0), bounds.y1); + const offsetX = event.pageX - (brushingOptions?.pageX || 0); + const offsetY = event.pageY - (brushingOptions?.pageY || 0); + + if (['left', 'right', 'top', 'bottom'].includes(brushingType ?? '')) { + this.updateBrush((prevBrush: BaseBrushState) => { + const { x: x0, y: y0 } = prevBrush.start; + const { x: x1, y: y1 } = prevBrush.end; + + return { + ...prevBrush, + isBrushing: true, + extent: { + ...prevBrush.extent, + ...this.getExtent( + { + x: + brushingType === 'left' + ? Math.min(Math.max(x0 + offsetX, prevBrush.bounds.x0), prevBrush.bounds.x1) + : x0, + y: + brushingType === 'bottom' + ? Math.min(Math.max(y0 + offsetY, prevBrush.bounds.y0), prevBrush.bounds.y1) + : y0, + }, + { + x: + brushingType === 'right' + ? Math.min(Math.max(x1 + offsetX, prevBrush.bounds.x0), prevBrush.bounds.x1) + : x1, + y: + brushingType === 'bottom' + ? Math.min(Math.max(y1 + offsetY, prevBrush.bounds.y0), prevBrush.bounds.y1) + : y1, + }, + ), + }, + }; + }); + } - if (brushingType === 'move') { + if (['move'].includes(brushingType ?? '')) { this.updateBrush((prevBrush: BaseBrushState) => { - const offsetX = event.pageX - (brushingOptions?.pageX || 0); - const offsetY = event.pageX - (brushingOptions?.pageY || 0); const { x: x0, y: y0 } = prevBrush.start; const { x: x1, y: y1 } = prevBrush.end; const validDx = @@ -202,37 +231,38 @@ export default class BaseBrush extends React.Component { - const extent = this.getExtent(newStart, end); - return { - ...prevBrush, - start: newStart, - end, - extent, + const { x: x0, y: y0 } = prevBrush.start; + const newEnd = { + x: Math.min(Math.max(x0 + offsetX, prevBrush.bounds.x0), prevBrush.bounds.x1), + y: Math.min(Math.max(y0 + offsetY, prevBrush.bounds.y0), prevBrush.bounds.y1), }; - }); - return; - } - - if (['select', 'right', 'bottom'].includes(brushingType ?? '')) { - const newEnd = { x: calculatedX, y: calculatedY }; - this.updateBrush((prevBrush: BaseBrushState) => { const extent = this.getExtent(start, newEnd); - return { + + const newState = { ...prevBrush, end: newEnd, extent, }; + + if (onBrushEnd) { + onBrushEnd(newState); + } + + if (resetOnEnd) { + this.reset(); + } + + return newState; }); } }; @@ -253,7 +283,7 @@ export default class BaseBrush extends React.Component { - const { onBrushStart, left, top, inheritedMargin } = this.props; + const { onBrushStart, left, top, inheritedMargin, useWindowMoveEvents } = this.props; const marginLeft = inheritedMargin?.left ? inheritedMargin.left : 0; const marginTop = inheritedMargin?.top ? inheritedMargin.top : 0; const start = { @@ -278,12 +308,13 @@ export default class BaseBrush extends React.Component { - const { left, top, inheritedMargin } = this.props; - if (!drag.isDragging) return; + const { left, top, inheritedMargin, useWindowMoveEvents } = this.props; + if (!drag.isDragging || useWindowMoveEvents) return; const marginLeft = inheritedMargin?.left || 0; const marginTop = inheritedMargin?.top || 0; const end = { @@ -302,34 +333,37 @@ export default class BaseBrush extends React.Component { - const { onBrushEnd, resetOnEnd } = this.props; - this.updateBrush((prevBrush: BaseBrushState) => { - const { extent } = prevBrush; - const newState = { - ...prevBrush, - start: { - x: extent.x0, - y: extent.y0, - }, - end: { - x: extent.x1, - y: extent.y1, - }, - isBrushing: false, - brushingType: undefined, - activeHandle: null, - }; + const { onBrushEnd, resetOnEnd, useWindowMoveEvents } = this.props; - if (onBrushEnd) { - onBrushEnd(newState); - } + if (!useWindowMoveEvents) { + this.updateBrush((prevBrush: BaseBrushState) => { + const { extent } = prevBrush; + const newState = { + ...prevBrush, + start: { + x: extent.x0, + y: extent.y0, + }, + end: { + x: extent.x1, + y: extent.y1, + }, + isBrushing: false, + brushingType: undefined, + activeHandle: null, + }; - if (resetOnEnd) { - this.reset(); - } + if (onBrushEnd) { + onBrushEnd(newState); + } - return newState; - }); + if (resetOnEnd) { + this.reset(); + } + + return newState; + }); + } }; getBrushWidth = () => { diff --git a/packages/visx-brush/src/BrushHandle.tsx b/packages/visx-brush/src/BrushHandle.tsx index 12e2b8b96..167707e3d 100644 --- a/packages/visx-brush/src/BrushHandle.tsx +++ b/packages/visx-brush/src/BrushHandle.tsx @@ -2,7 +2,8 @@ import React from 'react'; import Drag, { HandlerArgs as DragArgs } from '@visx/drag/lib/Drag'; import { BaseBrushState as BrushState, UpdateBrush } from './BaseBrush'; -import { BrushingType, ResizeTriggerAreas } from './types'; +import { BrushingOptions, BrushingType, ResizeTriggerAreas } from './types'; +import { getPageCoordinates } from './utils'; export type BrushHandleProps = { stageWidth: number; @@ -14,22 +15,22 @@ export type BrushHandleProps = { handle: { x: number; y: number; width: number; height: number }; useWindowMoveEvents?: boolean; isDragInProgress?: boolean; - onBrushHandleChange?: (type?: BrushingType) => void; + onBrushHandleChange?: (type?: BrushingType, options?: BrushingOptions) => void; }; /** BrushHandle's are placed along the bounds of the brush and handle Drag events which update the passed brush. */ export default class BrushHandle extends React.Component { - handleDragStart = () => { + handleDragStart = (drag: DragArgs) => { const { onBrushHandleChange, type } = this.props; if (onBrushHandleChange) { - onBrushHandleChange(type); + onBrushHandleChange(type, getPageCoordinates(drag.event)); } }; handleDragMove = (drag: DragArgs) => { - const { updateBrush, type, onBrushHandleChange } = this.props; - if (!drag.isDragging) return; + const { updateBrush, type, useWindowMoveEvents } = this.props; + if (!drag.isDragging || useWindowMoveEvents) return; updateBrush((prevBrush: BrushState) => { const { start, end } = prevBrush; @@ -87,38 +88,39 @@ export default class BrushHandle extends React.Component { return prevBrush; } }); - if (onBrushHandleChange) { - onBrushHandleChange(type); - } }; handleDragEnd = () => { - const { updateBrush, onBrushEnd, onBrushHandleChange } = this.props; - updateBrush((prevBrush: BrushState) => { - const { start, end, extent } = prevBrush; - start.x = Math.min(extent.x0, extent.x1); - start.y = Math.min(extent.y0, extent.y0); - end.x = Math.max(extent.x0, extent.x1); - end.y = Math.max(extent.y0, extent.y1); - const nextBrush: BrushState = { - ...prevBrush, - start, - end, - activeHandle: null, - isBrushing: false, - extent: { - x0: Math.min(start.x, end.x), - x1: Math.max(start.x, end.x), - y0: Math.min(start.y, end.y), - y1: Math.max(start.y, end.y), - }, - }; - if (onBrushEnd) { - onBrushEnd(nextBrush); - } + const { updateBrush, onBrushEnd, onBrushHandleChange, useWindowMoveEvents } = this.props; + + if (!useWindowMoveEvents) { + updateBrush((prevBrush: BrushState) => { + const { start, end, extent } = prevBrush; + start.x = Math.min(extent.x0, extent.x1); + start.y = Math.min(extent.y0, extent.y0); + end.x = Math.max(extent.x0, extent.x1); + end.y = Math.max(extent.y0, extent.y1); + const nextBrush: BrushState = { + ...prevBrush, + start, + end, + activeHandle: null, + isBrushing: false, + extent: { + x0: Math.min(start.x, end.x), + x1: Math.max(start.x, end.x), + y0: Math.min(start.y, end.y), + y1: Math.max(start.y, end.y), + }, + }; + if (onBrushEnd) { + onBrushEnd(nextBrush); + } + + return nextBrush; + }); + } - return nextBrush; - }); if (onBrushHandleChange) { onBrushHandleChange(); } diff --git a/packages/visx-brush/src/BrushSelection.tsx b/packages/visx-brush/src/BrushSelection.tsx index 506118c51..6e349b792 100644 --- a/packages/visx-brush/src/BrushSelection.tsx +++ b/packages/visx-brush/src/BrushSelection.tsx @@ -4,6 +4,7 @@ import Drag, { HandlerArgs as DragArgs } from '@visx/drag/lib/Drag'; import { BaseBrushState as BrushState, UpdateBrush } from './BaseBrush'; import { BrushingOptions, BrushingType } from './types'; +import { getPageCoordinates } from './utils'; const DRAGGING_OVERLAY_STYLES = { cursor: 'move' }; @@ -42,26 +43,15 @@ export default class BrushSelection extends React.Component< const { onMoveSelectionChange } = this.props; if (onMoveSelectionChange) { - let pageX; - let pageY; - if (window.TouchEvent && drag.event instanceof TouchEvent) { - const touchEvent = drag.event as React.TouchEvent; - pageX = touchEvent.touches[0].pageX; - pageY = touchEvent.touches[0].pageY; - } else { - const pointerEvent = drag.event as React.PointerEvent; - pageX = pointerEvent.pageX; - pageY = pointerEvent.pageY; - } - onMoveSelectionChange('move', { - pageX, - pageY, - }); + onMoveSelectionChange('move', getPageCoordinates(drag.event)); } }; selectionDragMove = (drag: DragArgs) => { - const { updateBrush, onMoveSelectionChange } = this.props; + const { updateBrush, useWindowMoveEvents } = this.props; + + if (useWindowMoveEvents) return; + updateBrush((prevBrush: BrushState) => { const { x: x0, y: y0 } = prevBrush.start; const { x: x1, y: y1 } = prevBrush.end; @@ -87,33 +77,34 @@ export default class BrushSelection extends React.Component< }, }; }); - if (onMoveSelectionChange) { - onMoveSelectionChange('move'); - } }; selectionDragEnd = () => { - const { updateBrush, onBrushEnd, onMoveSelectionChange } = this.props; - updateBrush((prevBrush: BrushState) => { - const nextBrush = { - ...prevBrush, - isBrushing: false, - start: { - ...prevBrush.start, - x: Math.min(prevBrush.extent.x0, prevBrush.extent.x1), - y: Math.min(prevBrush.extent.y0, prevBrush.extent.y1), - }, - end: { - ...prevBrush.end, - x: Math.max(prevBrush.extent.x0, prevBrush.extent.x1), - y: Math.max(prevBrush.extent.y0, prevBrush.extent.y1), - }, - }; - if (onBrushEnd) { - onBrushEnd(nextBrush); - } - return nextBrush; - }); + const { updateBrush, onBrushEnd, onMoveSelectionChange, useWindowMoveEvents } = this.props; + + if (!useWindowMoveEvents) { + updateBrush((prevBrush: BrushState) => { + const nextBrush = { + ...prevBrush, + isBrushing: false, + start: { + ...prevBrush.start, + x: Math.min(prevBrush.extent.x0, prevBrush.extent.x1), + y: Math.min(prevBrush.extent.y0, prevBrush.extent.y1), + }, + end: { + ...prevBrush.end, + x: Math.max(prevBrush.extent.x0, prevBrush.extent.x1), + y: Math.max(prevBrush.extent.y0, prevBrush.extent.y1), + }, + }; + if (onBrushEnd) { + onBrushEnd(nextBrush); + } + return nextBrush; + }); + } + if (onMoveSelectionChange) { onMoveSelectionChange(); } diff --git a/packages/visx-brush/src/utils.ts b/packages/visx-brush/src/utils.ts index 571069e07..25696f669 100644 --- a/packages/visx-brush/src/utils.ts +++ b/packages/visx-brush/src/utils.ts @@ -1,3 +1,5 @@ +import { MouseTouchOrPointerEvent } from '@visx/drag/lib/useDrag'; +import React from 'react'; import { Scale } from './types'; export function scaleInvert(scale: Scale, value: number) { @@ -53,3 +55,17 @@ export function getDomainFromExtent( return domain; } + +export function getPageCoordinates(event: MouseTouchOrPointerEvent) { + if (window.TouchEvent && event instanceof TouchEvent) { + return { + pageX: event.touches[0].pageX, + pageY: event.touches[0].pageY, + }; + } + const pointerEvent = event as React.PointerEvent; + return { + pageX: pointerEvent.pageX, + pageY: pointerEvent.pageY, + }; +} diff --git a/packages/visx-drag/src/useDrag.ts b/packages/visx-drag/src/useDrag.ts index 1e19abff0..b8145aff3 100644 --- a/packages/visx-drag/src/useDrag.ts +++ b/packages/visx-drag/src/useDrag.ts @@ -2,7 +2,7 @@ import React, { useCallback, useEffect, useRef } from 'react'; import { localPoint } from '@visx/event'; import useStateWithCallback from './util/useStateWithCallback'; -type MouseTouchOrPointerEvent = React.MouseEvent | React.TouchEvent | React.PointerEvent; +export type MouseTouchOrPointerEvent = React.MouseEvent | React.TouchEvent | React.PointerEvent; export type HandlerArgs = DragState & { /** Drag event. */ From 223453f78af4fa17398cbed667a7404acc61d225 Mon Sep 17 00:00:00 2001 From: Stanislav Germanovskii Date: Thu, 1 Jul 2021 16:27:12 +0300 Subject: [PATCH 09/15] Update packages/visx-brush/src/utils.ts Co-authored-by: Chris Williams --- packages/visx-brush/src/utils.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/visx-brush/src/utils.ts b/packages/visx-brush/src/utils.ts index 25696f669..4198aad51 100644 --- a/packages/visx-brush/src/utils.ts +++ b/packages/visx-brush/src/utils.ts @@ -57,7 +57,7 @@ export function getDomainFromExtent( } export function getPageCoordinates(event: MouseTouchOrPointerEvent) { - if (window.TouchEvent && event instanceof TouchEvent) { + if (typeof window !== 'undefined' && window.TouchEvent && event instanceof TouchEvent) { return { pageX: event.touches[0].pageX, pageY: event.touches[0].pageY, From b0f0bc36cfb534fd1c647bcf9f13e0d14c73bb4c Mon Sep 17 00:00:00 2001 From: Stanislav Germanovskii Date: Thu, 1 Jul 2021 16:27:38 +0300 Subject: [PATCH 10/15] Update packages/visx-brush/src/BaseBrush.tsx Co-authored-by: Chris Williams --- packages/visx-brush/src/BaseBrush.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/visx-brush/src/BaseBrush.tsx b/packages/visx-brush/src/BaseBrush.tsx index d40a5e405..6a6a3df71 100644 --- a/packages/visx-brush/src/BaseBrush.tsx +++ b/packages/visx-brush/src/BaseBrush.tsx @@ -211,7 +211,7 @@ export default class BaseBrush extends React.Component { const { x: x0, y: y0 } = prevBrush.start; const { x: x1, y: y1 } = prevBrush.end; From 5a6c4ffa706b5f57a7f7f89b1ca9466fc0fdaf17 Mon Sep 17 00:00:00 2001 From: Stanislav Germanovskii Date: Thu, 1 Jul 2021 16:27:48 +0300 Subject: [PATCH 11/15] Update packages/visx-brush/src/BaseBrush.tsx Co-authored-by: Chris Williams --- packages/visx-brush/src/BaseBrush.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/visx-brush/src/BaseBrush.tsx b/packages/visx-brush/src/BaseBrush.tsx index 6a6a3df71..995d06478 100644 --- a/packages/visx-brush/src/BaseBrush.tsx +++ b/packages/visx-brush/src/BaseBrush.tsx @@ -239,7 +239,7 @@ export default class BaseBrush extends React.Component { const { x: x0, y: y0 } = prevBrush.start; const newEnd = { From e99612ac74a76ddfc6dc73f2ac05987fcf4963ab Mon Sep 17 00:00:00 2001 From: Stanislav Germanovskii Date: Thu, 1 Jul 2021 16:28:03 +0300 Subject: [PATCH 12/15] Update packages/visx-brush/src/Brush.tsx Co-authored-by: Chris Williams --- packages/visx-brush/src/Brush.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/visx-brush/src/Brush.tsx b/packages/visx-brush/src/Brush.tsx index 813e6abe2..667c34c47 100644 --- a/packages/visx-brush/src/Brush.tsx +++ b/packages/visx-brush/src/Brush.tsx @@ -58,7 +58,7 @@ export type BrushProps = { handleSize: number; /** Reference to the BaseBrush component. */ innerRef?: React.MutableRefObject; - /** Prevent drag end on mouse leave from brush */ + /** Prevent drag end on mouse leaving from brush stage. */ useWindowMoveEvents?: boolean; }; From 01bdf5e25c53713e16567640667d358f61e28f6a Mon Sep 17 00:00:00 2001 From: Stanislav Germanovskii Date: Thu, 1 Jul 2021 16:29:48 +0300 Subject: [PATCH 13/15] fix(brush): rename useWindowMoveEvents to isControlled for child components --- packages/visx-brush/src/BaseBrush.tsx | 4 ++-- packages/visx-brush/src/BrushHandle.tsx | 20 ++++++++++---------- packages/visx-brush/src/BrushSelection.tsx | 20 ++++++++++---------- 3 files changed, 22 insertions(+), 22 deletions(-) diff --git a/packages/visx-brush/src/BaseBrush.tsx b/packages/visx-brush/src/BaseBrush.tsx index 995d06478..4d1dedc9d 100644 --- a/packages/visx-brush/src/BaseBrush.tsx +++ b/packages/visx-brush/src/BaseBrush.tsx @@ -608,7 +608,7 @@ export default class BaseBrush extends React.Component )} @@ -631,7 +631,7 @@ export default class BaseBrush extends React.Component diff --git a/packages/visx-brush/src/BrushHandle.tsx b/packages/visx-brush/src/BrushHandle.tsx index 167707e3d..afa6da686 100644 --- a/packages/visx-brush/src/BrushHandle.tsx +++ b/packages/visx-brush/src/BrushHandle.tsx @@ -13,7 +13,7 @@ export type BrushHandleProps = { onBrushEnd?: (brush: BrushState) => void; type: ResizeTriggerAreas; handle: { x: number; y: number; width: number; height: number }; - useWindowMoveEvents?: boolean; + isControlled?: boolean; isDragInProgress?: boolean; onBrushHandleChange?: (type?: BrushingType, options?: BrushingOptions) => void; }; @@ -29,8 +29,8 @@ export default class BrushHandle extends React.Component { }; handleDragMove = (drag: DragArgs) => { - const { updateBrush, type, useWindowMoveEvents } = this.props; - if (!drag.isDragging || useWindowMoveEvents) return; + const { updateBrush, type, isControlled } = this.props; + if (!drag.isDragging || isControlled) return; updateBrush((prevBrush: BrushState) => { const { start, end } = prevBrush; @@ -91,9 +91,9 @@ export default class BrushHandle extends React.Component { }; handleDragEnd = () => { - const { updateBrush, onBrushEnd, onBrushHandleChange, useWindowMoveEvents } = this.props; + const { updateBrush, onBrushEnd, onBrushHandleChange, isControlled } = this.props; - if (!useWindowMoveEvents) { + if (!isControlled) { updateBrush((prevBrush: BrushState) => { const { start, end, extent } = prevBrush; start.x = Math.min(extent.x0, extent.x1); @@ -133,7 +133,7 @@ export default class BrushHandle extends React.Component { brush, type, handle, - useWindowMoveEvents, + isControlled, isDragInProgress, } = this.props; const { x, y, width, height } = handle; @@ -147,7 +147,7 @@ export default class BrushHandle extends React.Component { onDragMove={this.handleDragMove} onDragEnd={this.handleDragEnd} resetOnStart - isDragging={useWindowMoveEvents ? isDragInProgress : undefined} + isDragging={isControlled ? isDragInProgress : undefined} > {({ dragStart, dragEnd, dragMove, isDragging }) => ( @@ -159,8 +159,8 @@ export default class BrushHandle extends React.Component { height={stageHeight} style={{ cursor }} onMouseMove={dragMove} - onMouseUp={useWindowMoveEvents ? undefined : dragEnd} - onMouseLeave={useWindowMoveEvents ? undefined : dragEnd} + onMouseUp={isControlled ? undefined : dragEnd} + onMouseLeave={isControlled ? undefined : dragEnd} /> )} { className={`visx-brush-handle-${type}`} onPointerDown={dragStart} onPointerMove={dragMove} - onPointerUp={useWindowMoveEvents ? undefined : dragEnd} + onPointerUp={isControlled ? undefined : dragEnd} style={{ cursor, pointerEvents: !!brush.activeHandle || !!brush.isBrushing ? 'none' : 'all', diff --git a/packages/visx-brush/src/BrushSelection.tsx b/packages/visx-brush/src/BrushSelection.tsx index 6e349b792..994cf30a4 100644 --- a/packages/visx-brush/src/BrushSelection.tsx +++ b/packages/visx-brush/src/BrushSelection.tsx @@ -25,7 +25,7 @@ export type BrushSelectionProps = { onMouseUp: PointerHandler; onClick: PointerHandler; selectedBoxStyle: React.SVGProps; - useWindowMoveEvents?: boolean; + isControlled?: boolean; isDragInProgress?: boolean; }; @@ -48,9 +48,9 @@ export default class BrushSelection extends React.Component< }; selectionDragMove = (drag: DragArgs) => { - const { updateBrush, useWindowMoveEvents } = this.props; + const { updateBrush, isControlled } = this.props; - if (useWindowMoveEvents) return; + if (isControlled) return; updateBrush((prevBrush: BrushState) => { const { x: x0, y: y0 } = prevBrush.start; @@ -80,9 +80,9 @@ export default class BrushSelection extends React.Component< }; selectionDragEnd = () => { - const { updateBrush, onBrushEnd, onMoveSelectionChange, useWindowMoveEvents } = this.props; + const { updateBrush, onBrushEnd, onMoveSelectionChange, isControlled } = this.props; - if (!useWindowMoveEvents) { + if (!isControlled) { updateBrush((prevBrush: BrushState) => { const nextBrush = { ...prevBrush, @@ -123,7 +123,7 @@ export default class BrushSelection extends React.Component< onMouseUp, onClick, selectedBoxStyle, - useWindowMoveEvents, + isControlled, isDragInProgress, } = this.props; @@ -135,7 +135,7 @@ export default class BrushSelection extends React.Component< onDragStart={this.selectionDragStart} onDragMove={this.selectionDragMove} onDragEnd={this.selectionDragEnd} - isDragging={useWindowMoveEvents ? isDragInProgress : undefined} + isDragging={isControlled ? isDragInProgress : undefined} > {({ isDragging, dragStart, dragEnd, dragMove }) => ( @@ -144,9 +144,9 @@ export default class BrushSelection extends React.Component< width={stageWidth} height={stageHeight} fill="transparent" - onPointerUp={useWindowMoveEvents ? undefined : dragEnd} + onPointerUp={isControlled ? undefined : dragEnd} onPointerMove={dragMove} - onPointerLeave={useWindowMoveEvents ? undefined : dragEnd} + onPointerLeave={isControlled ? undefined : dragEnd} style={DRAGGING_OVERLAY_STYLES} /> )} @@ -165,7 +165,7 @@ export default class BrushSelection extends React.Component< if (onMouseMove) onMouseMove(event); }} onPointerUp={event => { - if (!useWindowMoveEvents) { + if (!isControlled) { dragEnd(event); } if (onMouseUp) onMouseUp(event); From 93098bd176063fe01cb375514e8b080729fd0611 Mon Sep 17 00:00:00 2001 From: Stanislav Germanovskii Date: Thu, 1 Jul 2021 16:37:50 +0300 Subject: [PATCH 14/15] fix(brush): update brush Example.tsx --- packages/visx-demo/src/sandboxes/visx-brush/Example.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/visx-demo/src/sandboxes/visx-brush/Example.tsx b/packages/visx-demo/src/sandboxes/visx-brush/Example.tsx index 59aed25da..743761f17 100644 --- a/packages/visx-demo/src/sandboxes/visx-brush/Example.tsx +++ b/packages/visx-demo/src/sandboxes/visx-brush/Example.tsx @@ -193,6 +193,7 @@ function BrushChart({ onChange={onBrushChange} onClick={() => setFilteredStock(stock)} selectedBoxStyle={selectedBrushStyle} + useWindowMoveEvents /> From 55e93d5ab6207fae8ff3862f6c5f4569ff817e31 Mon Sep 17 00:00:00 2001 From: Stanislav Germanovskii Date: Fri, 2 Jul 2021 00:04:30 +0300 Subject: [PATCH 15/15] fix(brush): PR fixes --- packages/visx-brush/src/BaseBrush.tsx | 34 ++++++++++++---------- packages/visx-brush/src/BrushHandle.tsx | 4 +-- packages/visx-brush/src/BrushSelection.tsx | 4 +-- packages/visx-brush/src/types.ts | 2 +- 4 files changed, 23 insertions(+), 21 deletions(-) diff --git a/packages/visx-brush/src/BaseBrush.tsx b/packages/visx-brush/src/BaseBrush.tsx index 4d1dedc9d..3d4a22ed1 100644 --- a/packages/visx-brush/src/BaseBrush.tsx +++ b/packages/visx-brush/src/BaseBrush.tsx @@ -13,7 +13,7 @@ import { ResizeTriggerAreas, PartialBrushStartEnd, BrushingType, - BrushingOptions, + BrushPageOffset, } from './types'; import { getPageCoordinates } from './utils'; @@ -48,7 +48,7 @@ export type BaseBrushProps = { export type BaseBrushState = BrushShape & { activeHandle: ResizeTriggerAreas | null; isBrushing: boolean; - brushingOptions?: BrushingOptions; + brushPageOffset?: BrushPageOffset; brushingType?: BrushingType; }; @@ -127,19 +127,19 @@ export default class BaseBrush extends React.Component { + handleWindowPointerUp = () => { const { useWindowMoveEvents } = this.props; const { brushingType } = this.state; @@ -165,14 +165,16 @@ export default class BaseBrush extends React.Component { + handleWindowPointerMove = (event: MouseEvent) => { const { useWindowMoveEvents, onBrushEnd, resetOnEnd } = this.props; - const { brushingType, isBrushing, brushingOptions, start } = this.state; + const { brushingType, isBrushing, brushPageOffset, start } = this.state; if (!useWindowMoveEvents || !isBrushing) return; - const offsetX = event.pageX - (brushingOptions?.pageX || 0); - const offsetY = event.pageY - (brushingOptions?.pageY || 0); + /* We use event page coordinates to calculate the offset between the initial pointer position and + the current pointer position so Brush could be resized/moved relatively. */ + const offsetX = event.pageX - (brushPageOffset?.pageX || 0); + const offsetY = event.pageY - (brushPageOffset?.pageY || 0); if (['left', 'right', 'top', 'bottom'].includes(brushingType ?? '')) { this.updateBrush((prevBrush: BaseBrushState) => { @@ -308,7 +310,7 @@ export default class BaseBrush extends React.Component { + handleBrushingTypeChange = (type?: BrushingType, brushPageOffset?: BrushPageOffset) => { this.updateBrush((prevBrush: BaseBrushState) => { const next = { ...prevBrush, @@ -511,8 +513,8 @@ export default class BaseBrush extends React.Component void; + onBrushHandleChange?: (type?: BrushingType, options?: BrushPageOffset) => void; }; /** BrushHandle's are placed along the bounds of the brush and handle Drag events which update the passed brush. */ diff --git a/packages/visx-brush/src/BrushSelection.tsx b/packages/visx-brush/src/BrushSelection.tsx index 994cf30a4..fbdaf7fbc 100644 --- a/packages/visx-brush/src/BrushSelection.tsx +++ b/packages/visx-brush/src/BrushSelection.tsx @@ -3,7 +3,7 @@ import React from 'react'; import Drag, { HandlerArgs as DragArgs } from '@visx/drag/lib/Drag'; import { BaseBrushState as BrushState, UpdateBrush } from './BaseBrush'; -import { BrushingOptions, BrushingType } from './types'; +import { BrushPageOffset, BrushingType } from './types'; import { getPageCoordinates } from './utils'; const DRAGGING_OVERLAY_STYLES = { cursor: 'move' }; @@ -17,7 +17,7 @@ export type BrushSelectionProps = { stageHeight: number; brush: BrushState; updateBrush: (update: UpdateBrush) => void; - onMoveSelectionChange?: (type?: BrushingType, options?: BrushingOptions) => void; + onMoveSelectionChange?: (type?: BrushingType, options?: BrushPageOffset) => void; onBrushEnd?: (brush: BrushState) => void; disableDraggingSelection: boolean; onMouseLeave: PointerHandler; diff --git a/packages/visx-brush/src/types.ts b/packages/visx-brush/src/types.ts index 4d1ba6d50..b9705fae6 100644 --- a/packages/visx-brush/src/types.ts +++ b/packages/visx-brush/src/types.ts @@ -45,7 +45,7 @@ export type ResizeTriggerAreas = | 'bottomRight'; export type BrushingType = 'move' | 'select' | ResizeTriggerAreas; -export type BrushingOptions = { +export type BrushPageOffset = { pageX?: number; pageY?: number; };