A web-based diagramming tool for Event Modeling built on top of diagram-js.
-
Prerequisites
- Node.js 18+ (recommended: 20 LTS)
- npm 9+
-
Install
npm install
-
Start dev server
npm run dev- Open the URL shown in your terminal (default:
http://localhost:5173/)
-
Build for production
npm run build- Output in
dist/
-
Optional tooling
- Format:
npm run format - Lint:
npm run lint
- Format:
-
Directory layout
src/Editor.js: Editor bootstrap and module compositionsrc/main.js: App entry (creates the editor)src/modeling/*: Domain element factory, modeling commandssrc/providers/*: Palette provider(s)src/render/*: Custom renderer, text rendering, path mapsrc/spec/*: Domain specs (business objects)src/utils/*: Utility helperspublic/: Static assetsindex.html: App shell
References:
- diagram-js: https://github.com/bpmn-io/diagram-js
- Sliceify composes custom modules (providers, modeling, renderer) with diagram-js’ core features (palette, selection, move, connect, etc.).
- It separates domain data (businessObject) from presentation (geometry, color, label styles) on the diagram element.
- Modules are wired through diagram-js’ dependency injection (DI) container.
src/Editor.jsextendsDiagram(from diagram-js) and composes:- Custom modules:
providers,render,modeling - Built-in modules: palette, selection, move, connect, snapping, grid, etc.
- Custom modules:
src/main.jsinstantiatesEditor, passing the canvas container and optionaladditionalModules(to inject config or overrides).
- Each module exports a plain object with:
__depends__: other modules it requires__init__: services to instantiate eagerly- service providers:
name: ['type'|'value'|'factory', Provider]
- Services declare dependencies via
Service.$inject = [...]. - You can pass config via “value services” in
additionalModules, e.g.:{ 'config.Renderer': ['value', { defaultFillColor: 'white' }] }
-
Modeling (
src/modeling/index.js)- Registers:
elementFactory: custom factory for diagram elementsmodeling: extends diagram-js modeling to add commandsEMFactory: creates domain business objects (Specs) + defaults
- Depends on core diagram-js modeling, rules, selection, command stack.
- Registers:
-
Providers (
src/providers/index.js)- Registers the palette provider (
paletteProvider) which populates the left-hand palette.
- Registers the palette provider (
-
Rendering (
src/render/index.js)- Registers:
renderer: customBaseRendereroverride for shapestextRenderer: wraps diagram-jsTextutil with defaultspathMap: optional vector path utilities for icons/markers
- Registers:
- Located in
src/spec/*. - Examples:
Event,Element,Field. - Each spec uses normalized, lower-cased domain fields like:
type,name,description,metadata. - Defaults useful to the UI are attached to the prototype for palette/renderer usage:
Event.prototype.THUMBNAIL_CLASSEvent.prototype.DEFAULT_SIZEEvent.prototype.DEFAULT_COLOREvent.prototype.type = 'Event'
src/modeling/EMFactory.js- Maps a
typeto a Spec class and instantiates it:create(type) => new Spec(). - Provides default accessors:
getDefaultSize(semantic)andgetDefaultColor(semantic)
src/modeling/ElementFactory.js- Overrides
create(elementType, attrs)to:- Ensure
attrs.businessObject(creates viaEMFactoryif missing) - Apply defaults:
- width/height from
EMFactory.getDefaultSize() - color on the element (presentation), not on the business object
- width/height from
- Delegate to base factory to produce the actual shape
- Ensure
Example (from the palette):
const shape = elementFactory.createShape({ type: 'Event' });src/modeling/Modeling.jsextends diagram-jsBaseModeling.- Adds custom command
element.updatePropertiesthat mutates the business object via a handler (undo/redo through the command stack). - Provides a
connecthelper that consultsruleswhen connecting elements.
-
src/render/Renderer.jsextends diagram-jsBaseRenderer:- Declares handlers per element type (e.g.,
Event) to draw shapes. - Uses
renderEmbeddedLabelto create styled SVG text viatextRenderer. - Reads color from the element first (
element.color || bo.color). - Label styling can be customized per call via an options object (
align,fontSize,fontWeight,style).
- Declares handlers per element type (e.g.,
-
src/render/TextRenderer.jswraps diagram-jsTextutil with default font settings (configurable viaconfig.textRenderer). -
src/render/PathMap.jsprovides optional vector path utilities (for icons/markers). It is not required for simple rectangles/labels; use it if you add richer pictograms.
src/providers/PaletteProvider.js- Registers palette entries (tools + create actions).
- On click/drag, calls
elementFactory.createShape({ type })and starts thecreateinteraction. - Uses each Spec’s prototype constants for the entry (e.g.,
THUMBNAIL_CLASS).
- Domain (businessObject): holds domain fields only (
type,name,source, etc.). - Presentation (element): holds visual attributes (
x,y,width,height,color, label styles). - Renderer reads presentation attributes from the element.
- Create a Spec in
src/spec/:
export default class Command extends Element {}
Command.prototype.type = 'Command';
Command.prototype.DEFAULT_SIZE = { width: 120, height: 60 };
Command.prototype.DEFAULT_COLOR = '#4f46e5';
Command.prototype.THUMBNAIL_CLASS = 'palette-icon-create-command';- Register in
EMFactory:
typeToSpec.set('Command', Command);- Add a palette entry:
entries['create-Command'] = createAction(
'Command',
'state',
Command.prototype.THUMBNAIL_CLASS,
'Create Command'
);- Render it in
Renderer:
handlers.Command = (parentGfx, element) => {
const rect = drawRect(parentGfx, element.width, element.height, 0, {
fill: getFillColor(element, defaultFillColor),
stroke: getStrokeColor(element, defaultStrokeColor)
});
renderEmbeddedLabel(parentGfx, element, { align: 'left-top', fontWeight: 'bold' });
return rect;
};- (Optional) Define rules if creation or connections must be restricted.
- Create a custom
RuleProvidermodule to control creation, connection, and move behavior. This keeps the editor predictable as types and features grow.
- Export/import a JSON schema with:
- Elements:
id,type,x,y,width,height, and presentation attrs - Business objects: domain fields needed for your model
- Elements:
- Keep domain and presentation separated in your schema.
- Provide defaults at startup using
additionalModules:
new Editor({
container: document.querySelector('#canvas'),
additionalModules: [
{ 'config.Renderer': ['value', { defaultFillColor: 'white', defaultStrokeColor: '#111' }] },
{ 'config.textRenderer': ['value', { defaultStyle: { fontSize: 14 } }] }
]
});- diagram-js: https://github.com/bpmn-io/diagram-js