Compact and collapsible compound prop editor
* Create compact vector prop editor * MAke all compound props collapsible * Add collapsed indicator for compound props * Persist collapsed state accross component rerenders * Adjust dom playground to use the new vector prop Co-authored-by: Andrew Prifer <andrew.prifer@gmail.com>
This commit is contained in:
parent
f6361e7905
commit
feb3ad34b8
4 changed files with 168 additions and 34 deletions
|
@ -1,11 +1,14 @@
|
|||
import type {PropTypeConfig_Compound} from '@theatre/core/propTypes'
|
||||
import type {
|
||||
PropTypeConfig_Compound,
|
||||
PropTypeConfig_Number,
|
||||
} from '@theatre/core/propTypes'
|
||||
import {isPropConfigComposite} from '@theatre/shared/propTypes/utils'
|
||||
import type {$FixMe} from '@theatre/shared/utils/types'
|
||||
import {getPointerParts} from '@theatre/dataverse'
|
||||
import {Box, getPointerParts} from '@theatre/dataverse'
|
||||
import type {Pointer} from '@theatre/dataverse'
|
||||
import last from 'lodash-es/last'
|
||||
import {darken, transparentize} from 'polished'
|
||||
import React, {useMemo} from 'react'
|
||||
import React, {useLayoutEffect, useMemo} from 'react'
|
||||
import styled from 'styled-components'
|
||||
import {rowIndentationFormulaCSS} from '@theatre/studio/panels/DetailPanel/DeterminePropEditorForDetail/rowIndentationFormulaCSS'
|
||||
import {propNameTextCSS} from '@theatre/studio/propEditors/utils/propNameTextCSS'
|
||||
|
@ -20,11 +23,18 @@ import type {PropHighlighted} from '@theatre/studio/panels/SequenceEditorPanel/w
|
|||
import {whatPropIsHighlighted} from '@theatre/studio/panels/SequenceEditorPanel/whatPropIsHighlighted'
|
||||
import {deriver} from '@theatre/studio/utils/derive-utils'
|
||||
import {getDetailRowHighlightBackground} from './getDetailRowHighlightBackground'
|
||||
import NumberPropEditor from '@theatre/studio/propEditors/simpleEditors/NumberPropEditor'
|
||||
import type {IDetailSimplePropEditorProps} from './DetailSimplePropEditor'
|
||||
import {useEditingToolsForSimplePropInDetailsPanel} from '@theatre/studio/propEditors/useEditingToolsForSimpleProp'
|
||||
import {EllipsisFill} from '@theatre/studio/uiComponents/icons'
|
||||
import {usePrism} from '@theatre/react'
|
||||
import {val} from '@theatre/dataverse'
|
||||
|
||||
const Container = styled.div`
|
||||
--step: 15px;
|
||||
--left-pad: 15px;
|
||||
${pointerEventsAutoInNormalMode};
|
||||
--right-width: 60%;
|
||||
`
|
||||
|
||||
const Header = deriver(styled.div<{isHighlighted: PropHighlighted}>`
|
||||
|
@ -40,6 +50,7 @@ const Padding = styled.div`
|
|||
padding-left: ${rowIndentationFormulaCSS};
|
||||
display: flex;
|
||||
align-items: center;
|
||||
width: calc(100% - var(--right-width));
|
||||
`
|
||||
|
||||
const PropName = deriver(styled.div<{isHighlighted: PropHighlighted}>`
|
||||
|
@ -48,6 +59,7 @@ const PropName = deriver(styled.div<{isHighlighted: PropHighlighted}>`
|
|||
height: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
user-select: none;
|
||||
&:hover {
|
||||
color: white;
|
||||
|
@ -63,6 +75,51 @@ const SubProps = styled.div<{depth: number; lastSubIsComposite: boolean}>`
|
|||
/* padding: ${(props) => (props.lastSubIsComposite ? 0 : '4px')} 0; */
|
||||
`
|
||||
|
||||
const isVectorProp = (propConfig: PropTypeConfig_Compound<any>) => {
|
||||
const props = Object.entries(propConfig.props)
|
||||
|
||||
return (
|
||||
props.length <= 3 &&
|
||||
props.every(
|
||||
([name, conf]) =>
|
||||
conf.type === 'number' && ['x', 'y', 'z'].includes(name),
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
function VectorComponentEditor<TPropTypeConfig extends PropTypeConfig_Number>({
|
||||
propConfig,
|
||||
pointerToProp,
|
||||
obj,
|
||||
SimpleEditorComponent: EditorComponent,
|
||||
}: IDetailSimplePropEditorProps<TPropTypeConfig>) {
|
||||
const editingTools = useEditingToolsForSimplePropInDetailsPanel(
|
||||
pointerToProp,
|
||||
obj,
|
||||
propConfig,
|
||||
)
|
||||
|
||||
return (
|
||||
<NumberPropEditor
|
||||
editingTools={editingTools}
|
||||
propConfig={propConfig}
|
||||
value={editingTools.value}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
const InputContainer = styled.div`
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: stretch;
|
||||
padding: 0 8px 0 2px;
|
||||
box-sizing: border-box;
|
||||
height: 100%;
|
||||
width: var(--right-width);
|
||||
flex-shrink: 0;
|
||||
flex-grow: 0;
|
||||
`
|
||||
|
||||
export type ICompoundPropDetailEditorProps<
|
||||
TPropTypeConfig extends PropTypeConfig_Compound<any>,
|
||||
> = {
|
||||
|
@ -114,6 +171,24 @@ function DetailCompoundPropEditor<
|
|||
[pointerToProp],
|
||||
)
|
||||
|
||||
const globalPointerPath = `${obj.address.projectId},${obj.address.sheetId},${
|
||||
obj.address.sheetInstanceId
|
||||
},${obj.address.objectKey},${getPointerParts(pointerToProp).path.join()}`
|
||||
|
||||
useLayoutEffect(() => {
|
||||
if (!collapsedMap.has(globalPointerPath)) {
|
||||
collapsedMap.set(globalPointerPath, new Box(isVectorProp(propConfig)))
|
||||
}
|
||||
}, [])
|
||||
|
||||
const box = collapsedMap.get(globalPointerPath)
|
||||
|
||||
const isCollapsed = usePrism(() => {
|
||||
const box = collapsedMap.get(globalPointerPath)
|
||||
|
||||
return box ? val(box.derivation) : isVectorProp(propConfig)
|
||||
}, [box])
|
||||
|
||||
return (
|
||||
<Container>
|
||||
{contextMenu}
|
||||
|
@ -127,34 +202,65 @@ function DetailCompoundPropEditor<
|
|||
<PropName
|
||||
isHighlighted={isPropHighlightedD}
|
||||
ref={propNameContainerRef}
|
||||
onClick={() => {
|
||||
box?.set(!box.get())
|
||||
}}
|
||||
>
|
||||
{propName || 'Props'}
|
||||
<span>{propName || 'Props'}</span>
|
||||
{!isVectorProp(propConfig) && isCollapsed && (
|
||||
<EllipsisFill
|
||||
width={24}
|
||||
height={24}
|
||||
style={{
|
||||
transform: 'translateY(2px)',
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</PropName>
|
||||
</Padding>
|
||||
{isVectorProp(propConfig) && isCollapsed && (
|
||||
<InputContainer>
|
||||
{[...allSubs].map(([subPropKey, subPropConfig]) => {
|
||||
return (
|
||||
<VectorComponentEditor
|
||||
key={'prop-' + subPropKey}
|
||||
// @ts-ignore
|
||||
propConfig={subPropConfig}
|
||||
pointerToProp={pointerToProp[subPropKey] as Pointer<$FixMe>}
|
||||
obj={obj}
|
||||
/>
|
||||
)
|
||||
})}
|
||||
</InputContainer>
|
||||
)}
|
||||
</Header>
|
||||
|
||||
<SubProps
|
||||
// @ts-ignore
|
||||
style={{'--depth': visualIndentation}}
|
||||
depth={visualIndentation}
|
||||
lastSubIsComposite={lastSubPropIsComposite}
|
||||
>
|
||||
{[...nonCompositeSubs, ...compositeSubs].map(
|
||||
([subPropKey, subPropConfig]) => {
|
||||
return (
|
||||
<DeterminePropEditorForDetail
|
||||
key={'prop-' + subPropKey}
|
||||
propConfig={subPropConfig}
|
||||
pointerToProp={pointerToProp[subPropKey] as Pointer<$FixMe>}
|
||||
obj={obj}
|
||||
visualIndentation={visualIndentation + 1}
|
||||
/>
|
||||
)
|
||||
},
|
||||
)}
|
||||
</SubProps>
|
||||
{!isCollapsed && (
|
||||
<SubProps
|
||||
// @ts-ignore
|
||||
style={{'--depth': visualIndentation}}
|
||||
depth={visualIndentation}
|
||||
lastSubIsComposite={lastSubPropIsComposite}
|
||||
>
|
||||
{[...nonCompositeSubs, ...compositeSubs].map(
|
||||
([subPropKey, subPropConfig]) => {
|
||||
return (
|
||||
<DeterminePropEditorForDetail
|
||||
key={'prop-' + subPropKey}
|
||||
propConfig={subPropConfig}
|
||||
pointerToProp={pointerToProp[subPropKey] as Pointer<$FixMe>}
|
||||
obj={obj}
|
||||
visualIndentation={visualIndentation + 1}
|
||||
/>
|
||||
)
|
||||
},
|
||||
)}
|
||||
</SubProps>
|
||||
)}
|
||||
</Container>
|
||||
)
|
||||
}
|
||||
|
||||
export default React.memo(DetailCompoundPropEditor)
|
||||
|
||||
const collapsedMap = new Map<string, Box<boolean>>()
|
||||
|
|
21
theatre/studio/src/uiComponents/icons/EllipsisFill.tsx
Normal file
21
theatre/studio/src/uiComponents/icons/EllipsisFill.tsx
Normal file
|
@ -0,0 +1,21 @@
|
|||
import * as React from 'react'
|
||||
|
||||
function EllipsisFill(props: React.SVGProps<SVGSVGElement>) {
|
||||
return (
|
||||
<svg
|
||||
width={16}
|
||||
height={16}
|
||||
viewBox="0 0 16 16"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
{...props}
|
||||
>
|
||||
<path
|
||||
d="M10.667 8a1.333 1.333 0 112.666 0 1.333 1.333 0 01-2.666 0zm-4 0a1.333 1.333 0 112.666 0 1.333 1.333 0 01-2.666 0zm-4 0a1.333 1.333 0 112.666 0 1.333 1.333 0 01-2.666 0z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
|
||||
export default EllipsisFill
|
|
@ -17,3 +17,4 @@ export {default as Package} from './Package'
|
|||
export {default as Bell} from './Bell'
|
||||
export {default as Trash} from './Trash'
|
||||
export {default as AddImage} from './AddImage'
|
||||
export {default as EllipsisFill} from './EllipsisFill'
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue