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 {isPropConfigComposite} from '@theatre/shared/propTypes/utils'
import type SheetObject from '@theatre/core/sheetObjects/SheetObject'
import {usePrism} from '@theatre/react'
import type {$IntentionalAny} from '@theatre/shared/utils/types'
import {getPointerParts} from '@theatre/dataverse'
import type {
$IntentionalAny,
SerializableMap,
} from '@theatre/shared/utils/types'
import {getPointerParts, val} from '@theatre/dataverse'
import last from 'lodash-es/last'
import {darken, transparentize} from 'polished'
import React from 'react'
@ -16,6 +18,11 @@ import {
} from './utils/SingleRowPropEditor'
import DefaultOrStaticValueIndicator from './utils/DefaultValueIndicator'
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`
--step: 8px;
@ -45,6 +52,9 @@ const PropName = styled.div`
display: flex;
align-items: center;
user-select: none;
&:hover {
color: white;
}
${() => propNameText};
`
@ -72,53 +82,96 @@ const CompoundPropEditor: React.FC<{
([_, 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
return usePrism(() => {
return (
<Container>
{
<Header
// @ts-ignore
style={{'--depth': depth - 1}}
>
<Padding>
{/* <IconContainer>
<HiOutlineChevronRight />
</IconContainer> */}
{/* <NextPrevKeyframeCursors
jumpToPosition={voidFn}
toggleKeyframeOnCurrentPosition={voidFn}
/> */}
<DefaultOrStaticValueIndicator hasStaticOverride={false} />
<PropName>{propName || 'Props'}</PropName>
</Padding>
</Header>
}
return (
<Container>
{contextMenu}
<Header
// @ts-ignore
style={{'--depth': depth - 1}}
>
<Padding>
<DefaultOrStaticValueIndicator hasStaticOverride={false} />
<PropName ref={propNameContainerRef}>{propName || 'Props'}</PropName>
</Padding>
</Header>
<SubProps
// @ts-ignore
style={{'--depth': depth}}
depth={depth}
lastSubIsComposite={lastSubPropIsComposite}
>
{[...nonCompositeSubs, ...compositeSubs].map(
([subPropKey, subPropConfig]) => {
return (
<DeterminePropEditor
key={'prop-' + subPropKey}
propConfig={subPropConfig}
pointerToProp={pointerToProp[subPropKey]}
obj={obj}
depth={depth + 1}
/>
)
},
)}
</SubProps>
</Container>
)
}, [pointerToProp, obj])
<SubProps
// @ts-ignore
style={{'--depth': depth}}
depth={depth}
lastSubIsComposite={lastSubPropIsComposite}
>
{[...nonCompositeSubs, ...compositeSubs].map(
([subPropKey, subPropConfig]) => {
return (
<DeterminePropEditor
key={'prop-' + subPropKey}
propConfig={subPropConfig}
pointerToProp={pointerToProp[subPropKey]}
obj={obj}
depth={depth + 1}
/>
)
},
)}
</SubProps>
</Container>
)
}
export default CompoundPropEditor

View file

@ -18,7 +18,11 @@ import {
} from '@theatre/shared/utils/ids'
import removePathFromObject from '@theatre/shared/utils/removePathFromObject'
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 findLastIndex from 'lodash-es/findLastIndex'
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(
p: WithoutSheetInstance<SheetObjectAddress> & {trackId: string},
) {
@ -634,6 +665,15 @@ namespace stateEditors {
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(
p: WithoutSheetInstance<PropAddress> & {
value: SerializablePrimitive

View file

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

View file

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