diff --git a/devEnv/api-docs.mjs b/devEnv/api-docs.mjs new file mode 100644 index 0000000..ac26cd0 --- /dev/null +++ b/devEnv/api-docs.mjs @@ -0,0 +1,33 @@ +;(async function () { + // better quote function from https://github.com/google/zx/pull/167 + $.quote = function quote(arg) { + return arg + if (/^[a-z0-9/_.-]+$/i.test(arg)) { + return arg + } + return ( + `$'` + + arg + .replace(/\\/g, '\\\\') + .replace(/'/g, "\\'") + .replace(/\f/g, '\\f') + .replace(/\n/g, '\\n') + .replace(/\r/g, '\\r') + .replace(/\t/g, '\\t') + .replace(/\v/g, '\\v') + .replace(/\0/g, '\\0') + + `'` + ) + } + + const watch = argv.watch === true + + await Promise.all( + ['core', 'studio'].map( + (which) => + $`typedoc ${ + watch ? '--watch ' : '' + } --out docs/api/${which} --name "@theatre/${which}" --tsconfig theatre/tsconfig.json --excludeInternal --sort source-order --readme none --hideBreadcrumbs true --categorizeByGroup false --defaultCategory "Main APIs" --hideInPageTOC true theatre/${which}/src/index.ts`, + ), + ) +})() diff --git a/docs/.vuepress/config.js b/docs/.vuepress/config.js index 9431549..6fa999a 100644 --- a/docs/.vuepress/config.js +++ b/docs/.vuepress/config.js @@ -56,6 +56,7 @@ module.exports = { ], }, ], + sidebar: [ { title: 'Guide', @@ -64,8 +65,28 @@ module.exports = { { title: 'API', path: '/api', - // sidebarDepth: 2, - children: ['/api/core/', '/api/studio/'], + sidebarDepth: 2, + children: [ + { + title: '@theatre/core', + path: '/api/core/', + children: [ + { + title: 'Main exports', + path: '/api/core/', + }, + { + title: 'types', + path: '/api/core/modules/types', + }, + ], + }, + + { + title: '@theatre/studio', + path: '/api/studio/', + }, + ], }, { title: 'Support', diff --git a/package.json b/package.json index cc187ea..d92b98b 100644 --- a/package.json +++ b/package.json @@ -14,6 +14,7 @@ "postinstall": "husky install", "deploy": "zx devEnv/deploy.mjs", "lint:all": "eslint . --ext ts,tsx --ignore-path=.gitignore --rulesdir ./devEnv/eslint/rules", + "docs:api": "zx devEnv/api-docs.mjs", "docs:dev": "vuepress dev docs", "docs:build": "vuepress build docs" }, diff --git a/theatre/core/src/propTypes/index.ts b/theatre/core/src/propTypes/index.ts index b9f0455..132018b 100644 --- a/theatre/core/src/propTypes/index.ts +++ b/theatre/core/src/propTypes/index.ts @@ -1,14 +1,178 @@ +/** + * Usage: + * ```ts + * import {types as t} from '@theatre/core' + * + * const props = t.compound({ + * x: t.number(0), + * y: t.number(0) + * }) + * + * + * const obj = sheet.obj("An object", props) + * ``` + * @module types + */ import type {$IntentionalAny} from '@theatre/shared/utils/types' const s = Symbol('TheatrePropType_Basic') -type S = typeof s + +/** + * Creates a compound prop type (basically a JS object). + * Usage: + * ```ts + * // the root prop type of an object is always a compound + * const props = t.compound({ + * // compounds can be nested + * position: t.compound({ + * x: t.number(0), + * y: t.number(0) + * }) + * }) + * + * const obj = sheet.obj('key', props) + * console.log(obj.value) // {position: {x: 10.3, y: -1}} + * ``` + * @param props + * @param extras + * @returns + * + * @category Prop type definitions + */ +export const compound = ( + props: Props, + extras?: PropTypeConfigExtras, +): PropTypeConfig_Compound => { + return { + type: 'compound', + props, + valueType: null as $IntentionalAny, + [s]: 'TheatrePropType', + label: extras?.label, + } +} + +/** + * + * @param defaultValue + * @param opts + * @returns + * + * @category Prop type definitions + */ +export const number = ( + defaultValue: number, + opts?: { + nudgeFn?: PropTypeConfig_Number['nudgeFn'] + range?: PropTypeConfig_Number['range'] + nudgeMultiplier?: number + } & PropTypeConfigExtras, +): PropTypeConfig_Number => { + return { + type: 'number', + valueType: 0, + default: defaultValue, + [s]: 'TheatrePropType', + ...(opts ? opts : {}), + label: opts?.label, + nudgeFn: opts?.nudgeFn ?? defaultNumberNudgeFn, + nudgeMultiplier: + typeof opts?.nudgeMultiplier === 'number' ? opts.nudgeMultiplier : 1, + } +} + +/** + * + * @param defaultValue + * @param extras + * @returns + * + * @category Prop type definitions + */ +export const boolean = ( + defaultValue: boolean, + extras?: PropTypeConfigExtras, +): PropTypeConfig_Boolean => { + return { + type: 'boolean', + default: defaultValue, + valueType: null as $IntentionalAny, + [s]: 'TheatrePropType', + label: extras?.label, + } +} + +/** + * + * @param defaultValue + * @param extras + * @returns + * + * @category Prop type definitions + */ +export const string = ( + defaultValue: string, + extras: PropTypeConfigExtras, +): PropTypeConfig_String => { + return { + type: 'string', + default: defaultValue, + valueType: null as $IntentionalAny, + [s]: 'TheatrePropType', + label: extras.label, + } +} + +/** + * + * @param defaultValue + * @param options + * @param extras + * @returns + * + * @category Prop type definitions + */ +export function stringLiteral( + defaultValue: Extract, + options: Opts, + extras?: {as?: 'menu' | 'switch'} & PropTypeConfigExtras, +): PropTypeConfig_StringLiteral> { + return { + type: 'stringLiteral', + default: defaultValue, + options: {...options}, + [s]: 'TheatrePropType', + valueType: null as $IntentionalAny, + as: extras?.as ?? 'menu', + label: extras?.label, + } +} + +// export const rgba = ( +// defaultValue: {r: number; b: number; g: number; a: number}, +// extras?: PropTypeConfigExtras, +// ): PropTypeConfig_CSSRGBA => { +// return { +// type: 'cssrgba', +// valueType: null as $IntentionalAny, +// [s]: 'TheatrePropType', +// label: extras?.label, +// default: defaultValue, +// } +// } interface IBasePropType { valueType: ValueType + /** + * @internal + */ [s]: 'TheatrePropType' label: string | undefined } +/** + * @internal + */ export interface PropTypeConfig_Number extends IBasePropType { type: 'number' default: number @@ -16,7 +180,9 @@ export interface PropTypeConfig_Number extends IBasePropType { nudgeFn: NumberNudgeFn nudgeMultiplier: number } - +/** + * @internal + */ export type NumberNudgeFn = (p: { deltaX: number deltaFraction: number @@ -39,68 +205,29 @@ const defaultNumberNudgeFn: NumberNudgeFn = ({ return deltaX * magnitude * config.nudgeMultiplier } - -export const number = ( - defaultValue: number, - opts?: { - nudgeFn?: PropTypeConfig_Number['nudgeFn'] - range?: PropTypeConfig_Number['range'] - nudgeMultiplier?: number - } & PropTypeConfigExtras, -): PropTypeConfig_Number => { - return { - type: 'number', - valueType: 0, - default: defaultValue, - [s]: 'TheatrePropType', - ...(opts ? opts : {}), - label: opts?.label, - nudgeFn: opts?.nudgeFn ?? defaultNumberNudgeFn, - nudgeMultiplier: - typeof opts?.nudgeMultiplier === 'number' ? opts.nudgeMultiplier : 1, - } -} - +/** + * @internal + */ export interface PropTypeConfig_Boolean extends IBasePropType { type: 'boolean' default: boolean } - -export type PropTypeConfigExtras = { +/** + * @internal + */ +export interface PropTypeConfigExtras { label?: string } - -export const boolean = ( - defaultValue: boolean, - extras?: PropTypeConfigExtras, -): PropTypeConfig_Boolean => { - return { - type: 'boolean', - default: defaultValue, - valueType: null as $IntentionalAny, - [s]: 'TheatrePropType', - label: extras?.label, - } -} - -export interface PropTypeConfig_String extends IBasePropType { +/** + * @internal + */ export interface PropTypeConfig_String extends IBasePropType { type: 'string' default: string } -export const string = ( - defaultValue: string, - extras: PropTypeConfigExtras, -): PropTypeConfig_String => { - return { - type: 'string', - default: defaultValue, - valueType: null as $IntentionalAny, - [s]: 'TheatrePropType', - label: extras.label, - } -} - +/** + * @internal + */ export interface PropTypeConfig_StringLiteral extends IBasePropType { type: 'stringLiteral' @@ -109,22 +236,6 @@ export interface PropTypeConfig_StringLiteral as: 'menu' | 'switch' } -export function stringLiteral( - defaultValue: Extract, - options: Opts, - extras?: {as?: 'menu' | 'switch'} & PropTypeConfigExtras, -): PropTypeConfig_StringLiteral> { - return { - type: 'stringLiteral', - default: defaultValue, - options: {...options}, - [s]: 'TheatrePropType', - valueType: null as $IntentionalAny, - as: extras?.as ?? 'menu', - label: extras?.label, - } -} - type IValidCompoundProps = { [K in string]: PropTypeConfig } @@ -133,6 +244,8 @@ type IValidCompoundProps = { * @todo Determine if 'compound' is a clear term for what this is. * I didn't want to use 'object' as it could get confused with * SheetObject. + * + * @internal */ export interface PropTypeConfig_Compound extends IBasePropType<{[K in keyof Props]: Props[K]['valueType']}> { @@ -140,51 +253,31 @@ export interface PropTypeConfig_Compound props: Record } -export const compound = ( - props: Props, - extras?: PropTypeConfigExtras, -): PropTypeConfig_Compound => { - return { - type: 'compound', - props, - valueType: null as $IntentionalAny, - [s]: 'TheatrePropType', - label: extras?.label, - } -} - -export interface PropTypeConfig_CSSRGBA - extends IBasePropType<{r: number; g: number; b: number; a: number}> { - type: 'cssrgba' - default: {r: number; g: number; b: number; a: number} -} - -export const rgba = ( - defaultValue: {r: number; b: number; g: number; a: number}, - extras?: PropTypeConfigExtras, -): PropTypeConfig_CSSRGBA => { - return { - type: 'cssrgba', - valueType: null as $IntentionalAny, - [s]: 'TheatrePropType', - label: extras?.label, - default: defaultValue, - } -} - +// export interface PropTypeConfig_CSSRGBA +// extends IBasePropType<{r: number; g: number; b: number; a: number}> { +// type: 'cssrgba' +// default: {r: number; g: number; b: number; a: number} +// } +/** + * @internal + */ export interface PropTypeConfig_Enum extends IBasePropType<{}> { type: 'enum' cases: Record defaultCase: string } - +/** + * @category Prop type definitions + */ export type PropTypeConfig_AllPrimitives = | PropTypeConfig_Number | PropTypeConfig_Boolean | PropTypeConfig_String | PropTypeConfig_StringLiteral<$IntentionalAny> - | PropTypeConfig_CSSRGBA - +// | PropTypeConfig_CSSRGBA +/** + * @internal + */ export type PropTypeConfig = | PropTypeConfig_AllPrimitives | PropTypeConfig_Compound<$IntentionalAny> diff --git a/theatre/studio/src/TheatreStudio.ts b/theatre/studio/src/TheatreStudio.ts index d4593ea..a3c0bab 100644 --- a/theatre/studio/src/TheatreStudio.ts +++ b/theatre/studio/src/TheatreStudio.ts @@ -23,7 +23,9 @@ export interface ITransactionAPI { set(pointer: Pointer, value: V): void unset(pointer: Pointer): void } - +/** + * + */ export interface PaneClassDefinition< DataType extends PropTypeConfig_Compound<{}>, > { @@ -34,11 +36,28 @@ export interface PaneClassDefinition< }> } -export type IExtension = { +/** + * A Theatre.js Studio extension. You can define one either + * in a separate package, or within your project. + */ +export interface IExtension { + /** + * Pick a unique ID for your extension. Ideally the name would be unique if + * the extension was to be published to the npm repository. + */ id: string + /** + * Set this if you'd like to add a component to the global toolbar (on the top) + */ globalToolbar?: { + /** + * A basic react component. + */ component: React.ComponentType<{}> } + /** + * Introduces new pane types. + */ panes?: Array> } @@ -47,7 +66,25 @@ export type PaneInstance = { instanceId: string definition: PaneClassDefinition<$FixMe> } - +/** + * This is the public api of Theatre's studio. It is exposed through: + * + * Basic usage: + * ```ts + * import studio from '@theatre/studio' + * + * studio.initialize() + * ``` + * + * Usage with **tree-shaking**: + * ```ts + * import studio from '@theatre/studio' + * + * if (process.env.NODE_ENV !== 'production') { + * studio.initialize() + * } + * ``` + */ export interface IStudio { readonly ui: { /** @@ -65,17 +102,8 @@ export interface IStudio { } /** - * Initializes the studio. Call this in your index.js/index.ts module. - * - * Usage with **tree-shaking**: - * ```ts - * import studio from '@theratre/studio' - * - * // Add this check if you wish to not include the studio in your production bundle - * if (process.env.NODE_ENV === "development") { - * studio.initialize() - * } - * ``` + * Initializes the studio. Call it once in your index.js/index.ts module. + * It silently ignores subsequent calls. */ initialize(): void @@ -105,10 +133,23 @@ export interface IStudio { /** * The current selection, consisting of Sheets and Sheet Objects + * + * Example: + * ```ts + * console.log(studio.selection) // => [ISheetObject, ISheet] + * ``` */ readonly selection: Array - extend(extension: IExtension): void + /** + * Registers an extension + */ + extend( + /** + * The extension's definition + */ + extension: IExtension, + ): void getPanesOfType( paneClass: PaneClass, @@ -118,6 +159,12 @@ export interface IStudio { paneClass: PaneClass, ): PaneInstance + /** + * Returns the Theatre.js project that contains the studio's sheets and objects. + * + * It is useful if you'd like to have sheets/objects that are present only when + * studio is present. + */ getStudioProject(): IProject } diff --git a/theatre/studio/src/index.ts b/theatre/studio/src/index.ts index 5264000..1718e12 100644 --- a/theatre/studio/src/index.ts +++ b/theatre/studio/src/index.ts @@ -1,18 +1,23 @@ +/** + * @module @theatre/studio + */ import {setStudio} from '@theatre/studio/getStudio' import {Studio} from '@theatre/studio/Studio' -export type {IScrub} from '@theatre/studio/Scrub' -export type {IStudio} from '@theatre/studio/TheatreStudio' + import * as globalVariableNames from '@theatre/shared/globalVariableNames' import type {$FixMe} from '@theatre/shared/utils/types' import StudioBundle from './StudioBundle' import type CoreBundle from '@theatre/core/CoreBundle' -export {default as ToolbarSwitchSelect} from './uiComponents/toolbar/ToolbarSwitchSelect' -export {default as ToolbarIconButton} from './uiComponents/toolbar/ToolbarIconButton' -export {default as ToolbarDropdownSelect} from './uiComponents/toolbar/ToolbarDropdownSelect' +import type {IStudio} from '@theatre/studio/TheatreStudio' const studioPrivateAPI = new Studio() setStudio(studioPrivateAPI) -export const studio = studioPrivateAPI.publicApi + +/** + * The main instance of Studio. Read more at {@link IStudio} + */ +const studio: IStudio = studioPrivateAPI.publicApi + export default studio registerStudioBundle() @@ -60,3 +65,9 @@ function registerStudioBundle() { studioBundle.registerCoreBundle(possibleCoreBundle) } } + +export {default as ToolbarSwitchSelect} from './uiComponents/toolbar/ToolbarSwitchSelect' +export {default as ToolbarIconButton} from './uiComponents/toolbar/ToolbarIconButton' +export {default as ToolbarDropdownSelect} from './uiComponents/toolbar/ToolbarDropdownSelect' +export type {IScrub} from '@theatre/studio/Scrub' +export type {IStudio, IExtension} from '@theatre/studio/TheatreStudio' diff --git a/theatre/studio/src/panels/DetailPanel/propEditors/DeterminePropEditor.tsx b/theatre/studio/src/panels/DetailPanel/propEditors/DeterminePropEditor.tsx index df6772d..dda8cb9 100644 --- a/theatre/studio/src/panels/DetailPanel/propEditors/DeterminePropEditor.tsx +++ b/theatre/studio/src/panels/DetailPanel/propEditors/DeterminePropEditor.tsx @@ -68,7 +68,7 @@ const propEditorByPropType: { enum: () => <>, boolean: BooleanPropEditor, stringLiteral: StringLiteralPropEditor, - cssrgba: () => <>, + // cssrgba: () => <>, } const DeterminePropEditor: React.FC<{