@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,149 @@
|
|
|
1
|
+
import { TextAlign, type UmlNodeKind } from '@formicoidea/labre-core/model';
|
|
2
|
+
import type { UmlBox } from './component.js';
|
|
3
|
+
/**
|
|
4
|
+
* What a UML shape IS, as props — the ONE description the creation site, the
|
|
5
|
+
* morph and the templates all read (R17).
|
|
6
|
+
*
|
|
7
|
+
* The rule exists because a kind's appearance is written by the preset of the
|
|
8
|
+
* kind it was created as and nothing ever rewrites it afterwards: a second table
|
|
9
|
+
* restating the same sizes and colours would agree with this one on the day it
|
|
10
|
+
* was written and drift on the first restyle. Derived, they cannot — a morphed
|
|
11
|
+
* classifier and one freshly drawn from the toolbox are the same element.
|
|
12
|
+
*
|
|
13
|
+
* ## No colour decisions here, and that is the notation's doing
|
|
14
|
+
*
|
|
15
|
+
* UML 2.5.1 defines no palette. Every figure in the specification is black lines
|
|
16
|
+
* on white paper, and what tells a class from an interface from an enumeration
|
|
17
|
+
* is the KEYWORD written above the name (§9.5.4), never a hue. So this file
|
|
18
|
+
* seeds one ink and one paper off the shared neutral scale (R33, through
|
|
19
|
+
* `consts.ts`) and spends its judgement on the two things the spec DOES
|
|
20
|
+
* legislate: which kinds are a divided rectangle, and which are a picture.
|
|
21
|
+
*/
|
|
22
|
+
/**
|
|
23
|
+
* The kinds whose GLYPH draws the body, so the native shape underneath paints
|
|
24
|
+
* nothing at all.
|
|
25
|
+
*
|
|
26
|
+
* Twenty-five of thirty-eight, and the node renderer is the authority on which. A package is
|
|
27
|
+
* a tabbed folder (§12.2.4), a note a rectangle with a folded corner (Annex A),
|
|
28
|
+
* an actor a stick figure (§18.1.4) — none of which a native rect can be, so
|
|
29
|
+
* each is created `filled: false` with `StrokeStyle.None` and the renderer
|
|
30
|
+
* paints the outline and the body itself, reading `fillColor` / `strokeColor`
|
|
31
|
+
* off this same model. Both therefore stay editable from the ordinary shape
|
|
32
|
+
* toolbar, exactly like every other node's.
|
|
33
|
+
*
|
|
34
|
+
* Phase 2 added six more, and every one of them for the same reason:
|
|
35
|
+
*
|
|
36
|
+
* - `node`, `device` and `execution-environment` are the 3-D CUBE of §19.4.4 —
|
|
37
|
+
* three faces, two of them parallelograms, which no `shapeType` has;
|
|
38
|
+
* - `provided-interface` and `required-interface` are the ball and the socket
|
|
39
|
+
* of §10.4.4, each a curve on a stub rather than a filled area;
|
|
40
|
+
* - `port` is the one that looks as if a native rect would do (§11.3.4 draws a
|
|
41
|
+
* small square), and it is here because the square is the GLYPH: it is drawn
|
|
42
|
+
* to a fixed proportion of the element so that a port dragged bigger stays a
|
|
43
|
+
* port rather than becoming a box, and it is centred so the name written
|
|
44
|
+
* beside it lines up with it.
|
|
45
|
+
*
|
|
46
|
+
* The USE CASE is deliberately not on the list, and it is the one that looks as
|
|
47
|
+
* if it should be: an ellipse is not a rectangle either — but it is a native
|
|
48
|
+
* `shapeType`, so the platform draws it, fills it and hit-tests it with no glyph
|
|
49
|
+
* involved. Nor are `component` and `artifact`, which ARE the native filled
|
|
50
|
+
* rectangle with a small icon painted into the corner. A kind is on this list
|
|
51
|
+
* when the SHAPE LAYER cannot draw its body, not when it has a glyph.
|
|
52
|
+
*
|
|
53
|
+
* They stay hit-testable across their whole area all the same:
|
|
54
|
+
* `UmlNodeElementModel.includesPoint` forces the interior test regardless of
|
|
55
|
+
* `filled`, which is what keeps an unfilled package draggable and
|
|
56
|
+
* double-clickable across its body rather than along its (absent) stroke.
|
|
57
|
+
*/
|
|
58
|
+
export declare const GLYPH_BODY_KINDS: ReadonlySet<UmlNodeKind>;
|
|
59
|
+
/**
|
|
60
|
+
* The kinds the native shape draws with ROUNDED corners.
|
|
61
|
+
*
|
|
62
|
+
* Two, and both by the notation's own instruction: §15.3.4 draws an Action as a
|
|
63
|
+
* round-cornered rectangle and §14.2.4 draws a State as one. They are the only
|
|
64
|
+
* places this pack departs from `radius: 0`, which is not a house style but what
|
|
65
|
+
* §11.4.4 draws for a classifier — so the departure is as literal as the rule.
|
|
66
|
+
*
|
|
67
|
+
* A native `radius` rather than a glyph, which is what keeps both OFF
|
|
68
|
+
* {@link GLYPH_BODY_KINDS}: the shape layer fills, strokes, hit-tests and
|
|
69
|
+
* re-themes a rounded rect for free, and a glyph-drawn one would have to
|
|
70
|
+
* reimplement all four.
|
|
71
|
+
*/
|
|
72
|
+
export declare const ROUNDED_KINDS: ReadonlySet<UmlNodeKind>;
|
|
73
|
+
/**
|
|
74
|
+
* Every prop a UML shape is created with, for one kind and one box.
|
|
75
|
+
*
|
|
76
|
+
* `index` is deliberately NOT here: it is a layering statement the creation site
|
|
77
|
+
* makes about the handful of elements it is about to write in painting order,
|
|
78
|
+
* and it means nothing to an element that already exists.
|
|
79
|
+
*
|
|
80
|
+
* No `text` either, and that is R16 rather than an omission: a classifier's
|
|
81
|
+
* name, attributes and operations are canvas TEXT elements grouped with the
|
|
82
|
+
* shape, and the shape is a body and nothing else. Its text COLOUR and FACE are
|
|
83
|
+
* still seeded, because a shape's native inner text remains reachable — an
|
|
84
|
+
* author may type into it through the plain shape editor, and what they type
|
|
85
|
+
* must not arrive in some other diagram's font.
|
|
86
|
+
*/
|
|
87
|
+
export declare function umlNodeProps(kind: UmlNodeKind, box: {
|
|
88
|
+
xywh: string;
|
|
89
|
+
}): Record<string, unknown> & {
|
|
90
|
+
type: string;
|
|
91
|
+
};
|
|
92
|
+
/**
|
|
93
|
+
* What a kind is worth to a shape that ALREADY EXISTS — {@link umlNodeProps}
|
|
94
|
+
* minus the three things a morph has no business touching.
|
|
95
|
+
*
|
|
96
|
+
* ## Why the whole preset, and not `{kind, role}`
|
|
97
|
+
*
|
|
98
|
+
* Because on this pack's own table it is visibly not enough. Morphing a class to
|
|
99
|
+
* a package flips `filled` and `strokeStyle` (the folder is a glyph, the class
|
|
100
|
+
* is a native rect); morphing either to a use case rewrites `shapeType` from
|
|
101
|
+
* `rect` to `ellipse`. Two keys would leave a filled rectangle painted behind a
|
|
102
|
+
* tabbed folder, or an ellipse's label centred in a box that is still square.
|
|
103
|
+
*
|
|
104
|
+
* Shipped in phase 1 although the morph itself is tranche D's: the preset is
|
|
105
|
+
* what a morph is FOR, and writing it here beside the creation props is what
|
|
106
|
+
* stops the two from being written by different hands against different tables.
|
|
107
|
+
*/
|
|
108
|
+
export declare function umlMorphProps(kind: UmlNodeKind): Record<string, unknown>;
|
|
109
|
+
/**
|
|
110
|
+
* The fields to DELETE after morphing to `kind` — the keys some other kind
|
|
111
|
+
* writes and this one does not.
|
|
112
|
+
*
|
|
113
|
+
* EMPTY for every kind today, because no UML preset spreads anything
|
|
114
|
+
* conditionally: all thirty-eight write the same key set with different values. Kept
|
|
115
|
+
* anyway, and derived rather than hard-coded to `[]`, for the reason BPMN's and
|
|
116
|
+
* C4's equivalents exist at all — a patch cannot express absence, and the day
|
|
117
|
+
* one kind stops writing a key the previous kind's value would otherwise stay
|
|
118
|
+
* in the Y.Map, silently in force.
|
|
119
|
+
*/
|
|
120
|
+
export declare function umlMorphClears(kind: UmlNodeKind): readonly string[];
|
|
121
|
+
/**
|
|
122
|
+
* Every prop a compartment's TEXT element is created with.
|
|
123
|
+
*
|
|
124
|
+
* Passing them all explicitly is not belt and braces: `surface.addElement` runs
|
|
125
|
+
* new props through `EditPropsStore.applyLastProps('text', …)`, which merges
|
|
126
|
+
* whatever the user last set on a free text element underneath — a 24px face, a
|
|
127
|
+
* colour from another diagram, a left alignment. Explicit props win the merge,
|
|
128
|
+
* so the only ones that survive it are the ones written here. An operations
|
|
129
|
+
* compartment inheriting the colour of the last sticky note somebody typed would
|
|
130
|
+
* be a notation set by accident.
|
|
131
|
+
*
|
|
132
|
+
* `hasMaxWidth` is what keeps a long signature inside the classifier instead of
|
|
133
|
+
* running out over the canvas: the box wraps at its own width and grows
|
|
134
|
+
* downward, and the group grows with it, so a component always contains its own
|
|
135
|
+
* words.
|
|
136
|
+
*
|
|
137
|
+
* `bold` is offered for exactly one tier — the classifier's NAME, which is the
|
|
138
|
+
* heading of the box and, at 16px against a 13px feature line, is not set apart
|
|
139
|
+
* by size alone. Everything else is Regular, because UML reserves its type
|
|
140
|
+
* variations for meanings the grammar reads (italics for abstract, §9.2.4) and
|
|
141
|
+
* a decorative weight would compete with them.
|
|
142
|
+
*/
|
|
143
|
+
export declare function umlTextProps(role: string, box: UmlBox, options: {
|
|
144
|
+
fontSize: number;
|
|
145
|
+
align: TextAlign;
|
|
146
|
+
bold?: boolean;
|
|
147
|
+
}): Record<string, unknown> & {
|
|
148
|
+
type: string;
|
|
149
|
+
};
|
package/dist/presets.js
ADDED
|
@@ -0,0 +1,302 @@
|
|
|
1
|
+
import { FontFamily, FontStyle, FontWeight, ShapeStyle, StrokeStyle, TextAlign, } from '@formicoidea/labre-core/model';
|
|
2
|
+
import { Bound } from '@formicoidea/labre-core/global/gfx';
|
|
3
|
+
import { UML_CARD, UML_INK, UML_NAME_FONT_SIZE, UML_NODE_BOX, UML_NODE_RADIUS, UML_NODE_STROKE_WIDTH, } from './consts.js';
|
|
4
|
+
import { UML_ROLE_OF_KIND } from './roles.js';
|
|
5
|
+
/**
|
|
6
|
+
* What a UML shape IS, as props — the ONE description the creation site, the
|
|
7
|
+
* morph and the templates all read (R17).
|
|
8
|
+
*
|
|
9
|
+
* The rule exists because a kind's appearance is written by the preset of the
|
|
10
|
+
* kind it was created as and nothing ever rewrites it afterwards: a second table
|
|
11
|
+
* restating the same sizes and colours would agree with this one on the day it
|
|
12
|
+
* was written and drift on the first restyle. Derived, they cannot — a morphed
|
|
13
|
+
* classifier and one freshly drawn from the toolbox are the same element.
|
|
14
|
+
*
|
|
15
|
+
* ## No colour decisions here, and that is the notation's doing
|
|
16
|
+
*
|
|
17
|
+
* UML 2.5.1 defines no palette. Every figure in the specification is black lines
|
|
18
|
+
* on white paper, and what tells a class from an interface from an enumeration
|
|
19
|
+
* is the KEYWORD written above the name (§9.5.4), never a hue. So this file
|
|
20
|
+
* seeds one ink and one paper off the shared neutral scale (R33, through
|
|
21
|
+
* `consts.ts`) and spends its judgement on the two things the spec DOES
|
|
22
|
+
* legislate: which kinds are a divided rectangle, and which are a picture.
|
|
23
|
+
*/
|
|
24
|
+
/**
|
|
25
|
+
* The kinds whose GLYPH draws the body, so the native shape underneath paints
|
|
26
|
+
* nothing at all.
|
|
27
|
+
*
|
|
28
|
+
* Twenty-five of thirty-eight, and the node renderer is the authority on which. A package is
|
|
29
|
+
* a tabbed folder (§12.2.4), a note a rectangle with a folded corner (Annex A),
|
|
30
|
+
* an actor a stick figure (§18.1.4) — none of which a native rect can be, so
|
|
31
|
+
* each is created `filled: false` with `StrokeStyle.None` and the renderer
|
|
32
|
+
* paints the outline and the body itself, reading `fillColor` / `strokeColor`
|
|
33
|
+
* off this same model. Both therefore stay editable from the ordinary shape
|
|
34
|
+
* toolbar, exactly like every other node's.
|
|
35
|
+
*
|
|
36
|
+
* Phase 2 added six more, and every one of them for the same reason:
|
|
37
|
+
*
|
|
38
|
+
* - `node`, `device` and `execution-environment` are the 3-D CUBE of §19.4.4 —
|
|
39
|
+
* three faces, two of them parallelograms, which no `shapeType` has;
|
|
40
|
+
* - `provided-interface` and `required-interface` are the ball and the socket
|
|
41
|
+
* of §10.4.4, each a curve on a stub rather than a filled area;
|
|
42
|
+
* - `port` is the one that looks as if a native rect would do (§11.3.4 draws a
|
|
43
|
+
* small square), and it is here because the square is the GLYPH: it is drawn
|
|
44
|
+
* to a fixed proportion of the element so that a port dragged bigger stays a
|
|
45
|
+
* port rather than becoming a box, and it is centred so the name written
|
|
46
|
+
* beside it lines up with it.
|
|
47
|
+
*
|
|
48
|
+
* The USE CASE is deliberately not on the list, and it is the one that looks as
|
|
49
|
+
* if it should be: an ellipse is not a rectangle either — but it is a native
|
|
50
|
+
* `shapeType`, so the platform draws it, fills it and hit-tests it with no glyph
|
|
51
|
+
* involved. Nor are `component` and `artifact`, which ARE the native filled
|
|
52
|
+
* rectangle with a small icon painted into the corner. A kind is on this list
|
|
53
|
+
* when the SHAPE LAYER cannot draw its body, not when it has a glyph.
|
|
54
|
+
*
|
|
55
|
+
* They stay hit-testable across their whole area all the same:
|
|
56
|
+
* `UmlNodeElementModel.includesPoint` forces the interior test regardless of
|
|
57
|
+
* `filled`, which is what keeps an unfilled package draggable and
|
|
58
|
+
* double-clickable across its body rather than along its (absent) stroke.
|
|
59
|
+
*/
|
|
60
|
+
export const GLYPH_BODY_KINDS = new Set([
|
|
61
|
+
'package',
|
|
62
|
+
'note',
|
|
63
|
+
'actor',
|
|
64
|
+
'port',
|
|
65
|
+
'provided-interface',
|
|
66
|
+
'required-interface',
|
|
67
|
+
'node',
|
|
68
|
+
'device',
|
|
69
|
+
'execution-environment',
|
|
70
|
+
// Phase 2, the behaviour marks. Fourteen more, and every one of them for the
|
|
71
|
+
// reason the nine above are here — the shape layer cannot draw the body:
|
|
72
|
+
//
|
|
73
|
+
// - `initial`, `junction`, `activity-final`, `flow-final`, `final-state`,
|
|
74
|
+
// `shallow-history`, `deep-history`, `entry-point`, `exit-point` are
|
|
75
|
+
// CIRCLES with something inside them (a second disc, a cross, a letter) or
|
|
76
|
+
// a filled disc at a fixed proportion of the element — an `ellipse`
|
|
77
|
+
// shapeType would stretch into an egg and would carry nothing inside it;
|
|
78
|
+
// - `fork` is the bar of §15.3.4, drawn to a fixed proportion of the element
|
|
79
|
+
// so a fork dragged taller stays a bar rather than becoming a box;
|
|
80
|
+
// - `send-signal` and `accept-event` are the convex and concave pentagons of
|
|
81
|
+
// §16.3.4, which no `shapeType` has;
|
|
82
|
+
// - `time-event` is the hourglass of §16.10.4, two triangles meeting at a
|
|
83
|
+
// point;
|
|
84
|
+
// - `terminate` is a bare X — nothing but two strokes, and a filled
|
|
85
|
+
// rectangle behind it would be a box the notation does not draw.
|
|
86
|
+
//
|
|
87
|
+
// NOT on the list, and each looking as if it should be: `action` and `state`
|
|
88
|
+
// are the native rect with a `radius` (§15.3.4, §14.2.4 — a round-cornered
|
|
89
|
+
// rectangle IS a native rounded rect); `decision` and `choice` are the native
|
|
90
|
+
// `diamond` shapeType; `object-node` is the plain native rect of §15.4.4.
|
|
91
|
+
'initial',
|
|
92
|
+
'activity-final',
|
|
93
|
+
'flow-final',
|
|
94
|
+
'fork',
|
|
95
|
+
'send-signal',
|
|
96
|
+
'accept-event',
|
|
97
|
+
'time-event',
|
|
98
|
+
'final-state',
|
|
99
|
+
'junction',
|
|
100
|
+
'shallow-history',
|
|
101
|
+
'deep-history',
|
|
102
|
+
'entry-point',
|
|
103
|
+
'exit-point',
|
|
104
|
+
'terminate',
|
|
105
|
+
// Phase 3, the interaction marks. Two more, and again because the shape
|
|
106
|
+
// layer cannot draw the body:
|
|
107
|
+
//
|
|
108
|
+
// - `lifeline` is §17.3.4's head box over a DASHED SPINE, and the element is
|
|
109
|
+
// the 16-wide column the spine runs down — a native rect would paint a
|
|
110
|
+
// tall thin box where the notation draws a line, and would paint no head
|
|
111
|
+
// at all;
|
|
112
|
+
// - `destruction` is a bare X (§17.2.4), two strokes and nothing else, so a
|
|
113
|
+
// filled rectangle behind it would be a box the notation does not draw —
|
|
114
|
+
// the argument `terminate` already makes.
|
|
115
|
+
//
|
|
116
|
+
// `execution` is deliberately NOT here: §17.2.4 draws an
|
|
117
|
+
// ExecutionSpecification as a thin FILLED RECTANGLE on the spine, which is
|
|
118
|
+
// exactly what a native rect is. The shape layer fills it, strokes it,
|
|
119
|
+
// hit-tests it and re-themes it, and a glyph over it would reimplement all
|
|
120
|
+
// four — the same call `object-node` and `action` make.
|
|
121
|
+
'lifeline',
|
|
122
|
+
'destruction',
|
|
123
|
+
]);
|
|
124
|
+
/**
|
|
125
|
+
* The kinds the native shape draws with ROUNDED corners.
|
|
126
|
+
*
|
|
127
|
+
* Two, and both by the notation's own instruction: §15.3.4 draws an Action as a
|
|
128
|
+
* round-cornered rectangle and §14.2.4 draws a State as one. They are the only
|
|
129
|
+
* places this pack departs from `radius: 0`, which is not a house style but what
|
|
130
|
+
* §11.4.4 draws for a classifier — so the departure is as literal as the rule.
|
|
131
|
+
*
|
|
132
|
+
* A native `radius` rather than a glyph, which is what keeps both OFF
|
|
133
|
+
* {@link GLYPH_BODY_KINDS}: the shape layer fills, strokes, hit-tests and
|
|
134
|
+
* re-themes a rounded rect for free, and a glyph-drawn one would have to
|
|
135
|
+
* reimplement all four.
|
|
136
|
+
*/
|
|
137
|
+
export const ROUNDED_KINDS = new Set([
|
|
138
|
+
'action',
|
|
139
|
+
'state',
|
|
140
|
+
]);
|
|
141
|
+
/**
|
|
142
|
+
* The kinds the native shape draws as a DIAMOND.
|
|
143
|
+
*
|
|
144
|
+
* §15.3.4's decision/merge node and §14.2.4's choice pseudostate — two different
|
|
145
|
+
* metaclasses on two different diagrams, drawn as the same diamond, which is why
|
|
146
|
+
* they share this line and not a role. `diamond` is a native `shapeType`, so the
|
|
147
|
+
* platform fills it, strokes it and hit-tests it with no glyph involved, exactly
|
|
148
|
+
* as it does the use case's ellipse.
|
|
149
|
+
*/
|
|
150
|
+
const DIAMOND_KINDS = new Set([
|
|
151
|
+
'decision',
|
|
152
|
+
'choice',
|
|
153
|
+
]);
|
|
154
|
+
/**
|
|
155
|
+
* Every prop a UML shape is created with, for one kind and one box.
|
|
156
|
+
*
|
|
157
|
+
* `index` is deliberately NOT here: it is a layering statement the creation site
|
|
158
|
+
* makes about the handful of elements it is about to write in painting order,
|
|
159
|
+
* and it means nothing to an element that already exists.
|
|
160
|
+
*
|
|
161
|
+
* No `text` either, and that is R16 rather than an omission: a classifier's
|
|
162
|
+
* name, attributes and operations are canvas TEXT elements grouped with the
|
|
163
|
+
* shape, and the shape is a body and nothing else. Its text COLOUR and FACE are
|
|
164
|
+
* still seeded, because a shape's native inner text remains reachable — an
|
|
165
|
+
* author may type into it through the plain shape editor, and what they type
|
|
166
|
+
* must not arrive in some other diagram's font.
|
|
167
|
+
*/
|
|
168
|
+
export function umlNodeProps(kind, box) {
|
|
169
|
+
const glyphBody = GLYPH_BODY_KINDS.has(kind);
|
|
170
|
+
return {
|
|
171
|
+
type: 'umlNode',
|
|
172
|
+
kind,
|
|
173
|
+
// Semantic identity, posted next to `kind` — which stays untouched and keeps
|
|
174
|
+
// driving the rendering. The role is the authority on what the box MEANS,
|
|
175
|
+
// and it is the only thing that can say so: a class, an interface and an
|
|
176
|
+
// enumeration are the SAME rectangle, told apart by their keyword line
|
|
177
|
+
// (`./roles.ts`).
|
|
178
|
+
role: UML_ROLE_OF_KIND[kind],
|
|
179
|
+
// The kinds the platform draws for us that are not rectangles. §18.1.4
|
|
180
|
+
// draws a use case as an ellipse; §15.3.4 and §14.2.4 draw a decision and a
|
|
181
|
+
// choice as a diamond. A native `shapeType` means the platform fills it,
|
|
182
|
+
// strokes it and hit-tests it with no glyph involved.
|
|
183
|
+
shapeType: kind === 'use-case'
|
|
184
|
+
? 'ellipse'
|
|
185
|
+
: DIAMOND_KINDS.has(kind)
|
|
186
|
+
? 'diamond'
|
|
187
|
+
: 'rect',
|
|
188
|
+
filled: !glyphBody,
|
|
189
|
+
fillColor: UML_CARD,
|
|
190
|
+
strokeColor: UML_INK,
|
|
191
|
+
strokeWidth: UML_NODE_STROKE_WIDTH,
|
|
192
|
+
strokeStyle: glyphBody ? StrokeStyle.None : StrokeStyle.Solid,
|
|
193
|
+
shapeStyle: ShapeStyle.General,
|
|
194
|
+
// Zero roughness everywhere, and not a default worth revisiting: UML's
|
|
195
|
+
// figures are drawn with a ruler, and a hand-drawn wobble would be this
|
|
196
|
+
// pack inventing a house style for a notation that has one.
|
|
197
|
+
roughness: 0,
|
|
198
|
+
// The corner is a different question, and the notation answers it per
|
|
199
|
+
// artefact: square for a classifier (§11.4.4), ROUNDED for an action
|
|
200
|
+
// (§15.3.4) and a state (§14.2.4). So the zero here is the classifier's
|
|
201
|
+
// rule rather than a pack-wide one — see {@link ROUNDED_KINDS}.
|
|
202
|
+
radius: ROUNDED_KINDS.has(kind) ? UML_NODE_RADIUS : 0,
|
|
203
|
+
color: UML_INK,
|
|
204
|
+
fontFamily: FontFamily.Inter,
|
|
205
|
+
fontSize: UML_NAME_FONT_SIZE,
|
|
206
|
+
textAlign: TextAlign.Center,
|
|
207
|
+
xywh: box.xywh,
|
|
208
|
+
};
|
|
209
|
+
}
|
|
210
|
+
/**
|
|
211
|
+
* The box {@link umlMorphProps} hands {@link umlNodeProps} and then throws away.
|
|
212
|
+
* Never written to a document: a morph keeps the geometry the element already
|
|
213
|
+
* has, and this exists only because the one builder takes a box.
|
|
214
|
+
*/
|
|
215
|
+
const DISCARDED_BOX = '[0,0,0,0]';
|
|
216
|
+
/** What a morph must never rewrite: identity, geometry, and the user's words. */
|
|
217
|
+
const NOT_A_MORPH = ['type', 'xywh', 'text'];
|
|
218
|
+
/**
|
|
219
|
+
* What a kind is worth to a shape that ALREADY EXISTS — {@link umlNodeProps}
|
|
220
|
+
* minus the three things a morph has no business touching.
|
|
221
|
+
*
|
|
222
|
+
* ## Why the whole preset, and not `{kind, role}`
|
|
223
|
+
*
|
|
224
|
+
* Because on this pack's own table it is visibly not enough. Morphing a class to
|
|
225
|
+
* a package flips `filled` and `strokeStyle` (the folder is a glyph, the class
|
|
226
|
+
* is a native rect); morphing either to a use case rewrites `shapeType` from
|
|
227
|
+
* `rect` to `ellipse`. Two keys would leave a filled rectangle painted behind a
|
|
228
|
+
* tabbed folder, or an ellipse's label centred in a box that is still square.
|
|
229
|
+
*
|
|
230
|
+
* Shipped in phase 1 although the morph itself is tranche D's: the preset is
|
|
231
|
+
* what a morph is FOR, and writing it here beside the creation props is what
|
|
232
|
+
* stops the two from being written by different hands against different tables.
|
|
233
|
+
*/
|
|
234
|
+
export function umlMorphProps(kind) {
|
|
235
|
+
// Widened to the plain record on the way in: `type` is required on what the
|
|
236
|
+
// creation builder returns, and `delete` may only take an optional key.
|
|
237
|
+
const props = {
|
|
238
|
+
...umlNodeProps(kind, { xywh: DISCARDED_BOX }),
|
|
239
|
+
};
|
|
240
|
+
for (const key of NOT_A_MORPH)
|
|
241
|
+
delete props[key];
|
|
242
|
+
return props;
|
|
243
|
+
}
|
|
244
|
+
/**
|
|
245
|
+
* Every key ANY kind's props may carry — the union over the whole pack.
|
|
246
|
+
*
|
|
247
|
+
* Computed rather than listed, so a preset that starts spreading a key
|
|
248
|
+
* conditionally is covered on the day it is added rather than on the day
|
|
249
|
+
* somebody notices.
|
|
250
|
+
*/
|
|
251
|
+
const EVERY_MORPH_KEY = new Set(Object.keys(UML_NODE_BOX).flatMap(kind => Object.keys(umlMorphProps(kind))));
|
|
252
|
+
/**
|
|
253
|
+
* The fields to DELETE after morphing to `kind` — the keys some other kind
|
|
254
|
+
* writes and this one does not.
|
|
255
|
+
*
|
|
256
|
+
* EMPTY for every kind today, because no UML preset spreads anything
|
|
257
|
+
* conditionally: all thirty-eight write the same key set with different values. Kept
|
|
258
|
+
* anyway, and derived rather than hard-coded to `[]`, for the reason BPMN's and
|
|
259
|
+
* C4's equivalents exist at all — a patch cannot express absence, and the day
|
|
260
|
+
* one kind stops writing a key the previous kind's value would otherwise stay
|
|
261
|
+
* in the Y.Map, silently in force.
|
|
262
|
+
*/
|
|
263
|
+
export function umlMorphClears(kind) {
|
|
264
|
+
const present = new Set(Object.keys(umlMorphProps(kind)));
|
|
265
|
+
return [...EVERY_MORPH_KEY].filter(key => !present.has(key));
|
|
266
|
+
}
|
|
267
|
+
/**
|
|
268
|
+
* Every prop a compartment's TEXT element is created with.
|
|
269
|
+
*
|
|
270
|
+
* Passing them all explicitly is not belt and braces: `surface.addElement` runs
|
|
271
|
+
* new props through `EditPropsStore.applyLastProps('text', …)`, which merges
|
|
272
|
+
* whatever the user last set on a free text element underneath — a 24px face, a
|
|
273
|
+
* colour from another diagram, a left alignment. Explicit props win the merge,
|
|
274
|
+
* so the only ones that survive it are the ones written here. An operations
|
|
275
|
+
* compartment inheriting the colour of the last sticky note somebody typed would
|
|
276
|
+
* be a notation set by accident.
|
|
277
|
+
*
|
|
278
|
+
* `hasMaxWidth` is what keeps a long signature inside the classifier instead of
|
|
279
|
+
* running out over the canvas: the box wraps at its own width and grows
|
|
280
|
+
* downward, and the group grows with it, so a component always contains its own
|
|
281
|
+
* words.
|
|
282
|
+
*
|
|
283
|
+
* `bold` is offered for exactly one tier — the classifier's NAME, which is the
|
|
284
|
+
* heading of the box and, at 16px against a 13px feature line, is not set apart
|
|
285
|
+
* by size alone. Everything else is Regular, because UML reserves its type
|
|
286
|
+
* variations for meanings the grammar reads (italics for abstract, §9.2.4) and
|
|
287
|
+
* a decorative weight would compete with them.
|
|
288
|
+
*/
|
|
289
|
+
export function umlTextProps(role, box, options) {
|
|
290
|
+
return {
|
|
291
|
+
type: 'text',
|
|
292
|
+
role,
|
|
293
|
+
color: UML_INK,
|
|
294
|
+
fontFamily: FontFamily.Inter,
|
|
295
|
+
fontSize: options.fontSize,
|
|
296
|
+
fontWeight: options.bold ? FontWeight.SemiBold : FontWeight.Regular,
|
|
297
|
+
fontStyle: FontStyle.Normal,
|
|
298
|
+
textAlign: options.align,
|
|
299
|
+
hasMaxWidth: true,
|
|
300
|
+
xywh: new Bound(box.x, box.y, box.w, box.h).serialize(),
|
|
301
|
+
};
|
|
302
|
+
}
|