import type { $IntentionalAny } from '@theatre/shared/utils/types' ; import userReadableTypeOfValue from '@theatre/shared/utils/userReadableTypeOfValue' ; import { mapValues } from 'lodash-es' ; import type { IShorthandCompoundProps , IValidCompoundProps , ShorthandCompoundPropsToLonghandCompoundProps , } from './internals' ; import { sanitizeCompoundProps } from './internals' ; import { propTypeSymbol } from './internals' ; const validateCommonOpts = ( fnCallSignature , opts ? ) => { if ( process . env . NODE_ENV !== 'production' ) { if ( opts === undefined ) return ; if ( typeof opts !== 'object' || opts === null ) { throw new Error ( `opts in ${ fnCallSignature } must either be undefined or an object.` ) ; } if ( Object . prototype . hasOwnProperty . call ( opts , 'label' ) ) { const { label } = opts ; if ( typeof label !== 'string' ) { throw new Error ( `opts.label in ${ fnCallSignature } should be a string. ${ userReadableTypeOfValue ( label ) } given.` ) ; } if ( label . trim ( ) . length !== label . length ) { throw new Error ( `opts.label in ${ fnCallSignature } should not start/end with whitespace. "${ label }" given.` ) ; } if ( label . length === 0 ) { throw new Error ( `opts.label in ${ fnCallSignature } should not be an empty string. If you wish to have no label, remove opts.label from opts.` ) ; } } } } ; example export const compound = < Props extends IShorthandCompoundProps > ( props , opts ? ) : PropTypeConfig_Compound < ShorthandCompoundPropsToLonghandCompoundProps < Props > , Props > => { validateCommonOpts ( 't.compound(props, opts)' , opts ) ; const sanitizedProps = sanitizeCompoundProps ( props ) ; return { type : 'compound' , props : sanitizedProps , valueType : null as $IntentionalAny , [ propTypeSymbol ] : 'TheatrePropType' , label : opts ?. label , default : mapValues ( sanitizedProps , ( p ) => p . default ) as $IntentionalAny , } ; } ; example param defaultValue param opts returns export const number = ( defaultValue , opts ? ) : PropTypeConfig_Number => { if ( process . env . NODE_ENV !== 'production' ) { validateCommonOpts ( 't.number(defaultValue, opts)' , opts ) ; if ( typeof defaultValue !== 'number' || ! isFinite ( defaultValue ) ) { throw new Error ( `Argument defaultValue in t.number(defaultValue) must be a number. ${ userReadableTypeOfValue ( defaultValue ) } given.` ) ; } if ( typeof opts === 'object' && opts !== null ) { if ( Object . prototype . hasOwnProperty . call ( opts , 'range' ) ) { if ( ! Array . isArray ( opts . range ) ) { throw new Error ( `opts.range in t.number(defaultValue, opts) must be a tuple of two numbers. ${ userReadableTypeOfValue ( opts . range ) } given.` ) ; } if ( opts . range . length !== 2 ) { throw new Error ( `opts.range in t.number(defaultValue, opts) must have two elements. ${ opts . range . length } given.` ) ; } if ( ! opts . range . every ( ( n ) => typeof n === 'number' && ! isNaN ( n ) ) ) { throw new Error ( `opts.range in t.number(defaultValue, opts) must be a tuple of two numbers.` ) ; } if ( opts . range [ 0 ] >= opts . range [ 1 ] ) { throw new Error ( `opts.range[0] in t.number(defaultValue, opts) must be smaller than opts.range[1]. Given: ${ JSON . stringify ( opts . range ) }` ) ; } } if ( Object . prototype . hasOwnProperty . call ( opts , 'nudgeMultiplier' ) ) { if ( typeof opts ! . nudgeMultiplier !== 'number' || ! isFinite ( opts ! . nudgeMultiplier ) ) { throw new Error ( `opts.nudgeMultiplier in t.number(defaultValue, opts) must be a finite number. ${ userReadableTypeOfValue ( opts ! . nudgeMultiplier ) } given.` ) ; } } if ( Object . prototype . hasOwnProperty . call ( opts , 'nudgeFn' ) ) { if ( typeof opts ?. nudgeFn !== 'function' ) { throw new Error ( `opts.nudgeFn in t.number(defaultValue, opts) must be a function. ${ userReadableTypeOfValue ( opts ! . nudgeFn ) } given.` ) ; } } } } const sanitize = ! opts ?. sanitize ? _sanitizeNumber : ( val ) : number | undefined => { const n = _sanitizeNumber ( val ) ; if ( typeof n === 'number' ) { return opts . sanitize ! ( n ) ; } } ; return { type : 'number' , valueType : 0 , default : defaultValue , [ propTypeSymbol ] : 'TheatrePropType' , ... ( opts ? opts : { } ) , label : opts ?. label , nudgeFn : opts ?. nudgeFn ?? defaultNumberNudgeFn , nudgeMultiplier : typeof opts ?. nudgeMultiplier === 'number' ? opts . nudgeMultiplier : 1 , isScalar : true , sanitize : sanitize , interpolate : opts ?. interpolate ?? _interpolateNumber , } ; } ; const _sanitizeNumber = ( value ) : undefined | number => typeof value === 'number' && isFinite ( value ) ? value : undefined ; const _interpolateNumber = ( left , right , progression ) : number => { return left + progression * ( right - left ) ; } ; example param defaultValue param opts export const boolean = ( defaultValue , opts ? ) : PropTypeConfig_Boolean => { if ( process . env . NODE_ENV !== 'production' ) { validateCommonOpts ( 't.boolean(defaultValue, opts)' , opts ) ; if ( typeof defaultValue !== 'boolean' ) { throw new Error ( `defaultValue in t.boolean(defaultValue) must be a boolean. ${ userReadableTypeOfValue ( defaultValue ) } given.` ) ; } } return { type : 'boolean' , default : defaultValue , valueType : null as $IntentionalAny , [ propTypeSymbol ] : 'TheatrePropType' , label : opts ?. label , sanitize : _sanitizeBoolean , interpolate : leftInterpolate , } ; } ; const _sanitizeBoolean = ( val ) : boolean | undefined => { return typeof val === 'boolean' ? val : undefined ; } ; function leftInterpolate < T > ( left ) { return left ; } example param defaultValue param opts returns export const string = ( defaultValue , opts ? ) : PropTypeConfig_String => { if ( process . env . NODE_ENV !== 'production' ) { validateCommonOpts ( 't.string(defaultValue, opts)' , opts ) ; if ( typeof defaultValue !== 'string' ) { throw new Error ( `defaultValue in t.string(defaultValue) must be a string. ${ userReadableTypeOfValue ( defaultValue ) } given.` ) ; } } return { type : 'string' , default : defaultValue , valueType : null as $IntentionalAny , [ propTypeSymbol ] : 'TheatrePropType' , label : opts ?. label , sanitize ( value ) { if ( opts ?. sanitize ) return opts . sanitize ( value ) ; return typeof value === 'string' ? value : undefined ; } , interpolate : opts ?. interpolate ?? leftInterpolate , } ; } ; example returns export function stringLiteral < Opts extends { [ key in string ] : string ; } > ( /**
 * Default value (a string that equals one of the options)
 */ defaultValue , /**
 * The options. Use the `"value": "Label"` format.
 *
 * An object like `{[value]: Label}`. Example: `{r: "Red", "g": "Green"}`
 */ options , /**
 * opts.as Determines if editor is shown as a menu or a switch. Either 'menu' or 'switch'.  Default: 'menu'
 */ opts ? ) { return { type : 'stringLiteral' , default : defaultValue , options : { ... options } , [ propTypeSymbol ] : 'TheatrePropType' , valueType : null as $IntentionalAny , as : opts ?. as ?? 'menu' , label : opts ?. label , sanitize ( value ) { if ( typeof value !== 'string' ) return undefined ; if ( Object . hasOwnProperty . call ( options , value ) ) { return value as $IntentionalAny ; } else { return undefined ; } } , interpolate : leftInterpolate , } ; } export type Sanitizer < T > = ( value : unknown ) => T | undefined ; export type Interpolator < T > = ( left : T , right : T , progression : number ) => T ; interface IBasePropType < ValueType , PropTypes = ValueType > { valueType : ValueType ; [ propTypeSymbol ] : 'TheatrePropType' ; label : string | undefined ; isScalar ? : true ; sanitize ? : Sanitizer < PropTypes > ; interpolate ? : Interpolator < PropTypes > ; default : ValueType ; } export interface PropTypeConfig_Number extends IBasePropType < number > { type : 'number' ; default : number ; range ? : [ min : number , max : number ] ; nudgeFn : NumberNudgeFn ; nudgeMultiplier : number ; } export type NumberNudgeFn = ( p : { deltaX : number ; deltaFraction : number ; magnitude : number ; config : PropTypeConfig_Number ; } ) => number ; const defaultNumberNudgeFn = ( { config , deltaX , deltaFraction , magnitude , } ) => { const { range } = config ; if ( range ) { return ( deltaFraction * ( range [ 1 ] - range [ 0 ] ) * magnitude * config . nudgeMultiplier ) ; } return deltaX * magnitude * config . nudgeMultiplier ; } ; export interface PropTypeConfig_Boolean extends IBasePropType < boolean > { type : 'boolean' ; default : boolean ; } interface CommonOpts { label ? : string ; } export interface PropTypeConfig_String extends IBasePropType < string > { type : 'string' ; default : string ; } export interface PropTypeConfig_StringLiteral < T extends string > extends IBasePropType < T > { type : 'stringLiteral' ; default : T ; options : Record < T , string > ; as : 'menu' | 'switch' ; } /**
 *
 */ export interface PropTypeConfig_Compound < Props extends IValidCompoundProps , PropTypes = Props > extends IBasePropType < { [ K in keyof Props ] : Props [ K ] [ 'valueType' ] ; } , PropTypes > { type : 'compound' ; props : Record < string , PropTypeConfig > ; } export interface PropTypeConfig_Enum extends IBasePropType < { } > { type : 'enum' ; cases : Record < string , PropTypeConfig > ; defaultCase : string ; } export type PropTypeConfig_AllPrimitives = PropTypeConfig_Number | PropTypeConfig_Boolean | PropTypeConfig_String | PropTypeConfig_StringLiteral < $IntentionalAny > ; export type PropTypeConfig = PropTypeConfig_AllPrimitives | PropTypeConfig_Compound < $IntentionalAny > | PropTypeConfig_Enum ;