Context menu for compound props

* Make sub-props static
* Reset static sub-props

Also
* Context menu items can now be disabled
This commit is contained in:
Aria Minaei 2021-09-23 13:55:52 +02:00
parent 2c4b317934
commit 91a65752a9
4 changed files with 152 additions and 50 deletions

View file

@ -1,9 +1,11 @@
import type {PropTypeConfig_Compound} from '@theatre/core/propTypes' import type {PropTypeConfig_Compound} from '@theatre/core/propTypes'
import {isPropConfigComposite} from '@theatre/shared/propTypes/utils' import {isPropConfigComposite} from '@theatre/shared/propTypes/utils'
import type SheetObject from '@theatre/core/sheetObjects/SheetObject' import type SheetObject from '@theatre/core/sheetObjects/SheetObject'
import {usePrism} from '@theatre/react' import type {
import type {$IntentionalAny} from '@theatre/shared/utils/types' $IntentionalAny,
import {getPointerParts} from '@theatre/dataverse' SerializableMap,
} from '@theatre/shared/utils/types'
import {getPointerParts, val} from '@theatre/dataverse'
import last from 'lodash-es/last' import last from 'lodash-es/last'
import {darken, transparentize} from 'polished' import {darken, transparentize} from 'polished'
import React from 'react' import React from 'react'
@ -16,6 +18,11 @@ import {
} from './utils/SingleRowPropEditor' } from './utils/SingleRowPropEditor'
import DefaultOrStaticValueIndicator from './utils/DefaultValueIndicator' import DefaultOrStaticValueIndicator from './utils/DefaultValueIndicator'
import {pointerEventsAutoInNormalMode} from '@theatre/studio/css' import {pointerEventsAutoInNormalMode} from '@theatre/studio/css'
import type {IContextMenuItem} from '@theatre/studio/uiComponents/simpleContextMenu/useContextMenu'
import useContextMenu from '@theatre/studio/uiComponents/simpleContextMenu/useContextMenu'
import useRefAndState from '@theatre/studio/utils/useRefAndState'
import getStudio from '@theatre/studio/getStudio'
import getDeep from '@theatre/shared/utils/getDeep'
const Container = styled.div` const Container = styled.div`
--step: 8px; --step: 8px;
@ -45,6 +52,9 @@ const PropName = styled.div`
display: flex; display: flex;
align-items: center; align-items: center;
user-select: none; user-select: none;
&:hover {
color: white;
}
${() => propNameText}; ${() => propNameText};
` `
@ -72,29 +82,73 @@ const CompoundPropEditor: React.FC<{
([_, conf]) => !isPropConfigComposite(conf), ([_, conf]) => !isPropConfigComposite(conf),
) )
const [propNameContainerRef, propNameContainer] =
useRefAndState<HTMLDivElement | null>(null)
const [contextMenu] = useContextMenu(propNameContainer, {
items: () => {
const items: IContextMenuItem[] = []
const pathToProp = getPointerParts(pointerToProp).path
const validSequencedTracks = val(
obj.template.getMapOfValidSequenceTracks_forStudio(),
)
const possibleSequenceTrackIds = getDeep(validSequencedTracks, pathToProp)
const hasSequencedTracks = !!(
typeof possibleSequenceTrackIds === 'object' &&
possibleSequenceTrackIds &&
Object.keys(possibleSequenceTrackIds).length > 0
)
if (hasSequencedTracks) {
items.push({
label: 'Make All Static',
enabled: hasSequencedTracks,
callback: () => {
getStudio()!.transaction(({stateEditors}) => {
const propAddress = {...obj.address, pathToProp}
stateEditors.coreByProject.historic.sheetsById.sequence.setCompoundPropAsStatic(
{
...propAddress,
value: obj.getValueByPointer(
pointerToProp,
) as unknown as SerializableMap,
},
)
})
},
})
}
items.push({
label: 'Reset all',
callback: () => {
getStudio()!.transaction(({unset}) => {
unset(pointerToProp)
})
},
})
return items
},
})
const lastSubPropIsComposite = compositeSubs.length > 0 const lastSubPropIsComposite = compositeSubs.length > 0
return usePrism(() => {
return ( return (
<Container> <Container>
{ {contextMenu}
<Header <Header
// @ts-ignore // @ts-ignore
style={{'--depth': depth - 1}} style={{'--depth': depth - 1}}
> >
<Padding> <Padding>
{/* <IconContainer>
<HiOutlineChevronRight />
</IconContainer> */}
{/* <NextPrevKeyframeCursors
jumpToPosition={voidFn}
toggleKeyframeOnCurrentPosition={voidFn}
/> */}
<DefaultOrStaticValueIndicator hasStaticOverride={false} /> <DefaultOrStaticValueIndicator hasStaticOverride={false} />
<PropName>{propName || 'Props'}</PropName> <PropName ref={propNameContainerRef}>{propName || 'Props'}</PropName>
</Padding> </Padding>
</Header> </Header>
}
<SubProps <SubProps
// @ts-ignore // @ts-ignore
@ -118,7 +172,6 @@ const CompoundPropEditor: React.FC<{
</SubProps> </SubProps>
</Container> </Container>
) )
}, [pointerToProp, obj])
} }
export default CompoundPropEditor export default CompoundPropEditor

View file

@ -18,7 +18,11 @@ import {
} from '@theatre/shared/utils/ids' } from '@theatre/shared/utils/ids'
import removePathFromObject from '@theatre/shared/utils/removePathFromObject' import removePathFromObject from '@theatre/shared/utils/removePathFromObject'
import {transformNumber} from '@theatre/shared/utils/transformNumber' import {transformNumber} from '@theatre/shared/utils/transformNumber'
import type {IRange, SerializablePrimitive} from '@theatre/shared/utils/types' import type {
IRange,
SerializableMap,
SerializablePrimitive,
} from '@theatre/shared/utils/types'
import {current} from 'immer' import {current} from 'immer'
import findLastIndex from 'lodash-es/findLastIndex' import findLastIndex from 'lodash-es/findLastIndex'
import keyBy from 'lodash-es/keyBy' import keyBy from 'lodash-es/keyBy'
@ -461,6 +465,33 @@ namespace stateEditors {
) )
} }
export function setCompoundPropAsStatic(
p: WithoutSheetInstance<PropAddress> & {
value: SerializableMap
},
) {
const tracks = _ensureTracksOfObject(p)
for (const encodedPropPath of Object.keys(
tracks.trackIdByPropPath,
)) {
const propPath = JSON.parse(encodedPropPath)
const isSubOfTargetPath = p.pathToProp.every(
(key, i) => propPath[i] === key,
)
if (isSubOfTargetPath) {
const trackId = tracks.trackIdByPropPath[encodedPropPath]
if (typeof trackId !== 'string') continue
delete tracks.trackIdByPropPath[encodedPropPath]
delete tracks.trackData[trackId]
}
}
stateEditors.coreByProject.historic.sheetsById.staticOverrides.byObject.setValueOfCompoundProp(
p,
)
}
function _getTrack( function _getTrack(
p: WithoutSheetInstance<SheetObjectAddress> & {trackId: string}, p: WithoutSheetInstance<SheetObjectAddress> & {trackId: string},
) { ) {
@ -634,6 +665,15 @@ namespace stateEditors {
return byObject[p.objectKey]! return byObject[p.objectKey]!
} }
export function setValueOfCompoundProp(
p: WithoutSheetInstance<PropAddress> & {
value: SerializableMap
},
) {
const existingOverrides = _ensure(p)
set(existingOverrides, p.pathToProp, p.value)
}
export function setValueOfPrimitiveProp( export function setValueOfPrimitiveProp(
p: WithoutSheetInstance<PropAddress> & { p: WithoutSheetInstance<PropAddress> & {
value: SerializablePrimitive value: SerializablePrimitive

View file

@ -1,10 +1,11 @@
import noop from '@theatre/shared/utils/noop'
import type {ElementType} from 'react' import type {ElementType} from 'react'
import React from 'react' import React from 'react'
import styled from 'styled-components' import styled from 'styled-components'
export const height = 26 export const height = 26
const Container = styled.li` const Container = styled.li<{enabled: boolean}>`
height: ${height}px; height: ${height}px;
padding: 0 12px; padding: 0 12px;
margin: 0; margin: 0;
@ -13,6 +14,8 @@ const Container = styled.li`
font-size: 11px; font-size: 11px;
font-weight: 400; font-weight: 400;
position: relative; position: relative;
pointer-events: ${(props) => (props.enabled ? 'auto' : 'none')};
color: ${(props) => (props.enabled ? 'white' : '#AAA')};
&:after { &:after {
position: absolute; position: absolute;
@ -34,9 +37,13 @@ const Label = styled.span``
const Item: React.FC<{ const Item: React.FC<{
label: string | ElementType label: string | ElementType
onClick: (e: React.MouseEvent) => void onClick: (e: React.MouseEvent) => void
enabled: boolean
}> = (props) => { }> = (props) => {
return ( return (
<Container onClick={props.onClick}> <Container
onClick={props.enabled ? props.onClick : noop}
enabled={props.enabled}
>
<Label>{props.label}</Label> <Label>{props.label}</Label>
</Container> </Container>
) )

View file

@ -37,6 +37,7 @@ const Container = styled.ul`
export type IContextMenuItem = { export type IContextMenuItem = {
label: string | ElementType label: string | ElementType
callback?: (e: React.MouseEvent) => void callback?: (e: React.MouseEvent) => void
enabled?: boolean
// subs?: Item[] // subs?: Item[]
} }
@ -108,6 +109,7 @@ const RightClickMenu: React.FC<{
<Item <Item
key={`item-${i}`} key={`item-${i}`}
label={item.label} label={item.label}
enabled={item.enabled === false ? false : true}
onClick={(e) => { onClick={(e) => {
if (item.callback) { if (item.callback) {
item.callback(e) item.callback(e)