diff --git a/theatre/studio/src/panels/SequenceEditorPanel/DopeSheet/Left/AnyCompositeRow.tsx b/theatre/studio/src/panels/SequenceEditorPanel/DopeSheet/Left/AnyCompositeRow.tsx index d545121..dbfa4c2 100644 --- a/theatre/studio/src/panels/SequenceEditorPanel/DopeSheet/Left/AnyCompositeRow.tsx +++ b/theatre/studio/src/panels/SequenceEditorPanel/DopeSheet/Left/AnyCompositeRow.tsx @@ -5,8 +5,6 @@ import React from 'react' import {HiOutlineChevronRight} from 'react-icons/all' import styled from 'styled-components' import {propNameTextCSS} from '@theatre/studio/propEditors/utils/propNameTextCSS' -import type {ICollapsableItem} from '@theatre/studio/panels/SequenceEditorPanel/DopeSheet/useSequenceEditorCollapsable' -import {useVal} from '@theatre/react' export const Container = styled.li<{depth: number}>` --depth: ${(props) => props.depth}; @@ -71,9 +69,10 @@ const AnyCompositeRow: React.FC<{ leaf: SequenceEditorTree_Row label: React.ReactNode toggleSelect?: VoidFn + toggleCollapsed: VoidFn isSelected?: boolean isSelectable?: boolean - collapsable?: ICollapsableItem + isCollapsed: boolean }> = ({ leaf, label, @@ -81,10 +80,10 @@ const AnyCompositeRow: React.FC<{ isSelectable, isSelected, toggleSelect, - collapsable, + toggleCollapsed, + isCollapsed, }) => { const hasChildren = Array.isArray(children) && children.length > 0 - const isCollapsed = useVal(collapsable?.isCollapsed) ?? false return ( @@ -97,15 +96,12 @@ const AnyCompositeRow: React.FC<{ onClick={toggleSelect} isEven={leaf.n % 2 === 0} > - collapsable?.toggleCollapsed()} - > + {label} - {hasChildren && !isCollapsed && {children}} + {hasChildren && {children}} ) } diff --git a/theatre/studio/src/panels/SequenceEditorPanel/DopeSheet/Left/PropWithChildrenRow.tsx b/theatre/studio/src/panels/SequenceEditorPanel/DopeSheet/Left/PropWithChildrenRow.tsx index 9869c03..8f511d4 100644 --- a/theatre/studio/src/panels/SequenceEditorPanel/DopeSheet/Left/PropWithChildrenRow.tsx +++ b/theatre/studio/src/panels/SequenceEditorPanel/DopeSheet/Left/PropWithChildrenRow.tsx @@ -6,9 +6,7 @@ import {usePrism} from '@theatre/react' import React from 'react' import AnyCompositeRow from './AnyCompositeRow' import PrimitivePropRow from './PrimitivePropRow' -import {useSequenceEditorCollapsable} from '@theatre/studio/panels/SequenceEditorPanel/DopeSheet/useSequenceEditorCollapsable' -import {createStudioSheetItemKey} from '@theatre/shared/utils/ids' - +import {setCollapsedSheetObjectOrCompoundProp} from '@theatre/studio/panels/SequenceEditorPanel/DopeSheet/setCollapsedSheetObjectOrCompoundProp' export const decideRowByPropType = ( leaf: SequenceEditorTree_PropWithChildren | SequenceEditorTree_PrimitiveProp, ): React.ReactElement => @@ -27,19 +25,15 @@ export const decideRowByPropType = ( const PropWithChildrenRow: React.VFC<{ leaf: SequenceEditorTree_PropWithChildren }> = ({leaf}) => { - const collapsable = useSequenceEditorCollapsable( - createStudioSheetItemKey.forSheetObjectProp( - leaf.sheetObject, - leaf.pathToProp, - ), - ) - return usePrism(() => { return ( + setCollapsedSheetObjectOrCompoundProp(!leaf.isCollapsed, leaf) + } > {leaf.children.map((propLeaf) => decideRowByPropType(propLeaf))} diff --git a/theatre/studio/src/panels/SequenceEditorPanel/DopeSheet/Left/SheetObjectRow.tsx b/theatre/studio/src/panels/SequenceEditorPanel/DopeSheet/Left/SheetObjectRow.tsx index 7072ca5..d9f3c2f 100644 --- a/theatre/studio/src/panels/SequenceEditorPanel/DopeSheet/Left/SheetObjectRow.tsx +++ b/theatre/studio/src/panels/SequenceEditorPanel/DopeSheet/Left/SheetObjectRow.tsx @@ -3,22 +3,20 @@ import {usePrism} from '@theatre/react' import React from 'react' import AnyCompositeRow from './AnyCompositeRow' import {decideRowByPropType} from './PropWithChildrenRow' -import {useSequenceEditorCollapsable} from '@theatre/studio/panels/SequenceEditorPanel/DopeSheet/useSequenceEditorCollapsable' -import {createStudioSheetItemKey} from '@theatre/shared/utils/ids' +import {setCollapsedSheetObjectOrCompoundProp} from '@theatre/studio/panels/SequenceEditorPanel/DopeSheet/setCollapsedSheetObjectOrCompoundProp' const LeftSheetObjectRow: React.VFC<{ leaf: SequenceEditorTree_SheetObject }> = ({leaf}) => { - const collapsable = useSequenceEditorCollapsable( - createStudioSheetItemKey.forSheetObject(leaf.sheetObject), - ) - return usePrism(() => { return ( + setCollapsedSheetObjectOrCompoundProp(!leaf.isCollapsed, leaf) + } > {leaf.children.map((leaf) => decideRowByPropType(leaf))} diff --git a/theatre/studio/src/panels/SequenceEditorPanel/DopeSheet/Right/PrimitivePropRow.tsx b/theatre/studio/src/panels/SequenceEditorPanel/DopeSheet/Right/PrimitivePropRow.tsx index ce017e1..811ea92 100644 --- a/theatre/studio/src/panels/SequenceEditorPanel/DopeSheet/Right/PrimitivePropRow.tsx +++ b/theatre/studio/src/panels/SequenceEditorPanel/DopeSheet/Right/PrimitivePropRow.tsx @@ -27,13 +27,15 @@ const PrimitivePropRow: React.FC<{ console.error( `trackData type ${trackData?.type} is not yet supported on the sequence editor`, ) - return }> + return ( + }> + ) } else { const node = ( ) - return + return } }, [leaf, layoutP]) } diff --git a/theatre/studio/src/panels/SequenceEditorPanel/DopeSheet/Right/PropWithChildrenRow.tsx b/theatre/studio/src/panels/SequenceEditorPanel/DopeSheet/Right/PropWithChildrenRow.tsx index 2680d33..e21bb8b 100644 --- a/theatre/studio/src/panels/SequenceEditorPanel/DopeSheet/Right/PropWithChildrenRow.tsx +++ b/theatre/studio/src/panels/SequenceEditorPanel/DopeSheet/Right/PropWithChildrenRow.tsx @@ -8,8 +8,6 @@ import type {Pointer} from '@theatre/dataverse' import React from 'react' import PrimitivePropRow from './PrimitivePropRow' import RightRow from './Row' -import {useSequenceEditorCollapsable} from '@theatre/studio/panels/SequenceEditorPanel/DopeSheet/useSequenceEditorCollapsable' -import {createStudioSheetItemKey} from '@theatre/shared/utils/ids' export const decideRowByPropType = ( leaf: SequenceEditorTree_PropWithChildren | SequenceEditorTree_PrimitiveProp, @@ -33,18 +31,11 @@ const PropWithChildrenRow: React.VFC<{ leaf: SequenceEditorTree_PropWithChildren layoutP: Pointer }> = ({leaf, layoutP}) => { - const collapsable = useSequenceEditorCollapsable( - createStudioSheetItemKey.forSheetObjectProp( - leaf.sheetObject, - leaf.pathToProp, - ), - ) - return usePrism(() => { const node =
return ( - + {leaf.children.map((propLeaf) => decideRowByPropType(propLeaf, layoutP), )} diff --git a/theatre/studio/src/panels/SequenceEditorPanel/DopeSheet/Right/Row.tsx b/theatre/studio/src/panels/SequenceEditorPanel/DopeSheet/Right/Row.tsx index 12e3b9e..22f1873 100644 --- a/theatre/studio/src/panels/SequenceEditorPanel/DopeSheet/Right/Row.tsx +++ b/theatre/studio/src/panels/SequenceEditorPanel/DopeSheet/Right/Row.tsx @@ -1,8 +1,6 @@ import type {SequenceEditorTree_Row} from '@theatre/studio/panels/SequenceEditorPanel/layout/tree' import React from 'react' import styled from 'styled-components' -import type {ICollapsableItem} from '@theatre/studio/panels/SequenceEditorPanel/DopeSheet/useSequenceEditorCollapsable' -import {useVal} from '@theatre/react' const Container = styled.li<{}>` margin: 0; @@ -40,10 +38,9 @@ const Children = styled.ul` const RightRow: React.FC<{ leaf: SequenceEditorTree_Row node: React.ReactElement - collapsable?: ICollapsableItem -}> = ({leaf, children, node, collapsable}) => { + isCollapsed: boolean +}> = ({leaf, children, node, isCollapsed}) => { const hasChildren = Array.isArray(children) && children.length > 0 - const isCollapsed = useVal(collapsable?.isCollapsed) ?? false return ( @@ -53,7 +50,7 @@ const RightRow: React.FC<{ > {node} - {hasChildren && !isCollapsed && {children}} + {hasChildren && {children}} ) } diff --git a/theatre/studio/src/panels/SequenceEditorPanel/DopeSheet/Right/SheetObjectRow.tsx b/theatre/studio/src/panels/SequenceEditorPanel/DopeSheet/Right/SheetObjectRow.tsx index 0772276..770c7d3 100644 --- a/theatre/studio/src/panels/SequenceEditorPanel/DopeSheet/Right/SheetObjectRow.tsx +++ b/theatre/studio/src/panels/SequenceEditorPanel/DopeSheet/Right/SheetObjectRow.tsx @@ -5,20 +5,15 @@ import type {Pointer} from '@theatre/dataverse' import React from 'react' import {decideRowByPropType} from './PropWithChildrenRow' import RightRow from './Row' -import {useSequenceEditorCollapsable} from '@theatre/studio/panels/SequenceEditorPanel/DopeSheet/useSequenceEditorCollapsable' -import {createStudioSheetItemKey} from '@theatre/shared/utils/ids' const RightSheetObjectRow: React.VFC<{ leaf: SequenceEditorTree_SheetObject layoutP: Pointer }> = ({leaf, layoutP}) => { - const collapsable = useSequenceEditorCollapsable( - createStudioSheetItemKey.forSheetObject(leaf.sheetObject), - ) return usePrism(() => { const node =
return ( - + {leaf.children.map((leaf) => decideRowByPropType(leaf, layoutP))} ) diff --git a/theatre/studio/src/panels/SequenceEditorPanel/DopeSheet/setCollapsedSheetObjectOrCompoundProp.tsx b/theatre/studio/src/panels/SequenceEditorPanel/DopeSheet/setCollapsedSheetObjectOrCompoundProp.tsx new file mode 100644 index 0000000..81c20f2 --- /dev/null +++ b/theatre/studio/src/panels/SequenceEditorPanel/DopeSheet/setCollapsedSheetObjectOrCompoundProp.tsx @@ -0,0 +1,36 @@ +import type {StudioSheetItemKey} from '@theatre/shared/utils/ids' +import {createStudioSheetItemKey} from '@theatre/shared/utils/ids' +import getStudio from '@theatre/studio/getStudio' +import type {PathToProp} from '@theatre/shared/utils/addresses' +import type SheetObject from '@theatre/core/sheetObjects/SheetObject' + +// discriminated union +export function setCollapsedSheetObjectOrCompoundProp( + isCollapsed: boolean, + toCollapse: + | { + sheetObject: SheetObject + } + | { + sheetObject: SheetObject + pathToProp: PathToProp + }, +) { + const itemKey: StudioSheetItemKey = + 'pathToProp' in toCollapse + ? createStudioSheetItemKey.forSheetObjectProp( + toCollapse.sheetObject, + toCollapse.pathToProp, + ) + : createStudioSheetItemKey.forSheetObject(toCollapse.sheetObject) + + getStudio().transaction(({stateEditors}) => { + stateEditors.studio.ahistoric.projects.stateByProjectId.stateBySheetId.sequence.sequenceEditorCollapsableItems.set( + { + ...toCollapse.sheetObject.address, + studioSheetItemKey: itemKey, + isCollapsed, + }, + ) + }) +} diff --git a/theatre/studio/src/panels/SequenceEditorPanel/DopeSheet/useSequenceEditorCollapsable.tsx b/theatre/studio/src/panels/SequenceEditorPanel/DopeSheet/useSequenceEditorCollapsable.tsx deleted file mode 100644 index 9ce3f0f..0000000 --- a/theatre/studio/src/panels/SequenceEditorPanel/DopeSheet/useSequenceEditorCollapsable.tsx +++ /dev/null @@ -1,91 +0,0 @@ -import React, {useContext, useMemo} from 'react' -import type {IDerivation, Pointer} from '@theatre/dataverse' -import {prism, val, valueDerivation} from '@theatre/dataverse' -import type {StudioSheetItemKey, SheetId} from '@theatre/shared/utils/ids' -import type {SequenceEditorPanelLayout} from '@theatre/studio/panels/SequenceEditorPanel/layout/layout' -import {usePrism} from '@theatre/react' -import getStudio from '@theatre/studio/getStudio' - -/** - * Provided via context provided by {@link ProvideCollapsable}. - */ -export function useSequenceEditorCollapsable( - sheetItemKey: StudioSheetItemKey, -): ICollapsableItem { - const collapsableContext = useContext(CollapsableContext) - return useMemo( - () => collapsableContext.getCollapsable(sheetItemKey), - [sheetItemKey, collapsableContext], - ) -} - -/** - * Get this via {@link useSequenceEditorCollapsable} - */ -export type ICollapsableItem = { - isCollapsed: IDerivation - toggleCollapsed(): void -} -type ICollapsableContext = { - getCollapsable(sheetItemKey: StudioSheetItemKey): ICollapsableItem -} -const CollapsableContext = React.createContext(null!) -const ProviderChildrenMemo: React.FC<{}> = React.memo(({children}) => ( - <>{children} -)) - -/** - * Provide a context for managing collapsable items - * which are useable from {@link useSequenceEditorCollapsable}. - */ -export function ProvideCollapsable( - props: React.PropsWithChildren<{ - sheetId: SheetId - layoutP: Pointer - }>, -) { - const contextValue = usePrism((): ICollapsableContext => { - const studio = getStudio() - const sheetAddress = val(props.layoutP.sheet.address) - const collapsableItemsSetP = - getStudio().atomP.ahistoric.projects.stateByProjectId[ - sheetAddress.projectId - ].stateBySheetId[sheetAddress.sheetId].sequence - .sequenceEditorCollapsableItems - const setIsCollapsed = prism.memo( - 'setIsCollapsed', - () => { - return function setIsCollapsed( - studioSheetItemKey: StudioSheetItemKey, - isCollapsed: boolean, - ): void { - studio.transaction(({stateEditors}) => { - stateEditors.studio.ahistoric.projects.stateByProjectId.stateBySheetId.sequence.sequenceEditorCollapsableItems.set( - {...sheetAddress, studioSheetItemKey, isCollapsed}, - ) - }) - } - }, - [sheetAddress], - ) - return { - getCollapsable(itemId) { - const isCollapsedD = valueDerivation( - collapsableItemsSetP.byId[itemId].isCollapsed, - ).map((value) => value ?? false) - - return { - isCollapsed: isCollapsedD, - toggleCollapsed() { - setIsCollapsed(itemId, !isCollapsedD.getValue()) - }, - } - }, - } - }, [props.sheetId]) - return ( - - - - ) -} diff --git a/theatre/studio/src/panels/SequenceEditorPanel/SequenceEditorPanel.tsx b/theatre/studio/src/panels/SequenceEditorPanel/SequenceEditorPanel.tsx index ad80780..439d072 100644 --- a/theatre/studio/src/panels/SequenceEditorPanel/SequenceEditorPanel.tsx +++ b/theatre/studio/src/panels/SequenceEditorPanel/SequenceEditorPanel.tsx @@ -28,7 +28,6 @@ import { TitleBar_Punctuation, } from '@theatre/studio/panels/BasePanel/common' import type {UIPanelId} from '@theatre/shared/utils/ids' -import {ProvideCollapsable} from './DopeSheet/useSequenceEditorCollapsable' const Container = styled(PanelWrapper)` z-index: ${panelZIndexes.sequenceEditorPanel}; @@ -164,17 +163,15 @@ const Content: React.VFC<{}> = () => { return ( - - -
- - {graphEditorOpen && ( - - )} - {graphEditorAvailable && } - - - + +
+ + {graphEditorOpen && ( + + )} + {graphEditorAvailable && } + + ) }, [dims]) diff --git a/theatre/studio/src/panels/SequenceEditorPanel/layout/layout.ts b/theatre/studio/src/panels/SequenceEditorPanel/layout/layout.ts index 65ed5bc..077063a 100644 --- a/theatre/studio/src/panels/SequenceEditorPanel/layout/layout.ts +++ b/theatre/studio/src/panels/SequenceEditorPanel/layout/layout.ts @@ -186,7 +186,11 @@ export function sequenceEditorPanelLayout( .stateBySheetId[sheet.address.sheetId] return prism(() => { - const tree = subPrism('tree', () => calculateSequenceEditorTree(sheet), []) + const tree = subPrism( + 'tree', + () => calculateSequenceEditorTree(sheet, studio), + [], + ) const panelDims = val(panelDimsP) const graphEditorState = val( diff --git a/theatre/studio/src/panels/SequenceEditorPanel/layout/tree.ts b/theatre/studio/src/panels/SequenceEditorPanel/layout/tree.ts index 8879c09..d5b9e78 100644 --- a/theatre/studio/src/panels/SequenceEditorPanel/layout/tree.ts +++ b/theatre/studio/src/panels/SequenceEditorPanel/layout/tree.ts @@ -9,10 +9,12 @@ import type {IPropPathToTrackIdTree} from '@theatre/core/sheetObjects/SheetObjec import type Sheet from '@theatre/core/sheets/Sheet' import type {PathToProp} from '@theatre/shared/utils/addresses' import type {SequenceTrackId} from '@theatre/shared/utils/ids' +import {createStudioSheetItemKey} from '@theatre/shared/utils/ids' import type {$FixMe, $IntentionalAny} from '@theatre/shared/utils/types' -import {prism, val} from '@theatre/dataverse' +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' export type SequenceEditorTree_Row = { type: Type @@ -32,6 +34,7 @@ export type SequenceEditorTree_Sheet = SequenceEditorTree_Row<'sheet'> & { export type SequenceEditorTree_SheetObject = SequenceEditorTree_Row<'sheetObject'> & { + isCollapsed: boolean sheetObject: SheetObject children: Array< SequenceEditorTree_PropWithChildren | SequenceEditorTree_PrimitiveProp @@ -40,6 +43,7 @@ export type SequenceEditorTree_SheetObject = export type SequenceEditorTree_PropWithChildren = SequenceEditorTree_Row<'propWithChildren'> & { + isCollapsed: boolean sheetObject: SheetObject pathToProp: PathToProp children: Array< @@ -62,13 +66,14 @@ export type SequenceEditorTree_AllRowTypes = | SequenceEditorTree_PropWithChildren | SequenceEditorTree_PrimitiveProp -const heightOfAnyTitle = 28 +const HEIGHT_OF_ANY_TITLE = 28 /** * Must run inside prism() */ export const calculateSequenceEditorTree = ( sheet: Sheet, + studio: Studio, ): SequenceEditorTree => { prism.ensurePrism() let topSoFar = titleBarHeight @@ -81,12 +86,15 @@ export const calculateSequenceEditorTree = ( top: topSoFar, depth: -1, n: nSoFar, - nodeHeight: 0, - heightIncludingChildren: -1, // will defined this later + nodeHeight: 0, // always 0 + heightIncludingChildren: -1, // will define this later } - topSoFar += tree.nodeHeight nSoFar += 1 + const collapsableP = + studio.atomP.ahistoric.projects.stateByProjectId[sheet.address.projectId] + .stateBySheetId[sheet.address.sheetId].sequence.collapsableItems + for (const sheetObject of Object.values(val(sheet.objectsP))) { if (sheetObject) { addObject(sheetObject, tree.children, tree.depth + 1) @@ -105,29 +113,35 @@ export const calculateSequenceEditorTree = ( if (Object.keys(trackSetups).length === 0) return + const isCollapsedP = + collapsableP.byId[createStudioSheetItemKey.forSheetObject(sheetObject)] + .isCollapsed + const isCollapsed = valueDerivation(isCollapsedP).getValue() ?? false + const row: SequenceEditorTree_SheetObject = { type: 'sheetObject', + isCollapsed: isCollapsed, top: topSoFar, children: [], depth: level, n: nSoFar, sheetObject: sheetObject, - nodeHeight: heightOfAnyTitle, + nodeHeight: HEIGHT_OF_ANY_TITLE, heightIncludingChildren: -1, } arrayOfChildren.push(row) nSoFar += 1 - topSoFar += heightOfAnyTitle - - addProps( - sheetObject, - trackSetups, - [], - sheetObject.template.config, - row.children, - level + 1, - ) - + topSoFar += HEIGHT_OF_ANY_TITLE + if (!isCollapsed) { + addProps( + sheetObject, + trackSetups, + [], + sheetObject.template.config, + row.children, + level + 1, + ) + } row.heightIncludingChildren = topSoFar - row.top } @@ -201,31 +215,39 @@ export const calculateSequenceEditorTree = ( >, level: number, ) { + const isCollapsedP = + collapsableP.byId[ + createStudioSheetItemKey.forSheetObjectProp(sheetObject, pathToProp) + ].isCollapsed + const isCollapsed = valueDerivation(isCollapsedP).getValue() ?? false + const row: SequenceEditorTree_PropWithChildren = { type: 'propWithChildren', + isCollapsed: isCollapsed, pathToProp, sheetObject: sheetObject, top: topSoFar, children: [], - nodeHeight: heightOfAnyTitle, + nodeHeight: HEIGHT_OF_ANY_TITLE, heightIncludingChildren: -1, depth: level, trackMapping, n: nSoFar, } - topSoFar += heightOfAnyTitle - nSoFar += 1 arrayOfChildren.push(row) + topSoFar += HEIGHT_OF_ANY_TITLE + if (!isCollapsed) { + nSoFar += 1 - addProps( - sheetObject, - trackMapping, - pathToProp, - conf, - row.children, - level + 1, - ) - + addProps( + sheetObject, + trackMapping, + pathToProp, + conf, + row.children, + level + 1, + ) + } row.heightIncludingChildren = topSoFar - row.top } @@ -246,14 +268,14 @@ export const calculateSequenceEditorTree = ( sheetObject: sheetObject, pathToProp, top: topSoFar, - nodeHeight: heightOfAnyTitle, - heightIncludingChildren: heightOfAnyTitle, + nodeHeight: HEIGHT_OF_ANY_TITLE, + heightIncludingChildren: HEIGHT_OF_ANY_TITLE, trackId, n: nSoFar, } arrayOfChildren.push(row) nSoFar += 1 - topSoFar += heightOfAnyTitle + topSoFar += HEIGHT_OF_ANY_TITLE } return tree diff --git a/theatre/studio/src/store/stateEditors.ts b/theatre/studio/src/store/stateEditors.ts index 0c80419..76e5452 100644 --- a/theatre/studio/src/store/stateEditors.ts +++ b/theatre/studio/src/store/stateEditors.ts @@ -490,10 +490,9 @@ namespace stateEditors { stateEditors.studio.ahistoric.projects.stateByProjectId.stateBySheetId.sequence._ensure( p, ) - let existing = seq.sequenceEditorCollapsableItems + let existing = seq.collapsableItems if (!existing) { - existing = seq.sequenceEditorCollapsableItems = - pointableSetUtil.create() + existing = seq.collapsableItems = pointableSetUtil.create() } return existing } diff --git a/theatre/studio/src/store/types/ahistoric.ts b/theatre/studio/src/store/types/ahistoric.ts index bc0119e..26d68ad 100644 --- a/theatre/studio/src/store/types/ahistoric.ts +++ b/theatre/studio/src/store/types/ahistoric.ts @@ -48,7 +48,7 @@ export type StudioAhistoricState = { range: IRange } - sequenceEditorCollapsableItems?: PointableSet< + collapsableItems?: PointableSet< StudioSheetItemKey, { isCollapsed: boolean