Build your first module
This walkthrough adds a small panel to the demo application. It introduces the four pieces most Reactor features begin with: a module and store, plus a panel and workspace.
Create demo/module-hello using the package, TypeScript, and reactor.config.json files in demo/module-todos as a template. Set the config slug to module-hello. The demo launcher discovers every demo/module-* directory containing a Reactor config.
Copy only the package scaffolding:
package.jsontsconfig.jsonwebpack.config.jsreactor.config.json
Create a new src directory for the files below rather than copying the Todo implementation.
The module installs the feature. The store owns its state. The panel model lets a workspace place it. The workspace gives the panel an initial home.
1. Create a store
Stores own observable application state and services:
import { AbstractStore } from '@journeyapps/reactor-mod';
import { observable } from 'mobx';
export class HelloStore extends AbstractStore {
@observable accessor visits = 0;
constructor() {
super({ name: 'HELLO_STORE' });
}
visit() {
this.visits += 1;
this.logger.info('Hello panel visited', { visits: this.visits });
}
}
Constructors establish usable initial state. Override _init() only when the store has asynchronous boot work.
The panel can discover this store during registration. Keep its constructor state safe to read before asynchronous initialization.
2. Create a panel
A panel model represents serializable workspace state. Its factory describes how Reactor creates and renders it.
import React from 'react';
import { observer } from 'mobx-react';
import { WorkspaceModelFactoryEvent } from '@projectstorm/react-workspaces-core';
import {
CardWidget,
ioc,
PanelButtonWidget,
ReactorPanelFactory,
ReactorPanelModel
} from '@journeyapps/reactor-mod';
import { HelloStore } from './HelloStore';
export class HelloPanelModel extends ReactorPanelModel {
constructor() {
super('hello.panel');
this.setExpand(true, true);
}
}
const HelloPanelWidget = observer(() => {
const store = ioc.get(HelloStore);
return (
<CardWidget
title="Hello Reactor"
subHeading={`Visits: ${store.visits}`}
sections={[
{
key: 'hello',
content: () => <PanelButtonWidget label="Visit" icon="hand" action={() => store.visit()} />
}
]}
/>
);
});
export class HelloPanelFactory extends ReactorPanelFactory<HelloPanelModel> {
constructor() {
super({
type: 'hello.panel',
name: 'Hello',
icon: 'hand',
category: 'Examples',
isMultiple: false,
padding: true
});
}
protected generatePanelContent(_event: WorkspaceModelFactoryEvent<HelloPanelModel>) {
return <HelloPanelWidget />;
}
protected _generateModel() {
return new HelloPanelModel();
}
}
3. Install the feature from a module
import {
AbstractReactorModule,
ReactorModuleInitEvent,
ReactorModuleRegisterEvent,
WorkspaceModel,
WorkspaceStore
} from '@journeyapps/reactor-mod';
import { HelloStore } from './HelloStore';
import { HelloPanelFactory } from './HelloPanel';
export class HelloModule extends AbstractReactorModule {
constructor() {
super({ name: 'Hello module' });
}
register(event: ReactorModuleRegisterEvent) {
const workspaceStore = event.ioc.get(WorkspaceStore);
const panelFactory = new HelloPanelFactory();
event.registerStore(HelloStore, new HelloStore());
workspaceStore.registerFactory(panelFactory);
workspaceStore.registerWorkspaceGenerator({
generateWorkspace: async () => {
const root = workspaceStore.generateRootModel();
root.addModel(panelFactory.generateModel());
return new WorkspaceModel({ id: 'hello', name: 'Hello', model: root });
}
});
}
async init(_event: ReactorModuleInitEvent) {}
}
Export the module as the package default from src/index.ts:
import { HelloModule } from './HelloModule';
export default HelloModule;
4. Run it
Restart pnpm demo:watch, select the new module, and open the Hello workspace.
You now have the basic Reactor composition:
- the module installs the feature;
- the store owns its state;
- the panel factory makes the feature renderable;
- the workspace generator gives the panel a home.
Next, replace the inline callback with an action, then add an entity definition so generic Reactor surfaces understand the model.
This first panel uses an inline callback to keep the walkthrough focused. Make “Visit” an action when it needs to appear in another place or gain validation, logging, or progress.