@formicoidea/labre-framework-bpmn 0.23.0 → 0.23.1

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.
Files changed (41) hide show
  1. package/dist/consts.d.ts +42 -0
  2. package/{src/consts.ts → dist/consts.js} +11 -20
  3. package/dist/descriptor.d.ts +7 -0
  4. package/{src/descriptor.ts → dist/descriptor.js} +1 -1
  5. package/dist/effects.d.ts +10 -0
  6. package/dist/effects.js +7 -0
  7. package/dist/element-renderer.d.ts +13 -0
  8. package/dist/element-renderer.js +58 -0
  9. package/dist/element-view.d.ts +22 -0
  10. package/dist/element-view.js +103 -0
  11. package/dist/index.d.ts +2 -0
  12. package/dist/index.js +2 -0
  13. package/dist/node/node-renderer.d.ts +16 -0
  14. package/dist/node/node-renderer.js +43 -0
  15. package/dist/node/node-view.d.ts +17 -0
  16. package/dist/node/node-view.js +28 -0
  17. package/dist/templates/index.d.ts +3 -0
  18. package/dist/templates/index.js +114 -0
  19. package/dist/toolbar/bpmn-menu.d.ts +27 -0
  20. package/dist/toolbar/bpmn-menu.js +179 -0
  21. package/dist/toolbar/bpmn-senior-button.d.ts +16 -0
  22. package/{src/toolbar/bpmn-senior-button.ts → dist/toolbar/bpmn-senior-button.js} +33 -38
  23. package/dist/toolbar/config.d.ts +14 -0
  24. package/dist/toolbar/config.js +55 -0
  25. package/dist/toolbar/icons.d.ts +16 -0
  26. package/{src/toolbar/icons.ts → dist/toolbar/icons.js} +8 -14
  27. package/dist/toolbar/senior-tool.d.ts +2 -0
  28. package/{src/toolbar/senior-tool.ts → dist/toolbar/senior-tool.js} +5 -5
  29. package/dist/view.d.ts +7 -0
  30. package/dist/view.js +34 -0
  31. package/package.json +15 -6
  32. package/src/effects.ts +0 -14
  33. package/src/element-renderer.ts +0 -93
  34. package/src/element-view.ts +0 -116
  35. package/src/index.ts +0 -1
  36. package/src/node/node-renderer.ts +0 -65
  37. package/src/node/node-view.ts +0 -34
  38. package/src/templates/index.ts +0 -156
  39. package/src/toolbar/bpmn-menu.ts +0 -224
  40. package/src/toolbar/config.ts +0 -74
  41. package/src/view.ts +0 -37
