Skip to content
MotionActor
Esc
↑↓navigate↵open⌘Jpreview

Slots

A slot is a place in an actor that other actors are spawned into, in an order the scene controls.

A slot is a field that says where children of an actor can go. It holds nothing when the actor spawns. Whoever uses the actor fills it: the scene, the actor’s own onInit, or one of its commands. Use a slot when the actor is a container whose contents vary, such as a window, a list or a deck. For a part that every instance has, declare a child instead.

import { function derived<TThis, T>(fn: (this: TThis) => T): DerivedDefinition<T, TThis>derived, class LayoutActor<TEvents extends ActorEventMap = {}>LayoutActor, 
function signal<T>(): RequiredSignal<T> (+1 overload)
module signal
A timeline value on an actor, and an input to every spawn of it. Without an initial value the signal is required: `ctx.spawn` must supply it.
signal
,
function slot<const TAccepts extends SlotAccepts>(options: {
    accepts: TAccepts;
    multiple?: false;
}): OnePlaceField<TAccepts> (+1 overload)
Declare a place: a named position this actor's children are hosted in. Written as a field; the field reads as a handle once the actor spawns. ```ts screen = slot({ accepts: LayoutActor }); // this.screen.current letters = slot({ accepts: Letter, multiple: true }); // this.letters.all() ```
slot
} from "@motionactor/core";
class class LetterLetter extends class LayoutActor<TEvents extends ActorEventMap = {}>LayoutActor { Letter.char: ActorFieldSignal<string>char = signal<string>(initial: string): ActorFieldSignal<string> (+1 overload)
A timeline value on an actor, and an input to every spawn of it. Without an initial value the signal is required: `ctx.spawn` must supply it.
signal
("");
} export class class WordWord extends class LayoutActor<TEvents extends ActorEventMap = {}>LayoutActor { Word.text: ActorFieldSignal<string>text = signal<string>(initial: string): ActorFieldSignal<string> (+1 overload)
A timeline value on an actor, and an input to every spawn of it. Without an initial value the signal is required: `ctx.spawn` must supply it.
signal
("");
Word.letters: ManyPlaceField<typeof Letter>letters =
slot<typeof Letter>(options: {
    accepts: typeof Letter;
    multiple: true;
}): ManyPlaceField<typeof Letter> (+1 overload)
Declare a place: a named position this actor's children are hosted in. Written as a field; the field reads as a handle once the actor spawns. ```ts screen = slot({ accepts: LayoutActor }); // this.screen.current letters = slot({ accepts: Letter, multiple: true }); // this.letters.all() ```
slot
({ accepts: typeof Letteraccepts: class LetterLetter, multiple: truemultiple: true });
Word.spelled: DerivedDefinition<string, unknown>spelled = derived<unknown, string>(fn: (this: unknown) => string): DerivedDefinition<string, unknown>derived(() => this.Word.letters: ManyPlaceField<typeof Letter>letters.function all(): Letter[]
Children present at the reading frame, in current hosting order.
all
().Array<Letter>.map<string>(callbackfn: (value: Letter, index: number, array: Letter[]) => string, thisArg?: any): string[]
Calls a defined callback function on each element of an array, and returns an array that contains the results.
@paramcallbackfn A function that accepts up to three arguments. The map method calls the callbackfn function one time for each element in the array.@paramthisArg An object to which the this keyword can refer in the callbackfn function. If thisArg is omitted, undefined is used as the this value.
map
((letter: Letterletter) => letter: Letterletter.Letter.char: () => stringchar()).Array<string>.join(separator?: string): string
Adds all the elements of an array into a string, separated by the specified separator string.
@paramseparator A string used to separate one element of the array from the next in the resulting string. If omitted, the array elements are separated with a comma.
join
(""),
); Word.onInit(): voidonInit() { for (const const char: stringchar of this.Word.text: () => stringtext()) { this.Word.letters: ManyPlaceField<typeof Letter>letters.
function spawn(overrides?: {
    measuredBox?: undefined;
    transform?: {
        scale?: SignalInputValue<number> | undefined;
        rotation?: SignalInputValue<number> | undefined;
    } | undefined;
    id?: undefined;
    char?: SignalInputValue<string> | undefined;
    layout?: {
        x?: SignalInputValue<LayoutValue> | undefined;
        y?: SignalInputValue<LayoutValue> | undefined;
        width?: Readonly<{
            kind: "layout.percent";
            value: number;
        }> | SignalInputValue<number | `${number}%` | Readonly<{
            kind: "layout.percent";
            value: number;
        }>> | undefined;
        height?: Readonly<...> | ... 1 more ... | undefined;
    } | undefined;
    ... 18 more ...;
    parent?: undefined;
} | undefined): Letter (+1 overload)
Host a child of the one declared class.
spawn
({ char?: SignalInputValue<string> | undefinedchar });
} } }

