@peter.prib/renderengine 0.2.6 → 0.2.10
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/README.md +70 -9
- package/REFERENCE.md +303 -2
- package/dist/renderengine-workspace.umd.js +8851 -3305
- package/dist/renderengine.umd.js +5621 -1382
- package/examples/archimate.html +68 -66
- package/examples/data-model.html +251 -19
- package/package.json +1 -1
- package/src/bian.js +182 -0
- package/src/charting/DiagramEditor.js +5 -26
- package/src/charting/DiagramTypeRegistry.js +8 -2
- package/src/charting/FormDialog.js +398 -385
- package/src/charting/ModelCheckDialog.js +63 -0
- package/src/charting/ModelChecker.js +26 -0
- package/src/charting/RowsField.js +127 -0
- package/src/charting/ViewTabStrip.js +94 -81
- package/src/charting/datamodel/BianLandscapeReader.js +185 -0
- package/src/charting/datamodel/BianSemanticApiReader.js +183 -0
- package/src/charting/datamodel/ClassificationLens.js +33 -0
- package/src/charting/datamodel/ClassificationScheme.js +98 -0
- package/src/charting/datamodel/DataModel.js +574 -12
- package/src/charting/datamodel/DataModelChecker.js +338 -0
- package/src/charting/datamodel/DataModelCompared.js +127 -0
- package/src/charting/datamodel/DataModelContext.js +40 -10
- package/src/charting/datamodel/DataModelEditCommand.js +47 -0
- package/src/charting/datamodel/DataModelFileFormat.js +33 -0
- package/src/charting/datamodel/DataModelPerspective.js +55 -0
- package/src/charting/datamodel/DataType.js +108 -0
- package/src/charting/datamodel/EnumerationType.js +64 -0
- package/src/charting/datamodel/ServiceDomain.js +186 -0
- package/src/charting/datamodel/ServiceLandscape.js +104 -0
- package/src/charting/datamodel/SqlDdl.js +80 -0
- package/src/charting/datamodel/SqlDdlFileFormat.js +23 -0
- package/src/charting/datamodel/StructuredType.js +63 -0
- package/src/charting/datamodel/index.js +21 -5
- package/src/charting/datamodel/naming/CamelCaseStyle.js +13 -0
- package/src/charting/datamodel/naming/LowerWordsStyle.js +13 -0
- package/src/charting/datamodel/naming/NameStyle.js +57 -0
- package/src/charting/datamodel/naming/NamingStandard.js +132 -0
- package/src/charting/datamodel/naming/PascalCaseStyle.js +13 -0
- package/src/charting/datamodel/naming/SnakeCaseStyle.js +13 -0
- package/src/charting/datamodel/naming/TitleCaseStyle.js +25 -0
- package/src/charting/datamodel/naming/UpperSnakeStyle.js +13 -0
- package/src/charting/datamodel/naming/index.js +8 -0
- package/src/charting/datamodel/scorecard/CompletenessCategory.js +30 -0
- package/src/charting/datamodel/scorecard/ConsistencyCategory.js +33 -0
- package/src/charting/datamodel/scorecard/DefinitionsCategory.js +33 -0
- package/src/charting/datamodel/scorecard/ModelScorecard.js +113 -0
- package/src/charting/datamodel/scorecard/ReadabilityCategory.js +37 -0
- package/src/charting/datamodel/scorecard/ScorecardCategory.js +46 -0
- package/src/charting/datamodel/scorecard/StandardsCategory.js +29 -0
- package/src/charting/datamodel/scorecard/StructureCategory.js +32 -0
- package/src/charting/datamodel/scorecard/index.js +8 -0
- package/src/charting/diagramModel/MergeModelCommand.js +43 -37
- package/src/charting/diagramModel/OpenViewList.js +75 -0
- package/src/charting/diagramModel/ViewSetChecker.js +22 -34
- package/src/charting/diagrams/DataModelDiagram.js +4 -11
- package/src/charting/diagrams/DataModelRelationshipStrategy.js +19 -5
- package/src/charting/diagrams/EdgeGeometry.js +578 -553
- package/src/charting/diagrams/GraphDiagram.js +11 -1
- package/src/charting/diagrams/NodeGeometry.js +173 -158
- package/src/charting/glyphs/PersonGlyph.js +24 -23
- package/src/charting/toolbar/ToolbarSelect.js +6 -0
- package/src/datamodel.js +402 -4
- package/src/index.js +6 -4
- package/src/workspace/EditorShell.js +166 -67
- package/src/workspace/PanelLayout.js +56 -0
- package/src/workspace/layout/TabsNode.js +205 -191
- package/src/workspace/parts/ArchimateCanvasPart.js +5 -7
- package/src/workspace/parts/DataModelPart.js +534 -33
- package/src/workspace/parts/DiagramEditorPart.js +2 -8
- package/src/workspace/parts/EditorPart.js +461 -407
- package/src/workspace/parts/ModelFile.js +216 -187
- package/src/workspace/parts/ModelsPart.js +483 -192
- package/src/workspace/storage/BackupFolder.js +145 -0
- package/src/workspace/storage/KeyValueStore.js +92 -0
- package/src/workspace/storage/ModelLibrary.js +65 -0
- package/src/workspace/workArea.js +417 -413
- package/src/yaml/YamlReader.js +372 -0
package/README.md
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
# RenderEngine
|
|
2
2
|
|
|
3
|
-
**An ArchiMate modeller, a diagram editor and a chart library that run in any browser tab, with
|
|
4
|
-
runtime dependencies.**
|
|
3
|
+
**An ArchiMate modeller, a data modeller, a diagram editor and a chart library that run in any browser tab, with
|
|
4
|
+
no install and no runtime dependencies.**
|
|
5
5
|
|
|
6
6
|
Open an Archi model, edit it the way you would in Archi, and save it back to the same `.archimate` file, without
|
|
7
|
-
installing anything.
|
|
8
|
-
|
|
7
|
+
installing anything. Model data at three levels, governed and checked the way DAMA-DMBOK2 has it, with banking at
|
|
8
|
+
BIAN level built in. Or put the same editors in your own app: it's a JavaScript library and a set of workbench
|
|
9
|
+
parts, not a desktop application.
|
|
9
10
|
|
|
10
11
|

