Simplify useDrag()'s API and implement middle-button dragging in SequenceEditor

This commit is contained in:
Aria Minaei 2022-05-10 15:30:10 +02:00
parent e12d495f29
commit 202b61c48c
17 changed files with 995 additions and 972 deletions

View file

@ -21,52 +21,44 @@ const PanelDragZone: React.FC<
const [ref, node] = useRefAndState<HTMLDivElement>(null as $IntentionalAny)
const dragOpts: Parameters<typeof useDrag>[1] = useMemo(() => {
let stuffBeforeDrag = panelStuffRef.current
let tempTransaction: CommitOrDiscard | undefined
let unlock: VoidFn | undefined
return {
debugName: 'PanelDragZone',
lockCursorTo: 'move',
onDragStart() {
stuffBeforeDrag = panelStuffRef.current
if (unlock) {
const u = unlock
unlock = undefined
u()
}
unlock = panelStuff.addBoundsHighlightLock()
},
onDrag(dx, dy) {
const newDims: typeof panelStuff['dims'] = {
...stuffBeforeDrag.dims,
top: stuffBeforeDrag.dims.top + dy,
left: stuffBeforeDrag.dims.left + dx,
}
const position = panelDimsToPanelPosition(newDims, {
width: window.innerWidth,
height: window.innerHeight,
})
const stuffBeforeDrag = panelStuffRef.current
let tempTransaction: CommitOrDiscard | undefined
tempTransaction?.discard()
tempTransaction = getStudio()!.tempTransaction(({stateEditors}) => {
stateEditors.studio.historic.panelPositions.setPanelPosition({
position,
panelId: stuffBeforeDrag.panelId,
})
})
},
onDragEnd(dragHappened) {
if (unlock) {
const u = unlock
unlock = undefined
u()
const unlock = panelStuff.addBoundsHighlightLock()
return {
onDrag(dx, dy) {
const newDims: typeof panelStuff['dims'] = {
...stuffBeforeDrag.dims,
top: stuffBeforeDrag.dims.top + dy,
left: stuffBeforeDrag.dims.left + dx,
}
const position = panelDimsToPanelPosition(newDims, {
width: window.innerWidth,
height: window.innerHeight,
})
tempTransaction?.discard()
tempTransaction = getStudio()!.tempTransaction(({stateEditors}) => {
stateEditors.studio.historic.panelPositions.setPanelPosition({
position,
panelId: stuffBeforeDrag.panelId,
})
})
},
onDragEnd(dragHappened) {
unlock()
if (dragHappened) {
tempTransaction?.commit()
} else {
tempTransaction?.discard()
}
},
}
if (dragHappened) {
tempTransaction?.commit()
} else {
tempTransaction?.discard()
}
tempTransaction = undefined
},
}
}, [])

View file

@ -1,10 +1,10 @@
import useRefAndState from '@theatre/studio/utils/useRefAndState'
import type {$IntentionalAny, VoidFn} from '@theatre/shared/utils/types'
import type {$IntentionalAny} from '@theatre/shared/utils/types'
import getStudio from '@theatre/studio/getStudio'
import type {CommitOrDiscard} from '@theatre/studio/StudioStore/StudioStore'
import useDrag from '@theatre/studio/uiComponents/useDrag'
import {lighten} from 'polished'
import React, {useMemo, useRef, useState} from 'react'
import React, {useMemo, useRef} from 'react'
import styled from 'styled-components'
import {panelDimsToPanelPosition, usePanel} from './BasePanel'
import {pointerEventsAutoInNormalMode} from '@theatre/studio/css'
@ -142,96 +142,84 @@ const PanelResizeHandle: React.FC<{
const panelStuff = usePanel()
const panelStuffRef = useRef(panelStuff)
panelStuffRef.current = panelStuff
const [isDragging, setIsDragging] = useState(false)
const [ref, node] = useRefAndState<HTMLDivElement>(null as $IntentionalAny)
const dragOpts: Parameters<typeof useDrag>[1] = useMemo(() => {
let stuffBeforeDrag = panelStuffRef.current
let tempTransaction: CommitOrDiscard | undefined
let unlock: VoidFn | undefined
return {
debugName: 'PanelResizeHandle',
lockCursorTo: cursors[which],
onDragStart() {
stuffBeforeDrag = panelStuffRef.current
setIsDragging(true)
if (unlock) {
const u = unlock
unlock = undefined
u()
}
unlock = panelStuff.addBoundsHighlightLock()
},
onDrag(dx, dy) {
const newDims: typeof panelStuff['dims'] = {
...stuffBeforeDrag.dims,
}
let tempTransaction: CommitOrDiscard | undefined
if (which.startsWith('Bottom')) {
newDims.height = Math.max(
newDims.height + dy,
stuffBeforeDrag.minDims.height,
)
} else if (which.startsWith('Top')) {
const bottom = newDims.top + newDims.height
const top = Math.min(
bottom - stuffBeforeDrag.minDims.height,
newDims.top + dy,
)
const height = bottom - top
const stuffBeforeDrag = panelStuffRef.current
const unlock = panelStuff.addBoundsHighlightLock()
newDims.height = height
newDims.top = top
}
if (which.endsWith('Left')) {
const right = newDims.left + newDims.width
const left = Math.min(
right - stuffBeforeDrag.minDims.width,
newDims.left + dx,
)
const width = right - left
return {
onDrag(dx, dy) {
const newDims: typeof panelStuff['dims'] = {
...stuffBeforeDrag.dims,
}
newDims.width = width
newDims.left = left
} else if (which.endsWith('Right')) {
newDims.width = Math.max(
newDims.width + dx,
stuffBeforeDrag.minDims.width,
)
}
if (which.startsWith('Bottom')) {
newDims.height = Math.max(
newDims.height + dy,
stuffBeforeDrag.minDims.height,
)
} else if (which.startsWith('Top')) {
const bottom = newDims.top + newDims.height
const top = Math.min(
bottom - stuffBeforeDrag.minDims.height,
newDims.top + dy,
)
const height = bottom - top
const position = panelDimsToPanelPosition(newDims, {
width: window.innerWidth,
height: window.innerHeight,
})
newDims.height = height
newDims.top = top
}
if (which.endsWith('Left')) {
const right = newDims.left + newDims.width
const left = Math.min(
right - stuffBeforeDrag.minDims.width,
newDims.left + dx,
)
const width = right - left
tempTransaction?.discard()
tempTransaction = getStudio()!.tempTransaction(({stateEditors}) => {
stateEditors.studio.historic.panelPositions.setPanelPosition({
position,
panelId: stuffBeforeDrag.panelId,
})
})
},
onDragEnd(dragHappened) {
if (unlock) {
const u = unlock
unlock = undefined
u()
newDims.width = width
newDims.left = left
} else if (which.endsWith('Right')) {
newDims.width = Math.max(
newDims.width + dx,
stuffBeforeDrag.minDims.width,
)
}
const position = panelDimsToPanelPosition(newDims, {
width: window.innerWidth,
height: window.innerHeight,
})
tempTransaction?.discard()
tempTransaction = getStudio()!.tempTransaction(({stateEditors}) => {
stateEditors.studio.historic.panelPositions.setPanelPosition({
position,
panelId: stuffBeforeDrag.panelId,
})
})
},
onDragEnd(dragHappened) {
unlock()
if (dragHappened) {
tempTransaction?.commit()
} else {
tempTransaction?.discard()
}
},
}
setIsDragging(false)
if (dragHappened) {
tempTransaction?.commit()
} else {
tempTransaction?.discard()
}
tempTransaction = undefined
},
}
}, [which])
useDrag(node, dragOpts)
const [isDragging] = useDrag(node, dragOpts)
const Comp = els[which]
const isOnCorner = which.length <= 6

View file

@ -8,13 +8,8 @@ import {lighten} from 'polished'
import React from 'react'
import {useMemo, useRef} from 'react'
import styled from 'styled-components'
import type {
SequenceEditorPanelLayout,
DopeSheetSelection,
} from '@theatre/studio/panels/SequenceEditorPanel/layout/layout'
import {DOT_SIZE_PX} from './KeyframeDot'
import type KeyframeEditor from './KeyframeEditor'
import type Sequence from '@theatre/core/sequences/Sequence'
import usePopover from '@theatre/studio/uiComponents/Popover/usePopover'
import BasicPopover from '@theatre/studio/uiComponents/Popover/BasicPopover'
import CurveEditorPopover from './CurveEditorPopover/CurveEditorPopover'
@ -156,84 +151,74 @@ function useDragKeyframe(node: HTMLDivElement | null, props: IProps) {
propsRef.current = props
const gestureHandlers = useMemo<Parameters<typeof useDrag>[1]>(() => {
let toUnitSpace: SequenceEditorPanelLayout['scaledSpace']['toUnitSpace']
let tempTransaction: CommitOrDiscard | undefined
let propsAtStartOfDrag: IProps
let selectionDragHandlers:
| ReturnType<DopeSheetSelection['getDragHandlers']>
| undefined
let sequence: Sequence
return {
debugName: 'useDragKeyframe',
lockCursorTo: 'ew-resize',
onDragStart(event) {
const props = propsRef.current
let tempTransaction: CommitOrDiscard | undefined
if (props.selection) {
const {selection, leaf} = props
const {sheetObject} = leaf
selectionDragHandlers = selection.getDragHandlers({
...sheetObject.address,
pathToProp: leaf.pathToProp,
trackId: leaf.trackId,
keyframeId: props.keyframe.id,
domNode: node!,
positionAtStartOfDrag:
props.trackData.keyframes[props.index].position,
})
selectionDragHandlers.onDragStart?.(event)
return
return selection
.getDragHandlers({
...sheetObject.address,
pathToProp: leaf.pathToProp,
trackId: leaf.trackId,
keyframeId: props.keyframe.id,
domNode: node!,
positionAtStartOfDrag:
props.trackData.keyframes[props.index].position,
})
.onDragStart(event)
}
propsAtStartOfDrag = props
sequence = val(propsAtStartOfDrag.layoutP.sheet).getSequence()
const propsAtStartOfDrag = props
const sequence = val(propsAtStartOfDrag.layoutP.sheet).getSequence()
toUnitSpace = val(propsAtStartOfDrag.layoutP.scaledSpace.toUnitSpace)
},
onDrag(dx, dy, event) {
if (selectionDragHandlers) {
selectionDragHandlers.onDrag(dx, dy, event)
return
}
const delta = toUnitSpace(dx)
if (tempTransaction) {
tempTransaction.discard()
tempTransaction = undefined
}
tempTransaction = getStudio()!.tempTransaction(({stateEditors}) => {
stateEditors.coreByProject.historic.sheetsById.sequence.transformKeyframes(
{
...propsAtStartOfDrag.leaf.sheetObject.address,
trackId: propsAtStartOfDrag.leaf.trackId,
keyframeIds: [
propsAtStartOfDrag.keyframe.id,
propsAtStartOfDrag.trackData.keyframes[
propsAtStartOfDrag.index + 1
].id,
],
translate: delta,
scale: 1,
origin: 0,
snappingFunction: sequence.closestGridPosition,
},
)
})
},
onDragEnd(dragHappened) {
if (selectionDragHandlers) {
selectionDragHandlers.onDragEnd?.(dragHappened)
const toUnitSpace = val(
propsAtStartOfDrag.layoutP.scaledSpace.toUnitSpace,
)
selectionDragHandlers = undefined
return {
onDrag(dx, dy, event) {
const delta = toUnitSpace(dx)
if (tempTransaction) {
tempTransaction.discard()
tempTransaction = undefined
}
tempTransaction = getStudio()!.tempTransaction(({stateEditors}) => {
stateEditors.coreByProject.historic.sheetsById.sequence.transformKeyframes(
{
...propsAtStartOfDrag.leaf.sheetObject.address,
trackId: propsAtStartOfDrag.leaf.trackId,
keyframeIds: [
propsAtStartOfDrag.keyframe.id,
propsAtStartOfDrag.trackData.keyframes[
propsAtStartOfDrag.index + 1
].id,
],
translate: delta,
scale: 1,
origin: 0,
snappingFunction: sequence.closestGridPosition,
},
)
})
},
onDragEnd(dragHappened) {
if (dragHappened) {
if (tempTransaction) {
tempTransaction.commit()
}
} else {
if (tempTransaction) {
tempTransaction.discard()
}
}
},
}
if (dragHappened) {
if (tempTransaction) {
tempTransaction.commit()
}
} else {
if (tempTransaction) {
tempTransaction.discard()
}
}
tempTransaction = undefined
},
}
}, [])

View file

@ -256,15 +256,17 @@ function useKeyframeDrag(
lockCursorTo: 'move',
onDragStart() {
setFrozen(true)
},
onDrag(dx, dy) {
if (!svgNode) return
return {
onDrag(dx, dy) {
if (!svgNode) return
props.onCurveChange(setHandles(dx, dy))
},
onDragEnd(dragHappened) {
setFrozen(false)
props.onCancelCurveChange()
props.onCurveChange(setHandles(dx, dy))
},
onDragEnd(dragHappened) {
setFrozen(false)
props.onCancelCurveChange()
},
}
},
}),
[svgNode, props.trackData],

View file

@ -1,7 +1,3 @@
import type {
DopeSheetSelection,
SequenceEditorPanelLayout,
} from '@theatre/studio/panels/SequenceEditorPanel/layout/layout'
import getStudio from '@theatre/studio/getStudio'
import type {CommitOrDiscard} from '@theatre/studio/StudioStore/StudioStore'
import useContextMenu from '@theatre/studio/uiComponents/simpleContextMenu/useContextMenu'
@ -148,14 +144,6 @@ function useDragKeyframe(
propsRef.current = props
const useDragOpts = useMemo<UseDragOpts>(() => {
let toUnitSpace: SequenceEditorPanelLayout['scaledSpace']['toUnitSpace']
let tempTransaction: CommitOrDiscard | undefined
let propsAtStartOfDrag: IKeyframeDotProps
let selectionDragHandlers:
| ReturnType<DopeSheetSelection['getDragHandlers']>
| undefined
return {
debugName: 'KeyframeDot/useDragKeyframe',
@ -164,65 +152,61 @@ function useDragKeyframe(
if (props.selection) {
const {selection, leaf} = props
const {sheetObject} = leaf
selectionDragHandlers = selection.getDragHandlers({
...sheetObject.address,
pathToProp: leaf.pathToProp,
trackId: leaf.trackId,
keyframeId: props.keyframe.id,
domNode: node!,
positionAtStartOfDrag:
props.trackData.keyframes[props.index].position,
})
selectionDragHandlers.onDragStart?.(event)
return
return selection
.getDragHandlers({
...sheetObject.address,
pathToProp: leaf.pathToProp,
trackId: leaf.trackId,
keyframeId: props.keyframe.id,
domNode: node!,
positionAtStartOfDrag:
props.trackData.keyframes[props.index].position,
})
.onDragStart(event)
}
propsAtStartOfDrag = props
toUnitSpace = val(propsAtStartOfDrag.layoutP.scaledSpace.toUnitSpace)
},
onDrag(dx, dy, event) {
if (selectionDragHandlers) {
selectionDragHandlers.onDrag(dx, dy, event)
return
}
const original =
propsAtStartOfDrag.trackData.keyframes[propsAtStartOfDrag.index]
const newPosition = Math.max(
// check if our event hoversover a [data-pos] element
DopeSnap.checkIfMouseEventSnapToPos(event, {
ignore: node,
}) ??
// if we don't find snapping target, check the distance dragged + original position
original.position + toUnitSpace(dx),
// sanitize to minimum of zero
0,
const propsAtStartOfDrag = props
const toUnitSpace = val(
propsAtStartOfDrag.layoutP.scaledSpace.toUnitSpace,
)
tempTransaction?.discard()
tempTransaction = undefined
tempTransaction = getStudio()!.tempTransaction(({stateEditors}) => {
stateEditors.coreByProject.historic.sheetsById.sequence.replaceKeyframes(
{
...propsAtStartOfDrag.leaf.sheetObject.address,
trackId: propsAtStartOfDrag.leaf.trackId,
keyframes: [{...original, position: newPosition}],
snappingFunction: val(
propsAtStartOfDrag.layoutP.sheet,
).getSequence().closestGridPosition,
},
)
})
},
onDragEnd(dragHappened) {
if (selectionDragHandlers) {
selectionDragHandlers.onDragEnd?.(dragHappened)
let tempTransaction: CommitOrDiscard | undefined
selectionDragHandlers = undefined
return {
onDrag(dx, dy, event) {
const original =
propsAtStartOfDrag.trackData.keyframes[propsAtStartOfDrag.index]
const newPosition = Math.max(
// check if our event hoversover a [data-pos] element
DopeSnap.checkIfMouseEventSnapToPos(event, {
ignore: node,
}) ??
// if we don't find snapping target, check the distance dragged + original position
original.position + toUnitSpace(dx),
// sanitize to minimum of zero
0,
)
tempTransaction?.discard()
tempTransaction = undefined
tempTransaction = getStudio()!.tempTransaction(({stateEditors}) => {
stateEditors.coreByProject.historic.sheetsById.sequence.replaceKeyframes(
{
...propsAtStartOfDrag.leaf.sheetObject.address,
trackId: propsAtStartOfDrag.leaf.trackId,
keyframes: [{...original, position: newPosition}],
snappingFunction: val(
propsAtStartOfDrag.layoutP.sheet,
).getSequence().closestGridPosition,
},
)
})
},
onDragEnd(dragHappened) {
if (dragHappened) tempTransaction?.commit()
else tempTransaction?.discard()
},
}
if (dragHappened) tempTransaction?.commit()
else tempTransaction?.discard()
tempTransaction = undefined
},
}
}, [])

View file

@ -79,27 +79,30 @@ function useCaptureSelection(
}
val(layoutP.selectionAtom).setState({current: undefined})
},
onDrag(_dx, _dy, event) {
// const state = ref.current!
const rect = containerNode!.getBoundingClientRect()
const posInScaledSpace = event.clientX - rect.left
return {
onDrag(_dx, _dy, event) {
// const state = ref.current!
const rect = containerNode!.getBoundingClientRect()
const posInUnitSpace = val(layoutP.scaledSpace.toUnitSpace)(
posInScaledSpace,
)
const posInScaledSpace = event.clientX - rect.left
ref.current = {
positions: [ref.current!.positions[0], posInUnitSpace],
ys: [ref.current!.ys[0], event.clientY - rect.top],
const posInUnitSpace = val(layoutP.scaledSpace.toUnitSpace)(
posInScaledSpace,
)
ref.current = {
positions: [ref.current!.positions[0], posInUnitSpace],
ys: [ref.current!.ys[0], event.clientY - rect.top],
}
const selection = utils.boundsToSelection(layoutP, ref.current)
val(layoutP.selectionAtom).setState({current: selection})
},
onDragEnd(_dragHappened) {
ref.current = null
},
}
const selection = utils.boundsToSelection(layoutP, ref.current)
val(layoutP.selectionAtom).setState({current: selection})
},
onDragEnd(_dragHappened) {
ref.current = null
},
}
}, [layoutP, containerNode, ref]),
@ -183,59 +186,65 @@ namespace utils {
type: 'DopeSheetSelection',
byObjectKey: {},
getDragHandlers(origin) {
let tempTransaction: CommitOrDiscard | undefined
let toUnitSpace: SequenceEditorPanelLayout['scaledSpace']['toUnitSpace']
return {
debugName: 'DopeSheetSelectionView/boundsToSelection',
onDragStart() {
toUnitSpace = val(layoutP.scaledSpace.toUnitSpace)
},
onDrag(dx, _, event) {
if (tempTransaction) {
tempTransaction.discard()
tempTransaction = undefined
}
let tempTransaction: CommitOrDiscard | undefined
const snapPos = DopeSnap.checkIfMouseEventSnapToPos(event, {
ignore: origin.domNode,
})
const toUnitSpace = val(layoutP.scaledSpace.toUnitSpace)
let delta: number
if (snapPos != null) {
delta = snapPos - origin.positionAtStartOfDrag
} else {
delta = toUnitSpace(dx)
}
tempTransaction = getStudio()!.tempTransaction(({stateEditors}) => {
const transformKeyframes =
stateEditors.coreByProject.historic.sheetsById.sequence
.transformKeyframes
for (const objectKey of Object.keys(selection.byObjectKey)) {
const {byTrackId} = selection.byObjectKey[objectKey]!
for (const trackId of Object.keys(byTrackId)) {
const {byKeyframeId} = byTrackId[trackId]!
transformKeyframes({
trackId,
keyframeIds: Object.keys(byKeyframeId),
translate: delta,
scale: 1,
origin: 0,
snappingFunction: sheet.getSequence().closestGridPosition,
objectKey,
projectId: origin.projectId,
sheetId: origin.sheetId,
})
return {
onDrag(dx, _, event) {
if (tempTransaction) {
tempTransaction.discard()
tempTransaction = undefined
}
}
})
},
onDragEnd(dragHappened) {
if (dragHappened) tempTransaction?.commit()
else tempTransaction?.discard()
tempTransaction = undefined
const snapPos = DopeSnap.checkIfMouseEventSnapToPos(event, {
ignore: origin.domNode,
})
let delta: number
if (snapPos != null) {
delta = snapPos - origin.positionAtStartOfDrag
} else {
delta = toUnitSpace(dx)
}
tempTransaction = getStudio()!.tempTransaction(
({stateEditors}) => {
const transformKeyframes =
stateEditors.coreByProject.historic.sheetsById.sequence
.transformKeyframes
for (const objectKey of Object.keys(
selection.byObjectKey,
)) {
const {byTrackId} = selection.byObjectKey[objectKey]!
for (const trackId of Object.keys(byTrackId)) {
const {byKeyframeId} = byTrackId[trackId]!
transformKeyframes({
trackId,
keyframeIds: Object.keys(byKeyframeId),
translate: delta,
scale: 1,
origin: 0,
snappingFunction:
sheet.getSequence().closestGridPosition,
objectKey,
projectId: origin.projectId,
sheetId: origin.sheetId,
})
}
}
},
)
},
onDragEnd(dragHappened) {
if (dragHappened) tempTransaction?.commit()
else tempTransaction?.discard()
},
}
},
}
},

View file

@ -1,4 +1,3 @@
import type Sequence from '@theatre/core/sequences/Sequence'
import type {SequenceEditorPanelLayout} from '@theatre/studio/panels/SequenceEditorPanel/layout/layout'
import useDrag from '@theatre/studio/uiComponents/useDrag'
import useRefAndState from '@theatre/studio/utils/useRefAndState'
@ -50,7 +49,7 @@ const HorizontallyScrollableArea: React.FC<{
)
useHandlePanAndZoom(layoutP, containerNode)
useDragHandlers(layoutP, containerNode)
useDragPlayheadHandlers(layoutP, containerNode)
useUpdateScrollFromClippedSpaceRange(layoutP, containerNode)
return (
@ -70,31 +69,13 @@ const HorizontallyScrollableArea: React.FC<{
export default HorizontallyScrollableArea
function useDragHandlers(
function useDragPlayheadHandlers(
layoutP: Pointer<SequenceEditorPanelLayout>,
containerEl: HTMLDivElement | null,
) {
const handlers = useMemo((): Parameters<typeof useDrag>[1] => {
let posBeforeSeek = 0
let sequence: Sequence
let scaledSpaceToUnitSpace: typeof layoutP.scaledSpace.toUnitSpace.$$__pointer_type
const setIsSeeking = val(layoutP.seeker.setIsSeeking)
return {
debugName: 'HorizontallyScrollableArea',
onDrag(dx: number, _, event) {
const deltaPos = scaledSpaceToUnitSpace(dx)
const unsnappedPos = clamp(posBeforeSeek + deltaPos, 0, sequence.length)
let newPosition = unsnappedPos
const snapPos = DopeSnap.checkIfMouseEventSnapToPos(event, {})
if (snapPos != null) {
newPosition = snapPos
}
sequence.position = newPosition
},
onDragStart(event) {
if (event.target instanceof HTMLInputElement) {
// editing some value
@ -124,16 +105,38 @@ function useDragHandlers(
Infinity,
)
sequence = val(layoutP.sheet).getSequence()
const setIsSeeking = val(layoutP.seeker.setIsSeeking)
const sequence = val(layoutP.sheet).getSequence()
sequence.position = initialPositionInUnitSpace
posBeforeSeek = initialPositionInUnitSpace
scaledSpaceToUnitSpace = val(layoutP.scaledSpace.toUnitSpace)
const posBeforeSeek = initialPositionInUnitSpace
const scaledSpaceToUnitSpace = val(layoutP.scaledSpace.toUnitSpace)
setIsSeeking(true)
},
onDragEnd() {
setIsSeeking(false)
return {
onDrag(dx: number, _, event) {
const deltaPos = scaledSpaceToUnitSpace(dx)
const unsnappedPos = clamp(
posBeforeSeek + deltaPos,
0,
sequence.length,
)
let newPosition = unsnappedPos
const snapPos = DopeSnap.checkIfMouseEventSnapToPos(event, {})
if (snapPos != null) {
newPosition = snapPos
}
sequence.position = newPosition
},
onDragEnd() {
setIsSeeking(false)
},
}
},
}
}, [layoutP, containerEl])
@ -233,6 +236,39 @@ function useHandlePanAndZoom(
node.removeEventListener('wheel', receiveWheelEvent, listenerOptions)
}
}, [node, layoutP])
useDrag(
node,
useMemo<Parameters<typeof useDrag>[1]>(() => {
return {
onDragStart(e) {
const oldRange = val(layoutP.clippedSpace.range)
const setRange = val(layoutP.clippedSpace.setRange)
const scaledSpaceToUnitSpace = val(layoutP.scaledSpace.toUnitSpace)
e.preventDefault()
e.stopPropagation()
return {
onDrag(dx, dy, _, __, deltaYFromLastEvent) {
receiveVerticalWheelEvent({deltaY: -deltaYFromLastEvent})
const delta = -scaledSpaceToUnitSpace(dx)
const newRange = mapValues(
oldRange,
(originalPos) => originalPos + delta,
)
setRange(newRange)
},
}
},
debugName: 'HorizontallyScrollableArea Middle Button Drag',
buttons: [1],
lockCursorTo: 'grab',
}
}, [layoutP]),
)
}
function normalize(value: number, [min, max]: [min: number, max: number]) {

View file

@ -1,7 +1,7 @@
import {usePrism} from '@theatre/react'
import type {Pointer} from '@theatre/dataverse'
import {val} from '@theatre/dataverse'
import React, {useMemo, useRef, useState} from 'react'
import React, {useMemo, useRef} from 'react'
import styled from 'styled-components'
import type {SequenceEditorPanelLayout} from '@theatre/studio/panels/SequenceEditorPanel/layout/layout'
import {zIndexes} from '@theatre/studio/panels/SequenceEditorPanel/SequenceEditorPanel'
@ -10,7 +10,6 @@ import useRefAndState from '@theatre/studio/utils/useRefAndState'
import type {CommitOrDiscard} from '@theatre/studio/StudioStore/StudioStore'
import useDrag from '@theatre/studio/uiComponents/useDrag'
import getStudio from '@theatre/studio/getStudio'
import type Sheet from '@theatre/core/sheets/Sheet'
import usePopover from '@theatre/studio/uiComponents/Popover/usePopover'
import {
includeLockFrameStampAttrs,
@ -219,58 +218,56 @@ function useDragBulge(
): [isDragging: boolean] {
const propsRef = useRef(props)
propsRef.current = props
const [isDragging, setIsDragging] = useState(false)
useLockFrameStampPosition(isDragging, -1)
const gestureHandlers = useMemo<Parameters<typeof useDrag>[1]>(() => {
let toUnitSpace: SequenceEditorPanelLayout['scaledSpace']['toUnitSpace']
let tempTransaction: CommitOrDiscard | undefined
let propsAtStartOfDrag: IProps
let sheet: Sheet
let initialLength: number
return {
debugName: 'LengthIndicator/useDragBulge',
lockCursorTo: 'ew-resize',
onDragStart(event) {
setIsDragging(true)
propsAtStartOfDrag = propsRef.current
sheet = val(propsRef.current.layoutP.sheet)
initialLength = sheet.getSequence().length
let tempTransaction: CommitOrDiscard | undefined
toUnitSpace = val(propsAtStartOfDrag.layoutP.scaledSpace.toUnitSpace)
},
onDrag(dx, dy, event) {
const delta = toUnitSpace(dx)
if (tempTransaction) {
tempTransaction.discard()
tempTransaction = undefined
const propsAtStartOfDrag = propsRef.current
const sheet = val(propsRef.current.layoutP.sheet)
const initialLength = sheet.getSequence().length
const toUnitSpace = val(
propsAtStartOfDrag.layoutP.scaledSpace.toUnitSpace,
)
return {
onDrag(dx, dy, event) {
const delta = toUnitSpace(dx)
if (tempTransaction) {
tempTransaction.discard()
tempTransaction = undefined
}
tempTransaction = getStudio()!.tempTransaction(({stateEditors}) => {
stateEditors.coreByProject.historic.sheetsById.sequence.setLength(
{
...sheet.address,
length: initialLength + delta,
},
)
})
},
onDragEnd(dragHappened) {
if (dragHappened) {
if (tempTransaction) {
tempTransaction.commit()
}
} else {
if (tempTransaction) {
tempTransaction.discard()
}
}
},
}
tempTransaction = getStudio()!.tempTransaction(({stateEditors}) => {
stateEditors.coreByProject.historic.sheetsById.sequence.setLength({
...sheet.address,
length: initialLength + delta,
})
})
},
onDragEnd(dragHappened) {
setIsDragging(false)
if (dragHappened) {
if (tempTransaction) {
tempTransaction.commit()
}
} else {
if (tempTransaction) {
tempTransaction.discard()
}
}
tempTransaction = undefined
},
}
}, [])
useDrag(node, gestureHandlers)
const [isDragging] = useDrag(node, gestureHandlers)
useLockFrameStampPosition(isDragging, -1)
return [isDragging]
}

View file

@ -1,10 +1,8 @@
import type {SequenceEditorPanelLayout} from '@theatre/studio/panels/SequenceEditorPanel/layout/layout'
import getStudio from '@theatre/studio/getStudio'
import type {CommitOrDiscard} from '@theatre/studio/StudioStore/StudioStore'
import useContextMenu from '@theatre/studio/uiComponents/simpleContextMenu/useContextMenu'
import useDrag from '@theatre/studio/uiComponents/useDrag'
import useRefAndState from '@theatre/studio/utils/useRefAndState'
import type {VoidFn} from '@theatre/shared/utils/types'
import {val} from '@theatre/dataverse'
import {clamp} from 'lodash-es'
import React, {useMemo, useRef} from 'react'
@ -125,115 +123,130 @@ function useOurDrags(node: SVGCircleElement | null, props: IProps): void {
propsRef.current = props
const handlers = useMemo<Parameters<typeof useDrag>[1]>(() => {
let scaledToUnitSpace: SequenceEditorPanelLayout['scaledSpace']['toUnitSpace']
let verticalToExtremumSpace: SequenceEditorPanelLayout['graphEditorVerticalSpace']['toExtremumSpace']
let propsAtStartOfDrag: IProps
let tempTransaction: CommitOrDiscard | undefined
let unlockExtremums: VoidFn | undefined
return {
debugName: 'CurveHandler/useOurDrags',
lockCursorTo: 'move',
onDragStart() {
propsAtStartOfDrag = propsRef.current
let tempTransaction: CommitOrDiscard | undefined
scaledToUnitSpace = val(
const propsAtStartOfDrag = propsRef.current
const scaledToUnitSpace = val(
propsAtStartOfDrag.layoutP.scaledSpace.toUnitSpace,
)
verticalToExtremumSpace = val(
const verticalToExtremumSpace = val(
propsAtStartOfDrag.layoutP.graphEditorVerticalSpace.toExtremumSpace,
)
unlockExtremums = propsAtStartOfDrag.extremumSpace.lock()
},
onDrag(dxInScaledSpace, dy) {
if (tempTransaction) {
tempTransaction.discard()
tempTransaction = undefined
const unlockExtremums = propsAtStartOfDrag.extremumSpace.lock()
return {
onDrag(dxInScaledSpace, dy) {
if (tempTransaction) {
tempTransaction.discard()
tempTransaction = undefined
}
const {index, trackData} = propsAtStartOfDrag
const cur = trackData.keyframes[index]
const next = trackData.keyframes[index + 1]
const dPosInUnitSpace = scaledToUnitSpace(dxInScaledSpace)
let dPosInKeyframeDiffSpace =
dPosInUnitSpace / (next.position - cur.position)
const dyInVerticalSpace = -dy
const dYInExtremumSpace = verticalToExtremumSpace(dyInVerticalSpace)
const dYInValueSpace =
propsAtStartOfDrag.extremumSpace.deltaToValueSpace(
dYInExtremumSpace,
)
const curValue = props.isScalar ? (cur.value as number) : 0
const nextValue = props.isScalar ? (next.value as number) : 1
const dyInKeyframeDiffSpace =
dYInValueSpace / (nextValue - curValue)
if (propsAtStartOfDrag.which === 'left') {
const handleX = clamp(
cur.handles[2] + dPosInKeyframeDiffSpace,
0,
1,
)
const handleY = cur.handles[3] + dyInKeyframeDiffSpace
tempTransaction = getStudio()!.tempTransaction(
({stateEditors}) => {
stateEditors.coreByProject.historic.sheetsById.sequence.replaceKeyframes(
{
...propsAtStartOfDrag.sheetObject.address,
snappingFunction: val(
propsAtStartOfDrag.layoutP.sheet,
).getSequence().closestGridPosition,
trackId: propsAtStartOfDrag.trackId,
keyframes: [
{
...cur,
handles: [
cur.handles[0],
cur.handles[1],
handleX,
handleY,
],
},
],
},
)
},
)
} else {
const handleX = clamp(
next.handles[0] + dPosInKeyframeDiffSpace,
0,
1,
)
const handleY = next.handles[1] + dyInKeyframeDiffSpace
tempTransaction = getStudio()!.tempTransaction(
({stateEditors}) => {
stateEditors.coreByProject.historic.sheetsById.sequence.replaceKeyframes(
{
...propsAtStartOfDrag.sheetObject.address,
trackId: propsAtStartOfDrag.trackId,
snappingFunction: val(
propsAtStartOfDrag.layoutP.sheet,
).getSequence().closestGridPosition,
keyframes: [
{
...next,
handles: [
handleX,
handleY,
next.handles[2],
next.handles[3],
],
},
],
},
)
},
)
}
},
onDragEnd(dragHappened) {
unlockExtremums()
if (dragHappened) {
if (tempTransaction) {
tempTransaction.commit()
}
} else {
if (tempTransaction) {
tempTransaction.discard()
}
}
},
}
const {index, trackData} = propsAtStartOfDrag
const cur = trackData.keyframes[index]
const next = trackData.keyframes[index + 1]
const dPosInUnitSpace = scaledToUnitSpace(dxInScaledSpace)
let dPosInKeyframeDiffSpace =
dPosInUnitSpace / (next.position - cur.position)
const dyInVerticalSpace = -dy
const dYInExtremumSpace = verticalToExtremumSpace(dyInVerticalSpace)
const dYInValueSpace =
propsAtStartOfDrag.extremumSpace.deltaToValueSpace(dYInExtremumSpace)
const curValue = props.isScalar ? (cur.value as number) : 0
const nextValue = props.isScalar ? (next.value as number) : 1
const dyInKeyframeDiffSpace = dYInValueSpace / (nextValue - curValue)
if (propsAtStartOfDrag.which === 'left') {
const handleX = clamp(cur.handles[2] + dPosInKeyframeDiffSpace, 0, 1)
const handleY = cur.handles[3] + dyInKeyframeDiffSpace
tempTransaction = getStudio()!.tempTransaction(({stateEditors}) => {
stateEditors.coreByProject.historic.sheetsById.sequence.replaceKeyframes(
{
...propsAtStartOfDrag.sheetObject.address,
snappingFunction: val(
propsAtStartOfDrag.layoutP.sheet,
).getSequence().closestGridPosition,
trackId: propsAtStartOfDrag.trackId,
keyframes: [
{
...cur,
handles: [cur.handles[0], cur.handles[1], handleX, handleY],
},
],
},
)
})
} else {
const handleX = clamp(next.handles[0] + dPosInKeyframeDiffSpace, 0, 1)
const handleY = next.handles[1] + dyInKeyframeDiffSpace
tempTransaction = getStudio()!.tempTransaction(({stateEditors}) => {
stateEditors.coreByProject.historic.sheetsById.sequence.replaceKeyframes(
{
...propsAtStartOfDrag.sheetObject.address,
trackId: propsAtStartOfDrag.trackId,
snappingFunction: val(
propsAtStartOfDrag.layoutP.sheet,
).getSequence().closestGridPosition,
keyframes: [
{
...next,
handles: [
handleX,
handleY,
next.handles[2],
next.handles[3],
],
},
],
},
)
})
}
},
onDragEnd(dragHappened) {
if (unlockExtremums) {
const unlock = unlockExtremums
unlockExtremums = undefined
unlock()
}
if (dragHappened) {
if (tempTransaction) {
tempTransaction.commit()
}
} else {
if (tempTransaction) {
tempTransaction.discard()
}
}
tempTransaction = undefined
},
}
}, [])

View file

@ -1,10 +1,8 @@
import type {SequenceEditorPanelLayout} from '@theatre/studio/panels/SequenceEditorPanel/layout/layout'
import getStudio from '@theatre/studio/getStudio'
import type {CommitOrDiscard} from '@theatre/studio/StudioStore/StudioStore'
import useContextMenu from '@theatre/studio/uiComponents/simpleContextMenu/useContextMenu'
import useDrag from '@theatre/studio/uiComponents/useDrag'
import useRefAndState from '@theatre/studio/utils/useRefAndState'
import type {VoidFn} from '@theatre/shared/utils/types'
import {val} from '@theatre/dataverse'
import React, {useMemo, useRef, useState} from 'react'
import styled from 'styled-components'
@ -107,68 +105,62 @@ function useDragKeyframe(
propsRef.current = _props
const gestureHandlers = useMemo<Parameters<typeof useDrag>[1]>(() => {
let toUnitSpace: SequenceEditorPanelLayout['scaledSpace']['toUnitSpace']
let propsAtStartOfDrag: IProps
let tempTransaction: CommitOrDiscard | undefined
let unlockExtremums: VoidFn | undefined
return {
debugName: 'GraphEditorDotNonScalar/useDragKeyframe',
lockCursorTo: 'ew-resize',
onDragStart(event) {
setIsDragging(true)
const propsAtStartOfDrag = propsRef.current
propsAtStartOfDrag = propsRef.current
const toUnitSpace = val(
propsAtStartOfDrag.layoutP.scaledSpace.toUnitSpace,
)
toUnitSpace = val(propsAtStartOfDrag.layoutP.scaledSpace.toUnitSpace)
const unlockExtremums = propsAtStartOfDrag.extremumSpace.lock()
let tempTransaction: CommitOrDiscard | undefined
unlockExtremums = propsAtStartOfDrag.extremumSpace.lock()
},
onDrag(dx, dy) {
const original =
propsAtStartOfDrag.trackData.keyframes[propsAtStartOfDrag.index]
return {
onDrag(dx, dy) {
const original =
propsAtStartOfDrag.trackData.keyframes[propsAtStartOfDrag.index]
const deltaPos = toUnitSpace(dx)
const deltaPos = toUnitSpace(dx)
const updatedKeyframes: Keyframe[] = []
const updatedKeyframes: Keyframe[] = []
const cur: Keyframe = {
...original,
position: original.position + deltaPos,
value: original.value,
handles: [...original.handles],
const cur: Keyframe = {
...original,
position: original.position + deltaPos,
value: original.value,
handles: [...original.handles],
}
updatedKeyframes.push(cur)
tempTransaction?.discard()
tempTransaction = getStudio()!.tempTransaction(({stateEditors}) => {
stateEditors.coreByProject.historic.sheetsById.sequence.replaceKeyframes(
{
...propsAtStartOfDrag.sheetObject.address,
trackId: propsAtStartOfDrag.trackId,
keyframes: updatedKeyframes,
snappingFunction: val(
propsAtStartOfDrag.layoutP.sheet,
).getSequence().closestGridPosition,
},
)
})
},
onDragEnd(dragHappened) {
setIsDragging(false)
unlockExtremums()
if (dragHappened) {
tempTransaction?.commit()
} else {
tempTransaction?.discard()
}
},
}
updatedKeyframes.push(cur)
tempTransaction?.discard()
tempTransaction = getStudio()!.tempTransaction(({stateEditors}) => {
stateEditors.coreByProject.historic.sheetsById.sequence.replaceKeyframes(
{
...propsAtStartOfDrag.sheetObject.address,
trackId: propsAtStartOfDrag.trackId,
keyframes: updatedKeyframes,
snappingFunction: val(
propsAtStartOfDrag.layoutP.sheet,
).getSequence().closestGridPosition,
},
)
})
},
onDragEnd(dragHappened) {
setIsDragging(false)
if (unlockExtremums) {
const unlock = unlockExtremums
unlockExtremums = undefined
unlock()
}
if (dragHappened) {
tempTransaction?.commit()
} else {
tempTransaction?.discard()
}
tempTransaction = undefined
},
}
}, [])

View file

@ -1,10 +1,8 @@
import type {SequenceEditorPanelLayout} from '@theatre/studio/panels/SequenceEditorPanel/layout/layout'
import getStudio from '@theatre/studio/getStudio'
import type {CommitOrDiscard} from '@theatre/studio/StudioStore/StudioStore'
import useContextMenu from '@theatre/studio/uiComponents/simpleContextMenu/useContextMenu'
import useDrag from '@theatre/studio/uiComponents/useDrag'
import useRefAndState from '@theatre/studio/utils/useRefAndState'
import type {VoidFn} from '@theatre/shared/utils/types'
import {val} from '@theatre/dataverse'
import React, {useMemo, useRef, useState} from 'react'
import styled from 'styled-components'
@ -108,121 +106,122 @@ function useDragKeyframe(
propsRef.current = _props
const gestureHandlers = useMemo<Parameters<typeof useDrag>[1]>(() => {
let toUnitSpace: SequenceEditorPanelLayout['scaledSpace']['toUnitSpace']
let propsAtStartOfDrag: IProps
let tempTransaction: CommitOrDiscard | undefined
let verticalToExtremumSpace: SequenceEditorPanelLayout['graphEditorVerticalSpace']['toExtremumSpace']
let unlockExtremums: VoidFn | undefined
let keepSpeeds = false
return {
debugName: 'GraphEditorDotScalar/useDragKeyframe',
lockCursorTo: 'move',
onDragStart(event) {
setIsDragging(true)
keepSpeeds = !!event.altKey
const keepSpeeds = !!event.altKey
propsAtStartOfDrag = propsRef.current
const propsAtStartOfDrag = propsRef.current
toUnitSpace = val(propsAtStartOfDrag.layoutP.scaledSpace.toUnitSpace)
verticalToExtremumSpace = val(
const toUnitSpace = val(
propsAtStartOfDrag.layoutP.scaledSpace.toUnitSpace,
)
const verticalToExtremumSpace = val(
propsAtStartOfDrag.layoutP.graphEditorVerticalSpace.toExtremumSpace,
)
unlockExtremums = propsAtStartOfDrag.extremumSpace.lock()
},
onDrag(dx, dy) {
const original =
propsAtStartOfDrag.trackData.keyframes[propsAtStartOfDrag.index]
const unlockExtremums = propsAtStartOfDrag.extremumSpace.lock()
let tempTransaction: CommitOrDiscard | undefined
const deltaPos = toUnitSpace(dx)
const dyInVerticalSpace = -dy
const dYInExtremumSpace = verticalToExtremumSpace(dyInVerticalSpace)
return {
onDrag(dx, dy) {
const original =
propsAtStartOfDrag.trackData.keyframes[propsAtStartOfDrag.index]
const dYInValueSpace =
propsAtStartOfDrag.extremumSpace.deltaToValueSpace(dYInExtremumSpace)
const deltaPos = toUnitSpace(dx)
const dyInVerticalSpace = -dy
const dYInExtremumSpace = verticalToExtremumSpace(dyInVerticalSpace)
const updatedKeyframes: Keyframe[] = []
const dYInValueSpace =
propsAtStartOfDrag.extremumSpace.deltaToValueSpace(
dYInExtremumSpace,
)
const cur: Keyframe = {
...original,
position: original.position + deltaPos,
value: (original.value as number) + dYInValueSpace,
handles: [...original.handles],
}
const updatedKeyframes: Keyframe[] = []
updatedKeyframes.push(cur)
if (keepSpeeds) {
const prev =
propsAtStartOfDrag.trackData.keyframes[propsAtStartOfDrag.index - 1]
if (
prev &&
Math.abs((original.value as number) - (prev.value as number)) > 0
) {
const newPrev: Keyframe = {
...prev,
handles: [...prev.handles],
const cur: Keyframe = {
...original,
position: original.position + deltaPos,
value: (original.value as number) + dYInValueSpace,
handles: [...original.handles],
}
updatedKeyframes.push(newPrev)
newPrev.handles[3] = preserveRightHandle(
prev.handles[3],
prev.value as number,
prev.value as number,
original.value as number,
cur.value as number,
)
}
const next =
propsAtStartOfDrag.trackData.keyframes[propsAtStartOfDrag.index + 1]
if (
next &&
Math.abs((original.value as number) - (next.value as number)) > 0
) {
const newNext: Keyframe = {
...next,
handles: [...next.handles],
updatedKeyframes.push(cur)
if (keepSpeeds) {
const prev =
propsAtStartOfDrag.trackData.keyframes[
propsAtStartOfDrag.index - 1
]
if (
prev &&
Math.abs((original.value as number) - (prev.value as number)) >
0
) {
const newPrev: Keyframe = {
...prev,
handles: [...prev.handles],
}
updatedKeyframes.push(newPrev)
newPrev.handles[3] = preserveRightHandle(
prev.handles[3],
prev.value as number,
prev.value as number,
original.value as number,
cur.value as number,
)
}
const next =
propsAtStartOfDrag.trackData.keyframes[
propsAtStartOfDrag.index + 1
]
if (
next &&
Math.abs((original.value as number) - (next.value as number)) >
0
) {
const newNext: Keyframe = {
...next,
handles: [...next.handles],
}
updatedKeyframes.push(newNext)
newNext.handles[1] = preserveLeftHandle(
newNext.handles[1],
newNext.value as number,
newNext.value as number,
original.value as number,
cur.value as number,
)
}
}
updatedKeyframes.push(newNext)
newNext.handles[1] = preserveLeftHandle(
newNext.handles[1],
newNext.value as number,
newNext.value as number,
original.value as number,
cur.value as number,
)
}
}
tempTransaction?.discard()
tempTransaction = getStudio()!.tempTransaction(({stateEditors}) => {
stateEditors.coreByProject.historic.sheetsById.sequence.replaceKeyframes(
{
...propsAtStartOfDrag.sheetObject.address,
trackId: propsAtStartOfDrag.trackId,
keyframes: updatedKeyframes,
snappingFunction: val(
propsAtStartOfDrag.layoutP.sheet,
).getSequence().closestGridPosition,
},
)
})
},
onDragEnd(dragHappened) {
setIsDragging(false)
if (unlockExtremums) {
const unlock = unlockExtremums
unlockExtremums = undefined
unlock()
tempTransaction?.discard()
tempTransaction = getStudio()!.tempTransaction(({stateEditors}) => {
stateEditors.coreByProject.historic.sheetsById.sequence.replaceKeyframes(
{
...propsAtStartOfDrag.sheetObject.address,
trackId: propsAtStartOfDrag.trackId,
keyframes: updatedKeyframes,
snappingFunction: val(
propsAtStartOfDrag.layoutP.sheet,
).getSequence().closestGridPosition,
},
)
})
},
onDragEnd(dragHappened) {
setIsDragging(false)
unlockExtremums()
if (dragHappened) {
tempTransaction?.commit()
} else {
tempTransaction?.discard()
}
},
}
if (dragHappened) {
tempTransaction?.commit()
} else {
tempTransaction?.discard()
}
tempTransaction = undefined
},
}
}, [])

View file

@ -1,7 +1,7 @@
import type {Pointer} from '@theatre/dataverse'
import {prism, val} from '@theatre/dataverse'
import {usePrism, useVal} from '@theatre/react'
import type {$IntentionalAny, IRange} from '@theatre/shared/utils/types'
import type {$IntentionalAny} from '@theatre/shared/utils/types'
import {pointerEventsAutoInNormalMode} from '@theatre/studio/css'
import getStudio from '@theatre/studio/getStudio'
import type {SequenceEditorPanelLayout} from '@theatre/studio/panels/SequenceEditorPanel/layout/layout'
@ -179,81 +179,73 @@ const FocusRangeStrip: React.FC<{
const sheet = useVal(layoutP.sheet)
const gestureHandlers = useMemo((): Parameters<typeof useDrag>[1] => {
let sequence = sheet.getSequence()
let startPosBeforeDrag: number,
endPosBeforeDrag: number,
tempTransaction: CommitOrDiscard | undefined
let dragHappened = false
let existingRange: {enabled: boolean; range: IRange<number>} | undefined
let target: HTMLDivElement | undefined
let newStartPosition: number, newEndPosition: number
return {
debugName: 'FocusRangeStrip',
onDragStart(event) {
existingRange = existingRangeD.getValue()
let tempTransaction: CommitOrDiscard | undefined
let existingRange = existingRangeD.getValue()
if (!existingRange) return false
if (existingRange) {
startPosBeforeDrag = existingRange.range.start
endPosBeforeDrag = existingRange.range.end
dragHappened = false
sequence = val(layoutP.sheet).getSequence()
target = event.target as HTMLDivElement
isDraggingRef.current = true
} else {
return false
}
},
onDrag(dx) {
existingRange = existingRangeD.getValue()
if (existingRange) {
dragHappened = true
const deltaPos = scaledSpaceToUnitSpace(dx)
const startPosBeforeDrag = existingRange.range.start
const endPosBeforeDrag = existingRange.range.end
let dragHappened = false
const sequence = val(layoutP.sheet).getSequence()
isDraggingRef.current = true
const start = startPosBeforeDrag + deltaPos
let end = endPosBeforeDrag + deltaPos
return {
onDrag(dx) {
existingRange = existingRangeD.getValue()
if (existingRange) {
dragHappened = true
const deltaPos = scaledSpaceToUnitSpace(dx)
if (end < start) {
end = start
}
const start = startPosBeforeDrag + deltaPos
let end = endPosBeforeDrag + deltaPos
;[newStartPosition, newEndPosition] = clampRange(
[start, end],
[0, sequence.length],
).map((pos) => sequence.closestGridPosition(pos))
if (end < start) {
end = start
}
if (tempTransaction) {
tempTransaction.discard()
}
;[newStartPosition, newEndPosition] = clampRange(
[start, end],
[0, sequence.length],
).map((pos) => sequence.closestGridPosition(pos))
tempTransaction = getStudio().tempTransaction(({stateEditors}) => {
stateEditors.studio.ahistoric.projects.stateByProjectId.stateBySheetId.sequence.focusRange.set(
{
...sheet.address,
range: {
start: newStartPosition,
end: newEndPosition,
if (tempTransaction) {
tempTransaction.discard()
}
tempTransaction = getStudio().tempTransaction(
({stateEditors}) => {
stateEditors.studio.ahistoric.projects.stateByProjectId.stateBySheetId.sequence.focusRange.set(
{
...sheet.address,
range: {
start: newStartPosition,
end: newEndPosition,
},
enabled: existingRange?.enabled ?? true,
},
)
},
enabled: existingRange?.enabled ?? true,
},
)
})
}
},
onDragEnd() {
isDraggingRef.current = false
if (existingRange) {
if (dragHappened && tempTransaction !== undefined) {
tempTransaction.commit()
} else if (tempTransaction) {
tempTransaction.discard()
}
tempTransaction = undefined
}
if (target !== undefined) {
target = undefined
)
}
},
onDragEnd() {
isDraggingRef.current = false
if (existingRange) {
if (dragHappened && tempTransaction !== undefined) {
tempTransaction.commit()
} else if (tempTransaction) {
tempTransaction.discard()
}
}
},
}
},
lockCursorTo: 'grabbing',
}
}, [sheet, scaledSpaceToUnitSpace])

View file

@ -22,7 +22,6 @@ import {
useLockFrameStampPosition,
} from '@theatre/studio/panels/SequenceEditorPanel/FrameStampPositionProvider'
import {focusRangeStripTheme, RangeStrip} from './FocusRangeStrip'
import type Sheet from '@theatre/core/sheets/Sheet'
import DopeSnap from '@theatre/studio/panels/SequenceEditorPanel/RightOverlay/DopeSnap'
const TheDiv = styled.div<{enabled: boolean; type: 'start' | 'end'}>`
@ -172,87 +171,84 @@ const FocusRangeThumb: React.FC<{
)
const gestureHandlers = useMemo((): Parameters<typeof useDrag>[1] => {
let defaultRange: IRange
let range: IRange
let focusRangeEnabled: boolean
let posBeforeDrag: number
let tempTransaction: CommitOrDiscard | undefined
let minFocusRangeStripWidth: number
let sheet: Sheet
let scaledSpaceToUnitSpace: (s: number) => number
return {
debugName: 'FocusRangeThumb',
onDragStart() {
sheet = val(layoutP.sheet)
let tempTransaction: CommitOrDiscard | undefined
let range: IRange
const sheet = val(layoutP.sheet)
const sequence = sheet.getSequence()
defaultRange = {start: 0, end: sequence.length}
const defaultRange = {start: 0, end: sequence.length}
let existingRange = existingRangeD.getValue() || {
range: defaultRange,
enabled: false,
}
focusRangeEnabled = existingRange.enabled
const focusRangeEnabled = existingRange.enabled
posBeforeDrag = existingRange.range[thumbType]
scaledSpaceToUnitSpace = val(layoutP.scaledSpace.toUnitSpace)
minFocusRangeStripWidth = scaledSpaceToUnitSpace(
const posBeforeDrag = existingRange.range[thumbType]
const scaledSpaceToUnitSpace = val(layoutP.scaledSpace.toUnitSpace)
const minFocusRangeStripWidth = scaledSpaceToUnitSpace(
focusRangeStripTheme.rangeStripMinWidth,
)
},
onDrag(dx, _, event) {
let newPosition: number
const snapPos = DopeSnap.checkIfMouseEventSnapToPos(event, {
ignore: hitZoneNode,
})
if (snapPos != null) {
newPosition = snapPos
return {
onDrag(dx, _, event) {
let newPosition: number
const snapPos = DopeSnap.checkIfMouseEventSnapToPos(event, {
ignore: hitZoneNode,
})
if (snapPos != null) {
newPosition = snapPos
}
range = existingRangeD.getValue()?.range || defaultRange
const deltaPos = scaledSpaceToUnitSpace(dx)
const oldPosPlusDeltaPos = posBeforeDrag + deltaPos
// Make sure that the focus range has a minimal width
if (thumbType === 'start') {
// Prevent the start thumb from going below 0
newPosition = Math.max(
Math.min(
oldPosPlusDeltaPos,
range['end'] - minFocusRangeStripWidth,
),
0,
)
} else {
// Prevent the start thumb from going over the length of the sequence
newPosition = Math.min(
Math.max(
oldPosPlusDeltaPos,
range['start'] + minFocusRangeStripWidth,
),
sheet.getSequence().length,
)
}
const newPositionInFrame = sheet
.getSequence()
.closestGridPosition(newPosition)
if (tempTransaction !== undefined) {
tempTransaction.discard()
}
tempTransaction = getStudio().tempTransaction(({stateEditors}) => {
stateEditors.studio.ahistoric.projects.stateByProjectId.stateBySheetId.sequence.focusRange.set(
{
...sheet.address,
range: {...range, [thumbType]: newPositionInFrame},
enabled: focusRangeEnabled,
},
)
})
},
onDragEnd(dragHappened) {
if (dragHappened) tempTransaction?.commit()
else tempTransaction?.discard()
},
}
range = existingRangeD.getValue()?.range || defaultRange
const deltaPos = scaledSpaceToUnitSpace(dx)
const oldPosPlusDeltaPos = posBeforeDrag + deltaPos
// Make sure that the focus range has a minimal width
if (thumbType === 'start') {
// Prevent the start thumb from going below 0
newPosition = Math.max(
Math.min(
oldPosPlusDeltaPos,
range['end'] - minFocusRangeStripWidth,
),
0,
)
} else {
// Prevent the start thumb from going over the length of the sequence
newPosition = Math.min(
Math.max(
oldPosPlusDeltaPos,
range['start'] + minFocusRangeStripWidth,
),
sheet.getSequence().length,
)
}
const newPositionInFrame = sheet
.getSequence()
.closestGridPosition(newPosition)
if (tempTransaction !== undefined) {
tempTransaction.discard()
}
tempTransaction = getStudio().tempTransaction(({stateEditors}) => {
stateEditors.studio.ahistoric.projects.stateByProjectId.stateBySheetId.sequence.focusRange.set(
{
...sheet.address,
range: {...range, [thumbType]: newPositionInFrame},
enabled: focusRangeEnabled,
},
)
})
},
onDragEnd(dragHappened) {
if (dragHappened) tempTransaction?.commit()
else tempTransaction?.discard()
},
}
}, [layoutP])

View file

@ -1,9 +1,7 @@
import type Sequence from '@theatre/core/sequences/Sequence'
import type Sheet from '@theatre/core/sheets/Sheet'
import type {Pointer} from '@theatre/dataverse'
import {prism, val} from '@theatre/dataverse'
import {usePrism} from '@theatre/react'
import type {$IntentionalAny, VoidFn} from '@theatre/shared/utils/types'
import type {$IntentionalAny} from '@theatre/shared/utils/types'
import getStudio from '@theatre/studio/getStudio'
import {
panelDimsToPanelPosition,
@ -102,152 +100,150 @@ function usePanelDragZoneGestureHandlers(
const focusRangeCreationGestureHandlers = (): Parameters<
typeof useDrag
>[1] => {
let startPosInUnitSpace: number,
tempTransaction: CommitOrDiscard | undefined
let clippedSpaceToUnitSpace: (s: number) => number
let scaledSpaceToUnitSpace: (s: number) => number
let sequence: Sequence
let sheet: Sheet
let minFocusRangeStripWidth: number
return {
debugName: 'FocusRangeZone/focusRangeCreationGestureHandlers',
onDragStart(event) {
clippedSpaceToUnitSpace = val(layoutP.clippedSpace.toUnitSpace)
scaledSpaceToUnitSpace = val(layoutP.scaledSpace.toUnitSpace)
sheet = val(layoutP.sheet)
sequence = sheet.getSequence()
let tempTransaction: CommitOrDiscard | undefined
const clippedSpaceToUnitSpace = val(layoutP.clippedSpace.toUnitSpace)
const scaledSpaceToUnitSpace = val(layoutP.scaledSpace.toUnitSpace)
const sheet = val(layoutP.sheet)
const sequence = sheet.getSequence()
const targetElement: HTMLElement = event.target as HTMLElement
const rect = targetElement!.getBoundingClientRect()
startPosInUnitSpace = clippedSpaceToUnitSpace(
const startPosInUnitSpace = clippedSpaceToUnitSpace(
event.clientX - rect.left,
)
minFocusRangeStripWidth = scaledSpaceToUnitSpace(
const minFocusRangeStripWidth = scaledSpaceToUnitSpace(
focusRangeStripTheme.rangeStripMinWidth,
)
},
onDrag(dx) {
const deltaPos = scaledSpaceToUnitSpace(dx)
let start = startPosInUnitSpace
let end = startPosInUnitSpace + deltaPos
return {
onDrag(dx) {
const deltaPos = scaledSpaceToUnitSpace(dx)
;[start, end] = [
clamp(start, 0, sequence.length),
clamp(end, 0, sequence.length),
].map((pos) => sequence.closestGridPosition(pos))
let start = startPosInUnitSpace
let end = startPosInUnitSpace + deltaPos
if (end < start) {
;[start, end] = [
Math.max(Math.min(end, start - minFocusRangeStripWidth), 0),
start,
]
} else if (dx > 0) {
end = Math.min(
Math.max(end, start + minFocusRangeStripWidth),
sequence.length,
)
;[start, end] = [
clamp(start, 0, sequence.length),
clamp(end, 0, sequence.length),
].map((pos) => sequence.closestGridPosition(pos))
if (end < start) {
;[start, end] = [
Math.max(Math.min(end, start - minFocusRangeStripWidth), 0),
start,
]
} else if (dx > 0) {
end = Math.min(
Math.max(end, start + minFocusRangeStripWidth),
sequence.length,
)
}
if (tempTransaction) {
tempTransaction.discard()
}
tempTransaction = getStudio().tempTransaction(
({stateEditors}) => {
stateEditors.studio.ahistoric.projects.stateByProjectId.stateBySheetId.sequence.focusRange.set(
{
...sheet.address,
range: {start, end},
enabled: true,
},
)
},
)
},
onDragEnd(dragHappened) {
if (dragHappened && tempTransaction !== undefined) {
tempTransaction.commit()
} else if (tempTransaction) {
tempTransaction.discard()
}
},
}
if (tempTransaction) {
tempTransaction.discard()
}
tempTransaction = getStudio().tempTransaction(({stateEditors}) => {
stateEditors.studio.ahistoric.projects.stateByProjectId.stateBySheetId.sequence.focusRange.set(
{
...sheet.address,
range: {start, end},
enabled: true,
},
)
})
},
onDragEnd(dragHappened) {
if (dragHappened && tempTransaction !== undefined) {
tempTransaction.commit()
} else if (tempTransaction) {
tempTransaction.discard()
}
tempTransaction = undefined
},
lockCursorTo: 'ew-resize',
}
}
const panelMoveGestureHandlers = (): Parameters<typeof useDrag>[1] => {
let stuffBeforeDrag = panelStuffRef.current
let tempTransaction: CommitOrDiscard | undefined
let unlock: VoidFn | undefined
return {
debugName: 'FocusRangeZone/panelMoveGestureHandlers',
onDragStart() {
stuffBeforeDrag = panelStuffRef.current
if (unlock) {
const u = unlock
unlock = undefined
u()
}
unlock = panelStuffRef.current.addBoundsHighlightLock()
},
onDrag(dx, dy) {
const newDims: typeof panelStuffRef.current['dims'] = {
...stuffBeforeDrag.dims,
top: stuffBeforeDrag.dims.top + dy,
left: stuffBeforeDrag.dims.left + dx,
}
const position = panelDimsToPanelPosition(newDims, {
width: window.innerWidth,
height: window.innerHeight,
})
let tempTransaction: CommitOrDiscard | undefined
const stuffBeforeDrag = panelStuffRef.current
tempTransaction?.discard()
tempTransaction = getStudio()!.tempTransaction(({stateEditors}) => {
stateEditors.studio.historic.panelPositions.setPanelPosition({
position,
panelId: stuffBeforeDrag.panelId,
})
})
},
onDragEnd(dragHappened) {
if (unlock) {
const u = unlock
unlock = undefined
u()
const unlock = panelStuffRef.current.addBoundsHighlightLock()
return {
onDrag(dx, dy) {
const newDims: typeof panelStuffRef.current['dims'] = {
...stuffBeforeDrag.dims,
top: stuffBeforeDrag.dims.top + dy,
left: stuffBeforeDrag.dims.left + dx,
}
const position = panelDimsToPanelPosition(newDims, {
width: window.innerWidth,
height: window.innerHeight,
})
tempTransaction?.discard()
tempTransaction = getStudio()!.tempTransaction(
({stateEditors}) => {
stateEditors.studio.historic.panelPositions.setPanelPosition({
position,
panelId: stuffBeforeDrag.panelId,
})
},
)
},
onDragEnd(dragHappened) {
unlock()
if (dragHappened) {
tempTransaction?.commit()
} else {
tempTransaction?.discard()
}
},
}
if (dragHappened) {
tempTransaction?.commit()
} else {
tempTransaction?.discard()
}
tempTransaction = undefined
},
lockCursorTo: 'move',
}
}
let currentGestureHandlers: undefined | Parameters<typeof useDrag>[1]
return {
debugName: 'FocusRangeZone',
onDragStart(event) {
if (event.shiftKey) {
setMode('creating')
currentGestureHandlers = focusRangeCreationGestureHandlers()
} else {
setMode('moving-panel')
currentGestureHandlers = panelMoveGestureHandlers()
const [_mode, currentGestureHandlers] = event.shiftKey
? [
'creating' as 'creating',
focusRangeCreationGestureHandlers().onDragStart(event),
]
: [
'moving-panel' as 'moving-panel',
panelMoveGestureHandlers().onDragStart(event),
]
setMode(_mode)
if (currentGestureHandlers === false) return false
return {
onDrag(dx, dy, event, ddx, ddy) {
currentGestureHandlers.onDrag(dx, dy, event, ddx, ddy)
},
onDragEnd(dragHappened) {
setMode('none')
currentGestureHandlers.onDragEnd?.(dragHappened)
},
}
currentGestureHandlers.onDragStart!(event)
},
onDrag(dx, dy, event) {
currentGestureHandlers!.onDrag(dx, dy, event)
},
onDragEnd(dragHappened) {
setMode('none')
currentGestureHandlers!.onDragEnd!(dragHappened)
},
}
}, [layoutP, panelStuffRef])

View file

@ -247,46 +247,44 @@ function useDragMarker(
propsRef.current = props
const useDragOpts = useMemo<UseDragOpts>(() => {
let toUnitSpace: SequenceEditorPanelLayout['scaledSpace']['toUnitSpace']
let tempTransaction: CommitOrDiscard | undefined
let markerAtStartOfDrag: StudioHistoricStateSequenceEditorMarker
return {
debugName: `MarkerDot/useDragMarker (${props.marker.id})`,
onDragStart(_event) {
markerAtStartOfDrag = propsRef.current.marker
toUnitSpace = val(props.layoutP.scaledSpace.toUnitSpace)
},
onDrag(dx, _dy, event) {
const original = markerAtStartOfDrag
const newPosition = Math.max(
// check if our event hoversover a [data-pos] element
DopeSnap.checkIfMouseEventSnapToPos(event, {
ignore: node,
}) ??
// if we don't find snapping target, check the distance dragged + original position
original.position + toUnitSpace(dx),
// sanitize to minimum of zero
0,
)
const markerAtStartOfDrag = propsRef.current.marker
const toUnitSpace = val(props.layoutP.scaledSpace.toUnitSpace)
let tempTransaction: CommitOrDiscard | undefined
tempTransaction?.discard()
tempTransaction = getStudio()!.tempTransaction(({stateEditors}) => {
stateEditors.studio.historic.projects.stateByProjectId.stateBySheetId.sequenceEditor.replaceMarkers(
{
sheetAddress: val(props.layoutP.sheet.address),
markers: [{...original, position: newPosition}],
snappingFunction: val(props.layoutP.sheet).getSequence()
.closestGridPosition,
},
)
})
},
onDragEnd(dragHappened) {
if (dragHappened) tempTransaction?.commit()
else tempTransaction?.discard()
tempTransaction = undefined
return {
onDrag(dx, _dy, event) {
const original = markerAtStartOfDrag
const newPosition = Math.max(
// check if our event hoversover a [data-pos] element
DopeSnap.checkIfMouseEventSnapToPos(event, {
ignore: node,
}) ??
// if we don't find snapping target, check the distance dragged + original position
original.position + toUnitSpace(dx),
// sanitize to minimum of zero
0,
)
tempTransaction?.discard()
tempTransaction = getStudio()!.tempTransaction(({stateEditors}) => {
stateEditors.studio.historic.projects.stateByProjectId.stateBySheetId.sequenceEditor.replaceMarkers(
{
sheetAddress: val(props.layoutP.sheet.address),
markers: [{...original, position: newPosition}],
snappingFunction: val(props.layoutP.sheet).getSequence()
.closestGridPosition,
},
)
})
},
onDragEnd(dragHappened) {
if (dragHappened) tempTransaction?.commit()
else tempTransaction?.discard()
},
}
},
}
}, [])

View file

@ -1,4 +1,3 @@
import type Sequence from '@theatre/core/sequences/Sequence'
import type {SequenceEditorPanelLayout} from '@theatre/studio/panels/SequenceEditorPanel/layout/layout'
import RoomToClick from '@theatre/studio/uiComponents/RoomToClick'
import useDrag from '@theatre/studio/uiComponents/useDrag'
@ -203,32 +202,31 @@ const Playhead: React.FC<{layoutP: Pointer<SequenceEditorPanelLayout>}> = ({
)
const gestureHandlers = useMemo((): Parameters<typeof useDrag>[1] => {
const setIsSeeking = val(layoutP.seeker.setIsSeeking)
let posBeforeSeek = 0
let sequence: Sequence
let scaledSpaceToUnitSpace: typeof layoutP.scaledSpace.toUnitSpace.$$__pointer_type
return {
debugName: 'Playhead',
onDragStart() {
sequence = val(layoutP.sheet).getSequence()
posBeforeSeek = sequence.position
scaledSpaceToUnitSpace = val(layoutP.scaledSpace.toUnitSpace)
setIsSeeking(true)
},
onDrag(dx, _, event) {
const deltaPos = scaledSpaceToUnitSpace(dx)
const setIsSeeking = val(layoutP.seeker.setIsSeeking)
sequence.position =
DopeSnap.checkIfMouseEventSnapToPos(event, {
ignore: thumbNode,
}) ??
// unsnapped
clamp(posBeforeSeek + deltaPos, 0, sequence.length)
},
onDragEnd() {
setIsSeeking(false)
const sequence = val(layoutP.sheet).getSequence()
const posBeforeSeek = sequence.position
const scaledSpaceToUnitSpace = val(layoutP.scaledSpace.toUnitSpace)
setIsSeeking(true)
return {
onDrag(dx, _, event) {
const deltaPos = scaledSpaceToUnitSpace(dx)
sequence.position =
DopeSnap.checkIfMouseEventSnapToPos(event, {
ignore: thumbNode,
}) ??
// unsnapped
clamp(posBeforeSeek + deltaPos, 0, sequence.length)
},
onDragEnd() {
setIsSeeking(false)
},
}
},
}
}, [])

View file

@ -4,6 +4,28 @@ import useRefAndState from '@theatre/studio/utils/useRefAndState'
import {useCssCursorLock} from './PointerEventsHandler'
import type {CapturedPointer} from '@theatre/studio/UIRoot/PointerCapturing'
import {usePointerCapturing} from '@theatre/studio/UIRoot/PointerCapturing'
import noop from '@theatre/shared/utils/noop'
export enum MouseButton {
Left = 0,
Middle = 1,
// Not including Right because it _might_ interfere with chord clicking.
// So we'll wait for chord-clicking to land before exploring right-button gestures
}
/**
* dx, dy: delta x/y from the start of the drag
*/
type OnDragCallback = (
// deltaX/Y are counted from the start of the drag
deltaX: number,
deltaY: number,
event: MouseEvent,
dxFromLastEvent: number,
dyFromLastEvent: number,
) => void
type OnDragEndCallback = (dragHappened: boolean) => void
export type UseDragOpts = {
/**
@ -35,22 +57,23 @@ export type UseDragOpts = {
* onDragStart can be undefined, in which case, we always handle useDrag,
* but when defined, we can allow the handler to return false to indicate ignore this dragging
*/
onDragStart?: (event: MouseEvent) => void | false
/**
* Called at the end of the drag gesture.
* `dragHappened` will be `true` if the user actually moved the pointer
* (if onDrag isn't called, then this will be false becuase the user hasn't moved the pointer)
*/
onDragEnd?: (dragHappened: boolean) => void
/**
* This will be called 0 times if the gesture ends up being a click,
* or 1 or more times if it ends up being a drag gesture.
*
* `dx`: the delta x
* `dy`: the delta y
* `event`: the mouse event
*/
onDrag: (dx: number, dy: number, event: MouseEvent) => void
onDragStart: (event: MouseEvent) =>
| false
| {
/**
* Called at the end of the drag gesture.
* `dragHappened` will be `true` if the user actually moved the pointer
* (if onDrag isn't called, then this will be false becuase the user hasn't moved the pointer)
*/
onDragEnd?: OnDragEndCallback
onDrag: OnDragCallback
}
// which mouse button to use the drag event
buttons?:
| [MouseButton]
| [MouseButton, MouseButton]
| [MouseButton | MouseButton | MouseButton]
}
export default function useDrag(
@ -80,9 +103,15 @@ export default function useDrag(
}
}>({dragHappened: false, startPos: {x: 0, y: 0}})
const callbacksRef = useRef<{
onDrag: OnDragCallback
onDragEnd: OnDragEndCallback
}>({onDrag: noop, onDragEnd: noop})
const capturedPointerRef = useRef<undefined | CapturedPointer>()
useLayoutEffect(() => {
if (!target) return
let lastDeltas = [0, 0]
const getDistances = (event: MouseEvent): [number, number] => {
const {startPos} = stateRef.current
@ -94,14 +123,26 @@ export default function useDrag(
modeRef.current = 'dragging'
const deltas = getDistances(event)
optsRef.current.onDrag(deltas[0], deltas[1], event)
const [deltaFromLastX, deltaFromLastY] = [
deltas[0] - lastDeltas[0],
deltas[1] - lastDeltas[1],
]
lastDeltas = deltas
callbacksRef.current.onDrag(
deltas[0],
deltas[1],
event,
deltaFromLastX,
deltaFromLastY,
)
}
const dragEndHandler = () => {
removeDragListeners()
modeRef.current = 'notDragging'
optsRef.current.onDragEnd?.(stateRef.current.dragHappened)
callbacksRef.current.onDragEnd(stateRef.current.dragHappened)
}
const addDragListeners = () => {
@ -136,13 +177,18 @@ export default function useDrag(
const opts = optsRef.current
if (opts.disabled === true) return
if (event.button !== 0) return
const acceptedButtons: MouseButton[] = opts.buttons ?? [MouseButton.Left]
// onDragStart can be undefined, in which case, we always handle useDrag,
// but when defined, we can allow the handler to return false to indicate ignore this dragging
if (opts.onDragStart != null) {
const shouldIgnore = opts.onDragStart(event) === false
if (shouldIgnore) return
if (!acceptedButtons.includes(event.button)) return
const returnOfOnDragStart = opts.onDragStart(event)
if (returnOfOnDragStart === false) {
// we should ignore the gesture
return
} else {
callbacksRef.current.onDrag = returnOfOnDragStart.onDrag
callbacksRef.current.onDragEnd = returnOfOnDragStart.onDragEnd ?? noop
}
// need to capture pointer after we know the provided handler wants to handle drag start
@ -175,7 +221,7 @@ export default function useDrag(
target.removeEventListener('click', preventUnwantedClick as $FixMe)
if (modeRef.current !== 'notDragging') {
optsRef.current.onDragEnd?.(modeRef.current === 'dragging')
callbacksRef.current.onDragEnd?.(modeRef.current === 'dragging')
}
modeRef.current = 'notDragging'
}