Remove DraggableArea
in favor of useDrag
This commit is contained in:
parent
386a8fafac
commit
b547282d95
15 changed files with 179 additions and 338 deletions
|
@ -155,7 +155,7 @@ function useDragKeyframe(node: HTMLDivElement | null, props: IProps) {
|
||||||
const gestureHandlers = useMemo<Parameters<typeof useDrag>[1]>(() => {
|
const gestureHandlers = useMemo<Parameters<typeof useDrag>[1]>(() => {
|
||||||
return {
|
return {
|
||||||
debugName: 'useDragKeyframe',
|
debugName: 'useDragKeyframe',
|
||||||
lockCursorTo: 'ew-resize',
|
lockCSSCursorTo: 'ew-resize',
|
||||||
onDragStart(event) {
|
onDragStart(event) {
|
||||||
const props = propsRef.current
|
const props = propsRef.current
|
||||||
let tempTransaction: CommitOrDiscard | undefined
|
let tempTransaction: CommitOrDiscard | undefined
|
||||||
|
|
|
@ -253,7 +253,7 @@ function useKeyframeDrag(
|
||||||
const handlers = useFreezableMemo<Parameters<typeof useDrag>[1]>(
|
const handlers = useFreezableMemo<Parameters<typeof useDrag>[1]>(
|
||||||
(setFrozen) => ({
|
(setFrozen) => ({
|
||||||
debugName: 'CurveSegmentEditor/useKeyframeDrag',
|
debugName: 'CurveSegmentEditor/useKeyframeDrag',
|
||||||
lockCursorTo: 'move',
|
lockCSSCursorTo: 'move',
|
||||||
onDragStart() {
|
onDragStart() {
|
||||||
setFrozen(true)
|
setFrozen(true)
|
||||||
return {
|
return {
|
||||||
|
|
|
@ -60,7 +60,7 @@ function useCaptureSelection(
|
||||||
return {
|
return {
|
||||||
debugName: 'DopeSheetSelectionView/useCaptureSelection',
|
debugName: 'DopeSheetSelectionView/useCaptureSelection',
|
||||||
dontBlockMouseDown: true,
|
dontBlockMouseDown: true,
|
||||||
lockCursorTo: 'cell',
|
lockCSSCursorTo: 'cell',
|
||||||
onDragStart(event) {
|
onDragStart(event) {
|
||||||
if (!event.shiftKey || event.target instanceof HTMLInputElement) {
|
if (!event.shiftKey || event.target instanceof HTMLInputElement) {
|
||||||
return false
|
return false
|
||||||
|
|
|
@ -265,7 +265,7 @@ function useHandlePanAndZoom(
|
||||||
|
|
||||||
debugName: 'HorizontallyScrollableArea Middle Button Drag',
|
debugName: 'HorizontallyScrollableArea Middle Button Drag',
|
||||||
buttons: [1],
|
buttons: [1],
|
||||||
lockCursorTo: 'grabbing',
|
lockCSSCursorTo: 'grabbing',
|
||||||
}
|
}
|
||||||
}, [layoutP]),
|
}, [layoutP]),
|
||||||
)
|
)
|
||||||
|
|
|
@ -226,7 +226,7 @@ function useDragBulge(
|
||||||
const gestureHandlers = useMemo<Parameters<typeof useDrag>[1]>(() => {
|
const gestureHandlers = useMemo<Parameters<typeof useDrag>[1]>(() => {
|
||||||
return {
|
return {
|
||||||
debugName: 'LengthIndicator/useDragBulge',
|
debugName: 'LengthIndicator/useDragBulge',
|
||||||
lockCursorTo: 'ew-resize',
|
lockCSSCursorTo: 'ew-resize',
|
||||||
onDragStart(event) {
|
onDragStart(event) {
|
||||||
let tempTransaction: CommitOrDiscard | undefined
|
let tempTransaction: CommitOrDiscard | undefined
|
||||||
|
|
||||||
|
|
|
@ -125,7 +125,7 @@ function useOurDrags(node: SVGCircleElement | null, props: IProps): void {
|
||||||
const handlers = useMemo<Parameters<typeof useDrag>[1]>(() => {
|
const handlers = useMemo<Parameters<typeof useDrag>[1]>(() => {
|
||||||
return {
|
return {
|
||||||
debugName: 'CurveHandler/useOurDrags',
|
debugName: 'CurveHandler/useOurDrags',
|
||||||
lockCursorTo: 'move',
|
lockCSSCursorTo: 'move',
|
||||||
onDragStart() {
|
onDragStart() {
|
||||||
let tempTransaction: CommitOrDiscard | undefined
|
let tempTransaction: CommitOrDiscard | undefined
|
||||||
|
|
||||||
|
|
|
@ -107,7 +107,7 @@ function useDragKeyframe(
|
||||||
const gestureHandlers = useMemo<Parameters<typeof useDrag>[1]>(() => {
|
const gestureHandlers = useMemo<Parameters<typeof useDrag>[1]>(() => {
|
||||||
return {
|
return {
|
||||||
debugName: 'GraphEditorDotNonScalar/useDragKeyframe',
|
debugName: 'GraphEditorDotNonScalar/useDragKeyframe',
|
||||||
lockCursorTo: 'ew-resize',
|
lockCSSCursorTo: 'ew-resize',
|
||||||
onDragStart(event) {
|
onDragStart(event) {
|
||||||
setIsDragging(true)
|
setIsDragging(true)
|
||||||
const propsAtStartOfDrag = propsRef.current
|
const propsAtStartOfDrag = propsRef.current
|
||||||
|
|
|
@ -108,7 +108,7 @@ function useDragKeyframe(
|
||||||
const gestureHandlers = useMemo<Parameters<typeof useDrag>[1]>(() => {
|
const gestureHandlers = useMemo<Parameters<typeof useDrag>[1]>(() => {
|
||||||
return {
|
return {
|
||||||
debugName: 'GraphEditorDotScalar/useDragKeyframe',
|
debugName: 'GraphEditorDotScalar/useDragKeyframe',
|
||||||
lockCursorTo: 'move',
|
lockCSSCursorTo: 'move',
|
||||||
onDragStart(event) {
|
onDragStart(event) {
|
||||||
setIsDragging(true)
|
setIsDragging(true)
|
||||||
const keepSpeeds = !!event.altKey
|
const keepSpeeds = !!event.altKey
|
||||||
|
|
|
@ -246,7 +246,7 @@ const FocusRangeStrip: React.FC<{
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
lockCursorTo: 'grabbing',
|
lockCSSCursorTo: 'grabbing',
|
||||||
}
|
}
|
||||||
}, [sheet, scaledSpaceToUnitSpace])
|
}, [sheet, scaledSpaceToUnitSpace])
|
||||||
|
|
||||||
|
|
|
@ -169,7 +169,7 @@ function usePanelDragZoneGestureHandlers(
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
lockCursorTo: 'ew-resize',
|
lockCSSCursorTo: 'ew-resize',
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -214,7 +214,7 @@ function usePanelDragZoneGestureHandlers(
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
lockCursorTo: 'move',
|
lockCSSCursorTo: 'move',
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
import type {SequenceEditorPanelLayout} from '@theatre/studio/panels/SequenceEditorPanel/layout/layout'
|
import type {SequenceEditorPanelLayout} from '@theatre/studio/panels/SequenceEditorPanel/layout/layout'
|
||||||
import DraggableArea from '@theatre/studio/uiComponents/DraggableArea'
|
|
||||||
import {useVal} from '@theatre/react'
|
import {useVal} from '@theatre/react'
|
||||||
import type {IRange} from '@theatre/shared/utils/types'
|
import type {IRange} from '@theatre/shared/utils/types'
|
||||||
import type {Pointer} from '@theatre/dataverse'
|
import type {Pointer} from '@theatre/dataverse'
|
||||||
|
@ -11,6 +10,8 @@ import styled from 'styled-components'
|
||||||
import {zIndexes} from '@theatre/studio/panels/SequenceEditorPanel/SequenceEditorPanel'
|
import {zIndexes} from '@theatre/studio/panels/SequenceEditorPanel/SequenceEditorPanel'
|
||||||
import {includeLockFrameStampAttrs} from '@theatre/studio/panels/SequenceEditorPanel/FrameStampPositionProvider'
|
import {includeLockFrameStampAttrs} from '@theatre/studio/panels/SequenceEditorPanel/FrameStampPositionProvider'
|
||||||
import {pointerEventsAutoInNormalMode} from '@theatre/studio/css'
|
import {pointerEventsAutoInNormalMode} from '@theatre/studio/css'
|
||||||
|
import useRefAndState from '@theatre/studio/utils/useRefAndState'
|
||||||
|
import useDrag from '@theatre/studio/uiComponents/useDrag'
|
||||||
|
|
||||||
const Container = styled.div`
|
const Container = styled.div`
|
||||||
--threadHeight: 6px;
|
--threadHeight: 6px;
|
||||||
|
@ -174,13 +175,10 @@ const HorizontalScrollbar: React.FC<{
|
||||||
}
|
}
|
||||||
|
|
||||||
const self = {
|
const self = {
|
||||||
onDragStart() {
|
onRangeDragStart() {
|
||||||
noteValuesBeforeDrag()
|
noteValuesBeforeDrag()
|
||||||
},
|
return {
|
||||||
onDragEnd() {
|
onDrag(dx: number) {
|
||||||
setBeingDragged('nothing')
|
|
||||||
},
|
|
||||||
dragRange: (dx: number) => {
|
|
||||||
setBeingDragged('both')
|
setBeingDragged('both')
|
||||||
const deltaPosInUnitSpace = deltaXToDeltaPos(dx)
|
const deltaPosInUnitSpace = deltaXToDeltaPos(dx)
|
||||||
|
|
||||||
|
@ -191,14 +189,23 @@ const HorizontalScrollbar: React.FC<{
|
||||||
|
|
||||||
val(layoutP.clippedSpace.setRange)(newRange)
|
val(layoutP.clippedSpace.setRange)(newRange)
|
||||||
},
|
},
|
||||||
|
onDragEnd() {
|
||||||
|
setBeingDragged('nothing')
|
||||||
|
},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
dragRangeStart: (dx: number) => {
|
onRangeStartDragStart() {
|
||||||
|
noteValuesBeforeDrag()
|
||||||
|
return {
|
||||||
|
onDrag(dx: number) {
|
||||||
setBeingDragged('start')
|
setBeingDragged('start')
|
||||||
|
|
||||||
const deltaPosInUnitSpace = deltaXToDeltaPos(dx)
|
const deltaPosInUnitSpace = deltaXToDeltaPos(dx)
|
||||||
|
|
||||||
const newRange: IRange = {
|
const newRange: IRange = {
|
||||||
start: valuesBeforeDrag.clippedSpaceRange.start + deltaPosInUnitSpace,
|
start:
|
||||||
|
valuesBeforeDrag.clippedSpaceRange.start + deltaPosInUnitSpace,
|
||||||
end: valuesBeforeDrag.clippedSpaceRange.end,
|
end: valuesBeforeDrag.clippedSpaceRange.end,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -212,8 +219,16 @@ const HorizontalScrollbar: React.FC<{
|
||||||
|
|
||||||
val(layoutP.clippedSpace.setRange)(newRange)
|
val(layoutP.clippedSpace.setRange)(newRange)
|
||||||
},
|
},
|
||||||
|
onDragEnd() {
|
||||||
|
setBeingDragged('nothing')
|
||||||
|
},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
dragRangeEnd: (dx: number) => {
|
onRangeEndDragStart() {
|
||||||
|
noteValuesBeforeDrag()
|
||||||
|
return {
|
||||||
|
onDrag(dx: number) {
|
||||||
setBeingDragged('end')
|
setBeingDragged('end')
|
||||||
|
|
||||||
const deltaPosInUnitSpace = deltaXToDeltaPos(dx)
|
const deltaPosInUnitSpace = deltaXToDeltaPos(dx)
|
||||||
|
@ -233,58 +248,70 @@ const HorizontalScrollbar: React.FC<{
|
||||||
|
|
||||||
val(layoutP.clippedSpace.setRange)(newRange)
|
val(layoutP.clippedSpace.setRange)(newRange)
|
||||||
},
|
},
|
||||||
|
onDragEnd() {
|
||||||
|
setBeingDragged('nothing')
|
||||||
|
},
|
||||||
|
}
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
return self
|
return self
|
||||||
}, [layoutP, relevantValuesD])
|
}, [layoutP, relevantValuesD])
|
||||||
|
|
||||||
|
const [refRangeDrag, nodeRangeDrag] = useRefAndState<HTMLDivElement | null>(
|
||||||
|
null,
|
||||||
|
)
|
||||||
|
useDrag(nodeRangeDrag, {
|
||||||
|
debugName: 'HorizontalScrollbar/onRangeDrag',
|
||||||
|
onDragStart: handles.onRangeDragStart,
|
||||||
|
lockCSSCursorTo: 'ew-resize',
|
||||||
|
})
|
||||||
|
|
||||||
|
const [refRangeStartDrag, nodeRangeStartDrag] =
|
||||||
|
useRefAndState<HTMLDivElement | null>(null)
|
||||||
|
useDrag(nodeRangeStartDrag, {
|
||||||
|
debugName: 'HorizontalScrollbar/onRangeStartDrag',
|
||||||
|
onDragStart: handles.onRangeStartDragStart,
|
||||||
|
lockCSSCursorTo: 'w-resize',
|
||||||
|
})
|
||||||
|
|
||||||
|
const [refRangeEndDrag, nodeRangeEndDrag] =
|
||||||
|
useRefAndState<HTMLDivElement | null>(null)
|
||||||
|
useDrag(nodeRangeEndDrag, {
|
||||||
|
debugName: 'HorizontalScrollbar/onRangeEndDrag',
|
||||||
|
onDragStart: handles.onRangeEndDragStart,
|
||||||
|
lockCSSCursorTo: 'e-resize',
|
||||||
|
})
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Container
|
<Container
|
||||||
style={{bottom: bottom + 8 + 'px'}}
|
style={{bottom: bottom + 8 + 'px'}}
|
||||||
{...includeLockFrameStampAttrs('hide')}
|
{...includeLockFrameStampAttrs('hide')}
|
||||||
>
|
>
|
||||||
<TimeThread>
|
<TimeThread>
|
||||||
<DraggableArea
|
|
||||||
onDragStart={handles.onDragStart}
|
|
||||||
onDragEnd={handles.onDragEnd}
|
|
||||||
onDrag={handles.dragRange}
|
|
||||||
lockCursorTo="ew-resize"
|
|
||||||
>
|
|
||||||
<RangeBar
|
<RangeBar
|
||||||
|
ref={refRangeDrag}
|
||||||
style={{
|
style={{
|
||||||
width: `${rangeEndX - rangeStartX}px`,
|
width: `${rangeEndX - rangeStartX}px`,
|
||||||
transform: `translate3d(${rangeStartX}px, 0, 0)`,
|
transform: `translate3d(${rangeStartX}px, 0, 0)`,
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</DraggableArea>
|
|
||||||
<DraggableArea
|
|
||||||
onDragStart={handles.onDragStart}
|
|
||||||
onDrag={handles.dragRangeStart}
|
|
||||||
lockCursorTo="w-resize"
|
|
||||||
>
|
|
||||||
<RangeStartHandle
|
<RangeStartHandle
|
||||||
|
ref={refRangeStartDrag}
|
||||||
style={{transform: `translate3d(${rangeStartX}px, 0, 0)`}}
|
style={{transform: `translate3d(${rangeStartX}px, 0, 0)`}}
|
||||||
>
|
>
|
||||||
<Tooltip
|
<Tooltip active={beingDragged === 'both' || beingDragged === 'start'}>
|
||||||
active={beingDragged === 'both' || beingDragged === 'start'}
|
|
||||||
>
|
|
||||||
{unitPosToHumanReadablePos(clippedSpaceRange.start)}
|
{unitPosToHumanReadablePos(clippedSpaceRange.start)}
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
</RangeStartHandle>
|
</RangeStartHandle>
|
||||||
</DraggableArea>
|
|
||||||
<DraggableArea
|
|
||||||
onDragStart={handles.onDragStart}
|
|
||||||
onDrag={handles.dragRangeEnd}
|
|
||||||
lockCursorTo="e-resize"
|
|
||||||
>
|
|
||||||
<RangeEndHandle
|
<RangeEndHandle
|
||||||
|
ref={refRangeEndDrag}
|
||||||
style={{transform: `translate3d(${rangeEndX}px, 0, 0)`}}
|
style={{transform: `translate3d(${rangeEndX}px, 0, 0)`}}
|
||||||
>
|
>
|
||||||
<Tooltip active={beingDragged === 'both' || beingDragged === 'end'}>
|
<Tooltip active={beingDragged === 'both' || beingDragged === 'end'}>
|
||||||
{unitPosToHumanReadablePos(clippedSpaceRange.end)}
|
{unitPosToHumanReadablePos(clippedSpaceRange.end)}
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
</RangeEndHandle>
|
</RangeEndHandle>
|
||||||
</DraggableArea>
|
|
||||||
</TimeThread>
|
</TimeThread>
|
||||||
</Container>
|
</Container>
|
||||||
)
|
)
|
||||||
|
|
|
@ -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<HTMLElement | SVGElement>
|
|
||||||
onDragStart?: (
|
|
||||||
event: React.MouseEvent<HTMLElement | SVGElement>,
|
|
||||||
) => 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<IProps, {}> {
|
|
||||||
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<HTMLElement>) => {
|
|
||||||
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
|
|
|
@ -39,10 +39,10 @@ const CursorOverride = styled.div`
|
||||||
`
|
`
|
||||||
|
|
||||||
type Context = {
|
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<Context>({} as $IntentionalAny)
|
const context = createContext<Context>({} as $IntentionalAny)
|
||||||
|
|
||||||
|
@ -51,7 +51,7 @@ const PointerEventsHandler: React.FC<{
|
||||||
}> = (props) => {
|
}> = (props) => {
|
||||||
const [locks, setLocks] = useState<Lock[]>([])
|
const [locks, setLocks] = useState<Lock[]>([])
|
||||||
const contextValue = useMemo<Context>(() => {
|
const contextValue = useMemo<Context>(() => {
|
||||||
const getLock = (className: string, cursor: string) => {
|
const getLock = (className: string, cursor?: string) => {
|
||||||
const lock = {className, cursor}
|
const lock = {className, cursor}
|
||||||
setLocks((s) => [...s, lock])
|
setLocks((s) => [...s, lock])
|
||||||
const unlock = () => {
|
const unlock = () => {
|
||||||
|
@ -104,7 +104,7 @@ export const useCssCursorLock = (
|
||||||
enabled: boolean,
|
enabled: boolean,
|
||||||
className: string,
|
className: string,
|
||||||
/** e.g. `"ew"`, `"help"`, `"pointer"`, `"text"`, etc */
|
/** e.g. `"ew"`, `"help"`, `"pointer"`, `"text"`, etc */
|
||||||
cursor: string,
|
cursor?: string,
|
||||||
) => {
|
) => {
|
||||||
const ctx = useContext(context)
|
const ctx = useContext(context)
|
||||||
useLayoutEffect(() => {
|
useLayoutEffect(() => {
|
||||||
|
|
|
@ -2,10 +2,10 @@ import {clamp, isInteger, round} from 'lodash-es'
|
||||||
import type {MutableRefObject} from 'react'
|
import type {MutableRefObject} from 'react'
|
||||||
import React, {useMemo, useRef} from 'react'
|
import React, {useMemo, useRef} from 'react'
|
||||||
import styled from 'styled-components'
|
import styled from 'styled-components'
|
||||||
import DraggableArea from '@theatre/studio/uiComponents/DraggableArea'
|
|
||||||
import mergeRefs from 'react-merge-refs'
|
import mergeRefs from 'react-merge-refs'
|
||||||
import useRefAndState from '@theatre/studio/utils/useRefAndState'
|
import useRefAndState from '@theatre/studio/utils/useRefAndState'
|
||||||
import useOnClickOutside from '@theatre/studio/uiComponents/useOnClickOutside'
|
import useOnClickOutside from '@theatre/studio/uiComponents/useOnClickOutside'
|
||||||
|
import useDrag from '@theatre/studio/uiComponents/useDrag'
|
||||||
|
|
||||||
const Container = styled.div`
|
const Container = styled.div`
|
||||||
height: 100%;
|
height: 100%;
|
||||||
|
@ -71,9 +71,9 @@ const FillIndicator = styled.div`
|
||||||
}
|
}
|
||||||
`
|
`
|
||||||
|
|
||||||
function isFiniteFloat(s: string) {
|
const DragWrap = styled.div`
|
||||||
return isFinite(parseFloat(s))
|
display: contents;
|
||||||
}
|
`
|
||||||
|
|
||||||
type IState_NoFocus = {
|
type IState_NoFocus = {
|
||||||
mode: 'noFocus'
|
mode: 'noFocus'
|
||||||
|
@ -87,8 +87,6 @@ type IState_EditingViaKeyboard = {
|
||||||
|
|
||||||
type IState_Dragging = {
|
type IState_Dragging = {
|
||||||
mode: 'dragging'
|
mode: 'dragging'
|
||||||
valueBeforeDragging: number
|
|
||||||
currentDraggingValue: number
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type IState = IState_NoFocus | IState_EditingViaKeyboard | IState_Dragging
|
type IState = IState_NoFocus | IState_EditingViaKeyboard | IState_Dragging
|
||||||
|
@ -223,14 +221,32 @@ const BasicNumberInput: React.FC<{
|
||||||
|
|
||||||
stateRef.current = {
|
stateRef.current = {
|
||||||
mode: 'dragging',
|
mode: 'dragging',
|
||||||
valueBeforeDragging: curValue,
|
|
||||||
currentDraggingValue: curValue,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let valueBeforeDragging = curValue
|
||||||
|
let valueDuringDragging = curValue
|
||||||
|
|
||||||
bodyCursorBeforeDrag.current = document.body.style.cursor
|
bodyCursorBeforeDrag.current = document.body.style.cursor
|
||||||
}
|
|
||||||
|
|
||||||
const onDragEnd = (happened: boolean) => {
|
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,
|
||||||
|
})
|
||||||
|
|
||||||
|
valueDuringDragging = propsA.range
|
||||||
|
? clamp(newValue, propsA.range[0], propsA.range[1])
|
||||||
|
: newValue
|
||||||
|
|
||||||
|
propsRef.current.temporarilySetValue(valueDuringDragging)
|
||||||
|
},
|
||||||
|
onDragEnd(happened: boolean) {
|
||||||
if (!happened) {
|
if (!happened) {
|
||||||
propsRef.current.discardTemporaryValue()
|
propsRef.current.discardTemporaryValue()
|
||||||
stateRef.current = {mode: 'noFocus'}
|
stateRef.current = {mode: 'noFocus'}
|
||||||
|
@ -238,38 +254,15 @@ const BasicNumberInput: React.FC<{
|
||||||
inputRef.current!.focus()
|
inputRef.current!.focus()
|
||||||
inputRef.current!.setSelectionRange(0, 100)
|
inputRef.current!.setSelectionRange(0, 100)
|
||||||
} else {
|
} else {
|
||||||
const curState = stateRef.current as IState_Dragging
|
if (valueBeforeDragging === valueDuringDragging) {
|
||||||
const value = curState.currentDraggingValue
|
|
||||||
if (curState.valueBeforeDragging === value) {
|
|
||||||
propsRef.current.discardTemporaryValue()
|
propsRef.current.discardTemporaryValue()
|
||||||
} else {
|
} else {
|
||||||
propsRef.current.permanentlySetValue(value)
|
propsRef.current.permanentlySetValue(valueDuringDragging)
|
||||||
}
|
}
|
||||||
stateRef.current = {mode: 'noFocus'}
|
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 {
|
return {
|
||||||
|
@ -279,8 +272,6 @@ const BasicNumberInput: React.FC<{
|
||||||
onInputKeyDown,
|
onInputKeyDown,
|
||||||
onClick,
|
onClick,
|
||||||
onFocus,
|
onFocus,
|
||||||
onDragEnd,
|
|
||||||
onDrag,
|
|
||||||
}
|
}
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
|
@ -329,18 +320,17 @@ const BasicNumberInput: React.FC<{
|
||||||
/>
|
/>
|
||||||
) : null
|
) : null
|
||||||
|
|
||||||
|
const [refDrag, nodeDrag] = useRefAndState<HTMLDivElement | null>(null)
|
||||||
|
useDrag(nodeDrag, {
|
||||||
|
debugName: 'form/BasicNumberInput',
|
||||||
|
onDragStart: callbacks.transitionToDraggingMode,
|
||||||
|
lockCSSCursorTo: 'ew-resize',
|
||||||
|
disabled: stateRef.current.mode === 'editingViaKeyboard',
|
||||||
|
})
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Container className={propsA.className + ' ' + stateRef.current.mode}>
|
<Container className={propsA.className + ' ' + stateRef.current.mode}>
|
||||||
<DraggableArea
|
<DragWrap ref={refDrag}>{theInput}</DragWrap>
|
||||||
key="draggableArea"
|
|
||||||
onDragStart={callbacks.transitionToDraggingMode}
|
|
||||||
onDragEnd={callbacks.onDragEnd}
|
|
||||||
onDrag={callbacks.onDrag}
|
|
||||||
enabled={stateRef.current.mode !== 'editingViaKeyboard'}
|
|
||||||
lockCursorTo="ew-resize"
|
|
||||||
>
|
|
||||||
{theInput}
|
|
||||||
</DraggableArea>
|
|
||||||
{fillIndicator}
|
{fillIndicator}
|
||||||
</Container>
|
</Container>
|
||||||
)
|
)
|
||||||
|
|
|
@ -44,7 +44,7 @@ export type UseDragOpts = {
|
||||||
/**
|
/**
|
||||||
* The css cursor property during the gesture will be locked to this value
|
* 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
|
* 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
|
* if the user is just clicking (and not dragging). However, if the gesture turns
|
||||||
|
@ -88,9 +88,9 @@ export default function useDrag(
|
||||||
>('notDragging')
|
>('notDragging')
|
||||||
|
|
||||||
useCssCursorLock(
|
useCssCursorLock(
|
||||||
mode === 'dragging' && typeof opts.lockCursorTo === 'string',
|
mode === 'dragging' && typeof opts.lockCSSCursorTo === 'string',
|
||||||
'dragging',
|
'dragging',
|
||||||
opts.lockCursorTo!,
|
opts.lockCSSCursorTo,
|
||||||
)
|
)
|
||||||
|
|
||||||
const {capturePointer} = usePointerCapturing(`useDrag for ${opts.debugName}`)
|
const {capturePointer} = usePointerCapturing(`useDrag for ${opts.debugName}`)
|
||||||
|
|
Loading…
Reference in a new issue