@formicoidea/labre-ddd-shared 0.32.0 → 0.33.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 +1 -0
- package/dist/index.js +1 -0
- package/dist/shared/consts.d.ts +30 -1
- package/dist/shared/consts.js +118 -15
- package/dist/shared/legend-auto.d.ts +118 -0
- package/dist/shared/legend-auto.js +79 -0
- package/dist/shared/prefabs.d.ts +41 -4
- package/dist/shared/prefabs.js +83 -21
- package/dist/templates/components.js +9 -2
- package/dist/toolbar/icons.d.ts +12 -0
- package/dist/toolbar/icons.js +32 -1
- package/dist/toolbar/senior-buttons.d.ts +2 -0
- package/dist/toolbar/senior-buttons.js +4 -1
- package/package.json +2 -2
package/dist/index.d.ts
CHANGED
package/dist/index.js
CHANGED
package/dist/shared/consts.d.ts
CHANGED
|
@@ -13,7 +13,31 @@ export declare const SHADOW_COLOR = "#0000002e";
|
|
|
13
13
|
export declare const STICKY_FONT = "blocksuite:surface:Kalam";
|
|
14
14
|
export declare const STICKY_FONT_SIZE = 20;
|
|
15
15
|
export declare const STICKY_RADIUS = 6;
|
|
16
|
-
/**
|
|
16
|
+
/**
|
|
17
|
+
* Event Storming sticky palette — DDD Crew / Brandolini colour code.
|
|
18
|
+
*
|
|
19
|
+
* ## The aggregate (WS5)
|
|
20
|
+
*
|
|
21
|
+
* Brandolini's notation has a pale-yellow sticky for the AGGREGATE — the thing a
|
|
22
|
+
* command lands on and the thing that raises the event — and without it the
|
|
23
|
+
* canonical sentence `Command → Aggregate → Domain event` cannot be drawn at
|
|
24
|
+
* all, let alone checked. It was missing here until WS5.
|
|
25
|
+
*
|
|
26
|
+
* Its colour is the one judgement call in this table. The plan's indicative
|
|
27
|
+
* `#FDF0A0` measures **ΔE 3.5** from the actor's `#FFF1A8` — two stickies
|
|
28
|
+
* nobody could tell apart on a wall, which is exactly the failure the notation
|
|
29
|
+
* already has and the tool should not inherit. `#FAF2C9` is the same pale
|
|
30
|
+
* yellow taken two steps towards cream: ΔE 16.3 from the actor, 50.2 from the
|
|
31
|
+
* constraint, and still 21.7 from the white board it sits on, so it reads as
|
|
32
|
+
* "pale" rather than as "blank". The three yellows are then a LADDER — the
|
|
33
|
+
* constraint saturated, the actor light, the aggregate palest — which is how a
|
|
34
|
+
* reader tells them apart at a glance without a legend.
|
|
35
|
+
*
|
|
36
|
+
* Size carries the rest of the distinction, as it does on a real wall: the
|
|
37
|
+
* aggregate is created at 160 against the standard 120 (see
|
|
38
|
+
* `ddd-event-storming/src/commands.ts`). The palette says which; the format
|
|
39
|
+
* says how big.
|
|
40
|
+
*/
|
|
17
41
|
export declare const ES_STICKIES: readonly [{
|
|
18
42
|
readonly kind: "domainEvent";
|
|
19
43
|
readonly label: "Domain event";
|
|
@@ -24,6 +48,11 @@ export declare const ES_STICKIES: readonly [{
|
|
|
24
48
|
readonly label: "Command";
|
|
25
49
|
readonly fill: "#5BA3DB";
|
|
26
50
|
readonly text: "#06304d";
|
|
51
|
+
}, {
|
|
52
|
+
readonly kind: "aggregate";
|
|
53
|
+
readonly label: "Aggregate";
|
|
54
|
+
readonly fill: "#FAF2C9";
|
|
55
|
+
readonly text: "#5a4b00";
|
|
27
56
|
}, {
|
|
28
57
|
readonly kind: "actor";
|
|
29
58
|
readonly label: "Actor";
|
package/dist/shared/consts.js
CHANGED
|
@@ -13,18 +13,59 @@ export const SHADOW_COLOR = '#0000002e';
|
|
|
13
13
|
export const STICKY_FONT = 'blocksuite:surface:Kalam';
|
|
14
14
|
export const STICKY_FONT_SIZE = 20;
|
|
15
15
|
export const STICKY_RADIUS = 6;
|
|
16
|
-
/**
|
|
16
|
+
/**
|
|
17
|
+
* Event Storming sticky palette — DDD Crew / Brandolini colour code.
|
|
18
|
+
*
|
|
19
|
+
* ## The aggregate (WS5)
|
|
20
|
+
*
|
|
21
|
+
* Brandolini's notation has a pale-yellow sticky for the AGGREGATE — the thing a
|
|
22
|
+
* command lands on and the thing that raises the event — and without it the
|
|
23
|
+
* canonical sentence `Command → Aggregate → Domain event` cannot be drawn at
|
|
24
|
+
* all, let alone checked. It was missing here until WS5.
|
|
25
|
+
*
|
|
26
|
+
* Its colour is the one judgement call in this table. The plan's indicative
|
|
27
|
+
* `#FDF0A0` measures **ΔE 3.5** from the actor's `#FFF1A8` — two stickies
|
|
28
|
+
* nobody could tell apart on a wall, which is exactly the failure the notation
|
|
29
|
+
* already has and the tool should not inherit. `#FAF2C9` is the same pale
|
|
30
|
+
* yellow taken two steps towards cream: ΔE 16.3 from the actor, 50.2 from the
|
|
31
|
+
* constraint, and still 21.7 from the white board it sits on, so it reads as
|
|
32
|
+
* "pale" rather than as "blank". The three yellows are then a LADDER — the
|
|
33
|
+
* constraint saturated, the actor light, the aggregate palest — which is how a
|
|
34
|
+
* reader tells them apart at a glance without a legend.
|
|
35
|
+
*
|
|
36
|
+
* Size carries the rest of the distinction, as it does on a real wall: the
|
|
37
|
+
* aggregate is created at 160 against the standard 120 (see
|
|
38
|
+
* `ddd-event-storming/src/commands.ts`). The palette says which; the format
|
|
39
|
+
* says how big.
|
|
40
|
+
*/
|
|
17
41
|
export const ES_STICKIES = [
|
|
18
|
-
{
|
|
42
|
+
{
|
|
43
|
+
kind: 'domainEvent',
|
|
44
|
+
label: 'Domain event',
|
|
45
|
+
fill: '#F5963B',
|
|
46
|
+
text: '#5a3000',
|
|
47
|
+
},
|
|
19
48
|
{ kind: 'command', label: 'Command', fill: '#5BA3DB', text: '#06304d' },
|
|
49
|
+
// Placed after the command, in the order the grammar reads: an actor issues a
|
|
50
|
+
// command, a command lands on an aggregate, an aggregate raises an event.
|
|
51
|
+
{ kind: 'aggregate', label: 'Aggregate', fill: '#FAF2C9', text: '#5a4b00' },
|
|
20
52
|
{ kind: 'actor', label: 'Actor', fill: '#FFF1A8', text: '#5a4b00' },
|
|
21
53
|
{ kind: 'constraint', label: 'Constraint', fill: '#FFD84D', text: '#5a4b00' },
|
|
22
54
|
{ kind: 'policy', label: 'Policy', fill: '#C9A8E0', text: '#3d1f57' },
|
|
23
55
|
{ kind: 'readModel', label: 'Read model', fill: '#7ED38A', text: '#14502a' },
|
|
24
|
-
{
|
|
56
|
+
{
|
|
57
|
+
kind: 'system',
|
|
58
|
+
label: 'External system',
|
|
59
|
+
fill: '#F6A6C0',
|
|
60
|
+
text: '#5e1230',
|
|
61
|
+
},
|
|
25
62
|
];
|
|
26
63
|
/** Hotspot — neon-pink diamond. */
|
|
27
|
-
export const ES_HOTSPOT = {
|
|
64
|
+
export const ES_HOTSPOT = {
|
|
65
|
+
label: 'Hotspot',
|
|
66
|
+
fill: '#FF1E8E',
|
|
67
|
+
text: '#ffffff',
|
|
68
|
+
};
|
|
28
69
|
/** Context Map bounded-context bubble. */
|
|
29
70
|
export const CM_BUBBLE = {
|
|
30
71
|
fill: '#e6f0fa',
|
|
@@ -41,15 +82,69 @@ export const CM_BUBBLE = {
|
|
|
41
82
|
* Ways, Big Ball of Mud).
|
|
42
83
|
*/
|
|
43
84
|
export const CM_RELATIONSHIPS = [
|
|
44
|
-
{
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
{
|
|
52
|
-
|
|
85
|
+
{
|
|
86
|
+
kind: 'partnership',
|
|
87
|
+
label: 'Partnership',
|
|
88
|
+
abbrev: 'PS',
|
|
89
|
+
upDown: false,
|
|
90
|
+
dashed: false,
|
|
91
|
+
},
|
|
92
|
+
{
|
|
93
|
+
kind: 'sharedKernel',
|
|
94
|
+
label: 'Shared Kernel',
|
|
95
|
+
abbrev: 'SK',
|
|
96
|
+
upDown: false,
|
|
97
|
+
dashed: false,
|
|
98
|
+
},
|
|
99
|
+
{
|
|
100
|
+
kind: 'customerSupplier',
|
|
101
|
+
label: 'Customer / Supplier',
|
|
102
|
+
abbrev: 'C/S',
|
|
103
|
+
upDown: true,
|
|
104
|
+
dashed: false,
|
|
105
|
+
},
|
|
106
|
+
{
|
|
107
|
+
kind: 'conformist',
|
|
108
|
+
label: 'Conformist',
|
|
109
|
+
abbrev: 'CF',
|
|
110
|
+
upDown: true,
|
|
111
|
+
dashed: false,
|
|
112
|
+
},
|
|
113
|
+
{
|
|
114
|
+
kind: 'acl',
|
|
115
|
+
label: 'Anticorruption Layer',
|
|
116
|
+
abbrev: 'ACL',
|
|
117
|
+
upDown: true,
|
|
118
|
+
dashed: false,
|
|
119
|
+
},
|
|
120
|
+
{
|
|
121
|
+
kind: 'ohs',
|
|
122
|
+
label: 'Open Host Service',
|
|
123
|
+
abbrev: 'OHS',
|
|
124
|
+
upDown: true,
|
|
125
|
+
dashed: false,
|
|
126
|
+
},
|
|
127
|
+
{
|
|
128
|
+
kind: 'publishedLanguage',
|
|
129
|
+
label: 'Published Language',
|
|
130
|
+
abbrev: 'PL',
|
|
131
|
+
upDown: true,
|
|
132
|
+
dashed: false,
|
|
133
|
+
},
|
|
134
|
+
{
|
|
135
|
+
kind: 'separateWays',
|
|
136
|
+
label: 'Separate Ways',
|
|
137
|
+
abbrev: 'SW',
|
|
138
|
+
upDown: false,
|
|
139
|
+
dashed: true,
|
|
140
|
+
},
|
|
141
|
+
{
|
|
142
|
+
kind: 'bbom',
|
|
143
|
+
label: 'Big Ball of Mud',
|
|
144
|
+
abbrev: 'BBoM',
|
|
145
|
+
upDown: false,
|
|
146
|
+
dashed: true,
|
|
147
|
+
},
|
|
53
148
|
];
|
|
54
149
|
/**
|
|
55
150
|
* Context Map cloud (Big Ball of Mud / general-purpose boundary). The canvas
|
|
@@ -64,7 +159,10 @@ function buildCloud() {
|
|
|
64
159
|
for (let i = 0; i < N; i++) {
|
|
65
160
|
const t = (i / N) * Math.PI * 2;
|
|
66
161
|
const r = 0.4 + 0.05 * Math.sin(6 * t) + 0.03 * Math.sin(3 * t + 0.8);
|
|
67
|
-
pts.push([
|
|
162
|
+
pts.push([
|
|
163
|
+
clamp(0.5 + r * Math.cos(t)),
|
|
164
|
+
clamp(0.5 + r * 0.6 * Math.sin(t)),
|
|
165
|
+
]);
|
|
68
166
|
}
|
|
69
167
|
return pts;
|
|
70
168
|
}
|
|
@@ -72,7 +170,12 @@ export const CLOUD_VERTICES = buildCloud();
|
|
|
72
170
|
export const CLOUD = { w: 180, h: 120, fill: '#f0eef6', stroke: '#6d6e71' };
|
|
73
171
|
/** Team Topologies interaction modes (placeable markers + Notation legend). */
|
|
74
172
|
export const TEAM_TOPOLOGIES = [
|
|
75
|
-
{
|
|
173
|
+
{
|
|
174
|
+
kind: 'collaboration',
|
|
175
|
+
label: 'Collaboration',
|
|
176
|
+
letter: 'C',
|
|
177
|
+
fill: '#99ff99',
|
|
178
|
+
},
|
|
76
179
|
{ kind: 'xaas', label: 'X-as-a-Service', letter: 'X', fill: '#66b2ff' },
|
|
77
180
|
{ kind: 'facilitating', label: 'Facilitating', letter: 'F', fill: '#ffd84d' },
|
|
78
181
|
];
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import { Bound } from '@formicoidea/labre-core/global/gfx';
|
|
2
|
+
import type { BlockStdScope } from '@formicoidea/labre-core/std';
|
|
3
|
+
import { type GfxController, type RoleDefs, type RoleId } from '@formicoidea/labre-core/std/gfx';
|
|
4
|
+
import { type LegendRow, type LegendSection } from './prefabs.js';
|
|
5
|
+
/**
|
|
6
|
+
* The AUTOMATIC legend, shared by the three DDD backgrounds.
|
|
7
|
+
*
|
|
8
|
+
* Wardley's `createWardleyLegend` is the gesture this replicates — select the
|
|
9
|
+
* background, press one button, get a legend of what is actually drawn on it,
|
|
10
|
+
* dropped bottom-left and grouped so it can be moved and edited like anything
|
|
11
|
+
* else. Two things are deliberately NOT replicated:
|
|
12
|
+
*
|
|
13
|
+
* - **detection is by ROLE, never by `instanceof` or by fill colour.** A role is
|
|
14
|
+
* the semantic identity of an artefact (`framework/std`'s `role.ts`); a shape
|
|
15
|
+
* type is not, and a colour is a restyle away from lying. Reading roles also
|
|
16
|
+
* means a legend and a validation rule agree on what is on the board, because
|
|
17
|
+
* they read the same field;
|
|
18
|
+
* - **the glyphs are the DDD house prefab** ({@link addLegend}), not hand-drawn
|
|
19
|
+
* element-by-element replicas of the artefacts. The three DDD frameworks
|
|
20
|
+
* already document themselves with swatch rows, and a second visual language
|
|
21
|
+
* for the same job would be one too many.
|
|
22
|
+
*
|
|
23
|
+
* A framework contributes nothing but a TABLE: which role puts which row in the
|
|
24
|
+
* legend. Everything else — scanning the perimeter, resolving specialisations,
|
|
25
|
+
* dropping empty sections, placing and grouping the box — happens here once.
|
|
26
|
+
*/
|
|
27
|
+
/**
|
|
28
|
+
* One candidate row: `row` is listed only when `role` — or a role that
|
|
29
|
+
* specialises it — is carried by an element inside the background's perimeter.
|
|
30
|
+
*/
|
|
31
|
+
export interface AutoLegendEntry {
|
|
32
|
+
role: RoleId;
|
|
33
|
+
row: LegendRow;
|
|
34
|
+
/**
|
|
35
|
+
* Match `role` and `role` ALONE, without the specialisation walk.
|
|
36
|
+
*
|
|
37
|
+
* The default — an entry lights up for any of its children — is what a legend
|
|
38
|
+
* usually wants: one "Relation" row covers the twenty-two verbs, one
|
|
39
|
+
* "Sub-domain" row would cover the five kinds. It is also what a validation
|
|
40
|
+
* rule wants, and for the same reason: a statement about a family is a
|
|
41
|
+
* statement about every member of it.
|
|
42
|
+
*
|
|
43
|
+
* A legend sometimes wants the opposite, because it documents what is DRAWN
|
|
44
|
+
* rather than what is meant. EDGY's four base kinds are the case: Content
|
|
45
|
+
* specialises Object, so an entry on `edgy:object` would put an "Object" row
|
|
46
|
+
* on a board where nobody ever dropped a bare Object — the row would name a
|
|
47
|
+
* white square that is nowhere on the diagram. `exact` makes the base and its
|
|
48
|
+
* specialisations two separate statements, so each is listed when, and only
|
|
49
|
+
* when, it is the thing the user actually put down.
|
|
50
|
+
*
|
|
51
|
+
* Reach for it when a row's swatch would be a lie on a board carrying only
|
|
52
|
+
* children — a distinct colour, a distinct shape, a distinct wording. Leave it
|
|
53
|
+
* off when the parent row is a fair summary of the whole family.
|
|
54
|
+
*/
|
|
55
|
+
exact?: boolean;
|
|
56
|
+
}
|
|
57
|
+
export interface AutoLegendSectionSpec {
|
|
58
|
+
/** Sub-title, dropped along with the section when none of its rows appear. */
|
|
59
|
+
title?: string;
|
|
60
|
+
entries: readonly AutoLegendEntry[];
|
|
61
|
+
}
|
|
62
|
+
export interface AutoLegendSpec {
|
|
63
|
+
/**
|
|
64
|
+
* Box title. The three DDD tools all say "Legend" (PO recette, 26/08/2026:
|
|
65
|
+
* the boxes used to be titled in French, which was the one label in the
|
|
66
|
+
* library that was — identifiers and fallback wordings are English here, and
|
|
67
|
+
* the day a host ships a locale pack it translates a key, not a leftover).
|
|
68
|
+
*/
|
|
69
|
+
title: string;
|
|
70
|
+
width?: number;
|
|
71
|
+
/**
|
|
72
|
+
* The framework's role vocabulary, so a present role is matched against an
|
|
73
|
+
* entry's role THROUGH the specialisation chain: an entry written on a parent
|
|
74
|
+
* role lists itself as soon as any of its children is on the board — unless
|
|
75
|
+
* it asks for {@link AutoLegendEntry.exact}, which is the whole of the
|
|
76
|
+
* exception.
|
|
77
|
+
*/
|
|
78
|
+
roles: RoleDefs;
|
|
79
|
+
sections: readonly AutoLegendSectionSpec[];
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* The framework's OWN wording for a role, from the vocabulary that declares it.
|
|
83
|
+
*
|
|
84
|
+
* A legend row about `es:flow` says "Flow" because that is what the role def
|
|
85
|
+
* says it is called — the table below never restates a label the vocabulary
|
|
86
|
+
* already owns, so renaming a role renames its legend row.
|
|
87
|
+
*/
|
|
88
|
+
export declare function roleLabel(roles: RoleDefs, id: RoleId): string;
|
|
89
|
+
/**
|
|
90
|
+
* Every role carried by an element inside `bound`.
|
|
91
|
+
*
|
|
92
|
+
* Neutral elements (no role) contribute nothing, which is the whole
|
|
93
|
+
* compatibility promise: a board drawn before the vocabularies existed produces
|
|
94
|
+
* an empty set and therefore a legend with no rows, exactly like an empty board.
|
|
95
|
+
*/
|
|
96
|
+
export declare function rolesInBound(gfx: GfxController, bound: Bound): Set<RoleId>;
|
|
97
|
+
/**
|
|
98
|
+
* The sections to draw for a given set of present roles: every entry whose role
|
|
99
|
+
* is present (directly or through a specialisation, unless the entry asks for
|
|
100
|
+
* {@link AutoLegendEntry.exact}), in declaration order, with empty sections —
|
|
101
|
+
* sub-title included — dropped.
|
|
102
|
+
*
|
|
103
|
+
* When nothing is recognised the result is an EMPTY array, and the caller still
|
|
104
|
+
* draws the box: that is Wardley's behaviour (an empty map yields a framed
|
|
105
|
+
* "Legend" title and no rows), and it is the honest one — a legend of a board
|
|
106
|
+
* with nothing on it lists nothing, rather than inventing the full notation the
|
|
107
|
+
* user has not used.
|
|
108
|
+
*/
|
|
109
|
+
export declare function autoLegendSections(present: ReadonlySet<RoleId>, spec: AutoLegendSpec): LegendSection[];
|
|
110
|
+
/**
|
|
111
|
+
* Build the legend of what is drawn inside `background` and drop it bottom-left
|
|
112
|
+
* of it, grouped and selected.
|
|
113
|
+
*
|
|
114
|
+
* Returns the group id, or `undefined` when there is no surface to draw on.
|
|
115
|
+
*/
|
|
116
|
+
export declare function createAutoLegend(std: BlockStdScope, background: {
|
|
117
|
+
xywh: string;
|
|
118
|
+
}, spec: AutoLegendSpec): string | undefined;
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { Bound } from '@formicoidea/labre-core/global/gfx';
|
|
2
|
+
import { GfxControllerIdentifier, roleIsA, } from '@formicoidea/labre-core/std/gfx';
|
|
3
|
+
import { addLegend, measureLegend, } from './prefabs.js';
|
|
4
|
+
/**
|
|
5
|
+
* The framework's OWN wording for a role, from the vocabulary that declares it.
|
|
6
|
+
*
|
|
7
|
+
* A legend row about `es:flow` says "Flow" because that is what the role def
|
|
8
|
+
* says it is called — the table below never restates a label the vocabulary
|
|
9
|
+
* already owns, so renaming a role renames its legend row.
|
|
10
|
+
*/
|
|
11
|
+
export function roleLabel(roles, id) {
|
|
12
|
+
return roles[id]?.labelFallback ?? id;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Distance from the background's left edge / bottom edge, in model units.
|
|
16
|
+
* Wardley's numbers, kept identical so the two gestures put the box in the same
|
|
17
|
+
* place relative to their backgrounds.
|
|
18
|
+
*/
|
|
19
|
+
const INSET_X = 50;
|
|
20
|
+
const INSET_BOTTOM = 56;
|
|
21
|
+
/**
|
|
22
|
+
* Every role carried by an element inside `bound`.
|
|
23
|
+
*
|
|
24
|
+
* Neutral elements (no role) contribute nothing, which is the whole
|
|
25
|
+
* compatibility promise: a board drawn before the vocabularies existed produces
|
|
26
|
+
* an empty set and therefore a legend with no rows, exactly like an empty board.
|
|
27
|
+
*/
|
|
28
|
+
export function rolesInBound(gfx, bound) {
|
|
29
|
+
const present = new Set();
|
|
30
|
+
for (const el of gfx.getElementsByBound(bound, { type: 'canvas' })) {
|
|
31
|
+
if (el.role !== undefined)
|
|
32
|
+
present.add(el.role);
|
|
33
|
+
}
|
|
34
|
+
return present;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* The sections to draw for a given set of present roles: every entry whose role
|
|
38
|
+
* is present (directly or through a specialisation, unless the entry asks for
|
|
39
|
+
* {@link AutoLegendEntry.exact}), in declaration order, with empty sections —
|
|
40
|
+
* sub-title included — dropped.
|
|
41
|
+
*
|
|
42
|
+
* When nothing is recognised the result is an EMPTY array, and the caller still
|
|
43
|
+
* draws the box: that is Wardley's behaviour (an empty map yields a framed
|
|
44
|
+
* "Legend" title and no rows), and it is the honest one — a legend of a board
|
|
45
|
+
* with nothing on it lists nothing, rather than inventing the full notation the
|
|
46
|
+
* user has not used.
|
|
47
|
+
*/
|
|
48
|
+
export function autoLegendSections(present, spec) {
|
|
49
|
+
const sections = [];
|
|
50
|
+
for (const section of spec.sections) {
|
|
51
|
+
const rows = section.entries
|
|
52
|
+
.filter(entry => entry.exact
|
|
53
|
+
? present.has(entry.role)
|
|
54
|
+
: [...present].some(role => roleIsA(role, entry.role, spec.roles)))
|
|
55
|
+
.map(entry => entry.row);
|
|
56
|
+
if (rows.length)
|
|
57
|
+
sections.push({ title: section.title, rows });
|
|
58
|
+
}
|
|
59
|
+
return sections;
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Build the legend of what is drawn inside `background` and drop it bottom-left
|
|
63
|
+
* of it, grouped and selected.
|
|
64
|
+
*
|
|
65
|
+
* Returns the group id, or `undefined` when there is no surface to draw on.
|
|
66
|
+
*/
|
|
67
|
+
export function createAutoLegend(std, background, spec) {
|
|
68
|
+
const gfx = std.get(GfxControllerIdentifier);
|
|
69
|
+
const surface = gfx.surface;
|
|
70
|
+
if (!surface)
|
|
71
|
+
return undefined;
|
|
72
|
+
const bound = Bound.deserialize(background.xywh);
|
|
73
|
+
const sections = autoLegendSections(rolesInBound(gfx, bound), spec);
|
|
74
|
+
const { height } = measureLegend(sections, spec.width);
|
|
75
|
+
std.store.captureSync();
|
|
76
|
+
const id = addLegend(surface, std, bound.x + INSET_X, bound.y + bound.h - INSET_BOTTOM - height, { title: spec.title, sections, width: spec.width });
|
|
77
|
+
gfx.selection.set({ elements: [id], editing: false });
|
|
78
|
+
return id;
|
|
79
|
+
}
|
package/dist/shared/prefabs.d.ts
CHANGED
|
@@ -30,15 +30,23 @@ export declare function addSticky(surface: Surface, std: BlockStdScope, cx: numb
|
|
|
30
30
|
label: string;
|
|
31
31
|
shapeType?: 'rect' | 'diamond';
|
|
32
32
|
size?: number;
|
|
33
|
+
/**
|
|
34
|
+
* Stamped on the FACE, never on the shadow: the face is the artefact — it
|
|
35
|
+
* is what carries the words, what a connector attaches to and what the
|
|
36
|
+
* grammar rules read. The shadow is ink.
|
|
37
|
+
*/
|
|
38
|
+
role?: string;
|
|
33
39
|
}): string;
|
|
34
40
|
/**
|
|
35
41
|
* A Context Map bounded-context bubble: a rounded pill whose label is the
|
|
36
42
|
* pill's own shape text in `overflow` fit mode (fixed pill, fixed font — a
|
|
37
43
|
* long context name may spill out rather than deform the map).
|
|
38
44
|
*/
|
|
39
|
-
export declare function addBubble(surface: Surface, cx: number, cy: number, label: string): string;
|
|
45
|
+
export declare function addBubble(surface: Surface, cx: number, cy: number, label: string, role?: string): string;
|
|
40
46
|
/** A Core Domain dot (sub-domain / bounded context); optional label to its right. */
|
|
41
|
-
export declare function addDot(surface: Surface, std: BlockStdScope, cx: number, cy: number, fill: string, label?: string
|
|
47
|
+
export declare function addDot(surface: Surface, std: BlockStdScope, cx: number, cy: number, fill: string, label?: string,
|
|
48
|
+
/** Stamped on the ELLIPSE, never on the group: the dot is the artefact. */
|
|
49
|
+
role?: string): string;
|
|
42
50
|
/** A free-floating label. */
|
|
43
51
|
export declare function addLabel(surface: Surface, cx: number, cy: number, label: string, align?: 'left' | 'center', color?: string): string;
|
|
44
52
|
/** A relationship connector (the single connector unit reused for all patterns). */
|
|
@@ -47,12 +55,22 @@ export declare function addConnector(surface: Surface, x1: number, y1: number, x
|
|
|
47
55
|
dashed?: boolean;
|
|
48
56
|
stroke?: string;
|
|
49
57
|
strokeWidth?: number;
|
|
58
|
+
/** Edge role; `undefined` leaves the connector a neutral line. */
|
|
59
|
+
role?: string;
|
|
50
60
|
}): string;
|
|
51
|
-
/**
|
|
61
|
+
/**
|
|
62
|
+
* A Team Topologies interaction-mode marker: a coloured square + centred letter.
|
|
63
|
+
*
|
|
64
|
+
* The role rides on the SQUARE, never on the group — the same call as
|
|
65
|
+
* {@link addDot}: the square is the artefact, the letter is its glyph and the
|
|
66
|
+
* word beside it is a caption. `undefined` writes nothing, so a caller that
|
|
67
|
+
* passes none keeps producing the neutral drawing it always did.
|
|
68
|
+
*/
|
|
52
69
|
export declare function addMarker(surface: Surface, std: BlockStdScope, cx: number, cy: number, opts: {
|
|
53
70
|
fill: string;
|
|
54
71
|
letter: string;
|
|
55
72
|
label?: string;
|
|
73
|
+
role?: string;
|
|
56
74
|
}): string;
|
|
57
75
|
/** A cloud (Big Ball of Mud / general-purpose boundary): a smoothed closed bezier polygon. */
|
|
58
76
|
export declare function addCloud(surface: Surface, std: BlockStdScope, cx: number, cy: number, label?: string): string;
|
|
@@ -71,13 +89,32 @@ export interface LegendRow {
|
|
|
71
89
|
color: string;
|
|
72
90
|
letter?: string;
|
|
73
91
|
label: string;
|
|
92
|
+
/**
|
|
93
|
+
* `line` swatches only: draw the sample as two short segments rather than one
|
|
94
|
+
* bar, so a dashed pattern (Separate Ways, Big Ball of Mud, a Core Domain
|
|
95
|
+
* movement) reads in the legend the way it reads on the board. A legend that
|
|
96
|
+
* showed a solid bar for a dashed link would be telling a small lie about the
|
|
97
|
+
* notation it documents.
|
|
98
|
+
*/
|
|
99
|
+
dashed?: boolean;
|
|
74
100
|
}
|
|
75
101
|
export interface LegendSection {
|
|
76
102
|
title?: string;
|
|
77
103
|
rows: LegendRow[];
|
|
78
104
|
}
|
|
79
105
|
/**
|
|
80
|
-
*
|
|
106
|
+
* How big the box {@link addLegend} would draw for these sections is — without
|
|
107
|
+
* drawing it. The auto-legend needs the height to drop the box bottom-left of a
|
|
108
|
+
* background, and measuring is the only honest way to get it: the box grows with
|
|
109
|
+
* the number of sub-titles and rows, which is exactly what the detection pass
|
|
110
|
+
* decides.
|
|
111
|
+
*/
|
|
112
|
+
export declare function measureLegend(sections: readonly LegendSection[], width?: number): {
|
|
113
|
+
width: number;
|
|
114
|
+
height: number;
|
|
115
|
+
};
|
|
116
|
+
/**
|
|
117
|
+
* A boxed legend: a bordered container with a bold "Legend" title and bold
|
|
81
118
|
* section sub-titles, each row a swatch (dot / square+letter / line) + label.
|
|
82
119
|
* Shared by the Core Domain and Context Map tools. Returns the grouped id.
|
|
83
120
|
*/
|
package/dist/shared/prefabs.js
CHANGED
|
@@ -30,9 +30,11 @@ export function groupIds(std, ids) {
|
|
|
30
30
|
return result.groupId || ids[0];
|
|
31
31
|
}
|
|
32
32
|
function addShape(surface, x, y, w, h, opts) {
|
|
33
|
-
const { shapeType = 'rect', fill, stroke = NO_STROKE, strokeWidth = 0, radius = 0, label } = opts;
|
|
33
|
+
const { shapeType = 'rect', fill, stroke = NO_STROKE, strokeWidth = 0, radius = 0, label, role, } = opts;
|
|
34
34
|
return surface.addElement({
|
|
35
35
|
type: 'shape',
|
|
36
|
+
// `undefined` writes nothing: a neutral artefact keeps no `role` key.
|
|
37
|
+
role,
|
|
36
38
|
shapeType,
|
|
37
39
|
filled: true,
|
|
38
40
|
fillColor: fill,
|
|
@@ -74,7 +76,7 @@ function addText(surface, x, y, w, text, color, fontFamily, fontSize, textAlign
|
|
|
74
76
|
* text element; they keep working untouched.)
|
|
75
77
|
*/
|
|
76
78
|
export function addSticky(surface, std, cx, cy, opts) {
|
|
77
|
-
const { fill, text, label, shapeType = 'rect', size = STICKY_SIZE } = opts;
|
|
79
|
+
const { fill, text, label, shapeType = 'rect', size = STICKY_SIZE, role, } = opts;
|
|
78
80
|
const half = size / 2;
|
|
79
81
|
const radius = shapeType === 'rect' ? STICKY_RADIUS : 0;
|
|
80
82
|
const shadow = addShape(surface, cx - half + SHADOW_OFFSET, cy - half + SHADOW_OFFSET, size, size, {
|
|
@@ -86,6 +88,7 @@ export function addSticky(surface, std, cx, cy, opts) {
|
|
|
86
88
|
shapeType,
|
|
87
89
|
fill,
|
|
88
90
|
radius,
|
|
91
|
+
role,
|
|
89
92
|
label: {
|
|
90
93
|
text: label,
|
|
91
94
|
color: text,
|
|
@@ -101,13 +104,14 @@ export function addSticky(surface, std, cx, cy, opts) {
|
|
|
101
104
|
* pill's own shape text in `overflow` fit mode (fixed pill, fixed font — a
|
|
102
105
|
* long context name may spill out rather than deform the map).
|
|
103
106
|
*/
|
|
104
|
-
export function addBubble(surface, cx, cy, label) {
|
|
107
|
+
export function addBubble(surface, cx, cy, label, role) {
|
|
105
108
|
const { w, h, radius, fill, stroke, text } = CM_BUBBLE;
|
|
106
109
|
return addShape(surface, cx - w / 2, cy - h / 2, w, h, {
|
|
107
110
|
fill,
|
|
108
111
|
stroke,
|
|
109
112
|
strokeWidth: 1.5,
|
|
110
113
|
radius,
|
|
114
|
+
role,
|
|
111
115
|
label: {
|
|
112
116
|
text: label,
|
|
113
117
|
color: text,
|
|
@@ -118,13 +122,16 @@ export function addBubble(surface, cx, cy, label) {
|
|
|
118
122
|
});
|
|
119
123
|
}
|
|
120
124
|
/** A Core Domain dot (sub-domain / bounded context); optional label to its right. */
|
|
121
|
-
export function addDot(surface, std, cx, cy, fill, label
|
|
125
|
+
export function addDot(surface, std, cx, cy, fill, label,
|
|
126
|
+
/** Stamped on the ELLIPSE, never on the group: the dot is the artefact. */
|
|
127
|
+
role) {
|
|
122
128
|
const d = DOT_SIZE;
|
|
123
129
|
const dot = addShape(surface, cx - d / 2, cy - d / 2, d, d, {
|
|
124
130
|
shapeType: 'ellipse',
|
|
125
131
|
fill,
|
|
126
132
|
stroke: '#1f2328',
|
|
127
133
|
strokeWidth: 1.5,
|
|
134
|
+
role,
|
|
128
135
|
});
|
|
129
136
|
if (!label)
|
|
130
137
|
return dot;
|
|
@@ -137,9 +144,10 @@ export function addLabel(surface, cx, cy, label, align = 'center', color = LABEL
|
|
|
137
144
|
}
|
|
138
145
|
/** A relationship connector (the single connector unit reused for all patterns). */
|
|
139
146
|
export function addConnector(surface, x1, y1, x2, y2, opts = {}) {
|
|
140
|
-
const { rearArrow = false, dashed = false, stroke = LABEL_COLOR, strokeWidth = 2 } = opts;
|
|
147
|
+
const { rearArrow = false, dashed = false, stroke = LABEL_COLOR, strokeWidth = 2, role, } = opts;
|
|
141
148
|
return surface.addElement({
|
|
142
149
|
type: 'connector',
|
|
150
|
+
role,
|
|
143
151
|
mode: ConnectorMode.Straight,
|
|
144
152
|
stroke,
|
|
145
153
|
strokeWidth,
|
|
@@ -150,15 +158,23 @@ export function addConnector(surface, x1, y1, x2, y2, opts = {}) {
|
|
|
150
158
|
target: { position: [x2, y2] },
|
|
151
159
|
});
|
|
152
160
|
}
|
|
153
|
-
/**
|
|
161
|
+
/**
|
|
162
|
+
* A Team Topologies interaction-mode marker: a coloured square + centred letter.
|
|
163
|
+
*
|
|
164
|
+
* The role rides on the SQUARE, never on the group — the same call as
|
|
165
|
+
* {@link addDot}: the square is the artefact, the letter is its glyph and the
|
|
166
|
+
* word beside it is a caption. `undefined` writes nothing, so a caller that
|
|
167
|
+
* passes none keeps producing the neutral drawing it always did.
|
|
168
|
+
*/
|
|
154
169
|
export function addMarker(surface, std, cx, cy, opts) {
|
|
155
|
-
const { fill, letter, label } = opts;
|
|
170
|
+
const { fill, letter, label, role } = opts;
|
|
156
171
|
const s = MARKER_SIZE;
|
|
157
172
|
const box = addShape(surface, cx - s / 2, cy - s / 2, s, s, {
|
|
158
173
|
fill,
|
|
159
174
|
stroke: '#1f2328',
|
|
160
175
|
strokeWidth: 1.5,
|
|
161
176
|
radius: 4,
|
|
177
|
+
role,
|
|
162
178
|
});
|
|
163
179
|
const glyph = addText(surface, cx - s / 2, cy - 9, s, letter, '#1f2328', LABEL_FONT, 15);
|
|
164
180
|
const ids = [box, glyph];
|
|
@@ -213,25 +229,48 @@ export function addRelationship(surface, std, cx, cy, preset) {
|
|
|
213
229
|
return groupIds(std, ids);
|
|
214
230
|
}
|
|
215
231
|
/**
|
|
216
|
-
*
|
|
217
|
-
*
|
|
218
|
-
*
|
|
232
|
+
* The legend box's geometry, in one place because two callers need it: the one
|
|
233
|
+
* that DRAWS the box and the one that has to know how tall it will be before
|
|
234
|
+
* drawing it (see {@link measureLegend}).
|
|
219
235
|
*/
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
236
|
+
const LEGEND_METRICS = {
|
|
237
|
+
DEFAULT_W: 260,
|
|
238
|
+
PAD: 16,
|
|
239
|
+
TITLE_H: 32,
|
|
240
|
+
SUB_H: 26,
|
|
241
|
+
ROW_H: 28,
|
|
242
|
+
/** Swatch side (a `line` swatch is this long). */
|
|
243
|
+
SW: 16,
|
|
244
|
+
};
|
|
245
|
+
/**
|
|
246
|
+
* How big the box {@link addLegend} would draw for these sections is — without
|
|
247
|
+
* drawing it. The auto-legend needs the height to drop the box bottom-left of a
|
|
248
|
+
* background, and measuring is the only honest way to get it: the box grows with
|
|
249
|
+
* the number of sub-titles and rows, which is exactly what the detection pass
|
|
250
|
+
* decides.
|
|
251
|
+
*/
|
|
252
|
+
export function measureLegend(sections, width) {
|
|
253
|
+
const { DEFAULT_W, PAD, TITLE_H, SUB_H, ROW_H } = LEGEND_METRICS;
|
|
227
254
|
let subs = 0;
|
|
228
255
|
let rows = 0;
|
|
229
|
-
for (const s of
|
|
256
|
+
for (const s of sections) {
|
|
230
257
|
if (s.title)
|
|
231
258
|
subs++;
|
|
232
259
|
rows += s.rows.length;
|
|
233
260
|
}
|
|
234
|
-
|
|
261
|
+
return {
|
|
262
|
+
width: width ?? DEFAULT_W,
|
|
263
|
+
height: PAD * 2 + TITLE_H + subs * SUB_H + rows * ROW_H,
|
|
264
|
+
};
|
|
265
|
+
}
|
|
266
|
+
/**
|
|
267
|
+
* A boxed legend: a bordered container with a bold "Legend" title and bold
|
|
268
|
+
* section sub-titles, each row a swatch (dot / square+letter / line) + label.
|
|
269
|
+
* Shared by the Core Domain and Context Map tools. Returns the grouped id.
|
|
270
|
+
*/
|
|
271
|
+
export function addLegend(surface, std, x, y, opts) {
|
|
272
|
+
const { PAD, TITLE_H, SUB_H, ROW_H, SW } = LEGEND_METRICS;
|
|
273
|
+
const { width: W, height: H } = measureLegend(opts.sections, opts.width);
|
|
235
274
|
const ids = [
|
|
236
275
|
addShape(surface, x, y, W, H, {
|
|
237
276
|
fill: '#ffffff',
|
|
@@ -252,13 +291,36 @@ export function addLegend(surface, std, x, y, opts) {
|
|
|
252
291
|
const sx = x + PAD;
|
|
253
292
|
const midY = cy + ROW_H / 2;
|
|
254
293
|
if (row.swatch === 'dot') {
|
|
255
|
-
ids.push(addShape(surface, sx, midY - SW / 2, SW, SW, {
|
|
294
|
+
ids.push(addShape(surface, sx, midY - SW / 2, SW, SW, {
|
|
295
|
+
shapeType: 'ellipse',
|
|
296
|
+
fill: row.color,
|
|
297
|
+
stroke: '#1f2328',
|
|
298
|
+
strokeWidth: 1,
|
|
299
|
+
}));
|
|
256
300
|
}
|
|
257
301
|
else if (row.swatch === 'square') {
|
|
258
|
-
ids.push(addShape(surface, sx, midY - SW / 2, SW, SW, {
|
|
302
|
+
ids.push(addShape(surface, sx, midY - SW / 2, SW, SW, {
|
|
303
|
+
fill: row.color,
|
|
304
|
+
stroke: '#1f2328',
|
|
305
|
+
strokeWidth: 1,
|
|
306
|
+
radius: 3,
|
|
307
|
+
}));
|
|
259
308
|
if (row.letter)
|
|
260
309
|
ids.push(addText(surface, sx, midY - 8, SW, row.letter, '#1f2328', LABEL_FONT, 11));
|
|
261
310
|
}
|
|
311
|
+
else if (row.dashed) {
|
|
312
|
+
// Two 6-unit segments with a 4-unit gap: the same 16 units as a solid
|
|
313
|
+
// bar, read as a dash.
|
|
314
|
+
const seg = 6;
|
|
315
|
+
ids.push(addShape(surface, sx, midY - 2, seg, 4, {
|
|
316
|
+
fill: row.color,
|
|
317
|
+
radius: 1,
|
|
318
|
+
}));
|
|
319
|
+
ids.push(addShape(surface, sx + SW - seg, midY - 2, seg, 4, {
|
|
320
|
+
fill: row.color,
|
|
321
|
+
radius: 1,
|
|
322
|
+
}));
|
|
323
|
+
}
|
|
262
324
|
else {
|
|
263
325
|
ids.push(addShape(surface, sx, midY - 2, SW, 4, { fill: row.color, radius: 1 }));
|
|
264
326
|
}
|
|
@@ -10,7 +10,12 @@ import { CD_SUBDOMAINS, CLOUD, CLOUD_VERTICES, CM_BUBBLE, CM_RELATIONSHIPS, ES_H
|
|
|
10
10
|
const ATTRS = 'width="100%" height="100%" viewBox="0 0 135 80" xmlns="http://www.w3.org/2000/svg"';
|
|
11
11
|
const NO_STROKE = '#00000000';
|
|
12
12
|
function tpl(name, preview, elements) {
|
|
13
|
-
return {
|
|
13
|
+
return {
|
|
14
|
+
name,
|
|
15
|
+
type: 'template',
|
|
16
|
+
preview,
|
|
17
|
+
content: makeTemplateSnapshot(elements, name),
|
|
18
|
+
};
|
|
14
19
|
}
|
|
15
20
|
function stickyEl(fill, text, label, shapeType) {
|
|
16
21
|
return {
|
|
@@ -38,7 +43,9 @@ const mk = (c, l) => `<svg ${ATTRS}><rect x="48" y="20" width="40" height="40" r
|
|
|
38
43
|
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
44
|
/** Event Storming: one template per sticky kind + the hotspot. */
|
|
40
45
|
export const ES_TEMPLATES = [
|
|
41
|
-
...ES_STICKIES.map(p => tpl(`Event Storming — ${p.label}`, sq(p.fill), {
|
|
46
|
+
...ES_STICKIES.map(p => tpl(`Event Storming — ${p.label}`, sq(p.fill), {
|
|
47
|
+
s: stickyEl(p.fill, p.text, p.label, 'rect'),
|
|
48
|
+
})),
|
|
42
49
|
tpl('Event Storming — Hotspot', dia(ES_HOTSPOT.fill), {
|
|
43
50
|
s: stickyEl(ES_HOTSPOT.fill, ES_HOTSPOT.text, ES_HOTSPOT.label, 'diamond'),
|
|
44
51
|
}),
|
package/dist/toolbar/icons.d.ts
CHANGED
|
@@ -1,3 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Contextual-toolbar glyph — "generate the legend of what is on this
|
|
3
|
+
* background". One icon for the three DDD backgrounds because it is one
|
|
4
|
+
* gesture; the Core Domain chart has drawn this exact box-and-rows since its
|
|
5
|
+
* legend button shipped, and the two boards now borrow it rather than each
|
|
6
|
+
* redrawing a lookalike.
|
|
7
|
+
*
|
|
8
|
+
* `html` rather than `svg`: a toolbar action's `icon` is rendered as a standalone
|
|
9
|
+
* template, unlike the senior-button glyphs above which are interpolated into an
|
|
10
|
+
* outer `<svg>`.
|
|
11
|
+
*/
|
|
12
|
+
export declare const dddLegendIcon: import("lit-html").TemplateResult<1>;
|
|
1
13
|
/** Senior-button glyph — Event Storming (overlapping stickies). */
|
|
2
14
|
export declare const eventStormingToolbarIcon: import("lit-html").TemplateResult<2>;
|
|
3
15
|
/** Senior-button glyph — Core Domain Chart (axes + bands). */
|
package/dist/toolbar/icons.js
CHANGED
|
@@ -1,4 +1,35 @@
|
|
|
1
|
-
import { svg } from 'lit';
|
|
1
|
+
import { html, svg } from 'lit';
|
|
2
|
+
/**
|
|
3
|
+
* Contextual-toolbar glyph — "generate the legend of what is on this
|
|
4
|
+
* background". One icon for the three DDD backgrounds because it is one
|
|
5
|
+
* gesture; the Core Domain chart has drawn this exact box-and-rows since its
|
|
6
|
+
* legend button shipped, and the two boards now borrow it rather than each
|
|
7
|
+
* redrawing a lookalike.
|
|
8
|
+
*
|
|
9
|
+
* `html` rather than `svg`: a toolbar action's `icon` is rendered as a standalone
|
|
10
|
+
* template, unlike the senior-button glyphs above which are interpolated into an
|
|
11
|
+
* outer `<svg>`.
|
|
12
|
+
*/
|
|
13
|
+
export const dddLegendIcon = html `<svg
|
|
14
|
+
width="24"
|
|
15
|
+
height="24"
|
|
16
|
+
viewBox="0 0 24 24"
|
|
17
|
+
fill="none"
|
|
18
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
19
|
+
>
|
|
20
|
+
<rect
|
|
21
|
+
x="3"
|
|
22
|
+
y="4"
|
|
23
|
+
width="18"
|
|
24
|
+
height="16"
|
|
25
|
+
rx="2"
|
|
26
|
+
stroke="currentColor"
|
|
27
|
+
stroke-width="1.6"
|
|
28
|
+
/>
|
|
29
|
+
<circle cx="7" cy="9" r="1.6" fill="currentColor" />
|
|
30
|
+
<circle cx="7" cy="14" r="1.6" fill="currentColor" />
|
|
31
|
+
<path d="M11 9 H18 M11 14 H18" stroke="currentColor" stroke-width="1.4" />
|
|
32
|
+
</svg>`;
|
|
2
33
|
/** Senior-button glyph — Event Storming (overlapping stickies). */
|
|
3
34
|
export const eventStormingToolbarIcon = svg `<svg width="100%" height="100%" viewBox="0 0 56 56" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
4
35
|
<rect x="10" y="14" width="22" height="22" rx="2" transform="rotate(-6 21 25)" fill="#F5963B"/>
|
|
@@ -13,6 +13,8 @@ export declare abstract class DddSeniorButtonBase extends DddSeniorButtonBase_ba
|
|
|
13
13
|
type: typeof EmptyTool;
|
|
14
14
|
protected abstract menuTag: keyof HTMLElementTagNameMap;
|
|
15
15
|
protected abstract label: string;
|
|
16
|
+
/** i18n key resolved through the host catalogue; `label` is the fallback. */
|
|
17
|
+
protected abstract labelKey: string;
|
|
16
18
|
protected abstract icon: TemplateResult;
|
|
17
19
|
private _toggleMenu;
|
|
18
20
|
render(): TemplateResult<1>;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { DefaultTool } from '@formicoidea/labre-core/blocks/surface';
|
|
2
2
|
import { EmptyTool } from '@formicoidea/labre-core/gfx/pointer';
|
|
3
|
+
import { translateKey } from '@formicoidea/labre-core/shared/services';
|
|
3
4
|
import { EdgelessToolbarToolMixin } from '@formicoidea/labre-core/widgets/edgeless-toolbar';
|
|
4
5
|
import { SignalWatcher } from '@formicoidea/labre-core/global/lit';
|
|
5
6
|
import { css, html, LitElement } from 'lit';
|
|
@@ -65,7 +66,9 @@ export class DddSeniorButtonBase extends EdgelessToolbarToolMixin(SignalWatcher(
|
|
|
65
66
|
render() {
|
|
66
67
|
return html `<edgeless-toolbar-button
|
|
67
68
|
class="ddd-button"
|
|
68
|
-
.tooltip=${this.popper
|
|
69
|
+
.tooltip=${this.popper
|
|
70
|
+
? ''
|
|
71
|
+
: translateKey(this.edgeless.std, this.labelKey, this.label)}
|
|
69
72
|
.tooltipOffset=${4}
|
|
70
73
|
.active=${!!this.popper}
|
|
71
74
|
@click=${this._toggleMenu}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@formicoidea/labre-ddd-shared",
|
|
3
3
|
"description": "Labre ddd-shared — shared building blocks for @formicoidea/labre-core frameworks.",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.33.0",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"sideEffects": false,
|
|
7
7
|
"author": "lajola",
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
"dist"
|
|
20
20
|
],
|
|
21
21
|
"dependencies": {
|
|
22
|
-
"@formicoidea/labre-core": "0.
|
|
22
|
+
"@formicoidea/labre-core": "0.33.0",
|
|
23
23
|
"lit": "^3.2.0"
|
|
24
24
|
}
|
|
25
25
|
}
|