@miragon/event-storming-transforms 0.1.0
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/README.md +36 -0
- package/dist/index.cjs +139 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +47 -0
- package/dist/index.d.ts +47 -0
- package/dist/index.js +126 -0
- package/dist/index.js.map +1 -0
- package/package.json +43 -0
package/README.md
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# @miragon/event-storming-transforms
|
|
2
|
+
|
|
3
|
+
[](https://www.npmjs.com/package/@miragon/event-storming-transforms)
|
|
4
|
+
[](https://github.com/Miragon/event-storming-modeler/blob/main/LICENSE)
|
|
5
|
+
|
|
6
|
+
DOM-free, pure `EventStormingBoard → EventStormingBoard` transforms — move, kind, color, arrange.
|
|
7
|
+
No undo stack: every transform returns a new board and leaves the input untouched.
|
|
8
|
+
|
|
9
|
+
## Install
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
npm install @miragon/event-storming-transforms
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
## Usage
|
|
16
|
+
|
|
17
|
+
```ts
|
|
18
|
+
import { parseDSL } from '@miragon/event-storming-dsl';
|
|
19
|
+
import { moveElement, setStickyKind } from '@miragon/event-storming-transforms';
|
|
20
|
+
|
|
21
|
+
const board = parseDSL('event Order Placed [620, 300]');
|
|
22
|
+
const [orderPlaced] = board.elements;
|
|
23
|
+
|
|
24
|
+
const moved = moveElement(board, orderPlaced.id, { x: 800, y: 300 }); // returns a new board
|
|
25
|
+
const retyped = setStickyKind(moved, orderPlaced.id, 'policy');
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
Also included: `moveBy` (delta translation), `setColor`/`clearColor` (sticky color override),
|
|
29
|
+
`alignToRows` (snap stickies into per-kind swimlanes) and `spreadTimeline` (spread stickies
|
|
30
|
+
evenly along the timeline).
|
|
31
|
+
|
|
32
|
+
Part of the [Event Storming Modeler](https://github.com/Miragon/event-storming-modeler) monorepo.
|
|
33
|
+
|
|
34
|
+
## License
|
|
35
|
+
|
|
36
|
+
MIT
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var eventStormingSchemaModel = require('@miragon/event-storming-schema-model');
|
|
4
|
+
|
|
5
|
+
// src/util.ts
|
|
6
|
+
function updateElement(board, id, updater) {
|
|
7
|
+
let found = false;
|
|
8
|
+
const elements = board.elements.map((el) => {
|
|
9
|
+
if (el.id !== id) return el;
|
|
10
|
+
found = true;
|
|
11
|
+
return updater(el);
|
|
12
|
+
});
|
|
13
|
+
if (!found) throw new Error(`Element "${id}" not found.`);
|
|
14
|
+
return { ...board, elements };
|
|
15
|
+
}
|
|
16
|
+
function findElement(board, id) {
|
|
17
|
+
return board.elements.find((el) => el.id === id);
|
|
18
|
+
}
|
|
19
|
+
function compact(obj) {
|
|
20
|
+
const out = {};
|
|
21
|
+
for (const [k, v] of Object.entries(obj)) {
|
|
22
|
+
if (v !== void 0) out[k] = v;
|
|
23
|
+
}
|
|
24
|
+
return out;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// src/move.ts
|
|
28
|
+
function moveElement(board, elementId, position) {
|
|
29
|
+
const current = findElement(board, elementId);
|
|
30
|
+
if (!current) throw new Error(`Element "${elementId}" not found.`);
|
|
31
|
+
return moveBy(board, elementId, {
|
|
32
|
+
dx: position.x - current.position.x,
|
|
33
|
+
dy: position.y - current.position.y
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
function moveBy(board, elementId, { dx, dy }) {
|
|
37
|
+
return updateElement(board, elementId, (el) => {
|
|
38
|
+
const position = { x: el.position.x + dx, y: el.position.y + dy };
|
|
39
|
+
if (el.elementType === "drawing") {
|
|
40
|
+
return {
|
|
41
|
+
...el,
|
|
42
|
+
position,
|
|
43
|
+
points: el.points.map((point) => ({ x: point.x + dx, y: point.y + dy }))
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
return { ...el, position };
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// src/kind.ts
|
|
51
|
+
var STICKY_ELEMENT_TYPES = [
|
|
52
|
+
"event",
|
|
53
|
+
"command",
|
|
54
|
+
"actor",
|
|
55
|
+
"aggregate",
|
|
56
|
+
"policy",
|
|
57
|
+
"readmodel",
|
|
58
|
+
"external",
|
|
59
|
+
"hotspot"
|
|
60
|
+
];
|
|
61
|
+
function isStickyElementType(elementType) {
|
|
62
|
+
return STICKY_ELEMENT_TYPES.includes(elementType);
|
|
63
|
+
}
|
|
64
|
+
function setStickyKind(board, elementId, elementType) {
|
|
65
|
+
if (!isStickyElementType(elementType)) {
|
|
66
|
+
throw new Error(
|
|
67
|
+
`setStickyKind cannot retype to "${elementType}"; not a sticky kind.`
|
|
68
|
+
);
|
|
69
|
+
}
|
|
70
|
+
return updateElement(board, elementId, (el) => {
|
|
71
|
+
if (!isStickyElementType(el.elementType)) {
|
|
72
|
+
throw new Error(
|
|
73
|
+
`setStickyKind only applies to stickies; "${elementId}" is ${el.elementType}.`
|
|
74
|
+
);
|
|
75
|
+
}
|
|
76
|
+
return compact({
|
|
77
|
+
id: el.id,
|
|
78
|
+
elementType,
|
|
79
|
+
label: el.label,
|
|
80
|
+
position: el.position,
|
|
81
|
+
color: el.color
|
|
82
|
+
});
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// src/color.ts
|
|
87
|
+
function setColor(board, elementId, color) {
|
|
88
|
+
return updateElement(board, elementId, (el) => ({ ...el, color }));
|
|
89
|
+
}
|
|
90
|
+
function clearColor(board, elementId) {
|
|
91
|
+
return updateElement(
|
|
92
|
+
board,
|
|
93
|
+
elementId,
|
|
94
|
+
(el) => compact({ ...el, color: void 0 })
|
|
95
|
+
);
|
|
96
|
+
}
|
|
97
|
+
var LANE_Y = {
|
|
98
|
+
readmodel: 120,
|
|
99
|
+
actor: 220,
|
|
100
|
+
command: 320,
|
|
101
|
+
aggregate: 320,
|
|
102
|
+
event: 420,
|
|
103
|
+
policy: 520,
|
|
104
|
+
external: 620,
|
|
105
|
+
hotspot: 720
|
|
106
|
+
};
|
|
107
|
+
function alignToRows(board) {
|
|
108
|
+
const elements = board.elements.map(
|
|
109
|
+
(el) => isStickyElementType(el.elementType) ? { ...el, position: { x: el.position.x, y: LANE_Y[el.elementType] } } : el
|
|
110
|
+
);
|
|
111
|
+
return { ...board, elements };
|
|
112
|
+
}
|
|
113
|
+
function spreadTimeline(board, { gap = 180 } = {}) {
|
|
114
|
+
const stickies = eventStormingSchemaModel.sortByTimeline(board).filter((el) => isStickyElementType(el.elementType));
|
|
115
|
+
if (stickies.length === 0) return board;
|
|
116
|
+
const startX = Math.min(...stickies.map((el) => el.position.x));
|
|
117
|
+
const targetX = /* @__PURE__ */ new Map();
|
|
118
|
+
stickies.forEach((el, index) => targetX.set(el.id, startX + index * gap));
|
|
119
|
+
const elements = board.elements.map((el) => {
|
|
120
|
+
const x = targetX.get(el.id);
|
|
121
|
+
return x === void 0 ? el : { ...el, position: { x, y: el.position.y } };
|
|
122
|
+
});
|
|
123
|
+
return { ...board, elements };
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
exports.STICKY_ELEMENT_TYPES = STICKY_ELEMENT_TYPES;
|
|
127
|
+
exports.alignToRows = alignToRows;
|
|
128
|
+
exports.clearColor = clearColor;
|
|
129
|
+
exports.compact = compact;
|
|
130
|
+
exports.findElement = findElement;
|
|
131
|
+
exports.isStickyElementType = isStickyElementType;
|
|
132
|
+
exports.moveBy = moveBy;
|
|
133
|
+
exports.moveElement = moveElement;
|
|
134
|
+
exports.setColor = setColor;
|
|
135
|
+
exports.setStickyKind = setStickyKind;
|
|
136
|
+
exports.spreadTimeline = spreadTimeline;
|
|
137
|
+
exports.updateElement = updateElement;
|
|
138
|
+
//# sourceMappingURL=index.cjs.map
|
|
139
|
+
//# sourceMappingURL=index.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/util.ts","../src/move.ts","../src/kind.ts","../src/color.ts","../src/arrange.ts"],"names":["sortByTimeline"],"mappings":"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","file":"index.cjs","sourcesContent":["import type { BoardElement, EventStormingBoard } from '@miragon/event-storming-schema-model';\n\nexport function updateElement(\n board: EventStormingBoard,\n id: string,\n updater: (el: BoardElement) => BoardElement,\n): EventStormingBoard {\n let found = false;\n const elements = board.elements.map((el) => {\n if (el.id !== id) return el;\n found = true;\n return updater(el);\n });\n if (!found) throw new Error(`Element \"${id}\" not found.`);\n return { ...board, elements };\n}\n\nexport function findElement(board: EventStormingBoard, id: string): BoardElement | undefined {\n return board.elements.find((el) => el.id === id);\n}\n\n/** Removes `undefined` values so exactOptionalPropertyTypes is not violated. */\nexport function compact<T extends Record<string, unknown>>(obj: T): T {\n const out: Record<string, unknown> = {};\n for (const [k, v] of Object.entries(obj)) {\n if (v !== undefined) out[k] = v;\n }\n return out as T;\n}\n","import type { Coordinate, EventStormingBoard } from '@miragon/event-storming-schema-model';\nimport { updateElement, findElement } from './util.js';\n\nexport interface MoveDelta {\n readonly dx: number;\n readonly dy: number;\n}\n\n/** Pure function — no undo stack: returns a new board with the element centered at `position`. */\nexport function moveElement(\n board: EventStormingBoard,\n elementId: string,\n position: Coordinate,\n): EventStormingBoard {\n const current = findElement(board, elementId);\n if (!current) throw new Error(`Element \"${elementId}\" not found.`);\n return moveBy(board, elementId, {\n dx: position.x - current.position.x,\n dy: position.y - current.position.y,\n });\n}\n\n/** Translates an element by a pixel delta. The canvas is free — any finite target is valid. */\nexport function moveBy(\n board: EventStormingBoard,\n elementId: string,\n { dx, dy }: MoveDelta,\n): EventStormingBoard {\n return updateElement(board, elementId, (el) => {\n const position = { x: el.position.x + dx, y: el.position.y + dy };\n if (el.elementType === 'drawing') {\n // Drawing points are absolute board pixels and `position` mirrors the first point,\n // so the polyline must translate together with the position.\n return {\n ...el,\n position,\n points: el.points.map((point) => ({ x: point.x + dx, y: point.y + dy })),\n };\n }\n return { ...el, position };\n });\n}\n","import type {\n BoardElement,\n ElementType,\n EventStormingBoard,\n} from '@miragon/event-storming-schema-model';\nimport { updateElement, compact } from './util.js';\n\n/** The eight retypeable sticky kinds — every element kind except notes and drawings. */\nexport const STICKY_ELEMENT_TYPES = [\n 'event',\n 'command',\n 'actor',\n 'aggregate',\n 'policy',\n 'readmodel',\n 'external',\n 'hotspot',\n] as const;\n\nexport type StickyElementType = (typeof STICKY_ELEMENT_TYPES)[number];\n\nexport function isStickyElementType(elementType: ElementType): elementType is StickyElementType {\n return (STICKY_ELEMENT_TYPES as readonly ElementType[]).includes(elementType);\n}\n\n/**\n * Retypes a sticky (e.g. command → event), preserving id, label, position and color.\n * Notes and drawings are not stickies and cannot take part in retyping.\n */\nexport function setStickyKind(\n board: EventStormingBoard,\n elementId: string,\n elementType: StickyElementType,\n): EventStormingBoard {\n if (!isStickyElementType(elementType)) {\n throw new Error(\n `setStickyKind cannot retype to \"${elementType as string}\"; not a sticky kind.`,\n );\n }\n return updateElement(board, elementId, (el) => {\n if (!isStickyElementType(el.elementType)) {\n throw new Error(\n `setStickyKind only applies to stickies; \"${elementId}\" is ${el.elementType}.`,\n );\n }\n // Rebuild from the shared base fields so nothing kind-specific ever leaks across a retype.\n return compact({\n id: el.id,\n elementType,\n label: el.label,\n position: el.position,\n color: el.color,\n }) as BoardElement;\n });\n}\n","import type { BoardElement, EventStormingBoard } from '@miragon/event-storming-schema-model';\nimport { updateElement, compact } from './util.js';\n\n/** Sets the element's color override (CSS color, typically a hex sticky fill). */\nexport function setColor(\n board: EventStormingBoard,\n elementId: string,\n color: string,\n): EventStormingBoard {\n return updateElement(board, elementId, (el) => ({ ...el, color }));\n}\n\n/** Removes the color override so the element falls back to its per-kind default fill. */\nexport function clearColor(board: EventStormingBoard, elementId: string): EventStormingBoard {\n // compact drops the undefined value, so the `color` key is deleted rather than kept as undefined.\n return updateElement(\n board,\n elementId,\n (el) => compact({ ...el, color: undefined }) as unknown as BoardElement,\n );\n}\n","import { sortByTimeline, type EventStormingBoard } from '@miragon/event-storming-schema-model';\nimport { isStickyElementType, type StickyElementType } from './kind.js';\n\n/**\n * Per-kind lane centers (board pixels, top → bottom) for the classic picture-that-explains-\n * everything row layout. Command and aggregate share the middle lane.\n */\nconst LANE_Y: Readonly<Record<StickyElementType, number>> = {\n readmodel: 120,\n actor: 220,\n command: 320,\n aggregate: 320,\n event: 420,\n policy: 520,\n external: 620,\n hotspot: 720,\n};\n\n/**\n * Snaps every sticky's y to its per-kind lane, preserving x.\n * Notes and drawings are free annotations and stay untouched.\n */\nexport function alignToRows(board: EventStormingBoard): EventStormingBoard {\n const elements = board.elements.map((el) =>\n isStickyElementType(el.elementType)\n ? { ...el, position: { x: el.position.x, y: LANE_Y[el.elementType] } }\n : el,\n );\n return { ...board, elements };\n}\n\nexport interface SpreadTimelineOptions {\n /** Horizontal distance between neighboring stickies in board pixels. */\n readonly gap?: number;\n}\n\n/**\n * Redistributes the stickies' x evenly in timeline order (see `sortByTimeline`), starting at\n * the current leftmost sticky, preserving each y. Notes and drawings stay untouched.\n */\nexport function spreadTimeline(\n board: EventStormingBoard,\n { gap = 180 }: SpreadTimelineOptions = {},\n): EventStormingBoard {\n const stickies = sortByTimeline(board).filter((el) => isStickyElementType(el.elementType));\n if (stickies.length === 0) return board;\n const startX = Math.min(...stickies.map((el) => el.position.x));\n const targetX = new Map<string, number>();\n stickies.forEach((el, index) => targetX.set(el.id, startX + index * gap));\n const elements = board.elements.map((el) => {\n const x = targetX.get(el.id);\n return x === undefined ? el : { ...el, position: { x, y: el.position.y } };\n });\n return { ...board, elements };\n}\n"]}
|
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { EventStormingBoard, BoardElement, Coordinate, ElementType } from '@miragon/event-storming-schema-model';
|
|
2
|
+
|
|
3
|
+
declare function updateElement(board: EventStormingBoard, id: string, updater: (el: BoardElement) => BoardElement): EventStormingBoard;
|
|
4
|
+
declare function findElement(board: EventStormingBoard, id: string): BoardElement | undefined;
|
|
5
|
+
/** Removes `undefined` values so exactOptionalPropertyTypes is not violated. */
|
|
6
|
+
declare function compact<T extends Record<string, unknown>>(obj: T): T;
|
|
7
|
+
|
|
8
|
+
interface MoveDelta {
|
|
9
|
+
readonly dx: number;
|
|
10
|
+
readonly dy: number;
|
|
11
|
+
}
|
|
12
|
+
/** Pure function — no undo stack: returns a new board with the element centered at `position`. */
|
|
13
|
+
declare function moveElement(board: EventStormingBoard, elementId: string, position: Coordinate): EventStormingBoard;
|
|
14
|
+
/** Translates an element by a pixel delta. The canvas is free — any finite target is valid. */
|
|
15
|
+
declare function moveBy(board: EventStormingBoard, elementId: string, { dx, dy }: MoveDelta): EventStormingBoard;
|
|
16
|
+
|
|
17
|
+
/** The eight retypeable sticky kinds — every element kind except notes and drawings. */
|
|
18
|
+
declare const STICKY_ELEMENT_TYPES: readonly ["event", "command", "actor", "aggregate", "policy", "readmodel", "external", "hotspot"];
|
|
19
|
+
type StickyElementType = (typeof STICKY_ELEMENT_TYPES)[number];
|
|
20
|
+
declare function isStickyElementType(elementType: ElementType): elementType is StickyElementType;
|
|
21
|
+
/**
|
|
22
|
+
* Retypes a sticky (e.g. command → event), preserving id, label, position and color.
|
|
23
|
+
* Notes and drawings are not stickies and cannot take part in retyping.
|
|
24
|
+
*/
|
|
25
|
+
declare function setStickyKind(board: EventStormingBoard, elementId: string, elementType: StickyElementType): EventStormingBoard;
|
|
26
|
+
|
|
27
|
+
/** Sets the element's color override (CSS color, typically a hex sticky fill). */
|
|
28
|
+
declare function setColor(board: EventStormingBoard, elementId: string, color: string): EventStormingBoard;
|
|
29
|
+
/** Removes the color override so the element falls back to its per-kind default fill. */
|
|
30
|
+
declare function clearColor(board: EventStormingBoard, elementId: string): EventStormingBoard;
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Snaps every sticky's y to its per-kind lane, preserving x.
|
|
34
|
+
* Notes and drawings are free annotations and stay untouched.
|
|
35
|
+
*/
|
|
36
|
+
declare function alignToRows(board: EventStormingBoard): EventStormingBoard;
|
|
37
|
+
interface SpreadTimelineOptions {
|
|
38
|
+
/** Horizontal distance between neighboring stickies in board pixels. */
|
|
39
|
+
readonly gap?: number;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Redistributes the stickies' x evenly in timeline order (see `sortByTimeline`), starting at
|
|
43
|
+
* the current leftmost sticky, preserving each y. Notes and drawings stay untouched.
|
|
44
|
+
*/
|
|
45
|
+
declare function spreadTimeline(board: EventStormingBoard, { gap }?: SpreadTimelineOptions): EventStormingBoard;
|
|
46
|
+
|
|
47
|
+
export { type MoveDelta, STICKY_ELEMENT_TYPES, type SpreadTimelineOptions, type StickyElementType, alignToRows, clearColor, compact, findElement, isStickyElementType, moveBy, moveElement, setColor, setStickyKind, spreadTimeline, updateElement };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { EventStormingBoard, BoardElement, Coordinate, ElementType } from '@miragon/event-storming-schema-model';
|
|
2
|
+
|
|
3
|
+
declare function updateElement(board: EventStormingBoard, id: string, updater: (el: BoardElement) => BoardElement): EventStormingBoard;
|
|
4
|
+
declare function findElement(board: EventStormingBoard, id: string): BoardElement | undefined;
|
|
5
|
+
/** Removes `undefined` values so exactOptionalPropertyTypes is not violated. */
|
|
6
|
+
declare function compact<T extends Record<string, unknown>>(obj: T): T;
|
|
7
|
+
|
|
8
|
+
interface MoveDelta {
|
|
9
|
+
readonly dx: number;
|
|
10
|
+
readonly dy: number;
|
|
11
|
+
}
|
|
12
|
+
/** Pure function — no undo stack: returns a new board with the element centered at `position`. */
|
|
13
|
+
declare function moveElement(board: EventStormingBoard, elementId: string, position: Coordinate): EventStormingBoard;
|
|
14
|
+
/** Translates an element by a pixel delta. The canvas is free — any finite target is valid. */
|
|
15
|
+
declare function moveBy(board: EventStormingBoard, elementId: string, { dx, dy }: MoveDelta): EventStormingBoard;
|
|
16
|
+
|
|
17
|
+
/** The eight retypeable sticky kinds — every element kind except notes and drawings. */
|
|
18
|
+
declare const STICKY_ELEMENT_TYPES: readonly ["event", "command", "actor", "aggregate", "policy", "readmodel", "external", "hotspot"];
|
|
19
|
+
type StickyElementType = (typeof STICKY_ELEMENT_TYPES)[number];
|
|
20
|
+
declare function isStickyElementType(elementType: ElementType): elementType is StickyElementType;
|
|
21
|
+
/**
|
|
22
|
+
* Retypes a sticky (e.g. command → event), preserving id, label, position and color.
|
|
23
|
+
* Notes and drawings are not stickies and cannot take part in retyping.
|
|
24
|
+
*/
|
|
25
|
+
declare function setStickyKind(board: EventStormingBoard, elementId: string, elementType: StickyElementType): EventStormingBoard;
|
|
26
|
+
|
|
27
|
+
/** Sets the element's color override (CSS color, typically a hex sticky fill). */
|
|
28
|
+
declare function setColor(board: EventStormingBoard, elementId: string, color: string): EventStormingBoard;
|
|
29
|
+
/** Removes the color override so the element falls back to its per-kind default fill. */
|
|
30
|
+
declare function clearColor(board: EventStormingBoard, elementId: string): EventStormingBoard;
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Snaps every sticky's y to its per-kind lane, preserving x.
|
|
34
|
+
* Notes and drawings are free annotations and stay untouched.
|
|
35
|
+
*/
|
|
36
|
+
declare function alignToRows(board: EventStormingBoard): EventStormingBoard;
|
|
37
|
+
interface SpreadTimelineOptions {
|
|
38
|
+
/** Horizontal distance between neighboring stickies in board pixels. */
|
|
39
|
+
readonly gap?: number;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Redistributes the stickies' x evenly in timeline order (see `sortByTimeline`), starting at
|
|
43
|
+
* the current leftmost sticky, preserving each y. Notes and drawings stay untouched.
|
|
44
|
+
*/
|
|
45
|
+
declare function spreadTimeline(board: EventStormingBoard, { gap }?: SpreadTimelineOptions): EventStormingBoard;
|
|
46
|
+
|
|
47
|
+
export { type MoveDelta, STICKY_ELEMENT_TYPES, type SpreadTimelineOptions, type StickyElementType, alignToRows, clearColor, compact, findElement, isStickyElementType, moveBy, moveElement, setColor, setStickyKind, spreadTimeline, updateElement };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import { sortByTimeline } from '@miragon/event-storming-schema-model';
|
|
2
|
+
|
|
3
|
+
// src/util.ts
|
|
4
|
+
function updateElement(board, id, updater) {
|
|
5
|
+
let found = false;
|
|
6
|
+
const elements = board.elements.map((el) => {
|
|
7
|
+
if (el.id !== id) return el;
|
|
8
|
+
found = true;
|
|
9
|
+
return updater(el);
|
|
10
|
+
});
|
|
11
|
+
if (!found) throw new Error(`Element "${id}" not found.`);
|
|
12
|
+
return { ...board, elements };
|
|
13
|
+
}
|
|
14
|
+
function findElement(board, id) {
|
|
15
|
+
return board.elements.find((el) => el.id === id);
|
|
16
|
+
}
|
|
17
|
+
function compact(obj) {
|
|
18
|
+
const out = {};
|
|
19
|
+
for (const [k, v] of Object.entries(obj)) {
|
|
20
|
+
if (v !== void 0) out[k] = v;
|
|
21
|
+
}
|
|
22
|
+
return out;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// src/move.ts
|
|
26
|
+
function moveElement(board, elementId, position) {
|
|
27
|
+
const current = findElement(board, elementId);
|
|
28
|
+
if (!current) throw new Error(`Element "${elementId}" not found.`);
|
|
29
|
+
return moveBy(board, elementId, {
|
|
30
|
+
dx: position.x - current.position.x,
|
|
31
|
+
dy: position.y - current.position.y
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
function moveBy(board, elementId, { dx, dy }) {
|
|
35
|
+
return updateElement(board, elementId, (el) => {
|
|
36
|
+
const position = { x: el.position.x + dx, y: el.position.y + dy };
|
|
37
|
+
if (el.elementType === "drawing") {
|
|
38
|
+
return {
|
|
39
|
+
...el,
|
|
40
|
+
position,
|
|
41
|
+
points: el.points.map((point) => ({ x: point.x + dx, y: point.y + dy }))
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
return { ...el, position };
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// src/kind.ts
|
|
49
|
+
var STICKY_ELEMENT_TYPES = [
|
|
50
|
+
"event",
|
|
51
|
+
"command",
|
|
52
|
+
"actor",
|
|
53
|
+
"aggregate",
|
|
54
|
+
"policy",
|
|
55
|
+
"readmodel",
|
|
56
|
+
"external",
|
|
57
|
+
"hotspot"
|
|
58
|
+
];
|
|
59
|
+
function isStickyElementType(elementType) {
|
|
60
|
+
return STICKY_ELEMENT_TYPES.includes(elementType);
|
|
61
|
+
}
|
|
62
|
+
function setStickyKind(board, elementId, elementType) {
|
|
63
|
+
if (!isStickyElementType(elementType)) {
|
|
64
|
+
throw new Error(
|
|
65
|
+
`setStickyKind cannot retype to "${elementType}"; not a sticky kind.`
|
|
66
|
+
);
|
|
67
|
+
}
|
|
68
|
+
return updateElement(board, elementId, (el) => {
|
|
69
|
+
if (!isStickyElementType(el.elementType)) {
|
|
70
|
+
throw new Error(
|
|
71
|
+
`setStickyKind only applies to stickies; "${elementId}" is ${el.elementType}.`
|
|
72
|
+
);
|
|
73
|
+
}
|
|
74
|
+
return compact({
|
|
75
|
+
id: el.id,
|
|
76
|
+
elementType,
|
|
77
|
+
label: el.label,
|
|
78
|
+
position: el.position,
|
|
79
|
+
color: el.color
|
|
80
|
+
});
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// src/color.ts
|
|
85
|
+
function setColor(board, elementId, color) {
|
|
86
|
+
return updateElement(board, elementId, (el) => ({ ...el, color }));
|
|
87
|
+
}
|
|
88
|
+
function clearColor(board, elementId) {
|
|
89
|
+
return updateElement(
|
|
90
|
+
board,
|
|
91
|
+
elementId,
|
|
92
|
+
(el) => compact({ ...el, color: void 0 })
|
|
93
|
+
);
|
|
94
|
+
}
|
|
95
|
+
var LANE_Y = {
|
|
96
|
+
readmodel: 120,
|
|
97
|
+
actor: 220,
|
|
98
|
+
command: 320,
|
|
99
|
+
aggregate: 320,
|
|
100
|
+
event: 420,
|
|
101
|
+
policy: 520,
|
|
102
|
+
external: 620,
|
|
103
|
+
hotspot: 720
|
|
104
|
+
};
|
|
105
|
+
function alignToRows(board) {
|
|
106
|
+
const elements = board.elements.map(
|
|
107
|
+
(el) => isStickyElementType(el.elementType) ? { ...el, position: { x: el.position.x, y: LANE_Y[el.elementType] } } : el
|
|
108
|
+
);
|
|
109
|
+
return { ...board, elements };
|
|
110
|
+
}
|
|
111
|
+
function spreadTimeline(board, { gap = 180 } = {}) {
|
|
112
|
+
const stickies = sortByTimeline(board).filter((el) => isStickyElementType(el.elementType));
|
|
113
|
+
if (stickies.length === 0) return board;
|
|
114
|
+
const startX = Math.min(...stickies.map((el) => el.position.x));
|
|
115
|
+
const targetX = /* @__PURE__ */ new Map();
|
|
116
|
+
stickies.forEach((el, index) => targetX.set(el.id, startX + index * gap));
|
|
117
|
+
const elements = board.elements.map((el) => {
|
|
118
|
+
const x = targetX.get(el.id);
|
|
119
|
+
return x === void 0 ? el : { ...el, position: { x, y: el.position.y } };
|
|
120
|
+
});
|
|
121
|
+
return { ...board, elements };
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
export { STICKY_ELEMENT_TYPES, alignToRows, clearColor, compact, findElement, isStickyElementType, moveBy, moveElement, setColor, setStickyKind, spreadTimeline, updateElement };
|
|
125
|
+
//# sourceMappingURL=index.js.map
|
|
126
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/util.ts","../src/move.ts","../src/kind.ts","../src/color.ts","../src/arrange.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["import type { BoardElement, EventStormingBoard } from '@miragon/event-storming-schema-model';\n\nexport function updateElement(\n board: EventStormingBoard,\n id: string,\n updater: (el: BoardElement) => BoardElement,\n): EventStormingBoard {\n let found = false;\n const elements = board.elements.map((el) => {\n if (el.id !== id) return el;\n found = true;\n return updater(el);\n });\n if (!found) throw new Error(`Element \"${id}\" not found.`);\n return { ...board, elements };\n}\n\nexport function findElement(board: EventStormingBoard, id: string): BoardElement | undefined {\n return board.elements.find((el) => el.id === id);\n}\n\n/** Removes `undefined` values so exactOptionalPropertyTypes is not violated. */\nexport function compact<T extends Record<string, unknown>>(obj: T): T {\n const out: Record<string, unknown> = {};\n for (const [k, v] of Object.entries(obj)) {\n if (v !== undefined) out[k] = v;\n }\n return out as T;\n}\n","import type { Coordinate, EventStormingBoard } from '@miragon/event-storming-schema-model';\nimport { updateElement, findElement } from './util.js';\n\nexport interface MoveDelta {\n readonly dx: number;\n readonly dy: number;\n}\n\n/** Pure function — no undo stack: returns a new board with the element centered at `position`. */\nexport function moveElement(\n board: EventStormingBoard,\n elementId: string,\n position: Coordinate,\n): EventStormingBoard {\n const current = findElement(board, elementId);\n if (!current) throw new Error(`Element \"${elementId}\" not found.`);\n return moveBy(board, elementId, {\n dx: position.x - current.position.x,\n dy: position.y - current.position.y,\n });\n}\n\n/** Translates an element by a pixel delta. The canvas is free — any finite target is valid. */\nexport function moveBy(\n board: EventStormingBoard,\n elementId: string,\n { dx, dy }: MoveDelta,\n): EventStormingBoard {\n return updateElement(board, elementId, (el) => {\n const position = { x: el.position.x + dx, y: el.position.y + dy };\n if (el.elementType === 'drawing') {\n // Drawing points are absolute board pixels and `position` mirrors the first point,\n // so the polyline must translate together with the position.\n return {\n ...el,\n position,\n points: el.points.map((point) => ({ x: point.x + dx, y: point.y + dy })),\n };\n }\n return { ...el, position };\n });\n}\n","import type {\n BoardElement,\n ElementType,\n EventStormingBoard,\n} from '@miragon/event-storming-schema-model';\nimport { updateElement, compact } from './util.js';\n\n/** The eight retypeable sticky kinds — every element kind except notes and drawings. */\nexport const STICKY_ELEMENT_TYPES = [\n 'event',\n 'command',\n 'actor',\n 'aggregate',\n 'policy',\n 'readmodel',\n 'external',\n 'hotspot',\n] as const;\n\nexport type StickyElementType = (typeof STICKY_ELEMENT_TYPES)[number];\n\nexport function isStickyElementType(elementType: ElementType): elementType is StickyElementType {\n return (STICKY_ELEMENT_TYPES as readonly ElementType[]).includes(elementType);\n}\n\n/**\n * Retypes a sticky (e.g. command → event), preserving id, label, position and color.\n * Notes and drawings are not stickies and cannot take part in retyping.\n */\nexport function setStickyKind(\n board: EventStormingBoard,\n elementId: string,\n elementType: StickyElementType,\n): EventStormingBoard {\n if (!isStickyElementType(elementType)) {\n throw new Error(\n `setStickyKind cannot retype to \"${elementType as string}\"; not a sticky kind.`,\n );\n }\n return updateElement(board, elementId, (el) => {\n if (!isStickyElementType(el.elementType)) {\n throw new Error(\n `setStickyKind only applies to stickies; \"${elementId}\" is ${el.elementType}.`,\n );\n }\n // Rebuild from the shared base fields so nothing kind-specific ever leaks across a retype.\n return compact({\n id: el.id,\n elementType,\n label: el.label,\n position: el.position,\n color: el.color,\n }) as BoardElement;\n });\n}\n","import type { BoardElement, EventStormingBoard } from '@miragon/event-storming-schema-model';\nimport { updateElement, compact } from './util.js';\n\n/** Sets the element's color override (CSS color, typically a hex sticky fill). */\nexport function setColor(\n board: EventStormingBoard,\n elementId: string,\n color: string,\n): EventStormingBoard {\n return updateElement(board, elementId, (el) => ({ ...el, color }));\n}\n\n/** Removes the color override so the element falls back to its per-kind default fill. */\nexport function clearColor(board: EventStormingBoard, elementId: string): EventStormingBoard {\n // compact drops the undefined value, so the `color` key is deleted rather than kept as undefined.\n return updateElement(\n board,\n elementId,\n (el) => compact({ ...el, color: undefined }) as unknown as BoardElement,\n );\n}\n","import { sortByTimeline, type EventStormingBoard } from '@miragon/event-storming-schema-model';\nimport { isStickyElementType, type StickyElementType } from './kind.js';\n\n/**\n * Per-kind lane centers (board pixels, top → bottom) for the classic picture-that-explains-\n * everything row layout. Command and aggregate share the middle lane.\n */\nconst LANE_Y: Readonly<Record<StickyElementType, number>> = {\n readmodel: 120,\n actor: 220,\n command: 320,\n aggregate: 320,\n event: 420,\n policy: 520,\n external: 620,\n hotspot: 720,\n};\n\n/**\n * Snaps every sticky's y to its per-kind lane, preserving x.\n * Notes and drawings are free annotations and stay untouched.\n */\nexport function alignToRows(board: EventStormingBoard): EventStormingBoard {\n const elements = board.elements.map((el) =>\n isStickyElementType(el.elementType)\n ? { ...el, position: { x: el.position.x, y: LANE_Y[el.elementType] } }\n : el,\n );\n return { ...board, elements };\n}\n\nexport interface SpreadTimelineOptions {\n /** Horizontal distance between neighboring stickies in board pixels. */\n readonly gap?: number;\n}\n\n/**\n * Redistributes the stickies' x evenly in timeline order (see `sortByTimeline`), starting at\n * the current leftmost sticky, preserving each y. Notes and drawings stay untouched.\n */\nexport function spreadTimeline(\n board: EventStormingBoard,\n { gap = 180 }: SpreadTimelineOptions = {},\n): EventStormingBoard {\n const stickies = sortByTimeline(board).filter((el) => isStickyElementType(el.elementType));\n if (stickies.length === 0) return board;\n const startX = Math.min(...stickies.map((el) => el.position.x));\n const targetX = new Map<string, number>();\n stickies.forEach((el, index) => targetX.set(el.id, startX + index * gap));\n const elements = board.elements.map((el) => {\n const x = targetX.get(el.id);\n return x === undefined ? el : { ...el, position: { x, y: el.position.y } };\n });\n return { ...board, elements };\n}\n"]}
|
package/package.json
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@miragon/event-storming-transforms",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Pure functions that turn an Event Storming board into a new one, such as move, kind, color and arrange.",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"repository": {
|
|
7
|
+
"type": "git",
|
|
8
|
+
"url": "git+https://github.com/Miragon/event-storming-modeler.git",
|
|
9
|
+
"directory": "packages/transforms"
|
|
10
|
+
},
|
|
11
|
+
"homepage": "https://github.com/Miragon/event-storming-modeler/tree/main/packages/transforms#readme",
|
|
12
|
+
"bugs": "https://github.com/Miragon/event-storming-modeler/issues",
|
|
13
|
+
"type": "module",
|
|
14
|
+
"sideEffects": false,
|
|
15
|
+
"exports": {
|
|
16
|
+
".": {
|
|
17
|
+
"types": "./dist/index.d.ts",
|
|
18
|
+
"import": "./dist/index.js",
|
|
19
|
+
"require": "./dist/index.cjs"
|
|
20
|
+
}
|
|
21
|
+
},
|
|
22
|
+
"main": "./dist/index.cjs",
|
|
23
|
+
"module": "./dist/index.js",
|
|
24
|
+
"types": "./dist/index.d.ts",
|
|
25
|
+
"files": [
|
|
26
|
+
"dist"
|
|
27
|
+
],
|
|
28
|
+
"publishConfig": {
|
|
29
|
+
"access": "public"
|
|
30
|
+
},
|
|
31
|
+
"scripts": {
|
|
32
|
+
"build": "tsup",
|
|
33
|
+
"test": "vitest run"
|
|
34
|
+
},
|
|
35
|
+
"dependencies": {
|
|
36
|
+
"@miragon/event-storming-schema-model": "0.1.0"
|
|
37
|
+
},
|
|
38
|
+
"devDependencies": {
|
|
39
|
+
"tsup": "8.5.1",
|
|
40
|
+
"typescript": "6.0.3",
|
|
41
|
+
"vitest": "4.1.11"
|
|
42
|
+
}
|
|
43
|
+
}
|