@formicoidea/labre-framework-bpmn 0.23.0 → 0.23.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@formicoidea/labre-framework-bpmn",
3
3
  "description": "Labre bpmn framework for @formicoidea/labre-core.",
4
- "version": "0.23.0",
4
+ "version": "0.23.2",
5
5
  "type": "module",
6
6
  "sideEffects": false,
7
7
  "author": "lajola",
@@ -18,7 +18,7 @@
18
18
  "src"
19
19
  ],
20
20
  "dependencies": {
21
- "@formicoidea/labre-core": "0.23.0",
21
+ "@formicoidea/labre-core": "0.23.2",
22
22
  "lit": "^3.2.0"
23
23
  }
24
24
  }
package/src/consts.ts CHANGED
@@ -1,57 +1,57 @@
1
- import type { BpmnNodeKind } from '@formicoidea/labre-core/model';
2
-
3
- /**
4
- * Visual constants for the BPMN basics. Style "C" (hybrid): spec-accurate
5
- * shapes and line weights, with accent colour only on the event rings — the
6
- * task and gateway stay neutral. All of these are just the creation-time
7
- * defaults; every value is an editable shape property afterwards.
8
- */
9
-
10
- /** Accent stroke for the start event (thin green ring). */
11
- export const EVENT_START = '#43a06b';
12
- /** Accent stroke for the end event (thick red ring). */
13
- export const EVENT_END = '#cf5648';
14
- /** Neutral stroke for task / gateway (matches the EDGY base shapes). */
15
- export const NEUTRAL_STROKE = '#262626';
16
- /** Default fill for events / task / gateway. */
17
- export const NODE_FILL = '#ffffff';
18
-
19
- /** BPMN line weights: thin start ring, thick end ring, regular elsewhere. */
20
- export const START_WIDTH = 2;
21
- export const END_WIDTH = 4;
22
- export const NODE_STROKE_WIDTH = 2;
23
-
24
- /** Task corner radius (absolute px — a lightly rounded rectangle). */
25
- export const TASK_RADIUS = 10;
26
-
27
- /** Inner-text font for the task label. */
28
- export const INNER_FONT_SIZE = 18;
29
-
30
- /** Default node sizes (model units) per kind. */
31
- export const NODE_SIZE: Record<BpmnNodeKind, { w: number; h: number }> = {
32
- startEvent: { w: 56, h: 56 },
33
- endEvent: { w: 56, h: 56 },
34
- task: { w: 120, h: 72 },
35
- gatewayExclusive: { w: 72, h: 72 },
36
- };
37
-
38
- /** Default inner text per kind (only the task carries a label). */
39
- export const NODE_LABEL: Record<BpmnNodeKind, string> = {
40
- startEvent: '',
41
- endEvent: '',
42
- task: 'Task',
43
- gatewayExclusive: '',
44
- };
45
-
46
- /** Pool (background container) defaults. */
47
- export const POOL_BAND_WIDTH = 28;
48
- export const POOL_FRAME_COLOR = '#262626';
49
- export const POOL_BAND_FILL = '#f4f4f5';
50
- export const POOL_FRAME_WIDTH = 1.5;
51
- export const POOL_NAME_FONT_SIZE = 15;
52
- export const POOL_NAME_COLOR = '#262626';
53
- export const POOL_FONT_FAMILY = 'Inter, sans-serif';
54
-
55
- /** Sequence-flow connector preset. */
56
- export const SEQUENCE_STROKE = '#262626';
57
- export const SEQUENCE_WIDTH = 2;
1
+ import type { BpmnNodeKind } from '@formicoidea/labre-core/model';
2
+
3
+ /**
4
+ * Visual constants for the BPMN basics. Style "C" (hybrid): spec-accurate
5
+ * shapes and line weights, with accent colour only on the event rings — the
6
+ * task and gateway stay neutral. All of these are just the creation-time
7
+ * defaults; every value is an editable shape property afterwards.
8
+ */
9
+
10
+ /** Accent stroke for the start event (thin green ring). */
11
+ export const EVENT_START = '#43a06b';
12
+ /** Accent stroke for the end event (thick red ring). */
13
+ export const EVENT_END = '#cf5648';
14
+ /** Neutral stroke for task / gateway (matches the EDGY base shapes). */
15
+ export const NEUTRAL_STROKE = '#262626';
16
+ /** Default fill for events / task / gateway. */
17
+ export const NODE_FILL = '#ffffff';
18
+
19
+ /** BPMN line weights: thin start ring, thick end ring, regular elsewhere. */
20
+ export const START_WIDTH = 2;
21
+ export const END_WIDTH = 4;
22
+ export const NODE_STROKE_WIDTH = 2;
23
+
24
+ /** Task corner radius (absolute px — a lightly rounded rectangle). */
25
+ export const TASK_RADIUS = 10;
26
+
27
+ /** Inner-text font for the task label. */
28
+ export const INNER_FONT_SIZE = 18;
29
+
30
+ /** Default node sizes (model units) per kind. */
31
+ export const NODE_SIZE: Record<BpmnNodeKind, { w: number; h: number }> = {
32
+ startEvent: { w: 56, h: 56 },
33
+ endEvent: { w: 56, h: 56 },
34
+ task: { w: 120, h: 72 },
35
+ gatewayExclusive: { w: 72, h: 72 },
36
+ };
37
+
38
+ /** Default inner text per kind (only the task carries a label). */
39
+ export const NODE_LABEL: Record<BpmnNodeKind, string> = {
40
+ startEvent: '',
41
+ endEvent: '',
42
+ task: 'Task',
43
+ gatewayExclusive: '',
44
+ };
45
+
46
+ /** Pool (background container) defaults. */
47
+ export const POOL_BAND_WIDTH = 28;
48
+ export const POOL_FRAME_COLOR = '#262626';
49
+ export const POOL_BAND_FILL = '#f4f4f5';
50
+ export const POOL_FRAME_WIDTH = 1.5;
51
+ export const POOL_NAME_FONT_SIZE = 15;
52
+ export const POOL_NAME_COLOR = '#262626';
53
+ export const POOL_FONT_FAMILY = 'Inter, sans-serif';
54
+
55
+ /** Sequence-flow connector preset. */
56
+ export const SEQUENCE_STROKE = '#262626';
57
+ export const SEQUENCE_WIDTH = 2;
package/src/effects.ts CHANGED
@@ -1,14 +1,14 @@
1
- import { EdgelessBpmnMenu } from './toolbar/bpmn-menu';
2
- import { EdgelessBpmnSeniorButton } from './toolbar/bpmn-senior-button';
3
-
4
- export function effects() {
5
- customElements.define('edgeless-bpmn-menu', EdgelessBpmnMenu);
6
- customElements.define('edgeless-bpmn-senior-button', EdgelessBpmnSeniorButton);
7
- }
8
-
9
- declare global {
10
- interface HTMLElementTagNameMap {
11
- 'edgeless-bpmn-menu': EdgelessBpmnMenu;
12
- 'edgeless-bpmn-senior-button': EdgelessBpmnSeniorButton;
13
- }
14
- }
1
+ import { EdgelessBpmnMenu } from './toolbar/bpmn-menu';
2
+ import { EdgelessBpmnSeniorButton } from './toolbar/bpmn-senior-button';
3
+
4
+ export function effects() {
5
+ customElements.define('edgeless-bpmn-menu', EdgelessBpmnMenu);
6
+ customElements.define('edgeless-bpmn-senior-button', EdgelessBpmnSeniorButton);
7
+ }
8
+
9
+ declare global {
10
+ interface HTMLElementTagNameMap {
11
+ 'edgeless-bpmn-menu': EdgelessBpmnMenu;
12
+ 'edgeless-bpmn-senior-button': EdgelessBpmnSeniorButton;
13
+ }
14
+ }
@@ -1,93 +1,93 @@
1
- import {
2
- type ElementRenderer,
3
- ElementRendererExtension,
4
- } from '@formicoidea/labre-core/blocks/surface';
5
- import type { BpmnPoolElementModel } from '@formicoidea/labre-core/model';
6
-
7
- import {
8
- POOL_BAND_FILL,
9
- POOL_BAND_WIDTH,
10
- POOL_FONT_FAMILY,
11
- POOL_FRAME_COLOR,
12
- POOL_FRAME_WIDTH,
13
- POOL_NAME_COLOR,
14
- POOL_NAME_FONT_SIZE,
15
- } from './consts';
16
-
17
- /** Trace a rounded-rectangle path (no dependency on ctx.roundRect). */
18
- function roundedRectPath(
19
- ctx: CanvasRenderingContext2D,
20
- x: number,
21
- y: number,
22
- w: number,
23
- h: number,
24
- r: number
25
- ): void {
26
- const rr = Math.min(r, w / 2, h / 2);
27
- ctx.beginPath();
28
- ctx.moveTo(x + rr, y);
29
- ctx.lineTo(x + w - rr, y);
30
- ctx.arcTo(x + w, y, x + w, y + rr, rr);
31
- ctx.lineTo(x + w, y + h - rr);
32
- ctx.arcTo(x + w, y + h, x + w - rr, y + h, rr);
33
- ctx.lineTo(x + rr, y + h);
34
- ctx.arcTo(x, y + h, x, y + h - rr, rr);
35
- ctx.lineTo(x, y + rr);
36
- ctx.arcTo(x, y, x + rr, y, rr);
37
- ctx.closePath();
38
- }
39
-
40
- /**
41
- * Canvas renderer for a BPMN pool: a rounded-rect frame with a vertical name
42
- * band on the left (the participant name is drawn rotated, as in the spec).
43
- * Drawn directly in element space; the band width and font are fixed so they
44
- * stay legible at any pool size. Mirrors the other framework backgrounds.
45
- */
46
- export const bpmnPool: ElementRenderer<BpmnPoolElementModel> = (
47
- model,
48
- ctx,
49
- matrix
50
- ) => {
51
- const [, , w, h] = model.deserializedXYWH;
52
- const cx = w / 2;
53
- const cy = h / 2;
54
- ctx.setTransform(
55
- matrix.translateSelf(cx, cy).rotateSelf(model.rotate).translateSelf(-cx, -cy)
56
- );
57
-
58
- const band = Math.min(POOL_BAND_WIDTH, w);
59
- const inset = POOL_FRAME_WIDTH / 2;
60
-
61
- // Name band (left), filled.
62
- ctx.fillStyle = POOL_BAND_FILL;
63
- ctx.fillRect(0, 0, band, h);
64
-
65
- // Frame + band divider.
66
- ctx.strokeStyle = POOL_FRAME_COLOR;
67
- ctx.lineWidth = POOL_FRAME_WIDTH;
68
- ctx.lineJoin = 'round';
69
- roundedRectPath(ctx, inset, inset, w - POOL_FRAME_WIDTH, h - POOL_FRAME_WIDTH, 6);
70
- ctx.stroke();
71
- ctx.beginPath();
72
- ctx.moveTo(band, 0);
73
- ctx.lineTo(band, h);
74
- ctx.stroke();
75
-
76
- // Participant name, rotated to read up the band (skip when empty / too narrow).
77
- if (model.name && band > 12) {
78
- ctx.save();
79
- ctx.translate(band / 2, h / 2);
80
- ctx.rotate(-Math.PI / 2);
81
- ctx.fillStyle = POOL_NAME_COLOR;
82
- ctx.font = `600 ${POOL_NAME_FONT_SIZE}px ${POOL_FONT_FAMILY}`;
83
- ctx.textAlign = 'center';
84
- ctx.textBaseline = 'middle';
85
- ctx.fillText(model.name, 0, 0);
86
- ctx.restore();
87
- }
88
- };
89
-
90
- export const BpmnPoolRendererExtension = ElementRendererExtension(
91
- 'bpmnPool',
92
- bpmnPool
93
- );
1
+ import {
2
+ type ElementRenderer,
3
+ ElementRendererExtension,
4
+ } from '@formicoidea/labre-core/blocks/surface';
5
+ import type { BpmnPoolElementModel } from '@formicoidea/labre-core/model';
6
+
7
+ import {
8
+ POOL_BAND_FILL,
9
+ POOL_BAND_WIDTH,
10
+ POOL_FONT_FAMILY,
11
+ POOL_FRAME_COLOR,
12
+ POOL_FRAME_WIDTH,
13
+ POOL_NAME_COLOR,
14
+ POOL_NAME_FONT_SIZE,
15
+ } from './consts';
16
+
17
+ /** Trace a rounded-rectangle path (no dependency on ctx.roundRect). */
18
+ function roundedRectPath(
19
+ ctx: CanvasRenderingContext2D,
20
+ x: number,
21
+ y: number,
22
+ w: number,
23
+ h: number,
24
+ r: number
25
+ ): void {
26
+ const rr = Math.min(r, w / 2, h / 2);
27
+ ctx.beginPath();
28
+ ctx.moveTo(x + rr, y);
29
+ ctx.lineTo(x + w - rr, y);
30
+ ctx.arcTo(x + w, y, x + w, y + rr, rr);
31
+ ctx.lineTo(x + w, y + h - rr);
32
+ ctx.arcTo(x + w, y + h, x + w - rr, y + h, rr);
33
+ ctx.lineTo(x + rr, y + h);
34
+ ctx.arcTo(x, y + h, x, y + h - rr, rr);
35
+ ctx.lineTo(x, y + rr);
36
+ ctx.arcTo(x, y, x + rr, y, rr);
37
+ ctx.closePath();
38
+ }
39
+
40
+ /**
41
+ * Canvas renderer for a BPMN pool: a rounded-rect frame with a vertical name
42
+ * band on the left (the participant name is drawn rotated, as in the spec).
43
+ * Drawn directly in element space; the band width and font are fixed so they
44
+ * stay legible at any pool size. Mirrors the other framework backgrounds.
45
+ */
46
+ export const bpmnPool: ElementRenderer<BpmnPoolElementModel> = (
47
+ model,
48
+ ctx,
49
+ matrix
50
+ ) => {
51
+ const [, , w, h] = model.deserializedXYWH;
52
+ const cx = w / 2;
53
+ const cy = h / 2;
54
+ ctx.setTransform(
55
+ matrix.translateSelf(cx, cy).rotateSelf(model.rotate).translateSelf(-cx, -cy)
56
+ );
57
+
58
+ const band = Math.min(POOL_BAND_WIDTH, w);
59
+ const inset = POOL_FRAME_WIDTH / 2;
60
+
61
+ // Name band (left), filled.
62
+ ctx.fillStyle = POOL_BAND_FILL;
63
+ ctx.fillRect(0, 0, band, h);
64
+
65
+ // Frame + band divider.
66
+ ctx.strokeStyle = POOL_FRAME_COLOR;
67
+ ctx.lineWidth = POOL_FRAME_WIDTH;
68
+ ctx.lineJoin = 'round';
69
+ roundedRectPath(ctx, inset, inset, w - POOL_FRAME_WIDTH, h - POOL_FRAME_WIDTH, 6);
70
+ ctx.stroke();
71
+ ctx.beginPath();
72
+ ctx.moveTo(band, 0);
73
+ ctx.lineTo(band, h);
74
+ ctx.stroke();
75
+
76
+ // Participant name, rotated to read up the band (skip when empty / too narrow).
77
+ if (model.name && band > 12) {
78
+ ctx.save();
79
+ ctx.translate(band / 2, h / 2);
80
+ ctx.rotate(-Math.PI / 2);
81
+ ctx.fillStyle = POOL_NAME_COLOR;
82
+ ctx.font = `600 ${POOL_NAME_FONT_SIZE}px ${POOL_FONT_FAMILY}`;
83
+ ctx.textAlign = 'center';
84
+ ctx.textBaseline = 'middle';
85
+ ctx.fillText(model.name, 0, 0);
86
+ ctx.restore();
87
+ }
88
+ };
89
+
90
+ export const BpmnPoolRendererExtension = ElementRendererExtension(
91
+ 'bpmnPool',
92
+ bpmnPool
93
+ );
@@ -1,116 +1,116 @@
1
- import { EdgelessCRUDIdentifier } from '@formicoidea/labre-core/blocks/surface';
2
- import type { BpmnPoolElementModel } from '@formicoidea/labre-core/model';
3
- import type { PointerEventState } from '@formicoidea/labre-core/std';
4
- import {
5
- GfxElementModelView,
6
- GfxViewInteractionExtension,
7
- } from '@formicoidea/labre-core/std/gfx';
8
-
9
- /**
10
- * View for a BPMN pool. A double-click edits the participant name in place
11
- * (single field — the whole pool is the hit target). Mirrors the inline label
12
- * editor used by the EDGY / Wardley backgrounds.
13
- */
14
- export class BpmnPoolView extends GfxElementModelView<BpmnPoolElementModel> {
15
- static override type: string = 'bpmnPool';
16
-
17
- private _nameEditor: HTMLInputElement | null = null;
18
-
19
- override onCreated(): void {
20
- super.onCreated();
21
- this.on('dblclick', e => this._onDblClick(e));
22
- }
23
-
24
- override onDestroyed(): void {
25
- this._closeEditor();
26
- super.onDestroyed();
27
- }
28
-
29
- private _onDblClick(e: PointerEventState): void {
30
- if (this.model.isLocked()) return;
31
- this._openEditor(e);
32
- }
33
-
34
- private _openEditor(e: PointerEventState): void {
35
- this._closeEditor();
36
-
37
- const input = document.createElement('input');
38
- input.value = String(this.model.name ?? '');
39
- Object.assign(input.style, {
40
- position: 'fixed',
41
- left: `${e.raw.clientX}px`,
42
- top: `${e.raw.clientY}px`,
43
- transform: 'translate(-50%, -50%)',
44
- zIndex: '10000',
45
- minWidth: '140px',
46
- padding: '3px 8px',
47
- font: '14px Inter, sans-serif',
48
- color: 'var(--affine-text-primary-color, #1f2328)',
49
- background: 'var(--affine-background-overlay-panel-color, #ffffff)',
50
- border: '1px solid var(--affine-primary-color, #1e96eb)',
51
- borderRadius: '6px',
52
- boxShadow: 'var(--affine-shadow-2, 0 2px 8px rgba(0,0,0,0.18))',
53
- outline: 'none',
54
- });
55
- document.body.append(input);
56
- this._nameEditor = input;
57
-
58
- // Mark "editing" so the global edgeless key handlers (delete, escape, …)
59
- // don't act on the pool while the user types.
60
- this.gfx.selection.set({ elements: [this.model.id], editing: true });
61
-
62
- input.focus();
63
- input.select();
64
-
65
- const commit = () => {
66
- if (this._nameEditor !== input) return;
67
- const value = input.value;
68
- this._closeEditor();
69
- this.gfx.std.store.captureSync();
70
- this.gfx.std
71
- .get(EdgelessCRUDIdentifier)
72
- .updateElement(this.model.id, { name: value });
73
- };
74
-
75
- input.addEventListener('keydown', ev => {
76
- ev.stopPropagation();
77
- if (ev.key === 'Enter') {
78
- ev.preventDefault();
79
- commit();
80
- } else if (ev.key === 'Escape') {
81
- ev.preventDefault();
82
- this._closeEditor();
83
- }
84
- });
85
- input.addEventListener('blur', commit);
86
- }
87
-
88
- private _closeEditor(): void {
89
- if (!this._nameEditor) return;
90
- const input = this._nameEditor;
91
- this._nameEditor = null;
92
- input.remove();
93
- if (this.isConnected) {
94
- this.gfx.selection.set({ elements: [this.model.id], editing: false });
95
- }
96
- }
97
- }
98
-
99
- /**
100
- * Resize gating: the resize handles are hidden unless `model.resizeEnabled` is
101
- * true (toggled from the toolbar). Moving / selecting stays available.
102
- */
103
- export const BpmnPoolInteraction = GfxViewInteractionExtension<BpmnPoolView>(
104
- BpmnPoolView.type,
105
- {
106
- handleResize({ model }) {
107
- return {
108
- beforeResize({ set }) {
109
- if (!model.resizeEnabled) {
110
- set({ allowedHandlers: [] });
111
- }
112
- },
113
- };
114
- },
115
- }
116
- );
1
+ import { EdgelessCRUDIdentifier } from '@formicoidea/labre-core/blocks/surface';
2
+ import type { BpmnPoolElementModel } from '@formicoidea/labre-core/model';
3
+ import type { PointerEventState } from '@formicoidea/labre-core/std';
4
+ import {
5
+ GfxElementModelView,
6
+ GfxViewInteractionExtension,
7
+ } from '@formicoidea/labre-core/std/gfx';
8
+
9
+ /**
10
+ * View for a BPMN pool. A double-click edits the participant name in place
11
+ * (single field — the whole pool is the hit target). Mirrors the inline label
12
+ * editor used by the EDGY / Wardley backgrounds.
13
+ */
14
+ export class BpmnPoolView extends GfxElementModelView<BpmnPoolElementModel> {
15
+ static override type: string = 'bpmnPool';
16
+
17
+ private _nameEditor: HTMLInputElement | null = null;
18
+
19
+ override onCreated(): void {
20
+ super.onCreated();
21
+ this.on('dblclick', e => this._onDblClick(e));
22
+ }
23
+
24
+ override onDestroyed(): void {
25
+ this._closeEditor();
26
+ super.onDestroyed();
27
+ }
28
+
29
+ private _onDblClick(e: PointerEventState): void {
30
+ if (this.model.isLocked()) return;
31
+ this._openEditor(e);
32
+ }
33
+
34
+ private _openEditor(e: PointerEventState): void {
35
+ this._closeEditor();
36
+
37
+ const input = document.createElement('input');
38
+ input.value = String(this.model.name ?? '');
39
+ Object.assign(input.style, {
40
+ position: 'fixed',
41
+ left: `${e.raw.clientX}px`,
42
+ top: `${e.raw.clientY}px`,
43
+ transform: 'translate(-50%, -50%)',
44
+ zIndex: '10000',
45
+ minWidth: '140px',
46
+ padding: '3px 8px',
47
+ font: '14px Inter, sans-serif',
48
+ color: 'var(--affine-text-primary-color, #1f2328)',
49
+ background: 'var(--affine-background-overlay-panel-color, #ffffff)',
50
+ border: '1px solid var(--affine-primary-color, #1e96eb)',
51
+ borderRadius: '6px',
52
+ boxShadow: 'var(--affine-shadow-2, 0 2px 8px rgba(0,0,0,0.18))',
53
+ outline: 'none',
54
+ });
55
+ document.body.append(input);
56
+ this._nameEditor = input;
57
+
58
+ // Mark "editing" so the global edgeless key handlers (delete, escape, …)
59
+ // don't act on the pool while the user types.
60
+ this.gfx.selection.set({ elements: [this.model.id], editing: true });
61
+
62
+ input.focus();
63
+ input.select();
64
+
65
+ const commit = () => {
66
+ if (this._nameEditor !== input) return;
67
+ const value = input.value;
68
+ this._closeEditor();
69
+ this.gfx.std.store.captureSync();
70
+ this.gfx.std
71
+ .get(EdgelessCRUDIdentifier)
72
+ .updateElement(this.model.id, { name: value });
73
+ };
74
+
75
+ input.addEventListener('keydown', ev => {
76
+ ev.stopPropagation();
77
+ if (ev.key === 'Enter') {
78
+ ev.preventDefault();
79
+ commit();
80
+ } else if (ev.key === 'Escape') {
81
+ ev.preventDefault();
82
+ this._closeEditor();
83
+ }
84
+ });
85
+ input.addEventListener('blur', commit);
86
+ }
87
+
88
+ private _closeEditor(): void {
89
+ if (!this._nameEditor) return;
90
+ const input = this._nameEditor;
91
+ this._nameEditor = null;
92
+ input.remove();
93
+ if (this.isConnected) {
94
+ this.gfx.selection.set({ elements: [this.model.id], editing: false });
95
+ }
96
+ }
97
+ }
98
+
99
+ /**
100
+ * Resize gating: the resize handles are hidden unless `model.resizeEnabled` is
101
+ * true (toggled from the toolbar). Moving / selecting stays available.
102
+ */
103
+ export const BpmnPoolInteraction = GfxViewInteractionExtension<BpmnPoolView>(
104
+ BpmnPoolView.type,
105
+ {
106
+ handleResize({ model }) {
107
+ return {
108
+ beforeResize({ set }) {
109
+ if (!model.resizeEnabled) {
110
+ set({ allowedHandlers: [] });
111
+ }
112
+ },
113
+ };
114
+ },
115
+ }
116
+ );
package/src/index.ts CHANGED
@@ -1 +1 @@
1
- export {};
1
+ export {};