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 133b002adaaafe08dbac34b489f87985d62bee7b
Author: Marat Gubaidullin <ma...@talismancloud.io>
AuthorDate: Wed Dec 6 21:36:57 2023 -0500

    Preview fixes of new Designer
---
 karavan-space/src/designer/DesignerStore.ts        |  34 +--
 .../src/designer/route/DslConnections.tsx          | 200 ++++++++-------
 karavan-space/src/designer/route/RouteDesigner.tsx |   6 +-
 .../src/designer/route/element/DslElement.css      |  30 ++-
 .../src/designer/route/element/DslElement.tsx      | 265 +++-----------------
 .../designer/route/element/DslElementHeader.tsx    | 275 +++++++++++++++++++++
 .../src/designer/route/useRouteDesignerHook.tsx    |   3 +-
 karavan-space/src/designer/utils/CamelUi.tsx       |   3 +-
 karavan-space/src/designer/utils/EventBus.ts       |  34 +--
 .../src/main/webui/src/designer/DesignerStore.ts   |  34 +--
 .../webui/src/designer/route/DslConnections.tsx    | 200 ++++++++-------
 .../webui/src/designer/route/RouteDesigner.tsx     |   6 +-
 .../src/designer/route/element/DslElement.css      |  30 ++-
 .../src/designer/route/element/DslElement.tsx      | 265 +++-----------------
 .../designer/route/element/DslElementHeader.tsx    | 275 +++++++++++++++++++++
 .../src/designer/route/useRouteDesignerHook.tsx    |   3 +-
 .../src/main/webui/src/designer/utils/CamelUi.tsx  |   3 +-
 .../src/main/webui/src/designer/utils/EventBus.ts  |  34 +--
 18 files changed, 908 insertions(+), 792 deletions(-)

diff --git a/karavan-space/src/designer/DesignerStore.ts 
b/karavan-space/src/designer/DesignerStore.ts
index f9bc38d1..407713a7 100644
--- a/karavan-space/src/designer/DesignerStore.ts
+++ b/karavan-space/src/designer/DesignerStore.ts
@@ -16,7 +16,7 @@
  */
 
 import {CamelElement, Integration} from 
"karavan-core/lib/model/IntegrationDefinition";
-import {ButtonPosition, DslPosition, EventBus} from "./utils/EventBus";
+import {DslPosition, EventBus} from "./utils/EventBus";
 import {createWithEqualityFn} from "zustand/traditional";
 import {shallow} from "zustand/shallow";
 
@@ -120,10 +120,6 @@ interface ConnectionsState {
     deleteStep: (uuid: string) => void;
     clearSteps: () => void;
     setSteps: (steps: Map<string, DslPosition>) => void;
-    buttons: ButtonPosition[];
-    addButton: (button: ButtonPosition) => void;
-    deleteButton: (button: ButtonPosition) => void;
-    clearButtons: () => void;
 }
 
 export const useConnectionsStore = 
createWithEqualityFn<ConnectionsState>((set) => ({
@@ -138,6 +134,8 @@ export const useConnectionsStore = 
createWithEqualityFn<ConnectionsState>((set)
             // state.steps.clear();
             Array.from(state.steps.entries())
                 .filter(value => value[1]?.parent?.uuid !== uuid)
+                .filter(value => value[1]?.prevStep?.uuid !== uuid)
+                .filter(value => value[1]?.nextstep?.uuid !== uuid)
                 .forEach(value => state.steps.set(value[0], value[1]));
             state.steps.delete(uuid)
             return state;
@@ -151,31 +149,7 @@ 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;
-        })
-    },
-    clearButtons: () => {
-        set((state: ConnectionsState) => {
-            state.buttons.length = 0;
-            return state;
-        })
-    },
-    deleteButton: (button: ButtonPosition) => {
-        set((state: ConnectionsState) => {
-            const index = state.buttons.findIndex(b => b.uuid === button.uuid);
-            state.buttons.splice(index, 1);
-            return state;
-        })
-    },
+    }
 }), shallow)
 
 type DesignerState = {
diff --git a/karavan-space/src/designer/route/DslConnections.tsx 
b/karavan-space/src/designer/route/DslConnections.tsx
index 5f9467b6..c7651aac 100644
--- a/karavan-space/src/designer/route/DslConnections.tsx
+++ b/karavan-space/src/designer/route/DslConnections.tsx
@@ -14,15 +14,16 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import React, {useEffect} from 'react';
+import React, {JSX, useEffect, useState} from 'react';
 import '../karavan.css';
-import {CamelElement} from "karavan-core/lib/model/IntegrationDefinition";
-import {ButtonPosition, DslPosition, EventBus} from "../utils/EventBus";
+import {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";
+import {v4 as uuidv4} from "uuid";
 
 const overlapGap: number = 40;
 
@@ -31,34 +32,24 @@ export function DslConnections() {
     const [integration] = useIntegrationStore((state) => [state.integration], 
shallow)
     const [width, height, top, left, hideLogDSL] = useDesignerStore((s) =>
         [s.width, s.height, s.top, s.left, s.hideLogDSL], shallow)
-    const [steps, addStep, deleteStep, clearSteps, buttons, addButton, 
clearButtons, deleteButton] =
-        useConnectionsStore((s) => [s.steps, s.addStep, s.deleteStep, 
s.clearSteps,
-            s.buttons, s.addButton, s.clearButtons, s.deleteButton], shallow)
+    const [steps, addStep, deleteStep, clearSteps] =
+        useConnectionsStore((s) => [s.steps, s.addStep, s.deleteStep, 
s.clearSteps], shallow)
+
+    const [svgKey, setSvgKey] = useState<string>('svgKey');
 
     useEffect(() => {
         const sub1 = EventBus.onPosition()?.subscribe((evt: DslPosition) => 
setPosition(evt));
-        const sub2 = EventBus.onButtonPosition()?.subscribe((btn: 
ButtonPosition) => setButtonPosition(btn));
         return () => {
             sub1?.unsubscribe();
-            sub2?.unsubscribe();
         };
     });
 
     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));
+        setSvgKey(uuidv4())
     }, [integration]);
 
