WIP - Implement dynamic prop types

TODO: Tests
This commit is contained in:
Aria Minaei 2022-10-06 14:05:04 +02:00 committed by Aria
parent e708463377
commit d260f74c06
9 changed files with 50 additions and 29 deletions

View file

@ -166,6 +166,7 @@ export default class SheetObject implements IdentityDerivationProvider {
const tracksToProcess = val(tracksToProcessD) const tracksToProcess = val(tracksToProcessD)
const valsAtom = new Atom<SheetObjectPropsValue>({}) const valsAtom = new Atom<SheetObjectPropsValue>({})
const config = val(this.template.configPointer)
prism.effect( prism.effect(
'processTracks', 'processTracks',
@ -175,7 +176,7 @@ export default class SheetObject implements IdentityDerivationProvider {
for (const {trackId, pathToProp} of tracksToProcess) { for (const {trackId, pathToProp} of tracksToProcess) {
const derivation = this._trackIdToDerivation(trackId) const derivation = this._trackIdToDerivation(trackId)
const propConfig = getPropConfigByPath( const propConfig = getPropConfigByPath(
this.template.config, config,
pathToProp, pathToProp,
)! as Extract<PropTypeConfig, {interpolate: $IntentionalAny}> )! as Extract<PropTypeConfig, {interpolate: $IntentionalAny}>
@ -222,7 +223,7 @@ export default class SheetObject implements IdentityDerivationProvider {
} }
} }
}, },
tracksToProcess, [config, ...tracksToProcess],
) )
return valsAtom.pointer return valsAtom.pointer

View file

