diff --git a/theatre/studio/src/panels/DetailPanel/propEditors/CompoundPropEditor.tsx b/theatre/studio/src/panels/DetailPanel/propEditors/CompoundPropEditor.tsx index 8fe7c29..b01cb21 100644 --- a/theatre/studio/src/panels/DetailPanel/propEditors/CompoundPropEditor.tsx +++ b/theatre/studio/src/panels/DetailPanel/propEditors/CompoundPropEditor.tsx @@ -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(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 ( - - { -
- - {/* - - */} - {/* */} - - {propName || 'Props'} - -
- } + return ( + + {contextMenu} +
+ + + {propName || 'Props'} + +
- - {[...nonCompositeSubs, ...compositeSubs].map( - ([subPropKey, subPropConfig]) => { - return ( - - ) - }, - )} - -
- ) - }, [pointerToProp, obj]) + + {[...nonCompositeSubs, ...compositeSubs].map( + ([subPropKey, subPropConfig]) => { + return ( + + ) + }, + )} + +
+ ) } export default CompoundPropEditor diff --git a/theatre/studio/src/store/stateEditors.ts b/theatre/studio/src/store/stateEditors.ts index d04fc17..49bc8cf 100644 --- a/theatre/studio/src/store/stateEditors.ts +++ b/theatre/studio/src/store/stateEditors.ts @@ -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 & { + 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 & {trackId: string}, ) { @@ -634,6 +665,15 @@ namespace stateEditors { return byObject[p.objectKey]! } + export function setValueOfCompoundProp( + p: WithoutSheetInstance & { + value: SerializableMap + }, + ) { + const existingOverrides = _ensure(p) + set(existingOverrides, p.pathToProp, p.value) + } + export function setValueOfPrimitiveProp( p: WithoutSheetInstance & { value: SerializablePrimitive diff --git a/theatre/studio/src/uiComponents/simpleContextMenu/RightClickMenu/Item.tsx b/theatre/studio/src/uiComponents/simpleContextMenu/RightClickMenu/Item.tsx index 2a31259..b7991cf 100644 --- a/theatre/studio/src/uiComponents/simpleContextMenu/RightClickMenu/Item.tsx +++ b/theatre/studio/src/uiComponents/simpleContextMenu/RightClickMenu/Item.tsx @@ -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 ( - + ) diff --git a/theatre/studio/src/uiComponents/simpleContextMenu/RightClickMenu/RightClickMenu.tsx b/theatre/studio/src/uiComponents/simpleContextMenu/RightClickMenu/RightClickMenu.tsx index df05206..8bfc560 100644 --- a/theatre/studio/src/uiComponents/simpleContextMenu/RightClickMenu/RightClickMenu.tsx +++ b/theatre/studio/src/uiComponents/simpleContextMenu/RightClickMenu/RightClickMenu.tsx @@ -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<{ { if (item.callback) { item.callback(e)