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:
parent
2c4b317934
commit
91a65752a9
4 changed files with 152 additions and 50 deletions
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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>
|
||||
)
|
||||
|
|
|
@ -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)
|
||||
|
|
Loading…
Reference in a new issue