@formicoidea/labre-framework-wardley 0.23.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/package.json +24 -0
- package/src/consts.ts +72 -0
- package/src/descriptor.ts +8 -0
- package/src/effects.ts +17 -0
- package/src/element-renderer.ts +242 -0
- package/src/element-view.ts +143 -0
- package/src/gradient.ts +137 -0
- package/src/index.ts +1 -0
- package/src/label-layout.ts +126 -0
- package/src/legend.ts +438 -0
- package/src/node/consts.ts +109 -0
- package/src/node/node-renderer.ts +142 -0
- package/src/node/node-view.ts +11 -0
- package/src/templates/index.ts +236 -0
- package/src/templates/maps.ts +283 -0
- package/src/toolbar/config.ts +280 -0
- package/src/toolbar/icons.ts +150 -0
- package/src/toolbar/node-config.ts +28 -0
- package/src/toolbar/senior-tool.ts +11 -0
- package/src/toolbar/wardley-menu.ts +552 -0
- package/src/toolbar/wardley-senior-button.ts +154 -0
- package/src/view.ts +39 -0
|
@@ -0,0 +1,280 @@
|
|
|
1
|
+
import { EdgelessCRUDIdentifier } from '@formicoidea/labre-core/blocks/surface';
|
|
2
|
+
import { WardleyBackgroundElementModel } from '@formicoidea/labre-core/model';
|
|
3
|
+
import {
|
|
4
|
+
TelemetryProvider,
|
|
5
|
+
type ToolbarContext,
|
|
6
|
+
type ToolbarModuleConfig,
|
|
7
|
+
ToolbarModuleExtension,
|
|
8
|
+
} from '@formicoidea/labre-core/shared/services';
|
|
9
|
+
import { BlockFlavourIdentifier } from '@formicoidea/labre-core/std';
|
|
10
|
+
import { html, type TemplateResult } from 'lit';
|
|
11
|
+
|
|
12
|
+
import { createWardleyLegend } from '../legend';
|
|
13
|
+
import { wardleyLegendIcon } from './icons';
|
|
14
|
+
|
|
15
|
+
const ResizeIcon = html`<svg
|
|
16
|
+
width="24"
|
|
17
|
+
height="24"
|
|
18
|
+
viewBox="0 0 24 24"
|
|
19
|
+
fill="none"
|
|
20
|
+
stroke="currentColor"
|
|
21
|
+
stroke-width="1.6"
|
|
22
|
+
stroke-linecap="round"
|
|
23
|
+
stroke-linejoin="round"
|
|
24
|
+
>
|
|
25
|
+
<path d="M9 5H5v4M15 19h4v-4" />
|
|
26
|
+
<path d="M5 5l6 6M19 19l-6-6" />
|
|
27
|
+
</svg>`;
|
|
28
|
+
|
|
29
|
+
/** Gradient swatch — show/hide the variant gradient. */
|
|
30
|
+
const GradientIcon = html`<svg
|
|
31
|
+
width="24"
|
|
32
|
+
height="24"
|
|
33
|
+
viewBox="0 0 24 24"
|
|
34
|
+
fill="none"
|
|
35
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
36
|
+
>
|
|
37
|
+
<defs>
|
|
38
|
+
<linearGradient id="wardleyToolbarGrad" x1="0" y1="0" x2="1" y2="0">
|
|
39
|
+
<stop offset="0" stop-color="currentColor" stop-opacity="0.85" />
|
|
40
|
+
<stop offset="1" stop-color="currentColor" stop-opacity="0.1" />
|
|
41
|
+
</linearGradient>
|
|
42
|
+
</defs>
|
|
43
|
+
<rect
|
|
44
|
+
x="4"
|
|
45
|
+
y="5"
|
|
46
|
+
width="16"
|
|
47
|
+
height="14"
|
|
48
|
+
rx="2"
|
|
49
|
+
fill="url(#wardleyToolbarGrad)"
|
|
50
|
+
stroke="currentColor"
|
|
51
|
+
stroke-width="1.4"
|
|
52
|
+
/>
|
|
53
|
+
</svg>`;
|
|
54
|
+
|
|
55
|
+
/** Horizontal axis with a right-pointing arrow (Evolution / X). */
|
|
56
|
+
const XAxisIcon = html`<svg
|
|
57
|
+
width="24"
|
|
58
|
+
height="24"
|
|
59
|
+
viewBox="0 0 24 24"
|
|
60
|
+
fill="none"
|
|
61
|
+
stroke="currentColor"
|
|
62
|
+
stroke-width="1.6"
|
|
63
|
+
stroke-linecap="round"
|
|
64
|
+
stroke-linejoin="round"
|
|
65
|
+
>
|
|
66
|
+
<path d="M3 17h15" />
|
|
67
|
+
<path d="M15 14l3 3-3 3" />
|
|
68
|
+
</svg>`;
|
|
69
|
+
|
|
70
|
+
/** Vertical axis with an up-pointing arrow (Value Chain / Y). */
|
|
71
|
+
const YAxisIcon = html`<svg
|
|
72
|
+
width="24"
|
|
73
|
+
height="24"
|
|
74
|
+
viewBox="0 0 24 24"
|
|
75
|
+
fill="none"
|
|
76
|
+
stroke="currentColor"
|
|
77
|
+
stroke-width="1.6"
|
|
78
|
+
stroke-linecap="round"
|
|
79
|
+
stroke-linejoin="round"
|
|
80
|
+
>
|
|
81
|
+
<path d="M7 21V6" />
|
|
82
|
+
<path d="M4 9l3-3 3 3" />
|
|
83
|
+
</svg>`;
|
|
84
|
+
|
|
85
|
+
/** Dashed vertical column dividers. */
|
|
86
|
+
const ColumnsIcon = html`<svg
|
|
87
|
+
width="24"
|
|
88
|
+
height="24"
|
|
89
|
+
viewBox="0 0 24 24"
|
|
90
|
+
fill="none"
|
|
91
|
+
stroke="currentColor"
|
|
92
|
+
stroke-width="1.6"
|
|
93
|
+
stroke-linecap="round"
|
|
94
|
+
stroke-dasharray="3 3"
|
|
95
|
+
>
|
|
96
|
+
<path d="M9 4v16M15 4v16" />
|
|
97
|
+
</svg>`;
|
|
98
|
+
|
|
99
|
+
/** Column labels (short bars under the columns). */
|
|
100
|
+
const ColumnLabelsIcon = html`<svg
|
|
101
|
+
width="24"
|
|
102
|
+
height="24"
|
|
103
|
+
viewBox="0 0 24 24"
|
|
104
|
+
fill="currentColor"
|
|
105
|
+
stroke="none"
|
|
106
|
+
>
|
|
107
|
+
<rect x="3" y="15" width="4.5" height="3" rx="1" />
|
|
108
|
+
<rect x="9.75" y="15" width="4.5" height="3" rx="1" />
|
|
109
|
+
<rect x="16.5" y="15" width="4.5" height="3" rx="1" />
|
|
110
|
+
</svg>`;
|
|
111
|
+
|
|
112
|
+
/** Corner labels (Uncharted / Industrialized, top corners). */
|
|
113
|
+
const CornerLabelsIcon = html`<svg
|
|
114
|
+
width="24"
|
|
115
|
+
height="24"
|
|
116
|
+
viewBox="0 0 24 24"
|
|
117
|
+
fill="currentColor"
|
|
118
|
+
stroke="none"
|
|
119
|
+
>
|
|
120
|
+
<rect x="3" y="5" width="6" height="3" rx="1" />
|
|
121
|
+
<rect x="15" y="5" width="6" height="3" rx="1" />
|
|
122
|
+
</svg>`;
|
|
123
|
+
|
|
124
|
+
/** Visibility labels (Visible / Invisible) — an eye. */
|
|
125
|
+
const VisibilityIcon = html`<svg
|
|
126
|
+
width="24"
|
|
127
|
+
height="24"
|
|
128
|
+
viewBox="0 0 24 24"
|
|
129
|
+
fill="none"
|
|
130
|
+
stroke="currentColor"
|
|
131
|
+
stroke-width="1.6"
|
|
132
|
+
stroke-linecap="round"
|
|
133
|
+
stroke-linejoin="round"
|
|
134
|
+
>
|
|
135
|
+
<path d="M2 12s3.5-6 10-6 10 6 10 6-3.5 6-10 6S2 12 2 12z" />
|
|
136
|
+
<circle cx="12" cy="12" r="2.4" />
|
|
137
|
+
</svg>`;
|
|
138
|
+
|
|
139
|
+
type WardleyToggleProp =
|
|
140
|
+
| 'resizeEnabled'
|
|
141
|
+
| 'showGradient'
|
|
142
|
+
| 'showXAxis'
|
|
143
|
+
| 'showYAxis'
|
|
144
|
+
| 'showColumnDividers'
|
|
145
|
+
| 'showColumnLabels'
|
|
146
|
+
| 'showCornerLabels'
|
|
147
|
+
| 'showVisibilityLabels';
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* Build a toolbar toggle that flips a boolean flag on every selected Wardley
|
|
151
|
+
* background: `active` reflects the current state, `run` flips it (with an
|
|
152
|
+
* undo checkpoint). An optional `when` predicate hides the button.
|
|
153
|
+
*/
|
|
154
|
+
function booleanToggle(
|
|
155
|
+
id: string,
|
|
156
|
+
tooltip: string,
|
|
157
|
+
icon: TemplateResult,
|
|
158
|
+
prop: WardleyToggleProp,
|
|
159
|
+
when?: (ctx: ToolbarContext) => boolean
|
|
160
|
+
) {
|
|
161
|
+
return {
|
|
162
|
+
id,
|
|
163
|
+
tooltip,
|
|
164
|
+
icon,
|
|
165
|
+
when: when ?? true,
|
|
166
|
+
active(ctx: ToolbarContext) {
|
|
167
|
+
const models = ctx.getSurfaceModelsByType(WardleyBackgroundElementModel);
|
|
168
|
+
return models.length > 0 && models.every(model => model[prop]);
|
|
169
|
+
},
|
|
170
|
+
run(ctx: ToolbarContext) {
|
|
171
|
+
const models = ctx.getSurfaceModelsByType(WardleyBackgroundElementModel);
|
|
172
|
+
if (!models.length) return;
|
|
173
|
+
|
|
174
|
+
const enable = !models.every(model => model[prop]);
|
|
175
|
+
ctx.std.store.captureSync();
|
|
176
|
+
const crud = ctx.std.get(EdgelessCRUDIdentifier);
|
|
177
|
+
for (const model of models) {
|
|
178
|
+
crud.updateElement(model.id, { [prop]: enable });
|
|
179
|
+
}
|
|
180
|
+
},
|
|
181
|
+
};
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
export const wardleyToolbarConfig = {
|
|
185
|
+
actions: [
|
|
186
|
+
booleanToggle(
|
|
187
|
+
'a.toggle-resize',
|
|
188
|
+
'Enable / lock resizing',
|
|
189
|
+
ResizeIcon,
|
|
190
|
+
'resizeEnabled'
|
|
191
|
+
),
|
|
192
|
+
// Group 1 — evolution (X) side: axis, phase labels, columns, corner
|
|
193
|
+
// labels, and the gradient toggle.
|
|
194
|
+
{
|
|
195
|
+
id: 'b.evolution',
|
|
196
|
+
actions: [
|
|
197
|
+
booleanToggle(
|
|
198
|
+
'b.1-axis-x',
|
|
199
|
+
'Evolution axis (X)',
|
|
200
|
+
XAxisIcon,
|
|
201
|
+
'showXAxis'
|
|
202
|
+
),
|
|
203
|
+
booleanToggle(
|
|
204
|
+
'b.2-column-labels',
|
|
205
|
+
'Evolution phase labels',
|
|
206
|
+
ColumnLabelsIcon,
|
|
207
|
+
'showColumnLabels'
|
|
208
|
+
),
|
|
209
|
+
booleanToggle(
|
|
210
|
+
'b.3-columns',
|
|
211
|
+
'Columns (dividers)',
|
|
212
|
+
ColumnsIcon,
|
|
213
|
+
'showColumnDividers'
|
|
214
|
+
),
|
|
215
|
+
booleanToggle(
|
|
216
|
+
'b.4-corner-labels',
|
|
217
|
+
'Labels Uncharted / Industrialized',
|
|
218
|
+
CornerLabelsIcon,
|
|
219
|
+
'showCornerLabels'
|
|
220
|
+
),
|
|
221
|
+
// Only relevant when the selection has a gradient variant.
|
|
222
|
+
booleanToggle(
|
|
223
|
+
'b.5-gradient',
|
|
224
|
+
'Show / hide the gradient',
|
|
225
|
+
GradientIcon,
|
|
226
|
+
'showGradient',
|
|
227
|
+
ctx =>
|
|
228
|
+
ctx
|
|
229
|
+
.getSurfaceModelsByType(WardleyBackgroundElementModel)
|
|
230
|
+
.some(model => model.variant !== 'classic')
|
|
231
|
+
),
|
|
232
|
+
],
|
|
233
|
+
},
|
|
234
|
+
// Group 2 — value-chain (Y) side: axis and Visible/Invisible labels.
|
|
235
|
+
{
|
|
236
|
+
id: 'c.value-chain',
|
|
237
|
+
actions: [
|
|
238
|
+
booleanToggle(
|
|
239
|
+
'c.1-axis-y',
|
|
240
|
+
'Value Chain axis (Y)',
|
|
241
|
+
YAxisIcon,
|
|
242
|
+
'showYAxis'
|
|
243
|
+
),
|
|
244
|
+
booleanToggle(
|
|
245
|
+
'c.2-visibility-labels',
|
|
246
|
+
'Labels Visible / Invisible',
|
|
247
|
+
VisibilityIcon,
|
|
248
|
+
'showVisibilityLabels'
|
|
249
|
+
),
|
|
250
|
+
],
|
|
251
|
+
},
|
|
252
|
+
// Generate the auto-legend from the components present inside this
|
|
253
|
+
// background's perimeter (+ a gradient-meaning block for gradient variants).
|
|
254
|
+
{
|
|
255
|
+
id: 'd.legend',
|
|
256
|
+
tooltip: 'Generate the legend (components present)',
|
|
257
|
+
icon: wardleyLegendIcon,
|
|
258
|
+
run(ctx) {
|
|
259
|
+
const models = ctx.getSurfaceModelsByType(WardleyBackgroundElementModel);
|
|
260
|
+
const bg = models[0];
|
|
261
|
+
if (!bg) return;
|
|
262
|
+
createWardleyLegend(ctx.std, bg);
|
|
263
|
+
ctx.std.getOptional(TelemetryProvider)?.track('FrameworkLegendCreated', {
|
|
264
|
+
framework: 'wardley',
|
|
265
|
+
element: 'legend',
|
|
266
|
+
page: 'whiteboard editor',
|
|
267
|
+
segment: 'element toolbar',
|
|
268
|
+
module: 'wardley toolbar',
|
|
269
|
+
});
|
|
270
|
+
},
|
|
271
|
+
},
|
|
272
|
+
],
|
|
273
|
+
when: ctx =>
|
|
274
|
+
ctx.getSurfaceModelsByType(WardleyBackgroundElementModel).length > 0,
|
|
275
|
+
} as const satisfies ToolbarModuleConfig;
|
|
276
|
+
|
|
277
|
+
export const wardleyToolbarExtension = ToolbarModuleExtension({
|
|
278
|
+
id: BlockFlavourIdentifier('affine:surface:wardley'),
|
|
279
|
+
config: wardleyToolbarConfig,
|
|
280
|
+
});
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
import { svg } from 'lit';
|
|
2
|
+
|
|
3
|
+
/** Colored Wardley glyph (mockup proposal B) for the main toolbar button. */
|
|
4
|
+
export const wardleyToolbarIcon = svg`<svg width="100%" height="100%" viewBox="0 0 56 56" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
5
|
+
<defs>
|
|
6
|
+
<linearGradient id="wardley-evo" x1="0" y1="0" x2="0" y2="1">
|
|
7
|
+
<stop offset="0" stop-color="#ffffff" stop-opacity="0"/>
|
|
8
|
+
<stop offset="1" stop-color="#fdf3e3"/>
|
|
9
|
+
</linearGradient>
|
|
10
|
+
</defs>
|
|
11
|
+
<!-- footprint = 50x50 centered in the viewBox 56 (3px margin) -->
|
|
12
|
+
<svg x="3" y="3" width="48" height="48" viewBox="6 6 28 28">
|
|
13
|
+
<rect x="6" y="6" width="28" height="28" rx="1" fill="url(#wardley-evo)" />
|
|
14
|
+
<line x1="17" y1="12" x2="17" y2="30" stroke="#c3cad4" stroke-width="0.8" stroke-dasharray="1.6 1.6"/>
|
|
15
|
+
<line x1="25" y1="12" x2="25" y2="30" stroke="#c3cad4" stroke-width="0.8" stroke-dasharray="1.6 1.6"/>
|
|
16
|
+
<path d="M10 12 V30 H28" fill="none" stroke="#95948f" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round"/>
|
|
17
|
+
<path d="M10 6.5 L7 12 L13 12 Z" fill="#95948f"/>
|
|
18
|
+
<path d="M33.5 30 L28 27 L28 33 Z" fill="#95948f"/>
|
|
19
|
+
<path d="M14 14 L20.5 19 L27 16.5 M20.5 19 L24 26" stroke="#aab4c0" stroke-width="1.1" fill="none" stroke-linecap="round"/>
|
|
20
|
+
<circle cx="14" cy="14" r="2.6" fill="#1f6feb"/>
|
|
21
|
+
<circle cx="20.5" cy="19" r="2.6" fill="#16a394"/>
|
|
22
|
+
<circle cx="27" cy="16.5" r="2.6" fill="#e2a32b"/>
|
|
23
|
+
<circle cx="24" cy="26" r="2.6" fill="#d6455d"/>
|
|
24
|
+
</svg>
|
|
25
|
+
</svg>`;
|
|
26
|
+
|
|
27
|
+
/** Monochrome glyph for the "create background" menu item (uses currentColor). */
|
|
28
|
+
export const wardleyBackgroundIcon = svg`<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
29
|
+
<path d="M6 4 V19 H21" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round"/>
|
|
30
|
+
<path d="M6 2.6 L3.8 6 H8.2 Z" fill="currentColor"/>
|
|
31
|
+
<path d="M22.4 19 L19 16.8 V21.2 Z" fill="currentColor"/>
|
|
32
|
+
<line x1="11" y1="6" x2="11" y2="19" stroke="currentColor" stroke-width="0.9" stroke-dasharray="1.8 1.8" opacity="0.6"/>
|
|
33
|
+
<line x1="16" y1="6" x2="16" y2="19" stroke="currentColor" stroke-width="0.9" stroke-dasharray="1.8 1.8" opacity="0.6"/>
|
|
34
|
+
</svg>`;
|
|
35
|
+
|
|
36
|
+
/** Wardley red — matches the renderer presets (consts WARDLEY_RED). */
|
|
37
|
+
const RED = '#d6455d';
|
|
38
|
+
/** Gradient green — matches gradient.ts GRADIENT_GREEN. */
|
|
39
|
+
const GREEN = '#1f9e4d';
|
|
40
|
+
|
|
41
|
+
/** Component node (validated COMP-C2): single circle. */
|
|
42
|
+
export const wardleyComponentIcon = svg`<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
43
|
+
<circle cx="12" cy="12" r="6.3" fill="#fff" stroke="currentColor" stroke-width="1.5"/>
|
|
44
|
+
</svg>`;
|
|
45
|
+
|
|
46
|
+
/** Anchor (validated ANCH-B): person glyph inscribed in a circle. */
|
|
47
|
+
export const wardleyAnchorIcon = svg`<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
48
|
+
<circle cx="12" cy="12" r="6" fill="#fff" stroke="currentColor" stroke-width="1.6"/>
|
|
49
|
+
<circle cx="12" cy="9.4" r="1.8" fill="none" stroke="currentColor" stroke-width="1.3"/>
|
|
50
|
+
<path d="M7.4 15.9 C 8.2 12.1, 15.8 12.1, 16.6 15.9" fill="none" stroke="currentColor" stroke-width="1.4" stroke-linecap="round"/>
|
|
51
|
+
</svg>`;
|
|
52
|
+
|
|
53
|
+
/** Dependency link (validated LINK-A3): two outline nodes joined by a line. */
|
|
54
|
+
export const wardleyLinkIcon = svg`<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
55
|
+
<line x1="5.5" y1="16" x2="18.5" y2="8" stroke="currentColor" stroke-width="1.8"/>
|
|
56
|
+
<circle cx="5.5" cy="16" r="2.2" fill="#fff" stroke="currentColor" stroke-width="1.5"/>
|
|
57
|
+
<circle cx="18.5" cy="8" r="2.2" fill="#fff" stroke="currentColor" stroke-width="1.5"/>
|
|
58
|
+
</svg>`;
|
|
59
|
+
|
|
60
|
+
/** Evolution arrow (validated ARR-B): red dashed arrow. */
|
|
61
|
+
export const wardleyArrowIcon = svg`<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
62
|
+
<line x1="4.5" y1="12" x2="16" y2="12" stroke="${RED}" stroke-width="1.7" stroke-dasharray="2.4 2.2" stroke-linecap="round"/>
|
|
63
|
+
<path d="M14.5 7.8 L20 12 L14.5 16.2 Z" fill="${RED}"/>
|
|
64
|
+
</svg>`;
|
|
65
|
+
|
|
66
|
+
/** Inertia (validated INER): black bar barring the red dashed arrow. */
|
|
67
|
+
export const wardleyInertiaIcon = svg`<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
68
|
+
<line x1="3.5" y1="12" x2="16.5" y2="12" stroke="${RED}" stroke-width="1.7" stroke-dasharray="2.4 2.2" stroke-linecap="round"/>
|
|
69
|
+
<path d="M15 7.8 L20.5 12 L15 16.2 Z" fill="${RED}"/>
|
|
70
|
+
<rect x="9.6" y="5.4" width="2.9" height="13.2" rx="0.6" fill="currentColor"/>
|
|
71
|
+
</svg>`;
|
|
72
|
+
|
|
73
|
+
/** Pipeline (validated PIPE-D): wide thin rect + square handle astride the top edge, thin borders. */
|
|
74
|
+
export const wardleyPipelineIcon = svg`<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
75
|
+
<rect x="3.5" y="11" width="17" height="7" rx="1" fill="#fff" stroke="currentColor" stroke-width="1.2"/>
|
|
76
|
+
<rect x="10" y="9" width="4" height="4" rx="0.6" fill="#fff" stroke="currentColor" stroke-width="1.2"/>
|
|
77
|
+
</svg>`;
|
|
78
|
+
|
|
79
|
+
/** Market (validated MKT-B): thin outer circle + 3 thick-bordered nodes wired in a triangle. */
|
|
80
|
+
export const wardleyMarketIcon = svg`<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
81
|
+
<circle cx="12" cy="12" r="9.6" fill="#fff" stroke="currentColor" stroke-width="1.2"/>
|
|
82
|
+
<path d="M12 6.6 L7.3 14.7 H16.7 Z" fill="none" stroke="currentColor" stroke-width="1.1"/>
|
|
83
|
+
<circle cx="12" cy="6.6" r="2.3" fill="#fff" stroke="currentColor" stroke-width="2.1"/>
|
|
84
|
+
<circle cx="7.3" cy="14.7" r="2.3" fill="#fff" stroke="currentColor" stroke-width="2.1"/>
|
|
85
|
+
<circle cx="16.7" cy="14.7" r="2.3" fill="#fff" stroke="currentColor" stroke-width="2.1"/>
|
|
86
|
+
</svg>`;
|
|
87
|
+
|
|
88
|
+
/** Ecosystem (validated ECO-A): double border at the rim + hatching confined to the inner donut + hollow center. */
|
|
89
|
+
export const wardleyEcosystemIcon = svg`<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
90
|
+
<defs><clipPath id="ecoHatch"><path d="M12 3.8 a8.2 8.2 0 1 0 0.001 0 Z M12 7.8 a4.2 4.2 0 1 1 -0.001 0 Z" clip-rule="evenodd"/></clipPath></defs>
|
|
91
|
+
<circle cx="12" cy="12" r="9.8" fill="#fff" stroke="currentColor" stroke-width="1.2"/>
|
|
92
|
+
<g clip-path="url(#ecoHatch)" stroke="currentColor" stroke-width="0.6">
|
|
93
|
+
<line x1="2" y1="-8" x2="22" y2="12"/>
|
|
94
|
+
<line x1="2" y1="-6" x2="22" y2="14"/>
|
|
95
|
+
<line x1="2" y1="-4" x2="22" y2="16"/>
|
|
96
|
+
<line x1="2" y1="-2" x2="22" y2="18"/>
|
|
97
|
+
<line x1="2" y1="0" x2="22" y2="20"/>
|
|
98
|
+
<line x1="2" y1="2" x2="22" y2="22"/>
|
|
99
|
+
<line x1="2" y1="4" x2="22" y2="24"/>
|
|
100
|
+
<line x1="2" y1="6" x2="22" y2="26"/>
|
|
101
|
+
<line x1="2" y1="8" x2="22" y2="28"/>
|
|
102
|
+
<line x1="2" y1="10" x2="22" y2="30"/>
|
|
103
|
+
</g>
|
|
104
|
+
<circle cx="12" cy="12" r="8.5" fill="none" stroke="currentColor" stroke-width="0.8"/>
|
|
105
|
+
<circle cx="12" cy="12" r="4.2" fill="#fff" stroke="currentColor" stroke-width="1"/>
|
|
106
|
+
</svg>`;
|
|
107
|
+
|
|
108
|
+
/** Component + method (validated METH-A): component inscribed in a colored outer circle (default grey). */
|
|
109
|
+
export const wardleyMethodIcon = svg`<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
110
|
+
<circle cx="12" cy="12" r="9.6" fill="#d9d9d9" stroke="#1f2328" stroke-width="1.2"/>
|
|
111
|
+
<circle cx="12" cy="12" r="4.6" fill="#fff" stroke="#1f2328" stroke-width="1.2"/>
|
|
112
|
+
</svg>`;
|
|
113
|
+
|
|
114
|
+
/** Legend: a bordered box with glyph + line rows. */
|
|
115
|
+
export const wardleyLegendIcon = svg`<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
116
|
+
<rect x="3" y="4" width="18" height="16" rx="2" fill="none" stroke="currentColor" stroke-width="1.3"/>
|
|
117
|
+
<circle cx="6.6" cy="8" r="1.4" fill="currentColor"/>
|
|
118
|
+
<line x1="9.6" y1="8" x2="18" y2="8" stroke="currentColor" stroke-width="1.2" stroke-linecap="round"/>
|
|
119
|
+
<circle cx="6.6" cy="12" r="1.4" fill="currentColor"/>
|
|
120
|
+
<line x1="9.6" y1="12" x2="18" y2="12" stroke="currentColor" stroke-width="1.2" stroke-linecap="round"/>
|
|
121
|
+
<circle cx="6.6" cy="16" r="1.4" fill="currentColor"/>
|
|
122
|
+
<line x1="9.6" y1="16" x2="15" y2="16" stroke="currentColor" stroke-width="1.2" stroke-linecap="round"/>
|
|
123
|
+
</svg>`;
|
|
124
|
+
|
|
125
|
+
/** Opportunity gradient background: axes + green differential hump + red operational bump. */
|
|
126
|
+
export const wardleyOpportunityIcon = svg`<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
127
|
+
<path d="M4 20V4M4 20h16" stroke="currentColor" stroke-width="1.4" stroke-linecap="round"/>
|
|
128
|
+
<path d="M5 12 C6 7, 8.5 7, 9.5 11 C11 17, 14 19, 18 20" stroke="${GREEN}" stroke-width="1.7" fill="none" stroke-linecap="round"/>
|
|
129
|
+
<path d="M14.5 19.5 C15.5 16.5, 17 16.5, 18 19" stroke="${RED}" stroke-width="1.7" fill="none" stroke-linecap="round"/>
|
|
130
|
+
</svg>`;
|
|
131
|
+
|
|
132
|
+
/** Benefit/Investment gradient background: axes + zero line + green J-curve. */
|
|
133
|
+
export const wardleyBenefitIcon = svg`<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
134
|
+
<path d="M4 20V4M4 20h16" stroke="currentColor" stroke-width="1.4" stroke-linecap="round"/>
|
|
135
|
+
<line x1="4" y1="14" x2="20" y2="14" stroke="currentColor" stroke-width="0.9" opacity="0.45"/>
|
|
136
|
+
<path d="M5 17 C6 18.5, 7 18.5, 8 15 C9.5 9, 12 5.5, 13.5 8 C16 12, 18 14, 20 14.5" stroke="${GREEN}" stroke-width="1.7" fill="none" stroke-linecap="round"/>
|
|
137
|
+
</svg>`;
|
|
138
|
+
|
|
139
|
+
/** Evolution-gradient background (Wardley's S-curve presentation): grey at both edges fading to white center. */
|
|
140
|
+
export const wardleyEvolutionGradientIcon = svg`<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
141
|
+
<defs>
|
|
142
|
+
<linearGradient id="wardleyGreyU" x1="0" y1="0" x2="1" y2="0">
|
|
143
|
+
<stop offset="0" stop-color="#9aa0a6"/>
|
|
144
|
+
<stop offset="0.35" stop-color="#ffffff"/>
|
|
145
|
+
<stop offset="0.65" stop-color="#ffffff"/>
|
|
146
|
+
<stop offset="1" stop-color="#9aa0a6"/>
|
|
147
|
+
</linearGradient>
|
|
148
|
+
</defs>
|
|
149
|
+
<rect x="4" y="4.5" width="16" height="15" rx="2" fill="url(#wardleyGreyU)" stroke="currentColor" stroke-width="1.2"/>
|
|
150
|
+
</svg>`;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { shapeToolbarConfig } from '@formicoidea/labre-core/gfx/shape';
|
|
2
|
+
import {
|
|
3
|
+
type ToolbarModuleConfig,
|
|
4
|
+
ToolbarModuleExtension,
|
|
5
|
+
} from '@formicoidea/labre-core/shared/services';
|
|
6
|
+
import { BlockFlavourIdentifier } from '@formicoidea/labre-core/std';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Wardley nodes are {@link ShapeElementModel} subclasses, so the shape toolbar's
|
|
10
|
+
* actions (which target `getSurfaceModelsByType(ShapeElementModel)`) operate on
|
|
11
|
+
* them directly. We re-register only the color / line-style actions under the
|
|
12
|
+
* `wardleyNode` flavour so the circle's fill / stroke color / stroke width are
|
|
13
|
+
* editable — while excluding the shape-only actions (switch type, add inner
|
|
14
|
+
* text, edit vertices) that don't make sense for a Wardley node.
|
|
15
|
+
*/
|
|
16
|
+
const KEEP_ACTION_IDS = new Set(['e.color', 'd.style']);
|
|
17
|
+
|
|
18
|
+
const wardleyNodeToolbarConfig = {
|
|
19
|
+
actions: shapeToolbarConfig.actions.filter(action =>
|
|
20
|
+
KEEP_ACTION_IDS.has(action.id)
|
|
21
|
+
),
|
|
22
|
+
when: shapeToolbarConfig.when,
|
|
23
|
+
} as ToolbarModuleConfig;
|
|
24
|
+
|
|
25
|
+
export const wardleyNodeToolbarExtension = ToolbarModuleExtension({
|
|
26
|
+
id: BlockFlavourIdentifier('affine:surface:wardleyNode'),
|
|
27
|
+
config: wardleyNodeToolbarConfig,
|
|
28
|
+
});
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { SeniorToolExtension } from '@formicoidea/labre-core/widgets/edgeless-toolbar';
|
|
2
|
+
import { html } from 'lit';
|
|
3
|
+
|
|
4
|
+
export const wardleySeniorTool = SeniorToolExtension('wardley', ({ block }) => {
|
|
5
|
+
return {
|
|
6
|
+
name: 'Wardley map',
|
|
7
|
+
content: html`<edgeless-wardley-senior-button
|
|
8
|
+
.edgeless=${block}
|
|
9
|
+
></edgeless-wardley-senior-button>`,
|
|
10
|
+
};
|
|
11
|
+
});
|