feature: Add sequence editor row collapsing

* Add hover state for chevron
 * Add StudioSheetItemKey id for use with sequenceEditorCollapsableItems

Co-authored-by: Elliot <key.draw@gmail.com>
This commit is contained in:
Cole Lawrence 2022-05-16 13:37:40 -04:00
parent 6fd718a6e7
commit f6e408f610
15 changed files with 290 additions and 60 deletions

View file

@ -1,3 +1,6 @@
import type SheetObject from '@theatre/core/sheetObjects/SheetObject'
import type {PathToProp} from './addresses'
import stableValueHash from './stableJsonStringify'
import {nanoid as generateNonSecure} from 'nanoid/non-secure' import {nanoid as generateNonSecure} from 'nanoid/non-secure'
import type {Nominal} from './Nominal' import type {Nominal} from './Nominal'
@ -18,6 +21,33 @@ export type PaneInstanceId = Nominal<'PaneInstanceId'>
export type SequenceTrackId = Nominal<'SequenceTrackId'> export type SequenceTrackId = Nominal<'SequenceTrackId'>
export type SequenceMarkerId = Nominal<'SequenceMarkerId'> export type SequenceMarkerId = Nominal<'SequenceMarkerId'>
export type ObjectAddressKey = Nominal<'ObjectAddressKey'> export type ObjectAddressKey = Nominal<'ObjectAddressKey'>
/**
* Studio consistent identifier for identifying any individual item on a sheet
* including a SheetObject, a SheetObject's prop, etc.
*
* See {@link createStudioSheetItemKey}.
*
* @remarks
* This is the kind of type which should not find itself in Project state,
* due to how it is lossy in the case of additional model layers being introduced.
* e.g. When we introduce an extra layer of multiple sequences per sheet,
* all the {@link StudioSheetItemKey}s will have different generated values,
* because they'll have additional information (the "sequence id"). This means
* that all data attached to those item keys will become detached.
*
* This kind of constraint might be mitigated by a sort of migrations ability,
* but for the most part it's just going to be easier to try not using
* {@link StudioSheetItemKey} for any data that needs to stick around after
* version updates to Theatre.
*
* Alternatively, if you did want some kind of universal identifier for any item
* that can be persisted and survive project model changes, it's probably going
* to be easier to simply generate a unique id for all items you want to use in
* this way, and don't do any of this concatenating/JSON.stringify "hashing"
* stuff.
*/
export type StudioSheetItemKey = Nominal<'StudioSheetItemKey'>
/** UI panels can contain a {@link PaneInstanceId} or something else. */ /** UI panels can contain a {@link PaneInstanceId} or something else. */
export type UIPanelId = Nominal<'UIPanelId'> export type UIPanelId = Nominal<'UIPanelId'>
@ -32,3 +62,24 @@ export function asSequenceTrackId(s: string): SequenceTrackId {
export function generateSequenceMarkerId(): SequenceMarkerId { export function generateSequenceMarkerId(): SequenceMarkerId {
return generateNonSecure(10) as SequenceMarkerId return generateNonSecure(10) as SequenceMarkerId
} }
/**
* This will not necessarily maintain consistent key values if any
* versioning happens where something needs to
*/
export const createStudioSheetItemKey = {
forSheetObject(obj: SheetObject): StudioSheetItemKey {
return stableValueHash({
o: obj.address.objectKey,
}) as StudioSheetItemKey
},
forSheetObjectProp(
obj: SheetObject,
pathToProp: PathToProp,
): StudioSheetItemKey {
return stableValueHash({
o: obj.address.objectKey,
p: pathToProp,
}) as StudioSheetItemKey
},
}

View file

@ -39,7 +39,7 @@ export const rowBg = css`
} }
` `
const Row = styled.div` const LeftRow = styled.div`
display: flex; display: flex;
height: 30px; height: 30px;
justify-content: flex-start; justify-content: flex-start;
@ -126,7 +126,7 @@ export function SingleRowPropEditor<T>({
}) })
return ( return (
<Row> <LeftRow>
{contextMenu} {contextMenu}
<Left> <Left>
<ControlsContainer>{editingTools.controlIndicators}</ControlsContainer> <ControlsContainer>{editingTools.controlIndicators}</ControlsContainer>
@ -142,6 +142,6 @@ export function SingleRowPropEditor<T>({
</Left> </Left>
<InputContainer>{children}</InputContainer> <InputContainer>{children}</InputContainer>
</Row> </LeftRow>
) )
} }

View file

@ -5,6 +5,8 @@ import React from 'react'
import {HiOutlineChevronRight} from 'react-icons/all' import {HiOutlineChevronRight} from 'react-icons/all'
import styled from 'styled-components' import styled from 'styled-components'
import {propNameTextCSS} from '@theatre/studio/propEditors/utils/propNameTextCSS' 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}>` export const Container = styled.li<{depth: number}>`
--depth: ${(props) => props.depth}; --depth: ${(props) => props.depth};
@ -21,7 +23,7 @@ const Header = styled(BaseHeader)<{
isSelectable: boolean isSelectable: boolean
isSelected: boolean isSelected: boolean
}>` }>`
padding-left: calc(16px + var(--depth) * 20px); padding-left: calc(8px + var(--depth) * 20px);
display: flex; display: flex;
align-items: stretch; align-items: stretch;
@ -42,14 +44,21 @@ const Head_Label = styled.span`
flex-wrap: nowrap; flex-wrap: nowrap;
` `
const Head_Icon = styled.span<{isOpen: boolean}>` const Head_Icon = styled.span<{isCollapsed: boolean}>`
width: 12px; width: 12px;
margin-right: 8px; padding: 8px;
font-size: 9px; font-size: 9px;
display: flex; display: flex;
align-items: center; align-items: center;
transform: rotateZ(${(props) => (props.isOpen ? 90 : 0)}deg); transition: transform 0.05s ease-out, color 0.1s ease-out;
transform: rotateZ(${(props) => (props.isCollapsed ? 0 : 90)}deg);
color: #66686a;
&:hover {
transform: rotateZ(${(props) => (props.isCollapsed ? 15 : 75)}deg);
color: #c0c4c9;
}
` `
const Children = styled.ul` const Children = styled.ul`
@ -64,8 +73,18 @@ const AnyCompositeRow: React.FC<{
toggleSelect?: VoidFn toggleSelect?: VoidFn
isSelected?: boolean isSelected?: boolean
isSelectable?: boolean isSelectable?: boolean
}> = ({leaf, label, children, isSelectable, isSelected, toggleSelect}) => { collapsable?: ICollapsableItem
}> = ({
leaf,
label,
children,
isSelectable,
isSelected,
toggleSelect,
collapsable,
}) => {
const hasChildren = Array.isArray(children) && children.length > 0 const hasChildren = Array.isArray(children) && children.length > 0
const isCollapsed = useVal(collapsable?.isCollapsed) ?? false
return ( return (
<Container depth={leaf.depth}> <Container depth={leaf.depth}>
@ -78,12 +97,15 @@ const AnyCompositeRow: React.FC<{
onClick={toggleSelect} onClick={toggleSelect}
isEven={leaf.n % 2 === 0} isEven={leaf.n % 2 === 0}
> >
<Head_Icon isOpen={true}> <Head_Icon
isCollapsed={isCollapsed}
onClick={() => collapsable?.toggleCollapsed()}
>
<HiOutlineChevronRight /> <HiOutlineChevronRight />
</Head_Icon> </Head_Icon>
<Head_Label>{label}</Head_Label> <Head_Label>{label}</Head_Label>
</Header> </Header>
{hasChildren && <Children>{children}</Children>} {hasChildren && !isCollapsed && <Children>{children}</Children>}
</Container> </Container>
) )
} }

