From 707ad8a69cadd0fcab12e6ebcf66d9a02e53ec4e Mon Sep 17 00:00:00 2001 From: Nico Rehwaldt Date: Fri, 26 Apr 2024 17:49:13 +0200 Subject: [PATCH] feat: allow to type services and events This PR builds on upon diagram-js provided service and event typing: * https://github.com/bpmn-io/diagram-js/pull/862 It allows you to specify (public) types exposed by your BPMN toolkit trough a ServiceMap. Events exposed are parsed from the EventBus dynamic types. Closes #2121 --- lib/BaseModeler.js | 6 ++++- lib/BaseModeler.spec.ts | 39 ++++++++++++++++++++++++++++- lib/BaseViewer.js | 50 ++++++++++++++++++++++++++++++++++--- lib/BaseViewer.spec.ts | 38 ++++++++++++++++++++++++++-- lib/Modeler.js | 5 +++- lib/Modeler.spec.ts | 39 ++++++++++++++++++++++++++++- lib/NavigatedViewer.js | 4 +++ lib/NavigatedViewer.spec.ts | 38 +++++++++++++++++++++++++++- lib/Viewer.js | 6 ++++- lib/Viewer.spec.ts | 39 ++++++++++++++++++++++++++++- 10 files changed, 252 insertions(+), 12 deletions(-) diff --git a/lib/BaseModeler.js b/lib/BaseModeler.js index 0e34460231..c1649f0062 100644 --- a/lib/BaseModeler.js +++ b/lib/BaseModeler.js @@ -15,7 +15,11 @@ import BaseViewer from './BaseViewer'; /** * A base modeler for BPMN 2.0 diagrams. * - * See {@link Modeler} for a fully-featured modeler. + * See {@link bpmn-js/lib/Modeler} for a fully-featured modeler. + * + * @template [ServiceMap=null] + * + * @extends BaseViewer * * @param {BaseViewerOptions} [options] The options to configure the modeler. */ diff --git a/lib/BaseModeler.spec.ts b/lib/BaseModeler.spec.ts index a3b5b71275..a5fb6ad5dd 100644 --- a/lib/BaseModeler.spec.ts +++ b/lib/BaseModeler.spec.ts @@ -1,3 +1,6 @@ +import Canvas from 'diagram-js/lib/core/Canvas'; +import EventBus from 'diagram-js/lib/core/EventBus'; + import BaseModeler from './BaseModeler'; import { testViewer } from './BaseViewer.spec'; @@ -19,4 +22,38 @@ const extendedModeler = new BaseModeler({ propertiesPanel: { attachTo: '#properties-panel' } -}); \ No newline at end of file +}); + + +// typed API usage + +type FooEvent = { + /** + * Very cool field! + */ + foo: string; +}; + +type EventMap = { + + foo: FooEvent +}; + +type TypeMap = { + canvas: Canvas, + eventBus: EventBus +}; + +const typedModeler = new BaseModeler(); + +const bus = typedModeler.get('eventBus'); + +const canvas = typedModeler.get('canvas'); + +canvas.zoom('fit-viewport'); + +typedModeler.on('foo', event => { + console.log(event.foo); +}); + +typedModeler.get('eventBus').on('foo', e => console.log(e.foo)); \ No newline at end of file diff --git a/lib/BaseViewer.js b/lib/BaseViewer.js index d72edc320f..a5316bb379 100644 --- a/lib/BaseViewer.js +++ b/lib/BaseViewer.js @@ -31,6 +31,12 @@ import { importBpmnDiagram } from './import/Importer'; +/** + * @template T + * + * @typedef { import('diagram-js/lib/core/EventBus').default } EventBus + */ + /** * @template T * @@ -116,12 +122,22 @@ import { * } } SaveSVGDoneEvent */ +/** + * @template Type + * + * @typedef { Type extends { eventBus: EventBus } ? X : never } EventMap + */ + /** * A base viewer for BPMN 2.0 diagrams. * - * Have a look at {@link Viewer}, {@link NavigatedViewer} or {@link Modeler} for + * Have a look at {@link bpmn-js/lib/Viewer}, {@link bpmn-js/lib/NavigatedViewer} or {@link bpmn-js/lib/Modeler} for * bundles that include actual features. * + * @template [ServiceMap=null] + * + * @extends Diagram + * * @param {BaseViewerOptions} [options] The options to configure the viewer. */ export default function BaseViewer(options) { @@ -542,9 +558,18 @@ BaseViewer.prototype.destroy = function() { }; /** - * Register an event listener. + * @overlord * - * Remove an event listener via {@link BaseViewer#off}. + * Register an event listener for events with the given name. + * + * The callback will be invoked with `event, ...additionalArguments` + * that have been passed to {@link EventBus#fire}. + * + * Returning false from a listener will prevent the events default action + * (if any is specified). To stop an event from being processed further in + * other listeners execute {@link Event#stopPropagation}. + * + * Returning anything but `undefined` from a listener will stop the listener propagation. * * @template T * @@ -553,6 +578,25 @@ BaseViewer.prototype.destroy = function() { * @param {EventBusEventCallback} callback The callback. * @param {any} [that] Value of `this` the callback will be called with. */ +/** + * Register an event listener for events with the given name. + * + * The callback will be invoked with `event, ...additionalArguments` + * that have been passed to {@link EventBus#fire}. + * + * Returning false from a listener will prevent the events default action + * (if any is specified). To stop an event from being processed further in + * other listeners execute {@link Event#stopPropagation}. + * + * Returning anything but `undefined` from a listener will stop the listener propagation. + * + * @template {keyof EventMap} EventName + * + * @param {EventName} events to subscribe to + * @param {number} [priority=1000] listen priority + * @param {EventBusEventCallback<(EventMap)[EventName]>} callback + * @param {any} [that] callback context + */ BaseViewer.prototype.on = function(events, priority, callback, that) { return this.get('eventBus').on(events, priority, callback, that); }; diff --git a/lib/BaseViewer.spec.ts b/lib/BaseViewer.spec.ts index b81ad12f69..170e0f8430 100644 --- a/lib/BaseViewer.spec.ts +++ b/lib/BaseViewer.spec.ts @@ -1,6 +1,6 @@ import CommandStack from 'diagram-js/lib/command/CommandStack'; -import { Event } from 'diagram-js/lib/core/EventBus'; +import EventBus, { Event } from 'diagram-js/lib/core/EventBus'; import BaseViewer, { ImportDoneEvent, @@ -11,6 +11,7 @@ import BaseViewer, { } from './BaseViewer'; import OverlaysModule from 'diagram-js/lib/features/overlays'; +import Canvas from 'diagram-js/lib/core/Canvas'; const viewer = new BaseViewer(); @@ -170,4 +171,37 @@ export function testViewer(viewer: BaseViewer) { }); viewer.on('detach', () => {}); -} \ No newline at end of file +} + +// typed API usage + +type FooEvent = { + /** + * Very cool field! + */ + foo: string; +}; + +type EventMap = { + + foo: FooEvent +}; + +type TypeMap = { + canvas: Canvas, + eventBus: EventBus +}; + +const typedViewer = new BaseViewer(); + +const bus = typedViewer.get('eventBus'); + +const canvas = typedViewer.get('canvas'); + +canvas.zoom('fit-viewport'); + +typedViewer.on('foo', event => { + console.log(event.foo); +}); + +typedViewer.get('eventBus').on('foo', e => console.log(e.foo)); diff --git a/lib/Modeler.js b/lib/Modeler.js index 444e34c197..ee00256da2 100644 --- a/lib/Modeler.js +++ b/lib/Modeler.js @@ -64,7 +64,6 @@ var initialDiagram = /** * A modeler for BPMN 2.0 diagrams. * - * * ## Extending the Modeler * * In order to extend the viewer pass extension modules to bootstrap via the @@ -125,6 +124,10 @@ var initialDiagram = * var bpmnModeler = new Modeler({ additionalModules: [ overrideModule ]}); * ``` * + * @template [ServiceMap=null] + * + * @extends BaseModeler + * * @param {BaseViewerOptions} [options] The options to configure the modeler. */ export default function Modeler(options) { diff --git a/lib/Modeler.spec.ts b/lib/Modeler.spec.ts index 3e73ada638..b9b0d6cbed 100644 --- a/lib/Modeler.spec.ts +++ b/lib/Modeler.spec.ts @@ -1,3 +1,6 @@ +import Canvas from 'diagram-js/lib/core/Canvas'; +import EventBus from 'diagram-js/lib/core/EventBus'; + import Modeler from './Modeler'; import { testViewer } from './BaseViewer.spec'; @@ -21,4 +24,38 @@ const extendedModeler = new Modeler({ propertiesPanel: { attachTo: '#properties-panel' } -}); \ No newline at end of file +}); + + +// typed API usage + +type FooEvent = { + /** + * Very cool field! + */ + foo: string; +}; + +type EventMap = { + + foo: FooEvent +}; + +type TypeMap = { + canvas: Canvas, + eventBus: EventBus +}; + +const typedViewer = new Modeler(); + +const bus = typedViewer.get('eventBus'); + +const canvas = typedViewer.get('canvas'); + +canvas.zoom('fit-viewport'); + +typedViewer.on('foo', event => { + console.log(event.foo); +}); + +typedViewer.get('eventBus').on('foo', e => console.log(e.foo)); \ No newline at end of file diff --git a/lib/NavigatedViewer.js b/lib/NavigatedViewer.js index 7417777b07..535c24a816 100644 --- a/lib/NavigatedViewer.js +++ b/lib/NavigatedViewer.js @@ -13,6 +13,10 @@ import ZoomScrollModule from 'diagram-js/lib/navigation/zoomscroll'; /** * A viewer with mouse and keyboard navigation features. * + * @template [ServiceMap=null] + * + * @extends Viewer + * * @param {BaseViewerOptions} [options] */ export default function NavigatedViewer(options) { diff --git a/lib/NavigatedViewer.spec.ts b/lib/NavigatedViewer.spec.ts index 63591fdf49..268188ca2d 100644 --- a/lib/NavigatedViewer.spec.ts +++ b/lib/NavigatedViewer.spec.ts @@ -1,3 +1,6 @@ +import Canvas from 'diagram-js/lib/core/Canvas'; +import EventBus from 'diagram-js/lib/core/EventBus'; + import NavigatedViewer from './NavigatedViewer'; import { testViewer } from './BaseViewer.spec'; @@ -14,4 +17,37 @@ const extendedViewer = new NavigatedViewer({ propertiesPanel: { attachTo: '#properties-panel' } -}); \ No newline at end of file +}); + +// typed API usage + +type FooEvent = { + /** + * Very cool field! + */ + foo: string; +}; + +type EventMap = { + + foo: FooEvent +}; + +type TypeMap = { + canvas: Canvas, + eventBus: EventBus +}; + +const typedViewer = new NavigatedViewer(); + +const bus = typedViewer.get('eventBus'); + +const canvas = typedViewer.get('canvas'); + +canvas.zoom('fit-viewport'); + +typedViewer.on('foo', event => { + console.log(event.foo); +}); + +typedViewer.get('eventBus').on('foo', e => console.log(e.foo)); \ No newline at end of file diff --git a/lib/Viewer.js b/lib/Viewer.js index 0f9b341ee8..2c0acb33a3 100644 --- a/lib/Viewer.js +++ b/lib/Viewer.js @@ -17,7 +17,7 @@ import BaseViewer from './BaseViewer'; /** * A viewer for BPMN 2.0 diagrams. * - * Have a look at {@link NavigatedViewer} or {@link Modeler} for bundles that include + * Have a look at {@link bpmn-js/lib/NavigatedViewer} or {@link bpmn-js/lib/Modeler} for bundles that include * additional features. * * @@ -53,6 +53,10 @@ import BaseViewer from './BaseViewer'; * bpmnViewer.importXML(...); * ``` * + * @template [ServiceMap=null] + * + * @extends BaseViewer + * * @param {BaseViewerOptions} [options] The options to configure the viewer. */ export default function Viewer(options) { diff --git a/lib/Viewer.spec.ts b/lib/Viewer.spec.ts index 0f7abc140e..b7b2ed605b 100644 --- a/lib/Viewer.spec.ts +++ b/lib/Viewer.spec.ts @@ -1,3 +1,6 @@ +import Canvas from 'diagram-js/lib/core/Canvas'; +import EventBus from 'diagram-js/lib/core/EventBus'; + import Viewer from './Viewer'; import { testViewer } from './BaseViewer.spec'; @@ -14,4 +17,38 @@ const extendedViewer = new Viewer({ propertiesPanel: { attachTo: '#properties-panel' } -}); \ No newline at end of file +}); + + +// typed API usage + +type FooEvent = { + /** + * Very cool field! + */ + foo: string; +}; + +type EventMap = { + + foo: FooEvent +}; + +type TypeMap = { + canvas: Canvas, + eventBus: EventBus +}; + +const typedViewer = new Viewer(); + +const bus = typedViewer.get('eventBus'); + +const canvas = typedViewer.get('canvas'); + +canvas.zoom('fit-viewport'); + +typedViewer.on('foo', event => { + console.log(event.foo); +}); + +typedViewer.get('eventBus').on('foo', e => console.log(e.foo)); \ No newline at end of file