Make default and static overrides distinguishable

Right now it's easy for the user to distinguish between a sequenced prop and non-sequenced props.

However, among the non-sequenced ones, it's not possible to distinguish between those that have a static override, and those that don't. The goal of this commit is to make it easy to distinguish between the two.
This commit is contained in:
Aria Minaei 2023-02-03 12:48:19 +01:00
parent 5c135a3cb9
commit 6f8e91ed5f
5 changed files with 85 additions and 79 deletions

View file

@ -18,6 +18,7 @@ import type {
$FixMe, $FixMe,
$IntentionalAny, $IntentionalAny,
SerializableMap, SerializableMap,
SerializablePrimitive,
SerializableValue, SerializableValue,
} from '@theatre/shared/utils/types' } from '@theatre/shared/utils/types'
import type {Prism, Pointer} from '@theatre/dataverse' import type {Prism, Pointer} from '@theatre/dataverse'
@ -31,6 +32,7 @@ import {
isPropConfSequencable, isPropConfSequencable,
} from '@theatre/shared/propTypes/utils' } from '@theatre/shared/propTypes/utils'
import getOrderingOfPropTypeConfig from './getOrderingOfPropTypeConfig' import getOrderingOfPropTypeConfig from './getOrderingOfPropTypeConfig'
import type {SheetState_Historic} from '@theatre/core/projects/store/types/SheetState_Historic'
/** /**
* Given an object like: `{transform: {type: 'absolute', position: {x: 0}}}`, * Given an object like: `{transform: {type: 'absolute', position: {x: 0}}}`,
@ -64,6 +66,10 @@ export default class SheetObjectTemplate {
readonly _temp_actions_atom: Atom<SheetObjectActionsConfig> readonly _temp_actions_atom: Atom<SheetObjectActionsConfig>
readonly _cache = new SimpleCache() readonly _cache = new SimpleCache()
readonly project: Project readonly project: Project
readonly pointerToSheetState: Pointer<SheetState_Historic | undefined>
readonly pointerToStaticOverrides: Pointer<
SerializableMap<SerializablePrimitive> | undefined
>
get staticConfig() { get staticConfig() {
return this._config.get() return this._config.get()
@ -92,6 +98,14 @@ export default class SheetObjectTemplate {
this._config = new Atom(config) this._config = new Atom(config)
this._temp_actions_atom = new Atom(_temp_actions) this._temp_actions_atom = new Atom(_temp_actions)
this.project = sheetTemplate.project this.project = sheetTemplate.project
this.pointerToSheetState =
this.sheetTemplate.project.pointers.historic.sheetsById[
this.address.sheetId
]
this.pointerToStaticOverrides =
this.pointerToSheetState.staticOverrides.byObject[this.address.objectKey]
} }
createInstance( createInstance(
@ -132,17 +146,7 @@ export default class SheetObjectTemplate {
getStaticValues(): Prism<SerializableMap> { getStaticValues(): Prism<SerializableMap> {
return this._cache.get('getStaticValues', () => return this._cache.get('getStaticValues', () =>
prism(() => { prism(() => {
const pointerToSheetState = const json = val(this.pointerToStaticOverrides) ?? {}
this.sheetTemplate.project.pointers.historic.sheetsById[
this.address.sheetId
]
const json =
val(
pointerToSheetState.staticOverrides.byObject[
this.address.objectKey
],
) ?? {}
const config = val(this.configPointer) const config = val(this.configPointer)
const deserialized = config.deserializeAndSanitize(json) || {} const deserialized = config.deserializeAndSanitize(json) || {}

View file

@ -7,6 +7,7 @@ import type {
} from '@theatre/core/propTypes' } from '@theatre/core/propTypes'
import type {PathToProp} from '@theatre/shared/utils/addresses' import type {PathToProp} from '@theatre/shared/utils/addresses'
import type {$IntentionalAny} from '@theatre/shared/utils/types' import type {$IntentionalAny} from '@theatre/shared/utils/types'
import memoizeFn from '@theatre/shared/utils/memoizeFn'
/** /**
* Either compound or enum properties can be considered "composite" * Either compound or enum properties can be considered "composite"
@ -70,53 +71,32 @@ export function isPropConfSequencable(
return !isPropConfigComposite(conf) // now all non-compounds are sequencable return !isPropConfigComposite(conf) // now all non-compounds are sequencable
} }
const compoundPropSequenceabilityCache = new WeakMap<
PropTypeConfig_Compound<{}> | PropTypeConfig_Enum,
boolean
>()
/**
* See {@link compoundHasSimpleDescendantsImpl}
*/
export function compoundHasSimpleDescendants(
conf: PropTypeConfig_Compound<{}> | PropTypeConfig_Enum,
): boolean {
if (!compoundPropSequenceabilityCache.has(conf)) {
compoundPropSequenceabilityCache.set(
conf,
compoundHasSimpleDescendantsImpl(conf),
)
}
return compoundPropSequenceabilityCache.get(conf)!
}
/** /**
* This basically checks of the compound prop has at least one simple prop in its descendants. * This basically checks of the compound prop has at least one simple prop in its descendants.
* In other words, if the compound props has no subs, or its subs are only compounds that eventually * In other words, if the compound props has no subs, or its subs are only compounds that eventually
* don't have simple subs, this will return false. * don't have simple subs, this will return false.
*/ */
function compoundHasSimpleDescendantsImpl( export const compoundHasSimpleDescendants = memoizeFn(
conf: PropTypeConfig_Compound<{}> | PropTypeConfig_Enum, (conf: PropTypeConfig_Compound<{}> | PropTypeConfig_Enum): boolean => {
): boolean { if (conf.type === 'enum') {
if (conf.type === 'enum') { throw new Error(`Not implemented yet for enums`)
throw new Error(`Not implemented yet for enums`) }
}
for (const key in conf.props) { for (const key in conf.props) {
const subConf = conf.props[ const subConf = conf.props[
key as $IntentionalAny as keyof typeof conf.props key as $IntentionalAny as keyof typeof conf.props
] as PropTypeConfig ] as PropTypeConfig
if (isPropConfigComposite(subConf)) { if (isPropConfigComposite(subConf)) {
if (compoundHasSimpleDescendants(subConf)) { if (compoundHasSimpleDescendants(subConf)) {
return true
}
} else {
return true return true
} }
} else {
return true
} }
} return false
return false },
} )
/** /**
* Iterates recursively over the simple props of a compound prop. Returns a generator. * Iterates recursively over the simple props of a compound prop. Returns a generator.

View file

@ -2,9 +2,9 @@ import {transparentize} from 'polished'
import React from 'react' import React from 'react'
import styled from 'styled-components' import styled from 'styled-components'
export const theme = { const theme = {
defaultState: { defaultState: {
color: transparentize(0.85, `#C4C4C4`), color: transparentize(0.95, `#C4C4C4`),
}, },
withStaticOverride: { withStaticOverride: {
color: transparentize(0.85, `#C4C4C4`), color: transparentize(0.85, `#C4C4C4`),
@ -28,24 +28,31 @@ const Rect = styled.rect`
fill: currentColor; fill: currentColor;
` `
const Icon = () => ( const DefaultIcon = styled.div`
<svg width: 5px;
width="5" height: 5px;
height="5" border-radius: 1px;
viewBox="0 0 5 5" /* border: 1px solid currentColor; */
fill="none" background-color: currentColor;
xmlns="http://www.w3.org/2000/svg" `
>
<Rect width="5" height="5" /> const FilledIcon = styled.div`
</svg> width: 5px;
) height: 5px;
background-color: currentColor;
border-radius: 1px;
`
const DefaultOrStaticValueIndicator: React.FC<{hasStaticOverride: boolean}> = ( const DefaultOrStaticValueIndicator: React.FC<{hasStaticOverride: boolean}> = (
props, props,
) => { ) => {
return ( return (
<Container hasStaticOverride={props.hasStaticOverride}> <Container hasStaticOverride={props.hasStaticOverride}>
<Icon /> {props.hasStaticOverride ? (
<FilledIcon title="The default value is overridden" />
) : (
<DefaultIcon title="This is the default value for this prop" />
)}
</Container> </Container>
) )
} }

View file

@ -54,6 +54,13 @@ export function useEditingToolsForCompoundProp<T extends SerializablePrimitive>(
obj: SheetObject, obj: SheetObject,
propConfig: PropTypeConfig_Compound<{}>, propConfig: PropTypeConfig_Compound<{}>,
): Stuff { ): Stuff {
const pathToProp = getPointerParts(pointerToProp).path
const pointerToStaticOverrides = pointerDeep(
obj.template.pointerToStaticOverrides,
pathToProp,
)
return usePrism((): Stuff => { return usePrism((): Stuff => {
// if the compound has no simple descendants, then there isn't much the user can do with it // if the compound has no simple descendants, then there isn't much the user can do with it
if (!compoundHasSimpleDescendants(propConfig)) { if (!compoundHasSimpleDescendants(propConfig)) {
@ -67,8 +74,6 @@ export function useEditingToolsForCompoundProp<T extends SerializablePrimitive>(
} }
} }
const pathToProp = getPointerParts(pointerToProp).path
/** /**
* TODO This implementation is wrong because {@link stateEditors.studio.ephemeral.projects.stateByProjectId.stateBySheetId.stateByObjectKey.propsBeingScrubbed.flag} * TODO This implementation is wrong because {@link stateEditors.studio.ephemeral.projects.stateByProjectId.stateBySheetId.stateByObjectKey.propsBeingScrubbed.flag}
* does not prune empty objects * does not prune empty objects
@ -96,6 +101,11 @@ export function useEditingToolsForCompoundProp<T extends SerializablePrimitive>(
obj.template.getMapOfValidSequenceTracks_forStudio(), obj.template.getMapOfValidSequenceTracks_forStudio(),
) )
const staticOverrides = getDeep(
val(obj.template.getStaticValues()),
pathToProp,
)
const possibleSequenceTrackIds = getDeep( const possibleSequenceTrackIds = getDeep(
validSequencedTracks, validSequencedTracks,
pathToProp, pathToProp,
@ -106,9 +116,8 @@ export function useEditingToolsForCompoundProp<T extends SerializablePrimitive>(
Object.keys(possibleSequenceTrackIds).length !== 0 // check if object is empty or undefined Object.keys(possibleSequenceTrackIds).length !== 0 // check if object is empty or undefined
const listOfDescendantTrackIds: SequenceTrackId[] = [] const listOfDescendantTrackIds: SequenceTrackId[] = []
let hasOneOrMoreStatics = true let hasOneOrMoreStatics = staticOverrides !== undefined
if (hasOneOrMoreSequencedTracks) { if (hasOneOrMoreSequencedTracks) {
hasOneOrMoreStatics = false
for (const descendant of iteratePropType(propConfig, [])) { for (const descendant of iteratePropType(propConfig, [])) {
if (isPropConfigComposite(descendant.conf)) continue if (isPropConfigComposite(descendant.conf)) continue
const sequencedTrackIdBelongingToDescendant = getDeep( const sequencedTrackIdBelongingToDescendant = getDeep(
@ -214,7 +223,9 @@ export function useEditingToolsForCompoundProp<T extends SerializablePrimitive>(
...common, ...common,
type: 'AllStatic', type: 'AllStatic',
controlIndicators: ( controlIndicators: (
<DefaultOrStaticValueIndicator hasStaticOverride={false} /> <DefaultOrStaticValueIndicator
hasStaticOverride={hasOneOrMoreStatics}
/>
), ),
} }
} }

View file

@ -272,14 +272,20 @@ function createPrism<T extends SerializablePrimitive>(
} }
} }
contextMenuItems.push({ const allStaticOverrides = val(obj.template.getStaticValues())
label: 'Reset to default',
callback: () => { const staticOverride = getDeep(allStaticOverrides, pathToProp)
getStudio()!.transaction(({unset: unset}) => {
unset(pointerToProp) if (typeof staticOverride !== 'undefined') {
}) contextMenuItems.push({
}, label: 'Reset to default',
}) callback: () => {
getStudio()!.transaction(({unset: unset}) => {
unset(pointerToProp)
})
},
})
}
if (isSequencable) { if (isSequencable) {
contextMenuItems.push({ contextMenuItems.push({
@ -297,9 +303,7 @@ function createPrism<T extends SerializablePrimitive>(
}) })
} }
const statics = val(obj.template.getStaticValues()) if (typeof staticOverride !== 'undefined') {
if (typeof getDeep(statics, pathToProp) !== 'undefined') {
const ret: EditingToolsStatic<T> = { const ret: EditingToolsStatic<T> = {
...common, ...common,
type: 'Static', type: 'Static',