You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@camel.apache.org by ma...@apache.org on 2023/12/07 02:37:08 UTC

(camel-karavan) 02/04: Preview fixes of new Designer

This is an automated email from the ASF dual-hosted git repository.

marat pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/camel-karavan.git

commit 9d503ba51812e6b5e702172fd08b355005326749
Author: Marat Gubaidullin <ma...@talismancloud.io>
AuthorDate: Wed Dec 6 20:35:19 2023 -0500

    Preview fixes of new Designer
---
 karavan-designer/public/example/demo.camel.yaml    |  26 +++-
 karavan-designer/src/designer/DesignerStore.ts     |  31 ++--
 .../src/designer/route/DslConnections.tsx          | 170 ++++++++++++---------
 .../src/designer/route/RouteDesigner.tsx           |   6 +-
 .../src/designer/route/element/DslElement.css      |  19 +--
 .../src/designer/route/element/DslElement.tsx      |  55 ++-----
 .../designer/route/element/DslElementHeader.tsx    |  42 +----
 .../src/designer/route/useRouteDesignerHook.tsx    |   2 +-
 karavan-designer/src/designer/utils/EventBus.ts    |  11 +-
 9 files changed, 175 insertions(+), 187 deletions(-)

diff --git a/karavan-designer/public/example/demo.camel.yaml b/karavan-designer/public/example/demo.camel.yaml
index ccd98241..1bc2b8ae 100644
--- a/karavan-designer/public/example/demo.camel.yaml
+++ b/karavan-designer/public/example/demo.camel.yaml
@@ -25,6 +25,15 @@
                   - to:
                       uri: amqp
                       id: to-fbfe
+                  - choice:
+                      when:
+                        - expression:
+                            simple:
+                              id: simple-e78b
+                          id: when-b7d0
+                      otherwise:
+                        id: otherwise-40d0
+                      id: choice-8f6b
             otherwise:
               id: otherwise-382c
               steps:
@@ -38,12 +47,6 @@
               - to:
                   uri: kamelet:azure-cosmosdb-sink
                   id: to-1394
-- route:
-    nodePrefixId: route-d10
-    id: route-3ad9
-    from:
-      uri: kamelet:azure-storage-datalake-source
-      id: from-1516
 - route:
     nodePrefixId: route-171
     id: route-99f9
@@ -85,6 +88,15 @@
                   - log:
                       message: ${body}
                       id: log-77df
+                  - choice:
+                      when:
+                        - expression:
+                            simple:
+                              id: simple-c7db
+                          id: when-f058
+                      otherwise:
+                        id: otherwise-1e11
+                      id: choice-8374
                   - wireTap:
                       id: wireTap-a25e
             doFinally:
@@ -113,5 +125,7 @@
                           simple:
                             id: simple-f0dc
                         id: setBody-3c0c
+              - process:
+                  id: process-6d06
         - circuitBreaker:
             id: circuitBreaker-4af8
diff --git a/karavan-designer/src/designer/DesignerStore.ts b/karavan-designer/src/designer/DesignerStore.ts
index f9bc38d1..815f0f68 100644
--- a/karavan-designer/src/designer/DesignerStore.ts
+++ b/karavan-designer/src/designer/DesignerStore.ts
@@ -120,9 +120,9 @@ interface ConnectionsState {
     deleteStep: (uuid: string) => void;
     clearSteps: () => void;
     setSteps: (steps: Map<string, DslPosition>) => void;
-    buttons: ButtonPosition[];
-    addButton: (button: ButtonPosition) => void;
-    deleteButton: (button: ButtonPosition) => void;
+    buttons: Map<string, ButtonPosition>;
+    addButton: (uuid: string, button: ButtonPosition) => void;
+    deleteButton: (uuid: string) => void;
     clearButtons: () => void;
 }
 
