This is an automated email from the ASF dual-hosted git repository.
tiagobento pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/incubator-kie-tools.git
The following commit(s) were added to refs/heads/main by this push:
new 9469381265b NO-ISSUE: DMN Editor: Unable to "unhide" a hidden Decision
Service Node (#2371)
9469381265b is described below
commit 9469381265b1567e4758a41a2de6e1ab5a224776
Author: Kbowers <[email protected]>
AuthorDate: Tue Jun 4 00:03:22 2024 +0200
NO-ISSUE: DMN Editor: Unable to "unhide" a hidden Decision Service Node
(#2371)
Co-authored-by: Luiz Motta <[email protected]>
---
.../dmn-editor/src/autolayout/AutoLayoutHook.ts | 220 ---------
.../dmn-editor/src/autolayout/AutolayoutButton.tsx | 41 +-
.../{autoLayout.ts => autoLayoutInfo.ts} | 87 ++--
packages/dmn-editor/src/diagram/Diagram.tsx | 310 ++++++++----
.../dmn-editor/src/diagram/DiagramCommands.tsx | 56 ++-
packages/dmn-editor/src/diagram/nodes/Nodes.tsx | 8 +-
.../mutations/addExistingDecisionServiceToDrd.ts | 540 ++++++++++++++++-----
packages/dmn-editor/src/mutations/addOrGetDrd.ts | 4 +-
packages/dmn-editor/src/mutations/addShape.ts | 17 +-
.../src/mutations/applyAutoLayoutToDrd.ts | 225 +++++++++
packages/dmn-editor/src/mutations/deleteImport.ts | 14 +-
packages/dmn-editor/src/mutations/deleteNode.ts | 113 ++---
packages/dmn-editor/src/mutations/resizeNode.ts | 15 +-
.../mutations/updateDecisionServiceDividerLine.ts | 37 +-
...ainingDecisionServiceHrefsByDecisionHrefs.ts.ts | 12 +-
packages/dmn-editor/src/xml/xmlHrefs.ts | 21 +
16 files changed, 1122 insertions(+), 598 deletions(-)
diff --git a/packages/dmn-editor/src/autolayout/AutoLayoutHook.ts
b/packages/dmn-editor/src/autolayout/AutoLayoutHook.ts
deleted file mode 100644
index 1034b2dcced..00000000000
--- a/packages/dmn-editor/src/autolayout/AutoLayoutHook.ts
+++ /dev/null
@@ -1,220 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-import * as Elk from "elkjs/lib/elk.bundled.js";
-import { useCallback } from "react";
-import { PositionalNodeHandleId } from
"../diagram/connections/PositionalNodeHandles";
-import { EdgeType, NodeType } from "../diagram/connections/graphStructure";
-import { NODE_TYPES } from "../diagram/nodes/NodeTypes";
-import { addEdge } from "../mutations/addEdge";
-import { repositionNode } from "../mutations/repositionNode";
-import { resizeNode } from "../mutations/resizeNode";
-import { updateDecisionServiceDividerLine } from
"../mutations/updateDecisionServiceDividerLine";
-import { AutolayoutParentNode, FAKE_MARKER, visitNodeAndNested } from
"./autoLayout";
-import { State } from "../store/Store";
-import { DmnDiagramNodeData } from "../diagram/nodes/Nodes";
-import { DmnDiagramEdgeData } from "../diagram/edges/Edges";
-import { DMNDI15__DMNShape } from
"@kie-tools/dmn-marshaller/dist/schemas/dmn-1_5/ts-gen/types";
-import { XmlQName } from "@kie-tools/xml-parser-ts/dist/qNames";
-import * as RF from "reactflow";
-import { Normalized } from "../normalization/normalize";
-
-export function useAutoLayout() {
- return useCallback(
- ({
- s,
- autolayouted,
- parentNodesById,
- nodesById,
- edgesById,
- edges,
- dmnShapesByHref,
- }: {
- s: State;
- autolayouted: {
- isHorizontal: boolean;
- nodes: Elk.ElkNode[] | undefined;
- edges: Elk.ElkExtendedEdge[] | undefined;
- };
- parentNodesById: Map<string, AutolayoutParentNode>;
- nodesById: Map<string, RF.Node<DmnDiagramNodeData, string | undefined>>;
- edgesById: Map<string, RF.Edge<DmnDiagramEdgeData>>;
- edges: RF.Edge<DmnDiagramEdgeData>[];
- dmnShapesByHref: Map<
- string,
- Normalized<DMNDI15__DMNShape> & {
- index: number;
- dmnElementRefQName: XmlQName;
- }
- >;
- }) => {
- // 7. Update all nodes positions skipping empty groups, which will be
positioned manually after all nodes are done being repositioned.
- const autolayoutedElkNodesById = new Map<string, Elk.ElkNode>();
-
- for (const topLevelElkNode of autolayouted.nodes ?? []) {
- visitNodeAndNested(topLevelElkNode, { x: 100, y: 100 }, (elkNode,
positionOffset) => {
- if (elkNode.id.includes(FAKE_MARKER)) {
- return;
- }
-
- autolayoutedElkNodesById.set(elkNode.id, elkNode);
-
- const nodeId = elkNode.id;
- const node = nodesById.get(nodeId)!;
-
- repositionNode({
- definitions: s.dmn.model.definitions,
- drdIndex: s.computed(s).getDrdIndex(),
- controlWaypointsByEdge: new Map(),
- change: {
- nodeType: node.type as NodeType,
- type: "absolute",
- position: {
- x: elkNode.x! + positionOffset.x,
- y: elkNode.y! + positionOffset.y,
- },
- selectedEdges: [...edgesById.keys()],
- shapeIndex: node.data?.shape.index,
- sourceEdgeIndexes: edges.flatMap((e) =>
- e.source === nodeId && e.data?.dmnEdge ?
[e.data.dmnEdge.index] : []
- ),
- targetEdgeIndexes: edges.flatMap((e) =>
- e.target === nodeId && e.data?.dmnEdge ?
[e.data.dmnEdge.index] : []
- ),
- },
- });
- });
- }
-
- // 8. Resize all nodes using the sizes calculated by ELK.
- for (const topLevelElkNode of autolayouted.nodes ?? []) {
- visitNodeAndNested(topLevelElkNode, { x: 0, y: 0 }, (elkNode) => {
- if (elkNode.id.includes(FAKE_MARKER)) {
- return;
- }
-
- const nodeId = elkNode.id;
- const node = nodesById.get(nodeId)!;
-
- resizeNode({
- definitions: s.dmn.model.definitions,
- drdIndex: s.computed(s).getDrdIndex(),
- __readonly_dmnShapesByHref: dmnShapesByHref,
- snapGrid: s.diagram.snapGrid,
- change: {
- index: node.data.index,
- isExternal: !!node.data.dmnObjectQName.prefix,
- nodeType: node.type as NodeType,
- dimension: {
- "@_width": elkNode.width!,
- "@_height": elkNode.height!,
- },
- shapeIndex: node.data?.shape.index,
- sourceEdgeIndexes: edges.flatMap((e) =>
- e.source === nodeId && e.data?.dmnEdge ?
[e.data.dmnEdge.index] : []
- ),
- targetEdgeIndexes: edges.flatMap((e) =>
- e.target === nodeId && e.data?.dmnEdge ?
[e.data.dmnEdge.index] : []
- ),
- },
- });
- });
- }
-
- // 9. Updating Decision Service divider lines after all nodes are
repositioned and resized.
- for (const [parentNodeId] of parentNodesById) {
- const parentNode = nodesById.get(parentNodeId);
- if (parentNode?.type !== NODE_TYPES.decisionService) {
- continue;
- }
-
- const elkNode = autolayoutedElkNodesById.get(parentNodeId);
- if (!elkNode) {
- throw new Error(`Couldn't find Decision Service with id
${parentNode.id} at the autolayouted nodes map`);
- }
-
- /**
- * The second children of a Decision Service elkNode is a node
representing the Encapsulated section.
- * It's Y position will be exactly where the divider line should be.
- */
- const dividerLinerLocalYPosition = elkNode.children?.[1]?.y;
- if (!dividerLinerLocalYPosition) {
- throw new Error(
- `Couldn't find second child (which represents the Encapuslated
Decision section) of Decision Service with id ${parentNode.id} at the
autolayouted nodes map`
- );
- }
-
- updateDecisionServiceDividerLine({
- definitions: s.dmn.model.definitions,
- drdIndex: s.computed(s).getDrdIndex(),
- __readonly_dmnShapesByHref: dmnShapesByHref,
- drgElementIndex: parentNode.data.index,
- shapeIndex: parentNode.data.shape.index,
- snapGrid: s.diagram.snapGrid,
- localYPosition: dividerLinerLocalYPosition,
- });
- }
-
- // 10. Update the edges. Edges always go from top to bottom, removing
waypoints.
- for (const elkEdge of autolayouted.edges ?? []) {
- if (elkEdge.id.includes(FAKE_MARKER)) {
- continue;
- }
-
- const edge = edgesById.get(elkEdge.id)!;
-
- const sourceNode = nodesById.get(elkEdge.sources[0])!;
- const targetNode = nodesById.get(elkEdge.targets[0])!;
-
- // If the target is an external node, we don't have to create the edge.
- if (targetNode.data.dmnObjectQName.prefix) {
- continue;
- }
-
- addEdge({
- definitions: s.dmn.model.definitions,
- drdIndex: s.computed(s).getDrdIndex(),
- edge: {
- autoPositionedEdgeMarker: undefined,
- type: edge.type as EdgeType,
- targetHandle: PositionalNodeHandleId.Bottom,
- sourceHandle: PositionalNodeHandleId.Top,
- },
- sourceNode: {
- type: sourceNode.type as NodeType,
- href: sourceNode.id,
- data: sourceNode.data,
- bounds: sourceNode.data.shape["dc:Bounds"]!,
- shapeId: sourceNode.data.shape["@_id"],
- },
- targetNode: {
- type: targetNode.type as NodeType,
- href: targetNode.id,
- data: targetNode.data,
- bounds: targetNode.data.shape["dc:Bounds"]!,
- index: targetNode.data.index,
- shapeId: targetNode.data.shape["@_id"],
- },
- keepWaypoints: false,
- });
- }
- },
- []
- );
-}
diff --git a/packages/dmn-editor/src/autolayout/AutolayoutButton.tsx
b/packages/dmn-editor/src/autolayout/AutolayoutButton.tsx
index af4d703a8ab..c1a09f169eb 100644
--- a/packages/dmn-editor/src/autolayout/AutolayoutButton.tsx
+++ b/packages/dmn-editor/src/autolayout/AutolayoutButton.tsx
@@ -19,17 +19,15 @@
import * as React from "react";
import OptimizeIcon from "@patternfly/react-icons/dist/js/icons/optimize-icon";
-import { useAutoLayout } from "./AutoLayoutHook";
import { useDmnEditorStoreApi } from "../store/StoreContext";
-import { autoLayout } from "./autoLayout";
+import { getAutoLayoutedInfo } from "./autoLayoutInfo";
import { useExternalModels } from
"../includedModels/DmnEditorDependenciesContext";
+import { applyAutoLayoutToDrd } from "../mutations/applyAutoLayoutToDrd";
export function AutolayoutButton() {
const dmnEditorStoreApi = useDmnEditorStoreApi();
const { externalModelsByNamespace } = useExternalModels();
- const applyAutoLayout = useAutoLayout();
-
const onClick = React.useCallback(async () => {
const state = dmnEditorStoreApi.getState();
const snapGrid = state.diagram.snapGrid;
@@ -39,27 +37,30 @@ export function AutolayoutButton() {
const drgEdges =
state.computed(state).getDiagramData(externalModelsByNamespace).drgEdges;
const isAlternativeInputDataShape =
state.computed(state).isAlternativeInputDataShape();
- const { autolayouted, parentNodesById } = await autoLayout({
- snapGrid,
- nodesById,
- edgesById,
- nodes,
- drgEdges,
- isAlternativeInputDataShape,
+ const { __readonly_autoLayoutedInfo, __readonly_parentNodesById } = await
getAutoLayoutedInfo({
+ __readonly_snapGrid: snapGrid,
+ __readonly_nodesById: nodesById,
+ __readonly_edgesById: edgesById,
+ __readonly_nodes: nodes,
+ __readonly_drgEdges: drgEdges,
+ __readonly_isAlternativeInputDataShape: isAlternativeInputDataShape,
});
dmnEditorStoreApi.setState((s) => {
- applyAutoLayout({
- s,
- dmnShapesByHref: s.computed(s).indexedDrd().dmnShapesByHref,
- edges: s.computed(s).getDiagramData(externalModelsByNamespace).edges,
- edgesById:
s.computed(s).getDiagramData(externalModelsByNamespace).edgesById,
- nodesById:
s.computed(s).getDiagramData(externalModelsByNamespace).nodesById,
- autolayouted: autolayouted,
- parentNodesById: parentNodesById,
+ applyAutoLayoutToDrd({
+ state: s,
+ __readonly_dmnShapesByHref: s.computed(s).indexedDrd().dmnShapesByHref,
+ __readonly_edges:
s.computed(s).getDiagramData(externalModelsByNamespace).edges,
+ __readonly_edgesById:
s.computed(s).getDiagramData(externalModelsByNamespace).edgesById,
+ __readonly_nodesById:
s.computed(s).getDiagramData(externalModelsByNamespace).nodesById,
+ __readonly_autoLayoutedInfo,
+ __readonly_parentNodesById,
+ __readonly_drdIndex: s.computed(s).getDrdIndex(),
+ __readonly_dmnObjectNamespace: s.dmn.model.definitions["@_namespace"],
+ __readonly_externalDmnsIndex:
s.computed(s).getExternalModelTypesByNamespace(externalModelsByNamespace).dmns,
});
});
- }, [applyAutoLayout, dmnEditorStoreApi, externalModelsByNamespace]);
+ }, [dmnEditorStoreApi, externalModelsByNamespace]);
return (
<button className={"kie-dmn-editor--autolayout-panel-toggle-button"}
onClick={onClick} title={"Autolayout (beta)"}>
diff --git a/packages/dmn-editor/src/autolayout/autoLayout.ts
b/packages/dmn-editor/src/autolayout/autoLayoutInfo.ts
similarity index 84%
rename from packages/dmn-editor/src/autolayout/autoLayout.ts
rename to packages/dmn-editor/src/autolayout/autoLayoutInfo.ts
index ebb672d10c3..74cd1b79ba2 100644
--- a/packages/dmn-editor/src/autolayout/autoLayout.ts
+++ b/packages/dmn-editor/src/autolayout/autoLayoutInfo.ts
@@ -20,15 +20,16 @@
import { generateUuid } from "@kie-tools/boxed-expression-component/dist/api";
import { DC__Bounds } from
"@kie-tools/dmn-marshaller/dist/schemas/dmn-1_5/ts-gen/types";
import ELK, * as Elk from "elkjs/lib/elk.bundled.js";
+import { Edge, Node } from "reactflow";
import { NodeType } from "../diagram/connections/graphStructure";
+import { DmnDiagramEdgeData } from "../diagram/edges/Edges";
import { DrgEdge, getAdjMatrix, traverse } from "../diagram/graph/graph";
import { getContainmentRelationship } from "../diagram/maths/DmnMaths";
import { DEFAULT_NODE_SIZES, MIN_NODE_SIZES } from
"../diagram/nodes/DefaultSizes";
+import { DmnDiagramNodeData } from "../diagram/nodes/Nodes";
import { NODE_TYPES } from "../diagram/nodes/NodeTypes";
import { SnapGrid } from "../store/Store";
-import { DmnDiagramEdgeData } from "../diagram/edges/Edges";
-import { Edge, Node } from "reactflow";
-import { DmnDiagramNodeData } from "../diagram/nodes/Nodes";
+import { addNamespaceToHref, parseXmlHref } from "../xml/xmlHrefs";
const elk = new ELK();
@@ -77,20 +78,20 @@ export interface AutolayoutParentNode {
export const FAKE_MARKER = "__$FAKE$__";
-export async function autoLayout({
- snapGrid,
- nodesById,
- edgesById,
- nodes,
- drgEdges,
- isAlternativeInputDataShape,
+export async function getAutoLayoutedInfo({
+ __readonly_snapGrid,
+ __readonly_nodesById,
+ __readonly_edgesById,
+ __readonly_nodes,
+ __readonly_drgEdges,
+ __readonly_isAlternativeInputDataShape,
}: {
- snapGrid: SnapGrid;
- nodesById: Map<string, Node<DmnDiagramNodeData, string | undefined>>;
- edgesById: Map<string, Edge<DmnDiagramEdgeData>>;
- nodes: Node<DmnDiagramNodeData, string | undefined>[];
- drgEdges: DrgEdge[];
- isAlternativeInputDataShape: boolean;
+ __readonly_snapGrid: SnapGrid;
+ __readonly_nodesById: Map<string, Node<DmnDiagramNodeData, string |
undefined>>;
+ __readonly_edgesById: Map<string, Edge<DmnDiagramEdgeData>>;
+ __readonly_nodes: Node<DmnDiagramNodeData, string | undefined>[];
+ __readonly_drgEdges: DrgEdge[];
+ __readonly_isAlternativeInputDataShape: boolean;
}) {
const parentNodesById = new Map<string, AutolayoutParentNode>();
const nodeParentsById = new Map<string, Set<string>>();
@@ -101,21 +102,28 @@ export async function autoLayout({
*/
const fakeEdgesForElk = new Set<Elk.ElkExtendedEdge>();
- const adjMatrix = getAdjMatrix(drgEdges);
+ const adjMatrix = getAdjMatrix(__readonly_drgEdges);
// 1. First we populate the `parentNodesById` map so that we know exactly
what parent nodes we're dealing with. Decision Service nodes have two fake
nodes to represent Output and Encapsulated sections.
- for (const node of nodes) {
+ for (const node of __readonly_nodes) {
const dependencies = new Set<string>();
const dependents = new Set<string>();
if (node.data?.dmnObject?.__$$element === "decisionService") {
- const outputs = new Set([...(node.data.dmnObject.outputDecision ??
[]).map((s) => s["@_href"])]);
- const encapsulated = new
Set([...(node.data.dmnObject.encapsulatedDecision ?? []).map((s) =>
s["@_href"])]);
+ const { namespace } = parseXmlHref(node.id);
+ const outputs = new Set([
+ ...(node.data.dmnObject.outputDecision ?? []).map((s) =>
addNamespaceToHref({ href: s["@_href"], namespace })),
+ ]);
+ const encapsulated = new Set([
+ ...(node.data.dmnObject.encapsulatedDecision ?? []).map((s) =>
+ addNamespaceToHref({ href: s["@_href"], namespace })
+ ),
+ ]);
const idOfFakeNodeForOutputSection = `${node.id}${FAKE_MARKER}dsOutput`;
const idOfFakeNodeForEncapsulatedSection =
`${node.id}${FAKE_MARKER}dsEncapsulated`;
- const dsSize = MIN_NODE_SIZES[NODE_TYPES.decisionService]({ snapGrid });
+ const dsSize = MIN_NODE_SIZES[NODE_TYPES.decisionService]({ snapGrid:
__readonly_snapGrid });
parentNodesById.set(node.id, {
elkNode: {
id: node.id,
@@ -174,7 +182,7 @@ export async function autoLayout({
targets: [idOfFakeNodeForOutputSection],
});
} else if (node.data?.dmnObject?.__$$element === "group") {
- const groupSize = DEFAULT_NODE_SIZES[NODE_TYPES.group]({ snapGrid });
+ const groupSize = DEFAULT_NODE_SIZES[NODE_TYPES.group]({ snapGrid:
__readonly_snapGrid });
const groupBounds = node.data.shape["dc:Bounds"];
parentNodesById.set(node.id, {
decisionServiceSection: "n/a",
@@ -195,10 +203,10 @@ export async function autoLayout({
isInside: getContainmentRelationship({
bounds: bounds!,
container: groupBounds!,
- snapGrid,
- isAlternativeInputDataShape,
+ snapGrid: __readonly_snapGrid,
+ isAlternativeInputDataShape:
__readonly_isAlternativeInputDataShape,
containerMinSizes: MIN_NODE_SIZES[NODE_TYPES.group],
- boundsMinSizes: MIN_NODE_SIZES[nodesById.get(id)?.type as
NodeType],
+ boundsMinSizes: MIN_NODE_SIZES[__readonly_nodesById.get(id)?.type
as NodeType],
}).isInside,
decisionServiceSection: "n/a",
}),
@@ -209,13 +217,16 @@ export async function autoLayout({
}
// 2. Then we map all the nodes to elkNodes, including the parents. We
mutate parents on the fly when iterating over the nodes list.
- const elkNodes = nodes.flatMap((node) => {
+ const elkNodes = __readonly_nodes.flatMap((node) => {
const parent = parentNodesById.get(node.id);
if (parent) {
return [];
}
- const defaultSize = DEFAULT_NODE_SIZES[node.type as NodeType]({ snapGrid,
isAlternativeInputDataShape });
+ const defaultSize = DEFAULT_NODE_SIZES[node.type as NodeType]({
+ snapGrid: __readonly_snapGrid,
+ isAlternativeInputDataShape: __readonly_isAlternativeInputDataShape,
+ });
const elkNode: Elk.ElkNode = {
id: node.id,
width: node.data.shape["dc:Bounds"]?.["@_width"] ??
defaultSize["@_width"],
@@ -271,7 +282,7 @@ export async function autoLayout({
parentNode.dependents.add(n);
});
- const p = nodesById.get(parentNode.elkNode.id);
+ const p = __readonly_nodesById.get(parentNode.elkNode.id);
if (p?.type === NODE_TYPES.group && parentNode.elkNode.children?.length
=== 0) {
continue; // Ignore empty group nodes.
} else {
@@ -280,13 +291,13 @@ export async function autoLayout({
}
// 4. After we have all containment and hierarchical relationships defined,
we can add the fake edges so that ELK creates the structure correctly.
- for (const node of nodes) {
+ for (const node of __readonly_nodes) {
const parentNodes = [...parentNodesById.values()];
const dependents = parentNodes.filter((p) => p.hasDependencyTo({ id:
node.id }));
for (const dependent of dependents) {
// Not all nodes are present in all DRD
- if (nodesById.has(node.id) && nodesById.has(dependent.elkNode.id)) {
+ if (__readonly_nodesById.has(node.id) &&
__readonly_nodesById.has(dependent.elkNode.id)) {
fakeEdgesForElk.add({
id: `${generateUuid()}${FAKE_MARKER}__fake`,
sources: [node.id],
@@ -296,7 +307,7 @@ export async function autoLayout({
for (const p of nodeParentsById.get(node.id) ?? []) {
// Not all nodes are present in all DRD
- if (nodesById.has(p) && nodesById.has(dependent.elkNode.id)) {
+ if (__readonly_nodesById.has(p) &&
__readonly_nodesById.has(dependent.elkNode.id)) {
fakeEdgesForElk.add({
id: `${generateUuid()}${FAKE_MARKER}__fake`,
sources: [p],
@@ -309,7 +320,7 @@ export async function autoLayout({
const dependencies = parentNodes.filter((p) => p.isDependencyOf({ id:
node.id }));
for (const dependency of dependencies) {
// Not all nodes are present in all DRD
- if (nodesById.has(node.id) && nodesById.has(dependency.elkNode.id)) {
+ if (__readonly_nodesById.has(node.id) &&
__readonly_nodesById.has(dependency.elkNode.id)) {
fakeEdgesForElk.add({
id: `${generateUuid()}${FAKE_MARKER}__fake`,
sources: [dependency.elkNode.id],
@@ -319,7 +330,7 @@ export async function autoLayout({
for (const p of nodeParentsById.get(node.id) ?? []) {
// Not all nodes are present in all DRD
- if (nodesById.has(p) && nodesById.has(dependency.elkNode.id)) {
+ if (__readonly_nodesById.has(p) &&
__readonly_nodesById.has(dependency.elkNode.id)) {
fakeEdgesForElk.add({
id: `${generateUuid()}${FAKE_MARKER}__fake`,
sources: [dependency.elkNode.id],
@@ -333,9 +344,9 @@ export async function autoLayout({
// 5. Concatenate real and fake edges to pass to ELK.
const elkEdges = [
...fakeEdgesForElk,
- ...[...edgesById.values()].flatMap((e) => {
+ ...[...__readonly_edgesById.values()].flatMap((e) => {
// Not all nodes are present in all DRD
- if (nodesById.has(e.source) && nodesById.has(e.target)) {
+ if (__readonly_nodesById.has(e.source) &&
__readonly_nodesById.has(e.target)) {
return {
id: e.id,
sources: [e.source],
@@ -348,10 +359,10 @@ export async function autoLayout({
];
// 6. Run ELK.
- const autolayouted = await runElk(elkNodes, elkEdges, ELK_OPTIONS);
+ const autoLayoutedInfo = await runElk(elkNodes, elkEdges, ELK_OPTIONS);
return {
- autolayouted,
- parentNodesById,
+ __readonly_autoLayoutedInfo: autoLayoutedInfo,
+ __readonly_parentNodesById: parentNodesById,
};
}
diff --git a/packages/dmn-editor/src/diagram/Diagram.tsx
b/packages/dmn-editor/src/diagram/Diagram.tsx
index 5c47b7f4e2d..2081dbb94bd 100644
--- a/packages/dmn-editor/src/diagram/Diagram.tsx
+++ b/packages/dmn-editor/src/diagram/Diagram.tsx
@@ -26,6 +26,7 @@ import {
DC__Dimension,
DMN15__tDecisionService,
DMN15__tDefinitions,
+ DMNDI15__DMNDiagram,
} from "@kie-tools/dmn-marshaller/dist/schemas/dmn-1_5/ts-gen/types";
import { buildXmlQName } from "@kie-tools/xml-parser-ts/dist/qNames";
import { Button, ButtonVariant } from
"@patternfly/react-core/dist/js/components/Button";
@@ -112,16 +113,20 @@ import {
import { useExternalModels } from
"../includedModels/DmnEditorDependenciesContext";
import { xmlHrefToQName } from "../xml/xmlHrefToQName";
import {
+ addAutoGeneratedDecisionServiceToDrd,
+ addConflictingDecisionServiceToDrd,
+ StrategyForAddingDecisionServiceToDrd,
addExistingDecisionServiceToDrd,
+ getStrategyToAddExistingDecisionServiceToDrd,
getDecisionServicePropertiesRelativeToThisDmn,
} from "../mutations/addExistingDecisionServiceToDrd";
import { updateExpressionWidths } from "../mutations/updateExpressionWidths";
import { DiagramCommands } from "./DiagramCommands";
-import { autoLayout } from "../autolayout/autoLayout";
-import { useAutoLayout } from "../autolayout/AutoLayoutHook";
+import { getAutoLayoutedInfo } from "../autolayout/autoLayoutInfo";
import { autoGenerateDrd } from "../normalization/autoGenerateDrd";
import { Normalized, normalize } from "../normalization/normalize";
import OptimizeIcon from "@patternfly/react-icons/dist/js/icons/optimize-icon";
+import { applyAutoLayoutToDrd } from "../mutations/applyAutoLayoutToDrd";
const isFirefox = typeof (window as any).InstallTrigger !== "undefined"; //
See
https://stackoverflow.com/questions/9847580/how-to-detect-safari-chrome-ie-firefox-and-opera-browsers
@@ -289,7 +294,7 @@ export const Diagram = React.forwardRef<DiagramRef, {
container: React.RefObject
}, []);
const onDrop = useCallback(
- (e: React.DragEvent) => {
+ async (e: React.DragEvent) => {
e.preventDefault();
if (!container.current || !reactFlowInstance) {
@@ -341,50 +346,101 @@ export const Diagram = React.forwardRef<DiagramRef, {
container: React.RefObject
// --------- This is where we draw the line between the diagram and
the model.
- dmnEditorStoreApi.setState((state) => {
- const externalDmnsIndex = state
- .computed(state)
-
.getExternalModelTypesByNamespace(externalModelsByNamespace).dmns;
+ const state = dmnEditorStoreApi.getState();
+ const externalDmnsIndex = state
+ .computed(state)
+ .getExternalModelTypesByNamespace(externalModelsByNamespace).dmns;
- const externalNodeDmn =
externalDmnsIndex.get(externalNode.externalDrgElementNamespace);
- const externalDrgElement =
(externalNodeDmn?.model.definitions.drgElement ?? []).find(
- (e) => e["@_id"] === externalNode.externalDrgElementId
+ const externalNodeDmn =
externalDmnsIndex.get(externalNode.externalDrgElementNamespace);
+ const externalDrgElement =
(externalNodeDmn?.model.definitions.drgElement ?? []).find(
+ (e) => e["@_id"] === externalNode.externalDrgElementId
+ );
+ if (!externalNodeDmn || !externalDrgElement) {
+ throw new Error(
+ `Can't find DRG element with id
'${externalNode.externalDrgElementId}' on/or model with namespace
'${externalNode.externalDrgElementNamespace}'.`
);
- if (!externalNodeDmn || !externalDrgElement) {
- throw new Error(
- `Can't find DRG element with id
'${externalNode.externalDrgElementId}' on/or model with namespace
'${externalNode.externalDrgElementNamespace}'.`
- );
- }
+ }
- const externalNodeType =
getNodeTypeFromDmnObject(externalDrgElement)!;
+ const externalNodeType =
getNodeTypeFromDmnObject(externalDrgElement)!;
- const defaultExternalNodeDimensions =
DEFAULT_NODE_SIZES[externalNodeType]({
- snapGrid: state.diagram.snapGrid,
- isAlternativeInputDataShape:
state.computed(state).isAlternativeInputDataShape(),
- });
+ const defaultExternalNodeDimensions =
DEFAULT_NODE_SIZES[externalNodeType]({
+ snapGrid: state.diagram.snapGrid,
+ isAlternativeInputDataShape:
state.computed(state).isAlternativeInputDataShape(),
+ });
- const namespaceName = getXmlNamespaceDeclarationName({
- rootElement: state.dmn.model.definitions,
- namespace: externalNode.externalDrgElementNamespace,
- });
+ const externalNodeHref = buildXmlHref({
+ namespace: externalNode.externalDrgElementNamespace,
+ id: externalNode.externalDrgElementId,
+ });
- const externalNodeHref = buildXmlHref({
- namespace: externalNode.externalDrgElementNamespace,
- id: externalNode.externalDrgElementId,
+ if (externalDrgElement.__$$element === "decisionService") {
+ // Create a dereferencedState to enables us to edit the object
without changing the original state
+ // After we finish applying the changes it will be used to set the
new state.
+ const { computed, ...internalState } = state;
+ const dereferencedState: State = { computed,
...JSON.parse(JSON.stringify(internalState)) };
+ const drdIndex =
dereferencedState.computed(dereferencedState).getDrdIndex();
+
+ const {
+ strategyForAddingDecisionServiceToDrd,
+ indexedDrdContainingDecisionServiceDepiction,
+ decisionServiceHrefRelativeToThisDmn,
+ containedDecisionHrefsRelativeToThisDmn,
+ } = getStrategyToAddExistingDecisionServiceToDrd({
+ __readonly_definitions: dereferencedState.dmn.model.definitions,
+ __readonly_drgElement: externalDrgElement,
+ __readonly_decisionServiceNamespace:
externalNodeDmn.model.definitions["@_namespace"],
+ __readonly_drdIndex: drdIndex,
+ __readonly_externalDmnsIndex: externalDmnsIndex,
+ __readonly_indexedDrd:
dereferencedState.computed(dereferencedState).indexedDrd(),
+ __readonly_namespace:
dereferencedState.dmn.model.definitions["@_namespace"],
});
- if (externalDrgElement.__$$element === "decisionService") {
+ if (strategyForAddingDecisionServiceToDrd ===
StrategyForAddingDecisionServiceToDrd.CONFLICT) {
+ addConflictingDecisionServiceToDrd({
+ definitions: dereferencedState.dmn.model.definitions,
+ __readonly_drdIndex: drdIndex,
+ __readonly_dropPoint: dropPoint,
+ __readonly_decisionServiceHrefRelativeToThisDmn:
decisionServiceHrefRelativeToThisDmn,
+ });
+ } else if (strategyForAddingDecisionServiceToDrd ===
StrategyForAddingDecisionServiceToDrd.AUTO_GENERATE) {
+ await addAutoGeneratedDecisionServiceToDrd({
+ state: dereferencedState,
+ __readonly_decisionServiceNamespace:
externalNodeDmn.model.definitions["@_namespace"],
+ __readonly_drdIndex: drdIndex,
+ __readonly_externalDmnsIndex: externalDmnsIndex,
+ __readonly_containedDecisionHrefsRelativeToThisDmn:
containedDecisionHrefsRelativeToThisDmn,
+ __readonly_decisionServiceHrefRelativeToThisDmn:
decisionServiceHrefRelativeToThisDmn,
+ __readonly_snapGrid: dereferencedState.diagram.snapGrid,
+ __readonly_dropPoint: dropPoint,
+ __readonly_externalModelsByNamespace:
externalModelsByNamespace,
+ __readonly_isAlternativeInputDataShape: dereferencedState
+ .computed(dereferencedState)
+ .isAlternativeInputDataShape(),
+ });
+ } else if (
+ strategyForAddingDecisionServiceToDrd ===
StrategyForAddingDecisionServiceToDrd.COPY_FROM_ANOTHER_DRD
+ ) {
addExistingDecisionServiceToDrd({
- decisionService: externalDrgElement,
- decisionServiceNamespace:
externalNodeDmn.model.definitions["@_namespace"],
- drdIndex: state.computed(state).getDrdIndex(),
- dropPoint,
- externalDmnsIndex,
- thisDmnsDefinitions: state.dmn.model.definitions,
- thisDmnsIndexedDrd: state.computed(state).indexedDrd(),
- thisDmnsNamespace: state.dmn.model.definitions["@_namespace"],
+ definitions: dereferencedState.dmn.model.definitions,
+ __readonly_decisionServiceNamespace:
externalNodeDmn.model.definitions["@_namespace"],
+ __readonly_drdIndex: drdIndex,
+ __readonly_externalDmnsIndex: dereferencedState
+ .computed(dereferencedState)
+
.getExternalModelTypesByNamespace(externalModelsByNamespace).dmns,
+ __readonly_indexedDrd:
dereferencedState.computed(dereferencedState).indexedDrd(),
+ __readonly_indexedDrdContainingDecisionServiceDepiction:
indexedDrdContainingDecisionServiceDepiction!,
+ __readonly_containedDecisionHrefsRelativeToThisDmn:
containedDecisionHrefsRelativeToThisDmn,
+ __readonly_decisionServiceHrefRelativeToThisDmn:
decisionServiceHrefRelativeToThisDmn,
+ __readonly_dropPoint: dropPoint,
+ __readonly_namespace:
dereferencedState.dmn.model.definitions["@_namespace"],
});
- } else {
+ }
+ dmnEditorStoreApi.setState((state) => {
+ state.dmn.model =
JSON.parse(JSON.stringify(dereferencedState.dmn.model));
+ state.diagram._selectedNodes = [externalNodeHref];
+ });
+ } else {
+ dmnEditorStoreApi.setState((state) => {
const externalNodeType =
getNodeTypeFromDmnObject(externalDrgElement)!;
addShape({
definitions: state.dmn.model.definitions,
@@ -401,41 +457,95 @@ export const Diagram = React.forwardRef<DiagramRef, {
container: React.RefObject
},
},
});
- }
- state.diagram._selectedNodes = [externalNodeHref];
- });
-
+ state.diagram._selectedNodes = [externalNodeHref];
+ });
+ }
console.debug(`DMN DIAGRAM: Adding external node`,
JSON.stringify(externalNode));
} else if (e.dataTransfer.getData(MIME_TYPE_FOR_DMN_EDITOR_DRG_NODE)) {
const drgElement =
JSON.parse(e.dataTransfer.getData(MIME_TYPE_FOR_DMN_EDITOR_DRG_NODE)) as
Unpacked<
Normalized<DMN15__tDefinitions>["drgElement"]
>;
- dmnEditorStoreApi.setState((state) => {
- const nodeType = getNodeTypeFromDmnObject(drgElement);
- if (nodeType === undefined) {
- throw new Error("DMN DIAGRAM: It wasn't possible to determine
the node type");
- }
+ const nodeType = getNodeTypeFromDmnObject(drgElement);
+ if (nodeType === undefined) {
+ throw new Error("DMN DIAGRAM: It wasn't possible to determine the
node type");
+ }
- const defaultNodeDimensions = DEFAULT_NODE_SIZES[nodeType]({
- snapGrid: state.diagram.snapGrid,
- isAlternativeInputDataShape:
state.computed(state).isAlternativeInputDataShape(),
- });
+ if (drgElement.__$$element === "decisionService") {
+ const { computed, ...state } = dmnEditorStoreApi.getState();
+ const dereferencedState: State = { computed,
...JSON.parse(JSON.stringify(state)) };
+
+ const drdIndex =
dereferencedState.computed(dereferencedState).getDrdIndex();
+ const externalDmnsIndex = dereferencedState
+ .computed(dereferencedState)
+
.getExternalModelTypesByNamespace(externalModelsByNamespace).dmns;
- if (drgElement.__$$element === "decisionService") {
+ const {
+ strategyForAddingDecisionServiceToDrd,
+ indexedDrdContainingDecisionServiceDepiction,
+ decisionServiceHrefRelativeToThisDmn,
+ containedDecisionHrefsRelativeToThisDmn,
+ } = getStrategyToAddExistingDecisionServiceToDrd({
+ __readonly_definitions: dereferencedState.dmn.model.definitions,
+ __readonly_drgElement: drgElement,
+ __readonly_decisionServiceNamespace:
dereferencedState.dmn.model.definitions["@_namespace"],
+ __readonly_drdIndex: drdIndex,
+ __readonly_externalDmnsIndex: externalDmnsIndex,
+ __readonly_indexedDrd:
dereferencedState.computed(dereferencedState).indexedDrd(),
+ __readonly_namespace:
dereferencedState.dmn.model.definitions["@_namespace"],
+ });
+ if (strategyForAddingDecisionServiceToDrd ===
StrategyForAddingDecisionServiceToDrd.CONFLICT) {
+ addConflictingDecisionServiceToDrd({
+ definitions: dereferencedState.dmn.model.definitions,
+ __readonly_drdIndex: drdIndex,
+ __readonly_dropPoint: dropPoint,
+ __readonly_decisionServiceHrefRelativeToThisDmn:
decisionServiceHrefRelativeToThisDmn,
+ });
+ } else if (strategyForAddingDecisionServiceToDrd ===
StrategyForAddingDecisionServiceToDrd.AUTO_GENERATE) {
+ await addAutoGeneratedDecisionServiceToDrd({
+ state: dereferencedState,
+ __readonly_decisionServiceNamespace:
dereferencedState.dmn.model.definitions["@_namespace"],
+ __readonly_drdIndex: drdIndex,
+ __readonly_externalDmnsIndex: externalDmnsIndex,
+ __readonly_containedDecisionHrefsRelativeToThisDmn:
containedDecisionHrefsRelativeToThisDmn,
+ __readonly_decisionServiceHrefRelativeToThisDmn:
decisionServiceHrefRelativeToThisDmn,
+ __readonly_snapGrid: dereferencedState.diagram.snapGrid,
+ __readonly_dropPoint: dropPoint,
+ __readonly_externalModelsByNamespace:
externalModelsByNamespace,
+ __readonly_isAlternativeInputDataShape: dereferencedState
+ .computed(dereferencedState)
+ .isAlternativeInputDataShape(),
+ });
+ } else if (
+ strategyForAddingDecisionServiceToDrd ===
StrategyForAddingDecisionServiceToDrd.COPY_FROM_ANOTHER_DRD
+ ) {
addExistingDecisionServiceToDrd({
- decisionService: drgElement,
- decisionServiceNamespace:
state.dmn.model.definitions["@_namespace"],
- drdIndex: state.computed(state).getDrdIndex(),
- dropPoint,
- externalDmnsIndex:
state.computed(state).getExternalModelTypesByNamespace(externalModelsByNamespace)
- .dmns,
- thisDmnsDefinitions: state.dmn.model.definitions,
- thisDmnsIndexedDrd: state.computed(state).indexedDrd(),
- thisDmnsNamespace: state.dmn.model.definitions["@_namespace"],
+ definitions: dereferencedState.dmn.model.definitions,
+ __readonly_decisionServiceNamespace:
dereferencedState.dmn.model.definitions["@_namespace"],
+ __readonly_drdIndex: drdIndex,
+ __readonly_externalDmnsIndex: dereferencedState
+ .computed(dereferencedState)
+
.getExternalModelTypesByNamespace(externalModelsByNamespace).dmns,
+ __readonly_indexedDrd:
dereferencedState.computed(dereferencedState).indexedDrd(),
+ __readonly_indexedDrdContainingDecisionServiceDepiction:
indexedDrdContainingDecisionServiceDepiction!,
+ __readonly_containedDecisionHrefsRelativeToThisDmn:
containedDecisionHrefsRelativeToThisDmn,
+ __readonly_decisionServiceHrefRelativeToThisDmn:
decisionServiceHrefRelativeToThisDmn,
+ __readonly_dropPoint: dropPoint,
+ __readonly_namespace:
dereferencedState.dmn.model.definitions["@_namespace"],
});
- } else {
+ }
+ dmnEditorStoreApi.setState((state) => {
+ state.dmn.model =
JSON.parse(JSON.stringify(dereferencedState.dmn.model));
+ });
+ } else {
+ dmnEditorStoreApi.setState((state) => {
const nodeType = getNodeTypeFromDmnObject(drgElement)!;
+
+ const defaultNodeDimensions = DEFAULT_NODE_SIZES[nodeType]({
+ snapGrid: state.diagram.snapGrid,
+ isAlternativeInputDataShape:
state.computed(state).isAlternativeInputDataShape(),
+ });
+
addShape({
definitions: state.dmn.model.definitions,
drdIndex: state.computed(state).getDrdIndex(),
@@ -455,13 +565,12 @@ export const Diagram = React.forwardRef<DiagramRef, {
container: React.RefObject
},
},
});
- }
- });
-
+ });
+ }
console.debug(`DMN DIAGRAM: Adding DRG node`,
JSON.stringify(drgElement));
}
},
- [container, reactFlowInstance, dmnEditorStoreApi,
externalModelsByNamespace]
+ [container, dmnEditorStoreApi, externalModelsByNamespace,
reactFlowInstance]
);
const ongoingConnection = useDmnEditorStore((s) =>
s.diagram.ongoingConnection);
@@ -643,6 +752,10 @@ export const Diagram = React.forwardRef<DiagramRef, {
container: React.RefObject
drdIndex: state.computed(state).getDrdIndex(),
__readonly_dmnShapesByHref:
state.computed(state).indexedDrd().dmnShapesByHref,
snapGrid: state.diagram.snapGrid,
+ __readonly_dmnObjectNamespace:
node.data.dmnObjectNamespace,
+ __readonly_externalDmnsIndex: state
+ .computed(state)
+
.getExternalModelTypesByNamespace(externalModelsByNamespace).dmns,
change: {
isExternal: !!node.data.dmnObjectQName.prefix,
nodeType: node.type as NodeType,
@@ -717,7 +830,11 @@ export const Diagram = React.forwardRef<DiagramRef, {
container: React.RefObject
for (let i = 0; i <
containedDecisionHrefsRelativeToThisDmn.length; i++) {
const diagramData =
state.computed(state).getDiagramData(externalModelsByNamespace);
- const nestedNode =
diagramData.nodesById.get(containedDecisionHrefsRelativeToThisDmn[i])!;
+ const nestedNode =
diagramData.nodesById.get(containedDecisionHrefsRelativeToThisDmn[i]);
+ if (nestedNode?.data?.shape === undefined) {
+ // In case we have an incomple depiction of the
decision service in the current DRD
+ continue;
+ }
const snappedNestedNodeShapeWithAppliedDelta =
snapShapePosition(
state.diagram.snapGrid,
offsetShapePosition(nestedNode.data.shape, delta)
@@ -748,16 +865,18 @@ export const Diagram = React.forwardRef<DiagramRef, {
container: React.RefObject
console.debug(`DMN DIAGRAM: 'onNodesChange' --> remove
'${change.id}'`);
const node =
state.computed(state).getDiagramData(externalModelsByNamespace).nodesById.get(change.id)!;
deleteNode({
- drgEdges:
state.computed(state).getDiagramData(externalModelsByNamespace).drgEdges,
definitions: state.dmn.model.definitions,
- drdIndex: state.computed(state).getDrdIndex(),
- dmnObjectNamespace: node.data.dmnObjectNamespace ??
state.dmn.model.definitions["@_namespace"],
- dmnObjectQName: node.data.dmnObjectQName,
- dmnObjectId: node.data.dmnObject?.["@_id"],
- nodeNature: nodeNatures[node.type as NodeType],
+ __readonly_drgEdges:
state.computed(state).getDiagramData(externalModelsByNamespace).drgEdges,
+ __readonly_drdIndex: state.computed(state).getDrdIndex(),
+ __readonly_dmnObjectNamespace:
+ node.data.dmnObjectNamespace ??
state.dmn.model.definitions["@_namespace"],
+ __readonly_dmnObjectQName: node.data.dmnObjectQName,
+ __readonly_dmnObjectId: node.data.dmnObject?.["@_id"],
+ __readonly_nodeNature: nodeNatures[node.type as NodeType],
mode: NodeDeletionMode.FROM_DRG_AND_ALL_DRDS,
- externalDmnsIndex:
state.computed(state).getExternalModelTypesByNamespace(externalModelsByNamespace)
- .dmns,
+ __readonly_externalModelTypesByNamespace: state
+ .computed(state)
+
.getExternalModelTypesByNamespace(externalModelsByNamespace),
});
state.dispatch(state).diagram.setNodeStatus(node.id, {
selected: false,
@@ -1126,10 +1245,15 @@ export const Diagram = React.forwardRef<DiagramRef, {
container: React.RefObject
showEmptyState && nodes.length === 0 &&
drgElementsWithoutVisualRepresentationOnCurrentDrdLength === 0;
const canAutoGenerateDrd = useDmnEditorStore((s) =>
s.diagram.autoLayout.canAutoGenerateDrd);
+ const drdsLength = useDmnEditorStore((s) =>
s.dmn.model.definitions["dmndi:DMNDI"]?.["dmndi:DMNDiagram"]?.length);
+
+ const showAutoGenerateDrdModal = useMemo(() => {
+ return canAutoGenerateDrd && drdsLength === undefined;
+ }, [canAutoGenerateDrd, drdsLength]);
return (
<>
- {nodes.length === 0 && canAutoGenerateDrd && <DmnDiagramWithoutDrd />}
+ {showAutoGenerateDrdModal && <DmnDiagramWithoutDrd />}
{isEmptyStateShowing && !canAutoGenerateDrd && <DmnDiagramEmptyState
setShowEmptyState={setShowEmptyState} />}
<DiagramContainerContextProvider container={container}>
<svg style={{ position: "absolute", top: 0, left: 0 }}>
@@ -1201,7 +1325,6 @@ export const Diagram = React.forwardRef<DiagramRef, {
container: React.RefObject
function DmnDiagramWithoutDrd() {
const dmnEditorStoreApi = useDmnEditorStoreApi();
const { externalModelsByNamespace } = useExternalModels();
- const applyAutoLayout = useAutoLayout();
return (
<Bullseye
@@ -1281,25 +1404,30 @@ function DmnDiagramWithoutDrd() {
const dmnShapesByHref =
dereferencedState.computed(dereferencedState).indexedDrd().dmnShapesByHref;
// Auto layout the new DRD
- const { autolayouted, parentNodesById } = await autoLayout({
- snapGrid,
- nodesById,
- edgesById,
- nodes,
- drgEdges,
- isAlternativeInputDataShape,
+ const { __readonly_autoLayoutedInfo,
__readonly_parentNodesById } = await getAutoLayoutedInfo({
+ __readonly_snapGrid: snapGrid,
+ __readonly_nodesById: nodesById,
+ __readonly_edgesById: edgesById,
+ __readonly_nodes: nodes,
+ __readonly_drgEdges: drgEdges,
+ __readonly_isAlternativeInputDataShape:
isAlternativeInputDataShape,
});
dmnEditorStoreApi.setState((s) => {
s.diagram.autoLayout.canAutoGenerateDrd = false;
- applyAutoLayout({
- s: dereferencedState,
- dmnShapesByHref,
- edges: edges,
- edgesById: edgesById,
- nodesById: nodesById,
- autolayouted: autolayouted,
- parentNodesById: parentNodesById,
+ applyAutoLayoutToDrd({
+ state: dereferencedState,
+ __readonly_dmnShapesByHref: dmnShapesByHref,
+ __readonly_edges: edges,
+ __readonly_edgesById: edgesById,
+ __readonly_nodesById: nodesById,
+ __readonly_autoLayoutedInfo,
+ __readonly_parentNodesById,
+ __readonly_drdIndex:
dereferencedState.computed(dereferencedState).getDrdIndex(),
+ __readonly_dmnObjectNamespace:
dereferencedState.dmn.model.definitions["@_namespace"],
+ __readonly_externalDmnsIndex: dereferencedState
+ .computed(dereferencedState)
+
.getExternalModelTypesByNamespace(externalModelsByNamespace).dmns,
});
s.dmn.model = dereferencedState.dmn.model;
});
diff --git a/packages/dmn-editor/src/diagram/DiagramCommands.tsx
b/packages/dmn-editor/src/diagram/DiagramCommands.tsx
index cabb6654d24..af73112a7e5 100644
--- a/packages/dmn-editor/src/diagram/DiagramCommands.tsx
+++ b/packages/dmn-editor/src/diagram/DiagramCommands.tsx
@@ -148,16 +148,18 @@ export function DiagramCommands(props: {}) {
.forEach((node: RF.Node<DmnDiagramNodeData>) => {
if (copiedNodesById.has(node.id)) {
deleteNode({
- drgEdges:
state.computed(state).getDiagramData(externalModelsByNamespace).drgEdges,
+ __readonly_drgEdges:
state.computed(state).getDiagramData(externalModelsByNamespace).drgEdges,
definitions: state.dmn.model.definitions,
- drdIndex: state.computed(state).getDrdIndex(),
- dmnObjectNamespace: node.data.dmnObjectNamespace ??
state.dmn.model.definitions["@_namespace"],
- dmnObjectQName: node.data.dmnObjectQName,
- dmnObjectId: node.data.dmnObject?.["@_id"],
- nodeNature: nodeNatures[node.type as NodeType],
+ __readonly_drdIndex: state.computed(state).getDrdIndex(),
+ __readonly_dmnObjectNamespace:
+ node.data.dmnObjectNamespace ??
state.dmn.model.definitions["@_namespace"],
+ __readonly_dmnObjectQName: node.data.dmnObjectQName,
+ __readonly_dmnObjectId: node.data.dmnObject?.["@_id"],
+ __readonly_nodeNature: nodeNatures[node.type as NodeType],
mode: NodeDeletionMode.FROM_DRG_AND_ALL_DRDS,
- externalDmnsIndex:
state.computed(state).getExternalModelTypesByNamespace(externalModelsByNamespace)
- .dmns,
+ __readonly_externalModelTypesByNamespace: state
+ .computed(state)
+
.getExternalModelTypesByNamespace(externalModelsByNamespace),
});
state.dispatch(state).diagram.setNodeStatus(node.id, {
selected: false,
@@ -365,23 +367,25 @@ export function DiagramCommands(props: {}) {
if (
(selectedNodeIds.has(edge.source) &&
canRemoveNodeFromDrdOnly({
- externalDmnsIndex:
state.computed(state).getExternalModelTypesByNamespace(externalModelsByNamespace)
- .dmns,
+ __readonly_externalDmnsIndex: state
+ .computed(state)
+
.getExternalModelTypesByNamespace(externalModelsByNamespace).dmns,
definitions: state.dmn.model.definitions,
- drdIndex: state.computed(state).getDrdIndex(),
- dmnObjectNamespace:
+ __readonly_drdIndex: state.computed(state).getDrdIndex(),
+ __readonly_dmnObjectNamespace:
nodesById.get(edge.source)!.data.dmnObjectNamespace ??
state.dmn.model.definitions["@_namespace"],
- dmnObjectId:
nodesById.get(edge.source)!.data.dmnObject?.["@_id"],
+ __readonly_dmnObjectId:
nodesById.get(edge.source)!.data.dmnObject?.["@_id"],
})) ||
(selectedNodeIds.has(edge.target) &&
canRemoveNodeFromDrdOnly({
- externalDmnsIndex:
state.computed(state).getExternalModelTypesByNamespace(externalModelsByNamespace)
- .dmns,
+ __readonly_externalDmnsIndex: state
+ .computed(state)
+
.getExternalModelTypesByNamespace(externalModelsByNamespace).dmns,
definitions: state.dmn.model.definitions,
- drdIndex: state.computed(state).getDrdIndex(),
- dmnObjectNamespace:
+ __readonly_drdIndex: state.computed(state).getDrdIndex(),
+ __readonly_dmnObjectNamespace:
nodesById.get(edge.target)!.data.dmnObjectNamespace ??
state.dmn.model.definitions["@_namespace"],
- dmnObjectId:
nodesById.get(edge.target)!.data.dmnObject?.["@_id"],
+ __readonly_dmnObjectId:
nodesById.get(edge.target)!.data.dmnObject?.["@_id"],
}))
) {
deleteEdge({
@@ -400,14 +404,16 @@ export function DiagramCommands(props: {}) {
continue;
}
const { deletedDmnShapeOnCurrentDrd: deletedShape } = deleteNode({
- drgEdges: [], // Deleting from DRD only.
definitions: state.dmn.model.definitions,
- externalDmnsIndex:
state.computed(state).getExternalModelTypesByNamespace(externalModelsByNamespace).dmns,
- drdIndex: state.computed(state).getDrdIndex(),
- dmnObjectNamespace: node.data.dmnObjectNamespace ??
state.dmn.model.definitions["@_namespace"],
- dmnObjectQName: node.data.dmnObjectQName,
- dmnObjectId: node.data.dmnObject?.["@_id"],
- nodeNature: nodeNatures[node.type as NodeType],
+ __readonly_drgEdges: [], // Deleting from DRD only.
+ __readonly_externalModelTypesByNamespace: state
+ .computed(state)
+ .getExternalModelTypesByNamespace(externalModelsByNamespace),
+ __readonly_drdIndex: state.computed(state).getDrdIndex(),
+ __readonly_dmnObjectNamespace: node.data.dmnObjectNamespace ??
state.dmn.model.definitions["@_namespace"],
+ __readonly_dmnObjectQName: node.data.dmnObjectQName,
+ __readonly_dmnObjectId: node.data.dmnObject?.["@_id"],
+ __readonly_nodeNature: nodeNatures[node.type as NodeType],
mode: NodeDeletionMode.FROM_CURRENT_DRD_ONLY,
});
diff --git a/packages/dmn-editor/src/diagram/nodes/Nodes.tsx
b/packages/dmn-editor/src/diagram/nodes/Nodes.tsx
index b95c96b4bd5..724e8a7a4a0 100644
--- a/packages/dmn-editor/src/diagram/nodes/Nodes.tsx
+++ b/packages/dmn-editor/src/diagram/nodes/Nodes.tsx
@@ -869,7 +869,7 @@ export const DecisionServiceNode = React.memo(
const ref = useRef<SVGRectElement>(null);
const isExternal = !!dmnObjectQName.prefix;
-
+ const { externalModelsByNamespace } = useExternalModels();
const snapGrid = useDmnEditorStore((s) => s.diagram.snapGrid);
const enableCustomNodeStyles = useDmnEditorStore((s) =>
s.diagram.overlays.enableCustomNodeStyles);
const isHovered = useIsHovered(ref);
@@ -959,6 +959,10 @@ export const DecisionServiceNode = React.memo(
drdIndex: state.computed(state).getDrdIndex(),
__readonly_dmnShapesByHref:
state.computed(state).indexedDrd().dmnShapesByHref,
drgElementIndex: index,
+ __readonly_dmnObjectNamespace: dmnObjectNamespace,
+ __readonly_externalDmnsIndex: state
+ .computed(state)
+
.getExternalModelTypesByNamespace(externalModelsByNamespace).dmns,
shapeIndex: shape.index,
localYPosition: e.y,
snapGrid: state.diagram.snapGrid,
@@ -975,7 +979,7 @@ export const DecisionServiceNode = React.memo(
return () => {
selection.on(".drag", null);
};
- }, [decisionService, dmnEditorStoreApi, id, index, shape.index]);
+ }, [decisionService, dmnEditorStoreApi, dmnObjectNamespace,
externalModelsByNamespace, id, index, shape.index]);
const { fontCssProperties, shapeStyle } = useNodeStyle({
dmnStyle: shape["di:Style"],
diff --git
a/packages/dmn-editor/src/mutations/addExistingDecisionServiceToDrd.ts
b/packages/dmn-editor/src/mutations/addExistingDecisionServiceToDrd.ts
index 279dcabbf0b..3dcedc844c1 100644
--- a/packages/dmn-editor/src/mutations/addExistingDecisionServiceToDrd.ts
+++ b/packages/dmn-editor/src/mutations/addExistingDecisionServiceToDrd.ts
@@ -17,74 +17,91 @@
* under the License.
*/
+import { generateUuid } from "@kie-tools/boxed-expression-component/dist/api";
import {
DMN15__tDecisionService,
DMN15__tDefinitions,
} from "@kie-tools/dmn-marshaller/dist/schemas/dmn-1_5/ts-gen/types";
-import { DECISION_SERVICE_COLLAPSED_DIMENSIONS } from
"../diagram/nodes/DefaultSizes";
+import { ExternalModelsIndex } from "../DmnEditor";
+import { getAutoLayoutedInfo } from "../autolayout/autoLayoutInfo";
+import { DECISION_SERVICE_COLLAPSED_DIMENSIONS, MIN_NODE_SIZES } from
"../diagram/nodes/DefaultSizes";
import { NODE_TYPES } from "../diagram/nodes/NodeTypes";
-import { Computed } from "../store/Store";
+import { Normalized } from "../normalization/normalize";
+import { Computed, SnapGrid, State } from "../store/Store";
import { computeContainingDecisionServiceHrefsByDecisionHrefs } from
"../store/computed/computeContainingDecisionServiceHrefsByDecisionHrefs.ts";
+import { computeDiagramData } from "../store/computed/computeDiagramData";
+import { computeExternalModelsByType } from
"../store/computed/computeExternalModelsByType";
import { computeIndexedDrd } from "../store/computed/computeIndexes";
import { xmlHrefToQName } from "../xml/xmlHrefToQName";
import { buildXmlHref, parseXmlHref } from "../xml/xmlHrefs";
+import { addOrGetDrd } from "./addOrGetDrd";
import { addShape } from "./addShape";
+import { applyAutoLayoutToDrd } from "./applyAutoLayoutToDrd";
import { repositionNode } from "./repositionNode";
-import { Normalized } from "../normalization/normalize";
-import { generateUuid } from "@kie-tools/boxed-expression-component/dist/api";
+
+export enum StrategyForAddingDecisionServiceToDrd {
+ AUTO_GENERATE,
+ CONFLICT,
+ COPY_FROM_ANOTHER_DRD,
+}
/**
* When adding a Decision Service to a DRD, we need to bring all its
encapsulated and output Decisions with it,
* copying their layout from other DRDs, or formatting with autolayout.
+ * This method returns the strategy to be used when adding a Decision Service.
+ * The strategy should be used to call one of three methods:
+ * - AUTO_GENERATE: addAutoGeneratedDecisionServiceToDrd
+ * - CONFLICT: addConflictingDecisionServiceToDrd
+ * - COPY_FROM_ANOTHER_DRD: addExistingDecisionServiceToDrd
*/
-export function addExistingDecisionServiceToDrd({
- decisionServiceNamespace,
- decisionService,
- externalDmnsIndex,
- thisDmnsNamespace,
- thisDmnsDefinitions,
- thisDmnsIndexedDrd,
- drdIndex,
- dropPoint,
+export function getStrategyToAddExistingDecisionServiceToDrd({
+ __readonly_definitions,
+ __readonly_decisionServiceNamespace,
+ __readonly_drgElement,
+ __readonly_externalDmnsIndex,
+ __readonly_namespace,
+ __readonly_indexedDrd,
+ __readonly_drdIndex,
}: {
- decisionServiceNamespace: string;
- decisionService: Normalized<DMN15__tDecisionService>;
- externalDmnsIndex:
ReturnType<Computed["getExternalModelTypesByNamespace"]>["dmns"];
- thisDmnsNamespace: string;
- thisDmnsDefinitions: Normalized<DMN15__tDefinitions>;
- thisDmnsIndexedDrd: ReturnType<Computed["indexedDrd"]>;
- drdIndex: number;
- dropPoint: { x: number; y: number };
+ __readonly_definitions: Normalized<DMN15__tDefinitions>;
+ __readonly_decisionServiceNamespace: string;
+ __readonly_drgElement: Normalized<DMN15__tDecisionService>;
+ __readonly_externalDmnsIndex:
ReturnType<Computed["getExternalModelTypesByNamespace"]>["dmns"];
+ __readonly_namespace: string;
+ __readonly_indexedDrd: ReturnType<Computed["indexedDrd"]>;
+ __readonly_drdIndex: number;
}) {
const decisionServiceDmnDefinitions =
- !decisionServiceNamespace || decisionServiceNamespace === thisDmnsNamespace
- ? thisDmnsDefinitions
- : externalDmnsIndex.get(decisionServiceNamespace)?.model.definitions;
+ !__readonly_decisionServiceNamespace ||
__readonly_decisionServiceNamespace === __readonly_namespace
+ ? __readonly_definitions
+ :
__readonly_externalDmnsIndex.get(__readonly_decisionServiceNamespace)?.model.definitions;
if (!decisionServiceDmnDefinitions) {
- throw new Error(`DMN MUTATION: Can't find definitions for model with
namespace ${decisionServiceNamespace}`);
+ throw new Error(
+ `DMN MUTATION: Can't find definitions for model with namespace
${__readonly_decisionServiceNamespace}`
+ );
}
const { decisionServiceNamespaceForHref,
containedDecisionHrefsRelativeToThisDmn } =
getDecisionServicePropertiesRelativeToThisDmn({
- thisDmnsNamespace,
- decisionServiceNamespace,
- decisionService,
+ thisDmnsNamespace: __readonly_namespace,
+ decisionServiceNamespace: __readonly_decisionServiceNamespace,
+ decisionService: __readonly_drgElement,
});
const decisionServiceHrefRelativeToThisDmn = buildXmlHref({
namespace: decisionServiceNamespaceForHref,
- id: decisionService["@_id"]!,
+ id: __readonly_drgElement["@_id"]!,
});
const containingDecisionServiceHrefsByDecisionHrefsRelativeToThisDmn =
computeContainingDecisionServiceHrefsByDecisionHrefs({
- thisDmnsNamespace,
- drgElementsNamespace: decisionServiceNamespace,
+ thisDmnsNamespace: __readonly_namespace,
+ drgElementsNamespace: __readonly_decisionServiceNamespace,
drgElements: decisionServiceDmnDefinitions.drgElement,
});
const doesThisDrdHaveConflictingDecisionService =
containedDecisionHrefsRelativeToThisDmn.some((decisionHref) =>
(containingDecisionServiceHrefsByDecisionHrefsRelativeToThisDmn.get(decisionHref)
?? []).some((d) =>
- thisDmnsIndexedDrd.dmnShapesByHref.has(d)
+ __readonly_indexedDrd.dmnShapesByHref.has(d)
)
);
@@ -92,34 +109,21 @@ export function addExistingDecisionServiceToDrd({
// There's already, in this DRD, a Decision Service in expanded form that
contains a Decision that is contained by the Decision Service we're adding.
// As the DMN specification doesn't allow two copies of the same DRG
element to be depicted in the same DRD, we can't add the Decision Service in
expanded form.
// To not disallow depicting the Decision Service in this DRD, though, we
add it in collpased form.
- addShape({
- definitions: thisDmnsDefinitions,
- drdIndex,
- nodeType: NODE_TYPES.decisionService,
- shape: {
- "@_id": generateUuid(),
- "@_dmnElementRef":
xmlHrefToQName(decisionServiceHrefRelativeToThisDmn, thisDmnsDefinitions),
- "@_isCollapsed": true,
- "dc:Bounds": {
- "@_x": dropPoint.x,
- "@_y": dropPoint.y,
- "@_width": DECISION_SERVICE_COLLAPSED_DIMENSIONS.width,
- "@_height": DECISION_SERVICE_COLLAPSED_DIMENSIONS.height,
- },
- },
- });
- return;
+ return {
+ strategyForAddingDecisionServiceToDrd:
StrategyForAddingDecisionServiceToDrd.CONFLICT,
+ decisionServiceHrefRelativeToThisDmn,
+ containedDecisionHrefsRelativeToThisDmn,
+ };
}
const drds =
decisionServiceDmnDefinitions["dmndi:DMNDI"]?.["dmndi:DMNDiagram"] ?? [];
-
let indexedDrd: ReturnType<Computed["indexedDrd"]> | undefined;
for (let i = 0; i < drds.length; i++) {
- if (thisDmnsNamespace === decisionServiceNamespace && i === drdIndex) {
+ if (__readonly_namespace === __readonly_decisionServiceNamespace && i ===
__readonly_drdIndex) {
continue; // Skip the current DRD!
}
- const _indexedDrd = computeIndexedDrd(thisDmnsNamespace,
decisionServiceDmnDefinitions, i);
+ const _indexedDrd = computeIndexedDrd(__readonly_namespace,
decisionServiceDmnDefinitions, i);
const dsShape =
_indexedDrd.dmnShapesByHref.get(decisionServiceHrefRelativeToThisDmn);
const hasCompleteExpandedDepictionOfDecisionService =
dsShape &&
@@ -132,97 +136,381 @@ export function addExistingDecisionServiceToDrd({
}
}
- if (!indexedDrd) {
- // There's no DRD which inclues a complete expanded depiction of the
Decision Service. Let's proceed with auto-layout.
- // TODO: Tiago
+ if (indexedDrd === undefined) {
+ return {
+ strategyForAddingDecisionServiceToDrd:
StrategyForAddingDecisionServiceToDrd.AUTO_GENERATE,
+ decisionServiceHrefRelativeToThisDmn,
+ containedDecisionHrefsRelativeToThisDmn,
+ };
} else {
- // Let's copy the expanded depiction of the Decision Service from `drd`.
- // Adding or moving nodes that already exist in the current DRD to inside
the Decision Service.
- // The positions need all be relative to the Decision Service node, of
course.
- const dsShapeOnOtherDrd =
indexedDrd.dmnShapesByHref.get(decisionServiceHrefRelativeToThisDmn);
- if (
- dsShapeOnOtherDrd?.["dc:Bounds"]?.["@_x"] === undefined ||
- dsShapeOnOtherDrd?.["dc:Bounds"]?.["@_y"] === undefined
- ) {
+ return {
+ strategyForAddingDecisionServiceToDrd:
StrategyForAddingDecisionServiceToDrd.COPY_FROM_ANOTHER_DRD,
+ indexedDrdContainingDecisionServiceDepiction: indexedDrd,
+ decisionServiceHrefRelativeToThisDmn,
+ containedDecisionHrefsRelativeToThisDmn,
+ };
+ }
+}
+
+export function addConflictingDecisionServiceToDrd({
+ definitions,
+ __readonly_drdIndex,
+ __readonly_dropPoint,
+ __readonly_decisionServiceHrefRelativeToThisDmn,
+}: {
+ definitions: Normalized<DMN15__tDefinitions>;
+ __readonly_drdIndex: number;
+ __readonly_dropPoint: { x: number; y: number };
+ __readonly_decisionServiceHrefRelativeToThisDmn: string;
+}) {
+ addShape({
+ definitions: definitions,
+ drdIndex: __readonly_drdIndex,
+ nodeType: NODE_TYPES.decisionService,
+ shape: {
+ "@_id": generateUuid(),
+ "@_dmnElementRef":
xmlHrefToQName(__readonly_decisionServiceHrefRelativeToThisDmn, definitions),
+ "@_isCollapsed": true,
+ "dc:Bounds": {
+ "@_x": __readonly_dropPoint.x,
+ "@_y": __readonly_dropPoint.y,
+ "@_width": DECISION_SERVICE_COLLAPSED_DIMENSIONS.width,
+ "@_height": DECISION_SERVICE_COLLAPSED_DIMENSIONS.height,
+ },
+ },
+ });
+}
+
+export async function addAutoGeneratedDecisionServiceToDrd({
+ state,
+ __readonly_decisionServiceNamespace,
+ __readonly_externalDmnsIndex,
+ __readonly_drdIndex,
+ __readonly_snapGrid,
+ __readonly_decisionServiceHrefRelativeToThisDmn,
+ __readonly_containedDecisionHrefsRelativeToThisDmn,
+ __readonly_dropPoint,
+ __readonly_isAlternativeInputDataShape,
+ __readonly_externalModelsByNamespace,
+}: {
+ state: State;
+ __readonly_decisionServiceNamespace: string;
+ __readonly_externalDmnsIndex:
ReturnType<Computed["getExternalModelTypesByNamespace"]>["dmns"];
+ __readonly_drdIndex: number;
+ __readonly_snapGrid: SnapGrid;
+ __readonly_decisionServiceHrefRelativeToThisDmn: string;
+ __readonly_containedDecisionHrefsRelativeToThisDmn: string[];
+ __readonly_dropPoint: { x: number; y: number };
+ __readonly_isAlternativeInputDataShape: boolean;
+ __readonly_externalModelsByNamespace: ExternalModelsIndex | undefined;
+}) {
+ const drds =
state.dmn.model.definitions["dmndi:DMNDI"]?.["dmndi:DMNDiagram"];
+ // Create a Dummy DRD
+ // In case the model doesn't have a DRD, the Dummy DRD will be the new DRD.
+ // Otherwise, the Dummy DRD will be the next index.
+ const dummyDrdIndex = drds?.length === undefined ? 0 : drds.length;
+ addOrGetDrd({
+ definitions: state.dmn.model.definitions,
+ drdIndex: dummyDrdIndex,
+ });
+
+ // Add the Decision Service
+ const minNodeSize = MIN_NODE_SIZES[NODE_TYPES.decisionService]({
+ snapGrid: __readonly_snapGrid,
+ });
+ addShape({
+ definitions: state.dmn.model.definitions,
+ drdIndex: dummyDrdIndex,
+ nodeType: NODE_TYPES.decisionService,
+ shape: {
+ "@_id": generateUuid(),
+ "@_dmnElementRef":
xmlHrefToQName(__readonly_decisionServiceHrefRelativeToThisDmn,
state.dmn.model.definitions),
+ "dc:Bounds": {
+ "@_x": 0, // Auto-layout will be applied;
+ "@_y": 0, // Auto-layout will be applied;
+ ...minNodeSize,
+ },
+ },
+ });
+
+ // Add all Encapsulated and Output Decisions
+ for (const decisionHref of
__readonly_containedDecisionHrefsRelativeToThisDmn) {
+ const decisionNs = parseXmlHref(decisionHref).namespace;
+ const decisionDmnDefinitions =
+ !decisionNs || decisionNs === state.dmn.model.definitions["@_namespace"]
+ ? state.dmn.model.definitions
+ : __readonly_externalDmnsIndex.get(decisionNs)?.model.definitions;
+ if (!decisionDmnDefinitions) {
throw new Error(
- `DMN MUTATION: Complete DMNShape for Decision Service with href
${decisionServiceHrefRelativeToThisDmn} should've existed on the indexed DRD.`
+ `DMN MUTATION: Can't find definitions for model with namespace
${__readonly_decisionServiceNamespace}`
);
}
-
+ const minNodeSize = MIN_NODE_SIZES[NODE_TYPES.decision]({
+ snapGrid: __readonly_snapGrid,
+ });
addShape({
- definitions: thisDmnsDefinitions,
- drdIndex,
- nodeType: NODE_TYPES.decisionService,
+ definitions: state.dmn.model.definitions,
+ drdIndex: dummyDrdIndex,
+ nodeType: NODE_TYPES.decision,
shape: {
"@_id": generateUuid(),
- "@_dmnElementRef":
xmlHrefToQName(decisionServiceHrefRelativeToThisDmn, thisDmnsDefinitions),
+ "@_dmnElementRef": xmlHrefToQName(decisionHref,
state.dmn.model.definitions),
"dc:Bounds": {
- "@_x": dropPoint.x,
- "@_y": dropPoint.y,
- "@_width": dsShapeOnOtherDrd["dc:Bounds"]["@_width"],
- "@_height": dsShapeOnOtherDrd["dc:Bounds"]["@_height"],
+ "@_x": 0, // Auto-layout will be applied;
+ "@_y": 0, // Auto-layout will be applied;
+ ...minNodeSize,
},
},
});
+ }
+
+ // Compute the external model types by namespace after autogenerating the
Decision Service
+ const externalModelTypesByNamespace = computeExternalModelsByType(
+ state.dmn.model.definitions.import,
+ __readonly_externalModelsByNamespace
+ );
+
+ // Compute the Dummy DRD indexed drd after autogenerating the Decision
Service
+ const dummyIndexedDrd = computeIndexedDrd(
+ state.dmn.model.definitions["@_namespace"],
+ state.dmn.model.definitions,
+ dummyDrdIndex
+ );
+
+ // Compute the Dummy DRD diagram data after autogenerating the Decision
Service
+ const { nodes, nodesById, edges, edgesById, drgEdges } = computeDiagramData(
+ state.diagram,
+ state.dmn.model.definitions,
+ externalModelTypesByNamespace,
+ dummyIndexedDrd,
+ __readonly_isAlternativeInputDataShape
+ );
+
+ // Get the auto-layout info
+ const { __readonly_autoLayoutedInfo, __readonly_parentNodesById } = await
getAutoLayoutedInfo({
+ __readonly_snapGrid,
+ __readonly_nodesById: nodesById,
+ __readonly_edgesById: edgesById,
+ __readonly_nodes: nodes,
+ __readonly_drgEdges: drgEdges,
+ __readonly_isAlternativeInputDataShape:
__readonly_isAlternativeInputDataShape,
+ });
+
+ // Apply the auto-layouted info to the Dummy DRD
+ applyAutoLayoutToDrd({
+ state,
+ __readonly_dmnShapesByHref: dummyIndexedDrd.dmnShapesByHref,
+ __readonly_edges: edges,
+ __readonly_edgesById: edgesById,
+ __readonly_nodesById: nodesById,
+ __readonly_autoLayoutedInfo,
+ __readonly_parentNodesById,
+ __readonly_drdIndex: dummyDrdIndex,
+ __readonly_dmnObjectNamespace: __readonly_decisionServiceNamespace,
+ __readonly_externalDmnsIndex: externalModelTypesByNamespace.dmns,
+ });
+
+ // Save DS shape before applying the autolayout
+ const { "@_x": dsShapeX, "@_y": dsShapeY } =
dummyIndexedDrd.dmnShapesByHref.get(
+ __readonly_decisionServiceHrefRelativeToThisDmn
+ )!["dc:Bounds"]!;
+ // Reposition the auto generated Decision Service to the drop point
+ repositionNode({
+ definitions: state.dmn.model.definitions,
+ drdIndex: dummyDrdIndex,
+ controlWaypointsByEdge: new Map(),
+ change: {
+ nodeType: NODE_TYPES.decisionService,
+ type: "absolute",
+ position: { x: __readonly_dropPoint.x, y: __readonly_dropPoint.y },
+ shapeIndex:
dummyIndexedDrd.dmnShapesByHref.get(__readonly_decisionServiceHrefRelativeToThisDmn)?.index
?? 0,
+ selectedEdges: [],
+ sourceEdgeIndexes: [],
+ targetEdgeIndexes: [],
+ },
+ });
+
+ // Relatively reposition the auto generated Decisions to the drop point
+ for (const decisionHref of
__readonly_containedDecisionHrefsRelativeToThisDmn) {
+ const currentDecisionShape =
dummyIndexedDrd.dmnShapesByHref.get(decisionHref);
+
+ const x = __readonly_dropPoint.x +
currentDecisionShape!["dc:Bounds"]!["@_x"] - dsShapeX;
+ const y = __readonly_dropPoint.y +
currentDecisionShape!["dc:Bounds"]!["@_y"] - dsShapeY;
+
+ if (currentDecisionShape) {
+ repositionNode({
+ definitions: state.dmn.model.definitions,
+ drdIndex: dummyDrdIndex,
+ controlWaypointsByEdge: new Map(),
+ change: {
+ nodeType: NODE_TYPES.decision,
+ type: "absolute",
+ position: { x, y },
+ shapeIndex: currentDecisionShape.index,
+ selectedEdges: [],
+ sourceEdgeIndexes: [],
+ targetEdgeIndexes: [],
+ },
+ });
+ }
+ }
+
+ // Copy the DS and Decisions to current DRD and remove Dummy DRD;
+ if (dummyDrdIndex !== __readonly_drdIndex) {
+ // The auto generated shape.
+ const dsShape =
dummyIndexedDrd.dmnShapesByHref.get(__readonly_decisionServiceHrefRelativeToThisDmn)!;
+ drds![__readonly_drdIndex]["dmndi:DMNDiagramElement"] ??= [];
+ drds?.[__readonly_drdIndex]["dmndi:DMNDiagramElement"]?.push({ ...dsShape,
__$$element: "dmndi:DMNShape" });
+ for (const decisionHref of
__readonly_containedDecisionHrefsRelativeToThisDmn) {
+ const decisionShape = dummyIndexedDrd.dmnShapesByHref.get(decisionHref)!;
+ drds?.[__readonly_drdIndex]["dmndi:DMNDiagramElement"]?.push({
+ ...decisionShape,
+ __$$element: "dmndi:DMNShape",
+ });
+ }
+ drds?.pop(); // Remove Dummy DRD;
+ } else {
+ // In this case Dummy DRD is the current DRD
+ }
+}
+
+export function addExistingDecisionServiceToDrd({
+ definitions,
+ __readonly_decisionServiceNamespace,
+ __readonly_externalDmnsIndex,
+ __readonly_namespace,
+ __readonly_indexedDrd,
+ __readonly_indexedDrdContainingDecisionServiceDepiction,
+ __readonly_drdIndex,
+ __readonly_dropPoint,
+ __readonly_decisionServiceHrefRelativeToThisDmn,
+ __readonly_containedDecisionHrefsRelativeToThisDmn,
+}: {
+ definitions: Normalized<DMN15__tDefinitions>;
+ __readonly_decisionServiceNamespace: string;
+ __readonly_externalDmnsIndex:
ReturnType<Computed["getExternalModelTypesByNamespace"]>["dmns"];
+ __readonly_namespace: string;
+ __readonly_indexedDrd: ReturnType<Computed["indexedDrd"]>;
+ __readonly_indexedDrdContainingDecisionServiceDepiction:
ReturnType<Computed["indexedDrd"]>;
+ __readonly_drdIndex: number;
+ __readonly_dropPoint: { x: number; y: number };
+ __readonly_decisionServiceHrefRelativeToThisDmn: string;
+ __readonly_containedDecisionHrefsRelativeToThisDmn: string[];
+}) {
+ // Let's copy the expanded depiction of the Decision Service from `drd`.
+ // Adding or moving nodes that already exist in the current DRD to inside
the Decision Service.
+ // The positions need all be relative to the Decision Service node, of
course.
+ const dsShapeOnOtherDrd =
__readonly_indexedDrdContainingDecisionServiceDepiction.dmnShapesByHref.get(
+ __readonly_decisionServiceHrefRelativeToThisDmn
+ );
+ if (
+ dsShapeOnOtherDrd?.["dc:Bounds"]?.["@_x"] === undefined ||
+ dsShapeOnOtherDrd?.["dc:Bounds"]?.["@_y"] === undefined
+ ) {
+ throw new Error(
+ `DMN MUTATION: Complete DMNShape for Decision Service with href
${__readonly_decisionServiceHrefRelativeToThisDmn} should've existed on the
indexed DRD.`
+ );
+ }
+
+ const dsDividirLineOnOtherDrd =
dsShapeOnOtherDrd["dmndi:DMNDecisionServiceDividerLine"];
+ const decisionServiceDividerLineWaypoint = [
+ {
+ "@_x": __readonly_dropPoint.x,
+ "@_y":
+ (dsDividirLineOnOtherDrd?.["di:waypoint"]?.[0]["@_y"] ?? 0) -
+ (dsShapeOnOtherDrd?.["dc:Bounds"]?.["@_y"] ?? 0) +
+ __readonly_dropPoint.y,
+ },
+ {
+ "@_x":
+ (dsDividirLineOnOtherDrd?.["di:waypoint"]?.[1]["@_x"] ?? 0) -
+ (dsDividirLineOnOtherDrd?.["di:waypoint"]?.[0]["@_x"] ?? 0) +
+ __readonly_dropPoint.x,
+ "@_y":
+ (dsDividirLineOnOtherDrd?.["di:waypoint"]?.[0]["@_y"] ?? 0) -
+ (dsShapeOnOtherDrd?.["dc:Bounds"]?.["@_y"] ?? 0) +
+ __readonly_dropPoint.y,
+ },
+ ];
+ addShape({
+ definitions: definitions,
+ drdIndex: __readonly_drdIndex,
+ nodeType: NODE_TYPES.decisionService,
+ shape: {
+ "@_id": generateUuid(),
+ "@_dmnElementRef":
xmlHrefToQName(__readonly_decisionServiceHrefRelativeToThisDmn, definitions),
+ "dc:Bounds": {
+ "@_x": __readonly_dropPoint.x,
+ "@_y": __readonly_dropPoint.y,
+ "@_width": dsShapeOnOtherDrd["dc:Bounds"]["@_width"],
+ "@_height": dsShapeOnOtherDrd["dc:Bounds"]["@_height"],
+ },
+ },
+ decisionServiceDividerLineWaypoint,
+ });
- for (const decisionHref of containedDecisionHrefsRelativeToThisDmn) {
- const decisionShapeOnOtherDrd =
indexedDrd.dmnShapesByHref.get(decisionHref);
- if (
- decisionShapeOnOtherDrd?.["dc:Bounds"]?.["@_x"] === undefined ||
- decisionShapeOnOtherDrd?.["dc:Bounds"]?.["@_y"] === undefined ||
- decisionShapeOnOtherDrd?.["dc:Bounds"]?.["@_width"] === undefined ||
- decisionShapeOnOtherDrd?.["dc:Bounds"]?.["@_height"] === undefined
- ) {
+ for (const decisionHref of
__readonly_containedDecisionHrefsRelativeToThisDmn) {
+ const decisionShapeOnOtherDrd =
+
__readonly_indexedDrdContainingDecisionServiceDepiction.dmnShapesByHref.get(decisionHref);
+ if (
+ decisionShapeOnOtherDrd?.["dc:Bounds"]?.["@_x"] === undefined ||
+ decisionShapeOnOtherDrd?.["dc:Bounds"]?.["@_y"] === undefined ||
+ decisionShapeOnOtherDrd?.["dc:Bounds"]?.["@_width"] === undefined ||
+ decisionShapeOnOtherDrd?.["dc:Bounds"]?.["@_height"] === undefined
+ ) {
+ throw new Error(
+ `DMN MUTATION: Complete DMNShape for Decision with href
${decisionHref} should've existed on the indexed DRD.`
+ );
+ }
+
+ const x =
+ __readonly_dropPoint.x + (decisionShapeOnOtherDrd["dc:Bounds"]["@_x"] -
dsShapeOnOtherDrd["dc:Bounds"]["@_x"]);
+ const y =
+ __readonly_dropPoint.y + (decisionShapeOnOtherDrd["dc:Bounds"]["@_y"] -
dsShapeOnOtherDrd["dc:Bounds"]["@_y"]);
+
+ const existingDecisionShape =
__readonly_indexedDrd.dmnShapesByHref.get(decisionHref);
+ if (existingDecisionShape) {
+ repositionNode({
+ definitions: definitions,
+ drdIndex: __readonly_drdIndex,
+ controlWaypointsByEdge: new Map(),
+ change: {
+ nodeType: NODE_TYPES.decision,
+ type: "absolute",
+ position: { x, y },
+ shapeIndex: existingDecisionShape.index,
+ selectedEdges: [],
+ sourceEdgeIndexes: [],
+ targetEdgeIndexes: [],
+ },
+ });
+ } else {
+ const decisionNs = parseXmlHref(decisionHref).namespace;
+ const decisionDmnDefinitions =
+ !decisionNs || decisionNs === __readonly_namespace
+ ? definitions
+ : __readonly_externalDmnsIndex.get(decisionNs)?.model.definitions;
+ if (!decisionDmnDefinitions) {
throw new Error(
- `DMN MUTATION: Complete DMNShape for Decision with href
${decisionHref} should've existed on the indexed DRD.`
+ `DMN MUTATION: Can't find definitions for model with namespace
${__readonly_decisionServiceNamespace}`
);
}
- const x = dropPoint.x + (decisionShapeOnOtherDrd["dc:Bounds"]["@_x"] -
dsShapeOnOtherDrd["dc:Bounds"]["@_x"]);
- const y = dropPoint.y + (decisionShapeOnOtherDrd["dc:Bounds"]["@_y"] -
dsShapeOnOtherDrd["dc:Bounds"]["@_y"]);
-
- const existingDecisionShape =
thisDmnsIndexedDrd.dmnShapesByHref.get(decisionHref);
- if (existingDecisionShape) {
- repositionNode({
- definitions: thisDmnsDefinitions,
- drdIndex,
- controlWaypointsByEdge: new Map(),
- change: {
- nodeType: NODE_TYPES.decision,
- type: "absolute",
- position: { x, y },
- shapeIndex: existingDecisionShape.index,
- selectedEdges: [],
- sourceEdgeIndexes: [],
- targetEdgeIndexes: [],
- },
- });
- } else {
- const decisionNs = parseXmlHref(decisionHref).namespace;
- const decisionDmnDefinitions =
- !decisionNs || decisionNs === thisDmnsNamespace
- ? thisDmnsDefinitions
- : externalDmnsIndex.get(decisionNs)?.model.definitions;
- if (!decisionDmnDefinitions) {
- throw new Error(`DMN MUTATION: Can't find definitions for model with
namespace ${decisionServiceNamespace}`);
- }
-
- addShape({
- definitions: thisDmnsDefinitions,
- drdIndex,
- nodeType: NODE_TYPES.decision,
- shape: {
- "@_id": generateUuid(),
- "@_dmnElementRef": xmlHrefToQName(decisionHref,
thisDmnsDefinitions),
- "dc:Bounds": {
- "@_x": x,
- "@_y": y,
- "@_width": decisionShapeOnOtherDrd["dc:Bounds"]["@_width"],
- "@_height": decisionShapeOnOtherDrd["dc:Bounds"]["@_height"],
- },
+ addShape({
+ definitions: definitions,
+ drdIndex: __readonly_drdIndex,
+ nodeType: NODE_TYPES.decision,
+ shape: {
+ "@_id": generateUuid(),
+ "@_dmnElementRef": xmlHrefToQName(decisionHref, definitions),
+ "dc:Bounds": {
+ "@_x": x,
+ "@_y": y,
+ "@_width": decisionShapeOnOtherDrd["dc:Bounds"]["@_width"],
+ "@_height": decisionShapeOnOtherDrd["dc:Bounds"]["@_height"],
},
- });
- }
+ },
+ });
}
}
}
diff --git a/packages/dmn-editor/src/mutations/addOrGetDrd.ts
b/packages/dmn-editor/src/mutations/addOrGetDrd.ts
index 4247ba79b03..6cf19f7928c 100644
--- a/packages/dmn-editor/src/mutations/addOrGetDrd.ts
+++ b/packages/dmn-editor/src/mutations/addOrGetDrd.ts
@@ -32,7 +32,7 @@ export function addOrGetDrd({
definitions: Normalized<DMN15__tDefinitions>;
drdIndex: number;
}) {
- const defaultName = getDefaultDrdName({ drdIndex });
+ const drdName = getDefaultDrdName({ drdIndex: drdIndex });
// diagram
definitions["dmndi:DMNDI"] ??= {};
@@ -41,7 +41,7 @@ export function addOrGetDrd({
const defaultDiagram =
definitions["dmndi:DMNDI"]["dmndi:DMNDiagram"][drdIndex];
defaultDiagram["@_id"] ??= generateUuid();
- defaultDiagram["@_name"] ??= defaultName;
+ defaultDiagram["@_name"] ??= drdName;
defaultDiagram["@_useAlternativeInputDataShape"] ??= false;
defaultDiagram["dmndi:DMNDiagramElement"] ??= [];
diff --git a/packages/dmn-editor/src/mutations/addShape.ts
b/packages/dmn-editor/src/mutations/addShape.ts
index 9c5e795e412..fd716e34ae6 100644
--- a/packages/dmn-editor/src/mutations/addShape.ts
+++ b/packages/dmn-editor/src/mutations/addShape.ts
@@ -17,29 +17,42 @@
* under the License.
*/
-import { DMN15__tDefinitions, DMNDI15__DMNShape } from
"@kie-tools/dmn-marshaller/dist/schemas/dmn-1_5/ts-gen/types";
+import {
+ DC__Point,
+ DMN15__tDefinitions,
+ DMNDI15__DMNDecisionServiceDividerLine,
+ DMNDI15__DMNShape,
+} from "@kie-tools/dmn-marshaller/dist/schemas/dmn-1_5/ts-gen/types";
import { NodeType } from "../diagram/connections/graphStructure";
import { NODE_TYPES } from "../diagram/nodes/NodeTypes";
import { Normalized } from "../normalization/normalize";
import { addOrGetDrd } from "./addOrGetDrd";
import { getCentralizedDecisionServiceDividerLine } from
"./updateDecisionServiceDividerLine";
+import { generateUuid } from "@kie-tools/boxed-expression-component/dist/api";
export function addShape({
definitions,
drdIndex,
nodeType,
shape,
+ decisionServiceDividerLineWaypoint: decisionServiceDividerLineWaypoint,
}: {
definitions: Normalized<DMN15__tDefinitions>;
drdIndex: number;
nodeType: NodeType;
shape: Normalized<DMNDI15__DMNShape>;
+ decisionServiceDividerLineWaypoint?: DC__Point[];
}) {
const { diagramElements } = addOrGetDrd({ definitions, drdIndex });
diagramElements.push({
__$$element: "dmndi:DMNShape",
...(nodeType === NODE_TYPES.decisionService
- ? { "dmndi:DMNDecisionServiceDividerLine":
getCentralizedDecisionServiceDividerLine(shape["dc:Bounds"]!) }
+ ? {
+ "dmndi:DMNDecisionServiceDividerLine":
+ decisionServiceDividerLineWaypoint !== undefined
+ ? { "@_id": generateUuid(), "di:waypoint":
[...decisionServiceDividerLineWaypoint] }
+ : getCentralizedDecisionServiceDividerLine(shape["dc:Bounds"]!),
+ }
: {}),
...shape,
});
diff --git a/packages/dmn-editor/src/mutations/applyAutoLayoutToDrd.ts
b/packages/dmn-editor/src/mutations/applyAutoLayoutToDrd.ts
new file mode 100644
index 00000000000..0f3786a1b83
--- /dev/null
+++ b/packages/dmn-editor/src/mutations/applyAutoLayoutToDrd.ts
@@ -0,0 +1,225 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import { DMNDI15__DMNShape } from
"@kie-tools/dmn-marshaller/dist/schemas/dmn-1_5/ts-gen/types";
+import { XmlQName } from "@kie-tools/xml-parser-ts/dist/qNames";
+import * as Elk from "elkjs/lib/elk.bundled.js";
+import * as RF from "reactflow";
+import { EdgeType, NodeType } from "../diagram/connections/graphStructure";
+import { PositionalNodeHandleId } from
"../diagram/connections/PositionalNodeHandles";
+import { DmnDiagramEdgeData } from "../diagram/edges/Edges";
+import { DmnDiagramNodeData } from "../diagram/nodes/Nodes";
+import { NODE_TYPES } from "../diagram/nodes/NodeTypes";
+import { addEdge } from "../mutations/addEdge";
+import { repositionNode } from "../mutations/repositionNode";
+import { resizeNode } from "../mutations/resizeNode";
+import { updateDecisionServiceDividerLine } from
"../mutations/updateDecisionServiceDividerLine";
+import { Normalized } from "../normalization/normalize";
+import { State } from "../store/Store";
+import { AutolayoutParentNode, FAKE_MARKER, visitNodeAndNested } from
"../autolayout/autoLayoutInfo";
+import { ExternalDmnsIndex } from "../DmnEditor";
+
+export function applyAutoLayoutToDrd({
+ state,
+ __readonly_autoLayoutedInfo,
+ __readonly_parentNodesById,
+ __readonly_nodesById,
+ __readonly_edgesById,
+ __readonly_edges,
+ __readonly_dmnShapesByHref,
+ __readonly_drdIndex,
+ __readonly_dmnObjectNamespace,
+ __readonly_externalDmnsIndex,
+}: {
+ state: State;
+ __readonly_autoLayoutedInfo: {
+ isHorizontal: boolean;
+ nodes: Elk.ElkNode[] | undefined;
+ edges: Elk.ElkExtendedEdge[] | undefined;
+ };
+ __readonly_parentNodesById: Map<string, AutolayoutParentNode>;
+ __readonly_nodesById: Map<string, RF.Node<DmnDiagramNodeData, string |
undefined>>;
+ __readonly_edgesById: Map<string, RF.Edge<DmnDiagramEdgeData>>;
+ __readonly_edges: RF.Edge<DmnDiagramEdgeData>[];
+ __readonly_dmnShapesByHref: Map<
+ string,
+ Normalized<DMNDI15__DMNShape> & {
+ index: number;
+ dmnElementRefQName: XmlQName;
+ }
+ >;
+ __readonly_drdIndex: number;
+ __readonly_dmnObjectNamespace: string | undefined;
+ __readonly_externalDmnsIndex: ExternalDmnsIndex;
+}) {
+ // 7. Update all nodes positions skipping empty groups, which will be
positioned manually after all nodes are done being repositioned.
+ const autolayoutedElkNodesById = new Map<string, Elk.ElkNode>();
+
+ for (const topLevelElkNode of __readonly_autoLayoutedInfo.nodes ?? []) {
+ visitNodeAndNested(topLevelElkNode, { x: 100, y: 100 }, (elkNode,
positionOffset) => {
+ if (elkNode.id.includes(FAKE_MARKER)) {
+ return;
+ }
+
+ autolayoutedElkNodesById.set(elkNode.id, elkNode);
+
+ const nodeId = elkNode.id;
+ const node = __readonly_nodesById.get(nodeId)!;
+
+ repositionNode({
+ definitions: state.dmn.model.definitions,
+ drdIndex: __readonly_drdIndex,
+ controlWaypointsByEdge: new Map(),
+ change: {
+ nodeType: node.type as NodeType,
+ type: "absolute",
+ position: {
+ x: elkNode.x! + positionOffset.x,
+ y: elkNode.y! + positionOffset.y,
+ },
+ selectedEdges: [...__readonly_edgesById.keys()],
+ shapeIndex: node.data?.shape.index,
+ sourceEdgeIndexes: __readonly_edges.flatMap((e) =>
+ e.source === nodeId && e.data?.dmnEdge ? [e.data.dmnEdge.index] :
[]
+ ),
+ targetEdgeIndexes: __readonly_edges.flatMap((e) =>
+ e.target === nodeId && e.data?.dmnEdge ? [e.data.dmnEdge.index] :
[]
+ ),
+ },
+ });
+ });
+ }
+
+ // 8. Resize all nodes using the sizes calculated by ELK.
+ for (const topLevelElkNode of __readonly_autoLayoutedInfo.nodes ?? []) {
+ visitNodeAndNested(topLevelElkNode, { x: 0, y: 0 }, (elkNode) => {
+ if (elkNode.id.includes(FAKE_MARKER)) {
+ return;
+ }
+
+ const nodeId = elkNode.id;
+ const node = __readonly_nodesById.get(nodeId)!;
+
+ resizeNode({
+ definitions: state.dmn.model.definitions,
+ drdIndex: __readonly_drdIndex,
+ __readonly_dmnShapesByHref: __readonly_dmnShapesByHref,
+ snapGrid: state.diagram.snapGrid,
+ __readonly_dmnObjectNamespace,
+ __readonly_externalDmnsIndex,
+ change: {
+ index: node.data.index,
+ isExternal: !!node.data.dmnObjectQName.prefix,
+ nodeType: node.type as NodeType,
+ dimension: {
+ "@_width": elkNode.width!,
+ "@_height": elkNode.height!,
+ },
+ shapeIndex: node.data?.shape.index,
+ sourceEdgeIndexes: __readonly_edges.flatMap((e) =>
+ e.source === nodeId && e.data?.dmnEdge ? [e.data.dmnEdge.index] :
[]
+ ),
+ targetEdgeIndexes: __readonly_edges.flatMap((e) =>
+ e.target === nodeId && e.data?.dmnEdge ? [e.data.dmnEdge.index] :
[]
+ ),
+ },
+ });
+ });
+ }
+
+ // 9. Updating Decision Service divider lines after all nodes are
repositioned and resized.
+ for (const [parentNodeId] of __readonly_parentNodesById) {
+ const parentNode = __readonly_nodesById.get(parentNodeId);
+ if (parentNode?.type !== NODE_TYPES.decisionService) {
+ continue;
+ }
+
+ const elkNode = autolayoutedElkNodesById.get(parentNodeId);
+ if (!elkNode) {
+ throw new Error(`Couldn't find Decision Service with id ${parentNode.id}
at the autolayouted nodes map`);
+ }
+
+ /**
+ * The second children of a Decision Service elkNode is a node
representing the Encapsulated section.
+ * It's Y position will be exactly where the divider line should be.
+ */
+ const dividerLinerLocalYPosition = elkNode.children?.[1]?.y;
+ if (!dividerLinerLocalYPosition) {
+ throw new Error(
+ `Couldn't find second child (which represents the Encapuslated
Decision section) of Decision Service with id ${parentNode.id} at the
autolayouted nodes map`
+ );
+ }
+
+ updateDecisionServiceDividerLine({
+ definitions: state.dmn.model.definitions,
+ drdIndex: __readonly_drdIndex,
+ __readonly_dmnShapesByHref,
+ __readonly_dmnObjectNamespace,
+ __readonly_externalDmnsIndex,
+ drgElementIndex: parentNode.data.index,
+ shapeIndex: parentNode.data.shape.index,
+ snapGrid: state.diagram.snapGrid,
+ localYPosition: dividerLinerLocalYPosition,
+ });
+ }
+
+ // 10. Update the edges. Edges always go from top to bottom, removing
waypoints.
+ for (const elkEdge of __readonly_autoLayoutedInfo.edges ?? []) {
+ if (elkEdge.id.includes(FAKE_MARKER)) {
+ continue;
+ }
+
+ const edge = __readonly_edgesById.get(elkEdge.id)!;
+
+ const sourceNode = __readonly_nodesById.get(elkEdge.sources[0])!;
+ const targetNode = __readonly_nodesById.get(elkEdge.targets[0])!;
+
+ // If the target is an external node, we don't have to create the edge.
+ if (targetNode.data.dmnObjectQName.prefix) {
+ continue;
+ }
+
+ addEdge({
+ definitions: state.dmn.model.definitions,
+ drdIndex: __readonly_drdIndex,
+ edge: {
+ autoPositionedEdgeMarker: undefined,
+ type: edge.type as EdgeType,
+ targetHandle: PositionalNodeHandleId.Bottom,
+ sourceHandle: PositionalNodeHandleId.Top,
+ },
+ sourceNode: {
+ type: sourceNode.type as NodeType,
+ href: sourceNode.id,
+ data: sourceNode.data,
+ bounds: sourceNode.data.shape["dc:Bounds"]!,
+ shapeId: sourceNode.data.shape["@_id"],
+ },
+ targetNode: {
+ type: targetNode.type as NodeType,
+ href: targetNode.id,
+ data: targetNode.data,
+ bounds: targetNode.data.shape["dc:Bounds"]!,
+ index: targetNode.data.index,
+ shapeId: targetNode.data.shape["@_id"],
+ },
+ keepWaypoints: false,
+ });
+ }
+}
diff --git a/packages/dmn-editor/src/mutations/deleteImport.ts
b/packages/dmn-editor/src/mutations/deleteImport.ts
index ca2617e3260..34aa40bcc7a 100644
--- a/packages/dmn-editor/src/mutations/deleteImport.ts
+++ b/packages/dmn-editor/src/mutations/deleteImport.ts
@@ -61,13 +61,13 @@ export function deleteImport({
externalNodesByNamespace.get(deletedImport["@_namespace"])?.forEach((node)
=> {
deleteNode({
definitions,
- drgEdges: drgEdges,
- drdIndex: 0,
- nodeNature: nodeNatures[node.type! as NodeType],
- dmnObjectId: node.data.dmnObject?.["@_id"],
- dmnObjectQName: node.data.dmnObjectQName,
- dmnObjectNamespace: node.data.dmnObjectNamespace!,
- externalDmnsIndex: __readonly_externalModelTypesByNamespace.dmns,
+ __readonly_drgEdges: drgEdges,
+ __readonly_drdIndex: 0,
+ __readonly_nodeNature: nodeNatures[node.type! as NodeType],
+ __readonly_dmnObjectId: node.data.dmnObject?.["@_id"],
+ __readonly_dmnObjectQName: node.data.dmnObjectQName,
+ __readonly_dmnObjectNamespace: node.data.dmnObjectNamespace!,
+ __readonly_externalModelTypesByNamespace,
mode: NodeDeletionMode.FROM_DRG_AND_ALL_DRDS,
});
});
diff --git a/packages/dmn-editor/src/mutations/deleteNode.ts
b/packages/dmn-editor/src/mutations/deleteNode.ts
index 5a732691650..5f4affe9fa4 100644
--- a/packages/dmn-editor/src/mutations/deleteNode.ts
+++ b/packages/dmn-editor/src/mutations/deleteNode.ts
@@ -31,6 +31,7 @@ import { Computed } from "../store/Store";
import { computeContainingDecisionServiceHrefsByDecisionHrefs } from
"../store/computed/computeContainingDecisionServiceHrefsByDecisionHrefs.ts";
import { xmlHrefToQName } from "../xml/xmlHrefToQName";
import { Normalized } from "../normalization/normalize";
+import { NodeDmnObjects } from "../diagram/nodes/Nodes";
export enum NodeDeletionMode {
FROM_DRG_AND_ALL_DRDS,
@@ -39,23 +40,23 @@ export enum NodeDeletionMode {
export function deleteNode({
definitions,
- drgEdges,
- drdIndex,
- nodeNature,
- dmnObjectId,
- dmnObjectQName,
- dmnObjectNamespace,
- externalDmnsIndex,
+ __readonly_drgEdges,
+ __readonly_drdIndex,
+ __readonly_nodeNature,
+ __readonly_dmnObjectId,
+ __readonly_dmnObjectNamespace,
+ __readonly_dmnObjectQName,
+ __readonly_externalModelTypesByNamespace,
mode,
}: {
definitions: Normalized<DMN15__tDefinitions>;
- drgEdges: DrgEdge[];
- drdIndex: number;
- nodeNature: NodeNature;
- externalDmnsIndex:
ReturnType<Computed["getExternalModelTypesByNamespace"]>["dmns"];
- dmnObjectNamespace: string;
- dmnObjectId: string | undefined;
- dmnObjectQName: XmlQName;
+ __readonly_drgEdges: DrgEdge[];
+ __readonly_drdIndex: number;
+ __readonly_nodeNature: NodeNature;
+ __readonly_externalModelTypesByNamespace:
ReturnType<Computed["getExternalModelTypesByNamespace"]>;
+ __readonly_dmnObjectId: string | undefined;
+ __readonly_dmnObjectNamespace: string;
+ __readonly_dmnObjectQName: XmlQName;
mode: NodeDeletionMode;
}): {
deletedDmnObject: Unpacked<Normalized<DMN15__tDefinitions>["drgElement" |
"artifact"]> | undefined;
@@ -65,10 +66,10 @@ export function deleteNode({
mode === NodeDeletionMode.FROM_CURRENT_DRD_ONLY &&
!canRemoveNodeFromDrdOnly({
definitions,
- drdIndex,
- dmnObjectNamespace,
- dmnObjectId,
- externalDmnsIndex,
+ __readonly_drdIndex,
+ __readonly_dmnObjectNamespace,
+ __readonly_dmnObjectId,
+ __readonly_externalDmnsIndex:
__readonly_externalModelTypesByNamespace.dmns,
})
) {
console.warn("DMN MUTATION: Cannot hide a Decision that's contained by a
Decision Service from a DRD.");
@@ -78,14 +79,18 @@ export function deleteNode({
if (mode === NodeDeletionMode.FROM_DRG_AND_ALL_DRDS) {
// Delete Edges
// A DRD doesn't necessarily renders all edges of the DRG, so we need to
look for what DRG edges to delete when deleting a node from any DRD.
- const nodeId = buildXmlHref({ namespace: dmnObjectNamespace, id:
dmnObjectId! });
- for (let i = 0; i < drgEdges.length; i++) {
- const drgEdge = drgEdges[i];
+ const nodeId = buildXmlHref({
+ namespace:
+ __readonly_dmnObjectNamespace === definitions["@_namespace"] ?
undefined : __readonly_dmnObjectNamespace,
+ id: __readonly_dmnObjectId!,
+ });
+ for (let i = 0; i < __readonly_drgEdges.length; i++) {
+ const drgEdge = __readonly_drgEdges[i];
// Only delete edges that end at or start from the node being deleted.
if (drgEdge.sourceId === nodeId || drgEdge.targetId === nodeId) {
deleteEdge({
definitions,
- drdIndex,
+ drdIndex: __readonly_drdIndex,
mode: EdgeDeletionMode.FROM_DRG_AND_ALL_DRDS,
edge: {
id: drgEdge.id,
@@ -108,61 +113,61 @@ export function deleteNode({
}
}
- let dmnObject: Unpacked<Normalized<DMN15__tDefinitions>["drgElement" |
"artifact"]> | undefined;
+ let deletedDmnObject: Unpacked<Normalized<DMN15__tDefinitions>["drgElement"
| "artifact"]> | undefined;
// External or unknown nodes don't have a dmnObject associated with it, just
the shape..
- if (!dmnObjectQName.prefix) {
+ if (!__readonly_dmnObjectQName.prefix) {
// Delete the dmnObject itself
- if (nodeNature === NodeNature.ARTIFACT) {
+ if (__readonly_nodeNature === NodeNature.ARTIFACT) {
if (mode === NodeDeletionMode.FROM_DRG_AND_ALL_DRDS) {
- const nodeIndex = (definitions.artifact ?? []).findIndex((a) =>
a["@_id"] === dmnObjectId);
- dmnObject = definitions.artifact?.splice(nodeIndex, 1)?.[0];
+ const nodeIndex = (definitions.artifact ?? []).findIndex((a) =>
a["@_id"] === __readonly_dmnObjectId);
+ deletedDmnObject = definitions.artifact?.splice(nodeIndex, 1)?.[0];
} else {
throw new Error(`DMN MUTATION: Can't hide an artifact node.`);
}
- } else if (nodeNature === NodeNature.DRG_ELEMENT) {
- const nodeIndex = (definitions.drgElement ?? []).findIndex((d) =>
d["@_id"] === dmnObjectId);
- dmnObject =
+ } else if (__readonly_nodeNature === NodeNature.DRG_ELEMENT) {
+ const nodeIndex = (definitions.drgElement ?? []).findIndex((d) =>
d["@_id"] === __readonly_dmnObjectId);
+ deletedDmnObject =
mode === NodeDeletionMode.FROM_DRG_AND_ALL_DRDS
? definitions.drgElement?.splice(nodeIndex, 1)?.[0]
: definitions.drgElement?.[nodeIndex];
- } else if (nodeNature === NodeNature.UNKNOWN) {
+ } else if (__readonly_nodeNature === NodeNature.UNKNOWN) {
// Ignore. There's no dmnObject here.
} else {
- throw new Error(`DMN MUTATION: Unknown node nature '${nodeNature}'.`);
+ throw new Error(`DMN MUTATION: Unknown node nature
'${__readonly_nodeNature}'.`);
}
- if (!dmnObject && nodeNature !== NodeNature.UNKNOWN) {
+ if (!deletedDmnObject && __readonly_nodeNature !== NodeNature.UNKNOWN) {
/**
* We do not want to throw error in case of `nodeNature` equals to
`NodeNature.UNKNOWN`.
* In such scenario it is expected `dmnObject` is undefined as we can
not pair `dmnObject` with the `DMNShape`.
* However we are still able to delete at least the selected `DMNShape`
from the diagram.
*/
- throw new Error(`DMN MUTATION: Can't delete DMN object that doesn't
exist: ID=${dmnObjectId}`);
+ throw new Error(`DMN MUTATION: Can't delete DMN object that doesn't
exist: ID=${__readonly_dmnObjectId}`);
}
}
- const shapeDmnElementRef = buildXmlQName(dmnObjectQName);
+ const shapeDmnElementRef = buildXmlQName(__readonly_dmnObjectQName);
// Deleting the DMNShape's
let deletedDmnShapeOnCurrentDrd: Normalized<DMNDI15__DMNShape> | undefined;
- const deletedIdsOnDmnObjectTree = dmnObject
+ const deletedIdsOnDmnObjectTree = deletedDmnObject
? getNewDmnIdRandomizer()
- .ack({ json: [dmnObject], type: "DMN15__tDefinitions", attr:
"drgElement" })
+ .ack({ json: [deletedDmnObject], type: "DMN15__tDefinitions", attr:
"drgElement" })
.getOriginalIds()
: new Set<string>();
const drdCount = (definitions["dmndi:DMNDI"]?.["dmndi:DMNDiagram"] ??
[]).length;
for (let i = 0; i < drdCount; i++) {
- if (mode === NodeDeletionMode.FROM_CURRENT_DRD_ONLY && i !== drdIndex) {
+ if (mode === NodeDeletionMode.FROM_CURRENT_DRD_ONLY && i !==
__readonly_drdIndex) {
continue;
}
const { diagramElements, widthsExtension } = addOrGetDrd({ definitions,
drdIndex: i });
const dmnShapeIndex = (diagramElements ?? []).findIndex((d) =>
d["@_dmnElementRef"] === shapeDmnElementRef);
if (dmnShapeIndex >= 0) {
- if (i === drdIndex) {
+ if (i === __readonly_drdIndex) {
deletedDmnShapeOnCurrentDrd = diagramElements[dmnShapeIndex];
}
@@ -178,40 +183,40 @@ export function deleteNode({
repopulateInputDataAndDecisionsOnAllDecisionServices({ definitions });
return {
- deletedDmnObject: mode === NodeDeletionMode.FROM_DRG_AND_ALL_DRDS ?
dmnObject : undefined,
+ deletedDmnObject: mode === NodeDeletionMode.FROM_DRG_AND_ALL_DRDS ?
deletedDmnObject : undefined,
deletedDmnShapeOnCurrentDrd,
};
}
export function canRemoveNodeFromDrdOnly({
definitions,
- drdIndex,
- dmnObjectNamespace,
- dmnObjectId,
- externalDmnsIndex,
+ __readonly_drdIndex,
+ __readonly_dmnObjectNamespace,
+ __readonly_dmnObjectId,
+ __readonly_externalDmnsIndex,
}: {
- dmnObjectNamespace: string;
- dmnObjectId: string | undefined;
definitions: Normalized<DMN15__tDefinitions>;
- drdIndex: number;
- externalDmnsIndex:
ReturnType<Computed["getExternalModelTypesByNamespace"]>["dmns"];
+ __readonly_dmnObjectNamespace: string;
+ __readonly_dmnObjectId: string | undefined;
+ __readonly_drdIndex: number;
+ __readonly_externalDmnsIndex:
ReturnType<Computed["getExternalModelTypesByNamespace"]>["dmns"];
}) {
- const { diagramElements } = addOrGetDrd({ definitions, drdIndex });
+ const { diagramElements } = addOrGetDrd({ definitions, drdIndex:
__readonly_drdIndex });
const dmnObjectHref = buildXmlHref({
- namespace: dmnObjectNamespace === definitions["@_namespace"] ? "" :
dmnObjectNamespace,
- id: dmnObjectId!,
+ namespace: __readonly_dmnObjectNamespace === definitions["@_namespace"] ?
undefined : __readonly_dmnObjectNamespace,
+ id: __readonly_dmnObjectId!,
});
const drgElements =
- definitions["@_namespace"] === dmnObjectNamespace
+ definitions["@_namespace"] === __readonly_dmnObjectNamespace
? definitions.drgElement ?? []
- :
externalDmnsIndex.get(dmnObjectNamespace)?.model.definitions.drgElement ?? [];
+ :
__readonly_externalDmnsIndex.get(__readonly_dmnObjectNamespace)?.model.definitions.drgElement
?? [];
const containingDecisionServiceHrefsByDecisionHrefsRelativeToThisDmn =
computeContainingDecisionServiceHrefsByDecisionHrefs({
thisDmnsNamespace: definitions["@_namespace"],
- drgElementsNamespace: dmnObjectNamespace,
+ drgElementsNamespace: __readonly_dmnObjectNamespace,
drgElements,
});
diff --git a/packages/dmn-editor/src/mutations/resizeNode.ts
b/packages/dmn-editor/src/mutations/resizeNode.ts
index 6199148f5d3..ee991a1cc90 100644
--- a/packages/dmn-editor/src/mutations/resizeNode.ts
+++ b/packages/dmn-editor/src/mutations/resizeNode.ts
@@ -34,11 +34,14 @@ import { SnapGrid } from "../store/Store";
import { addOrGetDrd } from "./addOrGetDrd";
import { DECISION_SERVICE_DIVIDER_LINE_PADDING } from
"./updateDecisionServiceDividerLine";
import { Normalized } from "../normalization/normalize";
+import { ExternalDmnsIndex } from "../DmnEditor";
export function resizeNode({
definitions,
drdIndex,
__readonly_dmnShapesByHref,
+ __readonly_dmnObjectNamespace,
+ __readonly_externalDmnsIndex,
snapGrid,
change,
}: {
@@ -46,6 +49,8 @@ export function resizeNode({
drdIndex: number;
__readonly_dmnShapesByHref: Map<string, Normalized<DMNDI15__DMNShape> & {
index: number }>;
snapGrid: SnapGrid;
+ __readonly_dmnObjectNamespace: string | undefined;
+ __readonly_externalDmnsIndex: ExternalDmnsIndex;
change: {
nodeType: NodeType;
isExternal: boolean;
@@ -68,7 +73,15 @@ export function resizeNode({
const limit = { x: 0, y: 0 };
if (change.nodeType === NODE_TYPES.decisionService) {
- const ds = definitions.drgElement![change.index] as
Normalized<DMN15__tDecisionService>;
+ const externalDmn =
__readonly_externalDmnsIndex.get(__readonly_dmnObjectNamespace ?? "");
+
+ const ds =
+ externalDmn === undefined
+ ? (definitions.drgElement![change.index] as
Normalized<DMN15__tDecisionService>)
+ : (externalDmn.model.definitions.drgElement![change.index] as
Normalized<DMN15__tDecisionService>);
+ if (!ds) {
+ throw new Error("DMN MUTATION: Cannot reposition divider line of
non-existent Decision Service");
+ }
const dividerLineY =
shape["dmndi:DMNDecisionServiceDividerLine"]?.["di:waypoint"]?.[0]?.["@_y"] ??
shapeBounds["@_y"];
diff --git
a/packages/dmn-editor/src/mutations/updateDecisionServiceDividerLine.ts
b/packages/dmn-editor/src/mutations/updateDecisionServiceDividerLine.ts
index 5b0b3d2c684..d92ca3a67d1 100644
--- a/packages/dmn-editor/src/mutations/updateDecisionServiceDividerLine.ts
+++ b/packages/dmn-editor/src/mutations/updateDecisionServiceDividerLine.ts
@@ -31,6 +31,8 @@ import { SnapGrid } from "../store/Store";
import { NODE_TYPES } from "../diagram/nodes/NodeTypes";
import { Normalized } from "../normalization/normalize";
import { generateUuid } from "@kie-tools/boxed-expression-component/dist/api";
+import { addNamespaceToHref } from "../xml/xmlHrefs";
+import { ExternalDmnsIndex } from "../DmnEditor";
export const DECISION_SERVICE_DIVIDER_LINE_PADDING = 100;
@@ -38,6 +40,8 @@ export function updateDecisionServiceDividerLine({
definitions,
drdIndex,
__readonly_dmnShapesByHref,
+ __readonly_dmnObjectNamespace,
+ __readonly_externalDmnsIndex,
shapeIndex,
localYPosition,
drgElementIndex,
@@ -46,6 +50,8 @@ export function updateDecisionServiceDividerLine({
definitions: Normalized<DMN15__tDefinitions>;
drdIndex: number;
__readonly_dmnShapesByHref: Map<string, Normalized<DMNDI15__DMNShape> & {
index: number }>;
+ __readonly_dmnObjectNamespace: string | undefined;
+ __readonly_externalDmnsIndex: ExternalDmnsIndex;
shapeIndex: number;
localYPosition: number;
drgElementIndex: number;
@@ -59,7 +65,12 @@ export function updateDecisionServiceDividerLine({
throw new Error("DMN MUTATION: Cannot reposition divider line of
non-existent shape bounds");
}
- const ds = definitions.drgElement![drgElementIndex] as
Normalized<DMN15__tDecisionService>;
+ const externalDmn =
__readonly_externalDmnsIndex.get(__readonly_dmnObjectNamespace ?? "");
+
+ const ds =
+ externalDmn === undefined
+ ? (definitions.drgElement![drgElementIndex] as
Normalized<DMN15__tDecisionService>)
+ : (externalDmn.model.definitions.drgElement![drgElementIndex] as
Normalized<DMN15__tDecisionService>);
if (!ds) {
throw new Error("DMN MUTATION: Cannot reposition divider line of
non-existent Decision Service");
}
@@ -71,14 +82,32 @@ export function updateDecisionServiceDividerLine({
const snappedDimensions = snapShapeDimensions(snapGrid, shape,
decisionServiceMinSizes);
const upperLimit = (ds.outputDecision ?? []).reduce((acc, od) => {
+ // For external Decision Services, the Output Decision will have the
relative namespace. e.g. without namespace.
+ const href =
+ __readonly_dmnObjectNamespace !== undefined
+ ? addNamespaceToHref({
+ href: od["@_href"],
+ namespace:
+ definitions["@_namespace"] === __readonly_dmnObjectNamespace ?
undefined : __readonly_dmnObjectNamespace,
+ })
+ : od["@_href"];
const v =
- snapShapePosition(snapGrid,
__readonly_dmnShapesByHref.get(od["@_href"])!).y +
- snapShapeDimensions(snapGrid,
__readonly_dmnShapesByHref.get(od["@_href"])!, decisionMinSizes).height;
+ snapShapePosition(snapGrid, __readonly_dmnShapesByHref.get(href)!).y +
+ snapShapeDimensions(snapGrid, __readonly_dmnShapesByHref.get(href)!,
decisionMinSizes).height;
return v > acc ? v : acc;
}, snappedPosition.y + DECISION_SERVICE_DIVIDER_LINE_PADDING);
const lowerLimit = (ds.encapsulatedDecision ?? []).reduce((acc, ed) => {
- const v = snapShapePosition(snapGrid,
__readonly_dmnShapesByHref.get(ed["@_href"])!).y;
+ // For external Decision Services, the Encapsulated Decision will have the
relative namespace. e.g. without namespace.
+ const href =
+ __readonly_dmnObjectNamespace !== undefined
+ ? addNamespaceToHref({
+ href: ed["@_href"],
+ namespace:
+ definitions["@_namespace"] === __readonly_dmnObjectNamespace ?
undefined : __readonly_dmnObjectNamespace,
+ })
+ : ed["@_href"];
+ const v = snapShapePosition(snapGrid,
__readonly_dmnShapesByHref.get(href)!).y;
return v < acc ? v : acc;
}, snappedPosition.y + snappedDimensions.height -
DECISION_SERVICE_DIVIDER_LINE_PADDING);
diff --git
a/packages/dmn-editor/src/store/computed/computeContainingDecisionServiceHrefsByDecisionHrefs.ts.ts
b/packages/dmn-editor/src/store/computed/computeContainingDecisionServiceHrefsByDecisionHrefs.ts.ts
index 1c579853d9f..6365fa7fb88 100644
---
a/packages/dmn-editor/src/store/computed/computeContainingDecisionServiceHrefsByDecisionHrefs.ts.ts
+++
b/packages/dmn-editor/src/store/computed/computeContainingDecisionServiceHrefsByDecisionHrefs.ts.ts
@@ -31,7 +31,7 @@ export function
computeContainingDecisionServiceHrefsByDecisionHrefs({
drgElements: State["dmn"]["model"]["definitions"]["drgElement"];
}) {
drgElements ??= [];
- const decisionServiecHrefsByDecisionHrefs = new Map<string, string[]>();
+ const decisionServiceHrefsByDecisionHrefs = new Map<string, string[]>();
for (const drgElement of drgElements) {
const drgElementHref = buildXmlHref({
@@ -41,9 +41,9 @@ export function
computeContainingDecisionServiceHrefsByDecisionHrefs({
// Decision
if (drgElement.__$$element === "decision") {
- decisionServiecHrefsByDecisionHrefs.set(
+ decisionServiceHrefsByDecisionHrefs.set(
drgElementHref,
- decisionServiecHrefsByDecisionHrefs.get(drgElementHref) ?? []
+ decisionServiceHrefsByDecisionHrefs.get(drgElementHref) ?? []
);
}
// DS
@@ -55,8 +55,8 @@ export function
computeContainingDecisionServiceHrefsByDecisionHrefs({
});
for (const containedDecisionHref of
containedDecisionHrefsRelativeToThisDmn) {
- decisionServiecHrefsByDecisionHrefs.set(containedDecisionHref, [
- ...(decisionServiecHrefsByDecisionHrefs.get(containedDecisionHref)
?? []),
+ decisionServiceHrefsByDecisionHrefs.set(containedDecisionHref, [
+ ...(decisionServiceHrefsByDecisionHrefs.get(containedDecisionHref)
?? []),
drgElementHref,
]);
}
@@ -65,5 +65,5 @@ export function
computeContainingDecisionServiceHrefsByDecisionHrefs({
}
}
- return decisionServiecHrefsByDecisionHrefs;
+ return decisionServiceHrefsByDecisionHrefs;
}
diff --git a/packages/dmn-editor/src/xml/xmlHrefs.ts
b/packages/dmn-editor/src/xml/xmlHrefs.ts
index 2419b86a930..4fa05444828 100644
--- a/packages/dmn-editor/src/xml/xmlHrefs.ts
+++ b/packages/dmn-editor/src/xml/xmlHrefs.ts
@@ -39,3 +39,24 @@ export function parseXmlHref(href: string): XmlHref {
return { namespace: split[0] ? split[0] : undefined, id: split[1] };
}
+
+/**
+ * This function adds a `namespace` to an HREF. This operation will only
succed if
+ * the provided HREF doesn't have a `namespace`.
+ *
+ * In case the provided `namespace` is `undefined`, this function will return
the HREF.
+ *
+ * In case the provided HREF already have an `namespace`, this function will
return the HREF.
+ */
+export function addNamespaceToHref({ href, namespace }: { href: string;
namespace: string | undefined }) {
+ if (namespace === undefined) {
+ return href;
+ }
+
+ const { namespace: hrefNamespace, id } = parseXmlHref(href);
+ if (hrefNamespace !== undefined) {
+ return href;
+ }
+
+ return buildXmlHref({ namespace, id });
+}
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]