@peter.prib/renderengine 0.2.5 → 0.2.7
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 +219 -213
- package/REFERENCE.md +272 -6
- package/dist/renderengine-workspace.umd.js +11980 -7421
- package/dist/renderengine.umd.js +8991 -5784
- package/examples/archimate.html +68 -66
- package/examples/data-model.html +534 -0
- package/package.json +70 -68
- package/src/bian.js +182 -0
- package/src/charting/CheckboxTreeSelector.js +373 -304
- package/src/charting/DiagramEditor.js +53 -32
- package/src/charting/DiagramTypeRegistry.js +13 -3
- package/src/charting/ModelCheckDialog.js +63 -0
- package/src/charting/ModelChecker.js +26 -0
- package/src/charting/ViewTabStrip.js +94 -81
- package/src/charting/archimate/ArchimateCanvasEditor.js +825 -815
- package/src/charting/archimate/ArchimateElementTemplate.js +208 -195
- package/src/charting/datamodel/BianLandscapeReader.js +185 -0
- package/src/charting/datamodel/BianSemanticApiReader.js +183 -0
- package/src/charting/datamodel/DataModel.js +645 -0
- package/src/charting/datamodel/DataModelChecker.js +92 -0
- package/src/charting/datamodel/DataModelCompared.js +127 -0
- package/src/charting/datamodel/DataModelContext.js +108 -0
- package/src/charting/datamodel/DataModelFileFormat.js +33 -0
- package/src/charting/datamodel/DataModelPerspective.js +129 -0
- package/src/charting/datamodel/DataType.js +108 -0
- package/src/charting/datamodel/EnumerationType.js +51 -0
- package/src/charting/datamodel/LevelMapping.js +38 -0
- package/src/charting/datamodel/LevelSchema.js +41 -0
- package/src/charting/datamodel/ServiceDomain.js +177 -0
- package/src/charting/datamodel/ServiceLandscape.js +104 -0
- package/src/charting/datamodel/StructuredType.js +63 -0
- package/src/charting/datamodel/index.js +15 -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 +160 -86
- package/src/charting/diagrams/DataModelRelationshipStrategy.js +94 -38
- package/src/charting/diagrams/EdgeGeometry.js +578 -553
- package/src/charting/diagrams/GraphDiagram.js +11 -1
- package/src/charting/diagrams/NodeGeometry.js +169 -158
- package/src/charting/diagrams/nesting.js +31 -0
- package/src/charting/glyphs/PersonGlyph.js +24 -23
- package/src/charting/modelTree/ModelTree.js +202 -164
- package/src/charting/modelTree/ModelTreePanel.js +36 -3
- package/src/charting/modelTree/TreeViewMode.js +131 -110
- package/src/charting/modelTree/modes/FlatTreeMode.js +32 -46
- package/src/charting/modelTree/modes/LayerGroupedTreeMode.js +47 -62
- package/src/charting/modelTree/modes/TypeGroupedTreeMode.js +46 -60
- package/src/datamodel.js +132 -122
- package/src/diagramCatalog.js +35 -30
- package/src/index.js +2666 -2659
- package/src/workspace/EditorShell.js +166 -67
- package/src/workspace/PanelLayout.js +56 -0
- package/src/workspace/index.js +1 -0
- package/src/workspace/layout/TabsNode.js +205 -191
- package/src/workspace/parts/ArchimateCanvasPart.js +10 -9
- package/src/workspace/parts/ChartPart.js +6 -1
- package/src/workspace/parts/DataModelPart.js +544 -0
- 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/REFERENCE.md
CHANGED
|
@@ -718,6 +718,207 @@ types — see `ARCHITECTURE.md`):
|
|
|
718
718
|
renders a dashed line (a non-identifying relationship, in real ERD terms) regardless of notation;
|
|
719
719
|
unset/`true` stays solid. None of the three notations are pixel-perfect renditions of the real, full
|
|
720
720
|
notations (same calibration as `archimateDiagram`'s own icons) — see `src/datamodel.js`'s own doc comment.
|
|
721
|
+
An entity drawn as a box with its name inside (the conceptual level, and Chen) is `DataModelDiagram.ENTITY_BOX`
|
|
722
|
+
(140 × 56) unless it gives `width`/`height`; one with its own `shape` (any glyph: `'person'`, `'cube'`,
|
|
723
|
+
`'cylinder'`, ...) and `labelPosition: 'below'` keeps the shape's size. In Chen a relationship's diamond holds
|
|
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
|
+
note, another diamond) the nearest clear spot across the line (`RelationshipAsNodeStrategy.spotFor`,
|
|
726
|
+
`DataModelDiagram.boxOf`); its lines have no arrowheads; when no spot near the line is clear, the least covered one.
|
|
727
|
+
Each level and notation has a `label` for menus (`'Conceptual'`, `"Crow's Foot"`).
|
|
728
|
+
**Levels of their own, and how they map.** A `DataModel` (`src/charting/datamodel/`, exported from the package
|
|
729
|
+
root) keeps each level as its own schema, a `LevelSchema` (`model.level('conceptual' | 'logical' | 'physical')`):
|
|
730
|
+
its entities (`{id, name, description?, attributes?, x?, y?, color?, shape?, labelPosition?}`; `attributes`
|
|
731
|
+
`[{name, type?, isPK?, isFK?}]`, none for a concept, a physical entity's name its table's) and its relationships.
|
|
732
|
+
The levels are not one to one: a `LevelMapping` from each level to the next (`model.mapping('conceptual',
|
|
733
|
+
'logical')`) is pairs of ids, any number either way (a concept realised by several logical entities, several by
|
|
734
|
+
one table, or many many). `model.realising(level, conceptIds)` gives what realises some concepts at a level;
|
|
735
|
+
`model.conceptsOf(level, id)` what an entity or table realises. The older form, one entity carrying each level's
|
|
736
|
+
detail (`{ entities, relationships }`, as `dataModelDiagram` takes them), is read as three levels mapped one to one
|
|
737
|
+
(`DataModel.levelsFromCombined`).
|
|
738
|
+
|
|
739
|
+
**Schema, perspective, context.** On that structure, a `DataModelPerspective` is a stakeholder's viewpoint on the
|
|
740
|
+
concepts: the concepts it shows (`entities`, all when unset) under its names (`names`, and `relationshipNames` by
|
|
741
|
+
relationship id), and the context it is read in (`context`); a `DataModelContext` is the meaning, scope and
|
|
742
|
+
constraints: what concepts mean there (`meanings`), the rules that hold (`constraints: [{id?, text, applies:
|
|
743
|
+
[concept ids], x?, y?}]`) and where and when (`jurisdiction`, `validFrom`, `validTo`; `context.scope` reads
|
|
744
|
+
'AU · from 2024-07-01'). `model.view({ perspective, context, level })` (ids, either may be null) gives
|
|
745
|
+
`{entities, relationships, annotations, links}` for `dataModelDiagram`, the schema itself unchanged: at a level,
|
|
746
|
+
what realises the concepts shown (the concepts themselves under the perspective's names), each with its level's
|
|
747
|
+
detail, its `description` the context's meaning of its concepts, and `details` (shown on hover) naming the schema
|
|
748
|
+
entity behind a renamed concept, the concepts an entity or table realises, the context and the rules on them; the
|
|
749
|
+
level's relationships between shown entities; and each rule as a note on what realises the concepts it applies
|
|
750
|
+
to, placed per level (`model.notePlace(level, rule)`: the rule's own `x`, `y` on the concepts, `model.noteLayout`
|
|
751
|
+
on the others, else in a row below every box). With `level: 'mapping'` (`DataModel.MAPPING`, `model.mappingView()`)
|
|
752
|
+
it gives the levels side by side, a column each (ids `'level:id'`, each box drawn at its own `level`, where
|
|
753
|
+
`model.mappingLayout` left it), and a `link` per mapping between shown ones. `dataModelDiagram` draws an entity at
|
|
754
|
+
its own `level` when it has one; its `annotations` option (`[{id, text, attachTo: [entity ids], x?, y?}]`) draws
|
|
755
|
+
notes, and its `links` option (`[{source, target, label?}]`) plain lines, both dotted in every notation
|
|
756
|
+
(`DataModelDiagram.buildLink`); an entity's `details` show on hover, boxes and tables alike.
|
|
757
|
+
|
|
758
|
+
```js
|
|
759
|
+
const model = new DataModel({
|
|
760
|
+
levels: {
|
|
761
|
+
conceptual: { entities: [{ id: 'party', name: 'Party' }], relationships: [] },
|
|
762
|
+
logical: { entities: [{ id: 'person', name: 'Person', attributes: [{ name: 'id', isPK: true }] },
|
|
763
|
+
{ id: 'org', name: 'Organisation', attributes: [{ name: 'id', isPK: true }] }], relationships: [] },
|
|
764
|
+
physical: { entities: [{ id: 'party_t', name: 'party', attributes: [{ name: 'party_id', type: 'BIGINT', isPK: true }] }], relationships: [] },
|
|
765
|
+
},
|
|
766
|
+
mappings: [
|
|
767
|
+
{ from: 'conceptual', to: 'logical', pairs: [['party', 'person'], ['party', 'org']] }, // one to many
|
|
768
|
+
{ from: 'logical', to: 'physical', pairs: [['person', 'party_t'], ['org', 'party_t']] }, // many to one
|
|
769
|
+
],
|
|
770
|
+
perspectives: [{ id: 'customer', context: 'retail', names: { party: 'Customer' } }],
|
|
771
|
+
contexts: [{ id: 'retail', jurisdiction: 'AU', meanings: { party: 'Someone who buys from us.' },
|
|
772
|
+
constraints: [{ text: 'Prices include GST', applies: ['party'] }] }],
|
|
773
|
+
});
|
|
774
|
+
const { entities, relationships, annotations, links } = model.view({ perspective: 'customer', context: 'retail', level: 'mapping' });
|
|
775
|
+
engine.dataModelDiagram(entities, relationships, { annotations, links, level: 'conceptual' });
|
|
776
|
+
```
|
|
777
|
+
|
|
778
|
+
**Perspectives and contexts of one's own.** `model.addPerspective(spec)` / `updatePerspective(id, spec)` /
|
|
779
|
+
`removePerspective(id)`, and the same for contexts (an id is made from the name when none is given; a context
|
|
780
|
+
removed leaves its perspectives in none). Each class defines its own dialog: `DataModelPerspective.fields(model,
|
|
781
|
+
perspective?)` and `DataModelPerspective.specFrom(values, model)` (what it shows, what it calls each concept);
|
|
782
|
+
`DataModelContext.fields(model, context?)` and `DataModelContext.specFrom(values, model, context?)` (meanings, dates,
|
|
783
|
+
and rules a line each, `the rule | the concepts, by name`).
|
|
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
|
+
The ontology base keeps BIAN's **Agreement** (the contract) apart from its **Arrangement** (the terms a product is
|
|
815
|
+
provided on), and has **Product Feature** and **Condition**; its concepts say which BIAN business object they are
|
|
816
|
+
(`details.bian`, on hover).
|
|
817
|
+
|
|
818
|
+
**The service landscape.** Level ▸ **Service landscape** (offered once a model has a service domain;
|
|
819
|
+
`DataModel.LANDSCAPE`) draws the service domains as BIAN draws its landscape: a column per business area, a frame
|
|
820
|
+
per business domain in it, a box per service domain coloured by its functional pattern (grey for one BIAN lacks),
|
|
821
|
+
its pattern, control record and asset on hover. Those with no area or domain go under *No business area* /
|
|
822
|
+
*No business domain*. It is laid out from that structure each time (`ServiceLandscape.view(serviceDomains)`), so
|
|
823
|
+
a box dragged there is not kept; double-click one to see the model through it at the logical level. Mapping and
|
|
824
|
+
the landscape are `DataModel.OTHER_VIEWS`, the views that are no level, which the Level choice and `view()` read.
|
|
825
|
+
|
|
826
|
+
**Reading BIAN's landscape.** Model ▸ **Read service domains from a BIAN landscape…** reads an ArchiMate model
|
|
827
|
+
(an Archi file or the Exchange Format; `BianLandscapeReader.parse`). In BIAN's v14 model a business area is a
|
|
828
|
+
Grouping marked *BusinessArea*, composed of business domains (Capabilities marked *BusinessDomain*), composed of
|
|
829
|
+
service domains (Capabilities marked *ServiceDomain*), each realized by its functional pattern; other Groupings
|
|
830
|
+
aggregate service domains as overlays (a value chain, a release wave). Where the file keeps the stereotypes (Archi
|
|
831
|
+
specializations, or a *Stereotype* property) they say which is which: a service domain's business domain is the
|
|
832
|
+
container marked so and its area the one holding that; a Capability in a business domain is a service domain though
|
|
833
|
+
unmarked (BIAN leaves a few so); of copies of one, the placed one is it. Without stereotypes it goes by structure: a
|
|
834
|
+
Capability nothing sits in is a service domain, its nearest container its business domain, the outermost its area.
|
|
835
|
+
What holds what: composition before aggregation, then a box drawn inside another in a view. A service domain's
|
|
836
|
+
pattern is the one realizing it (an element named as a BIAN pattern), else its *Functional Pattern* property; its
|
|
837
|
+
asset its *Asset Type* property. The types, relationships, stereotypes and property keys are options of
|
|
838
|
+
`new BianLandscapeReader({...})`. Run over BIAN's v14 model it places all 344 service domains the model places in
|
|
839
|
+
their area and domain, and gives 349 of 350 their pattern; by structure alone it would take 820 Capabilities for
|
|
840
|
+
service domains.
|
|
841
|
+
`model.serviceDomainsCommand(specs, source)` brings them in as one undo step: a new one is added; one already
|
|
842
|
+
there (by id, else by name) keeps its control record, qualifiers and the rest, taking its area and domain from
|
|
843
|
+
the landscape, and its asset and pattern where it has none. `examples/bian-landscape-sample.xml` is a small
|
|
844
|
+
excerpt in that shape to try it with (not BIAN's landscape, which is BIAN's to publish).
|
|
845
|
+
|
|
846
|
+
```javascript
|
|
847
|
+
const specs = new BianLandscapeReader().read(BianLandscapeReader.parse(xml));
|
|
848
|
+
model.history.execute(model.serviceDomainsCommand(specs, '"bian.xml"'));
|
|
849
|
+
model.view({ level: DataModel.LANDSCAPE }); // { entities, folders, elementFolders, ... }
|
|
850
|
+
```
|
|
851
|
+
|
|
852
|
+
**Data types.** A model's `dataTypes` are the types its attributes have, each defined once: a `DataType` (a plain
|
|
853
|
+
named type, `basedOn` another), an `EnumerationType` (`values: [{ name, description? }]`) or a `StructuredType`
|
|
854
|
+
(`fields: [{ name, type?, description?, iso20022? }]`), its `kind` in a spec picking the class (`DataType.from`,
|
|
855
|
+
`register`), as BIAN publishes its data types and DAMA-DMBOK2 calls domains. An attribute names one by its `type`
|
|
856
|
+
(the type's id, else its name in any case: `model.typeOf(attribute)`); a plain type (`VARCHAR(20)`) names none.
|
|
857
|
+
On hover an entity lists its attributes' data types; `model.usesOf(id)` says where one is used. Level ▸ **Data types**
|
|
858
|
+
draws each as a box with a row per value or field, joined to the types a structure's fields are. Model ▸ **Data
|
|
859
|
+
types** ▸ makes one of each kind, edits or deletes each (a dialog per kind: values a line each, `value | meaning`;
|
|
860
|
+
fields `name | type`). They are saved, compared and merged as the rest of the model is (`DataTypesCompared`), and
|
|
861
|
+
the model check reports an enumeration with no values or a value twice, two types of one name, and a type nothing uses.
|
|
862
|
+
|
|
863
|
+
**ISO 20022.** An entity, an attribute or a data type may say which ISO 20022 type it is (`iso20022: 'CurrencyCode'`;
|
|
864
|
+
`DataType.iso20022Url(name)` its page in the ISO 20022 standards repository). On hover an entity lists its own and
|
|
865
|
+
its attributes' (an attribute's own, else its data type's).
|
|
866
|
+
|
|
867
|
+
**Reading a BIAN semantic API.** BIAN publishes a semantic API per service domain (OpenAPI files, YAML; the plain ones
|
|
868
|
+
and those extended with ISO 20022, at github.com/bian-official/public, Apache-2.0). Model ▸ **Read a BIAN service
|
|
869
|
+
domain (semantic API)…** reads one (`.yaml`, `.yml` or `.json`; `BianSemanticApiReader`): its control record and
|
|
870
|
+
behaviour qualifiers (the operations' `CR - …` and `BQ - …` tags) become logical entities with the attributes their
|
|
871
|
+
schemas have, the qualifiers joined to the control record; the schemas those attributes are become data types (an
|
|
872
|
+
`enum` an enumeration, each value's meaning from BIAN's description; an object a structure; else a plain type), as
|
|
873
|
+
deep as `depth` (1); a class the model already has as a logical entity stays a reference to it; the ISO 20022 version
|
|
874
|
+
gives each attribute its ISO 20022 type. The service domain is named by the API's title, its pattern and asset by its
|
|
875
|
+
control record's name. `model.bringCommand({ entities, relationships, dataTypes, serviceDomains }, label)` brings
|
|
876
|
+
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
|
|
877
|
+
types the new entities need come (and the types those need); a service domain there keeps what it has (as for a
|
|
878
|
+
landscape) and gains the behaviour qualifiers it lacks; one that would come out the same is left alone. Every one
|
|
879
|
+
of release 14's 258 APIs reads, in both versions.
|
|
880
|
+
|
|
881
|
+
```javascript
|
|
882
|
+
const read = new BianSemanticApiReader().read(BianSemanticApiReader.parse(yamlText));
|
|
883
|
+
model.history.execute(model.bringCommand(read, 'Read Current Account'));
|
|
884
|
+
model.typeOf(model.level('logical').entity('current-account-facility').attributes[1]); // EnumerationType
|
|
885
|
+
```
|
|
886
|
+
|
|
887
|
+
`YamlReader.parse(text)` (`src/yaml/YamlReader.js`) is the dependency-free reader it uses: block mappings and
|
|
888
|
+
sequences, quoted and plain scalars (typed as YAML 1.2's core schema has them), block scalars with chomping, flow
|
|
889
|
+
collections, comments; it refuses anchors, aliases and tags with the line they are on.
|
|
890
|
+
|
|
891
|
+
```js
|
|
892
|
+
const sd = model.addPerspective({ kind: 'service-domain', name: 'Current Account', asset: 'Current Account', pattern: 'Fulfill',
|
|
893
|
+
controlRecord: 'current-account', qualifiers: [{ name: 'Interest', entity: 'interest' }] });
|
|
894
|
+
sd.controlRecordName; // 'Current Account Facility'
|
|
895
|
+
sd.serviceOperations()[3].name; // 'initiate Current Account Facility'
|
|
896
|
+
```
|
|
897
|
+
|
|
898
|
+
**Compared and merged.** `model.compareWith(other)` is a `ModelComparison`, as for ArchiMate models, kind by kind
|
|
899
|
+
(`model.comparedKinds()`): each level's entities (`LevelEntitiesCompared`; where a box is drawn does not count) and
|
|
900
|
+
relationships (`LevelRelationshipsCompared`; one with no id known by its ends and name, and needing them), the
|
|
901
|
+
mappings (`MappingsCompared`, a pair each, needing its two entities), contexts and perspectives (a perspective
|
|
902
|
+
needing the context it is read in). `comparison.mergeCommand(keys)` brings the chosen differences and what they
|
|
903
|
+
need, as one step on `model.history`.
|
|
904
|
+
|
|
905
|
+
**Saved, kept and backed up.** A data model is a file of its own, `.datamodel.json` (`DataModelFileFormat`: its
|
|
906
|
+
`name`, levels, mappings, layouts, perspectives and contexts, `model.toJSON()`; the older one-entity form reads
|
|
907
|
+
too). It knows when it changed since it was saved (`model.edited()`, `markSaved()`, `isDirty()`, `onEdit(fn)`):
|
|
908
|
+
a perspective or context made, changed or removed, a box or note moved.
|
|
909
|
+
|
|
910
|
+
**In the workbench:** `DataModelPart` (type `data-model`, `RenderEngineWorkspace.DataModelPart`) shows one
|
|
911
|
+
model: File ▸ Open…, Save, Save As… (Ctrl+O, Ctrl+S; `part.file`, the `ModelFile` editors use, • while changed,
|
|
912
|
+
asking before the page is left with changes unsaved), and hosted by a `ModelsPart` (`editorType: 'data-model'`)
|
|
913
|
+
New ▸ (New model, From base: …), Duplicate model, Close model and This browser ▸ (with `keep`: kept in this browser
|
|
914
|
+
as it changes, reopened next time, backed up to a folder chosen once, as the modeller's models are); Edit ▸ Undo
|
|
915
|
+
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
|
|
916
|
+
it has some; a perspective brings its context, and the context's scope shows beside them), Level (Conceptual,
|
|
917
|
+
Logical, Physical, and Mapping), Notation, and Export ▸ PNG / SVG. A box or note dragged stays on the level it
|
|
918
|
+
was dragged on (the mapping view's in its own layout). `examples/data-model.html` (`npm run datamodel`) uses every
|
|
919
|
+
feature above. A data model can also be registered like a chart:
|
|
920
|
+
`engine.register(id, 'dataModel', { entities, relationships, level })` then `engine.refreshChart(id)` (each
|
|
921
|
+
diagram catalog entry names its data options, `args`).
|
|
721
922
|
- `railroadDiagram(diagramString)` — renders an EBNF grammar string as a railroad diagram. No node/edge
|
|
722
923
|
data at all.
|
|
723
924
|
|
|
@@ -1029,15 +1230,22 @@ do, and a folder of one type in the Layers tree too (`typeGlyph(typeName, { size
|
|
|
1029
1230
|
`TreeBuilder`'s `iconFor(node)` gives a node a picture in place of its `icon` text.
|
|
1030
1231
|
|
|
1031
1232
|
**Nesting.** In the ArchiMate canvas editor, dropping a box wholly inside an element's box nests it there (Archi's
|
|
1032
|
-
nested elements)
|
|
1233
|
+
nested elements); so does dropping an element with its centre on another element's box when a relationship a
|
|
1234
|
+
nesting stands for is allowed between them (`nestsOnDrop`), and the parent then grows to show it whole, with its
|
|
1235
|
+
own parents in turn (`DiagramEditor#fitNesting`, `DiagramEditor.NEST_MARGIN`: room around it, more at the top for
|
|
1236
|
+
the parent's name), all in the move's undo step. A nested box moves with its parent, is raised above it, and an Archi file writes it inside it (whatever order
|
|
1033
1237
|
they were added in). Dragged out again, it is taken out. The move and the nesting are one undo step. On a canvas, a
|
|
1034
1238
|
block holds boxes the same way. Nesting one element in another then asks which relationship it stands for, among
|
|
1035
1239
|
Archi's nesting kinds (`NESTING_KINDS`: composition, aggregation, assignment, realization, specialization) that the
|
|
1036
1240
|
rules allow from parent to child, and none when the two already have one; **Add** puts it in the model, drawn as no
|
|
1037
1241
|
line (the nesting shows it), as its own undo step; **No relationship** adds none. The shared editor does the
|
|
1038
1242
|
finding: after a move, `DiagramEditor#nestingChanges(ids)` gives each moved shape's new parent (the innermost shape
|
|
1039
|
-
that wholly holds it and `canHold` it
|
|
1040
|
-
(`NestInstanceCommand`) and `nested(changes)`; the
|
|
1243
|
+
that wholly holds it and `canHold` it, else the innermost its centre lies on that `nestsOnDrop` it), and a subclass
|
|
1244
|
+
supplies `parentOf`, `canHold`, `nestsOnDrop`, `nestCommand` (`NestInstanceCommand`) and `nested(changes)`; the
|
|
1245
|
+
template editor has no nesting. As in Archi, a line a nesting stands for (one of `NESTING_KINDS`) from a box to one
|
|
1246
|
+
nested in it is not drawn, at any depth (to a box nested in a box nested in it too: `hiddenByNesting`,
|
|
1247
|
+
`holdsNested` in `diagrams/nesting.js`); an Archi file's such lines are read in, kept and written back, just not
|
|
1248
|
+
drawn. ⚙ Settings ▸ **Lines to nested boxes** (`nodeOptions.showNestedLines`) shows them. Model checks: **Check model…**
|
|
1041
1249
|
warns of an element drawn inside another with no such relationship (`nested-without-relationship`), and a
|
|
1042
1250
|
relationship a nesting shows is not reported as unused (`viewNestings(view)`, `nestingRelationships` in
|
|
1043
1251
|
`diagrams/nesting.js`).
|
|
@@ -1103,7 +1311,14 @@ there (`editor.addImage(dataURL, point)`, one undo step, selected; the base edit
|
|
|
1103
1311
|
relationships) under the palette, for an element chosen on the canvas or in the tree. The tree's own filters (its
|
|
1104
1312
|
"Filters…" dialog: text, types, specializations, properties) dim what they leave out on the canvas too:
|
|
1105
1313
|
`modelTree.matchingElementIds()` (null with no filter on) and `modelTree.onFilterChange(fn)` feed
|
|
1106
|
-
`editor.setElementFilter`.
|
|
1314
|
+
`editor.setElementFilter`. Under the search box, **In:** chooses what the search covers, the whole model or only
|
|
1315
|
+
the diagram in front (`modelTree.setSearchScope('model' | 'view')`, `ModelTree.SEARCH_SCOPES`), and
|
|
1316
|
+
**Descriptions** looks for the text in elements' and relationships' documentation too
|
|
1317
|
+
(`modelTree.setSearchInDescriptions(true)`). The Filters… dialog opens showing the filters on now
|
|
1318
|
+
(`getTypeFilter()`, `getSpecializationFilter()`, `getPropertyFilter()`). Its properties start unticked (no property
|
|
1319
|
+
filter; `OptInCheckboxTreeSelector`: a value is ticked to keep what has it), and a property with more than 10
|
|
1320
|
+
values starts folded, with a box that narrows its values to those containing what is typed; ticking the property
|
|
1321
|
+
then ticks only the values shown (`CheckboxTreeSelector`'s `searchAbove`, `search(id, text)`). The analysis names an element's layer as the tree files it (`treeLayerOf`).
|
|
1107
1322
|
|
|
1108
1323
|
**Groups on the canvas.** An Archi group (a model folder with bounds on the view, `view.placedFolders`) is a
|
|
1109
1324
|
`ViewGroupTemplate`: Archi's box with a name tab (`TabbedBoxGlyph`), placed by its centre while its layout entry
|
|
@@ -1273,6 +1488,34 @@ mine.history.execute(comparison.mergeCommand(['view:id-extra'])); // the view,
|
|
|
1273
1488
|
mine.history.undo();
|
|
1274
1489
|
```
|
|
1275
1490
|
|
|
1491
|
+
**A model tab's menu.** Right-click a model's tab for what can be done with that model (`models.tabMenuItems(part)`):
|
|
1492
|
+
Save and Save As… (an editor that saves files), Duplicate model, Compare with ▸ and Merge from ▸, This browser ▸
|
|
1493
|
+
(with `keep`), Close model and Close other models. The work area hands a tab's right-click to its `tabMenu` option
|
|
1494
|
+
(`(partId, event) => shown`), through `TabsNode`'s `ctx.onTabMenu`; with none, the browser's own menu shows.
|
|
1495
|
+
|
|
1496
|
+
**Bases, and copies.** A `ModelsPart` spec's `bases` are reference models kept as given: each made from its own copy
|
|
1497
|
+
of its spec when the workbench is first drawn (`models.bases()`, parts never opened, edited or kept, so an open model
|
|
1498
|
+
sharing the spec's data cannot reach them). File ▸ New ▸ **From base: …** (`models.newFromBase(i)`,
|
|
1499
|
+
`models.baseItems()`) opens a model as a copy of one; Tools ▸ **Compare with** and **Merge from** offer them, after
|
|
1500
|
+
the other open models (`models.toolItems(part)`); File ▸ **Duplicate model** (`models.duplicate(part)`) opens a copy
|
|
1501
|
+
of the model in front, written and read back through its own file format. A copy, from a base or duplicated, is named
|
|
1502
|
+
"… (copy)" (`ModelsPart.copyName`, the suffix once).
|
|
1503
|
+
|
|
1504
|
+
**Kept in this browser, and backed up.** With `keep: true` in its spec, a `ModelsPart` keeps every model it holds
|
|
1505
|
+
in this browser, so work outlives the page without saving a file: a moment after each edit (`ModelsPart.KEEP_DELAY`)
|
|
1506
|
+
and at once when imported, each in its editor's own file format (`part.keepEntry()`: the file's format, else the
|
|
1507
|
+
part's save format), in a `ModelLibrary` over an IndexedDB store (`KeyValueStore`: `IndexedDbStore`, or a
|
|
1508
|
+
`MemoryStore` where the browser has none). Those open when the page closed open again next time
|
|
1509
|
+
(`models.ready` resolves once they have), in place of the starting model if it was left untouched; a tab closed
|
|
1510
|
+
is kept but not reopened. File ▸ **This browser** has **Open a kept model…**, **Stop keeping this model**, **Back
|
|
1511
|
+
up kept models now** and **Backup folder…**. Backups (`BackupFolder`, after au-accounts-app's) go to a folder chosen
|
|
1512
|
+
once and remembered (the File System Access API, in Chrome and Edge, from `file://` too): choose one that OneDrive,
|
|
1513
|
+
Google Drive or Dropbox syncs and they reach the cloud, with no sign-in in the page. Each is written stamped with
|
|
1514
|
+
the date and time (`BackupFolder.stamped('shop.archimate')` gives `shop-20261015-093012.archimate`), so they gather
|
|
1515
|
+
as a history, and under its plain name too (`keepUnstamped`); where a folder cannot be written (Firefox, Safari,
|
|
1516
|
+
permission refused) they download instead. `backups.log()` records each (newest first, `LOG_MAX`), and
|
|
1517
|
+
`backups.daysSinceLast()` how long ago the last was. `examples/archimate.html` keeps its models.
|
|
1518
|
+
|
|
1276
1519
|
```js
|
|
1277
1520
|
ws.addParts([{ id: 'models', type: 'models', editorType: 'archimate-canvas', newModel: { analysis: true },
|
|
1278
1521
|
models: [{ id: 'modeller', title: 'Sample model', elements: [...], views: [...] }] }]);
|
|
@@ -1671,6 +1914,7 @@ the shared `MenuBar` (see "Toolbars and dialogs" below):
|
|
|
1671
1914
|
- **`viewpoint`** — Settings ▸ Viewpoint (every built-in and custom viewpoint) and ▸ Viewpoint mode (fade/hide)
|
|
1672
1915
|
- **`distinctShapes`** — Settings ▸ Distinct shapes: on/off
|
|
1673
1916
|
- **`showDerived`** — Settings ▸ Show derived: on/off
|
|
1917
|
+
- **`showNestedLines`** — Settings ▸ Lines to nested boxes: hidden (default, Archi's way) / shown
|
|
1674
1918
|
- **`exportCSV`**, **`importCSV`**, **`exportArchimate`**, **`importArchimate`** — entries in the **File** menu,
|
|
1675
1919
|
after the view items (New view…, Properties…, Duplicate, Delete)
|
|
1676
1920
|
|
|
@@ -1858,6 +2102,11 @@ parts.children.length; // 3: front, top and side faces
|
|
|
1858
2102
|
context, and the template draws its parts as SVG (`glyphToSvg`; `glyphElement(part)` gives the tag and
|
|
1859
2103
|
attributes, by the SVG renderer's rules) and attaches lines to the glyph's `outline(ctx)`. Every glyph
|
|
1860
2104
|
has an outline: its box by default, the real shape for round, skewed and pointed ones.
|
|
2105
|
+
`graphDiagram` (and the data-model diagram drawn by it) does the same: a line ends where it meets the node's
|
|
2106
|
+
glyph outline (`NodeGeometry.outline(shape, size)`, crossed by `EdgeGeometry.rayPolygonT`), so it reaches a wide
|
|
2107
|
+
ellipse or product box rather than stopping short on a circle as wide as the node; a shape no glyph draws keeps
|
|
2108
|
+
that circle. Centred initials stand in for a name drawn below the shape: a node whose name is inside it gets none
|
|
2109
|
+
(`GraphDiagram.showsInitials(node)`).
|
|
1861
2110
|
|
|
1862
2111
|
**ArchiMate in the template editor.** `ArchimateElementTemplate(instance, model, {nodeOptions})` and
|
|
1863
2112
|
`ArchimateEdge(instance, view)` put an `ArchimateView` into a `DiagramEditor`. Neither keeps a copy:
|
|
@@ -1943,8 +2192,9 @@ Each ArchiMate relationship type gives its notation as line style values:
|
|
|
1943
2192
|
`DIAGRAM_TYPES` (20 categories in the groups General, Process, UML, Data, Infrastructure,
|
|
1944
2193
|
Business, Ideas) decides which templates the palette offers. An entry may say what it is (`description`), shown
|
|
1945
2194
|
when the pointer rests on its tile. **Enterprise Ontology** (`'enterprise-ontology'`, in Data) offers the
|
|
1946
|
-
universal concepts enterprise data models are built from, each described: Party,
|
|
1947
|
-
Event, Account / Ledger, Classification, Place, Time, Measure
|
|
2195
|
+
universal concepts enterprise data models are built from (after IBM IFW's data concepts), each described: Party,
|
|
2196
|
+
Role, Arrangement, Product, Resource, Event, Account / Ledger, Classification, Place, Time, Measure, Business
|
|
2197
|
+
Direction and Rule. `editor.initPaletteForDiagramType(container, key)`
|
|
1948
2198
|
renders a grouped category picker above a grid of draggable tiles. `editor.setPaletteCategory(key)`
|
|
1949
2199
|
switches category, and `onTemplateTypeChange(key)` (a constructor option) reports the switch.
|
|
1950
2200
|
Unknown keys fall back to `'generic'` (Basic Shapes).
|
|
@@ -2108,6 +2358,22 @@ its `fileFormats()`, none by default, then the image export), and its editor dis
|
|
|
2108
2358
|
import goes through `replaceModel(model)`, which a subclass follows in `modelReplaced(model)`.
|
|
2109
2359
|
`DiagramEditorPart`, `ArchimateEditorPart` and `ArchimateCanvasPart` extend it.
|
|
2110
2360
|
|
|
2361
|
+
**Tabs, as a browser has them.** A part's tabs are the views open (`part.openViews`, an `OpenViewList` over its
|
|
2362
|
+
model), not every view: a model opened or imported starts with its first view only, so an Archi file of many
|
|
2363
|
+
views does not fill the strip. Going to a view (choosing it in the model tree, a search match, a new view)
|
|
2364
|
+
opens its tab; a tab's **×** closes the tab and leaves the view in the model (one tab always stays, and the
|
|
2365
|
+
tab in front closed brings its neighbour); **+** after the tabs makes something new (`newItems()`: a view, or
|
|
2366
|
+
a menu of a view, a canvas, from a template). `part.viewTabs({ orientation })` builds the strip
|
|
2367
|
+
(`ViewTabStrip` with `canClose` and `onAdd`). Drawing every view for the HTML report opens none
|
|
2368
|
+
(`openViews.while(fn)`). The spec's `openViews` (view ids) chooses which open at the start.
|
|
2369
|
+
|
|
2370
|
+
**Panels that fold and resize.** The side panels (the model tree, the palette and analysis) have a splitter
|
|
2371
|
+
on their inner edge: drag it to resize the panel, drag it nearly shut or double-click it to fold the panel to
|
|
2372
|
+
a stub that names it, and click the stub to open it at its width again. `part.layout` (a `PanelLayout`:
|
|
2373
|
+
`resize(name, width)`, `toggle(name)`, `widthOf(name)`) keeps the widths and folds across renders; the spec's
|
|
2374
|
+
`layout` (`{ side: { width, collapsed }, aside: {...} }`) sets them at the start. `EditorShell` draws them
|
|
2375
|
+
when given a `layout` and stub `labels`.
|
|
2376
|
+
|
|
2111
2377
|
`DiagramEditorPart` (workspace bundle) puts this together: `MenuBar` (File creates and deletes
|
|
2112
2378
|
diagrams; Settings has the grid and line-style levels), the palette, a `ViewTabStrip` (right-click
|
|
2113
2379
|
a tab for its **Properties…**: name, documentation and the diagram's line style; **Duplicate**;
|