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

View file

@ -7,6 +7,7 @@ import type {
} from '@theatre/core/propTypes'
import type {PathToProp} from '@theatre/shared/utils/addresses'
import type {$IntentionalAny} from '@theatre/shared/utils/types'
import memoizeFn from '@theatre/shared/utils/memoizeFn'
/**
* Either compound or enum properties can be considered "composite"
@ -70,35 +71,13 @@ export function isPropConfSequencable(
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.
* 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.
*/
function compoundHasSimpleDescendantsImpl(
conf: PropTypeConfig_Compound<{}> | PropTypeConfig_Enum,
): boolean {
export const compoundHasSimpleDescendants = memoizeFn(
(conf: PropTypeConfig_Compound<{}> | PropTypeConfig_Enum): boolean => {
if (conf.type === 'enum') {
throw new Error(`Not implemented yet for enums`)
}
@ -116,7 +95,8 @@ function compoundHasSimpleDescendantsImpl(
}
}
return false
}
},
)
/**
* 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 styled from 'styled-components'
export const theme = {
const theme = {
defaultState: {
color: transparentize(0.85, `#C4C4C4`),
color: transparentize(0.95, `#C4C4C4`),
},
withStaticOverride: {
color: transparentize(0.85, `#C4C4C4`),
@ -28,24 +28,31 @@ const Rect = styled.rect`
fill: currentColor;
`
const Icon = () => (
<svg
width="5"
height="5"
viewBox="0 0 5 5"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<Rect width="5" height="5" />
</svg>
)
const DefaultIcon = styled.div`
width: 5px;
height: 5px;
border-radius: 1px;
/* border: 1px solid currentColor; */
background-color: currentColor;
`
const FilledIcon = styled.div`
width: 5px;
height: 5px;
background-color: currentColor;
border-radius: 1px;
`
const DefaultOrStaticValueIndicator: React.FC<{hasStaticOverride: boolean}> = (
props,
) => {
return (
<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>
)
}

View file

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

View file

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