diff --git a/theatre/studio/src/panels/SequenceEditorPanel/DopeSheet/Right/AggregatedKeyframeTrack/AggregateKeyframeEditor/AggregateKeyframeDot.tsx b/theatre/studio/src/panels/SequenceEditorPanel/DopeSheet/Right/AggregatedKeyframeTrack/AggregateKeyframeEditor/AggregateKeyframeDot.tsx index b075654..388682b 100644 --- a/theatre/studio/src/panels/SequenceEditorPanel/DopeSheet/Right/AggregatedKeyframeTrack/AggregateKeyframeEditor/AggregateKeyframeDot.tsx +++ b/theatre/studio/src/panels/SequenceEditorPanel/DopeSheet/Right/AggregatedKeyframeTrack/AggregateKeyframeEditor/AggregateKeyframeDot.tsx @@ -17,18 +17,86 @@ import type {KeyframeWithPathToPropFromCommonRoot} from '@theatre/studio/store/t import {commonRootOfPathsToProps} from '@theatre/shared/utils/addresses' import type {ILogger} from '@theatre/shared/logger' import DopeSnap from '@theatre/studio/panels/SequenceEditorPanel/RightOverlay/DopeSnap' +import type {EditingOptionsTree} from '@theatre/studio/panels/SequenceEditorPanel/DopeSheet/Right/BasicKeyframedTrack/KeyframeEditor/useSingleKeyframeInlineEditorPopover' +import {useKeyframeInlineEditorPopover} from '@theatre/studio/panels/SequenceEditorPanel/DopeSheet/Right/BasicKeyframedTrack/KeyframeEditor/useSingleKeyframeInlineEditorPopover' +import type { + SequenceEditorTree_PrimitiveProp, + SequenceEditorTree_PropWithChildren, + SequenceEditorTree_SheetObject, +} from '@theatre/studio/panels/SequenceEditorPanel/layout/tree' +import type {KeyframeWithTrack} from '@theatre/studio/panels/SequenceEditorPanel/DopeSheet/Right/collectAggregateKeyframes' type IAggregateKeyframeDotProps = { editorProps: IAggregateKeyframeEditorProps utils: IAggregateKeyframeEditorUtils } +function sheetObjectBuild( + viewModel: SequenceEditorTree_SheetObject, + keyframes: KeyframeWithTrack[], +): EditingOptionsTree | null { + const children = viewModel.children + .map((a) => + a.type === 'propWithChildren' + ? propWithChildrenBuild(a, keyframes) + : primitivePropBuild(a, keyframes), + ) + .filter((a): a is EditingOptionsTree => a !== null) + if (children.length === 0) return null + return { + type: 'sheetObject', + sheetObject: viewModel.sheetObject, + children, + } +} +function propWithChildrenBuild( + viewModel: SequenceEditorTree_PropWithChildren, + keyframes: KeyframeWithTrack[], +): EditingOptionsTree | null { + const children = viewModel.children + .map((a) => + a.type === 'propWithChildren' + ? propWithChildrenBuild(a, keyframes) + : primitivePropBuild(a, keyframes), + ) + .filter((a): a is EditingOptionsTree => a !== null) + if (children.length === 0) return null + return { + type: 'propWithChildren', + pathToProp: viewModel.pathToProp, + propConfig: viewModel.propConf, + children, + } +} +function primitivePropBuild( + viewModelLeaf: SequenceEditorTree_PrimitiveProp, + keyframes: KeyframeWithTrack[], +): EditingOptionsTree | null { + const keyframe = keyframes.find((kf) => kf.track.id === viewModelLeaf.trackId) + if (!keyframe) return null + return { + type: 'primitiveProp', + keyframe: keyframe.kf, + pathToProp: viewModelLeaf.pathToProp, + propConfig: viewModelLeaf.propConf, + sheetObject: viewModelLeaf.sheetObject, + trackId: viewModelLeaf.trackId, + } +} + export function AggregateKeyframeDot( props: React.PropsWithChildren, ) { const logger = useLogger('AggregateKeyframeDot') const {cur} = props.utils + const [inlineEditorPopover, openEditor, _, isInlineEditorPopoverOpen] = + useKeyframeInlineEditorPopover( + props.editorProps.viewModel.type === 'sheetObject' + ? sheetObjectBuild(props.editorProps.viewModel, cur.keyframes) + : propWithChildrenBuild(props.editorProps.viewModel, cur.keyframes), + ) + const presence = usePresence(props.utils.itemKey) presence.useRelations( () => @@ -57,6 +125,7 @@ export function AggregateKeyframeDot( // Need this for the dragging logic to be able to get the keyframe props // based on the position. {...DopeSnap.includePositionSnapAttrs(cur.position)} + onClick={(e) => openEditor(e, ref.current!)} /> {contextMenu} + {inlineEditorPopover} ) } diff --git a/theatre/studio/src/panels/SequenceEditorPanel/DopeSheet/Right/BasicKeyframedTrack/KeyframeEditor/DeterminePropEditorForSingleKeyframe.tsx b/theatre/studio/src/panels/SequenceEditorPanel/DopeSheet/Right/BasicKeyframedTrack/KeyframeEditor/DeterminePropEditorForSingleKeyframe.tsx index 3c7b928..9880737 100644 --- a/theatre/studio/src/panels/SequenceEditorPanel/DopeSheet/Right/BasicKeyframedTrack/KeyframeEditor/DeterminePropEditorForSingleKeyframe.tsx +++ b/theatre/studio/src/panels/SequenceEditorPanel/DopeSheet/Right/BasicKeyframedTrack/KeyframeEditor/DeterminePropEditorForSingleKeyframe.tsx @@ -1,28 +1,19 @@ import React from 'react' import styled from 'styled-components' -import type { - PropTypeConfig, - PropTypeConfig_AllSimples, -} from '@theatre/core/propTypes' -import type {IEditingTools} from '@theatre/studio/propEditors/utils/IEditingTools' -import type {PropConfigForType} from '@theatre/studio/propEditors/utils/PropConfigForType' +import type {PropTypeConfig_AllSimples} from '@theatre/core/propTypes' import type {ISimplePropEditorReactProps} from '@theatre/studio/propEditors/simpleEditors/ISimplePropEditorReactProps' import {simplePropEditorByPropType} from '@theatre/studio/propEditors/simpleEditors/simplePropEditorByPropType' import SingleKeyframeSimplePropEditor from './DeterminePropEditorForSingleKeyframe/SingleKeyframeSimplePropEditor' - -type IDeterminePropEditorForSingleKeyframeProps< - K extends PropTypeConfig['type'], -> = { - editingTools: IEditingTools['valueType']> - propConfig: PropConfigForType - keyframeValue: PropConfigForType['valueType'] - displayLabel?: string -} +import type { + EditingOptionsTree, + PrimitivePropEditingOptions, +} from './useSingleKeyframeInlineEditorPopover' +import last from 'lodash-es/last' +import {useTempTransactionEditingTools} from './useTempTransactionEditingTools' const SingleKeyframePropEditorContainer = styled.div` - padding: 2px; display: flex; align-items: stretch; @@ -30,7 +21,7 @@ const SingleKeyframePropEditorContainer = styled.div` min-width: 100px; } ` -const SingleKeyframePropLabel = styled.span` +const SingleKeyframePropLabel = styled.div` font-style: normal; font-weight: 400; font-size: 11px; @@ -42,6 +33,10 @@ const SingleKeyframePropLabel = styled.span` color: #919191; ` +const IndentedThing = styled.div` + margin-left: 24px; +` + /** * Given a propConfig, this function gives the corresponding prop editor for * use in the dope sheet inline prop editor on a keyframe. @@ -52,35 +47,73 @@ const SingleKeyframePropLabel = styled.span` * * @param p - propConfig object for any type of prop. */ -export function DeterminePropEditorForSingleKeyframe( - p: IDeterminePropEditorForSingleKeyframeProps, -) { - const propConfig = p.propConfig - - if (propConfig.type === 'compound') { - throw new Error( - 'We do not yet support editing compound props for a keyframe', +export function DeterminePropEditorForKeyframeTree(p: EditingOptionsTree) { + if (p.type === 'sheetObject') { + return ( + <> + + {p.sheetObject.address.objectKey} + + + {p.children.map((c, i) => ( + + ))} + + ) - } else if (propConfig.type === 'enum') { + } else if (p.type === 'propWithChildren') { + const label = p.propConfig.label ?? last(p.pathToProp) + return ( + <> + {label} + + {p.children.map((c, i) => ( + + ))} + + + ) + } else { + return + } +} + +function BeepBoop(p: PrimitivePropEditingOptions) { + const label = p.propConfig.label ?? last(p.pathToProp) + const editingTools = useEditingToolsForKeyframeEditorPopover(p) + + if (p.propConfig.type === 'enum') { // notice: enums are not implemented, yet. return <> } else { - const PropEditor = simplePropEditorByPropType[propConfig.type] - + const PropEditor = simplePropEditorByPropType[ + p.propConfig.type + ] as React.VFC> return ( - {p.displayLabel} + {label} - > - } - propConfig={propConfig} - editingTools={p.editingTools} - keyframeValue={p.keyframeValue} + SimpleEditorComponent={PropEditor} + propConfig={p.propConfig} + editingTools={editingTools} + keyframeValue={p.keyframe.value} /> ) } } + +function useEditingToolsForKeyframeEditorPopover( + props: PrimitivePropEditingOptions, +) { + const obj = props.sheetObject + return useTempTransactionEditingTools(({stateEditors}, value) => { + const newKeyframe = {...props.keyframe, value} + stateEditors.coreByProject.historic.sheetsById.sequence.replaceKeyframes({ + ...obj.address, + trackId: props.trackId, + keyframes: [newKeyframe], + snappingFunction: obj.sheet.getSequence().closestGridPosition, + }) + }) +} diff --git a/theatre/studio/src/panels/SequenceEditorPanel/DopeSheet/Right/BasicKeyframedTrack/KeyframeEditor/SingleKeyframeDot.tsx b/theatre/studio/src/panels/SequenceEditorPanel/DopeSheet/Right/BasicKeyframedTrack/KeyframeEditor/SingleKeyframeDot.tsx index ae9212e..312f231 100644 --- a/theatre/studio/src/panels/SequenceEditorPanel/DopeSheet/Right/BasicKeyframedTrack/KeyframeEditor/SingleKeyframeDot.tsx +++ b/theatre/studio/src/panels/SequenceEditorPanel/DopeSheet/Right/BasicKeyframedTrack/KeyframeEditor/SingleKeyframeDot.tsx @@ -23,7 +23,7 @@ import { snapToNone, snapToSome, } from '@theatre/studio/panels/SequenceEditorPanel/DopeSheet/Right/KeyframeSnapTarget' -import {useSingleKeyframeInlineEditorPopover} from './useSingleKeyframeInlineEditorPopover' +import {useKeyframeInlineEditorPopover} from './useSingleKeyframeInlineEditorPopover' import usePresence, { PresenceFlag, } from '@theatre/studio/uiComponents/usePresence' @@ -95,22 +95,21 @@ const HitZone = styled.div<{isInlineEditorPopoverOpen: boolean}>` type ISingleKeyframeDotProps = ISingleKeyframeEditorProps /** The ◆ you can grab onto in "keyframe editor" (aka "dope sheet" in other programs) */ -const SingleKeyframeDot: React.VFC = React.memo( - (props) => { - const logger = useLogger('SingleKeyframeDot', props.keyframe.id) - const presence = usePresence(props.itemKey) - const [ref, node] = useRefAndState(null) - - const [contextMenu] = useSingleKeyframeContextMenu(node, logger, props) - const [inlineEditorPopover, openEditor, _, isInlineEditorPopoverOpen] = - useSingleKeyframeInlineEditorPopover({ - keyframe: props.keyframe, - pathToProp: props.leaf.pathToProp, - propConf: props.leaf.propConf, - sheetObject: props.leaf.sheetObject, - trackId: props.leaf.trackId, - }) +const SingleKeyframeDot: React.VFC = (props) => { + const logger = useLogger('SingleKeyframeDot', props.keyframe.id) + const presence = usePresence(props.itemKey) + const [ref, node] = useRefAndState(null) + const [contextMenu] = useSingleKeyframeContextMenu(node, logger, props) + const [inlineEditorPopover, openEditor, _, isInlineEditorPopoverOpen] = + useKeyframeInlineEditorPopover({ + type: 'primitiveProp', + keyframe: props.keyframe, + pathToProp: props.leaf.pathToProp, + propConfig: props.leaf.propConf, + sheetObject: props.leaf.sheetObject, + trackId: props.leaf.trackId, + }) const [isDragging] = useDragForSingleKeyframeDot(node, props, { onClickFromDrag(dragStartEvent) { openEditor(dragStartEvent, ref.current!) diff --git a/theatre/studio/src/panels/SequenceEditorPanel/DopeSheet/Right/BasicKeyframedTrack/KeyframeEditor/useSingleKeyframeInlineEditorPopover.tsx b/theatre/studio/src/panels/SequenceEditorPanel/DopeSheet/Right/BasicKeyframedTrack/KeyframeEditor/useSingleKeyframeInlineEditorPopover.tsx index 2638983..270d8c0 100644 --- a/theatre/studio/src/panels/SequenceEditorPanel/DopeSheet/Right/BasicKeyframedTrack/KeyframeEditor/useSingleKeyframeInlineEditorPopover.tsx +++ b/theatre/studio/src/panels/SequenceEditorPanel/DopeSheet/Right/BasicKeyframedTrack/KeyframeEditor/useSingleKeyframeInlineEditorPopover.tsx @@ -1,53 +1,51 @@ import React from 'react' -import last from 'lodash-es/last' import usePopover from '@theatre/studio/uiComponents/Popover/usePopover' import BasicPopover from '@theatre/studio/uiComponents/Popover/BasicPopover' -import {useTempTransactionEditingTools} from './useTempTransactionEditingTools' -import {DeterminePropEditorForSingleKeyframe} from './DeterminePropEditorForSingleKeyframe' +import {DeterminePropEditorForKeyframeTree} from './DeterminePropEditorForSingleKeyframe' import type {SequenceTrackId} from '@theatre/shared/utils/ids' import type {Keyframe} from '@theatre/core/projects/store/types/SheetState_Historic' import type SheetObject from '@theatre/core/sheetObjects/SheetObject' -import type {PropTypeConfig} from '@theatre/core/propTypes' +import type { + PropTypeConfig_AllSimples, + PropTypeConfig_Compound, + PropTypeConfig_Enum, +} from '@theatre/core/propTypes' import type {PathToProp} from '@theatre/shared/utils/addresses' +import type {UnknownValidCompoundProps} from '@theatre/core/propTypes/internals' /** The editor that pops up when directly clicking a Keyframe. */ -export function useSingleKeyframeInlineEditorPopover( - props: SingleKeyframeEditingOptions, +export function useKeyframeInlineEditorPopover( + props: EditingOptionsTree | null, ) { - const editingTools = useEditingToolsForKeyframeEditorPopover(props) - const label = props.propConf.label ?? last(props.pathToProp) - return usePopover({debugName: 'useKeyframeInlineEditorPopover'}, () => ( - + {props === null ? undefined : ( + + )} )) } -type SingleKeyframeEditingOptions = { +export type EditingOptionsTree = + | SheetObjectEditingOptionsTree + | PropWithChildrenEditingOptionsTree + | PrimitivePropEditingOptions +type SheetObjectEditingOptionsTree = { + type: 'sheetObject' + sheetObject: SheetObject + children: EditingOptionsTree[] +} +type PropWithChildrenEditingOptionsTree = { + type: 'propWithChildren' + propConfig: PropTypeConfig_Compound + pathToProp: PathToProp + children: EditingOptionsTree[] +} +export type PrimitivePropEditingOptions = { + type: 'primitiveProp' keyframe: Keyframe - propConf: PropTypeConfig + propConfig: PropTypeConfig_AllSimples | PropTypeConfig_Enum // note: enums are not implemented yet sheetObject: SheetObject trackId: SequenceTrackId pathToProp: PathToProp } - -function useEditingToolsForKeyframeEditorPopover( - props: SingleKeyframeEditingOptions, -) { - const obj = props.sheetObject - return useTempTransactionEditingTools(({stateEditors}, value) => { - const newKeyframe = {...props.keyframe, value} - stateEditors.coreByProject.historic.sheetsById.sequence.replaceKeyframes({ - ...obj.address, - trackId: props.trackId, - keyframes: [newKeyframe], - snappingFunction: obj.sheet.getSequence().closestGridPosition, - }) - }) -} diff --git a/theatre/studio/src/panels/SequenceEditorPanel/GraphEditor/BasicKeyframedTrack/KeyframeEditor/GraphEditorDotNonScalar.tsx b/theatre/studio/src/panels/SequenceEditorPanel/GraphEditor/BasicKeyframedTrack/KeyframeEditor/GraphEditorDotNonScalar.tsx index 97d7bd7..805de39 100644 --- a/theatre/studio/src/panels/SequenceEditorPanel/GraphEditor/BasicKeyframedTrack/KeyframeEditor/GraphEditorDotNonScalar.tsx +++ b/theatre/studio/src/panels/SequenceEditorPanel/GraphEditor/BasicKeyframedTrack/KeyframeEditor/GraphEditorDotNonScalar.tsx @@ -16,7 +16,7 @@ import { useCssCursorLock, } from '@theatre/studio/uiComponents/PointerEventsHandler' import DopeSnap from '@theatre/studio/panels/SequenceEditorPanel/RightOverlay/DopeSnap' -import {useSingleKeyframeInlineEditorPopover} from '@theatre/studio/panels/SequenceEditorPanel/DopeSheet/Right/BasicKeyframedTrack/KeyframeEditor/useSingleKeyframeInlineEditorPopover' +import {useKeyframeInlineEditorPopover} from '@theatre/studio/panels/SequenceEditorPanel/DopeSheet/Right/BasicKeyframedTrack/KeyframeEditor/useSingleKeyframeInlineEditorPopover' import usePresence, { PresenceFlag, } from '@theatre/studio/uiComponents/usePresence' @@ -71,7 +71,7 @@ const GraphEditorDotNonScalar: React.VFC = (props) => { const curValue = props.which === 'left' ? 0 : 1 const [inlineEditorPopover, openEditor, _, _isInlineEditorPopoverOpen] = - useSingleKeyframeInlineEditorPopover({ + useKeyframeInlineEditorPopover({ keyframe: props.keyframe, pathToProp: props.pathToProp, propConf: props.propConfig, diff --git a/theatre/studio/src/panels/SequenceEditorPanel/GraphEditor/BasicKeyframedTrack/KeyframeEditor/GraphEditorDotScalar.tsx b/theatre/studio/src/panels/SequenceEditorPanel/GraphEditor/BasicKeyframedTrack/KeyframeEditor/GraphEditorDotScalar.tsx index 27005e9..fccccc9 100644 --- a/theatre/studio/src/panels/SequenceEditorPanel/GraphEditor/BasicKeyframedTrack/KeyframeEditor/GraphEditorDotScalar.tsx +++ b/theatre/studio/src/panels/SequenceEditorPanel/GraphEditor/BasicKeyframedTrack/KeyframeEditor/GraphEditorDotScalar.tsx @@ -16,7 +16,7 @@ import { useCssCursorLock, } from '@theatre/studio/uiComponents/PointerEventsHandler' import DopeSnap from '@theatre/studio/panels/SequenceEditorPanel/RightOverlay/DopeSnap' -import {useSingleKeyframeInlineEditorPopover} from '@theatre/studio/panels/SequenceEditorPanel/DopeSheet/Right/BasicKeyframedTrack/KeyframeEditor/useSingleKeyframeInlineEditorPopover' +import {useKeyframeInlineEditorPopover} from '@theatre/studio/panels/SequenceEditorPanel/DopeSheet/Right/BasicKeyframedTrack/KeyframeEditor/useSingleKeyframeInlineEditorPopover' import usePresence, { PresenceFlag, } from '@theatre/studio/uiComponents/usePresence' @@ -71,7 +71,7 @@ const GraphEditorDotScalar: React.VFC = (props) => { const cyInExtremumSpace = props.extremumSpace.fromValueSpace(curValue) const [inlineEditorPopover, openEditor, _, _isInlineEditorPopoverOpen] = - useSingleKeyframeInlineEditorPopover({ + useKeyframeInlineEditorPopover({ keyframe: props.keyframe, pathToProp: props.pathToProp, propConf: props.propConfig, diff --git a/theatre/studio/src/panels/SequenceEditorPanel/layout/tree.ts b/theatre/studio/src/panels/SequenceEditorPanel/layout/tree.ts index 877d9d0..ecbf42d 100644 --- a/theatre/studio/src/panels/SequenceEditorPanel/layout/tree.ts +++ b/theatre/studio/src/panels/SequenceEditorPanel/layout/tree.ts @@ -18,6 +18,7 @@ import {prism, val, valueDerivation} from '@theatre/dataverse' import logger from '@theatre/shared/logger' import {titleBarHeight} from '@theatre/studio/panels/BasePanel/common' import type {Studio} from '@theatre/studio/Studio' +import type {UnknownValidCompoundProps} from '@theatre/core/propTypes/internals' /** * Base "view model" for each row with common @@ -73,6 +74,7 @@ export type SequenceEditorTree_PropWithChildren = SequenceEditorTree_Row<'propWithChildren'> & { isCollapsed: boolean sheetObject: SheetObject + propConf: PropTypeConfig_Compound pathToProp: PathToProp children: Array< SequenceEditorTree_PropWithChildren | SequenceEditorTree_PrimitiveProp @@ -233,6 +235,7 @@ export const calculateSequenceEditorTree = ( addProp_compound( sheetObject, trackMapping, + conf, pathToProp, conf, arrayOfChildren, @@ -259,6 +262,7 @@ export const calculateSequenceEditorTree = ( function addProp_compound( sheetObject: SheetObject, trackMapping: IPropPathToTrackIdTree, + propConf: PropTypeConfig_Compound, pathToProp: PathToProp, conf: PropTypeConfig_Compound<$FixMe>, arrayOfChildren: Array< @@ -276,6 +280,7 @@ export const calculateSequenceEditorTree = ( const row: SequenceEditorTree_PropWithChildren = { type: 'propWithChildren', isCollapsed, + propConf, pathToProp, sheetItemKey: createStudioSheetItemKey.forSheetObjectProp( sheetObject,