Started implementing shothand prop types
This commit is contained in:
parent
4f66d57cf8
commit
2f44f53021
3 changed files with 95 additions and 13 deletions
|
@ -13,9 +13,10 @@
|
||||||
* ```
|
* ```
|
||||||
* @module types
|
* @module types
|
||||||
*/
|
*/
|
||||||
|
import {InvalidArgumentError} from '@theatre/shared/utils/errors'
|
||||||
import type {$IntentionalAny} from '@theatre/shared/utils/types'
|
import type {$IntentionalAny} from '@theatre/shared/utils/types'
|
||||||
|
import userReadableTypeOfValue from '@theatre/shared/utils/userReadableTypeOfValue'
|
||||||
const s = Symbol('TheatrePropType_Basic')
|
import {isLonghandPropType, propTypeSymbol, toLonghandProp} from './internals'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a compound prop type (basically a JS object).
|
* Creates a compound prop type (basically a JS object).
|
||||||
|
@ -43,11 +44,53 @@ export const compound = <Props extends IValidCompoundProps>(
|
||||||
props: Props,
|
props: Props,
|
||||||
extras?: PropTypeConfigExtras,
|
extras?: PropTypeConfigExtras,
|
||||||
): PropTypeConfig_Compound<Props> => {
|
): PropTypeConfig_Compound<Props> => {
|
||||||
|
let sanitizedProps: Props
|
||||||
|
if (process.env.NODE_ENV !== 'production') {
|
||||||
|
sanitizedProps = {} as $IntentionalAny
|
||||||
|
if (typeof props !== 'object' || !props) {
|
||||||
|
throw new InvalidArgumentError(
|
||||||
|
`t.compound() expects an object, like: {x: 10}. ${userReadableTypeOfValue(
|
||||||
|
props,
|
||||||
|
)} given.`,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
for (const key of Object.keys(props) as Array<keyof Props>) {
|
||||||
|
if (typeof key !== 'string') {
|
||||||
|
throw new InvalidArgumentError(
|
||||||
|
`t.compound()'s keys must be all strings. ${userReadableTypeOfValue(
|
||||||
|
key,
|
||||||
|
)} given.`,
|
||||||
|
)
|
||||||
|
} else if (key.length === 0 || !key.match(/^\w+$/)) {
|
||||||
|
throw new InvalidArgumentError(
|
||||||
|
`compound key ${userReadableTypeOfValue(
|
||||||
|
key,
|
||||||
|
)} is invalid. The keys must be alphanumeric and start with a letter.`,
|
||||||
|
)
|
||||||
|
} else if (key.length > 64) {
|
||||||
|
throw new InvalidArgumentError(
|
||||||
|
`compound key ${userReadableTypeOfValue(key)} is too long.`,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const val = props[key]
|
||||||
|
if (isLonghandPropType(val)) {
|
||||||
|
sanitizedProps[key as keyof Props] = val as $IntentionalAny
|
||||||
|
} else {
|
||||||
|
sanitizedProps[key as keyof Props] = toLonghandProp(
|
||||||
|
val,
|
||||||
|
) as $IntentionalAny
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
sanitizedProps = {...props}
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
type: 'compound',
|
type: 'compound',
|
||||||
props,
|
props: sanitizedProps,
|
||||||
valueType: null as $IntentionalAny,
|
valueType: null as $IntentionalAny,
|
||||||
[s]: 'TheatrePropType',
|
[propTypeSymbol]: 'TheatrePropType',
|
||||||
label: extras?.label,
|
label: extras?.label,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -72,7 +115,7 @@ export const number = (
|
||||||
type: 'number',
|
type: 'number',
|
||||||
valueType: 0,
|
valueType: 0,
|
||||||
default: defaultValue,
|
default: defaultValue,
|
||||||
[s]: 'TheatrePropType',
|
[propTypeSymbol]: 'TheatrePropType',
|
||||||
...(opts ? opts : {}),
|
...(opts ? opts : {}),
|
||||||
label: opts?.label,
|
label: opts?.label,
|
||||||
nudgeFn: opts?.nudgeFn ?? defaultNumberNudgeFn,
|
nudgeFn: opts?.nudgeFn ?? defaultNumberNudgeFn,
|
||||||
|
@ -97,7 +140,7 @@ export const boolean = (
|
||||||
type: 'boolean',
|
type: 'boolean',
|
||||||
default: defaultValue,
|
default: defaultValue,
|
||||||
valueType: null as $IntentionalAny,
|
valueType: null as $IntentionalAny,
|
||||||
[s]: 'TheatrePropType',
|
[propTypeSymbol]: 'TheatrePropType',
|
||||||
label: extras?.label,
|
label: extras?.label,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -112,14 +155,14 @@ export const boolean = (
|
||||||
*/
|
*/
|
||||||
export const string = (
|
export const string = (
|
||||||
defaultValue: string,
|
defaultValue: string,
|
||||||
extras: PropTypeConfigExtras,
|
extras?: PropTypeConfigExtras,
|
||||||
): PropTypeConfig_String => {
|
): PropTypeConfig_String => {
|
||||||
return {
|
return {
|
||||||
type: 'string',
|
type: 'string',
|
||||||
default: defaultValue,
|
default: defaultValue,
|
||||||
valueType: null as $IntentionalAny,
|
valueType: null as $IntentionalAny,
|
||||||
[s]: 'TheatrePropType',
|
[propTypeSymbol]: 'TheatrePropType',
|
||||||
label: extras.label,
|
label: extras?.label,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -141,7 +184,7 @@ export function stringLiteral<Opts extends {[key in string]: string}>(
|
||||||
type: 'stringLiteral',
|
type: 'stringLiteral',
|
||||||
default: defaultValue,
|
default: defaultValue,
|
||||||
options: {...options},
|
options: {...options},
|
||||||
[s]: 'TheatrePropType',
|
[propTypeSymbol]: 'TheatrePropType',
|
||||||
valueType: null as $IntentionalAny,
|
valueType: null as $IntentionalAny,
|
||||||
as: extras?.as ?? 'menu',
|
as: extras?.as ?? 'menu',
|
||||||
label: extras?.label,
|
label: extras?.label,
|
||||||
|
@ -163,7 +206,7 @@ export function stringLiteral<Opts extends {[key in string]: string}>(
|
||||||
|
|
||||||
interface IBasePropType<ValueType> {
|
interface IBasePropType<ValueType> {
|
||||||
valueType: ValueType
|
valueType: ValueType
|
||||||
[s]: 'TheatrePropType'
|
[propTypeSymbol]: 'TheatrePropType'
|
||||||
label: string | undefined
|
label: string | undefined
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
39
theatre/core/src/propTypes/internals.ts
Normal file
39
theatre/core/src/propTypes/internals.ts
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
import {InvalidArgumentError} from '@theatre/shared/utils/errors'
|
||||||
|
import type {$IntentionalAny} from '@theatre/shared/utils/types'
|
||||||
|
import userReadableTypeOfValue from '@theatre/shared/utils/userReadableTypeOfValue'
|
||||||
|
import {isPlainObject} from 'lodash-es'
|
||||||
|
import type {PropTypeConfig} from './index'
|
||||||
|
import * as t from './index'
|
||||||
|
|
||||||
|
export const propTypeSymbol = Symbol('TheatrePropType_Basic')
|
||||||
|
|
||||||
|
export function isLonghandPropType(t: unknown): t is PropTypeConfig {
|
||||||
|
return (
|
||||||
|
typeof t === 'object' &&
|
||||||
|
!!t &&
|
||||||
|
(t as $IntentionalAny)[propTypeSymbol] === 'TheatrePropType'
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function toLonghandProp(p: unknown): PropTypeConfig {
|
||||||
|
if (typeof p === 'number') {
|
||||||
|
return t.number(p)
|
||||||
|
} else if (typeof p === 'boolean') {
|
||||||
|
return t.boolean(p)
|
||||||
|
} else if (typeof p === 'string') {
|
||||||
|
return t.string(p)
|
||||||
|
} else if (typeof p === 'object' && !!p) {
|
||||||
|
if (isLonghandPropType(p)) return p
|
||||||
|
if (isPlainObject(p)) {
|
||||||
|
return t.compound(p as $IntentionalAny)
|
||||||
|
} else {
|
||||||
|
throw new InvalidArgumentError(
|
||||||
|
`This value is not a valid prop type: ${userReadableTypeOfValue(p)}`,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
throw new InvalidArgumentError(
|
||||||
|
`This value is not a valid prop type: ${userReadableTypeOfValue(p)}`,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
|
@ -34,11 +34,11 @@ export async function setupTestSheet(sheetState: SheetState_Historic) {
|
||||||
const objPublicAPI = sheetPublicAPI.object(
|
const objPublicAPI = sheetPublicAPI.object(
|
||||||
'obj',
|
'obj',
|
||||||
t.compound({
|
t.compound({
|
||||||
position: t.compound({
|
position: {
|
||||||
x: t.number(0),
|
x: t.number(0),
|
||||||
y: t.number(1),
|
y: t.number(1),
|
||||||
z: t.number(2),
|
z: t.number(2),
|
||||||
}),
|
},
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue