From 4b4fe858f945d202f0db8465b64970bbfe5df2c6 Mon Sep 17 00:00:00 2001 From: Colin Duffy Date: Mon, 23 Jan 2023 13:59:23 -0800 Subject: [PATCH] feat(289): Labeled markers (#362) --- .../RightOverlay/{ => Markers}/MarkerDot.tsx | 28 +++++- .../Markers/MarkerEditorPopover.tsx | 99 +++++++++++++++++++ .../RightOverlay/{ => Markers}/Markers.tsx | 0 .../RightOverlay/RightOverlay.tsx | 2 +- theatre/studio/src/store/stateEditors.ts | 10 ++ theatre/studio/src/store/types/historic.ts | 1 + 6 files changed, 137 insertions(+), 3 deletions(-) rename theatre/studio/src/panels/SequenceEditorPanel/RightOverlay/{ => Markers}/MarkerDot.tsx (90%) create mode 100644 theatre/studio/src/panels/SequenceEditorPanel/RightOverlay/Markers/MarkerEditorPopover.tsx rename theatre/studio/src/panels/SequenceEditorPanel/RightOverlay/{ => Markers}/Markers.tsx (100%) diff --git a/theatre/studio/src/panels/SequenceEditorPanel/RightOverlay/MarkerDot.tsx b/theatre/studio/src/panels/SequenceEditorPanel/RightOverlay/Markers/MarkerDot.tsx similarity index 90% rename from theatre/studio/src/panels/SequenceEditorPanel/RightOverlay/MarkerDot.tsx rename to theatre/studio/src/panels/SequenceEditorPanel/RightOverlay/Markers/MarkerDot.tsx index 55fb237..f99fa88 100644 --- a/theatre/studio/src/panels/SequenceEditorPanel/RightOverlay/MarkerDot.tsx +++ b/theatre/studio/src/panels/SequenceEditorPanel/RightOverlay/Markers/MarkerDot.tsx @@ -19,13 +19,16 @@ import type {UseDragOpts} from '@theatre/studio/uiComponents/useDrag' import type {CommitOrDiscard} from '@theatre/studio/StudioStore/StudioStore' import type {StudioHistoricStateSequenceEditorMarker} from '@theatre/studio/store/types' import {zIndexes} from '@theatre/studio/panels/SequenceEditorPanel/SequenceEditorPanel' -import DopeSnap from './DopeSnap' +import DopeSnap from '@theatre/studio/panels/SequenceEditorPanel/RightOverlay/DopeSnap' import {absoluteDims} from '@theatre/studio/utils/absoluteDims' -import {DopeSnapHitZoneUI} from './DopeSnapHitZoneUI' +import {DopeSnapHitZoneUI} from '@theatre/studio/panels/SequenceEditorPanel/RightOverlay/DopeSnapHitZoneUI' import { snapToAll, snapToNone, } from '@theatre/studio/panels/SequenceEditorPanel/DopeSheet/Right/KeyframeSnapTarget' +import usePopover from '@theatre/studio/uiComponents/Popover/usePopover' +import BasicPopover from '@theatre/studio/uiComponents/Popover/BasicPopover' +import MarkerEditorPopover from './MarkerEditorPopover' const MARKER_SIZE_W_PX = 12 const MARKER_SIZE_H_PX = 12 @@ -164,11 +167,32 @@ const MarkerDotVisible: React.VFC = ({ marker, }) + const { + node: popoverNode, + toggle: togglePopover, + close: closePopover, + } = usePopover({debugName: 'MarkerPopover'}, () => { + return ( + + + + ) + }) + return ( <> {contextMenu} + {popoverNode} { + togglePopover(e, markRef.current!) + }} {...DopeSnapHitZoneUI.reactProps({ isDragging, position: marker.position, diff --git a/theatre/studio/src/panels/SequenceEditorPanel/RightOverlay/Markers/MarkerEditorPopover.tsx b/theatre/studio/src/panels/SequenceEditorPanel/RightOverlay/Markers/MarkerEditorPopover.tsx new file mode 100644 index 0000000..2310f25 --- /dev/null +++ b/theatre/studio/src/panels/SequenceEditorPanel/RightOverlay/Markers/MarkerEditorPopover.tsx @@ -0,0 +1,99 @@ +import type {Pointer} from '@theatre/dataverse' +import React, {useLayoutEffect, useMemo, useRef} from 'react' +import styled from 'styled-components' +import type {SequenceEditorPanelLayout} from '@theatre/studio/panels/SequenceEditorPanel/layout/layout' +import {useVal} from '@theatre/react' +import getStudio from '@theatre/studio/getStudio' +import type {BasicNumberInputNudgeFn} from '@theatre/studio/uiComponents/form/BasicNumberInput' +import type {CommitOrDiscard} from '@theatre/studio/StudioStore/StudioStore' +import {propNameTextCSS} from '@theatre/studio/propEditors/utils/propNameTextCSS' +import type {StudioHistoricStateSequenceEditorMarker} from '@theatre/studio/store/types/historic' +import BasicStringInput from '@theatre/studio/uiComponents/form/BasicStringInput' + +const Container = styled.div` + display: flex; + gap: 8px; + /* padding: 4px 8px; */ + height: 28px; + align-items: center; +` + +const Label = styled.div` + ${propNameTextCSS}; + white-space: nowrap; +` + +const nudge: BasicNumberInputNudgeFn = ({deltaX}) => deltaX * 0.25 + +const MarkerEditorPopover: React.FC<{ + layoutP: Pointer + marker: StudioHistoricStateSequenceEditorMarker + /** + * Called when user hits enter/escape + */ + onRequestClose: (reason: string) => void +}> = ({layoutP, marker}) => { + const sheet = useVal(layoutP.sheet) + + const fns = useMemo(() => { + let tempTransaction: CommitOrDiscard | undefined + + return { + temporarilySetValue(newLabel: string): void { + if (tempTransaction) { + tempTransaction.discard() + tempTransaction = undefined + } + tempTransaction = getStudio()!.tempTransaction(({stateEditors}) => { + stateEditors.studio.historic.projects.stateByProjectId.stateBySheetId.sequenceEditor.updateMarker( + { + sheetAddress: sheet.address, + markerId: marker.id, + label: newLabel, + }, + ) + }) + }, + discardTemporaryValue(): void { + if (tempTransaction) { + tempTransaction.discard() + tempTransaction = undefined + } + }, + permanentlySetValue(newLabel: string): void { + if (tempTransaction) { + tempTransaction.discard() + tempTransaction = undefined + } + getStudio()!.transaction(({stateEditors}) => { + stateEditors.studio.historic.projects.stateByProjectId.stateBySheetId.sequenceEditor.updateMarker( + { + sheetAddress: sheet.address, + markerId: marker.id, + label: newLabel, + }, + ) + }) + }, + } + }, [layoutP, sheet]) + + const inputRef = useRef(null) + useLayoutEffect(() => { + inputRef.current!.focus() + }, []) + + return ( + + {/* */} + true} + inputRef={inputRef} + /> + + ) +} + +export default MarkerEditorPopover diff --git a/theatre/studio/src/panels/SequenceEditorPanel/RightOverlay/Markers.tsx b/theatre/studio/src/panels/SequenceEditorPanel/RightOverlay/Markers/Markers.tsx similarity index 100% rename from theatre/studio/src/panels/SequenceEditorPanel/RightOverlay/Markers.tsx rename to theatre/studio/src/panels/SequenceEditorPanel/RightOverlay/Markers/Markers.tsx diff --git a/theatre/studio/src/panels/SequenceEditorPanel/RightOverlay/RightOverlay.tsx b/theatre/studio/src/panels/SequenceEditorPanel/RightOverlay/RightOverlay.tsx index 8ff28bc..ac7133b 100644 --- a/theatre/studio/src/panels/SequenceEditorPanel/RightOverlay/RightOverlay.tsx +++ b/theatre/studio/src/panels/SequenceEditorPanel/RightOverlay/RightOverlay.tsx @@ -11,7 +11,7 @@ import HorizontalScrollbar from './HorizontalScrollbar' import Playhead from './Playhead' import TopStrip from './TopStrip' import FocusRangeCurtains from '@theatre/studio/panels/SequenceEditorPanel/DopeSheet/Right/FocusRangeCurtains' -import Markers from './Markers' +import Markers from './Markers/Markers' const Container = styled.div` position: absolute; diff --git a/theatre/studio/src/store/stateEditors.ts b/theatre/studio/src/store/stateEditors.ts index afa3a4f..d14c6b7 100644 --- a/theatre/studio/src/store/stateEditors.ts +++ b/theatre/studio/src/store/stateEditors.ts @@ -338,6 +338,16 @@ namespace stateEditors { pointableSetUtil.remove(currentMarkerSet, options.markerId), ) } + + export function updateMarker(options: { + sheetAddress: SheetAddress + markerId: SequenceMarkerId + label: string + }) { + const currentMarkerSet = _ensureMarkers(options.sheetAddress) + const marker = currentMarkerSet.byId[options.markerId] + if (marker !== undefined) marker.label = options.label + } } } } diff --git a/theatre/studio/src/store/types/historic.ts b/theatre/studio/src/store/types/historic.ts index 46e7f5b..09b7f1f 100644 --- a/theatre/studio/src/store/types/historic.ts +++ b/theatre/studio/src/store/types/historic.ts @@ -78,6 +78,7 @@ export type PaneInstanceDescriptor = { */ export type StudioHistoricStateSequenceEditorMarker = { id: SequenceMarkerId + label?: string /** * The position this marker takes in the sequence. *