|
|
11
12
|
|
|
@@ -18,7 +19,7 @@ git clone https://gitlab.com/peter.prib/renderengine.git
|
|
|
18
19
|
cd renderengine
|
|
19
20
|
npm start # the ArchiMate modeller
|
|
20
21
|
npm run diagram # the diagram editor (flowcharts, UML and more)
|
|
21
|
-
npm run datamodel #
|
|
22
|
+
npm run datamodel # the data modeller: an enterprise ontology and a retail bank at BIAN level
|
|
22
23
|
```
|
|
23
24
|
|
|
24
25
|
No server is needed, and nothing needs installing first, because the built bundles are in `dist/`. You can also
|
|
@@ -26,7 +27,7 @@ open the pages directly:
|
|
|
26
27
|
|
|
27
28
|
- `examples/archimate.html`: the ArchiMate modeller
|
|
28
29
|
- `examples/diagram-editor.html`: the diagram editor
|
|
29
|
-
- `examples/data-model.html`:
|
|
30
|
+
- `examples/data-model.html`: the data modeller
|
|
30
31
|
|
|
31
32
|
All work in current Chrome, Edge, Firefox and Safari. Save writes back to the file you opened in Chrome and Edge;
|
|
32
33
|
other browsers download the file instead.
|
|
@@ -39,6 +40,10 @@ other browsers download the file instead.
|
|
|
39
40
|
|
|
40
41
|
- **Open and save Archi files** (`.archimate`, and archives that include pictures). Save writes back to the file you
|
|
41
42
|
opened; Save As picks a new one. Unsaved changes are marked (•), and the page asks before you leave or close them.
|
|
43
|
+
- **Kept in the browser, backed up to the cloud.** Every model is kept in the browser as you edit it, and those open
|
|
44
|
+
reopen next time, with no file saved. File ▸ This browser backs them all up to a folder you choose once: pick one
|
|
45
|
+
that OneDrive, Google Drive or Dropbox syncs and the backups reach the cloud (each named with the date and time,
|
|
46
|
+
plus one under its plain name); browsers that cannot write to a folder download them instead.
|
|
42
47
|
- **Import a spreadsheet** into the open model: choose the name, type, id, documentation, property and "related
|
|
43
48
|
to" columns; existing elements are updated, not duplicated (Archi's elements.csv imports as it is).
|
|
44
49
|
- **draw.io export**: every view a page, in draw.io's ArchiMate shapes, with the elements' properties.
|
|
@@ -107,6 +112,61 @@ It runs in the browser and embeds in other apps. It also has:
|
|
|
107
112
|
|
|
108
113
|
---
|
|
109
114
|
|
|
115
|
+
## The data modeller
|
|
116
|
+
|
|
117
|
+
One model at the conceptual, logical and physical levels, read by each stakeholder in their own terms, governed and
|
|
118
|
+
checked the way DAMA-DMBOK2 has a model reviewed, and ready for banking at BIAN level. It shares the modeller's
|
|
119
|
+
workbench: several models open at once, undo for everything, files saved back where they came from, models kept in
|
|
120
|
+
the browser and backed up.
|
|
121
|
+
|
|
122
|
+

|
|
123
|
+
|
|
124
|
+
### One model, three levels
|
|
125
|
+
|
|
126
|
+
- **Concepts, logical entities and tables, each a schema of its own**, mapped any number either way: a concept
|
|
127
|
+
realised by several entities, several entities kept in one table. The **mapping view** draws the three side by
|
|
128
|
+
side, and **lineage** shows, for anything, what it realises and what realises it, all the way up and down.
|
|
129
|
+
- **Perspectives and contexts**: each stakeholder's names for the concepts, and what they mean in a context, with
|
|
130
|
+
its rules and where and when it applies, drawn as notes.
|
|
131
|
+
- **Chen, Crow's Foot and UML**, switched with one choice; **subtypes** (complete or not, disjoint or overlapping);
|
|
132
|
+
**data types and enumerations** defined once, code sets with their source and version, each linked to its
|
|
133
|
+
**ISO 20022** type.
|
|
134
|
+
- **Edit it on the diagram**: double-click an entity for its attribute table (keys, required, what a foreign key
|
|
135
|
+
references, classification), right-click for new and delete. Every edit is one undo step.
|
|
136
|
+
|
|
137
|
+
### Banking at BIAN level
|
|
138
|
+
|
|
139
|
+
- **Service domains** with their control records, behaviour qualifiers and the service operations BIAN names for
|
|
140
|
+
them, and the **service landscape**: every service domain in its business area and domain.
|
|
141
|
+
- **Read BIAN's own published material instead of retyping it.** A BIAN semantic API (all 258 of release 14 read,
|
|
142
|
+
plain or with ISO 20022) becomes a control record, its qualifiers, their data types and ISO 20022 types in one
|
|
143
|
+
step. BIAN's service landscape in ArchiMate becomes service domains in their areas and domains: run over BIAN's
|
|
144
|
+
v14 model, every one it places lands in the right place.
|
|
145
|
+
- BIAN's vocabulary, its functional patterns, artifacts, action terms and qualifier types, is as BIAN's v14 model
|
|
146
|
+
has it.
|
|
147
|
+
|
|
148
|
+
### Governed and checked (DAMA-DMBOK2)
|
|
149
|
+
|
|
150
|
+
- **Stewardship**: a business owner, data steward and technical custodian on each entity.
|
|
151
|
+
- **Classification**: sensitivity levels and categories (PII, PCI...) on entities and attributes, **checked down
|
|
152
|
+
the mappings**, so a table is never classified lower than the data it holds; a **lens** colours the model by it.
|
|
153
|
+
- **Check model** reviews it as DMBOK2 would: missing keys, foreign keys without relationships, repeating groups,
|
|
154
|
+
unresolved many-to-many, one name with two types, concepts without definitions, names against the **naming
|
|
155
|
+
standard** you set, **data quality rules** by dimension (completeness, validity, uniqueness...), and BIAN's one
|
|
156
|
+
owner per business object.
|
|
157
|
+
- **The Data Model Scorecard**: Steve Hoberman's ten categories, six measured from the model and four for a
|
|
158
|
+
reviewer, weighted as you choose, in one total.
|
|
159
|
+
|
|
160
|
+
### To the database and the warehouse
|
|
161
|
+
|
|
162
|
+
- **SQL DDL**: the physical level as `CREATE TABLE`s with keys, foreign keys and comments.
|
|
163
|
+
- **A dimensional scheme**: facts at their grain with additive, semi-additive or non-additive measures, dimensions
|
|
164
|
+
with their history type and conformed across facts, a **star schema** view, and checks as Kimball would make
|
|
165
|
+
them.
|
|
166
|
+
- **Bases** to start a model from, and **compare and merge** between models.
|
|
167
|
+
|
|
168
|
+
---
|
|
169
|
+
|
|
110
170
|
## The diagram editor
|
|
111
171
|
|
|
112
172
|
The ArchiMate modeller is built on a general diagram editor, and every editing feature above is part of that
|
|
@@ -177,9 +237,10 @@ page:
|
|
|
177
237
|
</script>
|
|
178
238
|
```
|
|
179
239
|
|
|
180
|
-
Use `editorType: 'diagram-editor'` with `DiagramEditorPart` for the diagram editor
|
|
181
|
-
|
|
182
|
-
UI at all
|
|
240
|
+
Use `editorType: 'diagram-editor'` with `DiagramEditorPart` for the diagram editor, and `editorType: 'data-model'`
|
|
241
|
+
with `DataModelPart` for the data modeller. The models are plain JavaScript APIs (`ArchimateModel`, `DiagramModel`,
|
|
242
|
+
`DataModel`), so you can also build, check, score, search, report on and export models with no UI at all, under
|
|
243
|
+
Node too: `new DataModel(spec).checker().run()`, `new SqlDdl().write(model)`.
|
|
183
244
|
|
|
184
245
|
---
|
|
185
246
|
|
package/REFERENCE.md
CHANGED
|
@@ -723,7 +723,7 @@ types — see `ARCHITECTURE.md`):
|
|
|
723
723
|
`'cylinder'`, ...) and `labelPosition: 'below'` keeps the shape's size. In Chen a relationship's diamond holds
|
|
724
724
|
its name and sits midway between its two entities when both have `x`/`y`, or when something is drawn there (an entity, a
|
|
725
725
|
note, another diamond) the nearest clear spot across the line (`RelationshipAsNodeStrategy.spotFor`,
|
|
726
|
-
`DataModelDiagram.boxOf`); its lines have no arrowheads.
|
|
726
|
+
`DataModelDiagram.boxOf`); its lines have no arrowheads; when no spot near the line is clear, the least covered one.
|
|
727
727
|
Each level and notation has a `label` for menus (`'Conceptual'`, `"Crow's Foot"`).
|
|
728
728
|
**Levels of their own, and how they map.** A `DataModel` (`src/charting/datamodel/`, exported from the package
|
|
729
729
|
root) keeps each level as its own schema, a `LevelSchema` (`model.level('conceptual' | 'logical' | 'physical')`):
|
|
@@ -782,8 +782,255 @@ types — see `ARCHITECTURE.md`):
|
|
|
782
782
|
`DataModelContext.fields(model, context?)` and `DataModelContext.specFrom(values, model, context?)` (meanings, dates,
|
|
783
783
|
and rules a line each, `the rule | the concepts, by name`).
|
|
784
784
|
|
|
785
|
+
**BIAN service domains.** `BianNotation` (`src/bian.js`, exported from the package root) holds BIAN's vocabulary:
|
|
786
|
+
the nineteen functional patterns, each with its generic artifact, description, creating action term and behaviour
|
|
787
|
+
qualifier type (`BianNotation.functionalPattern('Fulfill')`, a `BianFunctionalPattern`), the seventeen action terms
|
|
788
|
+
in their four groups, and the v14.0 behaviour qualifier types. Each pattern's artifact, creating term and qualifier
|
|
789
|
+
type are as BIAN's v14 model has them (its service landscape portal: the artifact a pattern accesses, the qualifier
|
|
790
|
+
type the artifact aggregates, the creating term the pattern aggregates), the names control records have as its
|
|
791
|
+
release 14 semantic APIs do (Fulfill's is a *Facility*, the guide's *Arrangement* kept in `formerArtifacts`); none
|
|
792
|
+
is `inferred`. BIAN spells *Analyze* (the guide's *Analyse* still finds it); `BianNotation.findPattern(text)` finds
|
|
793
|
+
one by its name in any case, and `BianNotation.patternOfControlRecord(name)` by the artifact a control record's name
|
|
794
|
+
ends with (`'CurrentAccountFacility'`: Fulfill). A `ServiceDomain` is a kind of perspective (`kind: 'service-domain'`; a perspective spec's
|
|
795
|
+
`kind` picks its class, `DataModelPerspective.from`, `register`): its business area and domain, the `asset` it manages,
|
|
796
|
+
its functional `pattern`, its `controlRecord` (a logical entity; `controlRecordName` is the asset and the pattern's
|
|
797
|
+
artifact, *Current Account Facility*) and `qualifiers` (`[{ name, type?, entity }]`). `serviceOperations()` gives
|
|
798
|
+
its semantic API as BIAN names it (*initiate Current Account Facility | Interest*, `BianNotation.serviceOperations`).
|
|
799
|
+
Seen through at the logical level it shows its control record and qualifiers whatever they realise, coloured and
|
|
800
|
+
saying what they are on hover, with a note of its service operations; a perspective kind adds to a view through
|
|
801
|
+
`alsoShows(level)`, `decorate(entity, level)` and `notes(level)`. Model ▸ **New service domain…** makes one
|
|
802
|
+
(`ServiceDomain.fields`, `specFrom`: the pattern from BIAN's, qualifiers a line each, `name | type | logical entity`).
|
|
803
|
+
The data-model example's second base, *Retail banking (BIAN)*, has six. See `BIAN_ALIGNMENT.md` for the design.
|
|
804
|
+
|
|
805
|
+
**Ownership and the model check.** As in BIAN, each business object (a logical entity) is owned by one service
|
|
806
|
+
domain: a perspective owns what `ownedEntities()` gives (none for a plain one; a service domain owns its control
|
|
807
|
+
record, its qualifiers' entities and those it names in `owns`, its dialog's *Also owns*), `model.ownersOf(id)`
|
|
808
|
+
says who owns one, and the logical level shows it on hover (*owned by*). `model.checker()` is a `DataModelChecker`
|
|
809
|
+
(a `ModelChecker`, the base `ViewSetChecker` extends too): a business object owned by more than one service domain
|
|
810
|
+
(`owned-twice`), owned by none once any are owned (`unowned`), a service domain without a control record or with
|
|
811
|
+
one, or a qualifier, that is no logical entity, or with a pattern BIAN lacks, and a mapping joining an entity that
|
|
812
|
+
is not there. Model ▸ **Check model…** lists the findings in the `ModelCheckDialog` the diagram editors' check uses;
|
|
813
|
+
choosing one shows it (the entity's level, the service domain seen through, the mapping view).
|
|
814
|
+
|
|
815
|
+
**Checked as DMBOK2 reviews a model.** The check also finds, at the logical and physical levels: an entity with
|
|
816
|
+
attributes and no primary key (`no-primary-key`); a foreign key whose `references` (an entity, or `entity.attribute`,
|
|
817
|
+
by id or name) is not there (`reference-missing`) or has no relationship with its entity either way
|
|
818
|
+
(`foreign-key-without-relationship`), one saying nothing on an entity with no relationship (`foreign-key-unrelated`);
|
|
819
|
+
attributes differing only by a number at the end, a repeating group (`repeating-group`); one name with different
|
|
820
|
+
types at a level, in any case (`type-conflict`); and at the physical level a many-to-many relationship, which a table
|
|
821
|
+
between resolves (`many-to-many`). A concept without a definition (`definition-missing`) and anything defined only
|
|
822
|
+
by its name (`definition-restates-name`) are reported too. A model may keep a **naming standard**
|
|
823
|
+
(`model.namingStandard`, a `NamingStandard`; none by default): per level, for its entities and attributes, a
|
|
824
|
+
`style` (a `NameStyle`: Title Case, lower case words, snake_case, UPPER_SNAKE_CASE, camelCase, PascalCase; each
|
|
825
|
+
says whether a name is written so and suggests it), `classWords` an attribute name must end with, a `maxLength`,
|
|
826
|
+
and `abbreviations` a name must use (`{ number: 'num' }`); names that break it are reported (`naming`), with how
|
|
827
|
+
they would be written. Model ▸ **Naming standard** ▸ sets it (`NamingStandard.fields`), starts from
|
|
828
|
+
`NamingStandard.CONVENTIONAL` or removes it, one undo step each (`model.namingStandardCommand(spec|null)`).
|
|
829
|
+
|
|
830
|
+
```javascript
|
|
831
|
+
new NamingStandard(NamingStandard.CONVENTIONAL).check('physical', 'attributes', 'account_number');
|
|
832
|
+
// ['has "number" where the standard writes "num"']
|
|
833
|
+
```
|
|
834
|
+
|
|
835
|
+
**Stewardship, classification, lineage.** Any entity may say who answers for it (`stewardship: { owner, steward,
|
|
836
|
+
custodian }`: business owner, data steward, technical custodian) and how it is classified (`sensitivity`, a level of
|
|
837
|
+
the model's scheme, and `categories`); so may an attribute (its row's Sensitivity and Categories). The entity dialog
|
|
838
|
+
has both, its attribute table the attributes'. A model keeps a `ClassificationScheme` (`model.classificationScheme`:
|
|
839
|
+
sensitivity `levels` in order, lowest first, each with a colour, and `categories`; the default Public, Internal,
|
|
840
|
+
Confidential, Restricted and PII, PCI, PHI, Financial, saved only when changed; Model ▸ **Classification scheme…**).
|
|
841
|
+
An entity's effective classification (`scheme.effective(entity)`) is the highest of its own and its attributes', with
|
|
842
|
+
all their categories. The check reports what realises a classified entity, down the mappings, classified lower or
|
|
843
|
+
without its categories (`classification-lost`), and a sensitivity the scheme lacks (`classification-unknown`). A
|
|
844
|
+
**classification lens** (a perspective kind, `ClassificationLens`; Model ▸ New classification lens…) colours each
|
|
845
|
+
entity by its effective sensitivity, unclassified grey. `model.lineage(level, id)` gives what an entity realises
|
|
846
|
+
(`up`, to its concepts) and what realises it (`down`, to its tables), through the mappings; on hover an entity
|
|
847
|
+
shows its stewardship, classification and lineage (*realises*, *realised by*). Right-click an entity ▸ **Show
|
|
848
|
+
lineage** opens the mapping view with its lineage as it is and the rest greyed; there, right-click another for its
|
|
849
|
+
lineage, or the canvas ▸ Show all. The banking base classifies its party data and has a *Sensitive data* lens.
|
|
850
|
+
|
|
851
|
+
**Subtypes.** A relationship's `kind` says what it is (`RelationshipKind`, registered as perspective kinds are): an
|
|
852
|
+
association (`AssociationKind`, the default: a cardinality at each end, Chen's diamond) or a generalization
|
|
853
|
+
(`GeneralizationKind`, `kind: 'generalization'`: the source a subtype of the target, `complete` and `disjoint`
|
|
854
|
+
said on it). Each kind draws its own edge (`DataModelDiagram.buildEdge`): a generalization in every notation as a
|
|
855
|
+
line to its supertype ending in a hollow triangle, labelled `{complete, disjoint}` (in Chen, `ISA {…}`, a line,
|
|
856
|
+
not a diamond), with no cardinalities. The relationship dialog has the kind and a generalization's constraints.
|
|
857
|
+
The check reports generalizations that go round (`generalization-cycle`) and takes none for many-to-many. The
|
|
858
|
+
ontology's logical Party is the supertype of Person and Organisation.
|
|
859
|
+
|
|
860
|
+
**Data quality rules.** A context's rule may say which data quality dimension it measures (`dimension`, one of
|
|
861
|
+
`DataModelContext.QUALITY_DIMENSIONS`: Completeness, Validity, Uniqueness, Timeliness, Accuracy, Consistency) and
|
|
862
|
+
which attribute of what realises its concepts it is on (`attribute`); it then reads *Completeness of legal name:
|
|
863
|
+
always given* in its note and on hover (`DataModelContext.ruleText`). A context's dialog edits its rules as a table
|
|
864
|
+
(the rule, its concepts by name, its dimension, its attribute). The check reports a rule on an attribute nothing
|
|
865
|
+
realising its concepts has (`rule-attribute-missing`; names compared as words, so *tax number* is `tax_number`)
|
|
866
|
+
and a dimension DMBOK2 does not name (`quality-dimension-unknown`).
|
|
867
|
+
|
|
868
|
+
**Code sets.** An enumeration, as reference data, may say where it comes from (`source`), who keeps it (`owner`) and
|
|
869
|
+
which version it is (`version`): in its dialog, saved, and on hover in the data types view (`codeSet`).
|
|
870
|
+
|
|
871
|
+
**SQL DDL.** Export ▸ **SQL DDL (physical level)** writes the physical level as SQL (`SqlDdl`, `SqlDdlFileFormat`, an
|
|
872
|
+
export-only `ModelFileFormat`): a `CREATE TABLE` per table, its columns' types (`defaultType` where one has none)
|
|
873
|
+
and `NOT NULL` for required and key columns, its `PRIMARY KEY` and `UNIQUE` columns; a `FOREIGN KEY` per column
|
|
874
|
+
that references another table (`references`: a table, by id or name, meaning its primary key, or `table.column`),
|
|
875
|
+
a comment where one cannot be made; `COMMENT ON`s for definitions (`comments: false` leaves them out). Names SQL
|
|
876
|
+
would not take bare are quoted. ANSI SQL as PostgreSQL reads it.
|
|
877
|
+
|
|
878
|
+
**A dimensional scheme.** At the logical and physical levels an entity may have a role (`EntityRole`, registered as
|
|
879
|
+
relationship kinds are): a **fact** (`FactRole`, `role: 'fact'`: what is measured, at a stated `grain`, its
|
|
880
|
+
attributes' `measure` additive, semi-additive or non-additive) or a **dimension** (`DimensionRole`: its `scdType`,
|
|
881
|
+
0 to 3, how it keeps history, `conformed` when facts share it). The entity dialog has the role and what that role
|
|
882
|
+
has; the attribute table a Measure column. Level ▸ **Star schemas** (offered once there is a fact; `DataModel.STAR`,
|
|
883
|
+
`model.starView()`) draws, at the physical level if it has a fact (else the logical), each fact in a row of stars
|
|
884
|
+
with the dimensions it is related to around it, a shared dimension once, by the first, in their roles' colours. The
|
|
885
|
+
check reports a fact without its grain (`fact-no-grain`), related to no dimension (`fact-no-dimension`) or with no
|
|
886
|
+
measure (`fact-no-measure`), a measure on what is no fact (`measure-outside-fact`), and two facts joined directly
|
|
887
|
+
(`fact-to-fact`). The banking base has a daily balance star (`fact_daily_balance`, `dim_account` of type 2,
|
|
888
|
+
a conformed `dim_date`).
|
|
889
|
+
|
|
890
|
+
**The Data Model Scorecard.** Model ▸ **Scorecard…** scores the model on the ten categories of Steve Hoberman's Data
|
|
891
|
+
Model Scorecard (`ModelScorecard`, `model.scored()`), the total in its title. Six are measured from the model, a
|
|
892
|
+
`MeasuredCategory` each, with notes on why: **Completeness** (the share, averaged, of entities defined, concepts
|
|
893
|
+
realised at the logical level, logical entities realised by a table, logical entities with an owner or steward),
|
|
894
|
+
**Structure** (logical entities and tables the check finds nothing structural on), **Standards** (names keeping the
|
|
895
|
+
naming standard; none without one), **Readability** (boxes clear of each other, associations named),
|
|
896
|
+
**Definitions** (definitions more than the name and at least four words), **Consistency** (logical entities and
|
|
897
|
+
tables the check finds nothing inconsistent on). **Correctness**, **Scheme**, **Abstraction** and **Data** are for a
|
|
898
|
+
reviewer (`ScorecardCategory`). A reviewer's score and note, kept in the model (`model.scorecard.reviews`), override
|
|
899
|
+
a measured score too. The total is the weighted average of the categories with a score, with how much of the weight
|
|
900
|
+
that covers; weights are the model's own (`model.scorecard.weights`), equal (10 each) by default: the book gives its
|
|
901
|
+
own weighting, and an organisation sets the one it reviews by. Saving is one undo step (`scorecardCommand`).
|
|
902
|
+
|
|
903
|
+
```javascript
|
|
904
|
+
model.scored().total(); // { score: 91, coverage: 50 }
|
|
905
|
+
```
|
|
906
|
+
|
|
907
|
+
```javascript
|
|
908
|
+
new SqlDdl().write(model); // 'CREATE TABLE party (
|
|
909
|
+
party_id BIGINT NOT NULL, …'
|
|
910
|
+
```
|
|
911
|
+
The ontology base keeps BIAN's **Agreement** (the contract) apart from its **Arrangement** (the terms a product is
|
|
912
|
+
provided on), and has **Product Feature** and **Condition**; its concepts say which BIAN business object they are
|
|
913
|
+
(`details.bian`, on hover).
|
|
914
|
+
|
|
915
|
+
**The service landscape.** Level ▸ **Service landscape** (offered once a model has a service domain;
|
|
916
|
+
`DataModel.LANDSCAPE`) draws the service domains as BIAN draws its landscape: a column per business area, a frame
|
|
917
|
+
per business domain in it, a box per service domain coloured by its functional pattern (grey for one BIAN lacks),
|
|
918
|
+
its pattern, control record and asset on hover. Those with no area or domain go under *No business area* /
|
|
919
|
+
*No business domain*. It is laid out from that structure each time (`ServiceLandscape.view(serviceDomains)`), so
|
|
920
|
+
a box dragged there is not kept; double-click one to see the model through it at the logical level. Mapping and
|
|
921
|
+
the landscape are `DataModel.OTHER_VIEWS`, the views that are no level, which the Level choice and `view()` read.
|
|
922
|
+
|
|
923
|
+
**Reading BIAN's landscape.** Model ▸ **Read service domains from a BIAN landscape…** reads an ArchiMate model
|
|
924
|
+
(an Archi file or the Exchange Format; `BianLandscapeReader.parse`). In BIAN's v14 model a business area is a
|
|
925
|
+
Grouping marked *BusinessArea*, composed of business domains (Capabilities marked *BusinessDomain*), composed of
|
|
926
|
+
service domains (Capabilities marked *ServiceDomain*), each realized by its functional pattern; other Groupings
|
|
927
|
+
aggregate service domains as overlays (a value chain, a release wave). Where the file keeps the stereotypes (Archi
|
|
928
|
+
specializations, or a *Stereotype* property) they say which is which: a service domain's business domain is the
|
|
929
|
+
container marked so and its area the one holding that; a Capability in a business domain is a service domain though
|
|
930
|
+
unmarked (BIAN leaves a few so); of copies of one, the placed one is it. Without stereotypes it goes by structure: a
|
|
931
|
+
Capability nothing sits in is a service domain, its nearest container its business domain, the outermost its area.
|
|
932
|
+
What holds what: composition before aggregation, then a box drawn inside another in a view. A service domain's
|
|
933
|
+
pattern is the one realizing it (an element named as a BIAN pattern), else its *Functional Pattern* property; its
|
|
934
|
+
asset its *Asset Type* property. The types, relationships, stereotypes and property keys are options of
|
|
935
|
+
`new BianLandscapeReader({...})`. Run over BIAN's v14 model it places all 344 service domains the model places in
|
|
936
|
+
their area and domain, and gives 349 of 350 their pattern; by structure alone it would take 820 Capabilities for
|
|
937
|
+
service domains.
|
|
938
|
+
`model.serviceDomainsCommand(specs, source)` brings them in as one undo step: a new one is added; one already
|
|
939
|
+
there (by id, else by name) keeps its control record, qualifiers and the rest, taking its area and domain from
|
|
940
|
+
the landscape, and its asset and pattern where it has none. `examples/bian-landscape-sample.xml` is a small
|
|
941
|
+
excerpt in that shape to try it with (not BIAN's landscape, which is BIAN's to publish).
|
|
942
|
+
|
|
943
|
+
```javascript
|
|
944
|
+
const specs = new BianLandscapeReader().read(BianLandscapeReader.parse(xml));
|
|
945
|
+
model.history.execute(model.serviceDomainsCommand(specs, '"bian.xml"'));
|
|
946
|
+
model.view({ level: DataModel.LANDSCAPE }); // { entities, folders, elementFolders, ... }
|
|
947
|
+
```
|
|
948
|
+
|
|
949
|
+
**Editing.** At a level of the schema, double-click an entity (or a relationship's line or Chen diamond) to edit it;
|
|
950
|
+
right-click one for Edit… and Delete, or the canvas for **New entity here…** and **New relationship…**; Model ▸ New
|
|
951
|
+
concept/entity/table… and New relationship… do the same. Each level says what its entities hold and gives its
|
|
952
|
+
dialog's fields (`DataModelNotation.level(name)`: a `ConceptualLevel` a name and a definition; a `LogicalLevel` and
|
|
953
|
+
a `PhysicalLevel`, both `AttributedLevel`s, the attributes or columns too, as a `rows` table: name, type (the model's
|
|
954
|
+
data types and plain types suggested at the logical level, SQL types at the physical), PK, FK, unique, required,
|
|
955
|
+
what a foreign key references, a definition), and reads it back over the entity (`entitySpecFrom`), so where it is
|
|
956
|
+
drawn and what the dialog does not show stay; `relationshipFields` and `relationshipSpecFrom` do the same for a
|
|
957
|
+
relationship (ends, cardinalities, name, identifying, definition). Every edit is one undo step, a
|
|
958
|
+
`DataModelEditCommand` (the model as it was before and after, `DataModel#restore` putting either back):
|
|
959
|
+
`saveEntityCommand(level, id|null, spec)`, `removeEntityCommand(level, id)` (its relationships and mapping pairs go,
|
|
960
|
+
and each perspective, service domain and context forgets it: `forgetEntity`), `saveRelationshipCommand(level,
|
|
961
|
+
index|null, spec)`, `removeRelationshipCommand(level, index)`; `relationshipIndex(level, drawn)` says which
|
|
962
|
+
relationship a line or a diamond is. These stand in for graphDiagram's own Rename and Delete, which change only
|
|
963
|
+
the drawing.
|
|
964
|
+
|
|
965
|
+
```javascript
|
|
966
|
+
model.history.execute(model.saveEntityCommand('logical', null, { name: 'Phone', attributes: [{ name: 'number', required: true }] }));
|
|
967
|
+
model.history.undo(); // gone again
|
|
968
|
+
```
|
|
969
|
+
|
|
970
|
+
**Data types.** A model's `dataTypes` are the types its attributes have, each defined once: a `DataType` (a plain
|
|
971
|
+
named type, `basedOn` another), an `EnumerationType` (`values: [{ name, description? }]`) or a `StructuredType`
|
|
972
|
+
(`fields: [{ name, type?, description?, iso20022? }]`), its `kind` in a spec picking the class (`DataType.from`,
|
|
973
|
+
`register`), as BIAN publishes its data types and DAMA-DMBOK2 calls domains. An attribute names one by its `type`
|
|
974
|
+
(the type's id, else its name in any case: `model.typeOf(attribute)`); a plain type (`VARCHAR(20)`) names none.
|
|
975
|
+
On hover an entity lists its attributes' data types; `model.usesOf(id)` says where one is used. Level ▸ **Data types**
|
|
976
|
+
draws each as a box with a row per value or field, joined to the types a structure's fields are. Model ▸ **Data
|
|
977
|
+
types** ▸ makes one of each kind, edits or deletes each (a dialog per kind: values a line each, `value | meaning`;
|
|
978
|
+
fields `name | type`). They are saved, compared and merged as the rest of the model is (`DataTypesCompared`), and
|
|
979
|
+
the model check reports an enumeration with no values or a value twice, two types of one name, and a type nothing uses.
|
|
980
|
+
|
|
981
|
+
**ISO 20022.** An entity, an attribute or a data type may say which ISO 20022 type it is (`iso20022: 'CurrencyCode'`;
|
|
982
|
+
`DataType.iso20022Url(name)` its page in the ISO 20022 standards repository). On hover an entity lists its own and
|
|
983
|
+
its attributes' (an attribute's own, else its data type's).
|
|
984
|
+
|
|
985
|
+
**Reading a BIAN semantic API.** BIAN publishes a semantic API per service domain (OpenAPI files, YAML; the plain ones
|
|
986
|
+
and those extended with ISO 20022, at github.com/bian-official/public, Apache-2.0). Model ▸ **Read a BIAN service
|
|
987
|
+
domain (semantic API)…** reads one (`.yaml`, `.yml` or `.json`; `BianSemanticApiReader`): its control record and
|
|
988
|
+
behaviour qualifiers (the operations' `CR - …` and `BQ - …` tags) become logical entities with the attributes their
|
|
989
|
+
schemas have, the qualifiers joined to the control record; the schemas those attributes are become data types (an
|
|
990
|
+
`enum` an enumeration, each value's meaning from BIAN's description; an object a structure; else a plain type), as
|
|
991
|
+
deep as `depth` (1); a class the model already has as a logical entity stays a reference to it; the ISO 20022 version
|
|
992
|
+
gives each attribute its ISO 20022 type. The service domain is named by the API's title, its pattern and asset by its
|
|
993
|
+
control record's name. `model.bringCommand({ entities, relationships, dataTypes, serviceDomains }, label)` brings
|
|
994
|
+
it in as one undo step: what is new is added; an entity or data type there by name stays and is used; only the data
|
|
995
|
+
types the new entities need come (and the types those need); a service domain there keeps what it has (as for a
|
|
996
|
+
landscape) and gains the behaviour qualifiers it lacks; one that would come out the same is left alone. Every one
|
|
997
|
+
of release 14's 258 APIs reads, in both versions.
|
|
998
|
+
|
|
999
|
+
```javascript
|
|
1000
|
+
const read = new BianSemanticApiReader().read(BianSemanticApiReader.parse(yamlText));
|
|
1001
|
+
model.history.execute(model.bringCommand(read, 'Read Current Account'));
|
|
1002
|
+
model.typeOf(model.level('logical').entity('current-account-facility').attributes[1]); // EnumerationType
|
|
1003
|
+
```
|
|
1004
|
+
|
|
1005
|
+
`YamlReader.parse(text)` (`src/yaml/YamlReader.js`) is the dependency-free reader it uses: block mappings and
|
|
1006
|
+
sequences, quoted and plain scalars (typed as YAML 1.2's core schema has them), block scalars with chomping, flow
|
|
1007
|
+
collections, comments; it refuses anchors, aliases and tags with the line they are on.
|
|
1008
|
+
|
|
1009
|
+
```js
|
|
1010
|
+
const sd = model.addPerspective({ kind: 'service-domain', name: 'Current Account', asset: 'Current Account', pattern: 'Fulfill',
|
|
1011
|
+
controlRecord: 'current-account', qualifiers: [{ name: 'Interest', entity: 'interest' }] });
|
|
1012
|
+
sd.controlRecordName; // 'Current Account Facility'
|
|
1013
|
+
sd.serviceOperations()[3].name; // 'initiate Current Account Facility'
|
|
1014
|
+
```
|
|
1015
|
+
|
|
1016
|
+
**Compared and merged.** `model.compareWith(other)` is a `ModelComparison`, as for ArchiMate models, kind by kind
|
|
1017
|
+
(`model.comparedKinds()`): each level's entities (`LevelEntitiesCompared`; where a box is drawn does not count) and
|
|
1018
|
+
relationships (`LevelRelationshipsCompared`; one with no id known by its ends and name, and needing them), the
|
|
1019
|
+
mappings (`MappingsCompared`, a pair each, needing its two entities), contexts and perspectives (a perspective
|
|
1020
|
+
needing the context it is read in). `comparison.mergeCommand(keys)` brings the chosen differences and what they
|
|
1021
|
+
need, as one step on `model.history`.
|
|
1022
|
+
|
|
1023
|
+
**Saved, kept and backed up.** A data model is a file of its own, `.datamodel.json` (`DataModelFileFormat`: its
|
|
1024
|
+
`name`, levels, mappings, layouts, perspectives and contexts, `model.toJSON()`; the older one-entity form reads
|
|
1025
|
+
too). It knows when it changed since it was saved (`model.edited()`, `markSaved()`, `isDirty()`, `onEdit(fn)`):
|
|
1026
|
+
a perspective or context made, changed or removed, a box or note moved.
|
|
1027
|
+
|
|
785
1028
|
**In the workbench:** `DataModelPart` (type `data-model`, `RenderEngineWorkspace.DataModelPart`) shows one
|
|
786
|
-
model:
|
|
1029
|
+
model: File ▸ Open…, Save, Save As… (Ctrl+O, Ctrl+S; `part.file`, the `ModelFile` editors use, • while changed,
|
|
1030
|
+
asking before the page is left with changes unsaved), and hosted by a `ModelsPart` (`editorType: 'data-model'`)
|
|
1031
|
+
New ▸ (New model, From base: …), Duplicate model, Close model and This browser ▸ (with `keep`: kept in this browser
|
|
1032
|
+
as it changes, reopened next time, backed up to a folder chosen once, as the modeller's models are); Edit ▸ Undo
|
|
1033
|
+
and Redo; Tools ▸ Compare with and Merge from (the other open models and the bases; a merge is one undo step); Model ▸ New / Edit / Delete perspective and context (those dialogs), Perspective and Context selects (when
|
|
787
1034
|
it has some; a perspective brings its context, and the context's scope shows beside them), Level (Conceptual,
|
|
788
1035
|
Logical, Physical, and Mapping), Notation, and Export ▸ PNG / SVG. A box or note dragged stays on the level it
|
|
789
1036
|
was dragged on (the mapping view's in its own layout). `examples/data-model.html` (`npm run datamodel`) uses every
|
|
@@ -1359,6 +1606,34 @@ mine.history.execute(comparison.mergeCommand(['view:id-extra'])); // the view,
|
|
|
1359
1606
|
mine.history.undo();
|
|
1360
1607
|
```
|
|
1361
1608
|
|
|
1609
|
+
**A model tab's menu.** Right-click a model's tab for what can be done with that model (`models.tabMenuItems(part)`):
|
|
1610
|
+
Save and Save As… (an editor that saves files), Duplicate model, Compare with ▸ and Merge from ▸, This browser ▸
|
|
1611
|
+
(with `keep`), Close model and Close other models. The work area hands a tab's right-click to its `tabMenu` option
|
|
1612
|
+
(`(partId, event) => shown`), through `TabsNode`'s `ctx.onTabMenu`; with none, the browser's own menu shows.
|
|
1613
|
+
|
|
1614
|
+
**Bases, and copies.** A `ModelsPart` spec's `bases` are reference models kept as given: each made from its own copy
|
|
1615
|
+
of its spec when the workbench is first drawn (`models.bases()`, parts never opened, edited or kept, so an open model
|
|
1616
|
+
sharing the spec's data cannot reach them). File ▸ New ▸ **From base: …** (`models.newFromBase(i)`,
|
|
1617
|
+
`models.baseItems()`) opens a model as a copy of one; Tools ▸ **Compare with** and **Merge from** offer them, after
|
|
1618
|
+
the other open models (`models.toolItems(part)`); File ▸ **Duplicate model** (`models.duplicate(part)`) opens a copy
|
|
1619
|
+
of the model in front, written and read back through its own file format. A copy, from a base or duplicated, is named
|
|
1620
|
+
"… (copy)" (`ModelsPart.copyName`, the suffix once).
|
|
1621
|
+
|
|
1622
|
+
**Kept in this browser, and backed up.** With `keep: true` in its spec, a `ModelsPart` keeps every model it holds
|
|
1623
|
+
in this browser, so work outlives the page without saving a file: a moment after each edit (`ModelsPart.KEEP_DELAY`)
|
|
1624
|
+
and at once when imported, each in its editor's own file format (`part.keepEntry()`: the file's format, else the
|
|
1625
|
+
part's save format), in a `ModelLibrary` over an IndexedDB store (`KeyValueStore`: `IndexedDbStore`, or a
|
|
1626
|
+
`MemoryStore` where the browser has none). Those open when the page closed open again next time
|
|
1627
|
+
(`models.ready` resolves once they have), in place of the starting model if it was left untouched; a tab closed
|
|
1628
|
+
is kept but not reopened. File ▸ **This browser** has **Open a kept model…**, **Stop keeping this model**, **Back
|
|
1629
|
+
up kept models now** and **Backup folder…**. Backups (`BackupFolder`, after au-accounts-app's) go to a folder chosen
|
|
1630
|
+
once and remembered (the File System Access API, in Chrome and Edge, from `file://` too): choose one that OneDrive,
|
|
1631
|
+
Google Drive or Dropbox syncs and they reach the cloud, with no sign-in in the page. Each is written stamped with
|
|
1632
|
+
the date and time (`BackupFolder.stamped('shop.archimate')` gives `shop-20261015-093012.archimate`), so they gather
|
|
1633
|
+
as a history, and under its plain name too (`keepUnstamped`); where a folder cannot be written (Firefox, Safari,
|
|
1634
|
+
permission refused) they download instead. `backups.log()` records each (newest first, `LOG_MAX`), and
|
|
1635
|
+
`backups.daysSinceLast()` how long ago the last was. `examples/archimate.html` keeps its models.
|
|
1636
|
+
|
|
1362
1637
|
```js
|
|
1363
1638
|
ws.addParts([{ id: 'models', type: 'models', editorType: 'archimate-canvas', newModel: { analysis: true },
|
|
1364
1639
|
models: [{ id: 'modeller', title: 'Sample model', elements: [...], views: [...] }] }]);
|
|
@@ -1945,6 +2220,11 @@ parts.children.length; // 3: front, top and side faces
|
|
|
1945
2220
|
context, and the template draws its parts as SVG (`glyphToSvg`; `glyphElement(part)` gives the tag and
|
|
1946
2221
|
attributes, by the SVG renderer's rules) and attaches lines to the glyph's `outline(ctx)`. Every glyph
|
|
1947
2222
|
has an outline: its box by default, the real shape for round, skewed and pointed ones.
|
|
2223
|
+
`graphDiagram` (and the data-model diagram drawn by it) does the same: a line ends where it meets the node's
|
|
2224
|
+
glyph outline (`NodeGeometry.outline(shape, size)`, crossed by `EdgeGeometry.rayPolygonT`), so it reaches a wide
|
|
2225
|
+
ellipse or product box rather than stopping short on a circle as wide as the node; a shape no glyph draws keeps
|
|
2226
|
+
that circle. Centred initials stand in for a name drawn below the shape: a node whose name is inside it gets none
|
|
2227
|
+
(`GraphDiagram.showsInitials(node)`).
|
|
1948
2228
|
|
|
1949
2229
|
**ArchiMate in the template editor.** `ArchimateElementTemplate(instance, model, {nodeOptions})` and
|
|
1950
2230
|
`ArchimateEdge(instance, view)` put an `ArchimateView` into a `DiagramEditor`. Neither keeps a copy:
|
|
@@ -2196,6 +2476,22 @@ its `fileFormats()`, none by default, then the image export), and its editor dis
|
|
|
2196
2476
|
import goes through `replaceModel(model)`, which a subclass follows in `modelReplaced(model)`.
|
|
2197
2477
|
`DiagramEditorPart`, `ArchimateEditorPart` and `ArchimateCanvasPart` extend it.
|
|
2198
2478
|
|
|
2479
|
+
**Tabs, as a browser has them.** A part's tabs are the views open (`part.openViews`, an `OpenViewList` over its
|
|
2480
|
+
model), not every view: a model opened or imported starts with its first view only, so an Archi file of many
|
|
2481
|
+
views does not fill the strip. Going to a view (choosing it in the model tree, a search match, a new view)
|
|
2482
|
+
opens its tab; a tab's **×** closes the tab and leaves the view in the model (one tab always stays, and the
|
|
2483
|
+
tab in front closed brings its neighbour); **+** after the tabs makes something new (`newItems()`: a view, or
|
|
2484
|
+
a menu of a view, a canvas, from a template). `part.viewTabs({ orientation })` builds the strip
|
|
2485
|
+
(`ViewTabStrip` with `canClose` and `onAdd`). Drawing every view for the HTML report opens none
|
|
2486
|
+
(`openViews.while(fn)`). The spec's `openViews` (view ids) chooses which open at the start.
|
|
2487
|
+
|
|
2488
|
+
**Panels that fold and resize.** The side panels (the model tree, the palette and analysis) have a splitter
|
|
2489
|
+
on their inner edge: drag it to resize the panel, drag it nearly shut or double-click it to fold the panel to
|
|
2490
|
+
a stub that names it, and click the stub to open it at its width again. `part.layout` (a `PanelLayout`:
|
|
2491
|
+
`resize(name, width)`, `toggle(name)`, `widthOf(name)`) keeps the widths and folds across renders; the spec's
|
|
2492
|
+
`layout` (`{ side: { width, collapsed }, aside: {...} }`) sets them at the start. `EditorShell` draws them
|
|
2493
|
+
when given a `layout` and stub `labels`.
|
|
2494
|
+
|
|
2199
2495
|
`DiagramEditorPart` (workspace bundle) puts this together: `MenuBar` (File creates and deletes
|
|
2200
2496
|
diagrams; Settings has the grid and line-style levels), the palette, a `ViewTabStrip` (right-click
|
|
2201
2497
|
a tab for its **Properties…**: name, documentation and the diagram's line style; **Duplicate**;
|
|
@@ -2252,6 +2548,11 @@ offers `text` as its first choice; any other field, a checkbox too, gets a check
|
|
|
2252
2548
|
fields: every field on "Keep" showing the value they share, read-only fields left out.
|
|
2253
2549
|
`onApply(values)` receives every field; `onClose(applied)` is told once when the dialog closes, whether it
|
|
2254
2550
|
was applied (false for Cancel, Escape or a click outside), so a caller waiting on a choice can settle.
|
|
2551
|
+
A `rows` field (`RowsField`) edits a list as a table: `columns` `[{ key, label, type?: 'text'|'checkbox'|'select',
|
|
2552
|
+
choices?, suggestions?, placeholder?, width? }]`, `value` the items, `addLabel`; a row each, moved up and down or removed,
|
|
2553
|
+
**+ Add** for a new one. It reads as the items in their order, each the item it was made from with the columns laid
|
|
2554
|
+
over it (what no column shows is kept; an empty text or an unticked box leaves its key out), a new row left empty
|
|
2555
|
+
dropped. It takes the dialog's whole width, its label above it.
|
|
2255
2556
|
`EdgeStyleDialog`, the grid-size and diagram-name prompts are all `FormDialog`s. A dialog fits the window
|
|
2256
2557
|
(at most 90% of its height): its fields scroll between the title and the buttons, which stay in view, and
|
|
2257
2558
|
dragging the title moves it. A `textarea` scrolls inside its own box (at most 40% of the window) and can be
|