@formicoidea/labre-ddd-shared 0.25.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/dist/index.d.ts +8 -0
- package/dist/index.js +8 -0
- package/dist/shared/consts.d.ts +180 -0
- package/dist/shared/consts.js +92 -0
- package/dist/shared/prefabs.d.ts +71 -0
- package/dist/shared/prefabs.js +214 -0
- package/dist/shared/utils.d.ts +10 -0
- package/dist/shared/utils.js +9 -0
- package/dist/templates/categories.d.ts +11 -0
- package/dist/templates/categories.js +20 -0
- package/dist/templates/components.d.ts +7 -0
- package/dist/templates/components.js +158 -0
- package/dist/toolbar/icons.d.ts +6 -0
- package/dist/toolbar/icons.js +20 -0
- package/dist/toolbar/menu-base.d.ts +23 -0
- package/dist/toolbar/menu-base.js +62 -0
- package/dist/toolbar/senior-buttons.d.ts +20 -0
- package/dist/toolbar/senior-buttons.js +90 -0
- package/package.json +25 -0
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export * from './shared/consts.js';
|
|
2
|
+
export * from './shared/prefabs.js';
|
|
3
|
+
export * from './shared/utils.js';
|
|
4
|
+
export { DddMenuBase } from './toolbar/menu-base.js';
|
|
5
|
+
export * from './toolbar/icons.js';
|
|
6
|
+
export { DddSeniorButtonBase } from './toolbar/senior-buttons.js';
|
|
7
|
+
export { CD_TEMPLATES, CM_TEMPLATES, ES_TEMPLATES, } from './templates/components.js';
|
|
8
|
+
export { contextMapTemplateCategory, coreDomainTemplateCategory, eventStormingTemplateCategory, } from './templates/categories.js';
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export * from './shared/consts.js';
|
|
2
|
+
export * from './shared/prefabs.js';
|
|
3
|
+
export * from './shared/utils.js';
|
|
4
|
+
export { DddMenuBase } from './toolbar/menu-base.js';
|
|
5
|
+
export * from './toolbar/icons.js';
|
|
6
|
+
export { DddSeniorButtonBase } from './toolbar/senior-buttons.js';
|
|
7
|
+
export { CD_TEMPLATES, CM_TEMPLATES, ES_TEMPLATES, } from './templates/components.js';
|
|
8
|
+
export { contextMapTemplateCategory, coreDomainTemplateCategory, eventStormingTemplateCategory, } from './templates/categories.js';
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared DDD palette and sizes. Every tool composes the same irreducible units
|
|
3
|
+
* (sticky, dot, bubble, connector, label) from these presets — no duplicated
|
|
4
|
+
* component definitions across the Event Storming / Context Map / Core Domain
|
|
5
|
+
* sub-menus.
|
|
6
|
+
*/
|
|
7
|
+
/** Square sticky side (px, canvas reference). */
|
|
8
|
+
export declare const STICKY_SIZE = 120;
|
|
9
|
+
/** Faux drop-shadow offset + colour (a plain offset rect behind the sticky). */
|
|
10
|
+
export declare const SHADOW_OFFSET = 7;
|
|
11
|
+
export declare const SHADOW_COLOR = "#0000002e";
|
|
12
|
+
/** Handwriting marker font, already shipped in the canvas font set. */
|
|
13
|
+
export declare const STICKY_FONT = "blocksuite:surface:Kalam";
|
|
14
|
+
export declare const STICKY_FONT_SIZE = 20;
|
|
15
|
+
export declare const STICKY_RADIUS = 6;
|
|
16
|
+
/** Event Storming sticky palette — DDD Crew / Brandolini colour code. */
|
|
17
|
+
export declare const ES_STICKIES: readonly [{
|
|
18
|
+
readonly kind: "domainEvent";
|
|
19
|
+
readonly label: "Domain event";
|
|
20
|
+
readonly fill: "#F5963B";
|
|
21
|
+
readonly text: "#5a3000";
|
|
22
|
+
}, {
|
|
23
|
+
readonly kind: "command";
|
|
24
|
+
readonly label: "Command";
|
|
25
|
+
readonly fill: "#5BA3DB";
|
|
26
|
+
readonly text: "#06304d";
|
|
27
|
+
}, {
|
|
28
|
+
readonly kind: "actor";
|
|
29
|
+
readonly label: "Actor";
|
|
30
|
+
readonly fill: "#FFF1A8";
|
|
31
|
+
readonly text: "#5a4b00";
|
|
32
|
+
}, {
|
|
33
|
+
readonly kind: "constraint";
|
|
34
|
+
readonly label: "Constraint";
|
|
35
|
+
readonly fill: "#FFD84D";
|
|
36
|
+
readonly text: "#5a4b00";
|
|
37
|
+
}, {
|
|
38
|
+
readonly kind: "policy";
|
|
39
|
+
readonly label: "Policy";
|
|
40
|
+
readonly fill: "#C9A8E0";
|
|
41
|
+
readonly text: "#3d1f57";
|
|
42
|
+
}, {
|
|
43
|
+
readonly kind: "readModel";
|
|
44
|
+
readonly label: "Read model";
|
|
45
|
+
readonly fill: "#7ED38A";
|
|
46
|
+
readonly text: "#14502a";
|
|
47
|
+
}, {
|
|
48
|
+
readonly kind: "system";
|
|
49
|
+
readonly label: "External system";
|
|
50
|
+
readonly fill: "#F6A6C0";
|
|
51
|
+
readonly text: "#5e1230";
|
|
52
|
+
}];
|
|
53
|
+
/** Hotspot — neon-pink diamond. */
|
|
54
|
+
export declare const ES_HOTSPOT: {
|
|
55
|
+
label: string;
|
|
56
|
+
fill: string;
|
|
57
|
+
text: string;
|
|
58
|
+
};
|
|
59
|
+
/** Context Map bounded-context bubble. */
|
|
60
|
+
export declare const CM_BUBBLE: {
|
|
61
|
+
fill: string;
|
|
62
|
+
stroke: string;
|
|
63
|
+
text: string;
|
|
64
|
+
w: number;
|
|
65
|
+
h: number;
|
|
66
|
+
radius: number;
|
|
67
|
+
};
|
|
68
|
+
/**
|
|
69
|
+
* Context Map relationship presets (DDD Crew notation). Each is the SAME
|
|
70
|
+
* connector unit + an abbreviation tag; `upDown` adds U/D markers (upstream →
|
|
71
|
+
* downstream); `dashed` marks the "no real integration" patterns (Separate
|
|
72
|
+
* Ways, Big Ball of Mud).
|
|
73
|
+
*/
|
|
74
|
+
export declare const CM_RELATIONSHIPS: readonly [{
|
|
75
|
+
readonly kind: "partnership";
|
|
76
|
+
readonly label: "Partnership";
|
|
77
|
+
readonly abbrev: "PS";
|
|
78
|
+
readonly upDown: false;
|
|
79
|
+
readonly dashed: false;
|
|
80
|
+
}, {
|
|
81
|
+
readonly kind: "sharedKernel";
|
|
82
|
+
readonly label: "Shared Kernel";
|
|
83
|
+
readonly abbrev: "SK";
|
|
84
|
+
readonly upDown: false;
|
|
85
|
+
readonly dashed: false;
|
|
86
|
+
}, {
|
|
87
|
+
readonly kind: "customerSupplier";
|
|
88
|
+
readonly label: "Customer / Supplier";
|
|
89
|
+
readonly abbrev: "C/S";
|
|
90
|
+
readonly upDown: true;
|
|
91
|
+
readonly dashed: false;
|
|
92
|
+
}, {
|
|
93
|
+
readonly kind: "conformist";
|
|
94
|
+
readonly label: "Conformist";
|
|
95
|
+
readonly abbrev: "CF";
|
|
96
|
+
readonly upDown: true;
|
|
97
|
+
readonly dashed: false;
|
|
98
|
+
}, {
|
|
99
|
+
readonly kind: "acl";
|
|
100
|
+
readonly label: "Anticorruption Layer";
|
|
101
|
+
readonly abbrev: "ACL";
|
|
102
|
+
readonly upDown: true;
|
|
103
|
+
readonly dashed: false;
|
|
104
|
+
}, {
|
|
105
|
+
readonly kind: "ohs";
|
|
106
|
+
readonly label: "Open Host Service";
|
|
107
|
+
readonly abbrev: "OHS";
|
|
108
|
+
readonly upDown: true;
|
|
109
|
+
readonly dashed: false;
|
|
110
|
+
}, {
|
|
111
|
+
readonly kind: "publishedLanguage";
|
|
112
|
+
readonly label: "Published Language";
|
|
113
|
+
readonly abbrev: "PL";
|
|
114
|
+
readonly upDown: true;
|
|
115
|
+
readonly dashed: false;
|
|
116
|
+
}, {
|
|
117
|
+
readonly kind: "separateWays";
|
|
118
|
+
readonly label: "Separate Ways";
|
|
119
|
+
readonly abbrev: "SW";
|
|
120
|
+
readonly upDown: false;
|
|
121
|
+
readonly dashed: true;
|
|
122
|
+
}, {
|
|
123
|
+
readonly kind: "bbom";
|
|
124
|
+
readonly label: "Big Ball of Mud";
|
|
125
|
+
readonly abbrev: "BBoM";
|
|
126
|
+
readonly upDown: false;
|
|
127
|
+
readonly dashed: true;
|
|
128
|
+
}];
|
|
129
|
+
export declare const CLOUD_VERTICES: number[][];
|
|
130
|
+
export declare const CLOUD: {
|
|
131
|
+
w: number;
|
|
132
|
+
h: number;
|
|
133
|
+
fill: string;
|
|
134
|
+
stroke: string;
|
|
135
|
+
};
|
|
136
|
+
/** Team Topologies interaction modes (placeable markers + Notation legend). */
|
|
137
|
+
export declare const TEAM_TOPOLOGIES: readonly [{
|
|
138
|
+
readonly kind: "collaboration";
|
|
139
|
+
readonly label: "Collaboration";
|
|
140
|
+
readonly letter: "C";
|
|
141
|
+
readonly fill: "#99ff99";
|
|
142
|
+
}, {
|
|
143
|
+
readonly kind: "xaas";
|
|
144
|
+
readonly label: "X-as-a-Service";
|
|
145
|
+
readonly letter: "X";
|
|
146
|
+
readonly fill: "#66b2ff";
|
|
147
|
+
}, {
|
|
148
|
+
readonly kind: "facilitating";
|
|
149
|
+
readonly label: "Facilitating";
|
|
150
|
+
readonly letter: "F";
|
|
151
|
+
readonly fill: "#ffd84d";
|
|
152
|
+
}];
|
|
153
|
+
export declare const MARKER_SIZE = 30;
|
|
154
|
+
/** Core Domain sub-domain / bounded-context dot presets (colours from template). */
|
|
155
|
+
export declare const CD_SUBDOMAINS: readonly [{
|
|
156
|
+
readonly kind: "bigBet";
|
|
157
|
+
readonly label: "Big-bet sub-domain";
|
|
158
|
+
readonly fill: "#9933ff";
|
|
159
|
+
}, {
|
|
160
|
+
readonly kind: "platform";
|
|
161
|
+
readonly label: "Platform sub-domain";
|
|
162
|
+
readonly fill: "#66b2ff";
|
|
163
|
+
}, {
|
|
164
|
+
readonly kind: "outsourced";
|
|
165
|
+
readonly label: "Outsourced / purchased";
|
|
166
|
+
readonly fill: "#99ff99";
|
|
167
|
+
}, {
|
|
168
|
+
readonly kind: "bcCurrent";
|
|
169
|
+
readonly label: "Bounded context";
|
|
170
|
+
readonly fill: "#ff3333";
|
|
171
|
+
}, {
|
|
172
|
+
readonly kind: "bcFuture";
|
|
173
|
+
readonly label: "Future position";
|
|
174
|
+
readonly fill: "#cccccc";
|
|
175
|
+
}];
|
|
176
|
+
export declare const DOT_SIZE = 26;
|
|
177
|
+
export declare const MOVEMENT_COLOR = "#ff3333";
|
|
178
|
+
export declare const LABEL_COLOR = "#1f2328";
|
|
179
|
+
export declare const LABEL_FONT = "blocksuite:surface:Inter";
|
|
180
|
+
export declare const LABEL_FONT_SIZE = 14;
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared DDD palette and sizes. Every tool composes the same irreducible units
|
|
3
|
+
* (sticky, dot, bubble, connector, label) from these presets — no duplicated
|
|
4
|
+
* component definitions across the Event Storming / Context Map / Core Domain
|
|
5
|
+
* sub-menus.
|
|
6
|
+
*/
|
|
7
|
+
/** Square sticky side (px, canvas reference). */
|
|
8
|
+
export const STICKY_SIZE = 120;
|
|
9
|
+
/** Faux drop-shadow offset + colour (a plain offset rect behind the sticky). */
|
|
10
|
+
export const SHADOW_OFFSET = 7;
|
|
11
|
+
export const SHADOW_COLOR = '#0000002e';
|
|
12
|
+
/** Handwriting marker font, already shipped in the canvas font set. */
|
|
13
|
+
export const STICKY_FONT = 'blocksuite:surface:Kalam';
|
|
14
|
+
export const STICKY_FONT_SIZE = 20;
|
|
15
|
+
export const STICKY_RADIUS = 6;
|
|
16
|
+
/** Event Storming sticky palette — DDD Crew / Brandolini colour code. */
|
|
17
|
+
export const ES_STICKIES = [
|
|
18
|
+
{ kind: 'domainEvent', label: 'Domain event', fill: '#F5963B', text: '#5a3000' },
|
|
19
|
+
{ kind: 'command', label: 'Command', fill: '#5BA3DB', text: '#06304d' },
|
|
20
|
+
{ kind: 'actor', label: 'Actor', fill: '#FFF1A8', text: '#5a4b00' },
|
|
21
|
+
{ kind: 'constraint', label: 'Constraint', fill: '#FFD84D', text: '#5a4b00' },
|
|
22
|
+
{ kind: 'policy', label: 'Policy', fill: '#C9A8E0', text: '#3d1f57' },
|
|
23
|
+
{ kind: 'readModel', label: 'Read model', fill: '#7ED38A', text: '#14502a' },
|
|
24
|
+
{ kind: 'system', label: 'External system', fill: '#F6A6C0', text: '#5e1230' },
|
|
25
|
+
];
|
|
26
|
+
/** Hotspot — neon-pink diamond. */
|
|
27
|
+
export const ES_HOTSPOT = { label: 'Hotspot', fill: '#FF1E8E', text: '#ffffff' };
|
|
28
|
+
/** Context Map bounded-context bubble. */
|
|
29
|
+
export const CM_BUBBLE = {
|
|
30
|
+
fill: '#e6f0fa',
|
|
31
|
+
stroke: '#2f6fb0',
|
|
32
|
+
text: '#06304d',
|
|
33
|
+
w: 150,
|
|
34
|
+
h: 70,
|
|
35
|
+
radius: 30,
|
|
36
|
+
};
|
|
37
|
+
/**
|
|
38
|
+
* Context Map relationship presets (DDD Crew notation). Each is the SAME
|
|
39
|
+
* connector unit + an abbreviation tag; `upDown` adds U/D markers (upstream →
|
|
40
|
+
* downstream); `dashed` marks the "no real integration" patterns (Separate
|
|
41
|
+
* Ways, Big Ball of Mud).
|
|
42
|
+
*/
|
|
43
|
+
export const CM_RELATIONSHIPS = [
|
|
44
|
+
{ kind: 'partnership', label: 'Partnership', abbrev: 'PS', upDown: false, dashed: false },
|
|
45
|
+
{ kind: 'sharedKernel', label: 'Shared Kernel', abbrev: 'SK', upDown: false, dashed: false },
|
|
46
|
+
{ kind: 'customerSupplier', label: 'Customer / Supplier', abbrev: 'C/S', upDown: true, dashed: false },
|
|
47
|
+
{ kind: 'conformist', label: 'Conformist', abbrev: 'CF', upDown: true, dashed: false },
|
|
48
|
+
{ kind: 'acl', label: 'Anticorruption Layer', abbrev: 'ACL', upDown: true, dashed: false },
|
|
49
|
+
{ kind: 'ohs', label: 'Open Host Service', abbrev: 'OHS', upDown: true, dashed: false },
|
|
50
|
+
{ kind: 'publishedLanguage', label: 'Published Language', abbrev: 'PL', upDown: true, dashed: false },
|
|
51
|
+
{ kind: 'separateWays', label: 'Separate Ways', abbrev: 'SW', upDown: false, dashed: true },
|
|
52
|
+
{ kind: 'bbom', label: 'Big Ball of Mud', abbrev: 'BBoM', upDown: false, dashed: true },
|
|
53
|
+
];
|
|
54
|
+
/**
|
|
55
|
+
* Context Map cloud (Big Ball of Mud / general-purpose boundary). The canvas
|
|
56
|
+
* polygon renderer draws straight segments (it ignores `smoothFlags`), so the
|
|
57
|
+
* cloud is a DENSE lumpy closed outline — enough points that the lineTo edges
|
|
58
|
+
* read as smooth curves. Normalized [0-1] coordinates relative to the bound.
|
|
59
|
+
*/
|
|
60
|
+
function buildCloud() {
|
|
61
|
+
const N = 56;
|
|
62
|
+
const clamp = (v) => Math.min(0.98, Math.max(0.02, v));
|
|
63
|
+
const pts = [];
|
|
64
|
+
for (let i = 0; i < N; i++) {
|
|
65
|
+
const t = (i / N) * Math.PI * 2;
|
|
66
|
+
const r = 0.4 + 0.05 * Math.sin(6 * t) + 0.03 * Math.sin(3 * t + 0.8);
|
|
67
|
+
pts.push([clamp(0.5 + r * Math.cos(t)), clamp(0.5 + r * 0.6 * Math.sin(t))]);
|
|
68
|
+
}
|
|
69
|
+
return pts;
|
|
70
|
+
}
|
|
71
|
+
export const CLOUD_VERTICES = buildCloud();
|
|
72
|
+
export const CLOUD = { w: 180, h: 120, fill: '#f0eef6', stroke: '#6d6e71' };
|
|
73
|
+
/** Team Topologies interaction modes (placeable markers + Notation legend). */
|
|
74
|
+
export const TEAM_TOPOLOGIES = [
|
|
75
|
+
{ kind: 'collaboration', label: 'Collaboration', letter: 'C', fill: '#99ff99' },
|
|
76
|
+
{ kind: 'xaas', label: 'X-as-a-Service', letter: 'X', fill: '#66b2ff' },
|
|
77
|
+
{ kind: 'facilitating', label: 'Facilitating', letter: 'F', fill: '#ffd84d' },
|
|
78
|
+
];
|
|
79
|
+
export const MARKER_SIZE = 30;
|
|
80
|
+
/** Core Domain sub-domain / bounded-context dot presets (colours from template). */
|
|
81
|
+
export const CD_SUBDOMAINS = [
|
|
82
|
+
{ kind: 'bigBet', label: 'Big-bet sub-domain', fill: '#9933ff' },
|
|
83
|
+
{ kind: 'platform', label: 'Platform sub-domain', fill: '#66b2ff' },
|
|
84
|
+
{ kind: 'outsourced', label: 'Outsourced / purchased', fill: '#99ff99' },
|
|
85
|
+
{ kind: 'bcCurrent', label: 'Bounded context', fill: '#ff3333' },
|
|
86
|
+
{ kind: 'bcFuture', label: 'Future position', fill: '#cccccc' },
|
|
87
|
+
];
|
|
88
|
+
export const DOT_SIZE = 26;
|
|
89
|
+
export const MOVEMENT_COLOR = '#ff3333';
|
|
90
|
+
export const LABEL_COLOR = '#1f2328';
|
|
91
|
+
export const LABEL_FONT = 'blocksuite:surface:Inter';
|
|
92
|
+
export const LABEL_FONT_SIZE = 14;
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import type { BlockStdScope } from '@formicoidea/labre-core/std';
|
|
2
|
+
import type { GfxController } from '@formicoidea/labre-core/std/gfx';
|
|
3
|
+
/**
|
|
4
|
+
* Irreducible reusable units composed by all three DDD sub-menus. Each helper
|
|
5
|
+
* wraps native `surface.addElement` (shape / text / connector / group); nothing
|
|
6
|
+
* here defines a new element type, so the same builders back Event Storming
|
|
7
|
+
* stickies, Context Map bubbles + relationships and Core Domain dots + arrows.
|
|
8
|
+
*/
|
|
9
|
+
type Surface = NonNullable<GfxController['surface']>;
|
|
10
|
+
/** Group several element ids; returns the group id (or the first id on failure). */
|
|
11
|
+
export declare function groupIds(std: BlockStdScope, ids: string[]): string;
|
|
12
|
+
/** A post-it: faux-shadow rect + coloured face + centred handwriting label, grouped. */
|
|
13
|
+
export declare function addSticky(surface: Surface, std: BlockStdScope, cx: number, cy: number, opts: {
|
|
14
|
+
fill: string;
|
|
15
|
+
text: string;
|
|
16
|
+
label: string;
|
|
17
|
+
shapeType?: 'rect' | 'diamond';
|
|
18
|
+
size?: number;
|
|
19
|
+
}): string;
|
|
20
|
+
/** A Context Map bounded-context bubble (rounded pill + centred label). */
|
|
21
|
+
export declare function addBubble(surface: Surface, std: BlockStdScope, cx: number, cy: number, label: string): string;
|
|
22
|
+
/** A Core Domain dot (sub-domain / bounded context); optional label to its right. */
|
|
23
|
+
export declare function addDot(surface: Surface, std: BlockStdScope, cx: number, cy: number, fill: string, label?: string): string;
|
|
24
|
+
/** A free-floating label. */
|
|
25
|
+
export declare function addLabel(surface: Surface, cx: number, cy: number, label: string, align?: 'left' | 'center', color?: string): string;
|
|
26
|
+
/** A relationship connector (the single connector unit reused for all patterns). */
|
|
27
|
+
export declare function addConnector(surface: Surface, x1: number, y1: number, x2: number, y2: number, opts?: {
|
|
28
|
+
rearArrow?: boolean;
|
|
29
|
+
dashed?: boolean;
|
|
30
|
+
stroke?: string;
|
|
31
|
+
strokeWidth?: number;
|
|
32
|
+
}): string;
|
|
33
|
+
/** A Team Topologies interaction-mode marker: a coloured square + centred letter. */
|
|
34
|
+
export declare function addMarker(surface: Surface, std: BlockStdScope, cx: number, cy: number, opts: {
|
|
35
|
+
fill: string;
|
|
36
|
+
letter: string;
|
|
37
|
+
label?: string;
|
|
38
|
+
}): string;
|
|
39
|
+
/** A cloud (Big Ball of Mud / general-purpose boundary): a smoothed closed bezier polygon. */
|
|
40
|
+
export declare function addCloud(surface: Surface, std: BlockStdScope, cx: number, cy: number, label?: string): string;
|
|
41
|
+
/**
|
|
42
|
+
* A Context Map relationship in DDD Crew notation: the connector, the pattern
|
|
43
|
+
* abbreviation in a small tag, and (for upstream/downstream patterns) the U / D
|
|
44
|
+
* markers — grouped so the whole pattern reads as one unit.
|
|
45
|
+
*/
|
|
46
|
+
export declare function addRelationship(surface: Surface, std: BlockStdScope, cx: number, cy: number, preset: {
|
|
47
|
+
abbrev: string;
|
|
48
|
+
upDown: boolean;
|
|
49
|
+
dashed: boolean;
|
|
50
|
+
}): string;
|
|
51
|
+
export interface LegendRow {
|
|
52
|
+
swatch: 'dot' | 'square' | 'line';
|
|
53
|
+
color: string;
|
|
54
|
+
letter?: string;
|
|
55
|
+
label: string;
|
|
56
|
+
}
|
|
57
|
+
export interface LegendSection {
|
|
58
|
+
title?: string;
|
|
59
|
+
rows: LegendRow[];
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* A boxed legend: a bordered container with a bold "Légende" title and bold
|
|
63
|
+
* section sub-titles, each row a swatch (dot / square+letter / line) + label.
|
|
64
|
+
* Shared by the Core Domain and Context Map tools. Returns the grouped id.
|
|
65
|
+
*/
|
|
66
|
+
export declare function addLegend(surface: Surface, std: BlockStdScope, x: number, y: number, opts: {
|
|
67
|
+
title: string;
|
|
68
|
+
sections: LegendSection[];
|
|
69
|
+
width?: number;
|
|
70
|
+
}): string;
|
|
71
|
+
export {};
|
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
import { createGroupCommand } from '@formicoidea/labre-core/gfx/group';
|
|
2
|
+
import { ConnectorMode, FontWeight, PointStyle, ShapeStyle, StrokeStyle, } from '@formicoidea/labre-core/model';
|
|
3
|
+
import { Bound } from '@formicoidea/labre-core/global/gfx';
|
|
4
|
+
import { CLOUD, CLOUD_VERTICES, CM_BUBBLE, DOT_SIZE, LABEL_COLOR, LABEL_FONT, LABEL_FONT_SIZE, MARKER_SIZE, SHADOW_COLOR, SHADOW_OFFSET, STICKY_FONT, STICKY_FONT_SIZE, STICKY_RADIUS, STICKY_SIZE, } from './consts';
|
|
5
|
+
const NO_STROKE = '#00000000';
|
|
6
|
+
/** Group several element ids; returns the group id (or the first id on failure). */
|
|
7
|
+
export function groupIds(std, ids) {
|
|
8
|
+
const [, result] = std.command.exec(createGroupCommand, { elements: ids });
|
|
9
|
+
return result.groupId || ids[0];
|
|
10
|
+
}
|
|
11
|
+
function addShape(surface, x, y, w, h, opts) {
|
|
12
|
+
const { shapeType = 'rect', fill, stroke = NO_STROKE, strokeWidth = 0, radius = 0 } = opts;
|
|
13
|
+
return surface.addElement({
|
|
14
|
+
type: 'shape',
|
|
15
|
+
shapeType,
|
|
16
|
+
filled: true,
|
|
17
|
+
fillColor: fill,
|
|
18
|
+
strokeColor: stroke,
|
|
19
|
+
strokeWidth,
|
|
20
|
+
shapeStyle: ShapeStyle.General,
|
|
21
|
+
roughness: 0,
|
|
22
|
+
radius,
|
|
23
|
+
xywh: new Bound(x, y, w, h).serialize(),
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
function addText(surface, x, y, w, text, color, fontFamily, fontSize, textAlign = 'center', bold = false) {
|
|
27
|
+
return surface.addElement({
|
|
28
|
+
type: 'text',
|
|
29
|
+
text,
|
|
30
|
+
color,
|
|
31
|
+
fontFamily,
|
|
32
|
+
fontSize,
|
|
33
|
+
textAlign,
|
|
34
|
+
fontWeight: bold ? FontWeight.SemiBold : FontWeight.Regular,
|
|
35
|
+
xywh: new Bound(x, y, w, fontSize + 10).serialize(),
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
/** A post-it: faux-shadow rect + coloured face + centred handwriting label, grouped. */
|
|
39
|
+
export function addSticky(surface, std, cx, cy, opts) {
|
|
40
|
+
const { fill, text, label, shapeType = 'rect', size = STICKY_SIZE } = opts;
|
|
41
|
+
const half = size / 2;
|
|
42
|
+
const radius = shapeType === 'rect' ? STICKY_RADIUS : 0;
|
|
43
|
+
const shadow = addShape(surface, cx - half + SHADOW_OFFSET, cy - half + SHADOW_OFFSET, size, size, {
|
|
44
|
+
shapeType,
|
|
45
|
+
fill: SHADOW_COLOR,
|
|
46
|
+
radius,
|
|
47
|
+
});
|
|
48
|
+
const face = addShape(surface, cx - half, cy - half, size, size, { shapeType, fill, radius });
|
|
49
|
+
const lbl = addText(surface, cx - half + 10, cy - STICKY_FONT_SIZE / 2, size - 20, label, text, STICKY_FONT, STICKY_FONT_SIZE);
|
|
50
|
+
return groupIds(std, [shadow, face, lbl]);
|
|
51
|
+
}
|
|
52
|
+
/** A Context Map bounded-context bubble (rounded pill + centred label). */
|
|
53
|
+
export function addBubble(surface, std, cx, cy, label) {
|
|
54
|
+
const { w, h, radius, fill, stroke, text } = CM_BUBBLE;
|
|
55
|
+
const box = addShape(surface, cx - w / 2, cy - h / 2, w, h, {
|
|
56
|
+
fill,
|
|
57
|
+
stroke,
|
|
58
|
+
strokeWidth: 1.5,
|
|
59
|
+
radius,
|
|
60
|
+
});
|
|
61
|
+
const lbl = addText(surface, cx - w / 2 + 8, cy - LABEL_FONT_SIZE / 2, w - 16, label, text, LABEL_FONT, LABEL_FONT_SIZE);
|
|
62
|
+
return groupIds(std, [box, lbl]);
|
|
63
|
+
}
|
|
64
|
+
/** A Core Domain dot (sub-domain / bounded context); optional label to its right. */
|
|
65
|
+
export function addDot(surface, std, cx, cy, fill, label) {
|
|
66
|
+
const d = DOT_SIZE;
|
|
67
|
+
const dot = addShape(surface, cx - d / 2, cy - d / 2, d, d, {
|
|
68
|
+
shapeType: 'ellipse',
|
|
69
|
+
fill,
|
|
70
|
+
stroke: '#1f2328',
|
|
71
|
+
strokeWidth: 1.5,
|
|
72
|
+
});
|
|
73
|
+
if (!label)
|
|
74
|
+
return dot;
|
|
75
|
+
const lbl = addText(surface, cx + d / 2 + 6, cy - LABEL_FONT_SIZE / 2, 170, label, LABEL_COLOR, LABEL_FONT, LABEL_FONT_SIZE, 'left');
|
|
76
|
+
return groupIds(std, [dot, lbl]);
|
|
77
|
+
}
|
|
78
|
+
/** A free-floating label. */
|
|
79
|
+
export function addLabel(surface, cx, cy, label, align = 'center', color = LABEL_COLOR) {
|
|
80
|
+
return addText(surface, cx, cy, 200, label, color, LABEL_FONT, LABEL_FONT_SIZE, align);
|
|
81
|
+
}
|
|
82
|
+
/** A relationship connector (the single connector unit reused for all patterns). */
|
|
83
|
+
export function addConnector(surface, x1, y1, x2, y2, opts = {}) {
|
|
84
|
+
const { rearArrow = false, dashed = false, stroke = LABEL_COLOR, strokeWidth = 2 } = opts;
|
|
85
|
+
return surface.addElement({
|
|
86
|
+
type: 'connector',
|
|
87
|
+
mode: ConnectorMode.Straight,
|
|
88
|
+
stroke,
|
|
89
|
+
strokeWidth,
|
|
90
|
+
strokeStyle: dashed ? StrokeStyle.Dash : StrokeStyle.Solid,
|
|
91
|
+
frontEndpointStyle: PointStyle.None,
|
|
92
|
+
rearEndpointStyle: rearArrow ? PointStyle.Arrow : PointStyle.None,
|
|
93
|
+
source: { position: [x1, y1] },
|
|
94
|
+
target: { position: [x2, y2] },
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
/** A Team Topologies interaction-mode marker: a coloured square + centred letter. */
|
|
98
|
+
export function addMarker(surface, std, cx, cy, opts) {
|
|
99
|
+
const { fill, letter, label } = opts;
|
|
100
|
+
const s = MARKER_SIZE;
|
|
101
|
+
const box = addShape(surface, cx - s / 2, cy - s / 2, s, s, {
|
|
102
|
+
fill,
|
|
103
|
+
stroke: '#1f2328',
|
|
104
|
+
strokeWidth: 1.5,
|
|
105
|
+
radius: 4,
|
|
106
|
+
});
|
|
107
|
+
const glyph = addText(surface, cx - s / 2, cy - 9, s, letter, '#1f2328', LABEL_FONT, 15);
|
|
108
|
+
const ids = [box, glyph];
|
|
109
|
+
if (label) {
|
|
110
|
+
ids.push(addText(surface, cx + s / 2 + 6, cy - LABEL_FONT_SIZE / 2, 150, label, LABEL_COLOR, LABEL_FONT, LABEL_FONT_SIZE, 'left'));
|
|
111
|
+
}
|
|
112
|
+
return groupIds(std, ids);
|
|
113
|
+
}
|
|
114
|
+
/** A cloud (Big Ball of Mud / general-purpose boundary): a smoothed closed bezier polygon. */
|
|
115
|
+
export function addCloud(surface, std, cx, cy, label) {
|
|
116
|
+
const { w, h, fill, stroke } = CLOUD;
|
|
117
|
+
const cloud = surface.addElement({
|
|
118
|
+
type: 'shape',
|
|
119
|
+
shapeType: 'polygon',
|
|
120
|
+
vertices: CLOUD_VERTICES,
|
|
121
|
+
isClosed: true,
|
|
122
|
+
filled: true,
|
|
123
|
+
fillColor: fill,
|
|
124
|
+
strokeColor: stroke,
|
|
125
|
+
strokeWidth: 1.5,
|
|
126
|
+
shapeStyle: ShapeStyle.General,
|
|
127
|
+
roughness: 0,
|
|
128
|
+
xywh: new Bound(cx - w / 2, cy - h / 2, w, h).serialize(),
|
|
129
|
+
});
|
|
130
|
+
if (!label)
|
|
131
|
+
return cloud;
|
|
132
|
+
const lbl = addText(surface, cx - w / 2 + 12, cy - LABEL_FONT_SIZE / 2, w - 24, label, '#3d3d3d', LABEL_FONT, LABEL_FONT_SIZE);
|
|
133
|
+
return groupIds(std, [cloud, lbl]);
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* A Context Map relationship in DDD Crew notation: the connector, the pattern
|
|
137
|
+
* abbreviation in a small tag, and (for upstream/downstream patterns) the U / D
|
|
138
|
+
* markers — grouped so the whole pattern reads as one unit.
|
|
139
|
+
*/
|
|
140
|
+
export function addRelationship(surface, std, cx, cy, preset) {
|
|
141
|
+
const conn = addConnector(surface, cx - 130, cy, cx + 130, cy, {
|
|
142
|
+
rearArrow: preset.upDown,
|
|
143
|
+
dashed: preset.dashed,
|
|
144
|
+
});
|
|
145
|
+
const tag = addShape(surface, cx - 26, cy - 42, 52, 24, {
|
|
146
|
+
fill: '#ffffff',
|
|
147
|
+
stroke: '#6d6e71',
|
|
148
|
+
strokeWidth: 1,
|
|
149
|
+
radius: 4,
|
|
150
|
+
});
|
|
151
|
+
const tagText = addText(surface, cx - 26, cy - 36, 52, preset.abbrev, LABEL_COLOR, LABEL_FONT, 13);
|
|
152
|
+
const ids = [conn, tag, tagText];
|
|
153
|
+
if (preset.upDown) {
|
|
154
|
+
ids.push(addText(surface, cx - 150, cy - 30, 24, 'U', LABEL_COLOR, LABEL_FONT, 13));
|
|
155
|
+
ids.push(addText(surface, cx + 126, cy - 30, 24, 'D', LABEL_COLOR, LABEL_FONT, 13));
|
|
156
|
+
}
|
|
157
|
+
return groupIds(std, ids);
|
|
158
|
+
}
|
|
159
|
+
/**
|
|
160
|
+
* A boxed legend: a bordered container with a bold "Légende" title and bold
|
|
161
|
+
* section sub-titles, each row a swatch (dot / square+letter / line) + label.
|
|
162
|
+
* Shared by the Core Domain and Context Map tools. Returns the grouped id.
|
|
163
|
+
*/
|
|
164
|
+
export function addLegend(surface, std, x, y, opts) {
|
|
165
|
+
const W = opts.width ?? 260;
|
|
166
|
+
const PAD = 16;
|
|
167
|
+
const TITLE_H = 32;
|
|
168
|
+
const SUB_H = 26;
|
|
169
|
+
const ROW_H = 28;
|
|
170
|
+
const SW = 16;
|
|
171
|
+
let subs = 0;
|
|
172
|
+
let rows = 0;
|
|
173
|
+
for (const s of opts.sections) {
|
|
174
|
+
if (s.title)
|
|
175
|
+
subs++;
|
|
176
|
+
rows += s.rows.length;
|
|
177
|
+
}
|
|
178
|
+
const H = PAD * 2 + TITLE_H + subs * SUB_H + rows * ROW_H;
|
|
179
|
+
const ids = [
|
|
180
|
+
addShape(surface, x, y, W, H, {
|
|
181
|
+
fill: '#ffffff',
|
|
182
|
+
stroke: '#6d6e71',
|
|
183
|
+
strokeWidth: 1.5,
|
|
184
|
+
radius: 8,
|
|
185
|
+
}),
|
|
186
|
+
];
|
|
187
|
+
let cy = y + PAD;
|
|
188
|
+
ids.push(addText(surface, x + PAD, cy, W - PAD * 2, opts.title, LABEL_COLOR, LABEL_FONT, 18, 'left', true));
|
|
189
|
+
cy += TITLE_H;
|
|
190
|
+
for (const sec of opts.sections) {
|
|
191
|
+
if (sec.title) {
|
|
192
|
+
ids.push(addText(surface, x + PAD, cy, W - PAD * 2, sec.title, LABEL_COLOR, LABEL_FONT, 14, 'left', true));
|
|
193
|
+
cy += SUB_H;
|
|
194
|
+
}
|
|
195
|
+
for (const row of sec.rows) {
|
|
196
|
+
const sx = x + PAD;
|
|
197
|
+
const midY = cy + ROW_H / 2;
|
|
198
|
+
if (row.swatch === 'dot') {
|
|
199
|
+
ids.push(addShape(surface, sx, midY - SW / 2, SW, SW, { shapeType: 'ellipse', fill: row.color, stroke: '#1f2328', strokeWidth: 1 }));
|
|
200
|
+
}
|
|
201
|
+
else if (row.swatch === 'square') {
|
|
202
|
+
ids.push(addShape(surface, sx, midY - SW / 2, SW, SW, { fill: row.color, stroke: '#1f2328', strokeWidth: 1, radius: 3 }));
|
|
203
|
+
if (row.letter)
|
|
204
|
+
ids.push(addText(surface, sx, midY - 8, SW, row.letter, '#1f2328', LABEL_FONT, 11));
|
|
205
|
+
}
|
|
206
|
+
else {
|
|
207
|
+
ids.push(addShape(surface, sx, midY - 2, SW, 4, { fill: row.color, radius: 1 }));
|
|
208
|
+
}
|
|
209
|
+
ids.push(addText(surface, sx + SW + 10, midY - LABEL_FONT_SIZE / 2, W - PAD * 2 - SW - 10, row.label, LABEL_COLOR, LABEL_FONT, 13, 'left'));
|
|
210
|
+
cy += ROW_H;
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
return groupIds(std, ids);
|
|
214
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Uniform fit of a fixed reference design (`refW × refH`) into an element of
|
|
3
|
+
* size `w × h`: scale factor + centring offsets (letterboxed), undistorted.
|
|
4
|
+
*/
|
|
5
|
+
export declare function refScale(w: number, h: number, refW: number, refH: number): {
|
|
6
|
+
s: number;
|
|
7
|
+
ox: number;
|
|
8
|
+
oy: number;
|
|
9
|
+
};
|
|
10
|
+
export declare const FONT_FAMILY = "Inter, sans-serif";
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Uniform fit of a fixed reference design (`refW × refH`) into an element of
|
|
3
|
+
* size `w × h`: scale factor + centring offsets (letterboxed), undistorted.
|
|
4
|
+
*/
|
|
5
|
+
export function refScale(w, h, refW, refH) {
|
|
6
|
+
const s = Math.min(w / refW, h / refH);
|
|
7
|
+
return { s, ox: (w - refW * s) / 2, oy: (h - refH * s) / 2 };
|
|
8
|
+
}
|
|
9
|
+
export const FONT_FAMILY = 'Inter, sans-serif';
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { TemplateCategory } from '@formicoidea/labre-core/gfx/template';
|
|
2
|
+
/**
|
|
3
|
+
* One Templates-panel section per senior button. These are gated by
|
|
4
|
+
* `ddd-templates` only — never by a senior-button flag — so the catalogue stays
|
|
5
|
+
* available even when a senior button is hidden. They live in the shared
|
|
6
|
+
* package so the aggregate package can register all DDD categories under the
|
|
7
|
+
* single `ddd-templates` flag.
|
|
8
|
+
*/
|
|
9
|
+
export declare const eventStormingTemplateCategory: TemplateCategory;
|
|
10
|
+
export declare const coreDomainTemplateCategory: TemplateCategory;
|
|
11
|
+
export declare const contextMapTemplateCategory: TemplateCategory;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { CD_TEMPLATES, CM_TEMPLATES, ES_TEMPLATES } from './components.js';
|
|
2
|
+
/**
|
|
3
|
+
* One Templates-panel section per senior button. These are gated by
|
|
4
|
+
* `ddd-templates` only — never by a senior-button flag — so the catalogue stays
|
|
5
|
+
* available even when a senior button is hidden. They live in the shared
|
|
6
|
+
* package so the aggregate package can register all DDD categories under the
|
|
7
|
+
* single `ddd-templates` flag.
|
|
8
|
+
*/
|
|
9
|
+
export const eventStormingTemplateCategory = {
|
|
10
|
+
name: 'Event Storming',
|
|
11
|
+
templates: ES_TEMPLATES,
|
|
12
|
+
};
|
|
13
|
+
export const coreDomainTemplateCategory = {
|
|
14
|
+
name: 'Core Domain Chart',
|
|
15
|
+
templates: CD_TEMPLATES,
|
|
16
|
+
};
|
|
17
|
+
export const contextMapTemplateCategory = {
|
|
18
|
+
name: 'Context Map',
|
|
19
|
+
templates: CM_TEMPLATES,
|
|
20
|
+
};
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { type Template } from '@formicoidea/labre-core/gfx/template';
|
|
2
|
+
/** Event Storming: one template per sticky kind + the hotspot. */
|
|
3
|
+
export declare const ES_TEMPLATES: Template[];
|
|
4
|
+
/** Core Domain: the chart background, the sub-domain dots, the team-topology markers, the movement arrow. */
|
|
5
|
+
export declare const CD_TEMPLATES: Template[];
|
|
6
|
+
/** Context Map: the bounded-context bubble, the cloud, the nine relationship patterns. */
|
|
7
|
+
export declare const CM_TEMPLATES: Template[];
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
import { makeTemplateSnapshot, surfaceText, } from '@formicoidea/labre-core/gfx/template';
|
|
2
|
+
import { ConnectorMode, FontFamily, PointStyle, ShapeStyle, StrokeStyle, TextAlign, } from '@formicoidea/labre-core/model';
|
|
3
|
+
import { CD_SUBDOMAINS, CLOUD, CLOUD_VERTICES, CM_BUBBLE, CM_RELATIONSHIPS, ES_HOTSPOT, ES_STICKIES, MOVEMENT_COLOR, TEAM_TOPOLOGIES, } from '../shared/consts';
|
|
4
|
+
/**
|
|
5
|
+
* Every DDD component (the things the three senior menus create) re-expressed as
|
|
6
|
+
* an insertable Template, so the whole toolbox is also available from the
|
|
7
|
+
* Templates panel. Authored as static surface-element JSON from the SAME consts
|
|
8
|
+
* the live prefabs use, so colours/labels stay in sync.
|
|
9
|
+
*/
|
|
10
|
+
const ATTRS = 'width="100%" height="100%" viewBox="0 0 135 80" xmlns="http://www.w3.org/2000/svg"';
|
|
11
|
+
const NO_STROKE = '#00000000';
|
|
12
|
+
function tpl(name, preview, elements) {
|
|
13
|
+
return { name, type: 'template', preview, content: makeTemplateSnapshot(elements, name) };
|
|
14
|
+
}
|
|
15
|
+
function stickyEl(fill, text, label, shapeType) {
|
|
16
|
+
return {
|
|
17
|
+
type: 'shape',
|
|
18
|
+
shapeType,
|
|
19
|
+
filled: true,
|
|
20
|
+
fillColor: fill,
|
|
21
|
+
strokeColor: NO_STROKE,
|
|
22
|
+
strokeWidth: 0,
|
|
23
|
+
shapeStyle: ShapeStyle.General,
|
|
24
|
+
roughness: 0,
|
|
25
|
+
radius: shapeType === 'rect' ? 6 : 0,
|
|
26
|
+
text: surfaceText(label),
|
|
27
|
+
color: text,
|
|
28
|
+
fontFamily: FontFamily.Kalam,
|
|
29
|
+
fontSize: 20,
|
|
30
|
+
textAlign: TextAlign.Center,
|
|
31
|
+
xywh: '[0,0,120,120]',
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
const sq = (c) => `<svg ${ATTRS}><rect x="38" y="13" width="60" height="54" rx="5" fill="${c}"/></svg>`;
|
|
35
|
+
const dia = (c) => `<svg ${ATTRS}><rect x="48" y="20" width="40" height="40" transform="rotate(45 68 40)" fill="${c}"/></svg>`;
|
|
36
|
+
const circ = (c) => `<svg ${ATTRS}><circle cx="68" cy="40" r="22" fill="${c}" stroke="#1f2328" stroke-width="1.5"/></svg>`;
|
|
37
|
+
const mk = (c, l) => `<svg ${ATTRS}><rect x="48" y="20" width="40" height="40" rx="5" fill="${c}" stroke="#1f2328" stroke-width="1.5"/><text x="68" y="47" text-anchor="middle" font-size="20" fill="#1f2328">${l}</text></svg>`;
|
|
38
|
+
const line = (dashed, arrow) => `<svg ${ATTRS} fill="none"><path d="M20 40 H${arrow ? 100 : 115}" stroke="#1f2328" stroke-width="2" stroke-dasharray="${dashed ? '5 4' : '0'}"/>${arrow ? '<path d="M98 32 L114 40 L98 48" stroke="#1f2328" stroke-width="2"/>' : ''}</svg>`;
|
|
39
|
+
/** Event Storming: one template per sticky kind + the hotspot. */
|
|
40
|
+
export const ES_TEMPLATES = [
|
|
41
|
+
...ES_STICKIES.map(p => tpl(`Event Storming — ${p.label}`, sq(p.fill), { s: stickyEl(p.fill, p.text, p.label, 'rect') })),
|
|
42
|
+
tpl('Event Storming — Hotspot', dia(ES_HOTSPOT.fill), {
|
|
43
|
+
s: stickyEl(ES_HOTSPOT.fill, ES_HOTSPOT.text, ES_HOTSPOT.label, 'diamond'),
|
|
44
|
+
}),
|
|
45
|
+
];
|
|
46
|
+
/** Core Domain: the chart background, the sub-domain dots, the team-topology markers, the movement arrow. */
|
|
47
|
+
export const CD_TEMPLATES = [
|
|
48
|
+
tpl('Core Domain Chart', `<svg ${ATTRS} fill="none"><rect x="10" y="6" width="115" height="62" fill="#4d9900" fill-opacity="0.5"/><rect x="10" y="6" width="22" height="62" fill="#9933ff" fill-opacity="0.5"/><path d="M10 68 V6 M10 68 H125" stroke="#1f2328" stroke-width="1.6"/></svg>`, { bg: { type: 'coreDomain', xywh: '[0,0,900,820]' } }),
|
|
49
|
+
...CD_SUBDOMAINS.map(d => tpl(`Core Domain — ${d.label}`, circ(d.fill), {
|
|
50
|
+
dot: {
|
|
51
|
+
type: 'shape',
|
|
52
|
+
shapeType: 'ellipse',
|
|
53
|
+
filled: true,
|
|
54
|
+
fillColor: d.fill,
|
|
55
|
+
strokeColor: '#1f2328',
|
|
56
|
+
strokeWidth: 1.5,
|
|
57
|
+
shapeStyle: ShapeStyle.General,
|
|
58
|
+
roughness: 0,
|
|
59
|
+
xywh: '[0,0,26,26]',
|
|
60
|
+
},
|
|
61
|
+
})),
|
|
62
|
+
...TEAM_TOPOLOGIES.map(t => tpl(`Team topology — ${t.label}`, mk(t.fill, t.letter), {
|
|
63
|
+
m: {
|
|
64
|
+
type: 'shape',
|
|
65
|
+
shapeType: 'rect',
|
|
66
|
+
filled: true,
|
|
67
|
+
fillColor: t.fill,
|
|
68
|
+
strokeColor: '#1f2328',
|
|
69
|
+
strokeWidth: 1.5,
|
|
70
|
+
shapeStyle: ShapeStyle.General,
|
|
71
|
+
roughness: 0,
|
|
72
|
+
radius: 4,
|
|
73
|
+
text: surfaceText(t.letter),
|
|
74
|
+
color: '#1f2328',
|
|
75
|
+
fontFamily: FontFamily.Inter,
|
|
76
|
+
fontSize: 15,
|
|
77
|
+
textAlign: TextAlign.Center,
|
|
78
|
+
xywh: '[0,0,30,30]',
|
|
79
|
+
},
|
|
80
|
+
})),
|
|
81
|
+
tpl('Core Domain — Movement over time', line(true, true), {
|
|
82
|
+
c: {
|
|
83
|
+
type: 'connector',
|
|
84
|
+
mode: ConnectorMode.Straight,
|
|
85
|
+
stroke: MOVEMENT_COLOR,
|
|
86
|
+
strokeWidth: 2,
|
|
87
|
+
strokeStyle: StrokeStyle.Dash,
|
|
88
|
+
frontEndpointStyle: PointStyle.None,
|
|
89
|
+
rearEndpointStyle: PointStyle.Arrow,
|
|
90
|
+
source: { position: [0, 80] },
|
|
91
|
+
target: { position: [160, 0] },
|
|
92
|
+
},
|
|
93
|
+
}),
|
|
94
|
+
];
|
|
95
|
+
/** Context Map: the bounded-context bubble, the cloud, the nine relationship patterns. */
|
|
96
|
+
export const CM_TEMPLATES = [
|
|
97
|
+
tpl('Context Map — Bounded Context', `<svg ${ATTRS} fill="none"><rect x="20" y="25" width="95" height="30" rx="15" fill="${CM_BUBBLE.fill}" stroke="${CM_BUBBLE.stroke}" stroke-width="1.6"/></svg>`, {
|
|
98
|
+
b: {
|
|
99
|
+
type: 'shape',
|
|
100
|
+
shapeType: 'rect',
|
|
101
|
+
filled: true,
|
|
102
|
+
fillColor: CM_BUBBLE.fill,
|
|
103
|
+
strokeColor: CM_BUBBLE.stroke,
|
|
104
|
+
strokeWidth: 1.5,
|
|
105
|
+
shapeStyle: ShapeStyle.General,
|
|
106
|
+
roughness: 0,
|
|
107
|
+
radius: CM_BUBBLE.radius,
|
|
108
|
+
text: surfaceText('Bounded Context'),
|
|
109
|
+
color: CM_BUBBLE.text,
|
|
110
|
+
fontFamily: FontFamily.Inter,
|
|
111
|
+
fontSize: 14,
|
|
112
|
+
textAlign: TextAlign.Center,
|
|
113
|
+
xywh: `[0,0,${CM_BUBBLE.w},${CM_BUBBLE.h}]`,
|
|
114
|
+
},
|
|
115
|
+
}),
|
|
116
|
+
tpl('Context Map — Cloud / System', `<svg ${ATTRS} fill="none"><path d="M30 52 C18 52 16 40 26 37 C24 25 42 24 46 32 C50 22 70 24 70 35 C84 32 90 46 78 50 Z" fill="${CLOUD.fill}" stroke="${CLOUD.stroke}" stroke-width="1.4"/></svg>`, {
|
|
117
|
+
c: {
|
|
118
|
+
type: 'shape',
|
|
119
|
+
shapeType: 'polygon',
|
|
120
|
+
vertices: CLOUD_VERTICES,
|
|
121
|
+
isClosed: true,
|
|
122
|
+
filled: true,
|
|
123
|
+
fillColor: CLOUD.fill,
|
|
124
|
+
strokeColor: CLOUD.stroke,
|
|
125
|
+
strokeWidth: 1.5,
|
|
126
|
+
shapeStyle: ShapeStyle.General,
|
|
127
|
+
roughness: 0,
|
|
128
|
+
text: surfaceText('System'),
|
|
129
|
+
color: '#3d3d3d',
|
|
130
|
+
fontFamily: FontFamily.Inter,
|
|
131
|
+
fontSize: 14,
|
|
132
|
+
textAlign: TextAlign.Center,
|
|
133
|
+
xywh: `[0,0,${CLOUD.w},${CLOUD.h}]`,
|
|
134
|
+
},
|
|
135
|
+
}),
|
|
136
|
+
...CM_RELATIONSHIPS.map(r => tpl(`Context Map — ${r.label}`, line(r.dashed, r.upDown), {
|
|
137
|
+
c: {
|
|
138
|
+
type: 'connector',
|
|
139
|
+
mode: ConnectorMode.Straight,
|
|
140
|
+
stroke: '#1f2328',
|
|
141
|
+
strokeWidth: 2,
|
|
142
|
+
strokeStyle: r.dashed ? StrokeStyle.Dash : StrokeStyle.Solid,
|
|
143
|
+
frontEndpointStyle: PointStyle.None,
|
|
144
|
+
rearEndpointStyle: r.upDown ? PointStyle.Arrow : PointStyle.None,
|
|
145
|
+
source: { position: [0, 0] },
|
|
146
|
+
target: { position: [240, 0] },
|
|
147
|
+
},
|
|
148
|
+
t: {
|
|
149
|
+
type: 'text',
|
|
150
|
+
text: surfaceText(r.abbrev),
|
|
151
|
+
color: '#1f2328',
|
|
152
|
+
fontFamily: FontFamily.Inter,
|
|
153
|
+
fontSize: 13,
|
|
154
|
+
textAlign: TextAlign.Center,
|
|
155
|
+
xywh: '[94,-30,52,22]',
|
|
156
|
+
},
|
|
157
|
+
})),
|
|
158
|
+
];
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/** Senior-button glyph — Event Storming (overlapping stickies). */
|
|
2
|
+
export declare const eventStormingToolbarIcon: import("lit-html").TemplateResult<2>;
|
|
3
|
+
/** Senior-button glyph — Core Domain Chart (axes + bands). */
|
|
4
|
+
export declare const coreDomainToolbarIcon: import("lit-html").TemplateResult<2>;
|
|
5
|
+
/** Senior-button glyph — Context Map (two bubbles + relation). */
|
|
6
|
+
export declare const contextMapToolbarIcon: import("lit-html").TemplateResult<2>;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { svg } from 'lit';
|
|
2
|
+
/** Senior-button glyph — Event Storming (overlapping stickies). */
|
|
3
|
+
export const eventStormingToolbarIcon = svg `<svg width="100%" height="100%" viewBox="0 0 56 56" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
4
|
+
<rect x="10" y="14" width="22" height="22" rx="2" transform="rotate(-6 21 25)" fill="#F5963B"/>
|
|
5
|
+
<rect x="24" y="18" width="22" height="22" rx="2" transform="rotate(5 35 29)" fill="#5BA3DB"/>
|
|
6
|
+
<rect x="17" y="26" width="22" height="22" rx="2" transform="rotate(-3 28 37)" fill="#FFD84D"/>
|
|
7
|
+
</svg>`;
|
|
8
|
+
/** Senior-button glyph — Core Domain Chart (axes + bands). */
|
|
9
|
+
export const coreDomainToolbarIcon = svg `<svg width="100%" height="100%" viewBox="0 0 56 56" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
10
|
+
<rect x="10" y="8" width="38" height="38" fill="#4d9900" fill-opacity="0.55"/>
|
|
11
|
+
<rect x="10" y="8" width="14" height="38" fill="#9933ff" fill-opacity="0.55"/>
|
|
12
|
+
<path d="M10 46 V8 M10 46 H48" stroke="#1f2328" stroke-width="2.4" stroke-linecap="round"/>
|
|
13
|
+
<circle cx="38" cy="18" r="4" fill="#9933ff"/>
|
|
14
|
+
</svg>`;
|
|
15
|
+
/** Senior-button glyph — Context Map (two bubbles + relation). */
|
|
16
|
+
export const contextMapToolbarIcon = svg `<svg width="100%" height="100%" viewBox="0 0 56 56" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
17
|
+
<rect x="6" y="10" width="22" height="14" rx="7" fill="#e6f0fa" stroke="#2f6fb0" stroke-width="1.6"/>
|
|
18
|
+
<rect x="28" y="32" width="22" height="14" rx="7" fill="#e6f0fa" stroke="#2f6fb0" stroke-width="1.6"/>
|
|
19
|
+
<path d="M20 24 L34 32" stroke="#1f2328" stroke-width="1.8"/>
|
|
20
|
+
</svg>`;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { EmptyTool } from '@formicoidea/labre-core/gfx/pointer';
|
|
2
|
+
import { LitElement } from 'lit';
|
|
3
|
+
declare const DddMenuBase_base: typeof LitElement & import("@formicoidea/labre-core/_pkgs/global/utils").Constructor<import("@formicoidea/labre-core/_pkgs/affine-widget-edgeless-toolbar").EdgelessToolbarToolClass>;
|
|
4
|
+
/**
|
|
5
|
+
* Shared base for the three DDD popover menus: the menu shell styles, the
|
|
6
|
+
* "create then return to selection (palette stays open)" finish helper and
|
|
7
|
+
* the telemetry track helper. Each subclass adds its own create* methods (built
|
|
8
|
+
* from the shared prefab builders) and its own render().
|
|
9
|
+
*/
|
|
10
|
+
export declare abstract class DddMenuBase extends DddMenuBase_base {
|
|
11
|
+
static styles: import("lit").CSSResult;
|
|
12
|
+
type: typeof EmptyTool;
|
|
13
|
+
/** Framework id for telemetry. */
|
|
14
|
+
protected abstract framework: 'event-storming' | 'core-domain' | 'context-map';
|
|
15
|
+
protected get surface(): import("@formicoidea/labre-core/std/gfx").SurfaceBlockModel | null;
|
|
16
|
+
protected get center(): {
|
|
17
|
+
cx: number;
|
|
18
|
+
cy: number;
|
|
19
|
+
};
|
|
20
|
+
protected finish(id: string): void;
|
|
21
|
+
protected track(event: 'FrameworkElementAdded' | 'FrameworkToolPicked', element: string): void;
|
|
22
|
+
}
|
|
23
|
+
export {};
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { DefaultTool } from '@formicoidea/labre-core/blocks/surface';
|
|
2
|
+
import { EmptyTool } from '@formicoidea/labre-core/gfx/pointer';
|
|
3
|
+
import { TelemetryProvider } from '@formicoidea/labre-core/shared/services';
|
|
4
|
+
import { EdgelessToolbarToolMixin } from '@formicoidea/labre-core/widgets/edgeless-toolbar';
|
|
5
|
+
import { css, LitElement } from 'lit';
|
|
6
|
+
/**
|
|
7
|
+
* Shared base for the three DDD popover menus: the menu shell styles, the
|
|
8
|
+
* "create then return to selection (palette stays open)" finish helper and
|
|
9
|
+
* the telemetry track helper. Each subclass adds its own create* methods (built
|
|
10
|
+
* from the shared prefab builders) and its own render().
|
|
11
|
+
*/
|
|
12
|
+
export class DddMenuBase extends EdgelessToolbarToolMixin(LitElement) {
|
|
13
|
+
constructor() {
|
|
14
|
+
super(...arguments);
|
|
15
|
+
this.type = EmptyTool;
|
|
16
|
+
}
|
|
17
|
+
static { this.styles = css `
|
|
18
|
+
:host {
|
|
19
|
+
position: absolute;
|
|
20
|
+
display: flex;
|
|
21
|
+
z-index: -1;
|
|
22
|
+
}
|
|
23
|
+
.menu-content {
|
|
24
|
+
display: flex;
|
|
25
|
+
align-items: center;
|
|
26
|
+
justify-content: center;
|
|
27
|
+
}
|
|
28
|
+
.button-group-container {
|
|
29
|
+
display: flex;
|
|
30
|
+
align-items: center;
|
|
31
|
+
gap: 10px;
|
|
32
|
+
fill: var(--affine-icon-color);
|
|
33
|
+
}
|
|
34
|
+
.button-group-container svg {
|
|
35
|
+
width: 24px;
|
|
36
|
+
height: 24px;
|
|
37
|
+
}
|
|
38
|
+
`; }
|
|
39
|
+
get surface() {
|
|
40
|
+
return this.gfx.surface;
|
|
41
|
+
}
|
|
42
|
+
get center() {
|
|
43
|
+
const { centerX, centerY } = this.gfx.viewport;
|
|
44
|
+
return { cx: centerX, cy: centerY };
|
|
45
|
+
}
|
|
46
|
+
finish(id) {
|
|
47
|
+
const { gfx } = this;
|
|
48
|
+
gfx.doc.captureSync();
|
|
49
|
+
gfx.tool.setTool(DefaultTool);
|
|
50
|
+
gfx.selection.set({ elements: [id], editing: false });
|
|
51
|
+
// Palette stays open so several objects can be added in a row.
|
|
52
|
+
}
|
|
53
|
+
track(event, element) {
|
|
54
|
+
this.edgeless.std.getOptional(TelemetryProvider)?.track(event, {
|
|
55
|
+
framework: this.framework,
|
|
56
|
+
element,
|
|
57
|
+
page: 'whiteboard editor',
|
|
58
|
+
segment: 'ddd toolbox',
|
|
59
|
+
module: `${this.framework} menu`,
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { EmptyTool } from '@formicoidea/labre-core/gfx/pointer';
|
|
2
|
+
import { LitElement, type TemplateResult } from 'lit';
|
|
3
|
+
declare const DddSeniorButtonBase_base: typeof LitElement & import("@formicoidea/labre-core/_pkgs/global/utils").Constructor<import("@formicoidea/labre-core/_pkgs/affine-widget-edgeless-toolbar").EdgelessToolbarToolClass>;
|
|
4
|
+
/**
|
|
5
|
+
* Shared base for the three DDD senior buttons. Each subclass only differs by
|
|
6
|
+
* the popover menu it opens, its tooltip and its glyph — the toggle / popper
|
|
7
|
+
* wiring is identical (mirrors the Cynefin senior button). The concrete
|
|
8
|
+
* subclasses live in their respective tool packages.
|
|
9
|
+
*/
|
|
10
|
+
export declare abstract class DddSeniorButtonBase extends DddSeniorButtonBase_base {
|
|
11
|
+
static styles: import("lit").CSSResult;
|
|
12
|
+
enableActiveBackground: boolean;
|
|
13
|
+
type: typeof EmptyTool;
|
|
14
|
+
protected abstract menuTag: keyof HTMLElementTagNameMap;
|
|
15
|
+
protected abstract label: string;
|
|
16
|
+
protected abstract icon: TemplateResult;
|
|
17
|
+
private _toggleMenu;
|
|
18
|
+
render(): TemplateResult<1>;
|
|
19
|
+
}
|
|
20
|
+
export {};
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { DefaultTool } from '@formicoidea/labre-core/blocks/surface';
|
|
2
|
+
import { EmptyTool } from '@formicoidea/labre-core/gfx/pointer';
|
|
3
|
+
import { EdgelessToolbarToolMixin } from '@formicoidea/labre-core/widgets/edgeless-toolbar';
|
|
4
|
+
import { SignalWatcher } from '@formicoidea/labre-core/global/lit';
|
|
5
|
+
import { css, html, LitElement } from 'lit';
|
|
6
|
+
/**
|
|
7
|
+
* Shared base for the three DDD senior buttons. Each subclass only differs by
|
|
8
|
+
* the popover menu it opens, its tooltip and its glyph — the toggle / popper
|
|
9
|
+
* wiring is identical (mirrors the Cynefin senior button). The concrete
|
|
10
|
+
* subclasses live in their respective tool packages.
|
|
11
|
+
*/
|
|
12
|
+
export class DddSeniorButtonBase extends EdgelessToolbarToolMixin(SignalWatcher(LitElement)) {
|
|
13
|
+
constructor() {
|
|
14
|
+
super(...arguments);
|
|
15
|
+
this.enableActiveBackground = true;
|
|
16
|
+
this.type = EmptyTool;
|
|
17
|
+
}
|
|
18
|
+
static { this.styles = css `
|
|
19
|
+
:host,
|
|
20
|
+
.ddd-button {
|
|
21
|
+
display: block;
|
|
22
|
+
width: 100%;
|
|
23
|
+
height: 100%;
|
|
24
|
+
}
|
|
25
|
+
:host {
|
|
26
|
+
position: relative;
|
|
27
|
+
}
|
|
28
|
+
.ddd-root {
|
|
29
|
+
width: 100%;
|
|
30
|
+
height: 64px;
|
|
31
|
+
cursor: pointer;
|
|
32
|
+
display: flex;
|
|
33
|
+
align-items: flex-end;
|
|
34
|
+
justify-content: center;
|
|
35
|
+
}
|
|
36
|
+
.ddd-card {
|
|
37
|
+
--y: -4px;
|
|
38
|
+
--s: 1;
|
|
39
|
+
width: 50px;
|
|
40
|
+
height: 50px;
|
|
41
|
+
margin-bottom: 4px;
|
|
42
|
+
transform: translateY(var(--y)) scale(var(--s));
|
|
43
|
+
transition: transform 0.3s ease;
|
|
44
|
+
}
|
|
45
|
+
.ddd-card svg {
|
|
46
|
+
display: block;
|
|
47
|
+
width: 100%;
|
|
48
|
+
height: 100%;
|
|
49
|
+
}
|
|
50
|
+
.ddd-root:hover .ddd-card {
|
|
51
|
+
--y: -10px;
|
|
52
|
+
--s: 1.07;
|
|
53
|
+
}
|
|
54
|
+
`; }
|
|
55
|
+
_toggleMenu() {
|
|
56
|
+
if (this.popper) {
|
|
57
|
+
this.popper.dispose();
|
|
58
|
+
this.popper = null;
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
this.setEdgelessTool(DefaultTool);
|
|
62
|
+
const menu = this.createPopper(this.menuTag, this);
|
|
63
|
+
menu.element.edgeless = this.edgeless;
|
|
64
|
+
const el = menu.element;
|
|
65
|
+
const wrap = el.parentElement;
|
|
66
|
+
if (wrap) {
|
|
67
|
+
wrap.style.overflow = 'visible';
|
|
68
|
+
wrap.style.justifyContent = 'flex-end';
|
|
69
|
+
}
|
|
70
|
+
Object.assign(el.style, {
|
|
71
|
+
position: 'static',
|
|
72
|
+
width: 'max-content',
|
|
73
|
+
maxWidth: 'calc(100vw - 16px)',
|
|
74
|
+
marginLeft: '0',
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
render() {
|
|
78
|
+
return html `<edgeless-toolbar-button
|
|
79
|
+
class="ddd-button"
|
|
80
|
+
.tooltip=${this.popper ? '' : this.label}
|
|
81
|
+
.tooltipOffset=${4}
|
|
82
|
+
.active=${!!this.popper}
|
|
83
|
+
@click=${this._toggleMenu}
|
|
84
|
+
>
|
|
85
|
+
<div class="ddd-root">
|
|
86
|
+
<div class="ddd-card">${this.icon}</div>
|
|
87
|
+
</div>
|
|
88
|
+
</edgeless-toolbar-button>`;
|
|
89
|
+
}
|
|
90
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@formicoidea/labre-ddd-shared",
|
|
3
|
+
"description": "Labre ddd-shared — shared building blocks for @formicoidea/labre-core frameworks.",
|
|
4
|
+
"version": "0.25.0",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"sideEffects": false,
|
|
7
|
+
"author": "lajola",
|
|
8
|
+
"contributors": [
|
|
9
|
+
"toeverything"
|
|
10
|
+
],
|
|
11
|
+
"license": "MPL-2.0",
|
|
12
|
+
"exports": {
|
|
13
|
+
".": {
|
|
14
|
+
"types": "./dist/index.d.ts",
|
|
15
|
+
"import": "./dist/index.js"
|
|
16
|
+
}
|
|
17
|
+
},
|
|
18
|
+
"files": [
|
|
19
|
+
"dist"
|
|
20
|
+
],
|
|
21
|
+
"dependencies": {
|
|
22
|
+
"@formicoidea/labre-core": "0.25.0",
|
|
23
|
+
"lit": "^3.2.0"
|
|
24
|
+
}
|
|
25
|
+
}
|