@@ -152,27 +152,24 @@ export const useConnectionsStore = createWithEqualityFn<ConnectionsState>((set)
     setSteps: (steps: Map<string, DslPosition>) => {
         set({steps: steps})
     },
-    buttons: [],
-    addButton: (button: ButtonPosition) => {
-        set((state: ConnectionsState) => {
-            const index = state.buttons.findIndex(b => b.uuid === button.uuid);
-            if (index !== -1) {
-                state.buttons.splice(index, 1);
-            }
-            state.buttons.push(button);
-            return state;
-        })
+    buttons: new Map<string, ButtonPosition>(),
+    addButton: (uuid: string, button: ButtonPosition) => {
+        set(state => ({
+            buttons: new Map(state.buttons).set(uuid, button),
+        }))
     },
     clearButtons: () => {
         set((state: ConnectionsState) => {
-            state.buttons.length = 0;
+            state.buttons.clear();
             return state;
         })
     },
-    deleteButton: (button: ButtonPosition) => {
+    deleteButton: (uuid: string) => {
         set((state: ConnectionsState) => {
-            const index = state.buttons.findIndex(b => b.uuid === button.uuid);
-            state.buttons.splice(index, 1);
+            Array.from(state.buttons.entries())
+                .filter(value => value[1].uuid !== uuid)
+                .forEach(value => state.buttons.set(value[0], value[1]));
+            state.buttons.delete(uuid)
             return state;
         })
     },
diff --git a/karavan-designer/src/designer/route/DslConnections.tsx b/karavan-designer/src/designer/route/DslConnections.tsx
index a4eef251..934fd89c 100644
--- a/karavan-designer/src/designer/route/DslConnections.tsx
+++ b/karavan-designer/src/designer/route/DslConnections.tsx
@@ -14,15 +14,15 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import React, {useEffect} from 'react';
+import React, {JSX, useEffect} from 'react';
 import '../karavan.css';
-import {CamelElement} from "karavan-core/lib/model/IntegrationDefinition";
 import {ButtonPosition, DslPosition, EventBus} from "../utils/EventBus";
 import {CamelUi} from "../utils/CamelUi";
 import {useConnectionsStore, useDesignerStore, useIntegrationStore} from "../DesignerStore";
 import {shallow} from "zustand/shallow";
 import {CamelDefinitionApiExt} from "karavan-core/lib/api/CamelDefinitionApiExt";
 import {TopologyUtils} from "karavan-core/lib/api/TopologyUtils";
+import {CamelElement} from "../../../../karavan-core/lib/model/IntegrationDefinition";
 
 const overlapGap: number = 40;
 
@@ -45,15 +45,17 @@ export function DslConnections() {
     });
 
     useEffect(() => {
-        const toDelete: string[] = Array.from(steps.keys()).filter(k => CamelDefinitionApiExt.findElementInIntegration(integration, k) === undefined);
-        toDelete.forEach(key => deleteStep(key));
+        const toDelete1: string[] = Array.from(steps.keys()).filter(k => CamelDefinitionApiExt.findElementInIntegration(integration, k) === undefined);
+        toDelete1.forEach(key => deleteStep(key));
+        const toDelete2: string[] = Array.from(buttons.keys()).filter(k => CamelDefinitionApiExt.findElementInIntegration(integration, k) === undefined);
+        toDelete2.forEach(key => deleteButton(key));
     }, [integration]);
 
     function setButtonPosition(btn: ButtonPosition) {
         if (btn.command === "add") {
-            addButton(btn);
+            addButton(btn.uuid, btn);
         } else if (btn.command === "delete") {
-            deleteButton(btn);
+            deleteButton(btn.uuid);
         } else if (btn.command === "clean") {
             clearButtons();
         }
@@ -103,8 +105,6 @@ export function DslConnections() {
             return (
                 <g key={pos.step.uuid + "-incoming"}>
                     <circle cx={incomingX} cy={fromY} r={r} className="circle-incoming"/>
-                    {/*<image x={imageX} y={imageY} href={CamelUi.getConnectionIconString(pos.step)} className="icon"/>*/}
-                    {/*<text x={imageX - 5} y={imageY + 40} className="caption" textAnchor="start">{CamelUi.getTitle(pos.step)}</text>*/}
                     <path d={`M ${lineX1},${lineY1} C ${lineX1},${lineY2} ${lineX2},${lineY1}  ${lineX2},${lineY2}`}
                           className="path-incoming" markerEnd="url(#arrowhead)"/>
                 </g>
@@ -189,8 +189,6 @@ export function DslConnections() {
             return (
                 <g key={pos.step.uuid + "-outgoing"}>
                     <circle cx={outgoingX} cy={outgoingY} r={r} className="circle-outgoing"/>
-                    {/*<image x={imageX} y={imageY} href={image} className="icon"/>*/}
-                    {/*<text x={imageX + 25} y={imageY + 40}  className="caption" textAnchor="end">{CamelUi.getOutgoingTitle(pos.step)}</text>*/}
                     <path
                         d={`M ${lineX1},${lineY1} C ${lineXi - 20}, ${lineY1} ${lineX1 - 15},${lineYi} ${lineXi},${lineYi} L ${lineX2},${lineY2}`}
                         className="path-incoming" markerEnd="url(#arrowhead)"/>
@@ -226,79 +224,105 @@ export function DslConnections() {
         )
     }
 
-    function hasSteps(step: CamelElement): boolean {
-        return (step.hasSteps() && !['FromDefinition'].includes(step.dslName))
-            || ['RouteDefinition', 'TryDefinition', 'ChoiceDefinition', 'SwitchDefinition'].includes(step.dslName);
+    function getNext(pos: DslPosition): CamelElement | undefined {
+        if (pos.nextstep) {
+            return pos.nextstep;
+        } else if (pos.parent) {
+            const parent = steps.get(pos.parent.uuid);
+            if (parent) return getNext(parent);
+        }
+    }
+
+    function isSpecial(pos: DslPosition): boolean {
+        return ['ChoiceDefinition', 'MulticastDefinition', 'TryDefinition'].includes(pos.step.dslName);
     }
 
-    function getPreviousStep(pos: DslPosition) {
-        return Array.from(steps.values())
-            .filter(p => pos.parent?.uuid === p.parent?.uuid)
-            .filter(p => p.inSteps)
-            .filter(p => p.position === pos.position - 1)[0];
+    function addArrowToList(list: JSX.Element[], from?: DslPosition, to?: DslPosition, fromHeader?: boolean, toHeader?: boolean): JSX.Element[]  {
+        const result: JSX.Element[] = [...list];
+        if (from && to) {
+            const rect1 = fromHeader === true ? from.headerRect : from.rect;
+            const rect2 = toHeader === true ? to.headerRect : to.rect;
+            result.push(getComplexArrow(from.step.uuid + "->" + to.step.uuid, rect1, rect2, toHeader === true));
+        }
+        return result;
     }
 
-    function getArrow(pos: DslPosition) {
-        const endX = pos.headerRect.x + pos.headerRect.width / 2 - left;
-        const endY = pos.headerRect.y - 9 - top;
-        if (pos.parent) {
+    function getArrow(pos: DslPosition): JSX.Element[] {
+        const list: JSX.Element[] = [];
+
+        if (pos.parent && pos.parent.dslName === 'FromDefinition' && pos.position === 0) {
+            // const parent = steps.get(pos.parent.uuid);
+            // list.push(...addArrowToList(list, parent, pos, true, false))
+        } else if (pos.parent && pos.parent.dslName === 'TryDefinition' && pos.position === 0) {
+            const parent = steps.get(pos.parent.uuid);
+            list.push(...addArrowToList(list, parent, pos, true, false))
+        } else if (pos.parent && ['CatchDefinition', 'FinallyDefinition'].includes(pos.parent.dslName)  && pos.position === 0) {
+            const parent = steps.get(pos.parent.uuid);
+            list.push(...addArrowToList(list, parent, pos, true, true))
+        } else if (pos.parent && pos.parent.dslName === 'MulticastDefinition') {
+            const parent = steps.get(pos.parent.uuid);
+            list.push(...addArrowToList(list, parent, pos, true, false))
+            if (parent?.nextstep) {
+                const to = steps.get(parent.nextstep.uuid);
+                list.push(...addArrowToList(list, pos, to, true, true))
+            }
+        } else if (pos.parent && pos.parent.dslName === 'ChoiceDefinition') {
             const parent = steps.get(pos.parent.uuid);
-            const showArrow = pos.prevStep !== undefined && !['TryDefinition', 'ChoiceDefinition'].includes(pos.prevStep.dslName);
-            const name = pos.prevStep?.dslName;
-            if (parent && showArrow) {
-                if ((!pos.inSteps || (pos.inSteps && pos.position === 0)) && parent.step.dslName !== 'MulticastDefinition') {
-                    return getArrows(pos);
-                } else if (parent.step.dslName === 'MulticastDefinition' && pos.inSteps) {
-                    return getArrows(pos)
-                } else if (pos.inSteps && pos.position > 0 && !hasSteps(pos.step)) {
-                    const prev = getPreviousStep(pos);
-                    if (prev) {
-                        const r = hasSteps(prev.step) ? prev.rect : prev.headerRect;
-                        const prevX = r.x + r.width / 2 - left;
-                        const prevY = r.y + r.height - top;
-                        return (
-                            <line name={name} x1={prevX} y1={prevY} x2={endX} y2={endY} className="path"
-                                  key={pos.step.uuid} markerEnd="url(#arrowhead)"/>
-                        )
-                    }
-                } else if (pos.inSteps && pos.position > 0 && hasSteps(pos.step)) {
-                    const prev = getPreviousStep(pos);
-                    if (prev) {
-                        const r = hasSteps(prev.step) ? prev.rect : prev.headerRect;
-                        const prevX = r.x + r.width / 2 - left;
-                        const prevY = r.y + r.height - top;
-                        return (
-                            <line name={name} x1={prevX} y1={prevY} x2={endX} y2={endY} className="path"
-                                  key={pos.step.uuid} markerEnd="url(#arrowhead)"/>
-                        )
-                    }
+            list.push(...addArrowToList(list, parent, pos, true, false))
+        } else if (pos.parent && ['WhenDefinition', 'OtherwiseDefinition', 'CatchDefinition', 'FinallyDefinition'].includes(pos.parent.dslName)) {
+            if (pos.position === 0) {
+                const parent = steps.get(pos.parent.uuid);
+                list.push(...addArrowToList(list, parent, pos, true, false))
+            }
+            if (pos.position === (pos.inStepsLength - 1) && !isSpecial(pos)) {
+                const nextElement = getNext(pos);
+                if (nextElement) {
+                    const next = steps.get(nextElement.uuid);
+                    list.push(...addArrowToList(list, pos, next, true, true))
                 }
             }
+        } else if (pos.step && !isSpecial(pos)) {
+            if (pos.nextstep) {
+                const next = steps.get(pos.nextstep.uuid);
+                const fromHeader = !pos.step.hasSteps();
+                list.push(...addArrowToList(list, pos, next, fromHeader, true))
+            }
+            if (pos.step.hasSteps() && (pos.step as any).steps.length > 0) {
+                const firstStep = (pos.step as any).steps[0];
+                const next = steps.get(firstStep.uuid);
+                list.push(...addArrowToList(list, pos, next, true, true))
+            }
         }
-    }
 
-    function getArrows(pos: DslPosition) {
-        if (pos.parent) {
-            const parent = steps.get(pos?.parent.uuid);
-            if (parent) {
-            const rect1 = parent.headerRect;
-            const rect2 = pos.headerRect;
-            return getComplexArrow(pos.step.uuid + ":" + pos.parent.uuid, rect1, rect2);
+        if (['WhenDefinition', 'OtherwiseDefinition'].includes(pos.step.dslName) && pos.step.hasSteps() && (pos.step as any).steps.length === 0) {
+            if (pos.nextstep) {
+                const to = steps.get(pos.nextstep.uuid);
+                list.push(...addArrowToList(list, pos, to, true, true))
+            } else {
+                const next = getNext(pos);
+                if (next) {
+                    const to = steps.get(next.uuid);
+                    list.push(...addArrowToList(list, pos, to, true, true))
+                }
             }
         }
-    }
 
-    function getButtonArrow(btn: ButtonPosition) {
-        const rect1 = btn.rect;
-        const uuid = btn.nextstep.uuid;
-        const nextStep = steps.get(uuid);
-        const rect2 = nextStep?.rect;
-        if (rect1 && rect2) {
-            return getComplexArrow(uuid + "-" + btn.nextstep.uuid, rect1, rect2);
+        if (pos.parent?.dslName === 'TryDefinition' && pos.inSteps && pos.position === (pos.inStepsLength - 1)) {
+            const parent = steps.get(pos.parent.uuid);
+            if (parent && parent.nextstep) {
+                const to = steps.get(parent.nextstep.uuid);
+                list.push(...addArrowToList(list, pos, to, true, true))
+            }
         }
+
+        if (!isSpecial(pos) && pos.inSteps && pos.nextstep && !pos.step.hasSteps() && pos.parent?.dslName !== 'MulticastDefinition') {
+            const to = steps.get(pos.nextstep.uuid);
+            list.push(...addArrowToList(list, pos, to, true, true))
+        }
+        return list;
     }
 
-    function getComplexArrow(key: string, rect1: DOMRect, rect2: DOMRect) {
+    function getComplexArrow(key: string, rect1: DOMRect, rect2: DOMRect, toHeader: boolean) {
             const startX = rect1.x + rect1.width / 2 - left;
             const startY = rect1.y + rect1.height - top - 2;
             const endX = rect2.x + rect2.width / 2 - left;
@@ -309,7 +333,7 @@ export function DslConnections() {
 
             const radX = gapX > 30 ? 20 : gapX/2;
             const radY = gapY > 30 ? 20 : gapY/2;
-            const endY = rect2.y - top - 9 - radY;
+            const endY = rect2.y - top - radY - (toHeader ? 9 : 6);
 
             const iRadX = startX > endX ? -1 * radX : radX;
             const iRadY = startY > endY ? -1 * radY : radY;
@@ -336,7 +360,7 @@ export function DslConnections() {
                 + ` L ${LX2} ${LY2}`
                 + ` Q ${Q2_X1} ${Q2_Y1} ${Q2_X2} ${Q2_Y2}`
             return (
-                <path key={key} d={path} className="path" markerEnd="url(#arrowhead)"/>
+                <path key={key} name={key} d={path} className="path" markerEnd="url(#arrowhead)"/>
             )
     }
 
@@ -353,11 +377,11 @@ export function DslConnections() {
                     </marker>
                 </defs>
                 {stepsArray.map(pos => getCircle(pos))}
-                {stepsArray.map(pos => getArrow(pos))}
-                {buttons.map(btn => getButtonArrow(btn)).filter(b => b !== undefined)}
+                <g>
+                    {stepsArray.map(pos => getArrow(pos)).flat(1)}
+                </g>
                 {getIncomings().map(p => getIncoming(p))}
                 {getOutgoings().map(p => getOutgoing(p))}
-                {/*{getInternals().map((p) => getInternalLines(p)).flat()}*/}
             </svg>
         )
     }
diff --git a/karavan-designer/src/designer/route/RouteDesigner.tsx b/karavan-designer/src/designer/route/RouteDesigner.tsx
index b6b1c942..b02ed20b 100644
--- a/karavan-designer/src/designer/route/RouteDesigner.tsx
+++ b/karavan-designer/src/designer/route/RouteDesigner.tsx
@@ -147,16 +147,17 @@ export function RouteDesigner() {
                      data-click="FLOWS"
                      onClick={event => {unselectElement(event)}}
                      ref={flowRef}>
-                    {routeConfigurations?.map((routeConfiguration, index: number) => (
+                    {routeConfigurations?.map((routeConfiguration, index: number, array) => (
                         <DslElement key={routeConfiguration.uuid}
                                     inSteps={false}
                                     position={index}
                                     step={routeConfiguration}
                                     nextStep={undefined}
                                     prevStep={undefined}
+                                    inStepsLength={array.length}
                                     parent={undefined}/>
                     ))}
-                    {routes?.map((route: any, index: number) => {
+                    {routes?.map((route: any, index: number, array) => {
                         return (
                             <DslElement key={route.uuid}
                                         inSteps={false}
@@ -164,6 +165,7 @@ export function RouteDesigner() {
                                         step={route}
                                         nextStep={undefined}
                                         prevStep={undefined}
+                                        inStepsLength={array.length}
                                         parent={undefined}/>
                         )
                     })}
diff --git a/karavan-designer/src/designer/route/element/DslElement.css b/karavan-designer/src/designer/route/element/DslElement.css
index 7af5bd51..61cc5af3 100644
--- a/karavan-designer/src/designer/route/element/DslElement.css
+++ b/karavan-designer/src/designer/route/element/DslElement.css
@@ -18,14 +18,19 @@
 .karavan .dsl-page .flows .step-element .header-route {
     display: block;
     background: transparent;
-    padding: 10px;
+    border-radius: 42px;
+    padding: 20px;
     margin: 0;
     z-index: 101;
     min-width: 260px;
 }
 
-.karavan .dsl-page .flows .step-element .header-bottom-line {
-    border-bottom: 1px dashed;
+.karavan .dsl-page .flows .step-element .header-bottom-selected {
+    border-bottom: 1px dashed var(--step-border-color-selected);
+}
+
+.karavan .dsl-page .flows .step-element .header-bottom-not-selected {
+    border-bottom: 1px dashed var(--pf-v5-global--Color--200);
 }
 
 .karavan .dsl-page .flows .step-element .header-route:hover {
@@ -48,7 +53,7 @@
 .karavan .step-element .header .delete-button,
 .element-builder .header .delete-button {
     position: absolute;
-    top: -7px;
+    top: -11px;
     line-height: 1;
     border: 0;
     padding: 0;
@@ -72,10 +77,6 @@
     height: 50px;
 }
 
-.karavan .step-element-selected {
-    background-color: rgba(var(--pf-v5-global--palette--blue-50), 1);
-}
-
 .karavan .step-element .selected .header-icon {
     border-color: var(--pf-v5-global--primary-color--100);
     background-color: var(--pf-v5-global--palette--blue-50);
@@ -152,7 +153,7 @@
 
 .karavan .step-element .insert-element-button {
     position: absolute;
-    top: -7px;
+    top: -11px;
     line-height: 1;
     border: 0;
     padding: 0;
diff --git a/karavan-designer/src/designer/route/element/DslElement.tsx b/karavan-designer/src/designer/route/element/DslElement.tsx
index a1b63a98..8f89a0f9 100644
--- a/karavan-designer/src/designer/route/element/DslElement.tsx
+++ b/karavan-designer/src/designer/route/element/DslElement.tsx
@@ -27,6 +27,7 @@ import {shallow} from "zustand/shallow";
 import {useRouteDesignerHook} from "../useRouteDesignerHook";
 import {AddElementIcon} from "./DslElementIcons";
 import {DslElementHeader} from "./DslElementHeader";
+import {TryDefinition} from "karavan-core/lib/model/CamelDefinition";
 
 interface Props {
     step: CamelElement,
@@ -35,12 +36,12 @@ interface Props {
     prevStep: CamelElement | undefined,
     inSteps: boolean
     position: number
+    inStepsLength: number
 }
 
 export function DslElement(props: Props) {
 
     const headerRef = React.useRef<HTMLDivElement>(null);
-    const addButtonRef = React.useRef<HTMLDivElement>(null);
     const {
         selectElement,
         moveElement,
@@ -94,10 +95,6 @@ export function DslElement(props: Props) {
         return selectedUuids.includes(props.step.uuid);
     }
 
-    function isElementHidden(): boolean {
-        return props.step.dslName === 'LogDefinition' && hideLogDSL;
-    }
-
     function hasBorder(): boolean {
         const step = props.step;
         if (['FilterDefinition', 'RouteDefinition', 'RouteConfigurationDefinition'].includes(step.dslName)) {
@@ -106,6 +103,7 @@ export function DslElement(props: Props) {
         if ([
             'FromDefinition',
             'TryDefinition',
+            'MulticastDefinition',
             'CatchDefinition', 'FinallyDefinition',
             'ChoiceDefinition',
             'SwitchDefinition', 'WhenDefinition', 'OtherwiseDefinition'
@@ -119,10 +117,6 @@ export function DslElement(props: Props) {
         return ['FromDefinition', 'RouteConfigurationDefinition', 'RouteDefinition', 'WhenDefinition', 'OtherwiseDefinition'].includes(props.step.dslName);
     }
 
-    function isRoute(): boolean {
-        return ['RouteDefinition'].includes(props.step.dslName);
-    }
-
     function isAddStepButtonLeft(): boolean {
         return ['MulticastDefinition']
             .includes(props.step.dslName);
@@ -139,28 +133,10 @@ export function DslElement(props: Props) {
         return children.filter((c: ChildElement) => c.name === 'steps' || c.multiple).length > 0 && props.inSteps;
     }
 
-    function sendButtonPosition(el: HTMLButtonElement | null) {
-        const {nextStep, step, parent} = props;
-        let needArrow = !hasBorder() && !['ChoiceDefinition', 'MulticastDefinition', 'TryDefinition'].includes(step.dslName);
-
-        if (parent
-            && ['TryDefinition'].includes(parent.dslName)
-            && !['CatchDefinition', 'FinallyDefinition'].includes(step.dslName)) {
-            needArrow = true;
-        }
-
-        if (el && nextStep && needArrow) {
-            const rect = headerRef.current?.getBoundingClientRect();
-
-            if (rect)
-                EventBus.sendButtonPosition("add", step.uuid, nextStep, rect);
-        }
-    }
 
     function sendPosition(el: HTMLDivElement | null) {
-        const {step, prevStep, parent} = props;
+        const {step, prevStep, nextStep, parent, inSteps, inStepsLength} = props;
         const isSelected = isElementSelected();
-        const isHidden = isElementHidden();
         if (el) {
             const header = Array.from(el.childNodes.values()).filter((n: any) => n.classList.contains("header"))[0];
             if (header) {
@@ -168,13 +144,9 @@ export function DslElement(props: Props) {
                 const headerRect = headerIcon.getBoundingClientRect();
                 const rect = el.getBoundingClientRect();
                 if (step.showChildren) {
-                    if (isHidden) {
-                        EventBus.sendPosition("add", step, prevStep, parent, rect, headerRect, props.position, props.inSteps, isSelected);
-                    } else {
-                        EventBus.sendPosition("add", step, prevStep, parent, rect, headerRect, props.position, props.inSteps, isSelected);
-                    }
+                    EventBus.sendPosition("add", step, prevStep, nextStep, parent, rect, headerRect, props.position, inStepsLength, inSteps, isSelected);
                 } else {
-                    EventBus.sendPosition("delete", step, prevStep, parent, new DOMRect(), new DOMRect(), 0);
+                    EventBus.sendPosition("delete", step, prevStep, nextStep, parent, new DOMRect(), new DOMRect(), 0, 0);
                 }
             }
         }
@@ -190,7 +162,6 @@ export function DslElement(props: Props) {
 
     function getChildrenElementsStyle(child: ChildElement, notOnlySteps: boolean) {
         const style: CSSProperties = {
-            // borderStyle: isBorder ? "dotted" : "none",
             borderColor: "var(--step-border-color)",
             borderWidth: "1px",
             borderRadius: "16px",
@@ -229,10 +200,12 @@ export function DslElement(props: Props) {
             return (
                 <div className={child.name + " has-child"} style={getChildrenElementsStyle(child, notOnlySteps)}
                      key={step.uuid + "-child-" + index}>
-                    {children.map((element, index) => {
+                    {children.map((element, index, array) => {
                             let prevStep = children.at(index - 1);
                             let nextStep: CamelElement | undefined = undefined;
-                            if (['TryDefinition', 'ChoiceDefinition'].includes(step.dslName)) {
+                            if ('ChoiceDefinition' === step.dslName) {
+                                nextStep = props.nextStep;
+                            } else if ('TryDefinition' === step.dslName && ['CatchDefinition', 'FinallyDefinition'].includes(element.dslName)) {
                                 nextStep = props.nextStep;
                             } else {
                                 nextStep = children.at(index + 1);
@@ -244,6 +217,7 @@ export function DslElement(props: Props) {
                                     step={element}
                                     nextStep={nextStep}
                                     prevStep={prevStep}
+                                    inStepsLength={array.length}
                                     parent={step}/>
                             </div>)
                         }
@@ -266,12 +240,10 @@ export function DslElement(props: Props) {
         const hideAddButton = step.dslName === 'StepDefinition' && !CamelDisplayUtil.isStepDefinitionExpanded(integration, step.uuid, selectedUuids.at(0));
         if (hideAddButton) return (<></>)
         else return (
-            <div ref={addButtonRef}>
-                <Tooltip position={"bottom"}
+                <Tooltip position={"left"}
                          content={<div>{"Add step to " + CamelDisplayUtil.getTitle(step)}</div>}
                 >
                     <button type="button"
-                            ref={el => sendButtonPosition(el)}
                             aria-label="Add"
                             onClick={e => onOpenSelector(e)}
                             className={isAddStepButtonLeft() ? "add-button add-button-left" : "add-button add-button-bottom"}>
@@ -279,13 +251,12 @@ export function DslElement(props: Props) {
                     </button>
 
                 </Tooltip>
-            </div>
         )
     }
 
     const element: CamelElement = props.step;
     const className = "step-element"
-        + (isElementSelected() ? " step-element-selected" : "") + (!props.step.showChildren ? " hidden-step" : "")
+        + (!props.step.showChildren ? " hidden-step" : "")
         + ((element as any).disabled ? " disabled " : "");
     return (
         <div key={"root" + element.uuid}
diff --git a/karavan-designer/src/designer/route/element/DslElementHeader.tsx b/karavan-designer/src/designer/route/element/DslElementHeader.tsx
index 9ac4ab99..d201d105 100644
--- a/karavan-designer/src/designer/route/element/DslElementHeader.tsx
+++ b/karavan-designer/src/designer/route/element/DslElementHeader.tsx
@@ -20,7 +20,6 @@ import '../../karavan.css';
 import './DslElement.css';
 import {CamelElement} from "karavan-core/lib/model/IntegrationDefinition";
 import {CamelUi} from "../../utils/CamelUi";
-import {EventBus} from "../../utils/EventBus";
 import {ChildElement, CamelDefinitionApiExt} from "karavan-core/lib/api/CamelDefinitionApiExt";
 import {CamelUtil} from "karavan-core/lib/api/CamelUtil";
 import {CamelDisplayUtil} from "karavan-core/lib/api/CamelDisplayUtil";
@@ -28,11 +27,7 @@ import {useDesignerStore} from "../../DesignerStore";
 import {shallow} from "zustand/shallow";
 import {useRouteDesignerHook} from "../useRouteDesignerHook";
 import {AddElementIcon, DeleteElementIcon, InsertElementIcon} from "./DslElementIcons";
-import {
-    InterceptDefinition,
-    InterceptFromDefinition,
-    InterceptSendToEndpointDefinition, OnCompletionDefinition, OnExceptionDefinition, RouteConfigurationDefinition
-} from "karavan-core/lib/model/CamelDefinition";
+import { RouteConfigurationDefinition} from "karavan-core/lib/model/CamelDefinition";
 
 interface Props {
     headerRef: React.RefObject<HTMLDivElement>
@@ -79,12 +74,8 @@ export function DslElementHeader(props: Props) {
         return selectedUuids.includes(props.step.uuid);
     }
 
-    function isElementHidden(): boolean {
-        return props.step.dslName === 'LogDefinition' && hideLogDSL;
-    }
-
     function isWide(): boolean {
-        return ['RouteConfigurationDefinition', 'RouteDefinition', 'ChoiceDefinition', 'SwitchDefinition', 'MulticastDefinition', 'TryDefinition', 'CircuitBreakerDefinition']
+        return ['RouteConfigurationDefinition', 'RouteDefinition', 'ChoiceDefinition', 'MulticastDefinition', 'TryDefinition', 'CircuitBreakerDefinition']
             .includes(props.step.dslName);
     }
 
@@ -129,29 +120,6 @@ export function DslElementHeader(props: Props) {
         return style;
     }
 
-    function sendPosition(el: HTMLDivElement | null) {
-        const {step, prevStep, parent} = props;
-        const isSelected = isElementSelected();
-        const isHidden = isElementHidden();
-        if (el) {
-            const header = Array.from(el.childNodes.values()).filter((n: any) => n.classList.contains("header"))[0];
-            if (header) {
-                const headerIcon: any = Array.from(header.childNodes.values()).filter((n: any) => n.classList.contains("header-icon"))[0];
-                const headerRect = headerIcon.getBoundingClientRect();
-                const rect = el.getBoundingClientRect();
-                if (step.showChildren) {
-                    if (isHidden) {
-                        EventBus.sendPosition("add", step, prevStep, parent, rect, headerRect, props.position, props.inSteps, isSelected);
-                    } else {
-                        EventBus.sendPosition("add", step, prevStep, parent, rect, headerRect, props.position, props.inSteps, isSelected);
-                    }
-                } else {
-                    EventBus.sendPosition("delete", step, prevStep, parent, new DOMRect(), new DOMRect(), 0);
-                }
-            }
-        }
-    }
-
     function getAvailableModels() { // TODO: make static list-of-values instead
         const step: CamelElement = props.step
         return CamelUi.getSelectorModelsForParent(step.dslName, false);
@@ -174,10 +142,13 @@ export function DslElementHeader(props: Props) {
         const classes: string[] = [];
         const step: CamelElement = props.step;
         if (step.dslName === 'RouteDefinition') {
-            classes.push(...'header-route', 'header-bottom-line')
+            classes.push('header-route')
+            classes.push('header-bottom-line')
+            classes.push(isElementSelected() ? 'header-bottom-selected' : 'header-bottom-not-selected')
         } else if (step.dslName === 'RouteConfigurationDefinition') {
             classes.push('header-route')
             if (hasElements(step)) classes.push('header-bottom-line')
+            classes.push(isElementSelected() ? 'header-bottom-selected' : 'header-bottom-not-selected')
         } else {
             classes.push('header')
         }
@@ -200,7 +171,6 @@ export function DslElementHeader(props: Props) {
             <div className={"dsl-element " + headerClasses} style={getHeaderStyle()} ref={props.headerRef}>
                 {!['RouteConfigurationDefinition', 'RouteDefinition'].includes(props.step.dslName) &&
                     <div
-                        ref={el => sendPosition(el)}
                         className={"header-icon"}
                         style={isWide() ? {width: ""} : {}}>
                         {CamelUi.getIconForElement(step)}
diff --git a/karavan-designer/src/designer/route/useRouteDesignerHook.tsx b/karavan-designer/src/designer/route/useRouteDesignerHook.tsx
index b65966a0..0f90bb10 100644
--- a/karavan-designer/src/designer/route/useRouteDesignerHook.tsx
+++ b/karavan-designer/src/designer/route/useRouteDesignerHook.tsx
@@ -105,7 +105,7 @@ export function useRouteDesignerHook () {
     }
 
     const deleteElement = () =>  {
-        EventBus.sendPosition("clean", new CamelElement(""), undefined, undefined, new DOMRect(), new DOMRect(), 0);
+        EventBus.sendPosition("clean", new CamelElement(""), undefined,undefined, undefined, new DOMRect(), new DOMRect(), 0, 0);
         EventBus.sendButtonPosition("clean", '',  new CamelElement(""), new DOMRect());
         let i = integration;
         selectedUuids.forEach(uuidToDelete => {
diff --git a/karavan-designer/src/designer/utils/EventBus.ts b/karavan-designer/src/designer/utils/EventBus.ts
index 55906f0f..2d075b00 100644
--- a/karavan-designer/src/designer/utils/EventBus.ts
+++ b/karavan-designer/src/designer/utils/EventBus.ts
@@ -38,10 +38,12 @@ export class ButtonPosition {
 export class DslPosition {
     step: CamelElement = new CamelElement("");
     prevStep: CamelElement | undefined;
+    nextstep: CamelElement | undefined;
     parent: CamelElement | undefined;
     inSteps: boolean = false;
     isSelected: boolean = false;
     position: number = 0;
+    inStepsLength: number = 0;
     rect: DOMRect = new DOMRect();
     headerRect: DOMRect = new DOMRect();
     command: "add" | "delete" | "clean" = "add";
@@ -49,20 +51,24 @@ export class DslPosition {
     constructor(command: "add" | "delete" | "clean",
                 step: CamelElement,
                 prevStep: CamelElement | undefined,
+                nextstep: CamelElement | undefined,
                 parent:CamelElement | undefined,
                 rect: DOMRect,
                 headerRect:DOMRect,
                 position: number,
+                inStepsLength: number,
                 inSteps: boolean = false,
                 isSelected: boolean = false) {
         this.command = command;
         this.step = step;
+        this.nextstep = nextstep;
         this.prevStep = prevStep;
         this.parent = parent;
         this.rect = rect;
         this.headerRect = headerRect;
         this.inSteps = inSteps;
         this.position = position;
+        this.inStepsLength = inStepsLength;
         this.isSelected = isSelected;
     }
 }
@@ -110,12 +116,15 @@ export const EventBus = {
     sendPosition: (command: "add" | "delete" | "clean",
                    step: CamelElement,
                    prevStep: CamelElement | undefined,
+                   nextstep: CamelElement | undefined,
                    parent: CamelElement | undefined,
                    rect: DOMRect,
                    headerRect: DOMRect,
                    position: number,
+                   inStepsLength: number,
                    inSteps: boolean = false,
-                   isSelected: boolean = false) => dslPositions.next(new DslPosition(command, step, prevStep, parent, rect, headerRect, position, inSteps, isSelected)),
+                   isSelected: boolean = false) => dslPositions.next(
+                       new DslPosition(command, step, prevStep, nextstep, parent, rect, headerRect, position, inStepsLength, inSteps, isSelected)),
     onPosition: () => dslPositions.asObservable(),
 
     sendButtonPosition: (command: "add" | "delete" | "clean", uuid: string,