View file

@ -4,9 +4,10 @@ import type {
} from '@theatre/studio/panels/SequenceEditorPanel/layout/tree' } from '@theatre/studio/panels/SequenceEditorPanel/layout/tree'
import {usePrism} from '@theatre/react' import {usePrism} from '@theatre/react'
import React from 'react' import React from 'react'
import styled from 'styled-components'
import AnyCompositeRow from './AnyCompositeRow' import AnyCompositeRow from './AnyCompositeRow'
import PrimitivePropRow from './PrimitivePropRow' import PrimitivePropRow from './PrimitivePropRow'
import {useSequenceEditorCollapsable} from '@theatre/studio/panels/SequenceEditorPanel/DopeSheet/useSequenceEditorCollapsable'
import {createStudioSheetItemKey} from '@theatre/shared/utils/ids'
export const decideRowByPropType = ( export const decideRowByPropType = (
leaf: SequenceEditorTree_PropWithChildren | SequenceEditorTree_PrimitiveProp, leaf: SequenceEditorTree_PropWithChildren | SequenceEditorTree_PrimitiveProp,
@ -23,16 +24,22 @@ export const decideRowByPropType = (
/> />
) )
const Container = styled.div`` const PropWithChildrenRow: React.VFC<{
const PropWithChildrenRow: React.FC<{
leaf: SequenceEditorTree_PropWithChildren leaf: SequenceEditorTree_PropWithChildren
}> = ({leaf}) => { }> = ({leaf}) => {
const collapsable = useSequenceEditorCollapsable(
createStudioSheetItemKey.forSheetObjectProp(
leaf.sheetObject,
leaf.pathToProp,
),
)
return usePrism(() => { return usePrism(() => {
return ( return (
<AnyCompositeRow <AnyCompositeRow
leaf={leaf} leaf={leaf}
label={leaf.pathToProp[leaf.pathToProp.length - 1]} label={leaf.pathToProp[leaf.pathToProp.length - 1]}
collapsable={collapsable}
> >
{leaf.children.map((propLeaf) => decideRowByPropType(propLeaf))} {leaf.children.map((propLeaf) => decideRowByPropType(propLeaf))}
</AnyCompositeRow> </AnyCompositeRow>

View file

@ -1,22 +1,29 @@
import type {SequenceEditorTree_SheetObject} from '@theatre/studio/panels/SequenceEditorPanel/layout/tree' import type {SequenceEditorTree_SheetObject} from '@theatre/studio/panels/SequenceEditorPanel/layout/tree'
import {usePrism} from '@theatre/react' import {usePrism} from '@theatre/react'
import React from 'react' import React from 'react'
import styled from 'styled-components' import AnyCompositeRow from './AnyCompositeRow'
import CompoundRow from './AnyCompositeRow'
import {decideRowByPropType} from './PropWithChildrenRow' import {decideRowByPropType} from './PropWithChildrenRow'
import {useSequenceEditorCollapsable} from '@theatre/studio/panels/SequenceEditorPanel/DopeSheet/useSequenceEditorCollapsable'
import {createStudioSheetItemKey} from '@theatre/shared/utils/ids'
const Container = styled.div`` const LeftSheetObjectRow: React.VFC<{
const SheetObjectRow: React.FC<{
leaf: SequenceEditorTree_SheetObject leaf: SequenceEditorTree_SheetObject
}> = ({leaf}) => { }> = ({leaf}) => {
const collapsable = useSequenceEditorCollapsable(
createStudioSheetItemKey.forSheetObject(leaf.sheetObject),
)
return usePrism(() => { return usePrism(() => {
return ( return (
<CompoundRow leaf={leaf} label={leaf.sheetObject.address.objectKey}> <AnyCompositeRow
leaf={leaf}
label={leaf.sheetObject.address.objectKey}
collapsable={collapsable}
>
{leaf.children.map((leaf) => decideRowByPropType(leaf))} {leaf.children.map((leaf) => decideRowByPropType(leaf))}
</CompoundRow> </AnyCompositeRow>
) )
}, [leaf]) }, [leaf])
} }
export default SheetObjectRow export default LeftSheetObjectRow

View file

@ -1,7 +1,7 @@
import type {SequenceEditorTree_Sheet} from '@theatre/studio/panels/SequenceEditorPanel/layout/tree' import type {SequenceEditorTree_Sheet} from '@theatre/studio/panels/SequenceEditorPanel/layout/tree'
import {usePrism} from '@theatre/react' import {usePrism} from '@theatre/react'
import React from 'react' import React from 'react'
import SheetObjectRow from './SheetObjectRow' import LeftSheetObjectRow from './SheetObjectRow'
const SheetRow: React.VFC<{ const SheetRow: React.VFC<{
leaf: SequenceEditorTree_Sheet leaf: SequenceEditorTree_Sheet
@ -10,7 +10,7 @@ const SheetRow: React.VFC<{
return ( return (
<> <>
{leaf.children.map((sheetObjectLeaf) => ( {leaf.children.map((sheetObjectLeaf) => (
<SheetObjectRow <LeftSheetObjectRow
key={'sheetObject-' + sheetObjectLeaf.sheetObject.address.objectKey} key={'sheetObject-' + sheetObjectLeaf.sheetObject.address.objectKey}
leaf={sheetObjectLeaf} leaf={sheetObjectLeaf}
/> />

View file

@ -6,7 +6,7 @@ import type {Pointer} from '@theatre/dataverse'
import {val} from '@theatre/dataverse' import {val} from '@theatre/dataverse'
import React from 'react' import React from 'react'
import KeyframedTrack from './BasicKeyframedTrack/BasicKeyframedTrack' import KeyframedTrack from './BasicKeyframedTrack/BasicKeyframedTrack'
import Row from './Row' import RightRow from './Row'
const PrimitivePropRow: React.FC<{ const PrimitivePropRow: React.FC<{
leaf: SequenceEditorTree_PrimitiveProp leaf: SequenceEditorTree_PrimitiveProp
@ -27,13 +27,13 @@ const PrimitivePropRow: React.FC<{
console.error( console.error(
`trackData type ${trackData?.type} is not yet supported on the sequence editor`, `trackData type ${trackData?.type} is not yet supported on the sequence editor`,
) )
return <Row leaf={leaf} node={<div />}></Row> return <RightRow leaf={leaf} node={<div />}></RightRow>
} else { } else {
const node = ( const node = (
<KeyframedTrack layoutP={layoutP} trackData={trackData} leaf={leaf} /> <KeyframedTrack layoutP={layoutP} trackData={trackData} leaf={leaf} />
) )
return <Row leaf={leaf} node={node}></Row> return <RightRow leaf={leaf} node={node}></RightRow>
} }
}, [leaf, layoutP]) }, [leaf, layoutP])
} }

View file

@ -7,7 +7,9 @@ import {usePrism} from '@theatre/react'
import type {Pointer} from '@theatre/dataverse' import type {Pointer} from '@theatre/dataverse'
import React from 'react' import React from 'react'
import PrimitivePropRow from './PrimitivePropRow' import PrimitivePropRow from './PrimitivePropRow'
import Row from './Row' import RightRow from './Row'
import {useSequenceEditorCollapsable} from '@theatre/studio/panels/SequenceEditorPanel/DopeSheet/useSequenceEditorCollapsable'
import {createStudioSheetItemKey} from '@theatre/shared/utils/ids'
export const decideRowByPropType = ( export const decideRowByPropType = (
leaf: SequenceEditorTree_PropWithChildren | SequenceEditorTree_PrimitiveProp, leaf: SequenceEditorTree_PropWithChildren | SequenceEditorTree_PrimitiveProp,
@ -27,21 +29,26 @@ export const decideRowByPropType = (
/> />
) )
const PropWithChildrenRow: React.FC<{ const PropWithChildrenRow: React.VFC<{
leaf: SequenceEditorTree_PropWithChildren leaf: SequenceEditorTree_PropWithChildren
layoutP: Pointer<SequenceEditorPanelLayout> layoutP: Pointer<SequenceEditorPanelLayout>
}> = ({leaf, layoutP}) => { }> = ({leaf, layoutP}) => {
const collapsable = useSequenceEditorCollapsable(
createStudioSheetItemKey.forSheetObjectProp(
leaf.sheetObject,
leaf.pathToProp,
),
)
return usePrism(() => { return usePrism(() => {
const node = <div /> const node = <div />
return ( return (
<Row leaf={leaf} node={node}> <RightRow leaf={leaf} node={node} collapsable={collapsable}>
{leaf.children.map((propLeaf) => {leaf.children.map((propLeaf) =>
decideRowByPropType(propLeaf, layoutP), decideRowByPropType(propLeaf, layoutP),
)} )}
</Row> </RightRow>
) )
}, [leaf, layoutP]) }, [leaf, layoutP])
} }
export default PropWithChildrenRow

View file

@ -1,6 +1,8 @@
import type {SequenceEditorTree_Row} from '@theatre/studio/panels/SequenceEditorPanel/layout/tree' import type {SequenceEditorTree_Row} from '@theatre/studio/panels/SequenceEditorPanel/layout/tree'
import React from 'react' import React from 'react'
import styled from 'styled-components' import styled from 'styled-components'
import type {ICollapsableItem} from '@theatre/studio/panels/SequenceEditorPanel/DopeSheet/useSequenceEditorCollapsable'
import {useVal} from '@theatre/react'
const Container = styled.li<{}>` const Container = styled.li<{}>`
margin: 0; margin: 0;
@ -35,11 +37,13 @@ const Children = styled.ul`
list-style: none; list-style: none;
` `
const Row: React.FC<{ const RightRow: React.FC<{
leaf: SequenceEditorTree_Row<unknown> leaf: SequenceEditorTree_Row<unknown>
node: React.ReactElement node: React.ReactElement
}> = ({leaf, children, node}) => { collapsable?: ICollapsableItem
}> = ({leaf, children, node, collapsable}) => {
const hasChildren = Array.isArray(children) && children.length > 0 const hasChildren = Array.isArray(children) && children.length > 0
const isCollapsed = useVal(collapsable?.isCollapsed) ?? false
return ( return (
<Container> <Container>
@ -49,9 +53,9 @@ const Row: React.FC<{
> >
{node} {node}
</NodeWrapper> </NodeWrapper>
{hasChildren && <Children>{children}</Children>} {hasChildren && !isCollapsed && <Children>{children}</Children>}
</Container> </Container>
) )
} }
export default Row export default RightRow

View file

@ -3,24 +3,26 @@ import type {SequenceEditorTree_SheetObject} from '@theatre/studio/panels/Sequen
import {usePrism} from '@theatre/react' import {usePrism} from '@theatre/react'
import type {Pointer} from '@theatre/dataverse' import type {Pointer} from '@theatre/dataverse'
import React from 'react' import React from 'react'
import styled from 'styled-components'
import {decideRowByPropType} from './PropWithChildrenRow' import {decideRowByPropType} from './PropWithChildrenRow'
import Row from './Row' import RightRow from './Row'
import {useSequenceEditorCollapsable} from '@theatre/studio/panels/SequenceEditorPanel/DopeSheet/useSequenceEditorCollapsable'
import {createStudioSheetItemKey} from '@theatre/shared/utils/ids'
const Container = styled.div`` const RightSheetObjectRow: React.VFC<{
const SheetObjectRow: React.FC<{
leaf: SequenceEditorTree_SheetObject leaf: SequenceEditorTree_SheetObject
layoutP: Pointer<SequenceEditorPanelLayout> layoutP: Pointer<SequenceEditorPanelLayout>
}> = ({leaf, layoutP}) => { }> = ({leaf, layoutP}) => {
const collapsable = useSequenceEditorCollapsable(
createStudioSheetItemKey.forSheetObject(leaf.sheetObject),
)
return usePrism(() => { return usePrism(() => {
const node = <div /> const node = <div />
return ( return (
<Row leaf={leaf} node={node}> <RightRow leaf={leaf} node={node} collapsable={collapsable}>
{leaf.children.map((leaf) => decideRowByPropType(leaf, layoutP))} {leaf.children.map((leaf) => decideRowByPropType(leaf, layoutP))}
</Row> </RightRow>
) )
}, [leaf, layoutP]) }, [leaf, layoutP])
} }
export default SheetObjectRow export default RightSheetObjectRow

View file

@ -3,7 +3,7 @@ import type {SequenceEditorTree_Sheet} from '@theatre/studio/panels/SequenceEdit
import {usePrism} from '@theatre/react' import {usePrism} from '@theatre/react'
import type {Pointer} from '@theatre/dataverse' import type {Pointer} from '@theatre/dataverse'
import React from 'react' import React from 'react'
import SheetObjectRow from './SheetObjectRow' import RightSheetObjectRow from './SheetObjectRow'
const SheetRow: React.FC<{ const SheetRow: React.FC<{
leaf: SequenceEditorTree_Sheet leaf: SequenceEditorTree_Sheet
@ -13,7 +13,7 @@ const SheetRow: React.FC<{
return ( return (
<> <>
{leaf.children.map((sheetObjectLeaf) => ( {leaf.children.map((sheetObjectLeaf) => (
<SheetObjectRow <RightSheetObjectRow
layoutP={layoutP} layoutP={layoutP}
key={'sheetObject-' + sheetObjectLeaf.sheetObject.address.objectKey} key={'sheetObject-' + sheetObjectLeaf.sheetObject.address.objectKey}
leaf={sheetObjectLeaf} leaf={sheetObjectLeaf}

View file

@ -0,0 +1,91 @@
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<boolean>
toggleCollapsed(): void
}
type ICollapsableContext = {
getCollapsable(sheetItemKey: StudioSheetItemKey): ICollapsableItem
}
const CollapsableContext = React.createContext<ICollapsableContext>(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<SequenceEditorPanelLayout>
}>,
) {
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 (
<CollapsableContext.Provider value={contextValue}>
<ProviderChildrenMemo children={props.children} />
</CollapsableContext.Provider>
)
}

View file

@ -28,6 +28,7 @@ import {
TitleBar_Punctuation, TitleBar_Punctuation,
} from '@theatre/studio/panels/BasePanel/common' } from '@theatre/studio/panels/BasePanel/common'
import type {UIPanelId} from '@theatre/shared/utils/ids' import type {UIPanelId} from '@theatre/shared/utils/ids'
import {ProvideCollapsable} from './DopeSheet/useSequenceEditorCollapsable'
const Container = styled(PanelWrapper)` const Container = styled(PanelWrapper)`
z-index: ${panelZIndexes.sequenceEditorPanel}; z-index: ${panelZIndexes.sequenceEditorPanel};
@ -163,15 +164,17 @@ const Content: React.VFC<{}> = () => {
return ( return (
<Container ref={containerRef}> <Container ref={containerRef}>
<LeftBackground style={{width: `${val(layoutP.leftDims.width)}px`}} /> <LeftBackground style={{width: `${val(layoutP.leftDims.width)}px`}} />
<FrameStampPositionProvider layoutP={layoutP}> <ProvideCollapsable sheetId={sheet.address.sheetId} layoutP={layoutP}>
<Header layoutP={layoutP} /> <FrameStampPositionProvider layoutP={layoutP}>
<DopeSheet key={key + '-dopeSheet'} layoutP={layoutP} /> <Header layoutP={layoutP} />
{graphEditorOpen && ( <DopeSheet key={key + '-dopeSheet'} layoutP={layoutP} />
<GraphEditor key={key + '-graphEditor'} layoutP={layoutP} /> {graphEditorOpen && (
)} <GraphEditor key={key + '-graphEditor'} layoutP={layoutP} />
{graphEditorAvailable && <GraphEditorToggle layoutP={layoutP} />} )}
<RightOverlay layoutP={layoutP} /> {graphEditorAvailable && <GraphEditorToggle layoutP={layoutP} />}
</FrameStampPositionProvider> <RightOverlay layoutP={layoutP} />
</FrameStampPositionProvider>
</ProvideCollapsable>
</Container> </Container>
) )
}, [dims]) }, [dims])

View file

@ -13,6 +13,7 @@ import type {
} from '@theatre/shared/utils/addresses' } from '@theatre/shared/utils/addresses'
import {encodePathToProp} from '@theatre/shared/utils/addresses' import {encodePathToProp} from '@theatre/shared/utils/addresses'
import type { import type {
StudioSheetItemKey,
KeyframeId, KeyframeId,
SequenceMarkerId, SequenceMarkerId,
SequenceTrackId, SequenceTrackId,
@ -482,6 +483,35 @@ namespace stateEditors {
).clippedSpaceRange = {...p.range} ).clippedSpaceRange = {...p.range}
} }
} }
export namespace sequenceEditorCollapsableItems {
function _ensure(p: WithoutSheetInstance<SheetAddress>) {
const seq =
stateEditors.studio.ahistoric.projects.stateByProjectId.stateBySheetId.sequence._ensure(
p,
)
let existing = seq.sequenceEditorCollapsableItems
if (!existing) {
existing = seq.sequenceEditorCollapsableItems =
pointableSetUtil.create()
}
return existing
}
export function set(
p: WithoutSheetInstance<SheetAddress> & {
studioSheetItemKey: StudioSheetItemKey
isCollapsed: boolean
},
) {
const collapsableSet = _ensure(p)
Object.assign(
collapsableSet,
pointableSetUtil.add(collapsableSet, p.studioSheetItemKey, {
isCollapsed: p.isCollapsed,
}),
)
}
}
} }
} }
} }

View file

@ -1,10 +1,9 @@
import type {ProjectState} from '@theatre/core/projects/store/storeTypes' import type {ProjectState} from '@theatre/core/projects/store/storeTypes'
import type {Keyframe} from '@theatre/core/projects/store/types/SheetState_Historic' import type {Keyframe} from '@theatre/core/projects/store/types/SheetState_Historic'
import type { import type {ProjectId, SheetId} from '@theatre/shared/utils/ids'
ProjectId,
SheetId,
} from '@theatre/shared/utils/ids'
import type {IRange, StrictRecord} from '@theatre/shared/utils/types' import type {IRange, StrictRecord} from '@theatre/shared/utils/types'
import type {PointableSet} from '@theatre/shared/utils/PointableSet'
import type {StudioSheetItemKey} from '@theatre/shared/utils/ids'
export type StudioAhistoricState = { export type StudioAhistoricState = {
visibilityState: 'everythingIsHidden' | 'everythingIsVisible' visibilityState: 'everythingIsHidden' | 'everythingIsVisible'
@ -48,6 +47,13 @@ export type StudioAhistoricState = {
enabled: boolean enabled: boolean
range: IRange range: IRange
} }
sequenceEditorCollapsableItems?: PointableSet<
StudioSheetItemKey,
{
isCollapsed: boolean
}
>
} }
} }
> >