import { createElement } from 'react' import { Effect, Layer, Schema as S } from 'effect' import { Composition, Engine, Event, Reducer, State, Ui, mount, one, provide, scene, slot, type CapturedScene, type DerivedContract, type SlotService, ui, } from '@playfast/reform' import { type CompositionChildSummary, type EngineServices, type WiredUi } from '@playfast/reform/internal' const GreetingBase: State.StateDefinition<'greeting', string, typeof S.String> = State.make( 'greeting', S.String, ) class Greeting extends GreetingBase {} class Greeted extends Event.make('Greeted', S.Struct({ text: S.String })) {} class Cleared extends Event.make('Cleared', S.Struct({})) {} class SetGreeting extends Reducer.make('SetGreeting', { states: [Greeting], events: [Greeted], }) {} class ClearGreeting extends Reducer.make('ClearGreeting', { states: [Greeting], events: [Cleared], }) {} const PanelUiBase: WiredUi< DerivedContract<{ props: S.Struct<{ greeting: typeof S.String }> events: { greet: S.Struct<{ text: typeof S.String }> clear: S.Struct<{}> } }>, 'Panel' > = ui('Panel', { props: S.Struct({ greeting: S.String }), events: { greet: S.Struct({ text: S.String }), clear: S.Struct({}) }, }) class PanelUi extends PanelUiBase {} class Panel extends Composition.make('Panel', { title: 'Panel', ui: PanelUi, states: [Greeting], })() {} class MainSlot extends slot('Main')() {} const ShellUiBase: Ui.UiClass<{ props: {}; slots: { Main: MainSlot } }, 'Shell'> = ui('Shell')<{ props: {} slots: { Main: MainSlot } }>() class ShellUi extends ShellUiBase {} class Shell extends Composition.make('Shell', { title: 'Shell', slots: { Main: MainSlot }, ui: ShellUi, })() {} type SlottedScene = CapturedScene< typeof ShellUi.Contract, readonly [], | EngineServices | State.StateStore<'greeting', string> | Ui.UiService<'Panel'> | Ui.UiService<'Shell'> | Composition.CompositionId<'Shell', Shell, MainSlot> | Composition.CompositionId<'Panel', Panel, never> | SlotService<'Main', MainSlot, CompositionChildSummary>, unknown, 'Shell', Shell > const makeSlottedScene = (): SlottedScene => { const presentation = Layer.mergeAll( provide( ShellUi, Ui.make(ShellUi, (_props, slots) => createElement(slots.Main, {})), ), provide( PanelUi, Ui.make(PanelUi, ({ greeting }) => greeting), ), provide(MainSlot, Panel), State.live(Greeting, 'hi'), ) const app = Layer.mergeAll( Composition.live(Shell, function* () { yield* Effect.void return mount({ props: {}, slots: { Main: one({}) } }) }), Composition.live(Panel, function* () { const greeting = yield* Greeting const greet = yield* Event.trigger(Greeted) const clearTrigger = yield* Event.trigger(Cleared) const clear = (): void => clearTrigger({}) return mount({ props: { greeting }, slots: {}, events: { greet, clear }, }) }), Reducer.live(SetGreeting, (_greeting, event) => event.text), Reducer.live(ClearGreeting, () => ''), ).pipe(Layer.provideMerge(presentation), Layer.provideMerge(Engine)) return scene(Shell, { provide: [app] }) } export const slottedScene: typeof makeSlottedScene = makeSlottedScene