@ -61,10 +61,14 @@ export default class SheetObjectTemplate {
readonly _cache = new SimpleCache() readonly _cache = new SimpleCache()
readonly project: Project readonly project: Project
get config() { get staticConfig() {
return this._config.getState() return this._config.getState()
} }
get configPointer() {
return this._config.pointer
}
constructor( constructor(
readonly sheetTemplate: SheetTemplate, readonly sheetTemplate: SheetTemplate,
objectKey: ObjectAddressKey, objectKey: ObjectAddressKey,
@ -95,7 +99,7 @@ export default class SheetObjectTemplate {
getDefaultValues(): IDerivation<SerializableMap> { getDefaultValues(): IDerivation<SerializableMap> {
return this._cache.get('getDefaultValues()', () => return this._cache.get('getDefaultValues()', () =>
prism(() => { prism(() => {
const config = val(this._config.pointer) const config = val(this.configPointer)
return getPropDefaultsOfSheetObject(config) return getPropDefaultsOfSheetObject(config)
}), }),
) )
@ -119,7 +123,7 @@ export default class SheetObjectTemplate {
], ],
) ?? {} ) ?? {}
const config = val(this._config.pointer) const config = val(this.configPointer)
const deserialized = config.deserializeAndSanitize(json) || {} const deserialized = config.deserializeAndSanitize(json) || {}
return deserialized return deserialized
}), }),
@ -154,12 +158,14 @@ export default class SheetObjectTemplate {
if (!trackIdByPropPath) return emptyArray as $IntentionalAny if (!trackIdByPropPath) return emptyArray as $IntentionalAny
const objectConfig = val(this.configPointer)
const _entries = Object.entries(trackIdByPropPath) const _entries = Object.entries(trackIdByPropPath)
for (const [pathToPropInString, trackId] of _entries) { for (const [pathToPropInString, trackId] of _entries) {
const pathToProp = parsePathToProp(pathToPropInString) const pathToProp = parsePathToProp(pathToPropInString)
if (!pathToProp) continue if (!pathToProp) continue
const propConfig = getPropConfigByPath(this.config, pathToProp) const propConfig = getPropConfigByPath(objectConfig, pathToProp)
const isSequencable = propConfig && isPropConfSequencable(propConfig) const isSequencable = propConfig && isPropConfSequencable(propConfig)
@ -168,7 +174,7 @@ export default class SheetObjectTemplate {
arrayOfIds.push({pathToProp, trackId: trackId!}) arrayOfIds.push({pathToProp, trackId: trackId!})
} }
const mapping = getOrderingOfPropTypeConfig(this.config) const mapping = getOrderingOfPropTypeConfig(objectConfig)
arrayOfIds.sort((a, b) => { arrayOfIds.sort((a, b) => {
const pathToPropA = a.pathToProp const pathToPropA = a.pathToProp

View file

@ -45,6 +45,7 @@ export interface ISheet {
* *
* @param key - Each object is identified by a key, which is a non-empty string * @param key - Each object is identified by a key, which is a non-empty string
* @param props - The props of the object. See examples * @param props - The props of the object. See examples
* @param options - TODO
* *
* @returns An Object * @returns An Object
* *
@ -68,6 +69,7 @@ export interface ISheet {
object<Props extends UnknownShorthandCompoundProps>( object<Props extends UnknownShorthandCompoundProps>(
key: string, key: string,
props: Props, props: Props,
options?: {override?: boolean},
): ISheetObject<Props> ): ISheetObject<Props>
/** /**
@ -95,6 +97,7 @@ export default class TheatreSheet implements ISheet {
object<Props extends UnknownShorthandCompoundProps>( object<Props extends UnknownShorthandCompoundProps>(
key: string, key: string,
config: Props, config: Props,
opts?: {override?: boolean},
): ISheetObject<Props> { ): ISheetObject<Props> {
const internal = privateAPI(this) const internal = privateAPI(this)
const sanitizedPath = validateAndSanitiseSlashedPathOrThrow( const sanitizedPath = validateAndSanitiseSlashedPathOrThrow(
@ -118,14 +121,22 @@ export default class TheatreSheet implements ISheet {
const prevConfig = weakMapOfUnsanitizedProps.get(existingObject) const prevConfig = weakMapOfUnsanitizedProps.get(existingObject)
if (prevConfig) { if (prevConfig) {
if (!deepEqual(config, prevConfig)) { if (!deepEqual(config, prevConfig)) {
if (opts?.override === true) {
const sanitizedConfig = compound(config)
existingObject.template.overrideConfig(sanitizedConfig)
weakMapOfUnsanitizedProps.set(existingObject, config)
return existingObject.publicApi as $IntentionalAny
} else {
throw new Error( throw new Error(
`You seem to have called sheet.object("${key}", config) twice, with different values for \`config\`. ` + `You seem to have called sheet.object("${key}", config) twice, with different values for \`config\`. ` +
`This is disallowed because changing the config of an object on the fly would make it difficult to reason about.\n\n` + `This is disallowed because changing the config of an object on the fly would make it difficult to reason about.\n\n` +
`You can fix this by either re-using the existing object, or calling sheet.object("${key}", config) with the same config.`, `You can fix this by either re-using the existing object, or calling sheet.object("${key}", config) with the same config.\n\n` +
`If you mean to override the object's config, set \`{override: true}\` in sheet.object("${key}", config, {override: true})`,
) )
} }
} }
} }
}
return existingObject.publicApi as $IntentionalAny return existingObject.publicApi as $IntentionalAny
} else { } else {

View file

@ -89,7 +89,7 @@ export default function createTransactionPrivateApi(
.getMapOfValidSequenceTracks_forStudio() .getMapOfValidSequenceTracks_forStudio()
.getValue() .getValue()
const propConfig = getPropConfigByPath(root.template.config, path) const propConfig = getPropConfigByPath(root.template.staticConfig, path)
if (!propConfig) { if (!propConfig) {
throw new Error( throw new Error(
@ -205,7 +205,7 @@ export default function createTransactionPrivateApi(
) )
const propConfig = getPropConfigByPath( const propConfig = getPropConfigByPath(
root.template.config, root.template.staticConfig,
path, path,
) as PropTypeConfig ) as PropTypeConfig

View file

@ -5,7 +5,6 @@ import type {
PropTypeConfig_AllSimples, PropTypeConfig_AllSimples,
} from '@theatre/core/propTypes' } from '@theatre/core/propTypes'
import type SheetObject from '@theatre/core/sheetObjects/SheetObject' import type SheetObject from '@theatre/core/sheetObjects/SheetObject'
import {getPropTypeByPointer} from '@theatre/studio/propEditors/utils/getPropTypeByPointer'
import {simplePropEditorByPropType} from '@theatre/studio/propEditors/simpleEditors/simplePropEditorByPropType' import {simplePropEditorByPropType} from '@theatre/studio/propEditors/simpleEditors/simplePropEditorByPropType'
import type {PropConfigForType} from '@theatre/studio/propEditors/utils/PropConfigForType' import type {PropConfigForType} from '@theatre/studio/propEditors/utils/PropConfigForType'
import type {ISimplePropEditorReactProps} from '@theatre/studio/propEditors/simpleEditors/ISimplePropEditorReactProps' import type {ISimplePropEditorReactProps} from '@theatre/studio/propEditors/simpleEditors/ISimplePropEditorReactProps'
@ -24,16 +23,13 @@ import DetailSimplePropEditor from './DeterminePropEditorForDetail/DetailSimpleP
*/ */
const DeterminePropEditorForDetail: React.VFC< const DeterminePropEditorForDetail: React.VFC<
IDeterminePropEditorForDetailProps<PropTypeConfig['type']> IDeterminePropEditorForDetailProps<PropTypeConfig['type']>
> = (p) => { > = ({propConfig, visualIndentation, pointerToProp, obj}) => {
const propConfig =
p.propConfig ?? getPropTypeByPointer(p.pointerToProp, p.obj)
if (propConfig.type === 'compound') { if (propConfig.type === 'compound') {
return ( return (
<DetailCompoundPropEditor <DetailCompoundPropEditor
obj={p.obj} obj={obj}
visualIndentation={p.visualIndentation} visualIndentation={visualIndentation}
pointerToProp={p.pointerToProp} pointerToProp={pointerToProp}
propConfig={propConfig} propConfig={propConfig}
/> />
) )
@ -50,9 +46,9 @@ const DeterminePropEditorForDetail: React.VFC<
ISimplePropEditorReactProps<PropTypeConfig_AllSimples> ISimplePropEditorReactProps<PropTypeConfig_AllSimples>
> >
} }
obj={p.obj} obj={obj}
visualIndentation={p.visualIndentation} visualIndentation={visualIndentation}
pointerToProp={p.pointerToProp} pointerToProp={pointerToProp}
propConfig={propConfig} propConfig={propConfig}
/> />
) )
@ -60,6 +56,7 @@ const DeterminePropEditorForDetail: React.VFC<
} }
export default DeterminePropEditorForDetail export default DeterminePropEditorForDetail
type IDeterminePropEditorForDetailProps<K extends PropTypeConfig['type']> = type IDeterminePropEditorForDetailProps<K extends PropTypeConfig['type']> =
IDetailEditablePropertyProps<K> & { IDetailEditablePropertyProps<K> & {
visualIndentation: number visualIndentation: number

View file

@ -3,6 +3,7 @@ import type SheetObject from '@theatre/core/sheetObjects/SheetObject'
import type {Pointer} from '@theatre/dataverse' import type {Pointer} from '@theatre/dataverse'
import type {$FixMe} from '@theatre/shared/utils/types' import type {$FixMe} from '@theatre/shared/utils/types'
import DeterminePropEditorForDetail from './DeterminePropEditorForDetail' import DeterminePropEditorForDetail from './DeterminePropEditorForDetail'
import {useVal} from '@theatre/react'
const ObjectDetails: React.FC<{ const ObjectDetails: React.FC<{
/** TODO: add support for multiple objects (it would show their common props) */ /** TODO: add support for multiple objects (it would show their common props) */
@ -10,13 +11,14 @@ const ObjectDetails: React.FC<{
}> = ({objects}) => { }> = ({objects}) => {
const obj = objects[0] const obj = objects[0]
const key = useMemo(() => JSON.stringify(obj.address), [obj]) const key = useMemo(() => JSON.stringify(obj.address), [obj])
const config = useVal(obj.template.configPointer)
return ( return (
<DeterminePropEditorForDetail <DeterminePropEditorForDetail
key={key} key={key}
obj={obj} obj={obj}
pointerToProp={obj.propsP as Pointer<$FixMe>} pointerToProp={obj.propsP as Pointer<$FixMe>}
propConfig={obj.template.config} propConfig={config}
visualIndentation={1} visualIndentation={1}
/> />
) )

View file

@ -18,6 +18,7 @@ import {
valueInProp, valueInProp,
} from '@theatre/shared/propTypes/utils' } from '@theatre/shared/propTypes/utils'
import type {PropTypeConfig_AllSimples} from '@theatre/core/propTypes' import type {PropTypeConfig_AllSimples} from '@theatre/core/propTypes'
import {useVal} from '@theatre/react'
export type ExtremumSpace = { export type ExtremumSpace = {
fromValueSpace: (v: number) => number fromValueSpace: (v: number) => number
@ -36,7 +37,7 @@ const BasicKeyframedTrack: React.VFC<{
}> = React.memo( }> = React.memo(
({layoutP, trackData, sheetObject, trackId, color, pathToProp}) => { ({layoutP, trackData, sheetObject, trackId, color, pathToProp}) => {
const propConfig = getPropConfigByPath( const propConfig = getPropConfigByPath(
sheetObject.template.config, useVal(sheetObject.template.configPointer),
pathToProp, pathToProp,
)! as PropTypeConfig_AllSimples )! as PropTypeConfig_AllSimples

View file

@ -157,6 +157,7 @@ export const calculateSequenceEditorTree = (
const trackSetups = val( const trackSetups = val(
sheetObject.template.getMapOfValidSequenceTracks_forStudio(), sheetObject.template.getMapOfValidSequenceTracks_forStudio(),
) )
const objectConfig = val(sheetObject.template.configPointer)
if (Object.keys(trackSetups).length === 0) return if (Object.keys(trackSetups).length === 0) return
@ -192,7 +193,7 @@ export const calculateSequenceEditorTree = (
sheetObject, sheetObject,
trackSetups, trackSetups,
[], [],
sheetObject.template.config, objectConfig,
row.children, row.children,
level + 1, level + 1,
shouldRender && !isCollapsed, shouldRender && !isCollapsed,

View file

@ -3,6 +3,8 @@ import type SheetObject from '@theatre/core/sheetObjects/SheetObject'
import {getPointerParts} from '@theatre/dataverse' import {getPointerParts} from '@theatre/dataverse'
/** /**
* @deprecated because it uses obj.template.staticConfig
*
* Returns the PropTypeConfig by path. Assumes `path` is a valid prop path and that * Returns the PropTypeConfig by path. Assumes `path` is a valid prop path and that
* it exists in obj. * it exists in obj.
* *
@ -20,7 +22,7 @@ export function getPropTypeByPointer(
pointerToProp: SheetObject['propsP'], pointerToProp: SheetObject['propsP'],
obj: SheetObject, obj: SheetObject,
): PropTypeConfig { ): PropTypeConfig {
const rootConf = obj.template.config const rootConf = obj.template.staticConfig
const p = getPointerParts(pointerToProp).path const p = getPointerParts(pointerToProp).path
let conf = rootConf as PropTypeConfig let conf = rootConf as PropTypeConfig