@formicoidea/labre-framework-uml 0.59.0 → 0.61.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/actions.d.ts +14 -13
- package/dist/actions.js +118 -258
- package/dist/component.d.ts +4 -4
- package/dist/component.js +4 -4
- package/dist/import.d.ts +7 -6
- package/dist/import.js +42 -141
- package/dist/index.d.ts +1 -0
- package/dist/index.js +3 -0
- package/dist/interchange-headless.d.ts +1 -0
- package/dist/interchange-headless.js +3 -0
- package/dist/keywords.d.ts +26 -0
- package/dist/keywords.js +32 -0
- package/dist/presets.d.ts +91 -2
- package/dist/presets.js +178 -3
- package/dist/toolbox.d.ts +84 -0
- package/dist/toolbox.js +172 -0
- package/package.json +3 -3
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import type { FrameworkToolbox, SerializedElementProps, ToolboxPoint } from '@formicoidea/labre-core/interchange';
|
|
2
|
+
import type { UmlDiagramKind, UmlFragmentOperator, UmlNodeKind } from '@formicoidea/labre-core/model';
|
|
3
|
+
import { type UmlEdgeRole } from './edge-styles.js';
|
|
4
|
+
import { type UmlFrameType } from './presets.js';
|
|
5
|
+
/**
|
|
6
|
+
* UML's toolbar as pure builders (`docs/adr/0045`): what `createUmlNode`,
|
|
7
|
+
* `createUmlClassifier`, the five frame actions and the twenty edge tools
|
|
8
|
+
* place, with no editor behind them. The actions call these, and the
|
|
9
|
+
* importers write an artefact through the same assembly
|
|
10
|
+
* (`umlArtefactElements`, `presets.ts`), so a class a host places, one drawn
|
|
11
|
+
* from the toolbar and one read out of a `.puml` file are the same elements.
|
|
12
|
+
*
|
|
13
|
+
* ## Data, not sixty-four builders
|
|
14
|
+
*
|
|
15
|
+
* {@link UML_TOOLBOX} is walked off the tables that already exist: the
|
|
16
|
+
* toolbar's own rows (`umlCommandsManifest`, in the toolbar's order), the
|
|
17
|
+
* node kinds (`UML_NODE_BOX`), the edge styles (`UML_EDGE_STYLE`) and six
|
|
18
|
+
* frame commands. A node command is `uml.add<Kind>` and an edge tool
|
|
19
|
+
* `uml.<role>Tool`, so a kind or a relationship added to its table and to the
|
|
20
|
+
* toolbar arrives here without a line changing; `toolbox.unit.spec.ts` says
|
|
21
|
+
* which rows are left out — the five interchange commands (export PlantUML,
|
|
22
|
+
* export XMI, import XMI, import PlantUML, import draw.io), which read or
|
|
23
|
+
* write a file and place nothing a builder could.
|
|
24
|
+
*/
|
|
25
|
+
/** The text slots of an artefact. */
|
|
26
|
+
export interface UmlNodeTexts {
|
|
27
|
+
/** The name, or a glyph's one word; the kind's English seed when absent. */
|
|
28
|
+
name?: string;
|
|
29
|
+
/**
|
|
30
|
+
* A divided box's second compartment, one member per line (an object's
|
|
31
|
+
* slots, a state's internal activities); the kind's seed when absent, and
|
|
32
|
+
* empty when given empty.
|
|
33
|
+
*/
|
|
34
|
+
attributes?: string;
|
|
35
|
+
/** A class's, an interface's or an enumeration's operations, likewise. */
|
|
36
|
+
operations?: string;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* A UML artefact at `at` (its top-left), at the size the toolbar draws it: the
|
|
40
|
+
* shape, its tiers and the group LAST (`umlArtefactElements`), with ids
|
|
41
|
+
* `<local>:shape`, `<local>:name`, `<local>:attributes`, `<local>:operations`
|
|
42
|
+
* and `<local>:group`. A relationship attaches to `<local>:shape`. A mark the
|
|
43
|
+
* notation draws no word on (an initial node, a fork…) is the shape alone.
|
|
44
|
+
*
|
|
45
|
+
* A port's, an interface mark's and a time event's word is written BESIDE
|
|
46
|
+
* the glyph (`UML_BESIDE_LABEL_KINDS`): the group is wider than the shape's
|
|
47
|
+
* box there, and only there.
|
|
48
|
+
*/
|
|
49
|
+
export declare function umlNodeElements(kind: UmlNodeKind, at: ToolboxPoint, texts?: UmlNodeTexts, local?: string): SerializedElementProps[];
|
|
50
|
+
/**
|
|
51
|
+
* A frame's name as `[key, English]`: the key the toolbar resolves at
|
|
52
|
+
* placement (`docs/adr/0023`), the English a host with no catalogue writes.
|
|
53
|
+
* The key is the frame ROLE's: the two are the same noun, and a second key
|
|
54
|
+
* would ask a host to word it twice. A combined fragment has none: its `name`
|
|
55
|
+
* is a guard, and the notation draws a guard only where there is one.
|
|
56
|
+
*/
|
|
57
|
+
export declare const UML_FRAME_SEED: {
|
|
58
|
+
readonly umlDiagram: readonly [string, "Diagram"];
|
|
59
|
+
readonly umlSubject: readonly [string, "Subject"];
|
|
60
|
+
readonly umlPartition: readonly [string, "Partition"];
|
|
61
|
+
readonly umlRegion: readonly [string, "Region"];
|
|
62
|
+
};
|
|
63
|
+
/** What a frame is born as, beyond its name. */
|
|
64
|
+
export interface UmlFrameOptions {
|
|
65
|
+
/** A diagram's kind, the word in its heading tag (Annex A); `class` when absent. */
|
|
66
|
+
kind?: UmlDiagramKind;
|
|
67
|
+
/** A fragment's interaction operator (§17.6.4); `alt` when absent. */
|
|
68
|
+
operator?: UmlFragmentOperator;
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* A frame at `at`, at the size the toolbar draws it, with id `local`.
|
|
72
|
+
*
|
|
73
|
+
* A diagram, a subject, a partition and a region take `name`, or their
|
|
74
|
+
* English seed. A combined fragment is born unnamed, as the toolbar draws it:
|
|
75
|
+
* its `name` is the guard of its first operand (`[x > 0]`), or under `ref` the
|
|
76
|
+
* name of the interaction it refers to (§17.7.4), and is written only when
|
|
77
|
+
* given. A partition is born vertical (the model's default, never restated)
|
|
78
|
+
* and a fragment with one operand and no `operands` (the model's promise).
|
|
79
|
+
*/
|
|
80
|
+
export declare function umlFrameElement(type: UmlFrameType, at: ToolboxPoint, name?: string, local?: string, options?: UmlFrameOptions): SerializedElementProps;
|
|
81
|
+
/** The connector an edge tool is armed with, between two ends. */
|
|
82
|
+
export declare function umlRelationElement(edge: UmlEdgeRole, from: string, to: string, label?: string): SerializedElementProps;
|
|
83
|
+
/** Every item of the UML toolbar that places elements, in the toolbar's order. */
|
|
84
|
+
export declare const UML_TOOLBOX: FrameworkToolbox;
|
package/dist/toolbox.js
ADDED
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
import { Bound } from '@formicoidea/labre-core/global/gfx';
|
|
2
|
+
import { umlCommandsManifest } from './commands-manifest.js';
|
|
3
|
+
import { umlCompartmentBoxes } from './component.js';
|
|
4
|
+
import { UML_NODE_BOX } from './consts.js';
|
|
5
|
+
import { UML_EDGE_STYLE } from './edge-styles.js';
|
|
6
|
+
import { UML_NAME_SEED, UML_OPERATIONS_SEED, UML_UNLABELLED_KINDS, umlBodySeed, } from './keywords.js';
|
|
7
|
+
import { UML_FRAME_BOX, umlArtefactElements, umlConnectorProps, umlFrameProps, } from './presets.js';
|
|
8
|
+
import { umlDiagramRoleKey, umlRoleKey, umlSubjectRoleKey } from './roles.js';
|
|
9
|
+
/**
|
|
10
|
+
* A UML artefact at `at` (its top-left), at the size the toolbar draws it: the
|
|
11
|
+
* shape, its tiers and the group LAST (`umlArtefactElements`), with ids
|
|
12
|
+
* `<local>:shape`, `<local>:name`, `<local>:attributes`, `<local>:operations`
|
|
13
|
+
* and `<local>:group`. A relationship attaches to `<local>:shape`. A mark the
|
|
14
|
+
* notation draws no word on (an initial node, a fork…) is the shape alone.
|
|
15
|
+
*
|
|
16
|
+
* A port's, an interface mark's and a time event's word is written BESIDE
|
|
17
|
+
* the glyph (`UML_BESIDE_LABEL_KINDS`): the group is wider than the shape's
|
|
18
|
+
* box there, and only there.
|
|
19
|
+
*/
|
|
20
|
+
export function umlNodeElements(kind, at, texts = {}, local = kind) {
|
|
21
|
+
const { w, h } = UML_NODE_BOX[kind];
|
|
22
|
+
return umlArtefactElements(kind, { x: at.x, y: at.y, w, h }, {
|
|
23
|
+
name: texts.name?.trim() || UML_NAME_SEED[kind],
|
|
24
|
+
// `??` and not `||`: an empty compartment is a statement (a class with
|
|
25
|
+
// no operations yet), where an empty name never is.
|
|
26
|
+
attributes: texts.attributes?.trimEnd() ?? umlBodySeed(kind)[1],
|
|
27
|
+
operations: texts.operations?.trimEnd() ?? UML_OPERATIONS_SEED,
|
|
28
|
+
}, part => `${local}:${part}`);
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* A frame's name as `[key, English]`: the key the toolbar resolves at
|
|
32
|
+
* placement (`docs/adr/0023`), the English a host with no catalogue writes.
|
|
33
|
+
* The key is the frame ROLE's: the two are the same noun, and a second key
|
|
34
|
+
* would ask a host to word it twice. A combined fragment has none: its `name`
|
|
35
|
+
* is a guard, and the notation draws a guard only where there is one.
|
|
36
|
+
*/
|
|
37
|
+
export const UML_FRAME_SEED = {
|
|
38
|
+
umlDiagram: [umlDiagramRoleKey, 'Diagram'],
|
|
39
|
+
umlSubject: [umlSubjectRoleKey, 'Subject'],
|
|
40
|
+
umlPartition: [umlRoleKey('partition'), 'Partition'],
|
|
41
|
+
umlRegion: [umlRoleKey('region'), 'Region'],
|
|
42
|
+
};
|
|
43
|
+
/**
|
|
44
|
+
* A frame at `at`, at the size the toolbar draws it, with id `local`.
|
|
45
|
+
*
|
|
46
|
+
* A diagram, a subject, a partition and a region take `name`, or their
|
|
47
|
+
* English seed. A combined fragment is born unnamed, as the toolbar draws it:
|
|
48
|
+
* its `name` is the guard of its first operand (`[x > 0]`), or under `ref` the
|
|
49
|
+
* name of the interaction it refers to (§17.7.4), and is written only when
|
|
50
|
+
* given. A partition is born vertical (the model's default, never restated)
|
|
51
|
+
* and a fragment with one operand and no `operands` (the model's promise).
|
|
52
|
+
*/
|
|
53
|
+
export function umlFrameElement(type, at, name, local = type, options = {}) {
|
|
54
|
+
const { w, h } = UML_FRAME_BOX[type];
|
|
55
|
+
const stated = name?.trim();
|
|
56
|
+
const fields = type === 'umlFragment'
|
|
57
|
+
? {
|
|
58
|
+
// The operator IS the fragment: written even at the model's default,
|
|
59
|
+
// so the pentagon never depends on a default nobody can see.
|
|
60
|
+
operator: options.operator ?? 'alt',
|
|
61
|
+
...(stated ? { name: stated } : {}),
|
|
62
|
+
}
|
|
63
|
+
: {
|
|
64
|
+
name: stated || UML_FRAME_SEED[type][1],
|
|
65
|
+
// Annex A's heading is `<kind> <name>`: a frame with no kind has
|
|
66
|
+
// nothing to write in its tag.
|
|
67
|
+
...(type === 'umlDiagram' ? { kind: options.kind ?? 'class' } : {}),
|
|
68
|
+
};
|
|
69
|
+
return {
|
|
70
|
+
...umlFrameProps(type, { xywh: new Bound(at.x, at.y, w, h).serialize() }, fields),
|
|
71
|
+
id: local,
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
/** The connector an edge tool is armed with, between two ends. */
|
|
75
|
+
export function umlRelationElement(edge, from, to, label) {
|
|
76
|
+
return {
|
|
77
|
+
...umlConnectorProps(edge),
|
|
78
|
+
source: { id: from },
|
|
79
|
+
target: { id: to },
|
|
80
|
+
...(label?.trim() ? { text: label.trim() } : {}),
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
/* ── The list, walked off the tables ──────────────────────────────────── */
|
|
84
|
+
const camel = (kebab) => kebab.replace(/-(\w)/g, (_, letter) => letter.toUpperCase());
|
|
85
|
+
const pascal = (kebab) => {
|
|
86
|
+
const word = camel(kebab);
|
|
87
|
+
return word.charAt(0).toUpperCase() + word.slice(1);
|
|
88
|
+
};
|
|
89
|
+
/** `uml.addUseCase` → `use-case`, for every kind the model draws. */
|
|
90
|
+
const NODE_OF_COMMAND = new Map(Object.keys(UML_NODE_BOX).map(kind => [
|
|
91
|
+
`uml.add${pascal(kind)}`,
|
|
92
|
+
kind,
|
|
93
|
+
]));
|
|
94
|
+
/** `uml.communicationPathTool` → `communication-path`, for every edge style. */
|
|
95
|
+
const EDGE_OF_COMMAND = new Map(Object.keys(UML_EDGE_STYLE).map(edge => [
|
|
96
|
+
`uml.${camel(edge)}Tool`,
|
|
97
|
+
edge,
|
|
98
|
+
]));
|
|
99
|
+
/**
|
|
100
|
+
* The six frame commands. The interaction use is a fragment under `ref`
|
|
101
|
+
* (§17.7.4): the same element, a command of its own because pointing at
|
|
102
|
+
* another diagram is a different modelling act from branching this one.
|
|
103
|
+
*/
|
|
104
|
+
const FRAME_OF_COMMAND = {
|
|
105
|
+
'uml.addDiagram': { id: 'diagram', type: 'umlDiagram' },
|
|
106
|
+
'uml.addSubject': { id: 'subject', type: 'umlSubject' },
|
|
107
|
+
'uml.addPartition': { id: 'partition', type: 'umlPartition' },
|
|
108
|
+
'uml.addRegion': { id: 'region', type: 'umlRegion' },
|
|
109
|
+
'uml.addFragment': { id: 'fragment', type: 'umlFragment' },
|
|
110
|
+
'uml.addInteractionUse': {
|
|
111
|
+
id: 'interaction-use',
|
|
112
|
+
type: 'umlFragment',
|
|
113
|
+
options: { operator: 'ref' },
|
|
114
|
+
},
|
|
115
|
+
};
|
|
116
|
+
/** The slots a kind's builder fills: the tiers its layout gives it. */
|
|
117
|
+
function slotsOf(kind) {
|
|
118
|
+
if (UML_UNLABELLED_KINDS.has(kind))
|
|
119
|
+
return [];
|
|
120
|
+
const { w, h } = UML_NODE_BOX[kind];
|
|
121
|
+
const boxes = umlCompartmentBoxes(kind, 0, 0, w, h);
|
|
122
|
+
return [
|
|
123
|
+
'name',
|
|
124
|
+
...(boxes.attributes ? ['attributes'] : []),
|
|
125
|
+
...(boxes.operations ? ['operations'] : []),
|
|
126
|
+
];
|
|
127
|
+
}
|
|
128
|
+
function toolbox() {
|
|
129
|
+
const items = [];
|
|
130
|
+
const links = [];
|
|
131
|
+
for (const row of umlCommandsManifest) {
|
|
132
|
+
const label = {
|
|
133
|
+
labelKey: row.labelKey,
|
|
134
|
+
labelFallback: row.labelFallback ?? row.id,
|
|
135
|
+
};
|
|
136
|
+
const kind = NODE_OF_COMMAND.get(row.id);
|
|
137
|
+
if (kind) {
|
|
138
|
+
items.push({
|
|
139
|
+
id: kind,
|
|
140
|
+
...label,
|
|
141
|
+
texts: slotsOf(kind),
|
|
142
|
+
size: UML_NODE_BOX[kind],
|
|
143
|
+
build: (at, texts, local) => umlNodeElements(kind, at, texts, local),
|
|
144
|
+
});
|
|
145
|
+
continue;
|
|
146
|
+
}
|
|
147
|
+
const frame = FRAME_OF_COMMAND[row.id];
|
|
148
|
+
if (frame) {
|
|
149
|
+
items.push({
|
|
150
|
+
id: frame.id,
|
|
151
|
+
...label,
|
|
152
|
+
texts: ['name'],
|
|
153
|
+
size: UML_FRAME_BOX[frame.type],
|
|
154
|
+
build: (at, texts, local) => [
|
|
155
|
+
umlFrameElement(frame.type, at, texts?.name, local ?? frame.id, frame.options),
|
|
156
|
+
],
|
|
157
|
+
});
|
|
158
|
+
continue;
|
|
159
|
+
}
|
|
160
|
+
const edge = EDGE_OF_COMMAND.get(row.id);
|
|
161
|
+
if (edge) {
|
|
162
|
+
links.push({
|
|
163
|
+
id: edge,
|
|
164
|
+
...label,
|
|
165
|
+
build: (from, to, text) => umlRelationElement(edge, from, to, text),
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
return { framework: 'uml', items, links };
|
|
170
|
+
}
|
|
171
|
+
/** Every item of the UML toolbar that places elements, in the toolbar's order. */
|
|
172
|
+
export const UML_TOOLBOX = toolbox();
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@formicoidea/labre-framework-uml",
|
|
3
3
|
"description": "Labre uml framework for @formicoidea/labre-core.",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.61.0",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"sideEffects": false,
|
|
7
7
|
"author": "lajola",
|
|
@@ -35,8 +35,8 @@
|
|
|
35
35
|
"dist"
|
|
36
36
|
],
|
|
37
37
|
"dependencies": {
|
|
38
|
-
"@formicoidea/labre-core": "0.
|
|
39
|
-
"@formicoidea/labre-ddd-shared": "0.
|
|
38
|
+
"@formicoidea/labre-core": "0.61.0",
|
|
39
|
+
"@formicoidea/labre-ddd-shared": "0.61.0",
|
|
40
40
|
"lit": "^3.2.0"
|
|
41
41
|
}
|
|
42
42
|
}
|