WIP - Implement dynamic prop types
TODO: Tests
This commit is contained in:
parent
e708463377
commit
d260f74c06
9 changed files with 50 additions and 29 deletions
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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,11 +121,19 @@ 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)) {
|
||||||
throw new Error(
|
if (opts?.override === true) {
|
||||||
`You seem to have called sheet.object("${key}", config) twice, with different values for \`config\`. ` +
|
const sanitizedConfig = compound(config)
|
||||||
`This is disallowed because changing the config of an object on the fly would make it difficult to reason about.\n\n` +
|
existingObject.template.overrideConfig(sanitizedConfig)
|
||||||
`You can fix this by either re-using the existing object, or calling sheet.object("${key}", config) with the same config.`,
|
weakMapOfUnsanitizedProps.set(existingObject, config)
|
||||||
)
|
return existingObject.publicApi as $IntentionalAny
|
||||||
|
} else {
|
||||||
|
throw new Error(
|
||||||
|
`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` +
|
||||||
|
`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})`,
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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
|
||||||
|
|
Loading…
Reference in a new issue