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 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,53 +82,96 @@ 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>
|
<DefaultOrStaticValueIndicator hasStaticOverride={false} />
|
||||||
{/* <IconContainer>
|
<PropName ref={propNameContainerRef}>{propName || 'Props'}</PropName>
|
||||||
<HiOutlineChevronRight />
|
</Padding>
|
||||||
</IconContainer> */}
|
</Header>
|
||||||
{/* <NextPrevKeyframeCursors
|
|
||||||
jumpToPosition={voidFn}
|
|
||||||
toggleKeyframeOnCurrentPosition={voidFn}
|
|
||||||
/> */}
|
|
||||||
<DefaultOrStaticValueIndicator hasStaticOverride={false} />
|
|
||||||
<PropName>{propName || 'Props'}</PropName>
|
|
||||||
</Padding>
|
|
||||||
</Header>
|
|
||||||
}
|
|
||||||
|
|
||||||
<SubProps
|
<SubProps
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
style={{'--depth': depth}}
|
style={{'--depth': depth}}
|
||||||
depth={depth}
|
depth={depth}
|
||||||
lastSubIsComposite={lastSubPropIsComposite}
|
lastSubIsComposite={lastSubPropIsComposite}
|
||||||
>
|
>
|
||||||
{[...nonCompositeSubs, ...compositeSubs].map(
|
{[...nonCompositeSubs, ...compositeSubs].map(
|
||||||
([subPropKey, subPropConfig]) => {
|
([subPropKey, subPropConfig]) => {
|
||||||
return (
|
return (
|
||||||
<DeterminePropEditor
|
<DeterminePropEditor
|
||||||
key={'prop-' + subPropKey}
|
key={'prop-' + subPropKey}
|
||||||
propConfig={subPropConfig}
|
propConfig={subPropConfig}
|
||||||
pointerToProp={pointerToProp[subPropKey]}
|
pointerToProp={pointerToProp[subPropKey]}
|
||||||
obj={obj}
|
obj={obj}
|
||||||
depth={depth + 1}
|
depth={depth + 1}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
)}
|
)}
|
||||||
</SubProps>
|
</SubProps>
|
||||||
</Container>
|
</Container>
|
||||||
)
|
)
|
||||||
}, [pointerToProp, obj])
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default CompoundPropEditor
|
export default CompoundPropEditor
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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>
|
||||||
)
|
)
|
||||||
|
|
|
@ -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)
|
||||||
|
|
Loading…
Reference in a new issue