@@ -1,116 +0,0 @@
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 DELETED
@@ -1 +0,0 @@
1
- export {};
@@ -1,65 +0,0 @@
1
- import {
2
- type ElementRenderer,
3
- ElementRendererExtension,
4
- } from '@formicoidea/labre-core/blocks/surface';
5
- import { shape as shapeRenderer } from '@formicoidea/labre-core/gfx/shape';
6
- import { type BpmnNodeElementModel, DefaultTheme } from '@formicoidea/labre-core/model';
7
-
8
- /**
9
- * Renderer for a BPMN flow-object node. The shape body (ellipse / rounded rect
10
- * / diamond) is drawn by REUSING the native shape renderer — so stroke width,
11
- * colors, inner text and theme behave exactly like a native shape. Only
12
- * `gatewayExclusive` is decorated: an X drawn on top in the node's (editable)
13
- * stroke color. Events and task are plain native shapes.
14
- *
15
- * Mirrors the EDGY node renderer.
16
- */
17
- export const bpmnNode: ElementRenderer<BpmnNodeElementModel> = (
18
- model,
19
- ctx,
20
- matrix,
21
- renderer,
22
- rc,
23
- bound
24
- ) => {
25
- const [, , w, h] = model.deserializedXYWH;
26
- const cx = w / 2;
27
- const cy = h / 2;
28
-
29
- // Capture the element-local transform BEFORE the shape renderer mutates the
30
- // matrix, so the glyph can be drawn in the same space afterwards.
31
- const glyphMatrix = DOMMatrix.fromMatrix(matrix)
32
- .translateSelf(cx, cy)
33
- .rotateSelf(model.rotate)
34
- .translateSelf(-cx, -cy);
35
-
36
- // Native shape (fill / stroke / inner text / theme handled natively).
37
- shapeRenderer(model, ctx, matrix, renderer, rc, bound);
38
-
39
- if (model.kind !== 'gatewayExclusive') return;
40
-
41
- const color = renderer.getColorValue(
42
- model.strokeColor,
43
- DefaultTheme.shapeStrokeColor,
44
- true
45
- );
46
-
47
- // ── Exclusive-gateway X, centred and sized to the diamond ───────────
48
- const r = Math.min(w, h) * 0.2;
49
- ctx.setTransform(glyphMatrix);
50
- ctx.translate(cx, cy);
51
- ctx.strokeStyle = color;
52
- ctx.lineWidth = Math.max(2, Math.min(w, h) * 0.06);
53
- ctx.lineCap = 'round';
54
- ctx.beginPath();
55
- ctx.moveTo(-r, -r);
56
- ctx.lineTo(r, r);
57
- ctx.moveTo(-r, r);
58
- ctx.lineTo(r, -r);
59
- ctx.stroke();
60
- };
61
-
62
- export const BpmnNodeRendererExtension = ElementRendererExtension(
63
- 'bpmnNode',
64
- bpmnNode
65
- );
@@ -1,34 +0,0 @@
1
- import { mountShapeTextEditor } from '@formicoidea/labre-core/gfx/shape';
2
- import {
3
- type BpmnNodeElementModel,
4
- ShapeElementModel,
5
- } from '@formicoidea/labre-core/model';
6
- import { GfxElementModelView } from '@formicoidea/labre-core/std/gfx';
7
-
8
- /**
9
- * View for a BPMN flow-object node. Registering it ensures `gfx.view.get(model)`
10
- * returns a view (required so move / select / connector interactions work).
11
- *
12
- * BPMN nodes are native shapes, so we reuse the shape inner-text editor: a
13
- * double-click mounts the editable text overlay (`mountShapeTextEditor`),
14
- * exactly like a native shape (used to label the task).
15
- *
16
- * Mirrors {@link EdgyNodeView}.
17
- */
18
- export class BpmnNodeView extends GfxElementModelView<BpmnNodeElementModel> {
19
- static override type: string = 'bpmnNode';
20
-
21
- override onCreated(): void {
22
- super.onCreated();
23
- this.on('dblclick', () => {
24
- const edgeless = this.std.view.getBlock(this.std.store.root!.id);
25
- if (
26
- edgeless &&
27
- !this.model.isLocked() &&
28
- this.model instanceof ShapeElementModel
29
- ) {
30
- mountShapeTextEditor(this.model, edgeless);
31
- }
32
- });
33
- }
34
- }
@@ -1,156 +0,0 @@
1
- import {
2
- makeTemplateSnapshot,
3
- type SurfaceElementsJSON,
4
- surfaceText,
5
- type Template,
6
- type TemplateCategory,
7
- } from '@formicoidea/labre-core/gfx/template';
8
- import {
9
- ConnectorMode,
10
- FontFamily,
11
- PointStyle,
12
- ShapeStyle,
13
- StrokeStyle,
14
- } from '@formicoidea/labre-core/model';
15
-
16
- import {
17
- END_WIDTH,
18
- EVENT_END,
19
- EVENT_START,
20
- INNER_FONT_SIZE,
21
- NEUTRAL_STROKE,
22
- NODE_FILL,
23
- NODE_SIZE,
24
- NODE_STROKE_WIDTH,
25
- SEQUENCE_STROKE,
26
- SEQUENCE_WIDTH,
27
- START_WIDTH,
28
- TASK_RADIUS,
29
- } from '../consts';
30
-
31
- type NodeKind = 'startEvent' | 'endEvent' | 'task' | 'gatewayExclusive';
32
-
33
- /** One BPMN flow-object node, as a surface-element JSON entry. */
34
- function node(kind: NodeKind, x: number, y: number, text?: string) {
35
- const { w, h } = NODE_SIZE[kind];
36
- const base: Record<string, unknown> = {
37
- type: 'bpmnNode',
38
- kind,
39
- filled: true,
40
- fillColor: NODE_FILL,
41
- shapeStyle: ShapeStyle.General,
42
- roughness: 0,
43
- xywh: `[${x},${y},${w},${h}]`,
44
- };
45
- if (kind === 'startEvent')
46
- return { ...base, shapeType: 'ellipse', strokeColor: EVENT_START, strokeWidth: START_WIDTH };
47
- if (kind === 'endEvent')
48
- return { ...base, shapeType: 'ellipse', strokeColor: EVENT_END, strokeWidth: END_WIDTH };
49
- if (kind === 'gatewayExclusive')
50
- return { ...base, shapeType: 'diamond', strokeColor: NEUTRAL_STROKE, strokeWidth: NODE_STROKE_WIDTH };
51
- return {
52
- ...base,
53
- shapeType: 'rect',
54
- radius: TASK_RADIUS,
55
- strokeColor: NEUTRAL_STROKE,
56
- strokeWidth: NODE_STROKE_WIDTH,
57
- text: surfaceText(text ?? 'Task'),
58
- color: NEUTRAL_STROKE,
59
- fontFamily: FontFamily.Inter,
60
- fontSize: INNER_FONT_SIZE,
61
- textAlign: 'center',
62
- };
63
- }
64
-
65
- function pool(x: number, y: number, w: number, h: number, name = 'Pool') {
66
- return { type: 'bpmnPool', name, xywh: `[${x},${y},${w},${h}]` };
67
- }
68
-
69
- /** A sequence-flow connector; ids are remapped on insert. */
70
- function seq(source: string, target: string) {
71
- return {
72
- type: 'connector',
73
- mode: ConnectorMode.Orthogonal,
74
- stroke: SEQUENCE_STROKE,
75
- strokeWidth: SEQUENCE_WIDTH,
76
- strokeStyle: StrokeStyle.Solid,
77
- frontEndpointStyle: PointStyle.None,
78
- rearEndpointStyle: PointStyle.Triangle,
79
- source: { id: source, position: [0.5, 0.5] },
80
- target: { id: target, position: [0.5, 0.5] },
81
- };
82
- }
83
-
84
- /** A standalone (free) sequence-flow arrow for the prefab card. */
85
- function freeSeq(): Record<string, unknown> {
86
- return {
87
- type: 'connector',
88
- mode: ConnectorMode.Orthogonal,
89
- stroke: SEQUENCE_STROKE,
90
- strokeWidth: SEQUENCE_WIDTH,
91
- strokeStyle: StrokeStyle.Solid,
92
- frontEndpointStyle: PointStyle.None,
93
- rearEndpointStyle: PointStyle.Triangle,
94
- source: { position: [0, 0] },
95
- target: { position: [140, 0] },
96
- };
97
- }
98
-
99
- const single = (el: Record<string, unknown>): SurfaceElementsJSON => ({ a: el });
100
-
101
- const PREVIEW_ATTRS = 'width="100%" height="100%" viewBox="0 0 135 80" xmlns="http://www.w3.org/2000/svg"';
102
-
103
- const previews = {
104
- process: `<svg ${PREVIEW_ATTRS} fill="none"><circle cx="16" cy="40" r="8" stroke="#43a06b" stroke-width="2"/><rect x="34" y="31" width="26" height="18" rx="3" stroke="#262626" stroke-width="1.6"/><path d="M78 31 L88 40 L78 49 L68 40 Z" stroke="#262626" stroke-width="1.4"/><path d="M73 37 L83 43 M83 37 L73 43" stroke="#262626" stroke-width="1.2"/><circle cx="118" cy="40" r="8" stroke="#cf5648" stroke-width="3"/><path d="M24 40 H34 M60 40 H68 M88 40 H110" stroke="#262626" stroke-width="1.2"/></svg>`,
105
- startEvent: `<svg ${PREVIEW_ATTRS} fill="none"><circle cx="67" cy="40" r="20" stroke="#43a06b" stroke-width="3"/></svg>`,
106
- endEvent: `<svg ${PREVIEW_ATTRS} fill="none"><circle cx="67" cy="40" r="20" stroke="#cf5648" stroke-width="5"/></svg>`,
107
- task: `<svg ${PREVIEW_ATTRS} fill="none"><rect x="34" y="24" width="66" height="32" rx="6" stroke="#262626" stroke-width="2.4"/></svg>`,
108
- gateway: `<svg ${PREVIEW_ATTRS} fill="none"><path d="M67 16 L92 40 L67 64 L42 40 Z" stroke="#262626" stroke-width="2.4" stroke-linejoin="round"/><path d="M58 31 L76 49 M76 31 L58 49" stroke="#262626" stroke-width="2.2" stroke-linecap="round"/></svg>`,
109
- sequence: `<svg ${PREVIEW_ATTRS} fill="none"><path d="M24 40 H96" stroke="#262626" stroke-width="2.4" stroke-linecap="round"/><path d="M94 33 L108 40 L94 47 Z" fill="#262626"/></svg>`,
110
- pool: `<svg ${PREVIEW_ATTRS} fill="none"><rect x="14" y="20" width="107" height="40" rx="3" stroke="#262626" stroke-width="2"/><path d="M30 20 V60" stroke="#262626" stroke-width="1.8"/><rect x="14" y="20" width="16" height="40" fill="#f4f4f5"/><path d="M30 20 V60" stroke="#262626" stroke-width="1.8"/></svg>`,
111
- };
112
-
113
- /** The lean BPMN basics: a simple worked process + every prefab the menu makes. */
114
- function bpmnTemplates(): Template[] {
115
- const process: SurfaceElementsJSON = {
116
- pool: pool(0, 0, 640, 200, 'Process'),
117
- start: node('startEvent', 40, 72),
118
- task1: node('task', 116, 64, 'Submit request'),
119
- gw: node('gatewayExclusive', 272, 64),
120
- task2: node('task', 376, 20, 'Fulfil'),
121
- task3: node('task', 376, 124, 'Reject'),
122
- end: node('endEvent', 556, 72),
123
- c1: seq('start', 'task1'),
124
- c2: seq('task1', 'gw'),
125
- c3: seq('gw', 'task2'),
126
- c4: seq('gw', 'task3'),
127
- c5: seq('task2', 'end'),
128
- c6: seq('task3', 'end'),
129
- };
130
-
131
- const t = (
132
- name: string,
133
- preview: string,
134
- elements: SurfaceElementsJSON
135
- ): Template => ({
136
- name,
137
- type: 'template',
138
- preview,
139
- content: makeTemplateSnapshot(elements, name),
140
- });
141
-
142
- return [
143
- t('Simple process', previews.process, process),
144
- t('Start event', previews.startEvent, single(node('startEvent', 0, 0))),
145
- t('End event', previews.endEvent, single(node('endEvent', 0, 0))),
146
- t('Task', previews.task, single(node('task', 0, 0))),
147
- t('Exclusive gateway', previews.gateway, single(node('gatewayExclusive', 0, 0))),
148
- t('Sequence flow', previews.sequence, single(freeSeq())),
149
- t('Pool', previews.pool, single(pool(0, 0, 560, 200))),
150
- ];
151
- }
152
-
153
- export const bpmnTemplateCategory: TemplateCategory = {
154
- name: 'BPMN',
155
- templates: bpmnTemplates(),
156
- };
@@ -1,224 +0,0 @@
1
- import { DefaultTool } from '@formicoidea/labre-core/blocks/surface';
2
- import { ConnectorTool } from '@formicoidea/labre-core/gfx/connector';
3
- import { EmptyTool } from '@formicoidea/labre-core/gfx/pointer';
4
- import {
5
- ConnectorMode,
6
- FontFamily,
7
- PointStyle,
8
- ShapeStyle,
9
- StrokeStyle,
10
- } from '@formicoidea/labre-core/model';
11
- import {
12
- EditPropsStore,
13
- TelemetryProvider,
14
- } from '@formicoidea/labre-core/shared/services';
15
- import { EdgelessToolbarToolMixin } from '@formicoidea/labre-core/widgets/edgeless-toolbar';
16
- import { Bound } from '@formicoidea/labre-core/global/gfx';
17
- import { css, html, LitElement } from 'lit';
18
-
19
- import {
20
- EVENT_END,
21
- EVENT_START,
22
- END_WIDTH,
23
- INNER_FONT_SIZE,
24
- NEUTRAL_STROKE,
25
- NODE_FILL,
26
- NODE_LABEL,
27
- NODE_SIZE,
28
- NODE_STROKE_WIDTH,
29
- SEQUENCE_STROKE,
30
- SEQUENCE_WIDTH,
31
- START_WIDTH,
32
- TASK_RADIUS,
33
- } from '../consts';
34
- import {
35
- bpmnEndIcon,
36
- bpmnGatewayIcon,
37
- bpmnPoolIcon,
38
- bpmnSequenceIcon,
39
- bpmnStartIcon,
40
- bpmnTaskIcon,
41
- } from './icons';
42
-
43
- type NodeKind = 'startEvent' | 'endEvent' | 'task' | 'gatewayExclusive';
44
-
45
- /** Per-kind native shape + accent presets (style C). */
46
- const NODE_PRESETS: Record<
47
- NodeKind,
48
- { shapeType: 'ellipse' | 'rect' | 'diamond'; stroke: string; width: number }
49
- > = {
50
- startEvent: { shapeType: 'ellipse', stroke: EVENT_START, width: START_WIDTH },
51
- endEvent: { shapeType: 'ellipse', stroke: EVENT_END, width: END_WIDTH },
52
- task: { shapeType: 'rect', stroke: NEUTRAL_STROKE, width: NODE_STROKE_WIDTH },
53
- gatewayExclusive: {
54
- shapeType: 'diamond',
55
- stroke: NEUTRAL_STROKE,
56
- width: NODE_STROKE_WIDTH,
57
- },
58
- };
59
-
60
- /**
61
- * The popover above the toolbar for the BPMN toolbox. Items create the flow
62
- * objects (native shapes, so they stay editable), the pool background, and arm
63
- * the native connector tool for sequence flows. Mirrors the EDGY menu.
64
- */
65
- export class EdgelessBpmnMenu extends EdgelessToolbarToolMixin(LitElement) {
66
- static override styles = css`
67
- :host {
68
- position: absolute;
69
- display: flex;
70
- z-index: -1;
71
- }
72
- .menu-content {
73
- display: flex;
74
- align-items: center;
75
- justify-content: center;
76
- }
77
- .button-group-container {
78
- display: flex;
79
- align-items: center;
80
- gap: 14px;
81
- fill: var(--affine-icon-color);
82
- }
83
- .button-group-container svg {
84
- width: 24px;
85
- height: 24px;
86
- }
87
- `;
88
-
89
- override type = EmptyTool;
90
-
91
- /** Create a flow-object node (native shape) centred on the viewport. */
92
- private _createNode(kind: NodeKind) {
93
- const surface = this.gfx.surface;
94
- if (!surface) return;
95
-
96
- const { w, h } = NODE_SIZE[kind];
97
- const { centerX: cx, centerY: cy } = this.gfx.viewport;
98
- const preset = NODE_PRESETS[kind];
99
-
100
- const id = surface.addElement({
101
- type: 'bpmnNode',
102
- kind,
103
- shapeType: preset.shapeType,
104
- filled: true,
105
- fillColor: NODE_FILL,
106
- strokeColor: preset.stroke,
107
- strokeWidth: preset.width,
108
- shapeStyle: ShapeStyle.General,
109
- roughness: 0,
110
- radius: kind === 'task' ? TASK_RADIUS : 0,
111
- text: NODE_LABEL[kind] || undefined,
112
- color: NEUTRAL_STROKE,
113
- fontFamily: FontFamily.Inter,
114
- fontSize: INNER_FONT_SIZE,
115
- textAlign: 'center',
116
- xywh: new Bound(cx - w / 2, cy - h / 2, w, h).serialize(),
117
- });
118
- this._track('FrameworkElementAdded', `node:${kind}`);
119
- this._finish(id);
120
- }
121
-
122
- /** Create a pool (background container) centred on the viewport. */
123
- private _createPool() {
124
- const surface = this.gfx.surface;
125
- if (!surface) return;
126
-
127
- const w = 560;
128
- const h = 200;
129
- const { centerX: cx, centerY: cy } = this.gfx.viewport;
130
- const id = surface.addElement({
131
- type: 'bpmnPool',
132
- xywh: new Bound(cx - w / 2, cy - h / 2, w, h).serialize(),
133
- });
134
- this._track('FrameworkElementAdded', 'pool');
135
- this._finish(id);
136
- }
137
-
138
- /**
139
- * Arm the native connector tool, pre-styled for a BPMN sequence flow:
140
- * orthogonal, solid, with a filled triangle head. The user then draws from
141
- * one node to another (endpoints attach to centers).
142
- */
143
- private _activateSequenceFlow() {
144
- this.edgeless.std.get(EditPropsStore).recordLastProps('connector', {
145
- mode: ConnectorMode.Orthogonal,
146
- stroke: SEQUENCE_STROKE,
147
- strokeStyle: StrokeStyle.Solid,
148
- strokeWidth: SEQUENCE_WIDTH,
149
- frontEndpointStyle: PointStyle.None,
150
- rearEndpointStyle: PointStyle.Triangle,
151
- });
152
- this._track('FrameworkToolPicked', 'connector:sequence');
153
- this.gfx.tool.setTool(ConnectorTool, { mode: ConnectorMode.Orthogonal });
154
- // Keep the palette open (native sub-menu behaviour).
155
- }
156
-
157
- private _finish(id: string) {
158
- const { gfx } = this;
159
- gfx.doc.captureSync();
160
- gfx.tool.setTool(DefaultTool);
161
- gfx.selection.set({ elements: [id], editing: false });
162
- // Keep the palette open (native sub-menu behaviour).
163
- }
164
-
165
- private _track(
166
- event: 'FrameworkElementAdded' | 'FrameworkToolPicked',
167
- element: string
168
- ) {
169
- this.edgeless.std.getOptional(TelemetryProvider)?.track(event, {
170
- framework: 'bpmn',
171
- element,
172
- page: 'whiteboard editor',
173
- segment: 'bpmn toolbox',
174
- module: 'bpmn menu',
175
- });
176
- }
177
-
178
- override render() {
179
- return html`
180
- <edgeless-slide-menu>
181
- <div class="menu-content">
182
- <div class="button-group-container">
183
- <edgeless-tool-icon-button
184
- .tooltip=${'Start event'}
185
- @click=${() => this._createNode('startEvent')}
186
- >
187
- ${bpmnStartIcon}
188
- </edgeless-tool-icon-button>
189
- <edgeless-tool-icon-button
190
- .tooltip=${'End event'}
191
- @click=${() => this._createNode('endEvent')}
192
- >
193
- ${bpmnEndIcon}
194
- </edgeless-tool-icon-button>
195
- <edgeless-tool-icon-button
196
- .tooltip=${'Task'}
197
- @click=${() => this._createNode('task')}
198
- >
199
- ${bpmnTaskIcon}
200
- </edgeless-tool-icon-button>
201
- <edgeless-tool-icon-button
202
- .tooltip=${'Exclusive gateway'}
203
- @click=${() => this._createNode('gatewayExclusive')}
204
- >
205
- ${bpmnGatewayIcon}
206
- </edgeless-tool-icon-button>
207
- <edgeless-tool-icon-button
208
- .tooltip=${'Sequence flow'}
209
- @click=${this._activateSequenceFlow}
210
- >
211
- ${bpmnSequenceIcon}
212
- </edgeless-tool-icon-button>
213
- <edgeless-tool-icon-button
214
- .tooltip=${'Pool'}
215
- @click=${this._createPool}
216
- >
217
- ${bpmnPoolIcon}
218
- </edgeless-tool-icon-button>
219
- </div>
220
- </div>
221
- </edgeless-slide-menu>
222
- `;
223
- }
224
- }
@@ -1,74 +0,0 @@
1
- import { EdgelessCRUDIdentifier } from '@formicoidea/labre-core/blocks/surface';
2
- import { BpmnPoolElementModel } from '@formicoidea/labre-core/model';
3
- import {
4
- type ToolbarContext,
5
- type ToolbarModuleConfig,
6
- ToolbarModuleExtension,
7
- } from '@formicoidea/labre-core/shared/services';
8
- import { BlockFlavourIdentifier } from '@formicoidea/labre-core/std';
9
- import { html, type TemplateResult } from 'lit';
10
-
11
- const ResizeIcon = html`<svg
12
- width="24"
13
- height="24"
14
- viewBox="0 0 24 24"
15
- fill="none"
16
- stroke="currentColor"
17
- stroke-width="1.6"
18
- stroke-linecap="round"
19
- stroke-linejoin="round"
20
- >
21
- <path d="M9 5H5v4M15 19h4v-4" />
22
- <path d="M5 5l6 6M19 19l-6-6" />
23
- </svg>`;
24
-
25
- type BpmnPoolToggleProp = 'resizeEnabled';
26
-
27
- /**
28
- * Build a toolbar toggle that flips a boolean flag on every selected pool:
29
- * `active` reflects the current state, `run` flips it (with an undo checkpoint).
30
- */
31
- function booleanToggle(
32
- id: string,
33
- tooltip: string,
34
- icon: TemplateResult,
35
- prop: BpmnPoolToggleProp
36
- ) {
37
- return {
38
- id,
39
- tooltip,
40
- icon,
41
- active(ctx: ToolbarContext) {
42
- const models = ctx.getSurfaceModelsByType(BpmnPoolElementModel);
43
- return models.length > 0 && models.every(model => model[prop]);
44
- },
45
- run(ctx: ToolbarContext) {
46
- const models = ctx.getSurfaceModelsByType(BpmnPoolElementModel);
47
- if (!models.length) return;
48
-
49
- const enable = !models.every(model => model[prop]);
50
- ctx.std.store.captureSync();
51
- const crud = ctx.std.get(EdgelessCRUDIdentifier);
52
- for (const model of models) {
53
- crud.updateElement(model.id, { [prop]: enable });
54
- }
55
- },
56
- };
57
- }
58
-
59
- export const bpmnPoolToolbarConfig = {
60
- actions: [
61
- booleanToggle(
62
- 'a.toggle-resize',
63
- 'Enable / lock resizing',
64
- ResizeIcon,
65
- 'resizeEnabled'
66
- ),
67
- ],
68
- when: ctx => ctx.getSurfaceModelsByType(BpmnPoolElementModel).length > 0,
69
- } as const satisfies ToolbarModuleConfig;
70
-
71
- export const bpmnPoolToolbarExtension = ToolbarModuleExtension({
72
- id: BlockFlavourIdentifier('affine:surface:bpmnPool'),
73
- config: bpmnPoolToolbarConfig,
74
- });