@formicoidea/labre-framework-uml 0.42.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/LICENSE +384 -0
- package/dist/actions.d.ts +430 -0
- package/dist/actions.js +724 -0
- package/dist/background.d.ts +153 -0
- package/dist/background.js +557 -0
- package/dist/board-hit.d.ts +137 -0
- package/dist/board-hit.js +191 -0
- package/dist/commands-manifest.d.ts +19 -0
- package/dist/commands-manifest.js +571 -0
- package/dist/commands.d.ts +4 -0
- package/dist/commands.js +983 -0
- package/dist/component.d.ts +521 -0
- package/dist/component.js +708 -0
- package/dist/consts.d.ts +365 -0
- package/dist/consts.js +463 -0
- package/dist/descriptor.d.ts +12 -0
- package/dist/descriptor.js +10 -0
- package/dist/drawio-decode.d.ts +32 -0
- package/dist/drawio-decode.js +96 -0
- package/dist/drawio-import.d.ts +133 -0
- package/dist/drawio-import.js +885 -0
- package/dist/edge-morph.d.ts +154 -0
- package/dist/edge-morph.js +153 -0
- package/dist/edge-styles.d.ts +78 -0
- package/dist/edge-styles.js +210 -0
- package/dist/effects.d.ts +9 -0
- package/dist/effects.js +6 -0
- package/dist/element-renderer.d.ts +64 -0
- package/dist/element-renderer.js +114 -0
- package/dist/element-view.d.ts +234 -0
- package/dist/element-view.js +430 -0
- package/dist/export.d.ts +61 -0
- package/dist/export.js +41 -0
- package/dist/filename.d.ts +17 -0
- package/dist/filename.js +26 -0
- package/dist/frame-tag.d.ts +70 -0
- package/dist/frame-tag.js +78 -0
- package/dist/grammar.d.ts +695 -0
- package/dist/grammar.js +1436 -0
- package/dist/import.d.ts +306 -0
- package/dist/import.js +1270 -0
- package/dist/index.d.ts +47 -0
- package/dist/index.js +67 -0
- package/dist/interchange.d.ts +125 -0
- package/dist/interchange.js +372 -0
- package/dist/keywords.d.ts +204 -0
- package/dist/keywords.js +361 -0
- package/dist/kinds.d.ts +73 -0
- package/dist/kinds.js +91 -0
- package/dist/legend.d.ts +30 -0
- package/dist/legend.js +255 -0
- package/dist/model.d.ts +862 -0
- package/dist/model.js +1316 -0
- package/dist/morph.d.ts +178 -0
- package/dist/morph.js +496 -0
- package/dist/node/act-glyphs.d.ts +30 -0
- package/dist/node/act-glyphs.js +142 -0
- package/dist/node/compartment-watcher.d.ts +107 -0
- package/dist/node/compartment-watcher.js +163 -0
- package/dist/node/fit.d.ts +85 -0
- package/dist/node/fit.js +129 -0
- package/dist/node/node-renderer.d.ts +49 -0
- package/dist/node/node-renderer.js +611 -0
- package/dist/node/node-view.d.ts +51 -0
- package/dist/node/node-view.js +89 -0
- package/dist/node/paint.d.ts +84 -0
- package/dist/node/paint.js +143 -0
- package/dist/node/sd-glyphs.d.ts +36 -0
- package/dist/node/sd-glyphs.js +79 -0
- package/dist/node/stm-glyphs.d.ts +22 -0
- package/dist/node/stm-glyphs.js +111 -0
- package/dist/node/tier-metrics.d.ts +38 -0
- package/dist/node/tier-metrics.js +45 -0
- package/dist/operators.d.ts +45 -0
- package/dist/operators.js +75 -0
- package/dist/plantuml-import.d.ts +113 -0
- package/dist/plantuml-import.js +1723 -0
- package/dist/plantuml.d.ts +94 -0
- package/dist/plantuml.js +985 -0
- package/dist/presets.d.ts +149 -0
- package/dist/presets.js +302 -0
- package/dist/profiles.d.ts +2 -0
- package/dist/profiles.js +358 -0
- package/dist/reading.d.ts +160 -0
- package/dist/reading.js +648 -0
- package/dist/roles.d.ts +257 -0
- package/dist/roles.js +997 -0
- package/dist/rules.d.ts +267 -0
- package/dist/rules.js +3090 -0
- package/dist/templates.d.ts +2 -0
- package/dist/templates.js +197 -0
- package/dist/toolbar/config.d.ts +228 -0
- package/dist/toolbar/config.js +833 -0
- package/dist/toolbar/icons.d.ts +440 -0
- package/dist/toolbar/icons.js +711 -0
- package/dist/toolbar/senior-tool.d.ts +11 -0
- package/dist/toolbar/senior-tool.js +21 -0
- package/dist/toolbar/uml-menu.d.ts +17 -0
- package/dist/toolbar/uml-menu.js +20 -0
- package/dist/toolbar/uml-senior-button.d.ts +18 -0
- package/dist/toolbar/uml-senior-button.js +22 -0
- package/dist/translations.d.ts +27 -0
- package/dist/translations.js +117 -0
- package/dist/view.d.ts +33 -0
- package/dist/view.js +236 -0
- package/dist/xmi-import.d.ts +174 -0
- package/dist/xmi-import.js +2268 -0
- package/dist/xmi.d.ts +61 -0
- package/dist/xmi.js +1860 -0
- package/dist/xml-reader.d.ts +157 -0
- package/dist/xml-reader.js +430 -0
- package/dist/xml.d.ts +72 -0
- package/dist/xml.js +96 -0
- package/package.json +38 -0
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
import { type MorphSpec } from '@formicoidea/labre-core/blocks/surface';
|
|
2
|
+
import type { RoleId } from '@formicoidea/labre-core/std/gfx';
|
|
3
|
+
import { type UmlEdgeRole } from './edge-styles.js';
|
|
4
|
+
/**
|
|
5
|
+
* What a UML RELATIONSHIP may become — the same dropdown as the node morph, on a
|
|
6
|
+
* selected connector.
|
|
7
|
+
*
|
|
8
|
+
* ## Why an edge needs this more than a node does
|
|
9
|
+
*
|
|
10
|
+
* Because an edge is the thing a modeller gets wrong most often, and redrawing
|
|
11
|
+
* it is the most expensive to undo. A node morph saves a box and three lines of
|
|
12
|
+
* words; an edge morph saves the two ENDPOINTS — which element each end is
|
|
13
|
+
* attached to, and where on its perimeter — and those are precisely what a
|
|
14
|
+
* delete-and-redraw destroys. "This is really a composition, not an
|
|
15
|
+
* association", discovered halfway through a class diagram, is modelling
|
|
16
|
+
* (§11.5.3 is a whole clause about when it is which), and today the only way
|
|
17
|
+
* through it is to delete the line, re-drag it between the same two boxes and
|
|
18
|
+
* hope both ends reattach where they were.
|
|
19
|
+
*
|
|
20
|
+
* ## The four families, and why they are four
|
|
21
|
+
*
|
|
22
|
+
* Grouped by what the relationship IS, which is also what the specification
|
|
23
|
+
* groups them by:
|
|
24
|
+
*
|
|
25
|
+
* - **association, aggregation, composition, communication path** — §11.5 and
|
|
26
|
+
* §19.4.4: one metaclass (Association), and the difference between the first
|
|
27
|
+
* three is `aggregation` = none / shared / composite. These are literally the
|
|
28
|
+
* same model element with one property changed, which is as close to "the
|
|
29
|
+
* same thing said more precisely" as UML gets. A CommunicationPath joins them
|
|
30
|
+
* rather than standing alone because §19.4.4 defines it AS an Association
|
|
31
|
+
* between nodes and draws it as one — a plain solid line — so a line an
|
|
32
|
+
* author drew between two servers and one they drew between two classes are
|
|
33
|
+
* the same drawing, and the swap is the correction a reader of a deployment
|
|
34
|
+
* diagram actually needs;
|
|
35
|
+
* - **generalization, realization** — §9.9.7 and §10.4.3: both say "this one
|
|
36
|
+
* conforms to that one", both are drawn with the hollow triangle, and the
|
|
37
|
+
* dashed line is the whole visual difference. A designer who drew a
|
|
38
|
+
* generalization to an interface meant a realization often enough that the
|
|
39
|
+
* swap is the point;
|
|
40
|
+
* - **dependency, include, extend** — §7.8 and §18.1.4: all three are
|
|
41
|
+
* dependencies drawn identically and told apart by their keyword alone, which
|
|
42
|
+
* is exactly the case where a picture cannot show a mistake and a dropdown
|
|
43
|
+
* can fix one;
|
|
44
|
+
* - **deploy, manifest** — §19.2.4 and §19.3.4: both are dependencies FROM an
|
|
45
|
+
* artifact drawn as that same dashed arrow, and what separates them is which
|
|
46
|
+
* end it lands on — a node (this artifact runs there) or a component (this
|
|
47
|
+
* artifact IS that component, in the flesh). They are their own family rather
|
|
48
|
+
* than three more members of the one above precisely because the confusion is
|
|
49
|
+
* between the two of them: an author who dropped a `.war` on a diagram and
|
|
50
|
+
* arrowed it at the wrong neighbour has made the one mistake this dropdown
|
|
51
|
+
* fixes, and offering them `«include»` alongside would be offering a use-case
|
|
52
|
+
* relationship on a deployment diagram.
|
|
53
|
+
*
|
|
54
|
+
* - **control flow, object flow** — §15.2.4: one drawing (a solid open arrow),
|
|
55
|
+
* told apart by what the line runs BETWEEN rather than by anything on it.
|
|
56
|
+
*
|
|
57
|
+
* - **synchronous, asynchronous and reply messages** — §17.4.4: one line, told
|
|
58
|
+
* apart by a filled head, an open head and a dash. See the declaration.
|
|
59
|
+
*
|
|
60
|
+
* ## The anchor, the transition and the two placed messages are in no family
|
|
61
|
+
*
|
|
62
|
+
* Deliberately, and the anchor is the edge equivalent of C4's `component`: an
|
|
63
|
+
* anchor joins a note to what it comments on (Annex A). It is not a
|
|
64
|
+
* relationship between classifiers at all, it carries no semantics, and
|
|
65
|
+
* offering to turn one into a composition would invite a diagram claiming that
|
|
66
|
+
* a comment owns a class. A kind in no family never offers the menu, so an
|
|
67
|
+
* anchor's toolbar is the plain connector's.
|
|
68
|
+
*
|
|
69
|
+
* A TRANSITION is alone for the opposite reason: it is drawn exactly like the
|
|
70
|
+
* two activity edges beside it (§14.2.4.8 and §15.2.4 are the same solid open
|
|
71
|
+
* arrow), and it is still not one of them, because it lives on the other
|
|
72
|
+
* diagram. A state machine's arrow joins two STATES and carries
|
|
73
|
+
* `trigger [guard] / effect`; an activity's joins two ACTIONS and carries a
|
|
74
|
+
* guard. Offering the swap would put an activity edge on a state machine, which
|
|
75
|
+
* is what the per-frame admissibility lists exist to refuse — a dropdown must
|
|
76
|
+
* not hand a user the error the audit is about to report.
|
|
77
|
+
*
|
|
78
|
+
* A CREATE and a DELETE message are alone for a third reason, and it is about
|
|
79
|
+
* WHERE THE LINE LANDS rather than about what it looks like. §17.4.4 draws a
|
|
80
|
+
* create message exactly like a reply and a delete message exactly like a
|
|
81
|
+
* synchronous call, and what makes each of them what it is, is its far end: a
|
|
82
|
+
* create arrives on the HEAD of the lifeline it brings into existence, and a
|
|
83
|
+
* delete arrives at a destruction cross. Offering them in the family above
|
|
84
|
+
* would let one click retype an ordinary call as a creation whose target is a
|
|
85
|
+
* spine — a line the drawing cannot show as wrong and `uml.message-endpoints`
|
|
86
|
+
* is about to report. The swap a modeller actually wants there is to move the
|
|
87
|
+
* end, which the canvas already does.
|
|
88
|
+
*/
|
|
89
|
+
export type UmlEdgeKind = UmlEdgeRole;
|
|
90
|
+
/**
|
|
91
|
+
* Every edge kind the pack draws — derived from the style table, which is the
|
|
92
|
+
* one place they are enumerated.
|
|
93
|
+
*/
|
|
94
|
+
export declare const UML_EDGE_KINDS: UmlEdgeKind[];
|
|
95
|
+
/** See the module docblock: four families, and the anchor in none of them. */
|
|
96
|
+
export declare const UML_EDGE_FAMILIES: readonly (readonly UmlEdgeKind[])[];
|
|
97
|
+
/**
|
|
98
|
+
* Which UML edge a connector's ROLE says it is — `undefined` for every connector
|
|
99
|
+
* that is not one of ours.
|
|
100
|
+
*
|
|
101
|
+
* `Partial` is the honest type rather than a defensive one: this question is
|
|
102
|
+
* asked of EVERY connector a user selects, and most of them are plain lines, a
|
|
103
|
+
* BPMN sequence flow or a C4 relationship. A total `Record<RoleId, …>` would
|
|
104
|
+
* type that answer away and leave `kindOf` promising a kind for a role it has
|
|
105
|
+
* never heard of.
|
|
106
|
+
*
|
|
107
|
+
* Derived from {@link UML_ROLE}, so a relationship renamed in the vocabulary is
|
|
108
|
+
* renamed here in the same edit.
|
|
109
|
+
*/
|
|
110
|
+
export declare const UML_EDGE_KIND_OF_ROLE: Readonly<Partial<Record<RoleId, UmlEdgeKind>>>;
|
|
111
|
+
/**
|
|
112
|
+
* The WHOLE patch one relationship kind is worth — the same six props
|
|
113
|
+
* `activateUmlEdge` arms a fresh connector with, read off the same table.
|
|
114
|
+
*
|
|
115
|
+
* ## Why all six and not just the role
|
|
116
|
+
*
|
|
117
|
+
* Because on this table it is visibly not enough: a composition and an
|
|
118
|
+
* association differ ONLY in the filled diamond on the front end, a realization
|
|
119
|
+
* and a generalization ONLY in the dashes. A `{role}` patch would write the
|
|
120
|
+
* meaning and leave the picture stating the previous one — a line the exporter
|
|
121
|
+
* calls a composition and a reader calls an association, which is the single
|
|
122
|
+
* worst outcome a notation pack can produce.
|
|
123
|
+
*
|
|
124
|
+
* The ink and the weight ride along for the reason the node morph's whole preset
|
|
125
|
+
* does: a morphed edge and one freshly drawn from the toolbox must be the same
|
|
126
|
+
* element, and an edge somebody restyled by hand is an edge whose style this
|
|
127
|
+
* gesture is explicitly resetting.
|
|
128
|
+
*
|
|
129
|
+
* ## The one prop that is NOT here
|
|
130
|
+
*
|
|
131
|
+
* `mode`. The router is a LAYOUT decision — straight, orthogonal, curved — and a
|
|
132
|
+
* user who dragged a line into an elbow to get it round a box did that to the
|
|
133
|
+
* drawing, not to the relationship. It is the geometry of the edge in the sense
|
|
134
|
+
* `xywh` is the geometry of a node, and the morph's contract is that geometry is
|
|
135
|
+
* the user's. The omission is inert on today's table anyway, since all nine are
|
|
136
|
+
* armed straight; it is stated so that it stays inert on purpose.
|
|
137
|
+
*/
|
|
138
|
+
export declare function umlEdgeProps(kind: UmlEdgeKind): Record<string, unknown>;
|
|
139
|
+
/**
|
|
140
|
+
* UML's edge morph declaration, handed to the generic `morphToolbarConfig`.
|
|
141
|
+
*
|
|
142
|
+
* `modelType` is `ConnectorElementModel` and there is NO `resolveTarget`: unlike
|
|
143
|
+
* a classifier, a relationship is one element — what the user selects, what
|
|
144
|
+
* carries the role and what the patch lands on are the same object. The gate is
|
|
145
|
+
* therefore entirely in {@link kindOf}: a plain connector, a BPMN sequence flow
|
|
146
|
+
* and a C4 relationship all carry a role this table does not know (or none at
|
|
147
|
+
* all), so they answer `undefined` and are never offered the menu.
|
|
148
|
+
*
|
|
149
|
+
* `clearOf` is empty, and hard-coded rather than derived: every kind writes the
|
|
150
|
+
* same six keys, because they all come from one table with no conditional
|
|
151
|
+
* spread in it. The day a row grows a key the others lack, this is where the
|
|
152
|
+
* deletion goes.
|
|
153
|
+
*/
|
|
154
|
+
export declare const UML_EDGE_MORPH_SPEC: MorphSpec<UmlEdgeKind>;
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
import { morphLabel, } from '@formicoidea/labre-core/blocks/surface';
|
|
2
|
+
import { ConnectorElementModel } from '@formicoidea/labre-core/model';
|
|
3
|
+
import { umlCommandIcons, umlCommands } from './commands.js';
|
|
4
|
+
import { UML_EDGE_WIDTH, UML_INK } from './consts.js';
|
|
5
|
+
import { UML_EDGE_STYLE } from './edge-styles.js';
|
|
6
|
+
import { UML_ROLE } from './roles.js';
|
|
7
|
+
/**
|
|
8
|
+
* Every edge kind the pack draws — derived from the style table, which is the
|
|
9
|
+
* one place they are enumerated.
|
|
10
|
+
*/
|
|
11
|
+
export const UML_EDGE_KINDS = Object.keys(UML_EDGE_STYLE);
|
|
12
|
+
/** See the module docblock: four families, and the anchor in none of them. */
|
|
13
|
+
export const UML_EDGE_FAMILIES = [
|
|
14
|
+
// Declaration order is menu order, and each family opens on its PLAIN member:
|
|
15
|
+
// the undecorated relationship is the honest first draft and the decorated one
|
|
16
|
+
// is the refinement — the same call the node morph and the sub-menu make.
|
|
17
|
+
// `communication-path` sits here because it is DRAWN as an association
|
|
18
|
+
// (§19.4.4) — while `roles.ts` files `uml:communication-path` flat, outside
|
|
19
|
+
// `uml:association`, so that association rules and readings never reach a
|
|
20
|
+
// deployment sheet. Two answers, two questions (drawing vs rule reach).
|
|
21
|
+
['association', 'aggregation', 'composition', 'communication-path'],
|
|
22
|
+
['generalization', 'realization'],
|
|
23
|
+
['dependency', 'include', 'extend'],
|
|
24
|
+
// APPENDED, never inserted: the three families above keep their index, which
|
|
25
|
+
// is what lets the suite go on naming the dependency family by position.
|
|
26
|
+
['deploy', 'manifest'],
|
|
27
|
+
// §15.2.4 — the two ACTIVITY edges, and the fifth family. Both are the same
|
|
28
|
+
// solid open arrow and the specification tells them apart by what the line
|
|
29
|
+
// runs BETWEEN: a flow that touches an object node is an object flow, every
|
|
30
|
+
// other one is a control flow. That is precisely the mistake a modeller makes
|
|
31
|
+
// — drawing the arrow through a data object and leaving it typed as control —
|
|
32
|
+
// and it is invisible on the canvas, which is the case a dropdown exists for.
|
|
33
|
+
['control-flow', 'object-flow'],
|
|
34
|
+
// §17.4.4 — the three MESSAGES an interaction is mostly made of, and the
|
|
35
|
+
// sixth family. This is the case the edge morph exists for, more clearly than
|
|
36
|
+
// any family above it: the three are one line told apart by a FILLED head, an
|
|
37
|
+
// open head and a dash, the distinction is the whole semantics of a sequence
|
|
38
|
+
// diagram — does the caller wait, or not — and it is exactly what a modeller
|
|
39
|
+
// gets backwards while laying out a conversation. Redrawing the line would
|
|
40
|
+
// cost both ends, which on a sequence diagram is the HEIGHT the message
|
|
41
|
+
// happens at as well as which participants it runs between.
|
|
42
|
+
//
|
|
43
|
+
// `message-sync` opens it, as every family above opens on its plain member:
|
|
44
|
+
// a call whose caller waits is what `a -> b` means everywhere, and the other
|
|
45
|
+
// two are the refinements.
|
|
46
|
+
['message-sync', 'message-async', 'message-reply'],
|
|
47
|
+
];
|
|
48
|
+
/**
|
|
49
|
+
* Which UML edge a connector's ROLE says it is — `undefined` for every connector
|
|
50
|
+
* that is not one of ours.
|
|
51
|
+
*
|
|
52
|
+
* `Partial` is the honest type rather than a defensive one: this question is
|
|
53
|
+
* asked of EVERY connector a user selects, and most of them are plain lines, a
|
|
54
|
+
* BPMN sequence flow or a C4 relationship. A total `Record<RoleId, …>` would
|
|
55
|
+
* type that answer away and leave `kindOf` promising a kind for a role it has
|
|
56
|
+
* never heard of.
|
|
57
|
+
*
|
|
58
|
+
* Derived from {@link UML_ROLE}, so a relationship renamed in the vocabulary is
|
|
59
|
+
* renamed here in the same edit.
|
|
60
|
+
*/
|
|
61
|
+
export const UML_EDGE_KIND_OF_ROLE = Object.fromEntries(UML_EDGE_KINDS.map(kind => [UML_ROLE[kind], kind]));
|
|
62
|
+
/**
|
|
63
|
+
* The creation command that arms each edge, keyed BY its kind.
|
|
64
|
+
*
|
|
65
|
+
* Derived from `telemetry.element` (`connector:composition`) exactly as the node
|
|
66
|
+
* morph derives its own from `node:interface`: the kind is already written down
|
|
67
|
+
* there, and a second table of labels and icons would drift from the sub-menu's.
|
|
68
|
+
*/
|
|
69
|
+
const EDGE_COMMANDS = new Map(umlCommands.flatMap(command => {
|
|
70
|
+
const element = command.telemetry?.element;
|
|
71
|
+
return element?.startsWith('connector:')
|
|
72
|
+
? [[element.slice('connector:'.length), command]]
|
|
73
|
+
: [];
|
|
74
|
+
}));
|
|
75
|
+
/** A kind's wording: the tool command's own key and English. */
|
|
76
|
+
function labelOf(kind) {
|
|
77
|
+
const command = EDGE_COMMANDS.get(kind);
|
|
78
|
+
return {
|
|
79
|
+
key: command?.labelKey,
|
|
80
|
+
fallback: command?.labelFallback ?? kind,
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
/** A kind's icon: the tool command's own, reused rather than redrawn. */
|
|
84
|
+
function iconOf(kind) {
|
|
85
|
+
const iconKey = EDGE_COMMANDS.get(kind)?.iconKey;
|
|
86
|
+
return ((iconKey && umlCommandIcons[iconKey]) || umlCommandIcons['uml.association']);
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* The WHOLE patch one relationship kind is worth — the same six props
|
|
90
|
+
* `activateUmlEdge` arms a fresh connector with, read off the same table.
|
|
91
|
+
*
|
|
92
|
+
* ## Why all six and not just the role
|
|
93
|
+
*
|
|
94
|
+
* Because on this table it is visibly not enough: a composition and an
|
|
95
|
+
* association differ ONLY in the filled diamond on the front end, a realization
|
|
96
|
+
* and a generalization ONLY in the dashes. A `{role}` patch would write the
|
|
97
|
+
* meaning and leave the picture stating the previous one — a line the exporter
|
|
98
|
+
* calls a composition and a reader calls an association, which is the single
|
|
99
|
+
* worst outcome a notation pack can produce.
|
|
100
|
+
*
|
|
101
|
+
* The ink and the weight ride along for the reason the node morph's whole preset
|
|
102
|
+
* does: a morphed edge and one freshly drawn from the toolbox must be the same
|
|
103
|
+
* element, and an edge somebody restyled by hand is an edge whose style this
|
|
104
|
+
* gesture is explicitly resetting.
|
|
105
|
+
*
|
|
106
|
+
* ## The one prop that is NOT here
|
|
107
|
+
*
|
|
108
|
+
* `mode`. The router is a LAYOUT decision — straight, orthogonal, curved — and a
|
|
109
|
+
* user who dragged a line into an elbow to get it round a box did that to the
|
|
110
|
+
* drawing, not to the relationship. It is the geometry of the edge in the sense
|
|
111
|
+
* `xywh` is the geometry of a node, and the morph's contract is that geometry is
|
|
112
|
+
* the user's. The omission is inert on today's table anyway, since all nine are
|
|
113
|
+
* armed straight; it is stated so that it stays inert on purpose.
|
|
114
|
+
*/
|
|
115
|
+
export function umlEdgeProps(kind) {
|
|
116
|
+
return {
|
|
117
|
+
role: UML_ROLE[kind],
|
|
118
|
+
stroke: UML_INK,
|
|
119
|
+
strokeWidth: UML_EDGE_WIDTH,
|
|
120
|
+
...UML_EDGE_STYLE[kind],
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* UML's edge morph declaration, handed to the generic `morphToolbarConfig`.
|
|
125
|
+
*
|
|
126
|
+
* `modelType` is `ConnectorElementModel` and there is NO `resolveTarget`: unlike
|
|
127
|
+
* a classifier, a relationship is one element — what the user selects, what
|
|
128
|
+
* carries the role and what the patch lands on are the same object. The gate is
|
|
129
|
+
* therefore entirely in {@link kindOf}: a plain connector, a BPMN sequence flow
|
|
130
|
+
* and a C4 relationship all carry a role this table does not know (or none at
|
|
131
|
+
* all), so they answer `undefined` and are never offered the menu.
|
|
132
|
+
*
|
|
133
|
+
* `clearOf` is empty, and hard-coded rather than derived: every kind writes the
|
|
134
|
+
* same six keys, because they all come from one table with no conditional
|
|
135
|
+
* spread in it. The day a row grows a key the others lack, this is where the
|
|
136
|
+
* deletion goes.
|
|
137
|
+
*/
|
|
138
|
+
export const UML_EDGE_MORPH_SPEC = {
|
|
139
|
+
framework: 'uml',
|
|
140
|
+
families: UML_EDGE_FAMILIES,
|
|
141
|
+
modelType: ConnectorElementModel,
|
|
142
|
+
kindOf: (model) => model.role === undefined ? undefined : UML_EDGE_KIND_OF_ROLE[model.role],
|
|
143
|
+
// A relationship's kind IS its role, one for one — there is no collapsing to
|
|
144
|
+
// do here, unlike the node table where two C4 kinds share one role.
|
|
145
|
+
roleOf: kind => UML_ROLE[kind],
|
|
146
|
+
propsOf: umlEdgeProps,
|
|
147
|
+
clearOf: () => [],
|
|
148
|
+
labelOf,
|
|
149
|
+
iconOf,
|
|
150
|
+
// The same wording as the node dropdown, and the same key: a user meets one
|
|
151
|
+
// affordance called one thing, whether what they selected is a box or a line.
|
|
152
|
+
label: morphLabel('com.labre.morph.toolbar.label', 'Change type'),
|
|
153
|
+
};
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { PointStyle, StrokeStyle } from '@formicoidea/labre-core/model';
|
|
2
|
+
/**
|
|
3
|
+
* The UML line notation, as data — read by the TOOL that arms a fresh connector
|
|
4
|
+
* (`actions.ts`) and by the MORPH that retypes one already drawn
|
|
5
|
+
* (`edge-morph.ts`).
|
|
6
|
+
*
|
|
7
|
+
* It lives in a module of its own for exactly that reason. A relationship drawn
|
|
8
|
+
* from the toolbox and one an author changed his mind about must be the same
|
|
9
|
+
* element down to the last endpoint, and the only way to guarantee that is for
|
|
10
|
+
* the two sites to read one table rather than to agree about two. The file also
|
|
11
|
+
* keeps the morph — pure data, no editor — free of `actions.ts`, which pulls in
|
|
12
|
+
* the std scope, the gfx controller and the surface.
|
|
13
|
+
*/
|
|
14
|
+
/**
|
|
15
|
+
* The edges the toolbox arms, by their role's local name.
|
|
16
|
+
*
|
|
17
|
+
* APPEND-ONLY, and the three at the bottom are phase 2's: a deployment diagram
|
|
18
|
+
* is drawn with lines of its own (§19.2.4, §19.3.4, §19.4.4) and they are added
|
|
19
|
+
* after the nine rather than filed among them, so a union somebody reads top to
|
|
20
|
+
* bottom still tells the story of how the pack grew.
|
|
21
|
+
*/
|
|
22
|
+
export type UmlEdgeRole = 'association' | 'aggregation' | 'composition' | 'generalization' | 'realization' | 'dependency' | 'anchor' | 'include' | 'extend' | 'deploy' | 'manifest' | 'communication-path' | 'control-flow' | 'object-flow' | 'transition' | 'message-sync' | 'message-async' | 'message-reply' | 'message-create' | 'message-delete';
|
|
23
|
+
/** The three style props an edge kind actually differs on. */
|
|
24
|
+
export interface UmlEdgeStyle {
|
|
25
|
+
strokeStyle: StrokeStyle;
|
|
26
|
+
frontEndpointStyle: PointStyle;
|
|
27
|
+
rearEndpointStyle: PointStyle;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* The whole UML line notation, in one table — §11.5.4 (association, aggregation
|
|
31
|
+
* and composition), §9.2.4 (generalization), §10.4.4 (interface realization),
|
|
32
|
+
* §7.8.4 (dependency and anchor), §18.1.4 (include, extend) and, since phase 2,
|
|
33
|
+
* §19.2.4 (deployment), §19.3.4 (manifestation) and §19.4.4 (communication
|
|
34
|
+
* path).
|
|
35
|
+
*
|
|
36
|
+
* ## Three props, and never a fourth
|
|
37
|
+
*
|
|
38
|
+
* UML tells its relationships apart by TWO things and only two: whether the line
|
|
39
|
+
* is solid or dashed, and what is drawn on its ends. Never by colour — the
|
|
40
|
+
* specification has none — and never by thickness, which is why `UML_EDGE_WIDTH`
|
|
41
|
+
* is one constant rather than a column here. A pack that distinguished an
|
|
42
|
+
* association from a dependency by making one heavier would be inventing a
|
|
43
|
+
* notation on top of the one it claims to implement.
|
|
44
|
+
*
|
|
45
|
+
* ## Which end carries the mark
|
|
46
|
+
*
|
|
47
|
+
* `front` is the SOURCE end, `rear` the target end, and the difference is the
|
|
48
|
+
* whole semantics of four of these nine:
|
|
49
|
+
*
|
|
50
|
+
* - **aggregation / composition** put their diamond on the FRONT, because the
|
|
51
|
+
* source of these roles is the WHOLE (`roles.ts`: "is composed of"). Hollow
|
|
52
|
+
* for shared aggregation, filled for composite — §11.5.4's one visual
|
|
53
|
+
* difference, and the one everybody misdraws;
|
|
54
|
+
* - **generalization / realization** put a hollow triangle on the REAR, because
|
|
55
|
+
* their source is the SPECIFIC one ("is a", "realizes") and the triangle
|
|
56
|
+
* points at the general. Solid for generalization, dashed for realization:
|
|
57
|
+
* §10.4.4 draws a realization as the generalization arrow on a broken line,
|
|
58
|
+
* and that is the only thing that separates them.
|
|
59
|
+
*
|
|
60
|
+
* ## The five that share a look
|
|
61
|
+
*
|
|
62
|
+
* A dependency, an include, an extend, a deployment and a manifestation are all
|
|
63
|
+
* a dashed line with an open arrowhead, and the specification is explicit that
|
|
64
|
+
* they are told apart by the KEYWORD written on them («use», «include»,
|
|
65
|
+
* «extend», «deploy», «manifest» — §7.8.4, §18.1.4, §19.2.4, §19.3.4), not by
|
|
66
|
+
* their drawing: all five ARE Dependencies. So they share a row here and differ
|
|
67
|
+
* only by the ROLE, which is what the export writes the keyword from. Nothing on
|
|
68
|
+
* the canvas is lost: the role is what the audit, the reading profile and the
|
|
69
|
+
* exporter all read.
|
|
70
|
+
*
|
|
71
|
+
* ## The anchor
|
|
72
|
+
*
|
|
73
|
+
* Dashed with nothing on either end (Annex A): an anchor joins a note to what it
|
|
74
|
+
* comments on and claims no direction, so an arrowhead would be the picture
|
|
75
|
+
* making a statement the role explicitly refuses to make — the same argument
|
|
76
|
+
* BPMN's association makes for the same absence.
|
|
77
|
+
*/
|
|
78
|
+
export declare const UML_EDGE_STYLE: Record<UmlEdgeRole, UmlEdgeStyle>;
|
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
import { PointStyle, StrokeStyle } from '@formicoidea/labre-core/model';
|
|
2
|
+
/**
|
|
3
|
+
* The whole UML line notation, in one table — §11.5.4 (association, aggregation
|
|
4
|
+
* and composition), §9.2.4 (generalization), §10.4.4 (interface realization),
|
|
5
|
+
* §7.8.4 (dependency and anchor), §18.1.4 (include, extend) and, since phase 2,
|
|
6
|
+
* §19.2.4 (deployment), §19.3.4 (manifestation) and §19.4.4 (communication
|
|
7
|
+
* path).
|
|
8
|
+
*
|
|
9
|
+
* ## Three props, and never a fourth
|
|
10
|
+
*
|
|
11
|
+
* UML tells its relationships apart by TWO things and only two: whether the line
|
|
12
|
+
* is solid or dashed, and what is drawn on its ends. Never by colour — the
|
|
13
|
+
* specification has none — and never by thickness, which is why `UML_EDGE_WIDTH`
|
|
14
|
+
* is one constant rather than a column here. A pack that distinguished an
|
|
15
|
+
* association from a dependency by making one heavier would be inventing a
|
|
16
|
+
* notation on top of the one it claims to implement.
|
|
17
|
+
*
|
|
18
|
+
* ## Which end carries the mark
|
|
19
|
+
*
|
|
20
|
+
* `front` is the SOURCE end, `rear` the target end, and the difference is the
|
|
21
|
+
* whole semantics of four of these nine:
|
|
22
|
+
*
|
|
23
|
+
* - **aggregation / composition** put their diamond on the FRONT, because the
|
|
24
|
+
* source of these roles is the WHOLE (`roles.ts`: "is composed of"). Hollow
|
|
25
|
+
* for shared aggregation, filled for composite — §11.5.4's one visual
|
|
26
|
+
* difference, and the one everybody misdraws;
|
|
27
|
+
* - **generalization / realization** put a hollow triangle on the REAR, because
|
|
28
|
+
* their source is the SPECIFIC one ("is a", "realizes") and the triangle
|
|
29
|
+
* points at the general. Solid for generalization, dashed for realization:
|
|
30
|
+
* §10.4.4 draws a realization as the generalization arrow on a broken line,
|
|
31
|
+
* and that is the only thing that separates them.
|
|
32
|
+
*
|
|
33
|
+
* ## The five that share a look
|
|
34
|
+
*
|
|
35
|
+
* A dependency, an include, an extend, a deployment and a manifestation are all
|
|
36
|
+
* a dashed line with an open arrowhead, and the specification is explicit that
|
|
37
|
+
* they are told apart by the KEYWORD written on them («use», «include»,
|
|
38
|
+
* «extend», «deploy», «manifest» — §7.8.4, §18.1.4, §19.2.4, §19.3.4), not by
|
|
39
|
+
* their drawing: all five ARE Dependencies. So they share a row here and differ
|
|
40
|
+
* only by the ROLE, which is what the export writes the keyword from. Nothing on
|
|
41
|
+
* the canvas is lost: the role is what the audit, the reading profile and the
|
|
42
|
+
* exporter all read.
|
|
43
|
+
*
|
|
44
|
+
* ## The anchor
|
|
45
|
+
*
|
|
46
|
+
* Dashed with nothing on either end (Annex A): an anchor joins a note to what it
|
|
47
|
+
* comments on and claims no direction, so an arrowhead would be the picture
|
|
48
|
+
* making a statement the role explicitly refuses to make — the same argument
|
|
49
|
+
* BPMN's association makes for the same absence.
|
|
50
|
+
*/
|
|
51
|
+
export const UML_EDGE_STYLE = {
|
|
52
|
+
association: {
|
|
53
|
+
strokeStyle: StrokeStyle.Solid,
|
|
54
|
+
frontEndpointStyle: PointStyle.None,
|
|
55
|
+
rearEndpointStyle: PointStyle.None,
|
|
56
|
+
},
|
|
57
|
+
aggregation: {
|
|
58
|
+
strokeStyle: StrokeStyle.Solid,
|
|
59
|
+
frontEndpointStyle: PointStyle.DiamondHollow,
|
|
60
|
+
rearEndpointStyle: PointStyle.None,
|
|
61
|
+
},
|
|
62
|
+
composition: {
|
|
63
|
+
strokeStyle: StrokeStyle.Solid,
|
|
64
|
+
frontEndpointStyle: PointStyle.Diamond,
|
|
65
|
+
rearEndpointStyle: PointStyle.None,
|
|
66
|
+
},
|
|
67
|
+
generalization: {
|
|
68
|
+
strokeStyle: StrokeStyle.Solid,
|
|
69
|
+
frontEndpointStyle: PointStyle.None,
|
|
70
|
+
rearEndpointStyle: PointStyle.TriangleHollow,
|
|
71
|
+
},
|
|
72
|
+
realization: {
|
|
73
|
+
strokeStyle: StrokeStyle.Dash,
|
|
74
|
+
frontEndpointStyle: PointStyle.None,
|
|
75
|
+
rearEndpointStyle: PointStyle.TriangleHollow,
|
|
76
|
+
},
|
|
77
|
+
dependency: {
|
|
78
|
+
strokeStyle: StrokeStyle.Dash,
|
|
79
|
+
frontEndpointStyle: PointStyle.None,
|
|
80
|
+
rearEndpointStyle: PointStyle.Arrow,
|
|
81
|
+
},
|
|
82
|
+
include: {
|
|
83
|
+
strokeStyle: StrokeStyle.Dash,
|
|
84
|
+
frontEndpointStyle: PointStyle.None,
|
|
85
|
+
rearEndpointStyle: PointStyle.Arrow,
|
|
86
|
+
},
|
|
87
|
+
extend: {
|
|
88
|
+
strokeStyle: StrokeStyle.Dash,
|
|
89
|
+
frontEndpointStyle: PointStyle.None,
|
|
90
|
+
rearEndpointStyle: PointStyle.Arrow,
|
|
91
|
+
},
|
|
92
|
+
anchor: {
|
|
93
|
+
strokeStyle: StrokeStyle.Dash,
|
|
94
|
+
frontEndpointStyle: PointStyle.None,
|
|
95
|
+
rearEndpointStyle: PointStyle.None,
|
|
96
|
+
},
|
|
97
|
+
// A Deployment is a Dependency (§19.2.4): the artifact is the client, the node
|
|
98
|
+
// the supplier, and the picture is the dashed open arrow every dependency
|
|
99
|
+
// wears — told apart, as always, by the keyword the export writes from the
|
|
100
|
+
// role rather than by a drawing of its own.
|
|
101
|
+
deploy: {
|
|
102
|
+
strokeStyle: StrokeStyle.Dash,
|
|
103
|
+
frontEndpointStyle: PointStyle.None,
|
|
104
|
+
rearEndpointStyle: PointStyle.Arrow,
|
|
105
|
+
},
|
|
106
|
+
// A Manifestation is an Abstraction, and therefore a Dependency too (§19.3.4)
|
|
107
|
+
// — same line, from the artifact to the component it is the physical form of.
|
|
108
|
+
manifest: {
|
|
109
|
+
strokeStyle: StrokeStyle.Dash,
|
|
110
|
+
frontEndpointStyle: PointStyle.None,
|
|
111
|
+
rearEndpointStyle: PointStyle.Arrow,
|
|
112
|
+
},
|
|
113
|
+
// A CommunicationPath is an Association between two nodes (§19.4.4), so it is
|
|
114
|
+
// drawn exactly as one: a plain solid line, and nothing on either end. The
|
|
115
|
+
// absence of arrowheads is the statement — a network link between two servers
|
|
116
|
+
// has no client and no supplier, and a head would invent one.
|
|
117
|
+
'communication-path': {
|
|
118
|
+
strokeStyle: StrokeStyle.Solid,
|
|
119
|
+
frontEndpointStyle: PointStyle.None,
|
|
120
|
+
rearEndpointStyle: PointStyle.None,
|
|
121
|
+
},
|
|
122
|
+
// ── Behaviour: the third drawing this table has ever needed ────────────────
|
|
123
|
+
//
|
|
124
|
+
// §15.2.4 draws an ActivityEdge as a SOLID line with an OPEN ARROWHEAD, and
|
|
125
|
+
// §14.2.4.8 draws a Transition as exactly the same line. That is a look no
|
|
126
|
+
// structural relationship wears: an association is solid with nothing on it,
|
|
127
|
+
// a dependency is the arrowhead on a DASHED line. The distinction matters and
|
|
128
|
+
// is the specification's own — a behaviour diagram states an ORDER of things
|
|
129
|
+
// and therefore always points, where a class diagram states a relationship
|
|
130
|
+
// and mostly does not.
|
|
131
|
+
//
|
|
132
|
+
// Control flow and object flow share the row, as the five dependencies above
|
|
133
|
+
// share theirs and for the same reason: §15.2.4 tells them apart by what sits
|
|
134
|
+
// at the ENDS of the line — an object node at one end makes it an object flow
|
|
135
|
+
// — not by a decoration on the line, and the ROLE is what the audit and the
|
|
136
|
+
// exporter read that from. (An object flow drawn between two pins can be
|
|
137
|
+
// shown with the pins collapsed into a rectangle on the line; that is a
|
|
138
|
+
// §15.4.4 shorthand for the NODES, not a second line style, so it does not
|
|
139
|
+
// belong here.)
|
|
140
|
+
'control-flow': {
|
|
141
|
+
strokeStyle: StrokeStyle.Solid,
|
|
142
|
+
frontEndpointStyle: PointStyle.None,
|
|
143
|
+
rearEndpointStyle: PointStyle.Arrow,
|
|
144
|
+
},
|
|
145
|
+
'object-flow': {
|
|
146
|
+
strokeStyle: StrokeStyle.Solid,
|
|
147
|
+
frontEndpointStyle: PointStyle.None,
|
|
148
|
+
rearEndpointStyle: PointStyle.Arrow,
|
|
149
|
+
},
|
|
150
|
+
// A Transition (§14.2.4.8) is the same arrow, and the `trigger [guard] /
|
|
151
|
+
// effect` it carries is written on the line as its centre label rather than
|
|
152
|
+
// drawn into it — which is why this row is a copy of the two above and the
|
|
153
|
+
// grammar, not the style table, is where that syntax lives.
|
|
154
|
+
transition: {
|
|
155
|
+
strokeStyle: StrokeStyle.Solid,
|
|
156
|
+
frontEndpointStyle: PointStyle.None,
|
|
157
|
+
rearEndpointStyle: PointStyle.Arrow,
|
|
158
|
+
},
|
|
159
|
+
// ── Phase 3: the MESSAGES of a sequence diagram (§17.4.4) ─────────────────
|
|
160
|
+
//
|
|
161
|
+
// The one place in this table where the DRAWING carries the meaning rather
|
|
162
|
+
// than merely accompanying it. §17.4.4 is explicit, and the three shapes are
|
|
163
|
+
// the whole vocabulary of an interaction:
|
|
164
|
+
//
|
|
165
|
+
// - a SYNCHRONOUS call is a solid line with a FILLED arrowhead — the caller
|
|
166
|
+
// waits, and the black triangle is what says so;
|
|
167
|
+
// - an ASYNCHRONOUS message is a solid line with an OPEN (stick) arrowhead —
|
|
168
|
+
// the caller does not wait, and the open head is the difference;
|
|
169
|
+
// - a REPLY is a DASHED line with an open arrowhead, travelling back.
|
|
170
|
+
//
|
|
171
|
+
// `Triangle` and `Arrow` are the library's filled triangle and its open
|
|
172
|
+
// stick head, which is exactly the pair §17.4.4 draws. Getting them the wrong
|
|
173
|
+
// way round would make every call on the diagram say the opposite of what the
|
|
174
|
+
// author meant — the one notation mistake in this pack that a reader cannot
|
|
175
|
+
// recover from, because nothing else on the line says which it is.
|
|
176
|
+
'message-sync': {
|
|
177
|
+
strokeStyle: StrokeStyle.Solid,
|
|
178
|
+
frontEndpointStyle: PointStyle.None,
|
|
179
|
+
rearEndpointStyle: PointStyle.Triangle,
|
|
180
|
+
},
|
|
181
|
+
'message-async': {
|
|
182
|
+
strokeStyle: StrokeStyle.Solid,
|
|
183
|
+
frontEndpointStyle: PointStyle.None,
|
|
184
|
+
rearEndpointStyle: PointStyle.Arrow,
|
|
185
|
+
},
|
|
186
|
+
'message-reply': {
|
|
187
|
+
strokeStyle: StrokeStyle.Dash,
|
|
188
|
+
frontEndpointStyle: PointStyle.None,
|
|
189
|
+
rearEndpointStyle: PointStyle.Arrow,
|
|
190
|
+
},
|
|
191
|
+
// A CREATE message (§17.4.4) is drawn as a dashed line with an open arrowhead
|
|
192
|
+
// landing on the HEAD of the lifeline it brings into existence — the same
|
|
193
|
+
// drawing as a reply, and told apart by the role and by where it lands, which
|
|
194
|
+
// is `rules.ts`'s business rather than this table's.
|
|
195
|
+
'message-create': {
|
|
196
|
+
strokeStyle: StrokeStyle.Dash,
|
|
197
|
+
frontEndpointStyle: PointStyle.None,
|
|
198
|
+
rearEndpointStyle: PointStyle.Arrow,
|
|
199
|
+
},
|
|
200
|
+
// A DELETE message is an ordinary call that happens to end at the X of a
|
|
201
|
+
// destruction occurrence (§17.4.4): the line is the synchronous one, and the
|
|
202
|
+
// cross at its end is a separate mark the author drops on the spine. So it
|
|
203
|
+
// shares the sync row rather than inventing a drawing the specification does
|
|
204
|
+
// not have.
|
|
205
|
+
'message-delete': {
|
|
206
|
+
strokeStyle: StrokeStyle.Solid,
|
|
207
|
+
frontEndpointStyle: PointStyle.None,
|
|
208
|
+
rearEndpointStyle: PointStyle.Triangle,
|
|
209
|
+
},
|
|
210
|
+
};
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { EdgelessUmlMenu } from './toolbar/uml-menu.js';
|
|
2
|
+
import { EdgelessUmlSeniorButton } from './toolbar/uml-senior-button.js';
|
|
3
|
+
export declare function effects(): void;
|
|
4
|
+
declare global {
|
|
5
|
+
interface HTMLElementTagNameMap {
|
|
6
|
+
'edgeless-uml-menu': EdgelessUmlMenu;
|
|
7
|
+
'edgeless-uml-senior-button': EdgelessUmlSeniorButton;
|
|
8
|
+
}
|
|
9
|
+
}
|
package/dist/effects.js
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { EdgelessUmlMenu } from './toolbar/uml-menu.js';
|
|
2
|
+
import { EdgelessUmlSeniorButton } from './toolbar/uml-senior-button.js';
|
|
3
|
+
export function effects() {
|
|
4
|
+
customElements.define('edgeless-uml-menu', EdgelessUmlMenu);
|
|
5
|
+
customElements.define('edgeless-uml-senior-button', EdgelessUmlSeniorButton);
|
|
6
|
+
}
|