From b547282d95385d98f1a72136cbf4319bcd3e60e0 Mon Sep 17 00:00:00 2001 From: vezwork Date: Fri, 13 May 2022 11:08:49 -0400 Subject: [PATCH] Remove `DraggableArea` in favor of `useDrag` --- .../KeyframeEditor/Connector.tsx | 2 +- .../CurveEditorPopover/CurveSegmentEditor.tsx | 2 +- .../Right/DopeSheetSelectionView.tsx | 2 +- .../Right/HorizontallyScrollableArea.tsx | 2 +- .../Right/LengthIndicator/LengthIndicator.tsx | 2 +- .../KeyframeEditor/CurveHandle.tsx | 2 +- .../GraphEditorDotNonScalar.tsx | 2 +- .../KeyframeEditor/GraphEditorDotScalar.tsx | 2 +- .../FocusRangeZone/FocusRangeStrip.tsx | 2 +- .../FocusRangeZone/FocusRangeZone.tsx | 4 +- .../RightOverlay/HorizontalScrollbar.tsx | 197 ++++++++++-------- .../studio/src/uiComponents/DraggableArea.tsx | 176 ---------------- .../src/uiComponents/PointerEventsHandler.tsx | 8 +- .../uiComponents/form/BasicNumberInput.tsx | 108 +++++----- theatre/studio/src/uiComponents/useDrag.ts | 6 +- 15 files changed, 179 insertions(+), 338 deletions(-) delete mode 100644 theatre/studio/src/uiComponents/DraggableArea.tsx diff --git a/theatre/studio/src/panels/SequenceEditorPanel/DopeSheet/Right/BasicKeyframedTrack/KeyframeEditor/Connector.tsx b/theatre/studio/src/panels/SequenceEditorPanel/DopeSheet/Right/BasicKeyframedTrack/KeyframeEditor/Connector.tsx index 3f5d1d0..42dc23a 100644 --- a/theatre/studio/src/panels/SequenceEditorPanel/DopeSheet/Right/BasicKeyframedTrack/KeyframeEditor/Connector.tsx +++ b/theatre/studio/src/panels/SequenceEditorPanel/DopeSheet/Right/BasicKeyframedTrack/KeyframeEditor/Connector.tsx @@ -155,7 +155,7 @@ function useDragKeyframe(node: HTMLDivElement | null, props: IProps) { const gestureHandlers = useMemo[1]>(() => { return { debugName: 'useDragKeyframe', - lockCursorTo: 'ew-resize', + lockCSSCursorTo: 'ew-resize', onDragStart(event) { const props = propsRef.current let tempTransaction: CommitOrDiscard | undefined diff --git a/theatre/studio/src/panels/SequenceEditorPanel/DopeSheet/Right/BasicKeyframedTrack/KeyframeEditor/CurveEditorPopover/CurveSegmentEditor.tsx b/theatre/studio/src/panels/SequenceEditorPanel/DopeSheet/Right/BasicKeyframedTrack/KeyframeEditor/CurveEditorPopover/CurveSegmentEditor.tsx index 7c3b4ee..b578873 100644 --- a/theatre/studio/src/panels/SequenceEditorPanel/DopeSheet/Right/BasicKeyframedTrack/KeyframeEditor/CurveEditorPopover/CurveSegmentEditor.tsx +++ b/theatre/studio/src/panels/SequenceEditorPanel/DopeSheet/Right/BasicKeyframedTrack/KeyframeEditor/CurveEditorPopover/CurveSegmentEditor.tsx @@ -253,7 +253,7 @@ function useKeyframeDrag( const handlers = useFreezableMemo[1]>( (setFrozen) => ({ debugName: 'CurveSegmentEditor/useKeyframeDrag', - lockCursorTo: 'move', + lockCSSCursorTo: 'move', onDragStart() { setFrozen(true) return { diff --git a/theatre/studio/src/panels/SequenceEditorPanel/DopeSheet/Right/DopeSheetSelectionView.tsx b/theatre/studio/src/panels/SequenceEditorPanel/DopeSheet/Right/DopeSheetSelectionView.tsx index 4279b63..6afd74f 100644 --- a/theatre/studio/src/panels/SequenceEditorPanel/DopeSheet/Right/DopeSheetSelectionView.tsx +++ b/theatre/studio/src/panels/SequenceEditorPanel/DopeSheet/Right/DopeSheetSelectionView.tsx @@ -60,7 +60,7 @@ function useCaptureSelection( return { debugName: 'DopeSheetSelectionView/useCaptureSelection', dontBlockMouseDown: true, - lockCursorTo: 'cell', + lockCSSCursorTo: 'cell', onDragStart(event) { if (!event.shiftKey || event.target instanceof HTMLInputElement) { return false diff --git a/theatre/studio/src/panels/SequenceEditorPanel/DopeSheet/Right/HorizontallyScrollableArea.tsx b/theatre/studio/src/panels/SequenceEditorPanel/DopeSheet/Right/HorizontallyScrollableArea.tsx index 3fbf174..feed734 100644 --- a/theatre/studio/src/panels/SequenceEditorPanel/DopeSheet/Right/HorizontallyScrollableArea.tsx +++ b/theatre/studio/src/panels/SequenceEditorPanel/DopeSheet/Right/HorizontallyScrollableArea.tsx @@ -265,7 +265,7 @@ function useHandlePanAndZoom( debugName: 'HorizontallyScrollableArea Middle Button Drag', buttons: [1], - lockCursorTo: 'grabbing', + lockCSSCursorTo: 'grabbing', } }, [layoutP]), ) diff --git a/theatre/studio/src/panels/SequenceEditorPanel/DopeSheet/Right/LengthIndicator/LengthIndicator.tsx b/theatre/studio/src/panels/SequenceEditorPanel/DopeSheet/Right/LengthIndicator/LengthIndicator.tsx index cd3b4ca..fb81d37 100644 --- a/theatre/studio/src/panels/SequenceEditorPanel/DopeSheet/Right/LengthIndicator/LengthIndicator.tsx +++ b/theatre/studio/src/panels/SequenceEditorPanel/DopeSheet/Right/LengthIndicator/LengthIndicator.tsx @@ -226,7 +226,7 @@ function useDragBulge( const gestureHandlers = useMemo[1]>(() => { return { debugName: 'LengthIndicator/useDragBulge', - lockCursorTo: 'ew-resize', + lockCSSCursorTo: 'ew-resize', onDragStart(event) { let tempTransaction: CommitOrDiscard | undefined diff --git a/theatre/studio/src/panels/SequenceEditorPanel/GraphEditor/BasicKeyframedTrack/KeyframeEditor/CurveHandle.tsx b/theatre/studio/src/panels/SequenceEditorPanel/GraphEditor/BasicKeyframedTrack/KeyframeEditor/CurveHandle.tsx index 09652ae..07d2614 100644 --- a/theatre/studio/src/panels/SequenceEditorPanel/GraphEditor/BasicKeyframedTrack/KeyframeEditor/CurveHandle.tsx +++ b/theatre/studio/src/panels/SequenceEditorPanel/GraphEditor/BasicKeyframedTrack/KeyframeEditor/CurveHandle.tsx @@ -125,7 +125,7 @@ function useOurDrags(node: SVGCircleElement | null, props: IProps): void { const handlers = useMemo[1]>(() => { return { debugName: 'CurveHandler/useOurDrags', - lockCursorTo: 'move', + lockCSSCursorTo: 'move', onDragStart() { let tempTransaction: CommitOrDiscard | undefined diff --git a/theatre/studio/src/panels/SequenceEditorPanel/GraphEditor/BasicKeyframedTrack/KeyframeEditor/GraphEditorDotNonScalar.tsx b/theatre/studio/src/panels/SequenceEditorPanel/GraphEditor/BasicKeyframedTrack/KeyframeEditor/GraphEditorDotNonScalar.tsx index 5ffdbe8..736366c 100644 --- a/theatre/studio/src/panels/SequenceEditorPanel/GraphEditor/BasicKeyframedTrack/KeyframeEditor/GraphEditorDotNonScalar.tsx +++ b/theatre/studio/src/panels/SequenceEditorPanel/GraphEditor/BasicKeyframedTrack/KeyframeEditor/GraphEditorDotNonScalar.tsx @@ -107,7 +107,7 @@ function useDragKeyframe( const gestureHandlers = useMemo[1]>(() => { return { debugName: 'GraphEditorDotNonScalar/useDragKeyframe', - lockCursorTo: 'ew-resize', + lockCSSCursorTo: 'ew-resize', onDragStart(event) { setIsDragging(true) const propsAtStartOfDrag = propsRef.current diff --git a/theatre/studio/src/panels/SequenceEditorPanel/GraphEditor/BasicKeyframedTrack/KeyframeEditor/GraphEditorDotScalar.tsx b/theatre/studio/src/panels/SequenceEditorPanel/GraphEditor/BasicKeyframedTrack/KeyframeEditor/GraphEditorDotScalar.tsx index 996376e..f685672 100644 --- a/theatre/studio/src/panels/SequenceEditorPanel/GraphEditor/BasicKeyframedTrack/KeyframeEditor/GraphEditorDotScalar.tsx +++ b/theatre/studio/src/panels/SequenceEditorPanel/GraphEditor/BasicKeyframedTrack/KeyframeEditor/GraphEditorDotScalar.tsx @@ -108,7 +108,7 @@ function useDragKeyframe( const gestureHandlers = useMemo[1]>(() => { return { debugName: 'GraphEditorDotScalar/useDragKeyframe', - lockCursorTo: 'move', + lockCSSCursorTo: 'move', onDragStart(event) { setIsDragging(true) const keepSpeeds = !!event.altKey diff --git a/theatre/studio/src/panels/SequenceEditorPanel/RightOverlay/FocusRangeZone/FocusRangeStrip.tsx b/theatre/studio/src/panels/SequenceEditorPanel/RightOverlay/FocusRangeZone/FocusRangeStrip.tsx index 9276419..e089751 100644 --- a/theatre/studio/src/panels/SequenceEditorPanel/RightOverlay/FocusRangeZone/FocusRangeStrip.tsx +++ b/theatre/studio/src/panels/SequenceEditorPanel/RightOverlay/FocusRangeZone/FocusRangeStrip.tsx @@ -246,7 +246,7 @@ const FocusRangeStrip: React.FC<{ } }, - lockCursorTo: 'grabbing', + lockCSSCursorTo: 'grabbing', } }, [sheet, scaledSpaceToUnitSpace]) diff --git a/theatre/studio/src/panels/SequenceEditorPanel/RightOverlay/FocusRangeZone/FocusRangeZone.tsx b/theatre/studio/src/panels/SequenceEditorPanel/RightOverlay/FocusRangeZone/FocusRangeZone.tsx index ab9197b..cccc48c 100644 --- a/theatre/studio/src/panels/SequenceEditorPanel/RightOverlay/FocusRangeZone/FocusRangeZone.tsx +++ b/theatre/studio/src/panels/SequenceEditorPanel/RightOverlay/FocusRangeZone/FocusRangeZone.tsx @@ -169,7 +169,7 @@ function usePanelDragZoneGestureHandlers( } }, - lockCursorTo: 'ew-resize', + lockCSSCursorTo: 'ew-resize', } } @@ -214,7 +214,7 @@ function usePanelDragZoneGestureHandlers( }, } }, - lockCursorTo: 'move', + lockCSSCursorTo: 'move', } } diff --git a/theatre/studio/src/panels/SequenceEditorPanel/RightOverlay/HorizontalScrollbar.tsx b/theatre/studio/src/panels/SequenceEditorPanel/RightOverlay/HorizontalScrollbar.tsx index af9424a..6e2981f 100644 --- a/theatre/studio/src/panels/SequenceEditorPanel/RightOverlay/HorizontalScrollbar.tsx +++ b/theatre/studio/src/panels/SequenceEditorPanel/RightOverlay/HorizontalScrollbar.tsx @@ -1,5 +1,4 @@ import type {SequenceEditorPanelLayout} from '@theatre/studio/panels/SequenceEditorPanel/layout/layout' -import DraggableArea from '@theatre/studio/uiComponents/DraggableArea' import {useVal} from '@theatre/react' import type {IRange} from '@theatre/shared/utils/types' import type {Pointer} from '@theatre/dataverse' @@ -11,6 +10,8 @@ import styled from 'styled-components' import {zIndexes} from '@theatre/studio/panels/SequenceEditorPanel/SequenceEditorPanel' import {includeLockFrameStampAttrs} from '@theatre/studio/panels/SequenceEditorPanel/FrameStampPositionProvider' import {pointerEventsAutoInNormalMode} from '@theatre/studio/css' +import useRefAndState from '@theatre/studio/utils/useRefAndState' +import useDrag from '@theatre/studio/uiComponents/useDrag' const Container = styled.div` --threadHeight: 6px; @@ -174,117 +175,143 @@ const HorizontalScrollbar: React.FC<{ } const self = { - onDragStart() { + onRangeDragStart() { noteValuesBeforeDrag() - }, - onDragEnd() { - setBeingDragged('nothing') - }, - dragRange: (dx: number) => { - setBeingDragged('both') - const deltaPosInUnitSpace = deltaXToDeltaPos(dx) + return { + onDrag(dx: number) { + setBeingDragged('both') + const deltaPosInUnitSpace = deltaXToDeltaPos(dx) - const newRange: IRange = mapValues( - valuesBeforeDrag.clippedSpaceRange, - (p) => p + deltaPosInUnitSpace, - ) + const newRange: IRange = mapValues( + valuesBeforeDrag.clippedSpaceRange, + (p) => p + deltaPosInUnitSpace, + ) - val(layoutP.clippedSpace.setRange)(newRange) + val(layoutP.clippedSpace.setRange)(newRange) + }, + onDragEnd() { + setBeingDragged('nothing') + }, + } }, - dragRangeStart: (dx: number) => { - setBeingDragged('start') + onRangeStartDragStart() { + noteValuesBeforeDrag() + return { + onDrag(dx: number) { + setBeingDragged('start') - const deltaPosInUnitSpace = deltaXToDeltaPos(dx) + const deltaPosInUnitSpace = deltaXToDeltaPos(dx) - const newRange: IRange = { - start: valuesBeforeDrag.clippedSpaceRange.start + deltaPosInUnitSpace, - end: valuesBeforeDrag.clippedSpaceRange.end, + const newRange: IRange = { + start: + valuesBeforeDrag.clippedSpaceRange.start + deltaPosInUnitSpace, + end: valuesBeforeDrag.clippedSpaceRange.end, + } + + if (newRange.start > newRange.end - 1) { + newRange.start = newRange.end - 1 + } + + if (newRange.start <= 0) { + newRange.start = 0 + } + + val(layoutP.clippedSpace.setRange)(newRange) + }, + onDragEnd() { + setBeingDragged('nothing') + }, } - - if (newRange.start > newRange.end - 1) { - newRange.start = newRange.end - 1 - } - - if (newRange.start <= 0) { - newRange.start = 0 - } - - val(layoutP.clippedSpace.setRange)(newRange) }, - dragRangeEnd: (dx: number) => { - setBeingDragged('end') + onRangeEndDragStart() { + noteValuesBeforeDrag() + return { + onDrag(dx: number) { + setBeingDragged('end') - const deltaPosInUnitSpace = deltaXToDeltaPos(dx) + const deltaPosInUnitSpace = deltaXToDeltaPos(dx) - const newRange: IRange = { - start: valuesBeforeDrag.clippedSpaceRange.start, - end: valuesBeforeDrag.clippedSpaceRange.end + deltaPosInUnitSpace, + const newRange: IRange = { + start: valuesBeforeDrag.clippedSpaceRange.start, + end: valuesBeforeDrag.clippedSpaceRange.end + deltaPosInUnitSpace, + } + + if (newRange.end < newRange.start + 1) { + newRange.end = newRange.start + 1 + } + + if (newRange.end >= valuesBeforeDrag.assumedLengthOfSequence) { + newRange.end = valuesBeforeDrag.assumedLengthOfSequence + } + + val(layoutP.clippedSpace.setRange)(newRange) + }, + onDragEnd() { + setBeingDragged('nothing') + }, } - - if (newRange.end < newRange.start + 1) { - newRange.end = newRange.start + 1 - } - - if (newRange.end >= valuesBeforeDrag.assumedLengthOfSequence) { - newRange.end = valuesBeforeDrag.assumedLengthOfSequence - } - - val(layoutP.clippedSpace.setRange)(newRange) }, } return self }, [layoutP, relevantValuesD]) + const [refRangeDrag, nodeRangeDrag] = useRefAndState( + null, + ) + useDrag(nodeRangeDrag, { + debugName: 'HorizontalScrollbar/onRangeDrag', + onDragStart: handles.onRangeDragStart, + lockCSSCursorTo: 'ew-resize', + }) + + const [refRangeStartDrag, nodeRangeStartDrag] = + useRefAndState(null) + useDrag(nodeRangeStartDrag, { + debugName: 'HorizontalScrollbar/onRangeStartDrag', + onDragStart: handles.onRangeStartDragStart, + lockCSSCursorTo: 'w-resize', + }) + + const [refRangeEndDrag, nodeRangeEndDrag] = + useRefAndState(null) + useDrag(nodeRangeEndDrag, { + debugName: 'HorizontalScrollbar/onRangeEndDrag', + onDragStart: handles.onRangeEndDragStart, + lockCSSCursorTo: 'e-resize', + }) + return ( - + - - - + {unitPosToHumanReadablePos(clippedSpaceRange.start)} + + + - - - {unitPosToHumanReadablePos(clippedSpaceRange.start)} - - - - - - - {unitPosToHumanReadablePos(clippedSpaceRange.end)} - - - + + {unitPosToHumanReadablePos(clippedSpaceRange.end)} + + ) diff --git a/theatre/studio/src/uiComponents/DraggableArea.tsx b/theatre/studio/src/uiComponents/DraggableArea.tsx deleted file mode 100644 index 8c3fb29..0000000 --- a/theatre/studio/src/uiComponents/DraggableArea.tsx +++ /dev/null @@ -1,176 +0,0 @@ -import {voidFn} from '@theatre/shared/utils' -import React from 'react' - -function createCursorLock(cursor: string) { - const el = document.createElement('div') - el.style.cssText = ` - position: fixed; - top: 0; - right: 0; - bottom: 0; - left: 0; - z-index: 9999999;` - - el.style.cursor = cursor - document.body.appendChild(el) - const relinquish = () => { - document.body.removeChild(el) - } - - return relinquish -} - -type IProps = { - children: React.ReactElement - onDragStart?: ( - event: React.MouseEvent, - ) => void | false - onDragEnd?: (dragHappened: boolean) => void - onDrag: (dx: number, dy: number, event: MouseEvent) => void - enabled?: boolean - shouldReturnMovement?: boolean - dontBlockMouseDown?: boolean - lockCursorTo?: string -} - -type State = { - dragHappened: boolean - startPos: { - x: number - y: number - } -} - -/** - * TODO low-hanging-fruit replace all DraggableArea instances with `react-use-gesture` - */ -class DraggableArea extends React.PureComponent { - s: State - mode: 'dragStartCalled' | 'dragging' | 'notDragging' = 'notDragging' - getDeltas: (e: MouseEvent) => [number, number] - relinquishCursorLock: () => void = voidFn - - constructor(props: IProps) { - super(props) - this.s = { - dragHappened: false, - startPos: { - x: 0, - y: 0, - }, - } - if (props.shouldReturnMovement) { - this.getDeltas = this.getMovements - } else { - this.getDeltas = this.getDistances - } - } - - render() { - const shouldRegisterEvents = this.props.enabled !== false - - const children = shouldRegisterEvents - ? React.cloneElement(this.props.children, { - onMouseDown: this.dragStartHandler, - onClickCapture: this.disableUnwantedClick, - }) - : this.props.children - - return children - } - - addDragListeners() { - document.addEventListener('mousemove', this.dragHandler) - document.addEventListener('mouseup', this.dragEndHandler) - } - - removeDragListeners() { - document.removeEventListener('mousemove', this.dragHandler) - document.removeEventListener('mouseup', this.dragEndHandler) - } - - disableUnwantedClick = (event: MouseEvent) => { - if (this.s.dragHappened) { - if (!this.props.dontBlockMouseDown && this.mode !== 'notDragging') { - event.stopPropagation() - event.preventDefault() - } - this.s.dragHappened = false - } - } - - UNSAFE_componentWillReceiveProps(newProps: IProps) { - if ( - newProps.lockCursorTo !== this.props.lockCursorTo && - this.s.dragHappened - ) { - this.relinquishCursorLock() - this.relinquishCursorLock = voidFn - if (newProps.lockCursorTo) { - this.relinquishCursorLock = createCursorLock(newProps.lockCursorTo) - } - } - } - - dragStartHandler = (event: React.MouseEvent) => { - if (event.button !== 0) return - const resultOfStart = - this.props.onDragStart && this.props.onDragStart(event) - - if (resultOfStart === false) return - - if (!this.props.dontBlockMouseDown) { - event.stopPropagation() - event.preventDefault() - } - - this.mode = 'dragStartCalled' - - const {screenX, screenY} = event - this.s.startPos = {x: screenX, y: screenY} - this.s.dragHappened = false - - this.addDragListeners() - } - - dragEndHandler = () => { - this.removeDragListeners() - this.mode = 'notDragging' - - this.props.onDragEnd && this.props.onDragEnd(this.s.dragHappened) - this.relinquishCursorLock() - this.relinquishCursorLock = voidFn - } - - dragHandler = (event: MouseEvent) => { - if (!this.s.dragHappened && this.props.lockCursorTo) { - this.relinquishCursorLock = createCursorLock(this.props.lockCursorTo) - } - if (!this.s.dragHappened) this.s.dragHappened = true - this.mode = 'dragging' - - const deltas = this.getDeltas(event) - this.props.onDrag(deltas[0], deltas[1], event) - } - - getDistances(event: MouseEvent): [number, number] { - const {startPos} = this.s - return [event.screenX - startPos.x, event.screenY - startPos.y] - } - - getMovements(event: MouseEvent): [number, number] { - return [event.movementX, event.movementY] - } - - componentWillUnmount() { - if (this.mode !== 'notDragging') { - this.relinquishCursorLock() - this.relinquishCursorLock = voidFn - this.removeDragListeners() - this.props.onDragEnd && this.props.onDragEnd(this.mode === 'dragging') - this.mode = 'notDragging' - } - } -} - -export default DraggableArea diff --git a/theatre/studio/src/uiComponents/PointerEventsHandler.tsx b/theatre/studio/src/uiComponents/PointerEventsHandler.tsx index 6a4f20c..bcc1673 100644 --- a/theatre/studio/src/uiComponents/PointerEventsHandler.tsx +++ b/theatre/studio/src/uiComponents/PointerEventsHandler.tsx @@ -39,10 +39,10 @@ const CursorOverride = styled.div` ` type Context = { - getLock: (className: string, cursor: string) => () => void + getLock: (className: string, cursor?: string) => () => void } -type Lock = {className: string; cursor: string} +type Lock = {className: string; cursor?: string} const context = createContext({} as $IntentionalAny) @@ -51,7 +51,7 @@ const PointerEventsHandler: React.FC<{ }> = (props) => { const [locks, setLocks] = useState([]) const contextValue = useMemo(() => { - const getLock = (className: string, cursor: string) => { + const getLock = (className: string, cursor?: string) => { const lock = {className, cursor} setLocks((s) => [...s, lock]) const unlock = () => { @@ -104,7 +104,7 @@ export const useCssCursorLock = ( enabled: boolean, className: string, /** e.g. `"ew"`, `"help"`, `"pointer"`, `"text"`, etc */ - cursor: string, + cursor?: string, ) => { const ctx = useContext(context) useLayoutEffect(() => { diff --git a/theatre/studio/src/uiComponents/form/BasicNumberInput.tsx b/theatre/studio/src/uiComponents/form/BasicNumberInput.tsx index d7ab8e6..3e4cf3a 100644 --- a/theatre/studio/src/uiComponents/form/BasicNumberInput.tsx +++ b/theatre/studio/src/uiComponents/form/BasicNumberInput.tsx @@ -2,10 +2,10 @@ import {clamp, isInteger, round} from 'lodash-es' import type {MutableRefObject} from 'react' import React, {useMemo, useRef} from 'react' import styled from 'styled-components' -import DraggableArea from '@theatre/studio/uiComponents/DraggableArea' import mergeRefs from 'react-merge-refs' import useRefAndState from '@theatre/studio/utils/useRefAndState' import useOnClickOutside from '@theatre/studio/uiComponents/useOnClickOutside' +import useDrag from '@theatre/studio/uiComponents/useDrag' const Container = styled.div` height: 100%; @@ -71,9 +71,9 @@ const FillIndicator = styled.div` } ` -function isFiniteFloat(s: string) { - return isFinite(parseFloat(s)) -} +const DragWrap = styled.div` + display: contents; +` type IState_NoFocus = { mode: 'noFocus' @@ -87,8 +87,6 @@ type IState_EditingViaKeyboard = { type IState_Dragging = { mode: 'dragging' - valueBeforeDragging: number - currentDraggingValue: number } type IState = IState_NoFocus | IState_EditingViaKeyboard | IState_Dragging @@ -223,55 +221,50 @@ const BasicNumberInput: React.FC<{ stateRef.current = { mode: 'dragging', - valueBeforeDragging: curValue, - currentDraggingValue: curValue, } + let valueBeforeDragging = curValue + let valueDuringDragging = curValue + bodyCursorBeforeDrag.current = document.body.style.cursor - } - const onDragEnd = (happened: boolean) => { - if (!happened) { - propsRef.current.discardTemporaryValue() - stateRef.current = {mode: 'noFocus'} + return { + // note: we use mx because we need to constrain the `valueDuringDragging` + // and dx will keep accumulating past any constraints + onDrag(_dx: number, _dy: number, _e: MouseEvent, mx: number) { + const newValue = + valueDuringDragging + + propsA.nudge({ + deltaX: mx, + deltaFraction: mx / inputWidth, + magnitude: 1, + }) - inputRef.current!.focus() - inputRef.current!.setSelectionRange(0, 100) - } else { - const curState = stateRef.current as IState_Dragging - const value = curState.currentDraggingValue - if (curState.valueBeforeDragging === value) { - propsRef.current.discardTemporaryValue() - } else { - propsRef.current.permanentlySetValue(value) - } - stateRef.current = {mode: 'noFocus'} + valueDuringDragging = propsA.range + ? clamp(newValue, propsA.range[0], propsA.range[1]) + : newValue + + propsRef.current.temporarilySetValue(valueDuringDragging) + }, + onDragEnd(happened: boolean) { + if (!happened) { + propsRef.current.discardTemporaryValue() + stateRef.current = {mode: 'noFocus'} + + inputRef.current!.focus() + inputRef.current!.setSelectionRange(0, 100) + } else { + if (valueBeforeDragging === valueDuringDragging) { + propsRef.current.discardTemporaryValue() + } else { + propsRef.current.permanentlySetValue(valueDuringDragging) + } + stateRef.current = {mode: 'noFocus'} + } + }, } } - const onDrag = (deltaX: number, _dy: number) => { - const curState = stateRef.current as IState_Dragging - - let newValue = - curState.valueBeforeDragging + - propsA.nudge({ - deltaX, - deltaFraction: deltaX / inputWidth, - magnitude: 1, - }) - - if (propsA.range) { - newValue = clamp(newValue, propsA.range[0], propsA.range[1]) - } - - stateRef.current = { - ...curState, - currentDraggingValue: newValue, - } - - propsRef.current.temporarilySetValue(newValue) - } - return { inputChange, onBlur, @@ -279,8 +272,6 @@ const BasicNumberInput: React.FC<{ onInputKeyDown, onClick, onFocus, - onDragEnd, - onDrag, } }, []) @@ -329,18 +320,17 @@ const BasicNumberInput: React.FC<{ /> ) : null + const [refDrag, nodeDrag] = useRefAndState(null) + useDrag(nodeDrag, { + debugName: 'form/BasicNumberInput', + onDragStart: callbacks.transitionToDraggingMode, + lockCSSCursorTo: 'ew-resize', + disabled: stateRef.current.mode === 'editingViaKeyboard', + }) + return ( - - {theInput} - + {theInput} {fillIndicator} ) diff --git a/theatre/studio/src/uiComponents/useDrag.ts b/theatre/studio/src/uiComponents/useDrag.ts index e9fc258..f0031c4 100644 --- a/theatre/studio/src/uiComponents/useDrag.ts +++ b/theatre/studio/src/uiComponents/useDrag.ts @@ -44,7 +44,7 @@ export type UseDragOpts = { /** * The css cursor property during the gesture will be locked to this value */ - lockCursorTo?: string + lockCSSCursorTo?: string /** * Called at the start of the gesture. Mind you, that this would be called, even * if the user is just clicking (and not dragging). However, if the gesture turns @@ -88,9 +88,9 @@ export default function useDrag( >('notDragging') useCssCursorLock( - mode === 'dragging' && typeof opts.lockCursorTo === 'string', + mode === 'dragging' && typeof opts.lockCSSCursorTo === 'string', 'dragging', - opts.lockCursorTo!, + opts.lockCSSCursorTo, ) const {capturePointer} = usePointerCapturing(`useDrag for ${opts.debugName}`)