LengthIndicator is now draggable
This commit is contained in:
parent
acf0283449
commit
c6c37992ac
1 changed files with 98 additions and 20 deletions
|
@ -1,24 +1,20 @@
|
||||||
import {usePrism} from '@theatre/dataverse-react'
|
import {usePrism} from '@theatre/dataverse-react'
|
||||||
import type {Pointer} from '@theatre/dataverse'
|
import type {Pointer} from '@theatre/dataverse'
|
||||||
|
import {Box} from '@theatre/dataverse'
|
||||||
import {val} from '@theatre/dataverse'
|
import {val} from '@theatre/dataverse'
|
||||||
import React from 'react'
|
import React, {useMemo, useRef} from 'react'
|
||||||
import styled from 'styled-components'
|
import styled from 'styled-components'
|
||||||
import type {SequenceEditorPanelLayout} from '@theatre/studio/panels/SequenceEditorPanel/layout/layout'
|
import type {SequenceEditorPanelLayout} from '@theatre/studio/panels/SequenceEditorPanel/layout/layout'
|
||||||
import {zIndexes} from '@theatre/studio/panels/SequenceEditorPanel/SequenceEditorPanel'
|
import {zIndexes} from '@theatre/studio/panels/SequenceEditorPanel/SequenceEditorPanel'
|
||||||
import {topStripHeight} from '@theatre/studio/panels/SequenceEditorPanel/RightOverlay/TopStrip'
|
import {topStripHeight} from '@theatre/studio/panels/SequenceEditorPanel/RightOverlay/TopStrip'
|
||||||
|
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'
|
||||||
|
|
||||||
const coverWidth = 1000
|
const coverWidth = 1000
|
||||||
|
|
||||||
const Cover = styled.div`
|
|
||||||
position: absolute;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
background-color: rgb(23 23 23 / 43%);
|
|
||||||
width: ${coverWidth}px;
|
|
||||||
z-index: ${() => zIndexes.lengthIndicatorCover};
|
|
||||||
transform-origin: left top;
|
|
||||||
`
|
|
||||||
|
|
||||||
const Strip = styled.div`
|
const Strip = styled.div`
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 0;
|
top: 0;
|
||||||
|
@ -26,7 +22,7 @@ const Strip = styled.div`
|
||||||
width: 4px;
|
width: 4px;
|
||||||
z-index: ${() => zIndexes.lengthIndicatorStrip};
|
z-index: ${() => zIndexes.lengthIndicatorStrip};
|
||||||
pointer-events: auto;
|
pointer-events: auto;
|
||||||
cursor: pointer;
|
cursor: ew-resize;
|
||||||
|
|
||||||
&:after {
|
&:after {
|
||||||
display: block;
|
display: block;
|
||||||
|
@ -39,7 +35,8 @@ const Strip = styled.div`
|
||||||
background-color: #000000a6;
|
background-color: #000000a6;
|
||||||
}
|
}
|
||||||
|
|
||||||
&:hover:after {
|
&:hover:after,
|
||||||
|
&.dragging:after {
|
||||||
background-color: #000000;
|
background-color: #000000;
|
||||||
}
|
}
|
||||||
`
|
`
|
||||||
|
@ -49,23 +46,43 @@ const Info = styled.div`
|
||||||
top: ${topStripHeight + 4}px;
|
top: ${topStripHeight + 4}px;
|
||||||
font-size: 10px;
|
font-size: 10px;
|
||||||
left: 4px;
|
left: 4px;
|
||||||
color: gray;
|
color: #eee;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
display: none;
|
display: none;
|
||||||
|
|
||||||
${Strip}:hover & {
|
${Strip}:hover &, ${Strip}.dragging & {
|
||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
`
|
`
|
||||||
|
|
||||||
const LengthIndicator: React.FC<{
|
const Cover = styled.div`
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
background-color: rgb(23 23 23 / 43%);
|
||||||
|
width: ${coverWidth}px;
|
||||||
|
z-index: ${() => zIndexes.lengthIndicatorCover};
|
||||||
|
transform-origin: left top;
|
||||||
|
|
||||||
|
${Strip}:hover ~ &, ${Strip}.dragging ~ & {
|
||||||
|
background-color: rgb(23 23 23 / 60%);
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
|
type IProps = {
|
||||||
layoutP: Pointer<SequenceEditorPanelLayout>
|
layoutP: Pointer<SequenceEditorPanelLayout>
|
||||||
}> = ({layoutP}) => {
|
}
|
||||||
|
|
||||||
|
const LengthIndicator: React.FC<IProps> = ({layoutP}) => {
|
||||||
|
const [stripRef, stripNode] = useRefAndState<HTMLDivElement | null>(null)
|
||||||
|
const [isDraggingD] = useDragStrip(stripNode, {layoutP})
|
||||||
|
|
||||||
return usePrism(() => {
|
return usePrism(() => {
|
||||||
const sheet = val(layoutP.sheet)
|
const sheet = val(layoutP.sheet)
|
||||||
const height = val(layoutP.rightDims.height)
|
const height = val(layoutP.rightDims.height)
|
||||||
|
|
||||||
const sequenceLength = sheet.getSequence().length
|
const sequence = sheet.getSequence()
|
||||||
|
const sequenceLength = sequence.length
|
||||||
const startInUnitSpace = sequenceLength
|
const startInUnitSpace = sequenceLength
|
||||||
|
|
||||||
let startX = val(layoutP.clippedSpace.fromUnitSpace)(startInUnitSpace)
|
let startX = val(layoutP.clippedSpace.fromUnitSpace)(startInUnitSpace)
|
||||||
|
@ -86,12 +103,17 @@ const LengthIndicator: React.FC<{
|
||||||
<>
|
<>
|
||||||
<Strip
|
<Strip
|
||||||
title="Change Sequence Length"
|
title="Change Sequence Length"
|
||||||
|
ref={stripRef}
|
||||||
style={{
|
style={{
|
||||||
height: height + 'px',
|
height: height + 'px',
|
||||||
transform: `translateX(${translateX === 0 ? -1000 : translateX}px)`,
|
transform: `translateX(${translateX === 0 ? -1000 : translateX}px)`,
|
||||||
}}
|
}}
|
||||||
|
className={val(isDraggingD) ? 'dragging' : ''}
|
||||||
>
|
>
|
||||||
<Info>Sequence Length: {sequenceLength}</Info>
|
<Info>
|
||||||
|
sequence.length:{' '}
|
||||||
|
{sequence.positionFormatter.formatForPlayhead(sequenceLength)}
|
||||||
|
</Info>
|
||||||
</Strip>
|
</Strip>
|
||||||
<Cover
|
<Cover
|
||||||
title="Length"
|
title="Length"
|
||||||
|
@ -110,7 +132,63 @@ const LengthIndicator: React.FC<{
|
||||||
/>
|
/>
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
}, [layoutP])
|
}, [layoutP, stripRef, isDraggingD])
|
||||||
|
}
|
||||||
|
|
||||||
|
function useDragStrip(node: HTMLDivElement | null, props: IProps) {
|
||||||
|
const propsRef = useRef(props)
|
||||||
|
propsRef.current = props
|
||||||
|
const isDragging = useMemo(() => new Box(false), [])
|
||||||
|
|
||||||
|
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 {
|
||||||
|
lockCursorTo: 'ew-resize',
|
||||||
|
onDragStart(event) {
|
||||||
|
propsAtStartOfDrag = propsRef.current
|
||||||
|
sheet = val(propsRef.current.layoutP.sheet)
|
||||||
|
initialLength = sheet.getSequence().length
|
||||||
|
|
||||||
|
toUnitSpace = val(propsAtStartOfDrag.layoutP.scaledSpace.toUnitSpace)
|
||||||
|
isDragging.set(true)
|
||||||
|
},
|
||||||
|
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) {
|
||||||
|
isDragging.set(false)
|
||||||
|
if (dragHappened) {
|
||||||
|
if (tempTransaction) {
|
||||||
|
tempTransaction.commit()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (tempTransaction) {
|
||||||
|
tempTransaction.discard()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
tempTransaction = undefined
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
useDrag(node, gestureHandlers)
|
||||||
|
|
||||||
|
return [isDragging.derivation]
|
||||||
}
|
}
|
||||||
|
|
||||||
export default LengthIndicator
|
export default LengthIndicator
|
||||||
|
|
Loading…
Reference in a new issue