Declaring

slot({ accepts, multiple }) takes the class it accepts, or an array of classes. Spawning anything else into it throws an error naming the slot and the class. A subclass of an accepted class is accepted, so slot({ accepts: LayoutActor }) takes any actor that has a box.

multiple is false by default, and the slot holds one actor at a time.

The handle

When the actor spawns, the field becomes a handle on the slot.

Handle One Multiple
spawn(Class, overrides) Spawns into the slot; throws while it is occupied Spawns at the end
spawn(overrides) The same, constructing the accepted class The same
current The occupant, or undefined
all() The occupants in order
reorder(next) Sets the order

spawn(overrides) needs one accepted class to construct, so it throws when accepts is an array or is Actor or LayoutActor itself. Pass the class then.

Reads go through the timeline. all() and current in a scene script give the occupants at the cursor; in a derived they give the occupants at the frame being resolved, so spelled above follows every change to the slot:

import { function createScene<TSceneExports, const I extends SceneInputs = Readonly<Record<never, SceneInput>>, const L extends SceneLayers = Readonly<Record<never, SceneLayer<BackendRef<string>>>>>(script: SceneScript<I, L, TSceneExports>, options?: SceneDefinitionOptions<I, L>): SceneDefinition<I, L, TSceneExports>
Declare a scene. Nothing compiles until a host builds it: `scene.build({ viewport, fps, inputs })` runs the script as `(ctx, inputs, viewport, layers)` and returns the compiled scene. `ctx.spawn` places a root actor in the default layer; `layers.<key>.spawn` places it in a declared one. `ctx.stage` stages another scene on this scene's clock, and a staged scene may stage again.
createScene
} from "@motionactor/core";
import { class WordWord } from "./word"; const
const compiled: CompiledScene<{
    word: Word;
}, CompiledLayersOf<Readonly<Record<never, SceneLayer<BackendRef<string>>>>>>
compiled
=
createScene<{
    word: Word;
}, Readonly<Record<never, SceneInput>>, Readonly<Record<never, SceneLayer<BackendRef<string>>>>>(script: SceneScript<Readonly<Record<never, SceneInput>>, Readonly<Record<never, SceneLayer<BackendRef<string>>>>, {
    word: Word;
}>, options?: SceneDefinitionOptions<Readonly<Record<never, SceneInput>>, Readonly<Record<never, SceneLayer<BackendRef<string>>>>> | undefined): SceneDefinition<...>
Declare a scene. Nothing compiles until a host builds it: `scene.build({ viewport, fps, inputs })` runs the script as `(ctx, inputs, viewport, layers)` and returns the compiled scene. `ctx.spawn` places a root actor in the default layer; `layers.<key>.spawn` places it in a declared one. `ctx.stage` stages another scene on this scene's clock, and a staged scene may stage again.
createScene
(function* (ctx: SceneContextctx) {
const const word: Wordword = ctx: SceneContextctx.
CompileContext.spawn<Word>(definition: ActorClass<Word>, overrides?: {
    text?: SignalInputValue<string> | undefined;
    measuredBox?: undefined;
    spelled?: undefined;
    onInit?: undefined;
    layout?: {
        x?: SignalInputValue<LayoutValue> | undefined;
        y?: SignalInputValue<LayoutValue> | undefined;
        width?: Readonly<{
            kind: "layout.percent";
            value: number;
        }> | SignalInputValue<number | `${number}%` | Readonly<{
            kind: "layout.percent";
            value: number;
        }>> | undefined;
        height?: Readonly<...> | ... 1 more ... | undefined;
    } | undefined;
    ... 19 more ...;
    parent?: undefined;
} | undefined, options?: {
    ...;
} | undefined): Word (+1 overload)
spawn
(class WordWord, { text?: SignalInputValue<string> | undefinedtext: "stop" });
yield* ctx: SceneContextctx.CompileContext.wait(duration: Duration): Generator<Frame, void, unknown>wait(1); const [const s: Letters, const t: Lettert, const o: Lettero, const p: Letterp] = const word: Wordword.Word.letters: ManyPlaceField<typeof Letter>letters.function all(): Letter[]
Children present at the reading frame, in current hosting order.
all
();
const word: Wordword.Word.letters: ManyPlaceField<typeof Letter>letters.function reorder(next: readonly Letter[]): void
Record a new order from the current frame on. `next` is exactly the children present now, reordered; a missing, extra or duplicate child throws.
reorder
([const p: Letterp, const o: Lettero, const t: Lettert, const s: Letters]);
yield* ctx: SceneContextctx.CompileContext.wait(duration: Duration): Generator<Frame, void, unknown>wait(1); yield* const t: Lettert.Actor<{}>.exit(_params?: unknown): Generator<Frame, void, unknown>exit(); yield* ctx: SceneContextctx.CompileContext.wait(duration: Duration): Generator<Frame, void, unknown>wait(1); return { word: Wordword }; }).
SceneDefinition<Readonly<Record<never, SceneInput>>, Readonly<Record<never, SceneLayer<BackendRef<string>>>>, { ...; }>.build(args: SceneBuildInput<Readonly<Record<never, SceneInput>>>): CompiledScene<{
    word: Word;
}, CompiledLayersOf<Readonly<Record<never, SceneLayer<BackendRef<string>>>>>>
build
({ viewport: SceneViewportviewport: { SceneViewport.width: numberwidth: 1920, SceneViewport.height: numberheight: 1080 }, fps: numberfps: 30 });
const compiled: CompiledScene<{
    word: Word;
}, CompiledLayersOf<Readonly<Record<never, SceneLayer<BackendRef<string>>>>>>
compiled
.
CompiledScene<{ ...; }, CompiledLayersOf<...>>.at(frame: Frame, options?: FrameResolutionOptions): FrameState<{
    word: Word;
}>
at
(0).
FrameState<{ ...; }>.scene: {
    word: ResolvedActorOfInstance<Word> | undefined;
}
scene
.word: ResolvedActorOfInstance<Word> | undefinedword?.spelled: string | undefinedspelled; // "stop"
const compiled: CompiledScene<{
    word: Word;
}, CompiledLayersOf<Readonly<Record<never, SceneLayer<BackendRef<string>>>>>>
compiled
.
CompiledScene<{ ...; }, CompiledLayersOf<...>>.at(frame: Frame, options?: FrameResolutionOptions): FrameState<{
    word: Word;
}>
at
(30).
FrameState<{ ...; }>.scene: {
    word: ResolvedActorOfInstance<Word> | undefined;
}
scene
.word: ResolvedActorOfInstance<Word> | undefinedword?.spelled: string | undefinedspelled; // "pots"
const compiled: CompiledScene<{
    word: Word;
}, CompiledLayersOf<Readonly<Record<never, SceneLayer<BackendRef<string>>>>>>
compiled
.
CompiledScene<{ ...; }, CompiledLayersOf<...>>.at(frame: Frame, options?: FrameResolutionOptions): FrameState<{
    word: Word;
}>
at
(60).
FrameState<{ ...; }>.scene: {
    word: ResolvedActorOfInstance<Word> | undefined;
}
scene
.word: ResolvedActorOfInstance<Word> | undefinedword?.spelled: string | undefinedspelled; // "pos"

reorder takes every current occupant once. Leaving one out, adding one or repeating one throws.

Leaving a slot

An occupant leaves when it exits. exit() runs its onExit and then despawns it, and the slot is free from that frame on. A one-actor slot can then take a new occupant.

A deck with one multiple slot. The deck’s deal and collect stagger the cards’ enter() and exit() through this.cards.all(), never touching a card’s state. The reorder between them changes the order all() returns, so collect takes the cards in their new order.

Drawing a slot

Each resolved actor lists its occupants’ ids by slot in slotChildren, in order. Each occupant has its host’s id in parentId and the slot’s name in slotName. A renderer receives the occupants already drawn, as slots, and places each element where the slot belongs.

Because a host only names the class it accepts, it can hold actors it knows nothing else about. The Desktop example has windows with slot({ accepts: LayoutActor }), and each window hosts an app it never imports.

Was this page helpful?