@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,521 @@
|
|
|
1
|
+
import type { UmlNodeKind } from '@formicoidea/labre-core/model';
|
|
2
|
+
import { type UmlRoleId } from './roles.js';
|
|
3
|
+
/**
|
|
4
|
+
* A UML component — the SHAPE and its own words, grouped.
|
|
5
|
+
*
|
|
6
|
+
* ## Why a UML artefact is several elements
|
|
7
|
+
*
|
|
8
|
+
* UML 2.5.1 draws a classifier as a rectangle divided into COMPARTMENTS
|
|
9
|
+
* (§9.2.4, §11.4.4): the name on top, the attributes under a line, the
|
|
10
|
+
* operations under a second one. Those compartments are not decoration — they
|
|
11
|
+
* are three separate statements about the same classifier, and an author edits
|
|
12
|
+
* them one at a time.
|
|
13
|
+
*
|
|
14
|
+
* The library already knows how to draw that: the same arrangement C4 arrived
|
|
15
|
+
* at through the PO's recette of 28/08/2026 (`gfx/c4/src/component.ts`), which
|
|
16
|
+
* R15/R16 then generalised into a rule. A component is a native `group` holding
|
|
17
|
+
* a `umlNode` shape that carries NO inner text and one canvas `text` element per
|
|
18
|
+
* compartment, each stamped with its own role. One click selects the classifier,
|
|
19
|
+
* a second descends into the compartment under the pointer, and every gesture
|
|
20
|
+
* the editor already has works on it.
|
|
21
|
+
*
|
|
22
|
+
* ## This module
|
|
23
|
+
*
|
|
24
|
+
* The two questions that arrangement raises, both PURE and both free of `std`
|
|
25
|
+
* and of Yjs, so the creation site, the node renderer, the exporter and a test
|
|
26
|
+
* all answer them the same way:
|
|
27
|
+
*
|
|
28
|
+
* - **where do the compartments go**, given a node's box
|
|
29
|
+
* ({@link umlCompartmentBoxes}) — and, as a by-product nobody else can
|
|
30
|
+
* compute, WHERE THE SEPARATORS ARE, which is what the renderer strokes;
|
|
31
|
+
* - **which words belong to which shape**, given a group and a flat element
|
|
32
|
+
* list ({@link umlComponentSiblings}) — resolved by group membership plus the
|
|
33
|
+
* role stamped on each text, never by position in `children`.
|
|
34
|
+
*
|
|
35
|
+
* The role stays on the SHAPE and on each text; the group carries none
|
|
36
|
+
* (`roles.ts`).
|
|
37
|
+
*/
|
|
38
|
+
/**
|
|
39
|
+
* The gap between a compartment's words and the line that closes it, and
|
|
40
|
+
* between the words and the outline of the box.
|
|
41
|
+
*
|
|
42
|
+
* One number for the top, the bottom and — through {@link UML_TIER_SIDE_INSET}
|
|
43
|
+
* — the sides, because UML draws one: the specification's figures are typeset
|
|
44
|
+
* with a single uniform padding inside every compartment, and a second value
|
|
45
|
+
* would read as a distinction between compartments that the notation never
|
|
46
|
+
* makes.
|
|
47
|
+
*/
|
|
48
|
+
export declare const UML_TIER_MARGIN = 8;
|
|
49
|
+
/**
|
|
50
|
+
* The side padding, as a FRACTION of the box width rather than the absolute
|
|
51
|
+
* {@link UML_TIER_MARGIN}.
|
|
52
|
+
*
|
|
53
|
+
* A UML box is resized far more often horizontally than vertically — an author
|
|
54
|
+
* widens a class to fit `+ findByCustomerId(id : CustomerId) : Order`, they do
|
|
55
|
+
* not make it taller to fit a longer word. A proportional inset keeps the text
|
|
56
|
+
* column looking deliberate at every width; an absolute one would leave a
|
|
57
|
+
* 200-unit box looking padded and a 600-unit box looking unpadded.
|
|
58
|
+
*/
|
|
59
|
+
export declare const UML_TIER_SIDE_INSET = 0.08;
|
|
60
|
+
/**
|
|
61
|
+
* Line height, as a multiple of the font size — the one number that turns
|
|
62
|
+
* "three lines of attributes" into a height.
|
|
63
|
+
*/
|
|
64
|
+
export declare const UML_TIER_LINE_HEIGHT = 1.4;
|
|
65
|
+
/**
|
|
66
|
+
* The gap between the NAME and the line under it.
|
|
67
|
+
*
|
|
68
|
+
* Smaller than {@link UML_TIER_MARGIN} on purpose: the name compartment of a
|
|
69
|
+
* fresh classifier holds one line and, for an interface or an enumeration, a
|
|
70
|
+
* keyword line above it (§9.5.4 — `«interface»`), and that pair has to read as
|
|
71
|
+
* one heading rather than as a block floating in an over-tall compartment.
|
|
72
|
+
*/
|
|
73
|
+
export declare const UML_NAME_GAP = 6;
|
|
74
|
+
/**
|
|
75
|
+
* How many lines the ATTRIBUTES compartment is sized for.
|
|
76
|
+
*
|
|
77
|
+
* Three, because that is what a default 200 × 120 box leaves room for once the
|
|
78
|
+
* name compartment and the margins are taken out, and because a classifier with
|
|
79
|
+
* three attributes and one or two operations is the shape of nearly every class
|
|
80
|
+
* anybody draws first. The operations compartment takes whatever is left, which
|
|
81
|
+
* is the one tier that must never be sized in lines: it is the bottom of the
|
|
82
|
+
* box, and a box an author has dragged taller has dragged it taller FOR the
|
|
83
|
+
* operations.
|
|
84
|
+
*
|
|
85
|
+
* Since the compartment watcher this is the DEFAULT rather than the sizing: a
|
|
86
|
+
* caller that has read the tier passes its real line count
|
|
87
|
+
* ({@link UmlTierLines}), and three is what a caller who has not read it gets —
|
|
88
|
+
* the stencil's own shape, which is every classifier's until somebody types.
|
|
89
|
+
*/
|
|
90
|
+
export declare const UML_ATTRIBUTE_LINES = 3;
|
|
91
|
+
/**
|
|
92
|
+
* The share of a `package` box its TAB occupies, top-left (§12.2.4).
|
|
93
|
+
*
|
|
94
|
+
* A fraction rather than an absolute, because the tab is part of the DRAWING:
|
|
95
|
+
* a package dragged to twice the height is the same folder, twice as big, and a
|
|
96
|
+
* tab pinned to 26 units would turn into a sliver. Exported so the node
|
|
97
|
+
* renderer strokes the tab exactly where the name compartment stops, rather
|
|
98
|
+
* than the two agreeing by coincidence.
|
|
99
|
+
*/
|
|
100
|
+
export declare const UML_PACKAGE_TAB = 0.2;
|
|
101
|
+
/**
|
|
102
|
+
* The share of an `actor` box the STICK FIGURE occupies, measured from the top
|
|
103
|
+
* (§18.1.4 — the figure, and its name written underneath it).
|
|
104
|
+
*
|
|
105
|
+
* Exported for the same reason as {@link UML_PACKAGE_TAB}: the renderer draws
|
|
106
|
+
* the figure ABOVE this line and {@link umlCompartmentBoxes} puts the name
|
|
107
|
+
* BELOW it, and a head overlapping its own label is what happens when two files
|
|
108
|
+
* each pick their own number.
|
|
109
|
+
*/
|
|
110
|
+
export declare const UML_ACTOR_FIGURE = 0.74;
|
|
111
|
+
/**
|
|
112
|
+
* The DEPTH of the 3-D cube a node, a device and an execution environment are
|
|
113
|
+
* drawn as (§19.4.4), as a fraction of the box's shorter side.
|
|
114
|
+
*
|
|
115
|
+
* A fraction of the SHORTER side rather than of the width, because the depth is
|
|
116
|
+
* drawn at 45° in both directions: taking it from the width alone would make a
|
|
117
|
+
* cube dragged wide and flat lose its top face off the top of the element.
|
|
118
|
+
*
|
|
119
|
+
* Exported for the reason {@link UML_PACKAGE_TAB} is: the renderer draws the
|
|
120
|
+
* three faces from this number and {@link umlCompartmentBoxes} writes the name
|
|
121
|
+
* INSIDE the front one, so a name floating over the cube's roof is what happens
|
|
122
|
+
* when two files each pick their own depth. 12% is the proportion §19.4.4's own
|
|
123
|
+
* figures show — enough to read as a solid, little enough to leave the front
|
|
124
|
+
* face the size of a box.
|
|
125
|
+
*/
|
|
126
|
+
export declare const UML_CUBE_DEPTH = 0.12;
|
|
127
|
+
/**
|
|
128
|
+
* The gap between a glyph too small to write in and the name written BESIDE it.
|
|
129
|
+
*
|
|
130
|
+
* Three kinds are in that position, and all three by the notation's own doing: a
|
|
131
|
+
* port is a small square on a border (§11.3.4), and the ball and socket of
|
|
132
|
+
* §10.4.4 are a curve on a stub. None of them has an inside, so the label goes
|
|
133
|
+
* next to the picture — which is exactly how the specification's figures write
|
|
134
|
+
* them.
|
|
135
|
+
*/
|
|
136
|
+
export declare const UML_BESIDE_LABEL_GAP = 6;
|
|
137
|
+
/**
|
|
138
|
+
* How wide that label is allowed to be before it wraps.
|
|
139
|
+
*
|
|
140
|
+
* An absolute rather than a fraction, unlike every other measurement here,
|
|
141
|
+
* because it is measuring the WORDS and not the glyph: a port dragged twice as
|
|
142
|
+
* big is a bigger square with the same name beside it, and a name column scaled
|
|
143
|
+
* to a 16-unit square would be four characters wide.
|
|
144
|
+
*/
|
|
145
|
+
export declare const UML_BESIDE_LABEL_WIDTH = 120;
|
|
146
|
+
/**
|
|
147
|
+
* How deep the POINT of a signal pentagon bites into its box (§16.3.4), as a
|
|
148
|
+
* fraction of the width.
|
|
149
|
+
*
|
|
150
|
+
* Exported for the reason {@link UML_PACKAGE_TAB} is: the renderer draws the
|
|
151
|
+
* pentagon from this number and {@link umlCompartmentBoxes} pulls the label box
|
|
152
|
+
* back out of the point by it, so a signal's name cannot end up running out
|
|
153
|
+
* through the tip. One number, two readers.
|
|
154
|
+
*
|
|
155
|
+
* The same depth for both shapes, because §16.3.4 draws the same pentagon twice
|
|
156
|
+
* and merely turns it: a send signal's point sticks OUT of the right edge, an
|
|
157
|
+
* accept event's bites IN from the left, and a reader tells them apart by which
|
|
158
|
+
* way the arrow of the silhouette runs. Two depths would make the pair look like
|
|
159
|
+
* two unrelated shapes.
|
|
160
|
+
*/
|
|
161
|
+
export declare const UML_SIGNAL_POINT = 0.16;
|
|
162
|
+
/** A box in the same units and origin as the node's own. */
|
|
163
|
+
export interface UmlBox {
|
|
164
|
+
x: number;
|
|
165
|
+
y: number;
|
|
166
|
+
w: number;
|
|
167
|
+
h: number;
|
|
168
|
+
}
|
|
169
|
+
/**
|
|
170
|
+
* The written compartments of one node, plus the separators between them.
|
|
171
|
+
*
|
|
172
|
+
* `attributes` and `operations` are optional because most of the pack has
|
|
173
|
+
* neither: a package, a note, an actor and a use case carry ONE label and no
|
|
174
|
+
* compartment at all (§12.2.4, §18.1.4, Annex A), and an object carries a name
|
|
175
|
+
* and its slots but no operations (§9.8.4 — an instance has values, not
|
|
176
|
+
* behaviour).
|
|
177
|
+
*
|
|
178
|
+
* `splits` are offsets DOWN FROM THE TOP of the node's box, in model units, and
|
|
179
|
+
* they are the renderer's whole instruction: stroke a full-width line at each.
|
|
180
|
+
* They are returned from here rather than recomputed there because they are not
|
|
181
|
+
* a second fact — a separator IS the boundary between two compartments, and two
|
|
182
|
+
* files deriving it separately is how a line ends up crossing the words it was
|
|
183
|
+
* supposed to sit under.
|
|
184
|
+
*/
|
|
185
|
+
export interface UmlCompartmentBoxes {
|
|
186
|
+
name: UmlBox;
|
|
187
|
+
attributes?: UmlBox;
|
|
188
|
+
operations?: UmlBox;
|
|
189
|
+
splits: number[];
|
|
190
|
+
}
|
|
191
|
+
/**
|
|
192
|
+
* How many LINES each compartment actually holds — the one thing a box cannot
|
|
193
|
+
* tell you.
|
|
194
|
+
*
|
|
195
|
+
* {@link umlCompartmentBoxes} is otherwise a pure function of a rectangle, and
|
|
196
|
+
* that was enough for as long as every classifier was assumed to hold the
|
|
197
|
+
* stencil it was born with: one name line, three attributes, and the operations
|
|
198
|
+
* taking the rest. An author who types five attributes into the second tier has
|
|
199
|
+
* overflowed it — the words run through the separator under them and out the
|
|
200
|
+
* bottom of the box — and nothing in a rectangle says so.
|
|
201
|
+
*
|
|
202
|
+
* So the counts are passed IN, from whoever has read the tiers
|
|
203
|
+
* ({@link umlTierLineCount}), and every field is optional: a caller that knows
|
|
204
|
+
* nothing about the words gets exactly the layout this module has always
|
|
205
|
+
* returned. `UmlCompartmentWatcher` is what supplies them after an edit
|
|
206
|
+
* commits, and the node renderer is what keeps the separators where the tiers
|
|
207
|
+
* then are.
|
|
208
|
+
*
|
|
209
|
+
* `operations` is read by {@link umlStackHeight} and NOT by
|
|
210
|
+
* {@link umlCompartmentBoxes}, and that asymmetry is the notation's: the
|
|
211
|
+
* operations compartment is the bottom of the box and always takes whatever is
|
|
212
|
+
* left (§11.4.4), so its line count says how tall the box must BE, never how
|
|
213
|
+
* tall the compartment is drawn.
|
|
214
|
+
*/
|
|
215
|
+
export interface UmlTierLines {
|
|
216
|
+
name?: number;
|
|
217
|
+
attributes?: number;
|
|
218
|
+
operations?: number;
|
|
219
|
+
}
|
|
220
|
+
/**
|
|
221
|
+
* How many lines one LOGICAL line of a tier is painted as — the measurement
|
|
222
|
+
* this module cannot make and {@link umlTierLineCount} therefore takes.
|
|
223
|
+
*
|
|
224
|
+
* A canvas text element is drawn WRAPPED: the renderer breaks each line at the
|
|
225
|
+
* element's own width before it paints it (`gfx/text`'s `wrapText`), so
|
|
226
|
+
* `+ findByCustomerIdAndStatus(id : CustomerId) : List<Order>` in a 168-unit
|
|
227
|
+
* compartment is one line in the document and three on the picture. Answering
|
|
228
|
+
* that needs a font context — a `getFontString` and a canvas measurer — which
|
|
229
|
+
* this module (pure, free of `std` and of the DOM) has none of, so the caller
|
|
230
|
+
* that HAS one passes the answer in.
|
|
231
|
+
*/
|
|
232
|
+
export type UmlTierWrap = (line: string) => number;
|
|
233
|
+
/**
|
|
234
|
+
* How many lines a tier's text is PAINTED as.
|
|
235
|
+
*
|
|
236
|
+
* Takes the VALUE rather than the element, for the reason {@link umlTierText}
|
|
237
|
+
* does — a `Y.Text` on a model, a string in a fixture. The author's newlines are
|
|
238
|
+
* counted always; `wrap` adds the ones the renderer makes, and without it the
|
|
239
|
+
* count is the newlines alone.
|
|
240
|
+
*
|
|
241
|
+
* ## Why `wrap` is not optional in practice
|
|
242
|
+
*
|
|
243
|
+
* It was, until the PO's recette of 14/09/2026. A tier is created with
|
|
244
|
+
* `hasMaxWidth`, so the text editor wraps a long signature at the compartment's
|
|
245
|
+
* width and grows the box downward while the author types — and the watcher then
|
|
246
|
+
* SHRANK that box back to one line's worth on commit, painting the wrapped
|
|
247
|
+
* remainder straight through the separator under it. Measured on a 200-unit
|
|
248
|
+
* class: three typed attribute lines, six painted, a 92.7-unit stack put back
|
|
249
|
+
* into a 54.6-unit compartment.
|
|
250
|
+
*
|
|
251
|
+
* So every caller that can measure must: `umlFitComponent` does, through
|
|
252
|
+
* `umlTierWrapper` (`node/tier-metrics.ts`), and the `wrap`-less form is what a
|
|
253
|
+
* fixture and a pure test get.
|
|
254
|
+
*
|
|
255
|
+
* An empty tier is one line, which is `\n`-count + 1 and also what §14.2.4
|
|
256
|
+
* wants: a state's behaviour compartment is seeded EMPTY and is still ruled off.
|
|
257
|
+
*/
|
|
258
|
+
export declare function umlTierLineCount(text: unknown, wrap?: UmlTierWrap): number;
|
|
259
|
+
/**
|
|
260
|
+
* The three glyphs with no INSIDE, whose name is therefore written next to them
|
|
261
|
+
* — outside the element's own box, which nothing else in this module does.
|
|
262
|
+
*
|
|
263
|
+
* Exported because that is a fact a caller has to know: a port's label is the
|
|
264
|
+
* one tier that makes the GROUP bigger than the shape it belongs to, and a test
|
|
265
|
+
* or a layout asserting "every tier is inside its node" has to say which three
|
|
266
|
+
* kinds the notation exempts (§11.3.4, §10.4.4).
|
|
267
|
+
*/
|
|
268
|
+
export declare const UML_BESIDE_LABEL_KINDS: ReadonlySet<UmlNodeKind>;
|
|
269
|
+
/**
|
|
270
|
+
* The kinds whose one tier is written in a HEAD drawn wider than the element
|
|
271
|
+
* itself — today the lifeline, and it is the second and last way a tier is
|
|
272
|
+
* allowed outside its own box.
|
|
273
|
+
*
|
|
274
|
+
* ## Why a lifeline is shaped like this at all
|
|
275
|
+
*
|
|
276
|
+
* §17.2.4 draws a lifeline as a NAMED BOX with a dashed line falling out of the
|
|
277
|
+
* bottom of it, and the dashed line is the part that matters: a message is
|
|
278
|
+
* attached to it at the height where it happens, and an execution bar sits on
|
|
279
|
+
* it. So the ELEMENT is the spine — a narrow, tall column whose left and right
|
|
280
|
+
* perimeters are where the connector layer puts an anchor — and the head is a
|
|
281
|
+
* rectangle the renderer draws across the top of it, centred, overflowing it by
|
|
282
|
+
* the same amount on each side (`consts.ts`, `UML_LIFELINE_HEAD`).
|
|
283
|
+
*
|
|
284
|
+
* That makes the head the one place the participant's name can go. Writing it
|
|
285
|
+
* inside a 16-unit column would be writing it on the dashes.
|
|
286
|
+
*
|
|
287
|
+
* ## Why it is not {@link UML_BESIDE_LABEL_KINDS}
|
|
288
|
+
*
|
|
289
|
+
* Because it is not beside anything: a port's name sits to the RIGHT of a glyph
|
|
290
|
+
* that has no inside, and a lifeline's name sits IN a head the renderer draws.
|
|
291
|
+
* The two would share nothing but the fact that a bounding box has to allow for
|
|
292
|
+
* them, and folding them together would give a lifeline a label floating 6
|
|
293
|
+
* units off the spine. Two sets, two placements, one invariant each.
|
|
294
|
+
*/
|
|
295
|
+
export declare const UML_HEAD_LABEL_KINDS: ReadonlySet<UmlNodeKind>;
|
|
296
|
+
/**
|
|
297
|
+
* WHICH tier role a kind's single word carries, for every kind whose word is a
|
|
298
|
+
* label rather than a name compartment.
|
|
299
|
+
*
|
|
300
|
+
* The companion of {@link glyphLabel}: that answers where the one tier GOES,
|
|
301
|
+
* this answers what it IS, and the two questions have had the same shape since
|
|
302
|
+
* the lifeline arrived — a placement chosen per kind and a role chosen per
|
|
303
|
+
* kind, which is exactly one table each.
|
|
304
|
+
*
|
|
305
|
+
* Today it says one thing: a lifeline's head carries `uml:lifeline-ident` and
|
|
306
|
+
* everything else carries `uml:label`. §17.3.4 prints a BNF for what goes in
|
|
307
|
+
* that head (`parseLifelineIdent` reads it back) and §18.1.4 prints none for an
|
|
308
|
+
* actor's word, so the two tiers answer "does this parse" differently and a
|
|
309
|
+
* single role could only ever have asked them the same question (`roles.ts`).
|
|
310
|
+
*
|
|
311
|
+
* It does NOT answer for the compartmented kinds or for the five glyphs named
|
|
312
|
+
* rather than labelled (`actions.ts`, `NAMED_GLYPHS`): those carry `uml:name`,
|
|
313
|
+
* which is a different statement — a tier a keyword may be written over — and a
|
|
314
|
+
* helper that folded all three answers together would be a second place to
|
|
315
|
+
* decide what the grammar reads.
|
|
316
|
+
*/
|
|
317
|
+
export declare function umlLabelRoleOf(kind: UmlNodeKind): UmlRoleId;
|
|
318
|
+
/**
|
|
319
|
+
* The compartments of a node, laid out against its own box.
|
|
320
|
+
*
|
|
321
|
+
* ## The rhythm of a classifier
|
|
322
|
+
*
|
|
323
|
+
* A margin, the name over one line, a small gap, THE LINE, three lines of
|
|
324
|
+
* attributes, THE SECOND LINE, and the operations take the rest down to the
|
|
325
|
+
* bottom margin. Which is exactly what a 200 × 120 box is tall enough for,
|
|
326
|
+
* because the box was derived from this stack (`consts.ts`) rather than the
|
|
327
|
+
* stack fitted into the box — so a fresh class is neither cramped nor half
|
|
328
|
+
* empty.
|
|
329
|
+
*
|
|
330
|
+
* The tiers are stacked by walking DOWN, each placed under the last, rather
|
|
331
|
+
* than by six independent offsets: six offsets is six chances for two
|
|
332
|
+
* compartments to overlap, and a walk cannot produce one.
|
|
333
|
+
*
|
|
334
|
+
* An `object` walks the same path and stops one tier early: §9.8.4 draws an
|
|
335
|
+
* instance specification as a name compartment over a SLOT compartment, and
|
|
336
|
+
* there is no third. So it gets one split, not two, and the slots take the rest
|
|
337
|
+
* of the box — the tier an author of an object diagram actually types in. A
|
|
338
|
+
* `component` (§11.6.4) and an `artifact` (§19.3.4) walk exactly the same path,
|
|
339
|
+
* for exactly the same reason: a name over one body tier.
|
|
340
|
+
*
|
|
341
|
+
* ## The ones that are not boxes
|
|
342
|
+
*
|
|
343
|
+
* A package, a note, an actor, a use case, the three cubes and the three
|
|
344
|
+
* interface glyphs are SHAPES the notation draws, not rectangles it divides, so
|
|
345
|
+
* each returns a single label box positioned against its own picture and no
|
|
346
|
+
* splits at all:
|
|
347
|
+
*
|
|
348
|
+
* - **package** — under the tab, centred in the body (§12.2.4);
|
|
349
|
+
* - **note** — the inner rectangle, top-aligned and read left, because a note
|
|
350
|
+
* holds a sentence rather than a title (Annex A);
|
|
351
|
+
* - **actor** — under the stick figure, which is where §18.1.4 writes it;
|
|
352
|
+
* - **use case** — centred in the ellipse, the only shape with no corner to
|
|
353
|
+
* hang a label in;
|
|
354
|
+
* - **node / device / execution-environment** — inside the cube's FRONT FACE,
|
|
355
|
+
* the only one of its three faces that is not drawn at an angle (§19.4.4);
|
|
356
|
+
* - **port / provided-interface / required-interface / time-event** — BESIDE
|
|
357
|
+
* the glyph, and outside the element's own box: a small square, a ball on a
|
|
358
|
+
* stick and a 40-unit hourglass have no inside to write in (§11.3.4,
|
|
359
|
+
* §10.4.4, §16.10.4). The one place this module returns a tier that is not
|
|
360
|
+
* contained by the node it belongs to, and
|
|
361
|
+
* {@link UML_BESIDE_LABEL_KINDS} is how it says so out loud;
|
|
362
|
+
* - **action / object-node** — centred in the box, which is where §15.3.4 and
|
|
363
|
+
* §15.4.4 write a verb phrase and a value's name;
|
|
364
|
+
* - **send-signal / accept-event** — centred too, but pulled back out of the
|
|
365
|
+
* pentagon's POINT by {@link UML_SIGNAL_POINT}, so the words never run out
|
|
366
|
+
* through the tip (§16.3.4).
|
|
367
|
+
*
|
|
368
|
+
* ## The ones with no words at all
|
|
369
|
+
*
|
|
370
|
+
* The control nodes of §15.3.4 and the pseudostates of §14.2.4 are MARKS — a
|
|
371
|
+
* disc, a bar, a bullseye, a cross — and carry no tier. They still get a box
|
|
372
|
+
* back from here, centred, because a box is always computable from a box and a
|
|
373
|
+
* second return shape would cost every caller a branch. What decides whether a
|
|
374
|
+
* text element is ever created is `UML_UNLABELLED_KINDS` in `keywords.ts`, and
|
|
375
|
+
* that is the one authority on the question.
|
|
376
|
+
*
|
|
377
|
+
* ## What this is not
|
|
378
|
+
*
|
|
379
|
+
* A creation-time answer, a render-time one and a commit-time one, and nothing
|
|
380
|
+
* more. The compartments are real elements from the moment they are drawn: an
|
|
381
|
+
* author who moves one has moved it, and nothing here runs on its own to put it
|
|
382
|
+
* back. The margins are absolutes, so a node dragged taller keeps them where
|
|
383
|
+
* they were and gives the extra height to the tier that takes "the rest".
|
|
384
|
+
*
|
|
385
|
+
* The commit-time caller is `UmlCompartmentWatcher` (`node/`), and it is the
|
|
386
|
+
* one that passes `lines`: when an edit into a tier commits, it re-asks this
|
|
387
|
+
* function where the compartments go now that the second one holds five lines
|
|
388
|
+
* instead of three, grows the node if the answer no longer fits
|
|
389
|
+
* ({@link umlStackHeight}) and writes the tiers to the boxes it gets back. It
|
|
390
|
+
* is still not a layout engine — nothing runs between keystrokes, and nothing
|
|
391
|
+
* reclaims height an author has dragged out.
|
|
392
|
+
*/
|
|
393
|
+
export declare function umlCompartmentBoxes(kind: UmlNodeKind, x: number, y: number, w: number, h: number, lines?: UmlTierLines): UmlCompartmentBoxes;
|
|
394
|
+
/**
|
|
395
|
+
* How tall the node has to BE for the words its tiers hold to fit — `null` for
|
|
396
|
+
* a kind that has no stack.
|
|
397
|
+
*
|
|
398
|
+
* The same walk as {@link umlCompartmentBoxes}, read the other way round: that
|
|
399
|
+
* function is given a box and hands back where the compartments go, this one is
|
|
400
|
+
* given the compartments and hands back the box they need. Both are here so
|
|
401
|
+
* they cannot disagree, which is the whole reason the separators were computed
|
|
402
|
+
* in this module in the first place.
|
|
403
|
+
*
|
|
404
|
+
* The last tier is the one difference between the two. `umlCompartmentBoxes`
|
|
405
|
+
* gives the operations compartment (or a one-split kind's body) EVERYTHING left
|
|
406
|
+
* down to the bottom margin, because a box an author has dragged taller has
|
|
407
|
+
* dragged it taller for them; this returns the height at which "everything
|
|
408
|
+
* left" is exactly the lines it holds, which is the smallest box that fits.
|
|
409
|
+
*
|
|
410
|
+
* `null` rather than a number for a package, an actor, a cube or a mark: those
|
|
411
|
+
* are pictures the notation draws, not rectangles it divides, and there is no
|
|
412
|
+
* honest answer to "how tall must this be" for a stick figure. It doubles as
|
|
413
|
+
* the predicate — a caller that gets a number is looking at a divided box.
|
|
414
|
+
*/
|
|
415
|
+
export declare function umlStackHeight(kind: UmlNodeKind, lines: UmlTierLines): number | null;
|
|
416
|
+
/**
|
|
417
|
+
* The little a resolution needs to know about an element — an id, a role, and
|
|
418
|
+
* whatever it says.
|
|
419
|
+
*
|
|
420
|
+
* `text` is `unknown` because on a real element it is a `Y.Text` and in a test
|
|
421
|
+
* it is a string, and this module has no business knowing which: it is read
|
|
422
|
+
* through {@link umlTierText}, which stringifies whatever it is given. That is
|
|
423
|
+
* also what keeps this file free of a Yjs import.
|
|
424
|
+
*/
|
|
425
|
+
export interface UmlComponentElement {
|
|
426
|
+
id: string;
|
|
427
|
+
role?: string;
|
|
428
|
+
text?: unknown;
|
|
429
|
+
}
|
|
430
|
+
/** The little a resolution needs to know about a group. */
|
|
431
|
+
export interface UmlComponentGroup {
|
|
432
|
+
id: string;
|
|
433
|
+
childIds: readonly string[];
|
|
434
|
+
}
|
|
435
|
+
/**
|
|
436
|
+
* One classifier's shape and its written compartments, resolved.
|
|
437
|
+
*
|
|
438
|
+
* Every field is optional, and that is the honest answer rather than a
|
|
439
|
+
* defensive one: a group whose texts were deleted, one whose shape was deleted,
|
|
440
|
+
* and a group an author made out of two unrelated boxes all exist, and none of
|
|
441
|
+
* them is an error. What a caller does with a missing tier — write nothing,
|
|
442
|
+
* fall back to the shape's own inner text — is the caller's decision, and the
|
|
443
|
+
* exporter and the node view make different ones.
|
|
444
|
+
*/
|
|
445
|
+
export interface UmlComponent {
|
|
446
|
+
node?: UmlComponentElement;
|
|
447
|
+
name?: UmlComponentElement;
|
|
448
|
+
attributes?: UmlComponentElement;
|
|
449
|
+
operations?: UmlComponentElement;
|
|
450
|
+
label?: UmlComponentElement;
|
|
451
|
+
/**
|
|
452
|
+
* §17.3.4's lifeline head — a fifth slot rather than a fifth user of
|
|
453
|
+
* {@link UmlComponent.label}, for the reason `roles.ts` splits the tier: what
|
|
454
|
+
* is written here is a production with a grammar, and what is written in a
|
|
455
|
+
* label is prose. A caller that wants "the word this artefact goes by",
|
|
456
|
+
* whatever tier holds it, asks for `name ?? label ?? ident` — three tiers,
|
|
457
|
+
* never both.
|
|
458
|
+
*/
|
|
459
|
+
ident?: UmlComponentElement;
|
|
460
|
+
}
|
|
461
|
+
/**
|
|
462
|
+
* Everything one group holds, sorted into the shape and its tiers.
|
|
463
|
+
*
|
|
464
|
+
* ## Group membership, then roles
|
|
465
|
+
*
|
|
466
|
+
* The group answers "which words are THIS classifier's" — two classes side by
|
|
467
|
+
* side both carry a `+ attribute : Type`, and only the grouping says which is
|
|
468
|
+
* which. The role then answers "which of these words is the operations
|
|
469
|
+
* compartment", which position in `children` cannot: a group's child order is
|
|
470
|
+
* an implementation detail that a reorder, a copy or a regroup rewrites, while
|
|
471
|
+
* a role is written on the element and travels with it.
|
|
472
|
+
*
|
|
473
|
+
* ## `name` and `label` are two fields, not one
|
|
474
|
+
*
|
|
475
|
+
* Because they are two roles, and the vocabulary means them differently: a
|
|
476
|
+
* `uml:name` is the name of a CLASSIFIER, parsed by the grammar for keywords in
|
|
477
|
+
* guillemets and for an abstract name in italics (§9.5.4); a `uml:label` is the
|
|
478
|
+
* plain text of an actor or a use case, which is a name and nothing else. An
|
|
479
|
+
* exporter that had to ask "is this the name field or the label field" of one
|
|
480
|
+
* merged slot would be asking about the element's KIND, which the shape already
|
|
481
|
+
* states. Keeping them apart costs one field and removes the question.
|
|
482
|
+
*
|
|
483
|
+
* The FIRST element of each role wins. A group holding two names is a group
|
|
484
|
+
* somebody built by hand out of two components, and document order is the same
|
|
485
|
+
* tie-break the export's geometric attribution uses.
|
|
486
|
+
*/
|
|
487
|
+
export declare function umlComponentSiblings(group: UmlComponentGroup, elements: readonly UmlComponentElement[]): UmlComponent;
|
|
488
|
+
/**
|
|
489
|
+
* The group an element belongs to — `undefined` when it belongs to none.
|
|
490
|
+
*
|
|
491
|
+
* The other direction of the same question, and the one the node view needs:
|
|
492
|
+
* given the shape somebody has just double-clicked, which group holds the text
|
|
493
|
+
* to open an editor on? The C4 pack answers it with `c4ComponentSiblings(id,
|
|
494
|
+
* groups)`; this returns the group itself rather than its id list, because the
|
|
495
|
+
* next call is always {@link umlComponentSiblings}, which takes exactly that.
|
|
496
|
+
*
|
|
497
|
+
* The FIRST group holding the element wins. Groups nest, so a component grouped
|
|
498
|
+
* again inside a bigger group has two ancestors; document order picks the one
|
|
499
|
+
* written first, which is the innermost — the one the creation site made.
|
|
500
|
+
*/
|
|
501
|
+
export declare function umlGroupOf(elementId: string, groups: readonly UmlComponentGroup[]): UmlComponentGroup | undefined;
|
|
502
|
+
/**
|
|
503
|
+
* The shape of a resolved component — its `umlNode`, or `undefined` for a group
|
|
504
|
+
* that holds none.
|
|
505
|
+
*
|
|
506
|
+
* A one-line accessor with a name, rather than `component.node` at eleven call
|
|
507
|
+
* sites, because the QUESTION is worth spelling: "which box are these words
|
|
508
|
+
* about". The exporter asks it to read a `kind`, the renderer to read a
|
|
509
|
+
* geometry, and a rule to read a role.
|
|
510
|
+
*/
|
|
511
|
+
export declare function umlNodeOfComponent(component: UmlComponent): UmlComponentElement | undefined;
|
|
512
|
+
/**
|
|
513
|
+
* Whatever a tier says, as a plain trimmed string.
|
|
514
|
+
*
|
|
515
|
+
* Takes the VALUE rather than the element, because that is what every caller
|
|
516
|
+
* has: a `Y.Text` off a model, a string off a fixture, `undefined` off a tier
|
|
517
|
+
* that was never created. Stringifying here is what lets the grammar, the
|
|
518
|
+
* exporter and the renderer stay free of a Yjs import — a `Y.Text` answers
|
|
519
|
+
* `toString()` with its content, which is the whole of the coupling.
|
|
520
|
+
*/
|
|
521
|
+
export declare function umlTierText(text: unknown): string;
|