-    function setButtonPosition(btn: ButtonPosition) {
-        if (btn.command === "add") {
-            addButton(btn);
-        } else if (btn.command === "delete") {
-            deleteButton(btn);
-        } else if (btn.command === "clean") {
-            clearButtons();
-        }
-    }
-
     function setPosition(evt: DslPosition) {
         if (evt.command === "add") {
             addStep(evt.step.uuid, evt);
@@ -103,8 +94,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 +178,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 +213,113 @@ 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;
+            const key = from.step.uuid + "->" + to.step.uuid;
+            result.push(getComplexArrow(key, 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 === 'TryDefinition' && 
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 === '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, 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, 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.parent?.dslName !== 'MulticastDefinition') {
+            const next = steps.get(pos.nextstep.uuid);
+            if (pos.step.hasSteps() && pos.prevStep) {
+            } else {
+                list.push(...addArrowToList(list, pos, next, true, true))
+            }
+        }
+
+        if (!isSpecial(pos) && pos.inSteps && pos.nextstep && 
pos.parent?.dslName !== 'MulticastDefinition') {
+            const next = steps.get(pos.nextstep.uuid);
+            if (next && !isSpecial(next) && next.inSteps) {
+                // console.log(pos)
+                // const to = steps.get(parent.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 +330,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,28 +357,27 @@ 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={uuidv4()} name={key} d={path} className="path" 
markerEnd="url(#arrowhead)"/>
             )
     }
 
     function getSvg() {
         const stepsArray = Array.from(steps.values());
+        const arrows = stepsArray.map(pos => getArrow(pos)).flat(1);
+        const uniqueArrows = [...new Map(arrows.map(item =>  [(item as 
any).key, item])).values()]
         return (
-            <svg
+            <svg key={svgKey}
                 style={{width: width, height: height, position: "absolute", 
left: 0, top: 0}}
                 viewBox={"0 0 " + (width) + " " + (height)}>
                 <defs>
-                    <marker id="arrowhead" markerWidth="9" markerHeight="6" 
refX="0" refY="3" orient="auto"
-                            className="arrow">
+                    <marker id="arrowhead" markerWidth="9" markerHeight="6" 
refX="0" refY="3" orient="auto" className="arrow">
                         <polygon points="0 0, 9 3, 0 6"/>
                     </marker>
                 </defs>
                 {stepsArray.map(pos => getCircle(pos))}
-                {stepsArray.map(pos => getArrow(pos))}
-                {buttons.map(btn => getButtonArrow(btn)).filter(b => b !== 
undefined)}
+                {uniqueArrows}
                 {getIncomings().map(p => getIncoming(p))}
                 {getOutgoings().map(p => getOutgoing(p))}
-                {/*{getInternals().map((p) => getInternalLines(p)).flat()}*/}
             </svg>
         )
     }
diff --git a/karavan-space/src/designer/route/RouteDesigner.tsx 
b/karavan-space/src/designer/route/RouteDesigner.tsx
index b6b1c942..b02ed20b 100644
--- a/karavan-space/src/designer/route/RouteDesigner.tsx
+++ b/karavan-space/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-space/src/designer/route/element/DslElement.css 
b/karavan-space/src/designer/route/element/DslElement.css
index 3039f422..61cc5af3 100644
--- a/karavan-space/src/designer/route/element/DslElement.css
+++ b/karavan-space/src/designer/route/element/DslElement.css
@@ -17,12 +17,24 @@
 
 .karavan .dsl-page .flows .step-element .header-route {
     display: block;
-    border: none;
     background: transparent;
-    padding: 0;
-    margin: 3px 24px 10px 24px;
-    /*min-width: 260px;*/
+    border-radius: 42px;
+    padding: 20px;
+    margin: 0;
     z-index: 101;
+    min-width: 260px;
+}
+
+.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 {
+    cursor: pointer;
 }
 
 .karavan .step-element .header-route .delete-button {
@@ -41,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;
@@ -65,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);
@@ -145,7 +153,7 @@
 
 .karavan .step-element .insert-element-button {
     position: absolute;
-    top: -7px;
+    top: -11px;
     line-height: 1;
     border: 0;
     padding: 0;
@@ -214,4 +222,4 @@
     width: 20px;
     height: 20px;
     background: white;
-}
\ No newline at end of file
+}
diff --git a/karavan-space/src/designer/route/element/DslElement.tsx 
b/karavan-space/src/designer/route/element/DslElement.tsx
index 1deb46da..967d3321 100644
--- a/karavan-space/src/designer/route/element/DslElement.tsx
+++ b/karavan-space/src/designer/route/element/DslElement.tsx
@@ -14,20 +14,19 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import React, {CSSProperties, useMemo, useState} from 'react';
-import {Text, Tooltip,} from '@patternfly/react-core';
+import React, {CSSProperties, useState} from 'react';
+import {Tooltip,} from '@patternfly/react-core';
 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";
 import {useDesignerStore, useIntegrationStore} from "../../DesignerStore";
 import {shallow} from "zustand/shallow";
 import {useRouteDesignerHook} from "../useRouteDesignerHook";
-import {AddElementIcon, DeleteElementIcon, InsertElementIcon} from 
"./DslElementIcons";
+import {AddElementIcon} from "./DslElementIcons";
+import {DslElementHeader} from "./DslElementHeader";
 
 interface Props {
     step: CamelElement,
@@ -36,12 +35,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,
@@ -70,11 +69,6 @@ export function DslElement(props: Props) {
         }
     }
 
-    function onDeleteElement(evt: React.MouseEvent) {
-        evt.stopPropagation();
-        onShowDeleteConfirmation(props.step.uuid);
-    }
-
     function onSelectElement(evt: React.MouseEvent) {
         evt.stopPropagation();
         selectElement(props.step);
@@ -100,18 +94,15 @@ 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'].includes(step.dslName)) {
+        if (['FilterDefinition', 'RouteDefinition', 
'RouteConfigurationDefinition'].includes(step.dslName)) {
             return true;
         }
-        if (['FromDefinition',
-            'RouteDefinition',
+        if ([
+            'FromDefinition',
             'TryDefinition',
+            'MulticastDefinition',
             'CatchDefinition', 'FinallyDefinition',
             'ChoiceDefinition',
             'SwitchDefinition', 'WhenDefinition', 'OtherwiseDefinition'
@@ -125,11 +116,6 @@ export function DslElement(props: Props) {
         return ['FromDefinition', 'RouteConfigurationDefinition', 
'RouteDefinition', 'WhenDefinition', 
'OtherwiseDefinition'].includes(props.step.dslName);
     }
 
-    function isWide(): boolean {
-        return ['RouteConfigurationDefinition', 'RouteDefinition', 
'ChoiceDefinition', 'SwitchDefinition', 'MulticastDefinition', 'TryDefinition', 
'CircuitBreakerDefinition']
-            .includes(props.step.dslName);
-    }
-
     function isAddStepButtonLeft(): boolean {
         return ['MulticastDefinition']
             .includes(props.step.dslName);
@@ -139,9 +125,6 @@ export function DslElement(props: Props) {
         return ['MulticastDefinition'].includes(props.step.dslName);
     }
 
-    function isRoot(): boolean {
-        return ['RouteConfigurationDefinition', 
'RouteDefinition'].includes(props.step?.dslName);
-    }
 
     function isInStepWithChildren() {
         const step: CamelElement = props.step;
@@ -149,71 +132,10 @@ export function DslElement(props: Props) {
         return children.filter((c: ChildElement) => c.name === 'steps' || 
c.multiple).length > 0 && props.inSteps;
     }
 
-    function getChildrenInfo(step: CamelElement): [boolean, number, boolean, 
number, number] {
-        const children = 
CamelDefinitionApiExt.getElementChildrenDefinition(step.dslName);
-        const hasStepsField = children.filter((c: ChildElement) => c.name === 
'steps').length === 1;
-        const stepsChildrenCount = children
-            .filter(c => c.name === 'steps')
-            .map((child: ChildElement, index: number) => {
-                const children: CamelElement[] = 
CamelDefinitionApiExt.getElementChildren(step, child);
-                return children.length;
-            }).reduce((a, b) => a + b, 0);
-
-        const hasNonStepsFields = children.filter(c => c.name !== 'steps' && 
c.name !== 'expression' && c.name !== 'onWhen').length > 0;
-        const childrenCount = children
-            .map((child: ChildElement, index: number) => {
-                const children: CamelElement[] = 
CamelDefinitionApiExt.getElementChildren(step, child);
-                return children.length;
-            }).reduce((a, b) => a + b, 0);
-        const nonStepChildrenCount = childrenCount - stepsChildrenCount;
-        return [hasStepsField, stepsChildrenCount, hasNonStepsFields, 
nonStepChildrenCount, childrenCount]
-    }
-
-    function hasWideChildrenElement() {
-        const [hasStepsField, stepsChildrenCount, hasNonStepsFields, 
nonStepChildrenCount, childrenCount] = getChildrenInfo(props.step);
-        if (isHorizontal() && stepsChildrenCount > 1) return true;
-        else if (hasStepsField && stepsChildrenCount > 0 && hasNonStepsFields 
&& nonStepChildrenCount > 0) return true;
-        else if (!hasStepsField && hasNonStepsFields && childrenCount > 1) 
return true;
-        else if (hasStepsField && stepsChildrenCount > 0 && hasNonStepsFields 
&& childrenCount > 1) return true;
-        else return false;
-    }
-
-    function hasBorderOverSteps(step: CamelElement) {
-        const [hasStepsField, stepsChildrenCount, hasNonStepsFields, 
nonStepChildrenCount] = getChildrenInfo(step);
-        if (hasStepsField && stepsChildrenCount > 0 && hasNonStepsFields && 
nonStepChildrenCount > 0) return true;
-        else return false;
-    }
-
-    function getHeaderStyle() {
-        const style: CSSProperties = {
-            width: isWide() ? "100%" : "",
-            fontWeight: isElementSelected() ? "bold" : "normal",
-        };
-        return style;
-    }
-
-    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) {
@@ -221,109 +143,14 @@ 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);
-                    }
-                } else {
-                    EventBus.sendPosition("delete", step, prevStep, parent, 
new DOMRect(), new DOMRect(), 0);
+                    EventBus.sendPosition("add", step, prevStep, nextStep, 
parent, rect, headerRect, props.position, inStepsLength, inSteps, isSelected);
                 }
             }
-        }
-    }
-
-    function getAvailableModels() { // TODO: make static list-of-values instead
-        const step: CamelElement = props.step
-        return CamelUi.getSelectorModelsForParent(step.dslName, false);
-    }
-
-    const availableModels = useMemo(
-        () => getAvailableModels(),
-        [props.step.dslName]
-    );
-
-
-    function getHeader() {
-        const step: CamelElement = props.step;
-        const parent = props.parent;
-        const inRouteConfiguration = parent !== undefined && parent.dslName 
=== 'RouteConfigurationDefinition';
-        const showAddButton = !['CatchDefinition', 
'RouteDefinition'].includes(step.dslName) && availableModels.length > 0;
-        const showInsertButton =
-            !['FromDefinition', 'RouteConfigurationDefinition', 
'RouteDefinition', 'CatchDefinition', 'FinallyDefinition', 'WhenDefinition', 
'OtherwiseDefinition'].includes(step.dslName)
-            && !inRouteConfiguration;
-        const headerClass = ['RouteConfigurationDefinition', 
'RouteDefinition'].includes(step.dslName) ? "header-route" : "header"
-        const headerClasses = isElementSelected() ? headerClass + " selected" 
: headerClass;
-        return (
-            <div className={"dsl-element " + headerClasses} 
style={getHeaderStyle()} ref={headerRef}>
-                {!['RouteConfigurationDefinition', 
'RouteDefinition'].includes(props.step.dslName) &&
-                    <div
-                        ref={el => sendPosition(el)}
-                        className={"header-icon"}
-                        style={isWide() ? {width: ""} : {}}>
-                        {CamelUi.getIconForElement(step)}
-                    </div>
-                }
-                <div className={hasWideChildrenElement() ? "header-text" : ""}>
-                    {hasWideChildrenElement() && <div className="spacer"/>}
-                    {getHeaderTextWithTooltip(step)}
-                </div>
-                {showInsertButton && getInsertElementButton()}
-                {getDeleteButton()}
-                {showAddButton && getAddElementButton()}
-            </div>
-        )
-    }
-
-    function getHeaderText(step: CamelElement): string {
-        if (isKamelet() && step.dslName === 'ToDefinition' && (step as 
any).uri === 'kamelet:sink') {
-            return "Sink";
-        } else if (isKamelet() && step.dslName === 'FromDefinition' && (step 
as any).uri === 'kamelet:source') {
-            return "Source";
         } else {
-            return (step as any).description ? (step as any).description : 
CamelUi.getElementTitle(props.step);
+            EventBus.sendPosition("delete", step, prevStep, nextStep, parent, 
new DOMRect(), new DOMRect(), 0, 0);
         }
     }
 
-    function getHeaderTextWithTooltip(step: CamelElement) {
-        const checkRequired = CamelUtil.checkRequired(step);
-        const title = getHeaderText(step);
-        let className = hasWideChildrenElement() ? "text text-right" : "text 
text-bottom";
-        if (!checkRequired[0]) className = className + " header-text-required";
-        if (checkRequired[0]) {
-            return <Text className={className}>{title}</Text>
-        } else return (
-            <Tooltip position={"right"} className="tooltip-required-field"
-                     content={checkRequired[1].map((text, i) => (<div 
key={i}>{text}</div>))}>
-                <Text className={className}>{title}</Text>
-            </Tooltip>
-        )
-    }
-
-    function getHeaderWithTooltip(tooltip: string | undefined) {
-        return (
-            <>
-                {getHeader()}
-                <Tooltip triggerRef={headerRef} position={"left"} 
content={<div>{tooltip}</div>}/>
-            </>
-
-        )
-    }
-
-    function getHeaderTooltip(): string | undefined {
-        if (CamelUi.isShowExpressionTooltip(props.step)) return 
CamelUi.getExpressionTooltip(props.step);
-        if (CamelUi.isShowUriTooltip(props.step)) return 
CamelUi.getUriTooltip(props.step);
-        return undefined;
-    }
-
-    function getElementHeader() {
-        const tooltip = getHeaderTooltip();
-        if (tooltip !== undefined && !isDragging) {
-            return getHeaderWithTooltip(tooltip);
-        }
-        return getHeader();
-    }
-
     function getChildrenStyle() {
         const style: CSSProperties = {
             display: "flex",
@@ -333,10 +160,7 @@ export function DslElement(props: Props) {
     }
 
     function getChildrenElementsStyle(child: ChildElement, notOnlySteps: 
boolean) {
-        const step = props.step;
-        const isBorder = child.name === 'steps' && hasBorderOverSteps(step);
         const style: CSSProperties = {
-            // borderStyle: isBorder ? "dotted" : "none",
             borderColor: "var(--step-border-color)",
             borderWidth: "1px",
             borderRadius: "16px",
@@ -375,10 +199,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 = undefined;
-                            if (['TryDefinition', 
'ChoiceDefinition'].includes(step.dslName)) {
+                            let nextStep: CamelElement | undefined = undefined;
+                            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);
@@ -390,6 +216,7 @@ export function DslElement(props: Props) {
                                     step={element}
                                     nextStep={nextStep}
                                     prevStep={prevStep}
+                                    inStepsLength={array.length}
                                     parent={step}/>
                             </div>)
                         }
@@ -408,16 +235,14 @@ export function DslElement(props: Props) {
     }
 
     function getAddStepButton() {
-        const {step, nextStep} = props;
+        const {step} = 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"}>
@@ -425,51 +250,12 @@ export function DslElement(props: Props) {
                     </button>
 
                 </Tooltip>
-            </div>
-        )
-    }
-
-    function getAddElementButton() {
-        return (
-            <Tooltip position={"bottom"}
-                     content={<div>{"Add DSL element to " + 
CamelDisplayUtil.getTitle(props.step)}</div>}>
-                <button
-                    type="button"
-                    aria-label="Add"
-                    onClick={e => onOpenSelector(e, false)}
-                    className={"add-element-button"}>
-                    <AddElementIcon/>
-                </button>
-            </Tooltip>
-        )
-    }
-
-    function getInsertElementButton() {
-        return (
-            <Tooltip position={"left"} content={<div>{"Insert element 
before"}</div>}>
-                <button type="button"
-                        aria-label="Insert"
-                        onClick={e => onOpenSelector(e, true, true)}
-                        className={"insert-element-button"}>
-                    <InsertElementIcon/>
-                </button>
-            </Tooltip>
-        )
-    }
-
-    function getDeleteButton() {
-        return (
-            <Tooltip position={"right"} content={<div>{"Delete 
element"}</div>}>
-                <button type="button" aria-label="Delete" onClick={e => 
onDeleteElement(e)} className="delete-button">
-                    <DeleteElementIcon/>
-                </button>
-            </Tooltip>
         )
     }
 
     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}
@@ -516,7 +302,14 @@ export function DslElement(props: Props) {
              onDrop={event => dragElement(event, element)}
              draggable={!isNotDraggable()}
         >
-            {getElementHeader()}
+            <DslElementHeader headerRef={headerRef}
+                              step={props.step}
+                              parent={props.parent}
+                              nextStep={props.nextStep}
+                              prevStep={props.prevStep}
+                              inSteps={props.inSteps}
+                              isDragging={isDragging}
+                              position={props.position}/>
             {getChildElements()}
         </div>
     )
diff --git a/karavan-space/src/designer/route/element/DslElementHeader.tsx 
b/karavan-space/src/designer/route/element/DslElementHeader.tsx
new file mode 100644
index 00000000..d201d105
--- /dev/null
+++ b/karavan-space/src/designer/route/element/DslElementHeader.tsx
@@ -0,0 +1,275 @@
+/*
+ * 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 React, {CSSProperties, useMemo} from 'react';
+import {Text, Tooltip,} from '@patternfly/react-core';
+import '../../karavan.css';
+import './DslElement.css';
+import {CamelElement} from "karavan-core/lib/model/IntegrationDefinition";
+import {CamelUi} from "../../utils/CamelUi";
+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";
+import {useDesignerStore} from "../../DesignerStore";
+import {shallow} from "zustand/shallow";
+import {useRouteDesignerHook} from "../useRouteDesignerHook";
+import {AddElementIcon, DeleteElementIcon, InsertElementIcon} from 
"./DslElementIcons";
+import { RouteConfigurationDefinition} from 
"karavan-core/lib/model/CamelDefinition";
+
+interface Props {
+    headerRef: React.RefObject<HTMLDivElement>
+    step: CamelElement,
+    parent: CamelElement | undefined,
+    nextStep: CamelElement | undefined,
+    prevStep: CamelElement | undefined,
+    inSteps: boolean
+    position: number
+    isDragging: boolean
+}
+
+export function DslElementHeader(props: Props) {
+
+    const {
+        selectElement,
+        moveElement,
+        onShowDeleteConfirmation,
+        openSelector,
+        isKamelet,
+        isSourceKamelet,
+        isActionKamelet
+    } = useRouteDesignerHook();
+
+    const [selectedUuids, selectedStep, showMoveConfirmation, 
setShowMoveConfirmation, hideLogDSL, setMoveElements] =
+        useDesignerStore((s) =>
+            [s.selectedUuids, s.selectedStep, s.showMoveConfirmation, 
s.setShowMoveConfirmation, s.hideLogDSL, s.setMoveElements], shallow)
+
+    function onOpenSelector(evt: React.MouseEvent, showSteps: boolean = true, 
isInsert: boolean = false) {
+        evt.stopPropagation();
+        if (isInsert && props.parent) {
+            openSelector(props.parent.uuid, props.parent.dslName, showSteps, 
props.position);
+        } else {
+            openSelector(props.step.uuid, props.step.dslName, showSteps);
+        }
+    }
+
+    function onDeleteElement(evt: React.MouseEvent) {
+        evt.stopPropagation();
+        onShowDeleteConfirmation(props.step.uuid);
+    }
+
+    function isElementSelected(): boolean {
+        return selectedUuids.includes(props.step.uuid);
+    }
+
+    function isWide(): boolean {
+        return ['RouteConfigurationDefinition', 'RouteDefinition', 
'ChoiceDefinition', 'MulticastDefinition', 'TryDefinition', 
'CircuitBreakerDefinition']
+            .includes(props.step.dslName);
+    }
+
+    function isHorizontal(): boolean {
+        return ['MulticastDefinition'].includes(props.step.dslName);
+    }
+
+    function getChildrenInfo(step: CamelElement): [boolean, number, boolean, 
number, number] {
+        const children = 
CamelDefinitionApiExt.getElementChildrenDefinition(step.dslName);
+        const hasStepsField = children.filter((c: ChildElement) => c.name === 
'steps').length === 1;
+        const stepsChildrenCount = children
+            .filter(c => c.name === 'steps')
+            .map((child: ChildElement, index: number) => {
+                const children: CamelElement[] = 
CamelDefinitionApiExt.getElementChildren(step, child);
+                return children.length;
+            }).reduce((a, b) => a + b, 0);
+
+        const hasNonStepsFields = children.filter(c => c.name !== 'steps' && 
c.name !== 'expression' && c.name !== 'onWhen').length > 0;
+        const childrenCount = children
+            .map((child: ChildElement, index: number) => {
+                const children: CamelElement[] = 
CamelDefinitionApiExt.getElementChildren(step, child);
+                return children.length;
+            }).reduce((a, b) => a + b, 0);
+        const nonStepChildrenCount = childrenCount - stepsChildrenCount;
+        return [hasStepsField, stepsChildrenCount, hasNonStepsFields, 
nonStepChildrenCount, childrenCount]
+    }
+
+    function hasWideChildrenElement() {
+        const [hasStepsField, stepsChildrenCount, hasNonStepsFields, 
nonStepChildrenCount, childrenCount] = getChildrenInfo(props.step);
+        if (isHorizontal() && stepsChildrenCount > 1) return true;
+        else if (hasStepsField && stepsChildrenCount > 0 && hasNonStepsFields 
&& nonStepChildrenCount > 0) return true;
+        else if (!hasStepsField && hasNonStepsFields && childrenCount > 1) 
return true;
+        else if (hasStepsField && stepsChildrenCount > 0 && hasNonStepsFields 
&& childrenCount > 1) return true;
+        else return false;
+    }
+
+    function getHeaderStyle() {
+        const style: CSSProperties = {
+            width: isWide() ? "100%" : "",
+            fontWeight: isElementSelected() ? "bold" : "normal",
+        };
+        return style;
+    }
+
+    function getAvailableModels() { // TODO: make static list-of-values instead
+        const step: CamelElement = props.step
+        return CamelUi.getSelectorModelsForParent(step.dslName, false);
+    }
+
+    const availableModels = useMemo(
+        () => getAvailableModels(),
+        [props.step.dslName]
+    );
+
+    function hasElements(rc: RouteConfigurationDefinition): boolean {
+        return (rc.interceptFrom !== undefined && rc.interceptFrom.length > 0)
+    || (rc.intercept !== undefined && rc.intercept.length > 0)
+    || (rc.interceptSendToEndpoint !== undefined && 
rc.interceptSendToEndpoint.length > 0)
+    || (rc.onException !== undefined && rc.onException.length > 0)
+    || (rc.onCompletion !== undefined && rc.onCompletion.length > 0)
+    }
+
+    function getHeaderClasses(): string {
+        const classes: string[] = [];
+        const step: CamelElement = props.step;
+        if (step.dslName === 'RouteDefinition') {
+            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')
+        }
+        if (isElementSelected()) {
+            classes.push("selected")
+        }
+        return classes.join(" ");
+    }
+
+    function getHeader() {
+        const step: CamelElement = props.step;
+        const parent = props.parent;
+        const inRouteConfiguration = parent !== undefined && parent.dslName 
=== 'RouteConfigurationDefinition';
+        const showAddButton = !['CatchDefinition', 
'RouteDefinition'].includes(step.dslName) && availableModels.length > 0;
+        const showInsertButton =
+            !['FromDefinition', 'RouteConfigurationDefinition', 
'RouteDefinition', 'CatchDefinition', 'FinallyDefinition', 'WhenDefinition', 
'OtherwiseDefinition'].includes(step.dslName)
+            && !inRouteConfiguration;
+        const headerClasses = getHeaderClasses();
+        return (
+            <div className={"dsl-element " + headerClasses} 
style={getHeaderStyle()} ref={props.headerRef}>
+                {!['RouteConfigurationDefinition', 
'RouteDefinition'].includes(props.step.dslName) &&
+                    <div
+                        className={"header-icon"}
+                        style={isWide() ? {width: ""} : {}}>
+                        {CamelUi.getIconForElement(step)}
+                    </div>
+                }
+                <div className={hasWideChildrenElement() ? "header-text" : ""}>
+                    {hasWideChildrenElement() && <div className="spacer"/>}
+                    {getHeaderTextWithTooltip(step)}
+                </div>
+                {showInsertButton && getInsertElementButton()}
+                {getDeleteButton()}
+                {showAddButton && getAddElementButton()}
+            </div>
+        )
+    }
+
+    function getHeaderText(step: CamelElement): string {
+        if (isKamelet() && step.dslName === 'ToDefinition' && (step as 
any).uri === 'kamelet:sink') {
+            return "Sink";
+        } else if (isKamelet() && step.dslName === 'FromDefinition' && (step 
as any).uri === 'kamelet:source') {
+            return "Source";
+        } else {
+            return (step as any).description ? (step as any).description : 
CamelUi.getElementTitle(props.step);
+        }
+    }
+
+    function getHeaderTextWithTooltip(step: CamelElement) {
+        const title = getHeaderText(step);
+        const checkRequired = CamelUtil.checkRequired(step);
+        let className = hasWideChildrenElement() ? "text text-right" : "text 
text-bottom";
+        if (!checkRequired[0]) className = className + " header-text-required";
+        if (checkRequired[0]) {
+            return <Text className={className}>{title}</Text>
+        } else return (
+            <Tooltip position={"right"} className="tooltip-required-field"
+                     content={checkRequired[1].map((text, i) => (<div 
key={i}>{text}</div>))}>
+                <Text className={className}>{title}</Text>
+            </Tooltip>
+        )
+    }
+
+    function getHeaderWithTooltip(tooltip: string | undefined) {
+        return (
+            <>
+                {getHeader()}
+                <Tooltip triggerRef={props.headerRef} position={"left"} 
content={<div>{tooltip}</div>}/>
+            </>
+
+        )
+    }
+
+    function getHeaderTooltip(): string | undefined {
+        if (CamelUi.isShowExpressionTooltip(props.step)) return 
CamelUi.getExpressionTooltip(props.step);
+        if (CamelUi.isShowUriTooltip(props.step)) return 
CamelUi.getUriTooltip(props.step);
+        return undefined;
+    }
+
+
+    function getAddElementButton() {
+        return (
+            <Tooltip position={"bottom"}
+                     content={<div>{"Add DSL element to " + 
CamelDisplayUtil.getTitle(props.step)}</div>}>
+                <button
+                    type="button"
+                    aria-label="Add"
+                    onClick={e => onOpenSelector(e, false)}
+                    className={"add-element-button"}>
+                    <AddElementIcon/>
+                </button>
+            </Tooltip>
+        )
+    }
+
+    function getInsertElementButton() {
+        return (
+            <Tooltip position={"left"} content={<div>{"Insert element 
before"}</div>}>
+                <button type="button"
+                        aria-label="Insert"
+                        onClick={e => onOpenSelector(e, true, true)}
+                        className={"insert-element-button"}>
+                    <InsertElementIcon/>
+                </button>
+            </Tooltip>
+        )
+    }
+
+    function getDeleteButton() {
+        return (
+            <Tooltip position={"right"} content={<div>{"Delete 
element"}</div>}>
+                <button type="button" aria-label="Delete" onClick={e => 
onDeleteElement(e)} className="delete-button">
+                    <DeleteElementIcon/>
+                </button>
+            </Tooltip>
+        )
+    }
+
+    const tooltip = getHeaderTooltip();
+    if (tooltip !== undefined && !props.isDragging) {
+        return getHeaderWithTooltip(tooltip);
+    }
+    return getHeader();
+}
diff --git a/karavan-space/src/designer/route/useRouteDesignerHook.tsx 
b/karavan-space/src/designer/route/useRouteDesignerHook.tsx
index b65966a0..1457adba 100644
--- a/karavan-space/src/designer/route/useRouteDesignerHook.tsx
+++ b/karavan-space/src/designer/route/useRouteDesignerHook.tsx
@@ -105,8 +105,7 @@ export function useRouteDesignerHook () {
     }
 
     const deleteElement = () =>  {
-        EventBus.sendPosition("clean", new CamelElement(""), undefined, 
undefined, new DOMRect(), new DOMRect(), 0);
-        EventBus.sendButtonPosition("clean", '',  new CamelElement(""), new 
DOMRect());
+        EventBus.sendPosition("clean", new CamelElement(""), 
undefined,undefined, undefined, new DOMRect(), new DOMRect(), 0, 0);
         let i = integration;
         selectedUuids.forEach(uuidToDelete => {
              i = CamelDefinitionApiExt.deleteStepFromIntegration(i, 
uuidToDelete);
diff --git a/karavan-space/src/designer/utils/CamelUi.tsx 
b/karavan-space/src/designer/utils/CamelUi.tsx
index 61a7d27c..5caae0a7 100644
--- a/karavan-space/src/designer/utils/CamelUi.tsx
+++ b/karavan-space/src/designer/utils/CamelUi.tsx
@@ -337,7 +337,7 @@ export class CamelUi {
     static getElementTitle = (element: CamelElement): string => {
         if (element.dslName === 'RouteDefinition') {
             const routeId = (element as RouteDefinition).id
-            return routeId ? "Route: " + routeId : 
CamelUtil.capitalizeName((element as any).stepName);
+            return routeId ? routeId : CamelUtil.capitalizeName((element as 
any).stepName);
         } else if (['ToDefinition', 'ToDynamicDefinition', 'FromDefinition', 
'KameletDefinition'].includes(element.dslName) && (element as any).uri) {
             const uri = (element as any).uri;
             const kameletTitle = uri && uri.startsWith("kamelet:") ? 
KameletApi.findKameletByUri(uri)?.title() : undefined;
@@ -787,4 +787,5 @@ export class CamelUi {
             .forEach((f: any) => result.push(f));
         return result;
     }
+
 }
\ No newline at end of file
diff --git a/karavan-space/src/designer/utils/EventBus.ts 
b/karavan-space/src/designer/utils/EventBus.ts
index 55906f0f..687bec9d 100644
--- a/karavan-space/src/designer/utils/EventBus.ts
+++ b/karavan-space/src/designer/utils/EventBus.ts
@@ -18,30 +18,15 @@ import {Subject} from 'rxjs';
 import {CamelElement, Integration} from 
"karavan-core/lib/model/IntegrationDefinition";
 import {v4 as uuidv4} from "uuid";
 
-export class ButtonPosition {
-    uuid: string = '';
-    nextstep: CamelElement = new CamelElement("");
-    rect: DOMRect = new DOMRect();
-    command: "add" | "delete" | "clean" = "add";
-
-    constructor(command: "add" | "delete" | "clean",
-                uuid: string,
-                nextstep: CamelElement,
-                rect: DOMRect) {
-        this.uuid = uuid;
-        this.command = command;
-        this.nextstep = nextstep;
-        this.rect = rect;
-    }
-}
-
 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 +34,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;
     }
 }
@@ -104,25 +93,22 @@ export class ToastMessage {
     }
 }
 const dslPositions = new Subject<DslPosition>();
-const buttonPositions = new Subject<ButtonPosition>();
 
 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,
-                   nextStep: CamelElement,
-                   rect: DOMRect) => buttonPositions.next(new 
ButtonPosition(command, uuid, nextStep, rect)),
-    onButtonPosition: () => buttonPositions.asObservable(),
-
     sendIntegrationUpdate: (i: Integration, propertyOnly: boolean) => 
updates.next(new IntegrationUpdate(i, propertyOnly)),
     onIntegrationUpdate: () => updates.asObservable(),
 
diff --git 
a/karavan-web/karavan-app/src/main/webui/src/designer/DesignerStore.ts 
b/karavan-web/karavan-app/src/main/webui/src/designer/DesignerStore.ts
index f9bc38d1..407713a7 100644
--- a/karavan-web/karavan-app/src/main/webui/src/designer/DesignerStore.ts
+++ b/karavan-web/karavan-app/src/main/webui/src/designer/DesignerStore.ts
@@ -16,7 +16,7 @@
  */
 
 import {CamelElement, Integration} from 
"karavan-core/lib/model/IntegrationDefinition";
-import {ButtonPosition, DslPosition, EventBus} from "./utils/EventBus";
+import {DslPosition, EventBus} from "./utils/EventBus";
 import {createWithEqualityFn} from "zustand/traditional";
 import {shallow} from "zustand/shallow";
 
@@ -120,10 +120,6 @@ interface ConnectionsState {
     deleteStep: (uuid: string) => void;
     clearSteps: () => void;
     setSteps: (steps: Map<string, DslPosition>) => void;
-    buttons: ButtonPosition[];
-    addButton: (button: ButtonPosition) => void;
-    deleteButton: (button: ButtonPosition) => void;
-    clearButtons: () => void;
 }
 
 export const useConnectionsStore = 
createWithEqualityFn<ConnectionsState>((set) => ({
@@ -138,6 +134,8 @@ export const useConnectionsStore = 
createWithEqualityFn<ConnectionsState>((set)
             // state.steps.clear();
             Array.from(state.steps.entries())
                 .filter(value => value[1]?.parent?.uuid !== uuid)
+                .filter(value => value[1]?.prevStep?.uuid !== uuid)
+                .filter(value => value[1]?.nextstep?.uuid !== uuid)
                 .forEach(value => state.steps.set(value[0], value[1]));
             state.steps.delete(uuid)
             return state;
@@ -151,31 +149,7 @@ 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;
-        })
-    },
-    clearButtons: () => {
-        set((state: ConnectionsState) => {
-            state.buttons.length = 0;
-            return state;
-        })
-    },
-    deleteButton: (button: ButtonPosition) => {
-        set((state: ConnectionsState) => {
-            const index = state.buttons.findIndex(b => b.uuid === button.uuid);
-            state.buttons.splice(index, 1);
-            return state;
-        })
-    },
+    }
 }), shallow)
 
 type DesignerState = {
diff --git 
a/karavan-web/karavan-app/src/main/webui/src/designer/route/DslConnections.tsx 
b/karavan-web/karavan-app/src/main/webui/src/designer/route/DslConnections.tsx
index 5f9467b6..c7651aac 100644
--- 
a/karavan-web/karavan-app/src/main/webui/src/designer/route/DslConnections.tsx
+++ 
b/karavan-web/karavan-app/src/main/webui/src/designer/route/DslConnections.tsx
@@ -14,15 +14,16 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import React, {useEffect} from 'react';
+import React, {JSX, useEffect, useState} from 'react';
 import '../karavan.css';
-import {CamelElement} from "karavan-core/lib/model/IntegrationDefinition";
-import {ButtonPosition, DslPosition, EventBus} from "../utils/EventBus";
+import {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";
+import {v4 as uuidv4} from "uuid";
 
 const overlapGap: number = 40;
 
@@ -31,34 +32,24 @@ export function DslConnections() {
     const [integration] = useIntegrationStore((state) => [state.integration], 
shallow)
     const [width, height, top, left, hideLogDSL] = useDesignerStore((s) =>
         [s.width, s.height, s.top, s.left, s.hideLogDSL], shallow)
-    const [steps, addStep, deleteStep, clearSteps, buttons, addButton, 
clearButtons, deleteButton] =
-        useConnectionsStore((s) => [s.steps, s.addStep, s.deleteStep, 
s.clearSteps,
-            s.buttons, s.addButton, s.clearButtons, s.deleteButton], shallow)
+    const [steps, addStep, deleteStep, clearSteps] =
+        useConnectionsStore((s) => [s.steps, s.addStep, s.deleteStep, 
s.clearSteps], shallow)
+
+    const [svgKey, setSvgKey] = useState<string>('svgKey');
 
     useEffect(() => {
         const sub1 = EventBus.onPosition()?.subscribe((evt: DslPosition) => 
setPosition(evt));
-        const sub2 = EventBus.onButtonPosition()?.subscribe((btn: 
ButtonPosition) => setButtonPosition(btn));
         return () => {
             sub1?.unsubscribe();
-            sub2?.unsubscribe();
         };
     });
 
     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));
+        setSvgKey(uuidv4())
     }, [integration]);
 
-    function setButtonPosition(btn: ButtonPosition) {
-        if (btn.command === "add") {
-            addButton(btn);
-        } else if (btn.command === "delete") {
-            deleteButton(btn);
-        } else if (btn.command === "clean") {
-            clearButtons();
-        }
-    }
-
     function setPosition(evt: DslPosition) {
         if (evt.command === "add") {
             addStep(evt.step.uuid, evt);
@@ -103,8 +94,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 +178,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 +213,113 @@ 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;
+            const key = from.step.uuid + "->" + to.step.uuid;
+            result.push(getComplexArrow(key, 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 === 'TryDefinition' && 
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 === '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, 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, 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.parent?.dslName !== 'MulticastDefinition') {
+            const next = steps.get(pos.nextstep.uuid);
+            if (pos.step.hasSteps() && pos.prevStep) {
+            } else {
+                list.push(...addArrowToList(list, pos, next, true, true))
+            }
+        }
+
+        if (!isSpecial(pos) && pos.inSteps && pos.nextstep && 
pos.parent?.dslName !== 'MulticastDefinition') {
+            const next = steps.get(pos.nextstep.uuid);
+            if (next && !isSpecial(next) && next.inSteps) {
+                // console.log(pos)
+                // const to = steps.get(parent.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 +330,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,28 +357,27 @@ 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={uuidv4()} name={key} d={path} className="path" 
markerEnd="url(#arrowhead)"/>
             )
     }
 
     function getSvg() {
         const stepsArray = Array.from(steps.values());
+        const arrows = stepsArray.map(pos => getArrow(pos)).flat(1);
+        const uniqueArrows = [...new Map(arrows.map(item =>  [(item as 
any).key, item])).values()]
         return (
-            <svg
+            <svg key={svgKey}
                 style={{width: width, height: height, position: "absolute", 
left: 0, top: 0}}
                 viewBox={"0 0 " + (width) + " " + (height)}>
                 <defs>
-                    <marker id="arrowhead" markerWidth="9" markerHeight="6" 
refX="0" refY="3" orient="auto"
-                            className="arrow">
+                    <marker id="arrowhead" markerWidth="9" markerHeight="6" 
refX="0" refY="3" orient="auto" className="arrow">
                         <polygon points="0 0, 9 3, 0 6"/>
                     </marker>
                 </defs>
                 {stepsArray.map(pos => getCircle(pos))}
-                {stepsArray.map(pos => getArrow(pos))}
-                {buttons.map(btn => getButtonArrow(btn)).filter(b => b !== 
undefined)}
+                {uniqueArrows}
                 {getIncomings().map(p => getIncoming(p))}
                 {getOutgoings().map(p => getOutgoing(p))}
-                {/*{getInternals().map((p) => getInternalLines(p)).flat()}*/}
             </svg>
         )
     }
diff --git 
a/karavan-web/karavan-app/src/main/webui/src/designer/route/RouteDesigner.tsx 
b/karavan-web/karavan-app/src/main/webui/src/designer/route/RouteDesigner.tsx
index b6b1c942..b02ed20b 100644
--- 
a/karavan-web/karavan-app/src/main/webui/src/designer/route/RouteDesigner.tsx
+++ 
b/karavan-web/karavan-app/src/main/webui/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-web/karavan-app/src/main/webui/src/designer/route/element/DslElement.css
 
b/karavan-web/karavan-app/src/main/webui/src/designer/route/element/DslElement.css
index 3039f422..61cc5af3 100644
--- 
a/karavan-web/karavan-app/src/main/webui/src/designer/route/element/DslElement.css
+++ 
b/karavan-web/karavan-app/src/main/webui/src/designer/route/element/DslElement.css
@@ -17,12 +17,24 @@
 
 .karavan .dsl-page .flows .step-element .header-route {
     display: block;
-    border: none;
     background: transparent;
-    padding: 0;
-    margin: 3px 24px 10px 24px;
-    /*min-width: 260px;*/
+    border-radius: 42px;
+    padding: 20px;
+    margin: 0;
     z-index: 101;
+    min-width: 260px;
+}
+
+.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 {
+    cursor: pointer;
 }
 
 .karavan .step-element .header-route .delete-button {
@@ -41,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;
@@ -65,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);
@@ -145,7 +153,7 @@
 
 .karavan .step-element .insert-element-button {
     position: absolute;
-    top: -7px;
+    top: -11px;
     line-height: 1;
     border: 0;
     padding: 0;
@@ -214,4 +222,4 @@
     width: 20px;
     height: 20px;
     background: white;
-}
\ No newline at end of file
+}
diff --git 
a/karavan-web/karavan-app/src/main/webui/src/designer/route/element/DslElement.tsx
 
b/karavan-web/karavan-app/src/main/webui/src/designer/route/element/DslElement.tsx
index 1deb46da..967d3321 100644
--- 
a/karavan-web/karavan-app/src/main/webui/src/designer/route/element/DslElement.tsx
+++ 
b/karavan-web/karavan-app/src/main/webui/src/designer/route/element/DslElement.tsx
@@ -14,20 +14,19 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import React, {CSSProperties, useMemo, useState} from 'react';
-import {Text, Tooltip,} from '@patternfly/react-core';
+import React, {CSSProperties, useState} from 'react';
+import {Tooltip,} from '@patternfly/react-core';
 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";
 import {useDesignerStore, useIntegrationStore} from "../../DesignerStore";
 import {shallow} from "zustand/shallow";
 import {useRouteDesignerHook} from "../useRouteDesignerHook";
-import {AddElementIcon, DeleteElementIcon, InsertElementIcon} from 
"./DslElementIcons";
+import {AddElementIcon} from "./DslElementIcons";
+import {DslElementHeader} from "./DslElementHeader";
 
 interface Props {
     step: CamelElement,
@@ -36,12 +35,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,
@@ -70,11 +69,6 @@ export function DslElement(props: Props) {
         }
     }
 
-    function onDeleteElement(evt: React.MouseEvent) {
-        evt.stopPropagation();
-        onShowDeleteConfirmation(props.step.uuid);
-    }
-
     function onSelectElement(evt: React.MouseEvent) {
         evt.stopPropagation();
         selectElement(props.step);
@@ -100,18 +94,15 @@ 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'].includes(step.dslName)) {
+        if (['FilterDefinition', 'RouteDefinition', 
'RouteConfigurationDefinition'].includes(step.dslName)) {
             return true;
         }
-        if (['FromDefinition',
-            'RouteDefinition',
+        if ([
+            'FromDefinition',
             'TryDefinition',
+            'MulticastDefinition',
             'CatchDefinition', 'FinallyDefinition',
             'ChoiceDefinition',
             'SwitchDefinition', 'WhenDefinition', 'OtherwiseDefinition'
@@ -125,11 +116,6 @@ export function DslElement(props: Props) {
         return ['FromDefinition', 'RouteConfigurationDefinition', 
'RouteDefinition', 'WhenDefinition', 
'OtherwiseDefinition'].includes(props.step.dslName);
     }
 
-    function isWide(): boolean {
-        return ['RouteConfigurationDefinition', 'RouteDefinition', 
'ChoiceDefinition', 'SwitchDefinition', 'MulticastDefinition', 'TryDefinition', 
'CircuitBreakerDefinition']
-            .includes(props.step.dslName);
-    }
-
     function isAddStepButtonLeft(): boolean {
         return ['MulticastDefinition']
             .includes(props.step.dslName);
@@ -139,9 +125,6 @@ export function DslElement(props: Props) {
         return ['MulticastDefinition'].includes(props.step.dslName);
     }
 
-    function isRoot(): boolean {
-        return ['RouteConfigurationDefinition', 
'RouteDefinition'].includes(props.step?.dslName);
-    }
 
     function isInStepWithChildren() {
         const step: CamelElement = props.step;
@@ -149,71 +132,10 @@ export function DslElement(props: Props) {
         return children.filter((c: ChildElement) => c.name === 'steps' || 
c.multiple).length > 0 && props.inSteps;
     }
 
-    function getChildrenInfo(step: CamelElement): [boolean, number, boolean, 
number, number] {
-        const children = 
CamelDefinitionApiExt.getElementChildrenDefinition(step.dslName);
-        const hasStepsField = children.filter((c: ChildElement) => c.name === 
'steps').length === 1;
-        const stepsChildrenCount = children
-            .filter(c => c.name === 'steps')
-            .map((child: ChildElement, index: number) => {
-                const children: CamelElement[] = 
CamelDefinitionApiExt.getElementChildren(step, child);
-                return children.length;
-            }).reduce((a, b) => a + b, 0);
-
-        const hasNonStepsFields = children.filter(c => c.name !== 'steps' && 
c.name !== 'expression' && c.name !== 'onWhen').length > 0;
-        const childrenCount = children
-            .map((child: ChildElement, index: number) => {
-                const children: CamelElement[] = 
CamelDefinitionApiExt.getElementChildren(step, child);
-                return children.length;
-            }).reduce((a, b) => a + b, 0);
-        const nonStepChildrenCount = childrenCount - stepsChildrenCount;
-        return [hasStepsField, stepsChildrenCount, hasNonStepsFields, 
nonStepChildrenCount, childrenCount]
-    }
-
-    function hasWideChildrenElement() {
-        const [hasStepsField, stepsChildrenCount, hasNonStepsFields, 
nonStepChildrenCount, childrenCount] = getChildrenInfo(props.step);
-        if (isHorizontal() && stepsChildrenCount > 1) return true;
-        else if (hasStepsField && stepsChildrenCount > 0 && hasNonStepsFields 
&& nonStepChildrenCount > 0) return true;
-        else if (!hasStepsField && hasNonStepsFields && childrenCount > 1) 
return true;
-        else if (hasStepsField && stepsChildrenCount > 0 && hasNonStepsFields 
&& childrenCount > 1) return true;
-        else return false;
-    }
-
-    function hasBorderOverSteps(step: CamelElement) {
-        const [hasStepsField, stepsChildrenCount, hasNonStepsFields, 
nonStepChildrenCount] = getChildrenInfo(step);
-        if (hasStepsField && stepsChildrenCount > 0 && hasNonStepsFields && 
nonStepChildrenCount > 0) return true;
-        else return false;
-    }
-
-    function getHeaderStyle() {
-        const style: CSSProperties = {
-            width: isWide() ? "100%" : "",
-            fontWeight: isElementSelected() ? "bold" : "normal",
-        };
-        return style;
-    }
-
-    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) {
@@ -221,109 +143,14 @@ 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);
-                    }
-                } else {
-                    EventBus.sendPosition("delete", step, prevStep, parent, 
new DOMRect(), new DOMRect(), 0);
+                    EventBus.sendPosition("add", step, prevStep, nextStep, 
parent, rect, headerRect, props.position, inStepsLength, inSteps, isSelected);
                 }
             }
-        }
-    }
-
-    function getAvailableModels() { // TODO: make static list-of-values instead
-        const step: CamelElement = props.step
-        return CamelUi.getSelectorModelsForParent(step.dslName, false);
-    }
-
-    const availableModels = useMemo(
-        () => getAvailableModels(),
-        [props.step.dslName]
-    );
-
-
-    function getHeader() {
-        const step: CamelElement = props.step;
-        const parent = props.parent;
-        const inRouteConfiguration = parent !== undefined && parent.dslName 
=== 'RouteConfigurationDefinition';
-        const showAddButton = !['CatchDefinition', 
'RouteDefinition'].includes(step.dslName) && availableModels.length > 0;
-        const showInsertButton =
-            !['FromDefinition', 'RouteConfigurationDefinition', 
'RouteDefinition', 'CatchDefinition', 'FinallyDefinition', 'WhenDefinition', 
'OtherwiseDefinition'].includes(step.dslName)
-            && !inRouteConfiguration;
-        const headerClass = ['RouteConfigurationDefinition', 
'RouteDefinition'].includes(step.dslName) ? "header-route" : "header"
-        const headerClasses = isElementSelected() ? headerClass + " selected" 
: headerClass;
-        return (
-            <div className={"dsl-element " + headerClasses} 
style={getHeaderStyle()} ref={headerRef}>
-                {!['RouteConfigurationDefinition', 
'RouteDefinition'].includes(props.step.dslName) &&
-                    <div
-                        ref={el => sendPosition(el)}
-                        className={"header-icon"}
-                        style={isWide() ? {width: ""} : {}}>
-                        {CamelUi.getIconForElement(step)}
-                    </div>
-                }
-                <div className={hasWideChildrenElement() ? "header-text" : ""}>
-                    {hasWideChildrenElement() && <div className="spacer"/>}
-                    {getHeaderTextWithTooltip(step)}
-                </div>
-                {showInsertButton && getInsertElementButton()}
-                {getDeleteButton()}
-                {showAddButton && getAddElementButton()}
-            </div>
-        )
-    }
-
-    function getHeaderText(step: CamelElement): string {
-        if (isKamelet() && step.dslName === 'ToDefinition' && (step as 
any).uri === 'kamelet:sink') {
-            return "Sink";
-        } else if (isKamelet() && step.dslName === 'FromDefinition' && (step 
as any).uri === 'kamelet:source') {
-            return "Source";
         } else {
-            return (step as any).description ? (step as any).description : 
CamelUi.getElementTitle(props.step);
+            EventBus.sendPosition("delete", step, prevStep, nextStep, parent, 
new DOMRect(), new DOMRect(), 0, 0);
         }
     }
 
-    function getHeaderTextWithTooltip(step: CamelElement) {
-        const checkRequired = CamelUtil.checkRequired(step);
-        const title = getHeaderText(step);
-        let className = hasWideChildrenElement() ? "text text-right" : "text 
text-bottom";
-        if (!checkRequired[0]) className = className + " header-text-required";
-        if (checkRequired[0]) {
-            return <Text className={className}>{title}</Text>
-        } else return (
-            <Tooltip position={"right"} className="tooltip-required-field"
-                     content={checkRequired[1].map((text, i) => (<div 
key={i}>{text}</div>))}>
-                <Text className={className}>{title}</Text>
-            </Tooltip>
-        )
-    }
-
-    function getHeaderWithTooltip(tooltip: string | undefined) {
-        return (
-            <>
-                {getHeader()}
-                <Tooltip triggerRef={headerRef} position={"left"} 
content={<div>{tooltip}</div>}/>
-            </>
-
-        )
-    }
-
-    function getHeaderTooltip(): string | undefined {
-        if (CamelUi.isShowExpressionTooltip(props.step)) return 
CamelUi.getExpressionTooltip(props.step);
-        if (CamelUi.isShowUriTooltip(props.step)) return 
CamelUi.getUriTooltip(props.step);
-        return undefined;
-    }
-
-    function getElementHeader() {
-        const tooltip = getHeaderTooltip();
-        if (tooltip !== undefined && !isDragging) {
-            return getHeaderWithTooltip(tooltip);
-        }
-        return getHeader();
-    }
-
     function getChildrenStyle() {
         const style: CSSProperties = {
             display: "flex",
@@ -333,10 +160,7 @@ export function DslElement(props: Props) {
     }
 
     function getChildrenElementsStyle(child: ChildElement, notOnlySteps: 
boolean) {
-        const step = props.step;
-        const isBorder = child.name === 'steps' && hasBorderOverSteps(step);
         const style: CSSProperties = {
-            // borderStyle: isBorder ? "dotted" : "none",
             borderColor: "var(--step-border-color)",
             borderWidth: "1px",
             borderRadius: "16px",
@@ -375,10 +199,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 = undefined;
-                            if (['TryDefinition', 
'ChoiceDefinition'].includes(step.dslName)) {
+                            let nextStep: CamelElement | undefined = undefined;
+                            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);
@@ -390,6 +216,7 @@ export function DslElement(props: Props) {
                                     step={element}
                                     nextStep={nextStep}
                                     prevStep={prevStep}
+                                    inStepsLength={array.length}
                                     parent={step}/>
                             </div>)
                         }
@@ -408,16 +235,14 @@ export function DslElement(props: Props) {
     }
 
     function getAddStepButton() {
-        const {step, nextStep} = props;
+        const {step} = 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"}>
@@ -425,51 +250,12 @@ export function DslElement(props: Props) {
                     </button>
 
                 </Tooltip>
-            </div>
-        )
-    }
-
-    function getAddElementButton() {
-        return (
-            <Tooltip position={"bottom"}
-                     content={<div>{"Add DSL element to " + 
CamelDisplayUtil.getTitle(props.step)}</div>}>
-                <button
-                    type="button"
-                    aria-label="Add"
-                    onClick={e => onOpenSelector(e, false)}
-                    className={"add-element-button"}>
-                    <AddElementIcon/>
-                </button>
-            </Tooltip>
-        )
-    }
-
-    function getInsertElementButton() {
-        return (
-            <Tooltip position={"left"} content={<div>{"Insert element 
before"}</div>}>
-                <button type="button"
-                        aria-label="Insert"
-                        onClick={e => onOpenSelector(e, true, true)}
-                        className={"insert-element-button"}>
-                    <InsertElementIcon/>
-                </button>
-            </Tooltip>
-        )
-    }
-
-    function getDeleteButton() {
-        return (
-            <Tooltip position={"right"} content={<div>{"Delete 
element"}</div>}>
-                <button type="button" aria-label="Delete" onClick={e => 
onDeleteElement(e)} className="delete-button">
-                    <DeleteElementIcon/>
-                </button>
-            </Tooltip>
         )
     }
 
     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}
@@ -516,7 +302,14 @@ export function DslElement(props: Props) {
              onDrop={event => dragElement(event, element)}
              draggable={!isNotDraggable()}
         >
-            {getElementHeader()}
+            <DslElementHeader headerRef={headerRef}
+                              step={props.step}
+                              parent={props.parent}
+                              nextStep={props.nextStep}
+                              prevStep={props.prevStep}
+                              inSteps={props.inSteps}
+                              isDragging={isDragging}
+                              position={props.position}/>
             {getChildElements()}
         </div>
     )
diff --git 
a/karavan-web/karavan-app/src/main/webui/src/designer/route/element/DslElementHeader.tsx
 
b/karavan-web/karavan-app/src/main/webui/src/designer/route/element/DslElementHeader.tsx
new file mode 100644
index 00000000..d201d105
--- /dev/null
+++ 
b/karavan-web/karavan-app/src/main/webui/src/designer/route/element/DslElementHeader.tsx
@@ -0,0 +1,275 @@
+/*
+ * 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 React, {CSSProperties, useMemo} from 'react';
+import {Text, Tooltip,} from '@patternfly/react-core';
+import '../../karavan.css';
+import './DslElement.css';
+import {CamelElement} from "karavan-core/lib/model/IntegrationDefinition";
+import {CamelUi} from "../../utils/CamelUi";
+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";
+import {useDesignerStore} from "../../DesignerStore";
+import {shallow} from "zustand/shallow";
+import {useRouteDesignerHook} from "../useRouteDesignerHook";
+import {AddElementIcon, DeleteElementIcon, InsertElementIcon} from 
"./DslElementIcons";
+import { RouteConfigurationDefinition} from 
"karavan-core/lib/model/CamelDefinition";
+
+interface Props {
+    headerRef: React.RefObject<HTMLDivElement>
+    step: CamelElement,
+    parent: CamelElement | undefined,
+    nextStep: CamelElement | undefined,
+    prevStep: CamelElement | undefined,
+    inSteps: boolean
+    position: number
+    isDragging: boolean
+}
+
+export function DslElementHeader(props: Props) {
+
+    const {
+        selectElement,
+        moveElement,
+        onShowDeleteConfirmation,
+        openSelector,
+        isKamelet,
+        isSourceKamelet,
+        isActionKamelet
+    } = useRouteDesignerHook();
+
+    const [selectedUuids, selectedStep, showMoveConfirmation, 
setShowMoveConfirmation, hideLogDSL, setMoveElements] =
+        useDesignerStore((s) =>
+            [s.selectedUuids, s.selectedStep, s.showMoveConfirmation, 
s.setShowMoveConfirmation, s.hideLogDSL, s.setMoveElements], shallow)
+
+    function onOpenSelector(evt: React.MouseEvent, showSteps: boolean = true, 
isInsert: boolean = false) {
+        evt.stopPropagation();
+        if (isInsert && props.parent) {
+            openSelector(props.parent.uuid, props.parent.dslName, showSteps, 
props.position);
+        } else {
+            openSelector(props.step.uuid, props.step.dslName, showSteps);
+        }
+    }
+
+    function onDeleteElement(evt: React.MouseEvent) {
+        evt.stopPropagation();
+        onShowDeleteConfirmation(props.step.uuid);
+    }
+
+    function isElementSelected(): boolean {
+        return selectedUuids.includes(props.step.uuid);
+    }
+
+    function isWide(): boolean {
+        return ['RouteConfigurationDefinition', 'RouteDefinition', 
'ChoiceDefinition', 'MulticastDefinition', 'TryDefinition', 
'CircuitBreakerDefinition']
+            .includes(props.step.dslName);
+    }
+
+    function isHorizontal(): boolean {
+        return ['MulticastDefinition'].includes(props.step.dslName);
+    }
+
+    function getChildrenInfo(step: CamelElement): [boolean, number, boolean, 
number, number] {
+        const children = 
CamelDefinitionApiExt.getElementChildrenDefinition(step.dslName);
+        const hasStepsField = children.filter((c: ChildElement) => c.name === 
'steps').length === 1;
+        const stepsChildrenCount = children
+            .filter(c => c.name === 'steps')
+            .map((child: ChildElement, index: number) => {
+                const children: CamelElement[] = 
CamelDefinitionApiExt.getElementChildren(step, child);
+                return children.length;
+            }).reduce((a, b) => a + b, 0);
+
+        const hasNonStepsFields = children.filter(c => c.name !== 'steps' && 
c.name !== 'expression' && c.name !== 'onWhen').length > 0;
+        const childrenCount = children
+            .map((child: ChildElement, index: number) => {
+                const children: CamelElement[] = 
CamelDefinitionApiExt.getElementChildren(step, child);
+                return children.length;
+            }).reduce((a, b) => a + b, 0);
+        const nonStepChildrenCount = childrenCount - stepsChildrenCount;
+        return [hasStepsField, stepsChildrenCount, hasNonStepsFields, 
nonStepChildrenCount, childrenCount]
+    }
+
+    function hasWideChildrenElement() {
+        const [hasStepsField, stepsChildrenCount, hasNonStepsFields, 
nonStepChildrenCount, childrenCount] = getChildrenInfo(props.step);
+        if (isHorizontal() && stepsChildrenCount > 1) return true;
+        else if (hasStepsField && stepsChildrenCount > 0 && hasNonStepsFields 
&& nonStepChildrenCount > 0) return true;
+        else if (!hasStepsField && hasNonStepsFields && childrenCount > 1) 
return true;
+        else if (hasStepsField && stepsChildrenCount > 0 && hasNonStepsFields 
&& childrenCount > 1) return true;
+        else return false;
+    }
+
+    function getHeaderStyle() {
+        const style: CSSProperties = {
+            width: isWide() ? "100%" : "",
+            fontWeight: isElementSelected() ? "bold" : "normal",
+        };
+        return style;
+    }
+
+    function getAvailableModels() { // TODO: make static list-of-values instead
+        const step: CamelElement = props.step
+        return CamelUi.getSelectorModelsForParent(step.dslName, false);
+    }
+
+    const availableModels = useMemo(
+        () => getAvailableModels(),
+        [props.step.dslName]
+    );
+
+    function hasElements(rc: RouteConfigurationDefinition): boolean {
+        return (rc.interceptFrom !== undefined && rc.interceptFrom.length > 0)
+    || (rc.intercept !== undefined && rc.intercept.length > 0)
+    || (rc.interceptSendToEndpoint !== undefined && 
rc.interceptSendToEndpoint.length > 0)
+    || (rc.onException !== undefined && rc.onException.length > 0)
+    || (rc.onCompletion !== undefined && rc.onCompletion.length > 0)
+    }
+
+    function getHeaderClasses(): string {
+        const classes: string[] = [];
+        const step: CamelElement = props.step;
+        if (step.dslName === 'RouteDefinition') {
+            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')
+        }
+        if (isElementSelected()) {
+            classes.push("selected")
+        }
+        return classes.join(" ");
+    }
+
+    function getHeader() {
+        const step: CamelElement = props.step;
+        const parent = props.parent;
+        const inRouteConfiguration = parent !== undefined && parent.dslName 
=== 'RouteConfigurationDefinition';
+        const showAddButton = !['CatchDefinition', 
'RouteDefinition'].includes(step.dslName) && availableModels.length > 0;
+        const showInsertButton =
+            !['FromDefinition', 'RouteConfigurationDefinition', 
'RouteDefinition', 'CatchDefinition', 'FinallyDefinition', 'WhenDefinition', 
'OtherwiseDefinition'].includes(step.dslName)
+            && !inRouteConfiguration;
+        const headerClasses = getHeaderClasses();
+        return (
+            <div className={"dsl-element " + headerClasses} 
style={getHeaderStyle()} ref={props.headerRef}>
+                {!['RouteConfigurationDefinition', 
'RouteDefinition'].includes(props.step.dslName) &&
+                    <div
+                        className={"header-icon"}
+                        style={isWide() ? {width: ""} : {}}>
+                        {CamelUi.getIconForElement(step)}
+                    </div>
+                }
+                <div className={hasWideChildrenElement() ? "header-text" : ""}>
+                    {hasWideChildrenElement() && <div className="spacer"/>}
+                    {getHeaderTextWithTooltip(step)}
+                </div>
+                {showInsertButton && getInsertElementButton()}
+                {getDeleteButton()}
+                {showAddButton && getAddElementButton()}
+            </div>
+        )
+    }
+
+    function getHeaderText(step: CamelElement): string {
+        if (isKamelet() && step.dslName === 'ToDefinition' && (step as 
any).uri === 'kamelet:sink') {
+            return "Sink";
+        } else if (isKamelet() && step.dslName === 'FromDefinition' && (step 
as any).uri === 'kamelet:source') {
+            return "Source";
+        } else {
+            return (step as any).description ? (step as any).description : 
CamelUi.getElementTitle(props.step);
+        }
+    }
+
+    function getHeaderTextWithTooltip(step: CamelElement) {
+        const title = getHeaderText(step);
+        const checkRequired = CamelUtil.checkRequired(step);
+        let className = hasWideChildrenElement() ? "text text-right" : "text 
text-bottom";
+        if (!checkRequired[0]) className = className + " header-text-required";
+        if (checkRequired[0]) {
+            return <Text className={className}>{title}</Text>
+        } else return (
+            <Tooltip position={"right"} className="tooltip-required-field"
+                     content={checkRequired[1].map((text, i) => (<div 
key={i}>{text}</div>))}>
+                <Text className={className}>{title}</Text>
+            </Tooltip>
+        )
+    }
+
+    function getHeaderWithTooltip(tooltip: string | undefined) {
+        return (
+            <>
+                {getHeader()}
+                <Tooltip triggerRef={props.headerRef} position={"left"} 
content={<div>{tooltip}</div>}/>
+            </>
+
+        )
+    }
+
+    function getHeaderTooltip(): string | undefined {
+        if (CamelUi.isShowExpressionTooltip(props.step)) return 
CamelUi.getExpressionTooltip(props.step);
+        if (CamelUi.isShowUriTooltip(props.step)) return 
CamelUi.getUriTooltip(props.step);
+        return undefined;
+    }
+
+
+    function getAddElementButton() {
+        return (
+            <Tooltip position={"bottom"}
+                     content={<div>{"Add DSL element to " + 
CamelDisplayUtil.getTitle(props.step)}</div>}>
+                <button
+                    type="button"
+                    aria-label="Add"
+                    onClick={e => onOpenSelector(e, false)}
+                    className={"add-element-button"}>
+                    <AddElementIcon/>
+                </button>
+            </Tooltip>
+        )
+    }
+
+    function getInsertElementButton() {
+        return (
+            <Tooltip position={"left"} content={<div>{"Insert element 
before"}</div>}>
+                <button type="button"
+                        aria-label="Insert"
+                        onClick={e => onOpenSelector(e, true, true)}
+                        className={"insert-element-button"}>
+                    <InsertElementIcon/>
+                </button>
+            </Tooltip>
+        )
+    }
+
+    function getDeleteButton() {
+        return (
+            <Tooltip position={"right"} content={<div>{"Delete 
element"}</div>}>
+                <button type="button" aria-label="Delete" onClick={e => 
onDeleteElement(e)} className="delete-button">
+                    <DeleteElementIcon/>
+                </button>
+            </Tooltip>
+        )
+    }
+
+    const tooltip = getHeaderTooltip();
+    if (tooltip !== undefined && !props.isDragging) {
+        return getHeaderWithTooltip(tooltip);
+    }
+    return getHeader();
+}
diff --git 
a/karavan-web/karavan-app/src/main/webui/src/designer/route/useRouteDesignerHook.tsx
 
b/karavan-web/karavan-app/src/main/webui/src/designer/route/useRouteDesignerHook.tsx
index b65966a0..1457adba 100644
--- 
a/karavan-web/karavan-app/src/main/webui/src/designer/route/useRouteDesignerHook.tsx
+++ 
b/karavan-web/karavan-app/src/main/webui/src/designer/route/useRouteDesignerHook.tsx
@@ -105,8 +105,7 @@ export function useRouteDesignerHook () {
     }
 
     const deleteElement = () =>  {
-        EventBus.sendPosition("clean", new CamelElement(""), undefined, 
undefined, new DOMRect(), new DOMRect(), 0);
-        EventBus.sendButtonPosition("clean", '',  new CamelElement(""), new 
DOMRect());
+        EventBus.sendPosition("clean", new CamelElement(""), 
undefined,undefined, undefined, new DOMRect(), new DOMRect(), 0, 0);
         let i = integration;
         selectedUuids.forEach(uuidToDelete => {
              i = CamelDefinitionApiExt.deleteStepFromIntegration(i, 
uuidToDelete);
diff --git 
a/karavan-web/karavan-app/src/main/webui/src/designer/utils/CamelUi.tsx 
b/karavan-web/karavan-app/src/main/webui/src/designer/utils/CamelUi.tsx
index 61a7d27c..5caae0a7 100644
--- a/karavan-web/karavan-app/src/main/webui/src/designer/utils/CamelUi.tsx
+++ b/karavan-web/karavan-app/src/main/webui/src/designer/utils/CamelUi.tsx
@@ -337,7 +337,7 @@ export class CamelUi {
     static getElementTitle = (element: CamelElement): string => {
         if (element.dslName === 'RouteDefinition') {
             const routeId = (element as RouteDefinition).id
-            return routeId ? "Route: " + routeId : 
CamelUtil.capitalizeName((element as any).stepName);
+            return routeId ? routeId : CamelUtil.capitalizeName((element as 
any).stepName);
         } else if (['ToDefinition', 'ToDynamicDefinition', 'FromDefinition', 
'KameletDefinition'].includes(element.dslName) && (element as any).uri) {
             const uri = (element as any).uri;
             const kameletTitle = uri && uri.startsWith("kamelet:") ? 
KameletApi.findKameletByUri(uri)?.title() : undefined;
@@ -787,4 +787,5 @@ export class CamelUi {
             .forEach((f: any) => result.push(f));
         return result;
     }
+
 }
\ No newline at end of file
diff --git 
a/karavan-web/karavan-app/src/main/webui/src/designer/utils/EventBus.ts 
b/karavan-web/karavan-app/src/main/webui/src/designer/utils/EventBus.ts
index 55906f0f..687bec9d 100644
--- a/karavan-web/karavan-app/src/main/webui/src/designer/utils/EventBus.ts
+++ b/karavan-web/karavan-app/src/main/webui/src/designer/utils/EventBus.ts
@@ -18,30 +18,15 @@ import {Subject} from 'rxjs';
 import {CamelElement, Integration} from 
"karavan-core/lib/model/IntegrationDefinition";
 import {v4 as uuidv4} from "uuid";
 
-export class ButtonPosition {
-    uuid: string = '';
-    nextstep: CamelElement = new CamelElement("");
-    rect: DOMRect = new DOMRect();
-    command: "add" | "delete" | "clean" = "add";
-
-    constructor(command: "add" | "delete" | "clean",
-                uuid: string,
-                nextstep: CamelElement,
-                rect: DOMRect) {
-        this.uuid = uuid;
-        this.command = command;
-        this.nextstep = nextstep;
-        this.rect = rect;
-    }
-}
-
 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 +34,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;
     }
 }
@@ -104,25 +93,22 @@ export class ToastMessage {
     }
 }
 const dslPositions = new Subject<DslPosition>();
-const buttonPositions = new Subject<ButtonPosition>();
 
 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,
-                   nextStep: CamelElement,
-                   rect: DOMRect) => buttonPositions.next(new 
ButtonPosition(command, uuid, nextStep, rect)),
-    onButtonPosition: () => buttonPositions.asObservable(),
-
     sendIntegrationUpdate: (i: Integration, propertyOnly: boolean) => 
updates.next(new IntegrationUpdate(i, propertyOnly)),
     onIntegrationUpdate: () => updates.asObservable(),
 

Reply via email to