@bpmn-ld/bpmn-ld 0.0.0-stage → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +201 -0
- package/README.md +125 -2
- package/contexts/combined.jsonld +3 -0
- package/contexts/core.jsonld +170 -0
- package/contexts/di.jsonld +37 -0
- package/dist/analyze/analyze.d.ts +3 -0
- package/dist/analyze/analyze.js +11 -0
- package/dist/analyze/findings.d.ts +8 -0
- package/dist/analyze/findings.js +1 -0
- package/dist/analyze/report.d.ts +3 -0
- package/dist/analyze/report.js +9 -0
- package/dist/analyze/rules/ad-hoc-completion-condition.d.ts +2 -0
- package/dist/analyze/rules/ad-hoc-completion-condition.js +7 -0
- package/dist/analyze/rules/cannot-reach-end.d.ts +2 -0
- package/dist/analyze/rules/cannot-reach-end.js +28 -0
- package/dist/analyze/rules/compensation-association.d.ts +2 -0
- package/dist/analyze/rules/compensation-association.js +13 -0
- package/dist/analyze/rules/dead-end.d.ts +2 -0
- package/dist/analyze/rules/dead-end.js +13 -0
- package/dist/analyze/rules/end-event.d.ts +2 -0
- package/dist/analyze/rules/end-event.js +8 -0
- package/dist/analyze/rules/error-catch.d.ts +2 -0
- package/dist/analyze/rules/error-catch.js +14 -0
- package/dist/analyze/rules/escalation-catch.d.ts +2 -0
- package/dist/analyze/rules/escalation-catch.js +14 -0
- package/dist/analyze/rules/gateway-cardinality.d.ts +3 -0
- package/dist/analyze/rules/gateway-cardinality.js +17 -0
- package/dist/analyze/rules/index.d.ts +4 -0
- package/dist/analyze/rules/index.js +21 -0
- package/dist/analyze/rules/instantiating-gateway.d.ts +2 -0
- package/dist/analyze/rules/instantiating-gateway.js +14 -0
- package/dist/analyze/rules/non-interrupting-start.d.ts +2 -0
- package/dist/analyze/rules/non-interrupting-start.js +14 -0
- package/dist/analyze/rules/reachability.d.ts +2 -0
- package/dist/analyze/rules/reachability.js +25 -0
- package/dist/analyze/rules/rule.d.ts +7 -0
- package/dist/analyze/rules/rule.js +1 -0
- package/dist/analyze/rules/signal-catch.d.ts +2 -0
- package/dist/analyze/rules/signal-catch.js +12 -0
- package/dist/analyze/rules/split-join-pairing.d.ts +3 -0
- package/dist/analyze/rules/split-join-pairing.js +50 -0
- package/dist/analyze/rules/start-event.d.ts +2 -0
- package/dist/analyze/rules/start-event.js +8 -0
- package/dist/analyze/rules/task-message-flow.d.ts +2 -0
- package/dist/analyze/rules/task-message-flow.js +19 -0
- package/dist/analyze/scope-graph.d.ts +56 -0
- package/dist/analyze/scope-graph.js +89 -0
- package/dist/analyze-cli.d.ts +1 -0
- package/dist/analyze-cli.js +5 -0
- package/dist/analyze-run.d.ts +12 -0
- package/dist/analyze-run.js +85 -0
- package/dist/bpmn-ld-cli.d.ts +2 -0
- package/dist/bpmn-ld-cli.js +97 -0
- package/dist/build-cli.d.ts +1 -0
- package/dist/build-cli.js +2 -0
- package/dist/build.d.ts +3 -0
- package/dist/build.js +38 -0
- package/dist/cheat-sheet-catalog.d.ts +10 -0
- package/dist/cheat-sheet-catalog.js +72 -0
- package/dist/cheat-sheet-cli.d.ts +1 -0
- package/dist/cheat-sheet-cli.js +7 -0
- package/dist/cheat-sheet-compose.d.ts +13 -0
- package/dist/cheat-sheet-compose.js +107 -0
- package/dist/cheat-sheet-run.d.ts +11 -0
- package/dist/cheat-sheet-run.js +65 -0
- package/dist/cheat-sheet-svg.d.ts +2 -0
- package/dist/cheat-sheet-svg.js +2 -0
- package/dist/cheat-sheet.d.ts +2 -0
- package/dist/cheat-sheet.js +2 -0
- package/dist/check-xml/check.d.ts +3 -0
- package/dist/check-xml/check.js +11 -0
- package/dist/check-xml/lint.d.ts +2 -0
- package/dist/check-xml/lint.js +47 -0
- package/dist/check-xml/moddle.d.ts +6 -0
- package/dist/check-xml/moddle.js +23 -0
- package/dist/check-xml/types.d.ts +20 -0
- package/dist/check-xml/types.js +1 -0
- package/dist/check-xml/xsd.d.ts +2 -0
- package/dist/check-xml/xsd.js +25 -0
- package/dist/check-xml-cli.d.ts +1 -0
- package/dist/check-xml-cli.js +5 -0
- package/dist/check-xml-run.d.ts +10 -0
- package/dist/check-xml-run.js +77 -0
- package/dist/convert/analyze-output.d.ts +3 -0
- package/dist/convert/analyze-output.js +8 -0
- package/dist/convert/convert.d.ts +19 -0
- package/dist/convert/convert.js +96 -0
- package/dist/convert/errors.d.ts +5 -0
- package/dist/convert/errors.js +8 -0
- package/dist/convert/extensions.d.ts +23 -0
- package/dist/convert/extensions.js +103 -0
- package/dist/convert/mapping-check.d.ts +15 -0
- package/dist/convert/mapping-check.js +66 -0
- package/dist/convert/mapping.d.ts +52 -0
- package/dist/convert/mapping.js +273 -0
- package/dist/convert/namespaces.d.ts +2 -0
- package/dist/convert/namespaces.js +19 -0
- package/dist/convert/validate-output.d.ts +2 -0
- package/dist/convert/validate-output.js +28 -0
- package/dist/convert/warnings.d.ts +9 -0
- package/dist/convert/warnings.js +67 -0
- package/dist/convert/xml-reader.d.ts +11 -0
- package/dist/convert/xml-reader.js +84 -0
- package/dist/convert-cli.d.ts +1 -0
- package/dist/convert-cli.js +5 -0
- package/dist/convert-run.d.ts +10 -0
- package/dist/convert-run.js +125 -0
- package/dist/coverage-cli.d.ts +1 -0
- package/dist/coverage-cli.js +8 -0
- package/dist/coverage-mappings.d.ts +3 -0
- package/dist/coverage-mappings.js +34 -0
- package/dist/coverage-scope.d.ts +11 -0
- package/dist/coverage-scope.js +187 -0
- package/dist/coverage-xsd.d.ts +15 -0
- package/dist/coverage-xsd.js +55 -0
- package/dist/coverage.d.ts +35 -0
- package/dist/coverage.js +152 -0
- package/dist/docs-cli.d.ts +1 -0
- package/dist/docs-cli.js +18 -0
- package/dist/docs.d.ts +8 -0
- package/dist/docs.js +136 -0
- package/dist/eval/checks.d.ts +19 -0
- package/dist/eval/checks.js +252 -0
- package/dist/eval/regenerate.d.ts +10 -0
- package/dist/eval/regenerate.js +48 -0
- package/dist/eval-regenerate-cli.d.ts +1 -0
- package/dist/eval-regenerate-cli.js +16 -0
- package/dist/export/data.d.ts +10 -0
- package/dist/export/data.js +127 -0
- package/dist/export/diagram.d.ts +8 -0
- package/dist/export/diagram.js +112 -0
- package/dist/export/element.d.ts +27 -0
- package/dist/export/element.js +175 -0
- package/dist/export/export.d.ts +22 -0
- package/dist/export/export.js +124 -0
- package/dist/export/extensions.d.ts +18 -0
- package/dist/export/extensions.js +122 -0
- package/dist/export/layout-export.d.ts +4 -0
- package/dist/export/layout-export.js +25 -0
- package/dist/export/mapping-check.d.ts +39 -0
- package/dist/export/mapping-check.js +175 -0
- package/dist/export/normalize.d.ts +3 -0
- package/dist/export/normalize.js +67 -0
- package/dist/export-cli.d.ts +1 -0
- package/dist/export-cli.js +5 -0
- package/dist/export-run.d.ts +10 -0
- package/dist/export-run.js +157 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.js +10 -0
- package/dist/install-skill-run.d.ts +8 -0
- package/dist/install-skill-run.js +30 -0
- package/dist/install-skill.d.ts +23 -0
- package/dist/install-skill.js +76 -0
- package/dist/layout/collaboration.d.ts +36 -0
- package/dist/layout/collaboration.js +152 -0
- package/dist/layout/di.d.ts +15 -0
- package/dist/layout/di.js +52 -0
- package/dist/layout/extract.d.ts +2 -0
- package/dist/layout/extract.js +99 -0
- package/dist/layout/lanes.d.ts +30 -0
- package/dist/layout/lanes.js +112 -0
- package/dist/layout/layering.d.ts +7 -0
- package/dist/layout/layering.js +53 -0
- package/dist/layout/layout.d.ts +12 -0
- package/dist/layout/layout.js +299 -0
- package/dist/layout/model.d.ts +52 -0
- package/dist/layout/model.js +10 -0
- package/dist/layout/placement.d.ts +25 -0
- package/dist/layout/placement.js +256 -0
- package/dist/layout/standalone.d.ts +8 -0
- package/dist/layout/standalone.js +50 -0
- package/dist/layout-cli.d.ts +1 -0
- package/dist/layout-cli.js +5 -0
- package/dist/layout-run.d.ts +9 -0
- package/dist/layout-run.js +105 -0
- package/dist/loader.d.ts +5 -0
- package/dist/loader.js +7 -0
- package/dist/namespace.d.ts +5 -0
- package/dist/namespace.js +5 -0
- package/dist/ontology.d.ts +19 -0
- package/dist/ontology.js +94 -0
- package/dist/paths.d.ts +1 -0
- package/dist/paths.js +2 -0
- package/dist/render/geometry.d.ts +40 -0
- package/dist/render/geometry.js +81 -0
- package/dist/render/renderer.d.ts +31 -0
- package/dist/render/renderer.js +369 -0
- package/dist/render/symbols.d.ts +10 -0
- package/dist/render/symbols.js +26 -0
- package/dist/render/text.d.ts +20 -0
- package/dist/render/text.js +28 -0
- package/dist/render-cli.d.ts +1 -0
- package/dist/render-cli.js +7 -0
- package/dist/render-run.d.ts +9 -0
- package/dist/render-run.js +161 -0
- package/dist/render.d.ts +4 -0
- package/dist/render.js +4 -0
- package/dist/schema-overrides.d.ts +14 -0
- package/dist/schema-overrides.js +36 -0
- package/dist/schema-validate.d.ts +5 -0
- package/dist/schema-validate.js +8 -0
- package/dist/schema.d.ts +6 -0
- package/dist/schema.js +114 -0
- package/dist/shacl.d.ts +15 -0
- package/dist/shacl.js +37 -0
- package/dist/validate-cli.d.ts +1 -0
- package/dist/validate-cli.js +5 -0
- package/dist/validate-run.d.ts +8 -0
- package/dist/validate-run.js +58 -0
- package/dist/validate.d.ts +11 -0
- package/dist/validate.js +94 -0
- package/dist/versioning-cli.d.ts +1 -0
- package/dist/versioning-cli.js +18 -0
- package/dist/versioning.d.ts +17 -0
- package/dist/versioning.js +45 -0
- package/ontology/core.ttl +993 -0
- package/ontology/di.ttl +167 -0
- package/package.json +77 -4
- package/reference/bpmn-xsd/BPMN20.xsd +37 -0
- package/reference/bpmn-xsd/BPMNDI.xsd +100 -0
- package/reference/bpmn-xsd/DC.xsd +29 -0
- package/reference/bpmn-xsd/DI.xsd +100 -0
- package/reference/bpmn-xsd/Semantic.xsd +1562 -0
- package/reference/rospocher/ATTRIBUTION.md +22 -0
- package/reference/rospocher/BPMNOntology.owl +8937 -0
- package/reference/rospocher-verified.json +221 -0
- package/schema/bpmn-ld.schema.json +15230 -0
- package/shapes/core.shapes.ttl +398 -0
- package/skills/bpmn-ld/SKILL.md +154 -0
- package/skills/bpmn-ld/evals/models/order-approval-di.jsonld +296 -0
- package/skills/bpmn-ld/evals/models/refund-di.bpmn +82 -0
- package/skills/bpmn-ld/evals/models/refund.bpmn +18 -0
- package/skills/bpmn-ld/evals/outputs/explain-collaboration.md +22 -0
- package/skills/bpmn-ld/evals/outputs/explain-order-approval.md +24 -0
- package/skills/bpmn-ld/evals/outputs/explain-refund.md +17 -0
- package/skills/bpmn-ld/evals/outputs/generate-activity-compensation.jsonld +39 -0
- package/skills/bpmn-ld/evals/outputs/generate-ad-hoc-sub-process.jsonld +52 -0
- package/skills/bpmn-ld/evals/outputs/generate-boundary-event.jsonld +27 -0
- package/skills/bpmn-ld/evals/outputs/generate-collaboration.jsonld +49 -0
- package/skills/bpmn-ld/evals/outputs/generate-conditions.jsonld +93 -0
- package/skills/bpmn-ld/evals/outputs/generate-diagram-properties.jsonld +340 -0
- package/skills/bpmn-ld/evals/outputs/generate-error.jsonld +217 -0
- package/skills/bpmn-ld/evals/outputs/generate-escalation.jsonld +231 -0
- package/skills/bpmn-ld/evals/outputs/generate-event-definition-ref.jsonld +67 -0
- package/skills/bpmn-ld/evals/outputs/generate-event-sub-process.jsonld +54 -0
- package/skills/bpmn-ld/evals/outputs/generate-exclusive-choice.jsonld +32 -0
- package/skills/bpmn-ld/evals/outputs/generate-instantiating-gateway.jsonld +24 -0
- package/skills/bpmn-ld/evals/outputs/generate-lane-partition.jsonld +46 -0
- package/skills/bpmn-ld/evals/outputs/generate-link-event.expect.json +11 -0
- package/skills/bpmn-ld/evals/outputs/generate-link-event.jsonld +57 -0
- package/skills/bpmn-ld/evals/outputs/generate-loop.jsonld +30 -0
- package/skills/bpmn-ld/evals/outputs/generate-message.jsonld +61 -0
- package/skills/bpmn-ld/evals/outputs/generate-parallel-branches.jsonld +25 -0
- package/skills/bpmn-ld/evals/outputs/generate-process-attributes.jsonld +49 -0
- package/skills/bpmn-ld/evals/outputs/generate-script-task.jsonld +18 -0
- package/skills/bpmn-ld/evals/outputs/generate-signal.jsonld +178 -0
- package/skills/bpmn-ld/evals/outputs/generate-sub-process.jsonld +31 -0
- package/skills/bpmn-ld/evals/outputs/generate-targeted-compensation.jsonld +40 -0
- package/skills/bpmn-ld/evals/outputs/generate-task-implementation.jsonld +18 -0
- package/skills/bpmn-ld/evals/outputs/generate-task-message-references.jsonld +49 -0
- package/skills/bpmn-ld/evals/outputs/generate-transaction.jsonld +48 -0
- package/skills/bpmn-ld/evals/outputs/modify-order-approval.jsonld +376 -0
- package/skills/bpmn-ld/evals/outputs/modify-refund.jsonld +373 -0
- package/skills/bpmn-ld/evals/outputs/order-approval.jsonld +29 -0
- package/skills/bpmn-ld/evals/outputs/refine-order-approval.jsonld +377 -0
- package/skills/bpmn-ld/evals/prompts/explain-collaboration.md +3 -0
- package/skills/bpmn-ld/evals/prompts/explain-order-approval.md +3 -0
- package/skills/bpmn-ld/evals/prompts/explain-refund.md +3 -0
- package/skills/bpmn-ld/evals/prompts/generate-activity-compensation.md +1 -0
- package/skills/bpmn-ld/evals/prompts/generate-ad-hoc-sub-process.md +1 -0
- package/skills/bpmn-ld/evals/prompts/generate-boundary-event.md +1 -0
- package/skills/bpmn-ld/evals/prompts/generate-collaboration.md +1 -0
- package/skills/bpmn-ld/evals/prompts/generate-conditions.md +1 -0
- package/skills/bpmn-ld/evals/prompts/generate-diagram-properties.md +1 -0
- package/skills/bpmn-ld/evals/prompts/generate-error.md +1 -0
- package/skills/bpmn-ld/evals/prompts/generate-escalation.md +1 -0
- package/skills/bpmn-ld/evals/prompts/generate-event-definition-ref.md +1 -0
- package/skills/bpmn-ld/evals/prompts/generate-event-sub-process.md +1 -0
- package/skills/bpmn-ld/evals/prompts/generate-exclusive-choice.md +1 -0
- package/skills/bpmn-ld/evals/prompts/generate-instantiating-gateway.md +1 -0
- package/skills/bpmn-ld/evals/prompts/generate-lane-partition.md +1 -0
- package/skills/bpmn-ld/evals/prompts/generate-link-event.md +1 -0
- package/skills/bpmn-ld/evals/prompts/generate-loop.md +1 -0
- package/skills/bpmn-ld/evals/prompts/generate-message.md +1 -0
- package/skills/bpmn-ld/evals/prompts/generate-parallel-branches.md +1 -0
- package/skills/bpmn-ld/evals/prompts/generate-process-attributes.md +1 -0
- package/skills/bpmn-ld/evals/prompts/generate-script-task.md +1 -0
- package/skills/bpmn-ld/evals/prompts/generate-signal.md +1 -0
- package/skills/bpmn-ld/evals/prompts/generate-sub-process.md +1 -0
- package/skills/bpmn-ld/evals/prompts/generate-targeted-compensation.md +1 -0
- package/skills/bpmn-ld/evals/prompts/generate-task-implementation.md +1 -0
- package/skills/bpmn-ld/evals/prompts/generate-task-message-references.md +1 -0
- package/skills/bpmn-ld/evals/prompts/generate-transaction.md +1 -0
- package/skills/bpmn-ld/evals/prompts/modify-order-approval.md +4 -0
- package/skills/bpmn-ld/evals/prompts/modify-refund.md +4 -0
- package/skills/bpmn-ld/evals/prompts/order-approval.md +1 -0
- package/skills/bpmn-ld/evals/prompts/refine-order-approval.md +8 -0
- package/skills/bpmn-ld/examples/positive/activity-compensation.jsonld +39 -0
- package/skills/bpmn-ld/examples/positive/ad-hoc-sub-process.jsonld +52 -0
- package/skills/bpmn-ld/examples/positive/boundary-event.jsonld +27 -0
- package/skills/bpmn-ld/examples/positive/collaboration.jsonld +49 -0
- package/skills/bpmn-ld/examples/positive/conditions.jsonld +93 -0
- package/skills/bpmn-ld/examples/positive/diagram-properties.jsonld +340 -0
- package/skills/bpmn-ld/examples/positive/error.jsonld +208 -0
- package/skills/bpmn-ld/examples/positive/escalation.jsonld +231 -0
- package/skills/bpmn-ld/examples/positive/event-definition-ref.jsonld +142 -0
- package/skills/bpmn-ld/examples/positive/event-sub-process.jsonld +54 -0
- package/skills/bpmn-ld/examples/positive/exclusive-choice.jsonld +32 -0
- package/skills/bpmn-ld/examples/positive/instantiating-gateway.jsonld +24 -0
- package/skills/bpmn-ld/examples/positive/lane-partition.jsonld +46 -0
- package/skills/bpmn-ld/examples/positive/link-event.expect.json +10 -0
- package/skills/bpmn-ld/examples/positive/link-event.jsonld +138 -0
- package/skills/bpmn-ld/examples/positive/loop.jsonld +30 -0
- package/skills/bpmn-ld/examples/positive/message.jsonld +60 -0
- package/skills/bpmn-ld/examples/positive/order-approval.jsonld +29 -0
- package/skills/bpmn-ld/examples/positive/parallel-branches.jsonld +25 -0
- package/skills/bpmn-ld/examples/positive/process-attributes.jsonld +49 -0
- package/skills/bpmn-ld/examples/positive/script-task.jsonld +18 -0
- package/skills/bpmn-ld/examples/positive/signal.jsonld +150 -0
- package/skills/bpmn-ld/examples/positive/sub-process.jsonld +31 -0
- package/skills/bpmn-ld/examples/positive/targeted-compensation.jsonld +40 -0
- package/skills/bpmn-ld/examples/positive/task-implementation.jsonld +18 -0
- package/skills/bpmn-ld/examples/positive/task-message-references.jsonld +151 -0
- package/skills/bpmn-ld/examples/positive/transaction.jsonld +48 -0
- package/skills/bpmn-ld/references/clarification-and-refinement.md +60 -0
- package/skills/bpmn-ld/references/common-mistakes.md +110 -0
- package/skills/bpmn-ld/references/explain-and-document.md +36 -0
- package/skills/bpmn-ld/references/modify-existing-model.md +49 -0
- package/skills/bpmn-ld/references/patterns.md +292 -0
- package/skills/bpmn-ld/references/render-output.md +1 -0
- package/skills/bpmn-ld/references/rule-catalogue.md +183 -0
- package/skills/bpmn-ld/references/vocabulary-summary.md +65 -0
- package/skills/bpmn-ld/template.jsonld +14 -0
- package/templates/combined.jsonld +3 -0
- package/templates/core.jsonld +170 -0
- package/templates/core.shapes.ttl +398 -0
- package/templates/core.ttl +993 -0
- package/templates/di.jsonld +37 -0
- package/templates/di.ttl +167 -0
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
import { arrangeLanes, bandsBottom, buildLaneLayout, extendLastBand, laneBoxes, laneBoxesFromNodes, LANE_HEADER } from "./lanes.js";
|
|
2
|
+
import { allFlows, allNodes } from "./model.js";
|
|
3
|
+
import { measure, ORIGIN, placeScope } from "./placement.js";
|
|
4
|
+
export const POOL_HEADER = LANE_HEADER;
|
|
5
|
+
const POOL_MARGIN = 20;
|
|
6
|
+
const POOL_GAP = 50;
|
|
7
|
+
const EMPTY_POOL_HEIGHT = 60;
|
|
8
|
+
const MESSAGE_CLEARANCE = 15;
|
|
9
|
+
const MESSAGE_PITCH = 8;
|
|
10
|
+
const pointsOf = (routes) => [...routes.values()].flat();
|
|
11
|
+
const clamp = (value, low, high) => Math.min(high, Math.max(low, value));
|
|
12
|
+
const centerX = (box) => box.x + box.width / 2;
|
|
13
|
+
export const layoutCollaboration = (participants, messageFlows, existing) => {
|
|
14
|
+
const { fixed, obstacles, missingFlows } = existing;
|
|
15
|
+
const known = participants.map((participant) => fixed.get(participant.id)).filter((box) => box !== undefined);
|
|
16
|
+
const poolX = known.length > 0 ? Math.min(...known.map((box) => box.x)) : ORIGIN.x;
|
|
17
|
+
let cursor = Math.max(ORIGIN.y, ...known.map((box) => box.y + box.height + POOL_GAP));
|
|
18
|
+
const pooled = [];
|
|
19
|
+
const routes = new Map();
|
|
20
|
+
for (const participant of participants) {
|
|
21
|
+
const { model } = participant;
|
|
22
|
+
const fixedBox = fixed.get(participant.id);
|
|
23
|
+
const hasContent = model !== undefined && (model.nodes.length > 0 || participant.lanes !== undefined);
|
|
24
|
+
if (!hasContent) {
|
|
25
|
+
const pool = fixedBox ?? { x: poolX, y: cursor, width: 0, height: EMPTY_POOL_HEIGHT };
|
|
26
|
+
if (!fixedBox)
|
|
27
|
+
cursor += pool.height + POOL_GAP;
|
|
28
|
+
pooled.push({ participant, pool, fixedPool: fixedBox !== undefined, anchored: false, required: pool.width });
|
|
29
|
+
continue;
|
|
30
|
+
}
|
|
31
|
+
const anchored = allNodes(model).some((node) => fixed.has(node.id));
|
|
32
|
+
const left = fixedBox?.x ?? poolX;
|
|
33
|
+
const top = fixedBox?.y ?? cursor;
|
|
34
|
+
const laneLayout = participant.lanes ? buildLaneLayout(participant.lanes, model) : undefined;
|
|
35
|
+
const indent = POOL_HEADER * ((participant.unframed ? 0 : 1) + (laneLayout ? 1 + laneLayout.depth : 0)) + POOL_MARGIN;
|
|
36
|
+
const context = {
|
|
37
|
+
fixed,
|
|
38
|
+
obstacles,
|
|
39
|
+
missingFlows,
|
|
40
|
+
boxes: new Map(),
|
|
41
|
+
routes: new Map(),
|
|
42
|
+
lanes: new Map(),
|
|
43
|
+
arrange: laneLayout ? (scope, layering, layered, origin) => (scope === model ? arrangeLanes(laneLayout, scope, layering, layered, origin) : undefined) : undefined,
|
|
44
|
+
};
|
|
45
|
+
measure(model);
|
|
46
|
+
placeScope(model, { x: left + indent, y: laneLayout ? top : top + POOL_MARGIN }, context);
|
|
47
|
+
for (const [id, route] of context.routes)
|
|
48
|
+
routes.set(id, route);
|
|
49
|
+
const boxes = [...context.boxes.values()];
|
|
50
|
+
const points = pointsOf(context.routes);
|
|
51
|
+
const right = Math.max(...boxes.map((box) => box.x + box.width), ...points.map((p) => p.x), left + indent);
|
|
52
|
+
const lowest = Math.max(...boxes.map((box) => box.y + box.height), ...points.map((p) => p.y));
|
|
53
|
+
let pool;
|
|
54
|
+
if (fixedBox)
|
|
55
|
+
pool = fixedBox;
|
|
56
|
+
else {
|
|
57
|
+
const x = anchored ? Math.min(poolX, Math.min(...boxes.map((box) => box.x)) - indent) : poolX;
|
|
58
|
+
let y = top;
|
|
59
|
+
let bottom;
|
|
60
|
+
if (laneLayout && !anchored) {
|
|
61
|
+
extendLastBand(laneLayout, Math.max(...points.map((p) => p.y), ...boxes.map((box) => box.y + box.height), 0) + POOL_MARGIN / 2);
|
|
62
|
+
bottom = bandsBottom(laneLayout);
|
|
63
|
+
}
|
|
64
|
+
else {
|
|
65
|
+
if (anchored)
|
|
66
|
+
y = Math.min(...boxes.map((box) => box.y)) - POOL_MARGIN;
|
|
67
|
+
bottom = lowest + POOL_MARGIN;
|
|
68
|
+
}
|
|
69
|
+
pool = { x, y, width: right + POOL_MARGIN - x, height: Math.max(bottom - y, EMPTY_POOL_HEIGHT) };
|
|
70
|
+
cursor = Math.max(cursor, pool.y + pool.height + POOL_GAP);
|
|
71
|
+
}
|
|
72
|
+
pooled.push({ participant, pool, fixedPool: fixedBox !== undefined, anchored, required: pool.width, context, laneLayout });
|
|
73
|
+
}
|
|
74
|
+
const width = Math.max(0, ...pooled.filter((item) => !item.participant.unframed).map((item) => item.required));
|
|
75
|
+
for (const item of pooled)
|
|
76
|
+
if (!item.fixedPool && !item.participant.unframed)
|
|
77
|
+
item.pool.width = width;
|
|
78
|
+
const shapes = [];
|
|
79
|
+
const boxes = new Map();
|
|
80
|
+
const poolOf = new Map();
|
|
81
|
+
for (const item of pooled) {
|
|
82
|
+
const { participant, pool } = item;
|
|
83
|
+
if (!participant.unframed) {
|
|
84
|
+
shapes.push({ element: participant.id, kind: "pool", box: pool });
|
|
85
|
+
boxes.set(participant.id, pool);
|
|
86
|
+
poolOf.set(participant.id, pool);
|
|
87
|
+
}
|
|
88
|
+
if (item.laneLayout) {
|
|
89
|
+
const inset = participant.unframed ? 0 : POOL_HEADER;
|
|
90
|
+
const computed = item.anchored || item.fixedPool ? laneBoxesFromNodes(item.laneLayout, pool, item.context.boxes, inset) : laneBoxes(item.laneLayout, pool, inset);
|
|
91
|
+
for (const lane of item.laneLayout.lanes) {
|
|
92
|
+
const box = fixed.get(lane.id) ?? computed.get(lane.id);
|
|
93
|
+
shapes.push({ element: lane.id, kind: "lane", box });
|
|
94
|
+
boxes.set(lane.id, box);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
for (const [id, box] of item.context?.lanes ?? []) {
|
|
98
|
+
const placed = fixed.get(id) ?? box;
|
|
99
|
+
shapes.push({ element: id, kind: "lane", box: placed });
|
|
100
|
+
boxes.set(id, placed);
|
|
101
|
+
}
|
|
102
|
+
if (participant.model)
|
|
103
|
+
for (const node of allNodes(participant.model)) {
|
|
104
|
+
const box = item.context?.boxes.get(node.id);
|
|
105
|
+
if (!box)
|
|
106
|
+
continue;
|
|
107
|
+
shapes.push({ element: node.id, kind: "node", box, node });
|
|
108
|
+
boxes.set(node.id, box);
|
|
109
|
+
poolOf.set(node.id, pool);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
const edges = [];
|
|
113
|
+
for (const item of pooled)
|
|
114
|
+
if (item.participant.model)
|
|
115
|
+
for (const flow of allFlows(item.participant.model)) {
|
|
116
|
+
const route = routes.get(flow.id);
|
|
117
|
+
if (route)
|
|
118
|
+
edges.push({ element: flow.id, source: flow.source, target: flow.target, route });
|
|
119
|
+
}
|
|
120
|
+
const poolIds = new Set(participants.filter((participant) => !participant.unframed).map((participant) => participant.id));
|
|
121
|
+
const used = new Map();
|
|
122
|
+
for (const flow of messageFlows) {
|
|
123
|
+
const source = boxes.get(flow.source);
|
|
124
|
+
const target = boxes.get(flow.target);
|
|
125
|
+
if (!source || !target)
|
|
126
|
+
continue;
|
|
127
|
+
const sourcePool = poolOf.get(flow.source);
|
|
128
|
+
const targetPool = poolOf.get(flow.target);
|
|
129
|
+
const down = sourcePool === targetPool ? centerY(source) <= centerY(target) : sourcePool.y < targetPool.y;
|
|
130
|
+
const upper = down ? sourcePool : targetPool;
|
|
131
|
+
const index = used.get(upper) ?? 0;
|
|
132
|
+
used.set(upper, index + 1);
|
|
133
|
+
const within = (box, x) => clamp(x, box.x + POOL_HEADER, box.x + box.width);
|
|
134
|
+
const sourceIsPool = poolIds.has(flow.source);
|
|
135
|
+
const targetIsPool = poolIds.has(flow.target);
|
|
136
|
+
const preferred = !sourceIsPool ? centerX(source) : !targetIsPool ? centerX(target) : centerX(source);
|
|
137
|
+
const sx = sourceIsPool ? within(source, preferred) : centerX(source);
|
|
138
|
+
const tx = targetIsPool ? within(target, sx) : centerX(target);
|
|
139
|
+
const sy = down ? source.y + source.height : source.y;
|
|
140
|
+
const ty = down ? target.y : target.y + target.height;
|
|
141
|
+
let route;
|
|
142
|
+
if (sx === tx)
|
|
143
|
+
route = [{ x: sx, y: sy }, { x: tx, y: ty }];
|
|
144
|
+
else {
|
|
145
|
+
const middle = sourcePool === targetPool ? Math.round((sy + ty) / 2) : upper.y + upper.height + MESSAGE_CLEARANCE + index * MESSAGE_PITCH;
|
|
146
|
+
route = [{ x: sx, y: sy }, { x: sx, y: middle }, { x: tx, y: middle }, { x: tx, y: ty }];
|
|
147
|
+
}
|
|
148
|
+
edges.push({ element: flow.id, source: flow.source, target: flow.target, route });
|
|
149
|
+
}
|
|
150
|
+
return { shapes, edges };
|
|
151
|
+
};
|
|
152
|
+
const centerY = (box) => box.y + box.height / 2;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { Box, Json, Placement, Point, ProcessModel, FlowNode } from "./model.ts";
|
|
2
|
+
export interface PreviousIds {
|
|
3
|
+
diagram?: string;
|
|
4
|
+
plane?: string;
|
|
5
|
+
name?: unknown;
|
|
6
|
+
resolution?: unknown;
|
|
7
|
+
messageVisibleKinds: Map<string, unknown>;
|
|
8
|
+
shapes: Map<string, string>;
|
|
9
|
+
edges: Map<string, string>;
|
|
10
|
+
}
|
|
11
|
+
export declare const diagramProperties: (previous?: PreviousIds) => Json;
|
|
12
|
+
export declare const shapeElement: (id: string, node: FlowNode, box: Box) => Json;
|
|
13
|
+
export declare const containerElement: (id: string, element: string, box: Box) => Json;
|
|
14
|
+
export declare const edgeElement: (id: string, element: string, source: string | undefined, target: string | undefined, route: Point[], messageVisibleKind?: unknown) => Json;
|
|
15
|
+
export declare const buildDiagram: (model: ProcessModel, placement: Placement, ids: (preferred: string) => string, previous?: PreviousIds) => Json;
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { allFlows, allNodes } from "./model.js";
|
|
2
|
+
export const diagramProperties = (previous) => ({
|
|
3
|
+
...(previous?.name === undefined ? {} : { name: previous.name }),
|
|
4
|
+
...(previous?.resolution === undefined ? {} : { resolution: previous.resolution }),
|
|
5
|
+
});
|
|
6
|
+
export const shapeElement = (id, node, box) => ({
|
|
7
|
+
id,
|
|
8
|
+
type: "BPMNShape",
|
|
9
|
+
bpmnElement: node.id,
|
|
10
|
+
...(node.collapsed === undefined ? {} : { isExpanded: !node.collapsed }),
|
|
11
|
+
bounds: { type: "Bounds", x: box.x, y: box.y, width: box.width, height: box.height },
|
|
12
|
+
});
|
|
13
|
+
export const containerElement = (id, element, box) => ({
|
|
14
|
+
id,
|
|
15
|
+
type: "BPMNShape",
|
|
16
|
+
bpmnElement: element,
|
|
17
|
+
isHorizontal: true,
|
|
18
|
+
bounds: { type: "Bounds", x: box.x, y: box.y, width: box.width, height: box.height },
|
|
19
|
+
});
|
|
20
|
+
export const edgeElement = (id, element, source, target, route, messageVisibleKind) => ({
|
|
21
|
+
id,
|
|
22
|
+
type: "BPMNEdge",
|
|
23
|
+
bpmnElement: element,
|
|
24
|
+
...(messageVisibleKind === undefined ? {} : { messageVisibleKind }),
|
|
25
|
+
...(source === undefined ? {} : { sourceElement: source }),
|
|
26
|
+
...(target === undefined ? {} : { targetElement: target }),
|
|
27
|
+
waypoints: route.map((point) => ({ type: "Point", x: point.x, y: point.y })),
|
|
28
|
+
});
|
|
29
|
+
export const buildDiagram = (model, placement, ids, previous) => {
|
|
30
|
+
const shapeIds = new Map();
|
|
31
|
+
const planeElements = [];
|
|
32
|
+
for (const [lane, box] of placement.lanes ?? [])
|
|
33
|
+
planeElements.push(containerElement(ids(previous?.shapes.get(lane) ?? `Shape_${lane}`), lane, box));
|
|
34
|
+
for (const node of allNodes(model)) {
|
|
35
|
+
const id = ids(previous?.shapes.get(node.id) ?? `Shape_${node.id}`);
|
|
36
|
+
shapeIds.set(node.id, id);
|
|
37
|
+
planeElements.push(shapeElement(id, node, placement.boxes.get(node.id)));
|
|
38
|
+
}
|
|
39
|
+
for (const flow of allFlows(model)) {
|
|
40
|
+
const route = placement.routes.get(flow.id);
|
|
41
|
+
if (!route)
|
|
42
|
+
continue;
|
|
43
|
+
const id = ids(previous?.edges.get(flow.id) ?? `Edge_${flow.id}`);
|
|
44
|
+
planeElements.push(edgeElement(id, flow.id, flow.source && shapeIds.get(flow.source), flow.target && shapeIds.get(flow.target), route, previous?.messageVisibleKinds.get(flow.id)));
|
|
45
|
+
}
|
|
46
|
+
return {
|
|
47
|
+
id: ids(previous?.diagram ?? `Diagram_${model.id}`),
|
|
48
|
+
type: "BPMNDiagram",
|
|
49
|
+
...diagramProperties(previous),
|
|
50
|
+
plane: { id: ids(previous?.plane ?? `Plane_${model.id}`), type: "BPMNPlane", bpmnElement: model.id, planeElements },
|
|
51
|
+
};
|
|
52
|
+
};
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import { extractLanes, laneHooks } from "./lanes.js";
|
|
2
|
+
const EVENT_SIZE = { width: 36, height: 36 };
|
|
3
|
+
const GATEWAY_SIZE = { width: 50, height: 50 };
|
|
4
|
+
const ACTIVITY_SIZE = { width: 100, height: 80 };
|
|
5
|
+
const DATA_OBJECT_SIZE = { width: 36, height: 50 };
|
|
6
|
+
const DATA_STORE_SIZE = { width: 50, height: 50 };
|
|
7
|
+
const isSubProcess = (type) => type.endsWith("SubProcess") || type === "Transaction";
|
|
8
|
+
const sizeOf = (type) => {
|
|
9
|
+
if (type.endsWith("Event"))
|
|
10
|
+
return EVENT_SIZE;
|
|
11
|
+
if (type.endsWith("Gateway"))
|
|
12
|
+
return GATEWAY_SIZE;
|
|
13
|
+
if (type.endsWith("Task") || isSubProcess(type) || type === "CallActivity")
|
|
14
|
+
return ACTIVITY_SIZE;
|
|
15
|
+
return undefined;
|
|
16
|
+
};
|
|
17
|
+
const dataSizeOf = (type) => type === "DataObjectReference" ? DATA_OBJECT_SIZE : type === "DataStoreReference" ? DATA_STORE_SIZE : undefined;
|
|
18
|
+
const idsOf = (value) => [value].flat().filter((item) => typeof item === "string");
|
|
19
|
+
export const extractProcess = (container, collapsed = new Set()) => {
|
|
20
|
+
const nodes = [];
|
|
21
|
+
const flows = [];
|
|
22
|
+
const data = [];
|
|
23
|
+
const sequenceFlows = [];
|
|
24
|
+
const elements = [];
|
|
25
|
+
for (const element of container.flowElements ?? []) {
|
|
26
|
+
if (element.type === "SequenceFlow") {
|
|
27
|
+
sequenceFlows.push(element);
|
|
28
|
+
continue;
|
|
29
|
+
}
|
|
30
|
+
elements.push(element);
|
|
31
|
+
const dataSize = dataSizeOf(element.type);
|
|
32
|
+
if (dataSize) {
|
|
33
|
+
data.push({ id: element.id, type: element.type, size: { ...dataSize } });
|
|
34
|
+
continue;
|
|
35
|
+
}
|
|
36
|
+
const size = sizeOf(element.type);
|
|
37
|
+
if (!size)
|
|
38
|
+
continue;
|
|
39
|
+
const node = { id: element.id, type: element.type, size: { ...size } };
|
|
40
|
+
if (isSubProcess(element.type)) {
|
|
41
|
+
const content = extractProcess(element, collapsed);
|
|
42
|
+
node.collapsed = collapsed.has(element.id) || content.nodes.length === 0;
|
|
43
|
+
if (!node.collapsed) {
|
|
44
|
+
node.content = content;
|
|
45
|
+
const lanes = extractLanes(element);
|
|
46
|
+
if (lanes)
|
|
47
|
+
content.lanes = laneHooks(lanes, content);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
if (element.type === "BoundaryEvent" && typeof element.attachedToRef === "string")
|
|
51
|
+
node.attachedTo = element.attachedToRef;
|
|
52
|
+
nodes.push(node);
|
|
53
|
+
}
|
|
54
|
+
const known = new Set(nodes.map((node) => node.id));
|
|
55
|
+
for (const node of nodes)
|
|
56
|
+
if (node.attachedTo && (!known.has(node.attachedTo) || node.attachedTo === node.id))
|
|
57
|
+
delete node.attachedTo;
|
|
58
|
+
for (const node of nodes)
|
|
59
|
+
if (node.attachedTo && nodes.find((host) => host.id === node.attachedTo).attachedTo)
|
|
60
|
+
delete node.attachedTo;
|
|
61
|
+
const stubs = [];
|
|
62
|
+
for (const flow of sequenceFlows) {
|
|
63
|
+
const source = typeof flow.sourceRef === "string" && known.has(flow.sourceRef) ? flow.sourceRef : undefined;
|
|
64
|
+
const target = typeof flow.targetRef === "string" && known.has(flow.targetRef) ? flow.targetRef : undefined;
|
|
65
|
+
if (source && target)
|
|
66
|
+
flows.push({ id: flow.id, source, target });
|
|
67
|
+
else if (source || target)
|
|
68
|
+
stubs.push({ id: flow.id, source, target });
|
|
69
|
+
}
|
|
70
|
+
const dataIds = new Set(data.map((item) => item.id));
|
|
71
|
+
const associations = [];
|
|
72
|
+
for (const element of elements) {
|
|
73
|
+
if (!known.has(element.id))
|
|
74
|
+
continue;
|
|
75
|
+
for (const association of [element.dataInputAssociations ?? []].flat()) {
|
|
76
|
+
if (typeof association?.id !== "string")
|
|
77
|
+
continue;
|
|
78
|
+
const source = idsOf(association.sourceRef).find((id) => dataIds.has(id));
|
|
79
|
+
if (source)
|
|
80
|
+
associations.push({ id: association.id, source, target: element.id });
|
|
81
|
+
}
|
|
82
|
+
for (const association of [element.dataOutputAssociations ?? []].flat()) {
|
|
83
|
+
if (typeof association?.id !== "string")
|
|
84
|
+
continue;
|
|
85
|
+
const target = idsOf(association.targetRef).find((id) => dataIds.has(id));
|
|
86
|
+
if (target)
|
|
87
|
+
associations.push({ id: association.id, source: element.id, target });
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
for (const artifact of [container.artifacts ?? []].flat()) {
|
|
91
|
+
if (artifact?.type !== "Association" || typeof artifact.id !== "string")
|
|
92
|
+
continue;
|
|
93
|
+
const source = idsOf(artifact.sourceRef).find((id) => known.has(id));
|
|
94
|
+
const target = idsOf(artifact.targetRef).find((id) => known.has(id));
|
|
95
|
+
if (source && target)
|
|
96
|
+
associations.push({ id: artifact.id, source, target });
|
|
97
|
+
}
|
|
98
|
+
return { id: container.id, nodes, flows, data, stubs, associations };
|
|
99
|
+
};
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import type { Layering } from "./layering.ts";
|
|
2
|
+
import type { Box, Json, LaneHooks, Point, ProcessModel } from "./model.ts";
|
|
3
|
+
export declare const LANE_HEADER = 30;
|
|
4
|
+
export interface LaneNode {
|
|
5
|
+
id: string;
|
|
6
|
+
refs: string[];
|
|
7
|
+
children: LaneNode[];
|
|
8
|
+
depth: number;
|
|
9
|
+
first: number;
|
|
10
|
+
last: number;
|
|
11
|
+
}
|
|
12
|
+
export interface Band {
|
|
13
|
+
nodes: string[];
|
|
14
|
+
top: number;
|
|
15
|
+
height: number;
|
|
16
|
+
}
|
|
17
|
+
export interface LaneLayout {
|
|
18
|
+
lanes: LaneNode[];
|
|
19
|
+
bands: Band[];
|
|
20
|
+
depth: number;
|
|
21
|
+
}
|
|
22
|
+
export declare const extractLanes: (process: Json) => LaneNode[] | undefined;
|
|
23
|
+
export declare const flattenLanes: (lanes: LaneNode[]) => LaneNode[];
|
|
24
|
+
export declare const buildLaneLayout: (roots: LaneNode[], model: ProcessModel) => LaneLayout;
|
|
25
|
+
export declare const arrangeLanes: (layout: LaneLayout, model: ProcessModel, layering: Layering, layered: Map<string, Box>, origin: Point) => void;
|
|
26
|
+
export declare const extendLastBand: (layout: LaneLayout, bottom: number) => void;
|
|
27
|
+
export declare const bandsBottom: (layout: LaneLayout) => number;
|
|
28
|
+
export declare const laneBoxes: (layout: LaneLayout, pool: Box, inset?: number) => Map<string, Box>;
|
|
29
|
+
export declare const laneBoxesFromNodes: (layout: LaneLayout, pool: Box, boxes: Map<string, Box>, inset?: number) => Map<string, Box>;
|
|
30
|
+
export declare const laneHooks: (roots: LaneNode[], model: ProcessModel) => LaneHooks;
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import { boundaryBox, reserveBelow, ROW_GAP } from "./placement.js";
|
|
2
|
+
export const LANE_HEADER = 30;
|
|
3
|
+
const LANE_PAD = 20;
|
|
4
|
+
const MIN_BAND = 80;
|
|
5
|
+
const list = (value) => (Array.isArray(value) ? value : value && typeof value === "object" ? [value] : []).filter((item) => item && typeof item === "object");
|
|
6
|
+
const refsOf = (lane) => [lane.flowNodeRef].flat().filter((ref) => typeof ref === "string");
|
|
7
|
+
const parseLanes = (laneSet, depth) => list(laneSet.lanes)
|
|
8
|
+
.filter((lane) => typeof lane.id === "string")
|
|
9
|
+
.map((lane) => ({
|
|
10
|
+
id: lane.id,
|
|
11
|
+
refs: refsOf(lane),
|
|
12
|
+
children: list(lane.childLaneSet).flatMap((child) => parseLanes(child, depth + 1)),
|
|
13
|
+
depth,
|
|
14
|
+
first: 0,
|
|
15
|
+
last: 0,
|
|
16
|
+
}));
|
|
17
|
+
export const extractLanes = (process) => {
|
|
18
|
+
for (const laneSet of list(process.laneSets)) {
|
|
19
|
+
const lanes = parseLanes(laneSet, 0);
|
|
20
|
+
if (lanes.length > 0)
|
|
21
|
+
return lanes;
|
|
22
|
+
}
|
|
23
|
+
return undefined;
|
|
24
|
+
};
|
|
25
|
+
const descendantRefs = (lane) => new Set(lane.children.flatMap((child) => [...child.refs, ...descendantRefs(child)]));
|
|
26
|
+
export const flattenLanes = (lanes) => lanes.flatMap((lane) => [lane, ...flattenLanes(lane.children)]);
|
|
27
|
+
export const buildLaneLayout = (roots, model) => {
|
|
28
|
+
const known = new Set(model.nodes.filter((node) => !node.attachedTo).map((node) => node.id));
|
|
29
|
+
const claimed = new Set();
|
|
30
|
+
const bands = [];
|
|
31
|
+
const claim = (refs) => {
|
|
32
|
+
const mine = refs.filter((ref) => known.has(ref) && !claimed.has(ref));
|
|
33
|
+
mine.forEach((ref) => claimed.add(ref));
|
|
34
|
+
return mine;
|
|
35
|
+
};
|
|
36
|
+
const visit = (lane) => {
|
|
37
|
+
lane.first = bands.length;
|
|
38
|
+
if (lane.children.length === 0)
|
|
39
|
+
bands.push({ nodes: claim(lane.refs), top: 0, height: 0 });
|
|
40
|
+
else {
|
|
41
|
+
const below = descendantRefs(lane);
|
|
42
|
+
const own = claim(lane.refs.filter((ref) => !below.has(ref)));
|
|
43
|
+
if (own.length > 0)
|
|
44
|
+
bands.push({ nodes: own, top: 0, height: 0 });
|
|
45
|
+
lane.children.forEach(visit);
|
|
46
|
+
}
|
|
47
|
+
lane.last = Math.max(lane.first, bands.length - 1);
|
|
48
|
+
};
|
|
49
|
+
roots.forEach(visit);
|
|
50
|
+
const rest = model.nodes.filter((node) => !node.attachedTo && !claimed.has(node.id)).map((node) => node.id);
|
|
51
|
+
if (rest.length > 0)
|
|
52
|
+
bands.push({ nodes: rest, top: 0, height: 0 });
|
|
53
|
+
const all = flattenLanes(roots);
|
|
54
|
+
return { lanes: all, bands, depth: Math.max(0, ...all.map((lane) => lane.depth)) };
|
|
55
|
+
};
|
|
56
|
+
export const arrangeLanes = (layout, model, layering, layered, origin) => {
|
|
57
|
+
const sizes = new Map(model.nodes.map((node) => [node.id, node.size]));
|
|
58
|
+
const slot = (id) => sizes.get(id).height + reserveBelow(model, id);
|
|
59
|
+
const stacks = (band) => layering.layers.map((layer) => {
|
|
60
|
+
const inBand = layer.filter((id) => band.nodes.includes(id));
|
|
61
|
+
return { ids: inBand, height: inBand.reduce((sum, id) => sum + slot(id), 0) + ROW_GAP * Math.max(0, inBand.length - 1) };
|
|
62
|
+
});
|
|
63
|
+
let top = origin.y;
|
|
64
|
+
for (const band of layout.bands) {
|
|
65
|
+
const columns = stacks(band);
|
|
66
|
+
band.top = top;
|
|
67
|
+
band.height = Math.max(MIN_BAND, Math.max(0, ...columns.map((column) => column.height)) + 2 * LANE_PAD);
|
|
68
|
+
for (const column of columns) {
|
|
69
|
+
let y = top + (band.height - column.height) / 2;
|
|
70
|
+
for (const id of column.ids) {
|
|
71
|
+
layered.set(id, { ...layered.get(id), y });
|
|
72
|
+
y += slot(id) + ROW_GAP;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
top += band.height;
|
|
76
|
+
}
|
|
77
|
+
for (const node of model.nodes)
|
|
78
|
+
if (node.attachedTo)
|
|
79
|
+
layered.set(node.id, boundaryBox(model, node, layered.get(node.attachedTo)));
|
|
80
|
+
};
|
|
81
|
+
export const extendLastBand = (layout, bottom) => {
|
|
82
|
+
const last = layout.bands.at(-1);
|
|
83
|
+
if (last && last.top + last.height < bottom)
|
|
84
|
+
last.height = bottom - last.top;
|
|
85
|
+
};
|
|
86
|
+
export const bandsBottom = (layout) => {
|
|
87
|
+
const last = layout.bands.at(-1);
|
|
88
|
+
return last ? last.top + last.height : 0;
|
|
89
|
+
};
|
|
90
|
+
export const laneBoxes = (layout, pool, inset = LANE_HEADER) => new Map(layout.lanes.map((lane) => {
|
|
91
|
+
const top = layout.bands[lane.first].top;
|
|
92
|
+
const last = layout.bands[lane.last];
|
|
93
|
+
return [
|
|
94
|
+
lane.id,
|
|
95
|
+
{ x: pool.x + inset + LANE_HEADER * lane.depth, y: top, width: pool.width - inset - LANE_HEADER * lane.depth, height: last.top + last.height - top },
|
|
96
|
+
];
|
|
97
|
+
}));
|
|
98
|
+
export const laneBoxesFromNodes = (layout, pool, boxes, inset = LANE_HEADER) => new Map(layout.lanes.map((lane) => {
|
|
99
|
+
const nodes = [lane, ...flattenLanes(lane.children)].flatMap((item) => item.refs).map((ref) => boxes.get(ref)).filter((box) => box !== undefined);
|
|
100
|
+
const top = nodes.length > 0 ? Math.max(pool.y, Math.min(...nodes.map((box) => box.y)) - LANE_PAD) : pool.y;
|
|
101
|
+
const bottom = nodes.length > 0 ? Math.min(pool.y + pool.height, Math.max(...nodes.map((box) => box.y + box.height)) + LANE_PAD) : pool.y + Math.min(MIN_BAND, pool.height);
|
|
102
|
+
return [lane.id, { x: pool.x + inset + LANE_HEADER * lane.depth, y: top, width: pool.width - inset - LANE_HEADER * lane.depth, height: bottom - top }];
|
|
103
|
+
}));
|
|
104
|
+
export const laneHooks = (roots, model) => {
|
|
105
|
+
const layout = buildLaneLayout(roots, model);
|
|
106
|
+
return {
|
|
107
|
+
ids: layout.lanes.map((lane) => lane.id),
|
|
108
|
+
indent: LANE_HEADER * (layout.depth + 1) + LANE_PAD,
|
|
109
|
+
arrange: (layering, layered, origin) => arrangeLanes(layout, model, layering, layered, origin),
|
|
110
|
+
boxes: (frame, boxes, anchored) => (anchored ? laneBoxesFromNodes(layout, frame, boxes, 0) : laneBoxes(layout, frame, 0)),
|
|
111
|
+
};
|
|
112
|
+
};
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
const findBackFlows = (nodes, flows) => {
|
|
2
|
+
const outgoing = new Map();
|
|
3
|
+
for (const node of nodes)
|
|
4
|
+
outgoing.set(node.id, []);
|
|
5
|
+
for (const flow of flows)
|
|
6
|
+
outgoing.get(flow.source).push({ id: flow.id, target: flow.target });
|
|
7
|
+
const state = new Map();
|
|
8
|
+
const back = new Set();
|
|
9
|
+
const visit = (id) => {
|
|
10
|
+
state.set(id, "open");
|
|
11
|
+
for (const edge of outgoing.get(id)) {
|
|
12
|
+
const seen = state.get(edge.target);
|
|
13
|
+
if (seen === "open")
|
|
14
|
+
back.add(edge.id);
|
|
15
|
+
else if (seen === undefined)
|
|
16
|
+
visit(edge.target);
|
|
17
|
+
}
|
|
18
|
+
state.set(id, "done");
|
|
19
|
+
};
|
|
20
|
+
const incoming = new Set(flows.map((flow) => flow.target));
|
|
21
|
+
const starts = nodes.filter((node) => !incoming.has(node.id));
|
|
22
|
+
for (const node of [...starts, ...nodes])
|
|
23
|
+
if (!state.has(node.id))
|
|
24
|
+
visit(node.id);
|
|
25
|
+
return back;
|
|
26
|
+
};
|
|
27
|
+
export const assignLayers = (model) => {
|
|
28
|
+
const hostOf = new Map(model.nodes.map((node) => [node.id, node.attachedTo ?? node.id]));
|
|
29
|
+
const nodes = model.nodes.filter((node) => !node.attachedTo);
|
|
30
|
+
const flows = model.flows.map((flow) => ({ id: flow.id, source: hostOf.get(flow.source), target: hostOf.get(flow.target) }));
|
|
31
|
+
const backFlows = findBackFlows(nodes, flows);
|
|
32
|
+
const forward = flows.filter((flow) => !backFlows.has(flow.id));
|
|
33
|
+
const layerOf = new Map(nodes.map((node) => [node.id, 0]));
|
|
34
|
+
for (let pass = 0; pass < nodes.length; pass++) {
|
|
35
|
+
let changed = false;
|
|
36
|
+
for (const flow of forward) {
|
|
37
|
+
const wanted = layerOf.get(flow.source) + 1;
|
|
38
|
+
if (layerOf.get(flow.target) < wanted) {
|
|
39
|
+
layerOf.set(flow.target, wanted);
|
|
40
|
+
changed = true;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
if (!changed)
|
|
44
|
+
break;
|
|
45
|
+
}
|
|
46
|
+
const layers = [];
|
|
47
|
+
for (const node of nodes)
|
|
48
|
+
(layers[layerOf.get(node.id)] ??= []).push(node.id);
|
|
49
|
+
for (const node of model.nodes)
|
|
50
|
+
if (node.attachedTo)
|
|
51
|
+
layerOf.set(node.id, layerOf.get(node.attachedTo));
|
|
52
|
+
return { layers: layers.map((layer) => layer ?? []), layerOf, backFlows };
|
|
53
|
+
};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { type Json } from "./model.ts";
|
|
2
|
+
export interface LayoutResult {
|
|
3
|
+
document: Json;
|
|
4
|
+
}
|
|
5
|
+
export interface LayoutOptions {
|
|
6
|
+
force?: boolean;
|
|
7
|
+
}
|
|
8
|
+
export declare class LayoutInputError extends Error {
|
|
9
|
+
readonly errors: string[];
|
|
10
|
+
constructor(errors: string[]);
|
|
11
|
+
}
|
|
12
|
+
export declare const layoutDocument: (document: unknown, options?: LayoutOptions) => LayoutResult;
|