From c74aa1b930019dddcbe19cbd39d460308595504e Mon Sep 17 00:00:00 2001 From: Aria Minaei Date: Tue, 7 Jun 2022 12:56:51 +0200 Subject: [PATCH] Show hovered prop from sequence editor in details panel * Remove an un-needed prism Co-authored-by: Cole Lawrence Co-authored-by: Aria Minaei --- .../DetailCompoundPropEditor.tsx | 28 ++++-- .../DetailSimplePropEditor.tsx | 22 ++++- .../SingleRowPropEditor.tsx | 23 +++-- .../getDetailRowHighlightBackground.tsx | 13 +++ .../rowIndentationFormulaCSS.tsx | 1 + .../DopeSheet/Left/AnyCompositeRow.tsx | 20 +++- .../DopeSheet/Left/PrimitivePropRow.tsx | 7 +- .../DopeSheet/Left/PropWithChildrenRow.tsx | 27 +++--- .../Left/usePropHighlightMouseEnter.tsx | 40 ++++++++ .../whatPropIsHighlighted.ts | 96 +++++++++++++++++++ 10 files changed, 240 insertions(+), 37 deletions(-) create mode 100644 theatre/studio/src/panels/DetailPanel/DeterminePropEditorForDetail/getDetailRowHighlightBackground.tsx create mode 100644 theatre/studio/src/panels/DetailPanel/DeterminePropEditorForDetail/rowIndentationFormulaCSS.tsx create mode 100644 theatre/studio/src/panels/SequenceEditorPanel/DopeSheet/Left/usePropHighlightMouseEnter.tsx create mode 100644 theatre/studio/src/panels/SequenceEditorPanel/whatPropIsHighlighted.ts diff --git a/theatre/studio/src/panels/DetailPanel/DeterminePropEditorForDetail/DetailCompoundPropEditor.tsx b/theatre/studio/src/panels/DetailPanel/DeterminePropEditorForDetail/DetailCompoundPropEditor.tsx index f8525d2..97781a0 100644 --- a/theatre/studio/src/panels/DetailPanel/DeterminePropEditorForDetail/DetailCompoundPropEditor.tsx +++ b/theatre/studio/src/panels/DetailPanel/DeterminePropEditorForDetail/DetailCompoundPropEditor.tsx @@ -5,9 +5,9 @@ import {getPointerParts} from '@theatre/dataverse' import type {Pointer} from '@theatre/dataverse' import last from 'lodash-es/last' import {darken, transparentize} from 'polished' -import React from 'react' +import React, {useMemo} from 'react' import styled from 'styled-components' -import {indentationFormula} from '@theatre/studio/panels/DetailPanel/DeterminePropEditorForDetail/SingleRowPropEditor' +import {rowIndentationFormulaCSS} from '@theatre/studio/panels/DetailPanel/DeterminePropEditorForDetail/rowIndentationFormulaCSS' import {propNameTextCSS} from '@theatre/studio/propEditors/utils/propNameTextCSS' import {pointerEventsAutoInNormalMode} from '@theatre/studio/css' import useRefAndState from '@theatre/studio/utils/useRefAndState' @@ -16,6 +16,10 @@ import type SheetObject from '@theatre/core/sheetObjects/SheetObject' import useContextMenu from '@theatre/studio/uiComponents/simpleContextMenu/useContextMenu' import {useEditingToolsForCompoundProp} from '@theatre/studio/propEditors/useEditingToolsForCompoundProp' +import type {PropHighlighted} from '@theatre/studio/panels/SequenceEditorPanel/whatPropIsHighlighted' +import {whatPropIsHighlighted} from '@theatre/studio/panels/SequenceEditorPanel/whatPropIsHighlighted' +import {deriver} from '@theatre/studio/utils/derive-utils' +import {getDetailRowHighlightBackground} from './getDetailRowHighlightBackground' const Container = styled.div` --step: 15px; @@ -23,15 +27,17 @@ const Container = styled.div` ${pointerEventsAutoInNormalMode}; ` -const Header = styled.div` +const Header = deriver(styled.div<{isHighlighted: PropHighlighted}>` height: 30px; display: flex; align-items: stretch; position: relative; -` + + background-color: ${getDetailRowHighlightBackground}; +`) const Padding = styled.div` - padding-left: ${indentationFormula}; + padding-left: ${rowIndentationFormulaCSS}; display: flex; align-items: center; ` @@ -99,12 +105,22 @@ function DetailCompoundPropEditor< const lastSubPropIsComposite = compositeSubs.length > 0 + const isPropHighlightedD = useMemo( + () => + whatPropIsHighlighted.getIsPropHighlightedD({ + ...obj.address, + pathToProp: getPointerParts(pointerToProp).path, + }), + [pointerToProp], + ) + // previous versions of the DetailCompoundPropEditor had a context menu item for "Reset values". return ( {contextMenu}
@@ -138,4 +154,4 @@ function DetailCompoundPropEditor< ) } -export default DetailCompoundPropEditor +export default React.memo(DetailCompoundPropEditor) diff --git a/theatre/studio/src/panels/DetailPanel/DeterminePropEditorForDetail/DetailSimplePropEditor.tsx b/theatre/studio/src/panels/DetailPanel/DeterminePropEditorForDetail/DetailSimplePropEditor.tsx index a82431a..ba6db62 100644 --- a/theatre/studio/src/panels/DetailPanel/DeterminePropEditorForDetail/DetailSimplePropEditor.tsx +++ b/theatre/studio/src/panels/DetailPanel/DeterminePropEditorForDetail/DetailSimplePropEditor.tsx @@ -2,12 +2,14 @@ import type { IBasePropType, PropTypeConfig_AllSimples, } from '@theatre/core/propTypes' -import React from 'react' +import React, {useMemo} from 'react' import {useEditingToolsForSimplePropInDetailsPanel} from '@theatre/studio/propEditors/useEditingToolsForSimpleProp' import {SingleRowPropEditor} from '@theatre/studio/panels/DetailPanel/DeterminePropEditorForDetail/SingleRowPropEditor' import type {Pointer} from '@theatre/dataverse' +import {getPointerParts} from '@theatre/dataverse' import type SheetObject from '@theatre/core/sheetObjects/SheetObject' import type {ISimplePropEditorReactProps} from '@theatre/studio/propEditors/simpleEditors/ISimplePropEditorReactProps' +import {whatPropIsHighlighted} from '@theatre/studio/panels/SequenceEditorPanel/whatPropIsHighlighted' export type IDetailSimplePropEditorProps< TPropTypeConfig extends IBasePropType, @@ -37,9 +39,23 @@ function DetailSimplePropEditor< propConfig, ) + const isPropHighlightedD = useMemo( + () => + whatPropIsHighlighted.getIsPropHighlightedD({ + ...obj.address, + pathToProp: getPointerParts(pointerToProp).path, + }), + [pointerToProp], + ) + return ( ` display: flex; height: 30px; justify-content: flex-start; @@ -28,11 +32,13 @@ const Container = styled.div` --right-width: 60%; position: relative; ${pointerEventsAutoInNormalMode}; -` + + background-color: ${getDetailRowHighlightBackground}; +`) const Left = styled.div` box-sizing: border-box; - padding-left: ${indentationFormula}; + padding-left: ${rowIndentationFormulaCSS}; padding-right: 4px; display: flex; flex-direction: row; @@ -84,6 +90,7 @@ type ISingleRowPropEditorProps = { propConfig: propTypes.PropTypeConfig pointerToProp: Pointer editingTools: ReturnType + isPropHighlightedD: IDerivation } export function SingleRowPropEditor({ @@ -91,6 +98,7 @@ export function SingleRowPropEditor({ pointerToProp, editingTools, children, + isPropHighlightedD, }: React.PropsWithChildren>): React.ReactElement< any, any @@ -105,11 +113,10 @@ export function SingleRowPropEditor({ }) return ( - + {contextMenu} {editingTools.controlIndicators} - ` --depth: ${(props) => props.depth}; @@ -66,7 +71,10 @@ const LeftRowChildren = styled.ul` ` const AnyCompositeRow: React.FC<{ - leaf: SequenceEditorTree_Row + leaf: + | SequenceEditorTree_PrimitiveProp + | SequenceEditorTree_PropWithChildren + | SequenceEditorTree_SheetObject label: React.ReactNode toggleSelect?: VoidFn toggleCollapsed: VoidFn @@ -85,8 +93,12 @@ const AnyCompositeRow: React.FC<{ }) => { const hasChildren = Array.isArray(children) && children.length > 0 + const containerRef = useRef(null) + + usePropHighlightMouseEnter(containerRef.current, leaf) + return leaf.shouldRender ? ( - + (null) + + usePropHighlightMouseEnter(containerRef.current, leaf) + return ( - + = ({leaf}) => { - return usePrism(() => { - return ( - - setCollapsedSheetObjectOrCompoundProp(!leaf.isCollapsed, leaf) - } - > - {leaf.children.map((propLeaf) => decideRowByPropType(propLeaf))} - - ) - }, [leaf]) + return ( + + setCollapsedSheetObjectOrCompoundProp(!leaf.isCollapsed, leaf) + } + > + {leaf.children.map((propLeaf) => decideRowByPropType(propLeaf))} + + ) } export default PropWithChildrenRow diff --git a/theatre/studio/src/panels/SequenceEditorPanel/DopeSheet/Left/usePropHighlightMouseEnter.tsx b/theatre/studio/src/panels/SequenceEditorPanel/DopeSheet/Left/usePropHighlightMouseEnter.tsx new file mode 100644 index 0000000..9a72ebd --- /dev/null +++ b/theatre/studio/src/panels/SequenceEditorPanel/DopeSheet/Left/usePropHighlightMouseEnter.tsx @@ -0,0 +1,40 @@ +import type {SequenceEditorTree_AllRowTypes} from '@theatre/studio/panels/SequenceEditorPanel/layout/tree' +import type {PropAddress} from '@theatre/shared/utils/addresses' +import {useLayoutEffect} from 'react' +import {whatPropIsHighlighted} from '@theatre/studio/panels/SequenceEditorPanel/whatPropIsHighlighted' + +/** This should ignore if */ +export function usePropHighlightMouseEnter( + node: HTMLElement | null, + leaf: SequenceEditorTree_AllRowTypes, +) { + useLayoutEffect(() => { + if (!node) return + if (leaf.type !== 'propWithChildren' && leaf.type !== 'primitiveProp') + return + + let unlock: null | (() => void) = null + const propAddress: PropAddress = { + ...leaf.sheetObject.address, + pathToProp: leaf.pathToProp, + } + + function onMouseEnter() { + unlock = whatPropIsHighlighted.replaceLock(propAddress, () => { + // cleanup on forced unlock + }) + } + function onMouseLeave() { + unlock?.() + } + + node.addEventListener('mouseenter', onMouseEnter) + node.addEventListener('mouseleave', onMouseLeave) + + return () => { + unlock?.() + node.removeEventListener('mouseenter', onMouseEnter) + node.removeEventListener('mouseleave', onMouseLeave) + } + }, [node]) +} diff --git a/theatre/studio/src/panels/SequenceEditorPanel/whatPropIsHighlighted.ts b/theatre/studio/src/panels/SequenceEditorPanel/whatPropIsHighlighted.ts new file mode 100644 index 0000000..0f0207f --- /dev/null +++ b/theatre/studio/src/panels/SequenceEditorPanel/whatPropIsHighlighted.ts @@ -0,0 +1,96 @@ +import type {IDerivation} from '@theatre/dataverse' +import {val} from '@theatre/dataverse' +import {Atom} from '@theatre/dataverse' +import {prism} from '@theatre/dataverse' +import type { + PropAddress, + WithoutSheetInstance, +} from '@theatre/shared/utils/addresses' + +import pointerDeep from '@theatre/shared/utils/pointerDeep' +import type {$IntentionalAny, VoidFn} from '@theatre/shared/utils/types' +import lodashSet from 'lodash-es/set' + +/** constant global manager */ +export const whatPropIsHighlighted = createWhatPropIsHighlightedState() + +export type PropHighlighted = 'self' | 'descendent' | null + +/** Only used in prop highlighting with boolean. */ +type PathToPropAsDeepObject = { + [key in string]: T | PathToPropAsDeepObject +} + +function createWhatPropIsHighlightedState() { + let lastLockId = 0 + const whatIsHighlighted = new Atom< + | {hasLock: false; deepPath?: undefined} + | { + hasLock: true + lockId: number + cleanup: () => void + deepPath: PathToPropAsDeepObject + } + >({hasLock: false}) + + return { + replaceLock(address: WithoutSheetInstance, cleanup: VoidFn) { + const lockId = lastLockId++ + + const existingState = whatIsHighlighted.getState() + if (existingState.hasLock) existingState.cleanup() + + whatIsHighlighted.setState({ + hasLock: true, + lockId, + cleanup, + deepPath: arrayToDeepObject(addressToArray(address)), + }) + + return function unlock() { + const curr = whatIsHighlighted.getState() + if (curr.hasLock && curr.lockId === lockId) { + curr.cleanup() + whatIsHighlighted.setState({hasLock: false}) + } + } + }, + getIsPropHighlightedD( + address: WithoutSheetInstance, + ): IDerivation { + const highlightedP = pointerDeep( + whatIsHighlighted.pointer.deepPath, + addressToArray(address), + ) + return prism(() => { + const value = val(highlightedP) + return value === true + ? 'self' + : // obj continues deep path prop from here + value + ? 'descendent' + : // some other prop or no lock + null + }) + }, + } +} + +function addressToArray( + address: WithoutSheetInstance, +): Array { + return [ + address.projectId, + address.sheetId, + address.objectKey, + ...address.pathToProp, + ] +} + +function arrayToDeepObject( + arr: Array, +): PathToPropAsDeepObject { + const obj = {} + lodashSet(obj, arr, true) + return obj as $IntentionalAny +}