More progress on shorthand types

This commit is contained in:
Aria Minaei 2021-09-06 11:26:00 +02:00
parent aefb769855
commit f6cf3711f4
14 changed files with 109 additions and 103 deletions

View file

@ -2,16 +2,15 @@ import type {IScrub, IStudio} from '@theatre/studio'
import studio from '@theatre/studio' import studio from '@theatre/studio'
import React, {useLayoutEffect, useMemo, useState} from 'react' import React, {useLayoutEffect, useMemo, useState} from 'react'
import type {ISheet, ISheetObject, IProject} from '@theatre/core' import type {ISheet, ISheetObject, IProject} from '@theatre/core'
import {types as t} from '@theatre/core'
import type {UseDragOpts} from './useDrag' import type {UseDragOpts} from './useDrag'
import useDrag from './useDrag' import useDrag from './useDrag'
studio.initialize() studio.initialize()
const boxObjectConfig = t.compound({ const boxObjectConfig = {
x: t.number(0), x: 0,
y: t.number(0), y: 0,
}) }
const Box: React.FC<{ const Box: React.FC<{
id: string id: string

View file

@ -1,14 +1,13 @@
import studio from '@theatre/studio' import studio from '@theatre/studio'
import {useLayoutEffect, useMemo, useState} from 'react' import {useLayoutEffect, useMemo, useState} from 'react'
import {types as t} from '@theatre/core'
import useDrag from './useDrag' import useDrag from './useDrag'
studio.initialize() studio.initialize()
const boxObjectConfig = t.compound({ const boxObjectConfig = {
x: t.number(0), x: 0,
y: t.number(0), y: 0,
}) }
const Box = ({id, sheet, selectedObject}) => { const Box = ({id, sheet, selectedObject}) => {
// This is cheap to call and always returns the same value, so no need for useMemo() // This is cheap to call and always returns the same value, so no need for useMemo()

View file

@ -3,15 +3,14 @@ import type {UseDragOpts} from './useDrag'
import useDrag from './useDrag' import useDrag from './useDrag'
import React, {useLayoutEffect, useMemo, useState} from 'react' import React, {useLayoutEffect, useMemo, useState} from 'react'
import type {IProject, ISheet} from '@theatre/core' import type {IProject, ISheet} from '@theatre/core'
import {types as t} from '@theatre/core'
import type {IScrub, IStudio} from '@theatre/studio' import type {IScrub, IStudio} from '@theatre/studio'
studio.initialize() studio.initialize()
const boxObjectConfig = t.compound({ const boxObjectConfig = {
x: t.number(0), x: 0,
y: t.number(0), y: 0,
}) }
const Box: React.FC<{ const Box: React.FC<{
id: string id: string

View file

@ -3,28 +3,27 @@ import useDrag from '@theatre/studio/uiComponents/useDrag'
import React, {useLayoutEffect, useMemo, useState} from 'react' import React, {useLayoutEffect, useMemo, useState} from 'react'
import studio from '@theatre/studio' import studio from '@theatre/studio'
import type {IProject, ISheet} from '@theatre/core' import type {IProject, ISheet} from '@theatre/core'
import {types as t} from '@theatre/core'
import type {IScrub, IStudio} from '@theatre/studio' import type {IScrub, IStudio} from '@theatre/studio'
studio.initialize() studio.initialize()
const boxObjectConfig = t.compound({ const boxObjectConfig = {
position: t.compound({ position: {
x: t.number(0), x: 0,
y: t.number(0), y: 0,
z: t.number(0), z: 0,
}), },
scale: t.compound({ scale: {
x: t.number(0), x: 0,
y: t.number(0), y: 0,
z: t.number(0), z: 0,
origin: t.compound({ origin: {
x: t.number(0), x: 0,
y: t.number(0), y: 0,
}), },
w: t.number(0), w: 0,
}), },
}) }
const Box: React.FC<{ const Box: React.FC<{
id: string id: string

View file

@ -15,13 +15,13 @@ import {drawTurtlePlan, makeTurtlePlan} from './turtle'
studio.initialize() studio.initialize()
const objConfig = types.compound({ const objConfig = {
startingPoint: types.compound({ startingPoint: {
x: types.number(0.5, {range: [0, 1]}), x: types.number(0.5, {range: [0, 1]}),
y: types.number(0.5, {range: [0, 1]}), y: types.number(0.5, {range: [0, 1]}),
}), },
scale: types.number(1, {range: [0.1, 1000]}), scale: types.number(1, {range: [0.1, 1000]}),
}) }
const TurtleRenderer: React.FC<{ const TurtleRenderer: React.FC<{
sheet: ISheet sheet: ISheet

View file

@ -6,7 +6,7 @@ let sheet: ISheet | undefined = undefined
let sheetObject: ISheetObject<typeof editorSheetObjectConfig> | undefined = let sheetObject: ISheetObject<typeof editorSheetObjectConfig> | undefined =
undefined undefined
const editorSheetObjectConfig = types.compound({ const editorSheetObjectConfig = {
viewport: types.compound( viewport: types.compound(
{ {
showAxes: types.boolean(true, {label: 'Axes'}), showAxes: types.boolean(true, {label: 'Axes'}),
@ -47,7 +47,7 @@ const editorSheetObjectConfig = types.compound({
}, },
{label: 'Transform Controls'}, {label: 'Transform Controls'},
), ),
}) }
export function getEditorSheet(): ISheet { export function getEditorSheet(): ISheet {
if (!sheet) { if (!sheet) {

View file

@ -2,6 +2,7 @@ import {useLayoutEffect, useRef, useState} from 'react'
import {allRegisteredObjects} from '../store' import {allRegisteredObjects} from '../store'
import studio from '@theatre/studio' import studio from '@theatre/studio'
import type {ISheetObject} from '@theatre/core' import type {ISheetObject} from '@theatre/core'
import type {$IntentionalAny} from '../types'
export function useSelected(): undefined | string { export function useSelected(): undefined | string {
const [state, set] = useState<string | undefined>(undefined) const [state, set] = useState<string | undefined>(undefined)
@ -13,7 +14,7 @@ export function useSelected(): undefined | string {
const item = selection.find( const item = selection.find(
(s): s is ISheetObject => (s): s is ISheetObject =>
s.type === 'Theatre_SheetObject_PublicAPI' && s.type === 'Theatre_SheetObject_PublicAPI' &&
allRegisteredObjects.has(s), allRegisteredObjects.has(s as $IntentionalAny),
) )
if (!item) { if (!item) {
set(undefined) set(undefined)
@ -31,7 +32,8 @@ export function useSelected(): undefined | string {
export function getSelected(): undefined | string { export function getSelected(): undefined | string {
const item = studio.selection.find( const item = studio.selection.find(
(s): s is ISheetObject => (s): s is ISheetObject =>
s.type === 'Theatre_SheetObject_PublicAPI' && allRegisteredObjects.has(s), s.type === 'Theatre_SheetObject_PublicAPI' &&
allRegisteredObjects.has(s as $IntentionalAny),
) )
if (!item) { if (!item) {
return undefined return undefined

View file

@ -11,20 +11,20 @@ import {types} from '@theatre/core'
import type {ISheetObject} from '@theatre/core' import type {ISheetObject} from '@theatre/core'
import {useThree} from '@react-three/fiber' import {useThree} from '@react-three/fiber'
const camConf = types.compound({ const camConf = {
transform: types.compound({ transform: {
position: types.compound({ position: {
x: types.number(10), x: types.number(10),
y: types.number(10), y: types.number(10),
z: types.number(0), z: types.number(0),
}), },
target: types.compound({ target: {
x: types.number(0), x: types.number(0),
y: types.number(0), y: types.number(0),
z: types.number(0), z: types.number(0),
}), },
}), },
lens: types.compound({ lens: {
zoom: types.number(1, {range: [0.0001, 10]}), zoom: types.number(1, {range: [0.0001, 10]}),
fov: types.number(50, {range: [1, 1000]}), fov: types.number(50, {range: [1, 1000]}),
near: types.number(0.1, {range: [0, Infinity]}), near: types.number(0.1, {range: [0, Infinity]}),
@ -32,8 +32,8 @@ const camConf = types.compound({
focus: types.number(10, {range: [0, Infinity]}), focus: types.number(10, {range: [0, Infinity]}),
filmGauge: types.number(35, {range: [0, Infinity]}), filmGauge: types.number(35, {range: [0, Infinity]}),
filmOffset: types.number(0, {range: [0, Infinity]}), filmOffset: types.number(0, {range: [0, Infinity]}),
}), },
}) }
export default function useSnapshotEditorCamera( export default function useSnapshotEditorCamera(
snapshotEditorSheet: ISheet, snapshotEditorSheet: ISheet,
@ -144,7 +144,9 @@ function usePassValuesFromTheatreToCamera(
if (!cam || orbitControls === null) return if (!cam || orbitControls === null) return
const obj = objRef.current! const obj = objRef.current!
const setFromTheatre = (props: typeof camConf['valueType']): void => { const setFromTheatre = (
props: ISheetObject<typeof camConf>['value'],
): void => {
const {position, target} = props.transform const {position, target} = props.transform
cam.zoom = props.lens.zoom cam.zoom = props.lens.zoom
cam.fov = props.lens.fov cam.fov = props.lens.fov

View file

@ -22,23 +22,23 @@ const positionComp = types.number(1, {nudgeMultiplier: 0.1})
const rotationComp = types.number(1, {nudgeMultiplier: 0.02}) const rotationComp = types.number(1, {nudgeMultiplier: 0.02})
const scaleComp = types.number(1, {nudgeMultiplier: 0.1}) const scaleComp = types.number(1, {nudgeMultiplier: 0.1})
export const baseSheetObjectType = types.compound({ export const baseSheetObjectType = {
position: types.compound({ position: {
x: positionComp, x: positionComp,
y: positionComp, y: positionComp,
z: positionComp, z: positionComp,
}), },
rotation: types.compound({ rotation: {
x: rotationComp, x: rotationComp,
y: rotationComp, y: rotationComp,
z: rotationComp, z: rotationComp,
}), },
scale: types.compound({ scale: {
x: scaleComp, x: scaleComp,
y: scaleComp, y: scaleComp,
z: scaleComp, z: scaleComp,
}), },
}) }
export type BaseSheetObjectType = ISheetObject<typeof baseSheetObjectType> export type BaseSheetObjectType = ISheetObject<typeof baseSheetObjectType>

View file

@ -12,13 +12,13 @@ import {propTypeSymbol} from './internals'
* Usage: * Usage:
* ```ts * ```ts
* // the root prop type of an object is always a compound * // the root prop type of an object is always a compound
* const props = t.compound({ * const props = {
* // compounds can be nested * // compounds can be nested
* position: t.compound({ * position: t.compound({
* x: t.number(0), * x: t.number(0),
* y: t.number(0) * y: t.number(0)
* }) * })
* }) * }
* *
* const obj = sheet.obj('key', props) * const obj = sheet.obj('key', props)
* console.log(obj.value) // {position: {x: 10.3, y: -1}} * console.log(obj.value) // {position: {x: 10.3, y: -1}}

View file

@ -28,7 +28,8 @@ export type IShorthandCompoundProps = {
[K in string]: IShorthandProp [K in string]: IShorthandProp
} }
type ShorthandPropToLonghandProp<P extends IShorthandProp> = P extends string export type ShorthandPropToLonghandProp<P extends IShorthandProp> =
P extends string
? PropTypeConfig_String ? PropTypeConfig_String
: P extends number : P extends number
? PropTypeConfig_Number ? PropTypeConfig_Number

View file

@ -6,37 +6,37 @@ import type {SheetObjectAddress} from '@theatre/shared/utils/addresses'
import SimpleCache from '@theatre/shared/utils/SimpleCache' import SimpleCache from '@theatre/shared/utils/SimpleCache'
import type { import type {
$FixMe, $FixMe,
$IntentionalAny,
DeepPartialOfSerializableValue, DeepPartialOfSerializableValue,
VoidFn, VoidFn,
} from '@theatre/shared/utils/types' } from '@theatre/shared/utils/types'
import type {IDerivation, Pointer} from '@theatre/dataverse' import type {IDerivation, Pointer} from '@theatre/dataverse'
import {prism, val} from '@theatre/dataverse' import {prism, val} from '@theatre/dataverse'
import type {PropTypeConfig_Compound} from '@theatre/core/propTypes'
import type SheetObject from './SheetObject' import type SheetObject from './SheetObject'
import type {
IShorthandCompoundProps,
ShorthandPropToLonghandProp,
} from '@theatre/core/propTypes/internals'
export interface ISheetObject< export interface ISheetObject<Props extends IShorthandCompoundProps = {}> {
Props extends PropTypeConfig_Compound<$IntentionalAny> = PropTypeConfig_Compound<$IntentionalAny>,
> {
readonly type: 'Theatre_SheetObject_PublicAPI' readonly type: 'Theatre_SheetObject_PublicAPI'
/** /**
* *
*/ */
readonly value: Props['valueType'] readonly value: ShorthandPropToLonghandProp<Props>['valueType']
readonly props: Pointer<Props['valueType']> readonly props: Pointer<this['value']>
readonly sheet: ISheet readonly sheet: ISheet
readonly project: IProject readonly project: IProject
readonly address: SheetObjectAddress readonly address: SheetObjectAddress
onValuesChange(fn: (values: Props['valueType']) => void): VoidFn onValuesChange(fn: (values: this['value']) => void): VoidFn
// prettier-ignore // prettier-ignore
set initialValue(value: DeepPartialOfSerializableValue<Props['valueType']>) set initialValue(value: DeepPartialOfSerializableValue<this['value']>)
} }
export default class TheatreSheetObject< export default class TheatreSheetObject<
Props extends PropTypeConfig_Compound<$IntentionalAny>, Props extends IShorthandCompoundProps = {},
> implements ISheetObject<Props> > implements ISheetObject<Props>
{ {
get type(): 'Theatre_SheetObject_PublicAPI' { get type(): 'Theatre_SheetObject_PublicAPI' {
@ -51,7 +51,7 @@ export default class TheatreSheetObject<
setPrivateAPI(this, internals) setPrivateAPI(this, internals)
} }
get props(): Pointer<Props['valueType']> { get props(): Pointer<this['value']> {
return privateAPI(this).propsP as $FixMe return privateAPI(this).propsP as $FixMe
} }
@ -67,7 +67,7 @@ export default class TheatreSheetObject<
return {...privateAPI(this).address} return {...privateAPI(this).address}
} }
private _valuesDerivation(): IDerivation<Props['valueType']> { private _valuesDerivation(): IDerivation<this['value']> {
return this._cache.get('onValuesChangeDerivation', () => { return this._cache.get('onValuesChangeDerivation', () => {
const sheetObject = privateAPI(this) const sheetObject = privateAPI(this)
const d: IDerivation<Props> = prism(() => { const d: IDerivation<Props> = prism(() => {
@ -77,15 +77,15 @@ export default class TheatreSheetObject<
}) })
} }
onValuesChange(fn: (values: Props['valueType']) => void): VoidFn { onValuesChange(fn: (values: this['value']) => void): VoidFn {
return this._valuesDerivation().tapImmediate(coreTicker, fn) return this._valuesDerivation().tapImmediate(coreTicker, fn)
} }
get value(): Props['valueType'] { get value(): ShorthandPropToLonghandProp<Props>['valueType'] {
return this._valuesDerivation().getValue() return this._valuesDerivation().getValue()
} }
set initialValue(val: DeepPartialOfSerializableValue<Props['valueType']>) { set initialValue(val: DeepPartialOfSerializableValue<this['value']>) {
privateAPI(this).setInitialValue(val) privateAPI(this).setInitialValue(val)
} }
} }

View file

@ -3,6 +3,7 @@ import type {IProject} from '@theatre/core/projects/TheatreProject'
import type TheatreSequence from '@theatre/core/sequences/TheatreSequence' import type TheatreSequence from '@theatre/core/sequences/TheatreSequence'
import type {ISequence} from '@theatre/core/sequences/TheatreSequence' import type {ISequence} from '@theatre/core/sequences/TheatreSequence'
import type {PropTypeConfig_Compound} from '@theatre/core/propTypes' import type {PropTypeConfig_Compound} from '@theatre/core/propTypes'
import {compound} from '@theatre/core/propTypes'
import type {ISheetObject} from '@theatre/core/sheetObjects/TheatreSheetObject' import type {ISheetObject} from '@theatre/core/sheetObjects/TheatreSheetObject'
import type Sheet from '@theatre/core/sheets/Sheet' import type Sheet from '@theatre/core/sheets/Sheet'
import type {SheetAddress} from '@theatre/shared/utils/addresses' import type {SheetAddress} from '@theatre/shared/utils/addresses'
@ -11,6 +12,7 @@ import {validateAndSanitiseSlashedPathOrThrow} from '@theatre/shared/utils/slash
import type {$IntentionalAny} from '@theatre/shared/utils/types' import type {$IntentionalAny} from '@theatre/shared/utils/types'
import userReadableTypeOfValue from '@theatre/shared/utils/userReadableTypeOfValue' import userReadableTypeOfValue from '@theatre/shared/utils/userReadableTypeOfValue'
import deepEqual from 'fast-deep-equal' import deepEqual from 'fast-deep-equal'
import type {IShorthandCompoundProps} from '@theatre/core/propTypes/internals'
export type SheetObjectConfig< export type SheetObjectConfig<
Props extends PropTypeConfig_Compound<$IntentionalAny>, Props extends PropTypeConfig_Compound<$IntentionalAny>,
@ -21,14 +23,16 @@ export interface ISheet {
readonly project: IProject readonly project: IProject
readonly address: SheetAddress readonly address: SheetAddress
object<Props extends PropTypeConfig_Compound<$IntentionalAny>>( object<Props extends IShorthandCompoundProps>(
key: string, key: string,
config: SheetObjectConfig<Props>, config: Props,
): ISheetObject<Props> ): ISheetObject<Props>
readonly sequence: ISequence readonly sequence: ISequence
} }
const weakMapOfUnsanitizedProps = new WeakMap()
export default class TheatreSheet implements ISheet { export default class TheatreSheet implements ISheet {
get type(): 'Theatre_Sheet_PublicAPI' { get type(): 'Theatre_Sheet_PublicAPI' {
return 'Theatre_Sheet_PublicAPI' return 'Theatre_Sheet_PublicAPI'
@ -40,9 +44,9 @@ export default class TheatreSheet implements ISheet {
setPrivateAPI(this, sheet) setPrivateAPI(this, sheet)
} }
object<Props extends PropTypeConfig_Compound<$IntentionalAny>>( object<Props extends IShorthandCompoundProps>(
key: string, key: string,
config: SheetObjectConfig<Props>, config: Props,
): ISheetObject<Props> { ): ISheetObject<Props> {
const internal = privateAPI(this) const internal = privateAPI(this)
const sanitizedPath = validateAndSanitiseSlashedPathOrThrow( const sanitizedPath = validateAndSanitiseSlashedPathOrThrow(
@ -67,7 +71,11 @@ export default class TheatreSheet implements ISheet {
return existingObject.publicApi as $IntentionalAny return existingObject.publicApi as $IntentionalAny
} else { } else {
const object = internal.createObject(sanitizedPath, nativeObject, config) const object = internal.createObject(
sanitizedPath,
nativeObject,
compound(config),
)
return object.publicApi as $IntentionalAny return object.publicApi as $IntentionalAny
} }
} }

View file

@ -31,16 +31,13 @@ export async function setupTestSheet(sheetState: SheetState_Historic) {
ticker.tick() ticker.tick()
await project.ready await project.ready
const sheetPublicAPI = project.sheet('Sheet') const sheetPublicAPI = project.sheet('Sheet')
const objPublicAPI = sheetPublicAPI.object( const objPublicAPI = sheetPublicAPI.object('obj', {
'obj',
t.compound({
position: { position: {
x: 0, x: 0,
y: t.number(1), y: t.number(1),
z: t.number(2), z: t.number(2),
}, },
}), })
)
const obj = privateAPI(objPublicAPI) const obj = privateAPI(objPublicAPI)