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

[camel-karavan] branch feature-836 updated (27ebf976 -> 4f8a93e1)

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

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


    from 27ebf976 Delete element for #836
     new 63223071 Download YAML and PNG #836
     new 64a8c66a Fix connections issues #836
     new f432faaf Copy and Paste for #836
     new bff7ef31 Copy and Paste for #836
     new 425975b3 Properties for #836
     new 4f8a93e1 Fixed issue with Expressions for #836

The 6 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 karavan-core/src/core/api/CamelUtil.ts             |   2 +-
 karavan-designer/public/example/demo.camel.yaml    |  39 +-
 karavan-designer/src/designer/KaravanDesigner.tsx  |  18 +-
 karavan-designer/src/designer/KaravanStore.ts      |  39 +-
 .../src/designer/rest/RestDesigner.tsx             |   9 +-
 .../src/designer/route/DslConnections.tsx          |   2 -
 karavan-designer/src/designer/route/DslElement.tsx | 135 +++---
 .../src/designer/route/DslProperties.tsx           | 213 +++------
 .../src/designer/route/DslSelector.tsx             |  26 +-
 .../src/designer/route/RouteDesigner.tsx           | 134 ++----
 .../designer/route/property/DataFormatField.tsx    |  15 +-
 .../designer/route/property/DslPropertyField.tsx   | 516 +++++++++++----------
 .../designer/route/property/ExpressionField.tsx    | 181 ++++----
 .../src/designer/route/property/ObjectField.tsx    |  12 +-
 .../src/designer/route/usePropertiesHook.tsx       |  94 ++++
 .../src/designer/route/useRouteDesignerHook.tsx    | 339 ++++++--------
 karavan-designer/src/designer/utils/CamelUi.tsx    |   4 +-
 .../src/designer/utils/KaravanIcons.tsx            |  22 +
 18 files changed, 845 insertions(+), 955 deletions(-)
 create mode 100644 karavan-designer/src/designer/route/usePropertiesHook.tsx


[camel-karavan] 04/06: Copy and Paste for #836

Posted by ma...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

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

commit bff7ef31af9b0d3ad46b5d8629030a29f42b207a
Author: Marat Gubaidullin <ma...@Marats-MacBook-Pro.local>
AuthorDate: Thu Aug 24 09:11:32 2023 -0400

    Copy and Paste for #836
---
 karavan-designer/src/designer/KaravanDesigner.tsx   |  6 ++----
 karavan-designer/src/designer/KaravanStore.ts       | 11 ++++++++---
 karavan-designer/src/designer/route/DslSelector.tsx | 16 +++++-----------
 .../src/designer/route/RouteDesigner.tsx            | 21 +++++----------------
 .../src/designer/route/useRouteDesignerHook.tsx     |  8 +++-----
 5 files changed, 23 insertions(+), 39 deletions(-)

diff --git a/karavan-designer/src/designer/KaravanDesigner.tsx b/karavan-designer/src/designer/KaravanDesigner.tsx
index 940df7e2..3c68baee 100644
--- a/karavan-designer/src/designer/KaravanDesigner.tsx
+++ b/karavan-designer/src/designer/KaravanDesigner.tsx
@@ -66,10 +66,8 @@ export class KaravanInstance {
 export const KaravanDesigner = (props: Props) => {
 
     const [tab, setTab] = useState<string>('routes');
-    const [propertyOnly, setPropertyOnly, setDark] = useDesignerStore((state) =>
-        [state.propertyOnly, state.setPropertyOnly, state.setDark], shallow )
-    const [integration, setIntegration] = useIntegrationStore((state) => 
-        [state.integration, state.setIntegration], shallow )
+    const [propertyOnly, setDark] = useDesignerStore((state) => [state.propertyOnly, state.setDark], shallow )
+    const [integration, setIntegration] = useIntegrationStore((state) => [state.integration, state.setIntegration], shallow )
 
     useEffect(() => {
         setIntegration(makeIntegration(props.yaml, props.filename));
diff --git a/karavan-designer/src/designer/KaravanStore.ts b/karavan-designer/src/designer/KaravanStore.ts
index daae7f4f..3bc87681 100644
--- a/karavan-designer/src/designer/KaravanStore.ts
+++ b/karavan-designer/src/designer/KaravanStore.ts
@@ -25,10 +25,16 @@ interface IntegrationState {
     setIntegration: (integration: Integration) => void;
 }
 
-export const useIntegrationStore = createWithEqualityFn<IntegrationState>((set) => ({
+export const useIntegrationStore = createWithEqualityFn<IntegrationState>((setState, getState, store) => ({
     integration: Integration.createNew("demo", "plain"),
     setIntegration: (integration: Integration) => {
-        set({integration: integration})
+        setState((state: IntegrationState) => {
+            if (JSON.stringify(state.integration) === (JSON.stringify(integration))) {
+                return {integration: state.integration};
+            } else {
+                return {integration: integration};
+            }
+        })
     },
 }), shallow)
 
@@ -50,7 +56,6 @@ interface SelectorStateState {
     addSelectedLabel: (label: string) => void;
     deleteSelectedLabel: (label: string) => void;
     clearSelectedLabels: () => void;
-    setSelectedLabels: (selectedLabels: string []) => void;
 }
 
 export const useSelectorStore = createWithEqualityFn<SelectorStateState>((set) => ({
diff --git a/karavan-designer/src/designer/route/DslSelector.tsx b/karavan-designer/src/designer/route/DslSelector.tsx
index af0668e2..44786c8e 100644
--- a/karavan-designer/src/designer/route/DslSelector.tsx
+++ b/karavan-designer/src/designer/route/DslSelector.tsx
@@ -35,15 +35,15 @@ interface Props {
 export const DslSelector = (props: Props) => {
 
     const [showSelector, showSteps, parentId, parentDsl, selectorTabIndex, setShowSelector, setSelectorTabIndex,
-        selectedPosition, selectedLabels, setSelectedLabels, addSelectedLabel, deleteSelectedLabelbels] =
+        selectedPosition, selectedLabels, addSelectedLabel, deleteSelectedLabel] =
         useSelectorStore((s) =>
             [s.showSelector, s.showSteps, s.parentId, s.parentDsl, s.selectorTabIndex, s.setShowSelector, s.setSelectorTabIndex,
-                s.selectedPosition, s.selectedLabels, s.setSelectedLabels, s.addSelectedLabel, s.deleteSelectedLabel], shallow)
+                s.selectedPosition, s.selectedLabels, s.addSelectedLabel, s.deleteSelectedLabel], shallow)
 
 
     const [dark] = useDesignerStore((s) => [s.dark], shallow)
 
-    const {deleteElement, selectElement, moveElement, onShowDeleteConfirmation, onDslSelect, openSelector, createRouteConfiguration} = useRouteDesignerHook();
+    const {onDslSelect} = useRouteDesignerHook();
 
 
     const [filter, setFilter] = useState<string>('');
@@ -56,15 +56,10 @@ export const DslSelector = (props: Props) => {
         setSelectorTabIndex(eventKey);
     }
 
-    // function componentDidUpdate(prevProps: Readonly<Props>, prevState: Readonly<State>, snapshot?: any) {
-    //     if (prevProps.parentDsl !== props.parentDsl) {
-    //         this.setState({tabIndex: CamelUi.getSelectorModelTypes(props.parentDsl, props.showSteps)[0][0]});
-    //     }
-    // }
-
     function selectDsl(evt: React.MouseEvent, dsl: any) {
         evt.stopPropagation();
         setFilter('');
+        setShowSelector(false);
         onDslSelect(dsl, parentId, selectedPosition);
     }
 
@@ -118,11 +113,10 @@ export const DslSelector = (props: Props) => {
         if (!selectedLabels.includes(eipLabel)) {
             addSelectedLabel(eipLabel);
         } else {
-            deleteSelectedLabelbels(eipLabel);
+            deleteSelectedLabel(eipLabel);
         }
     }
 
-
     const isEip = selectorTabIndex === 'eip';
     const title = parentDsl === undefined ? "Select source" : "Select step";
     const navigation: string = selectorTabIndex ? selectorTabIndex.toString() : '';
diff --git a/karavan-designer/src/designer/route/RouteDesigner.tsx b/karavan-designer/src/designer/route/RouteDesigner.tsx
index ce79383e..a69105cb 100644
--- a/karavan-designer/src/designer/route/RouteDesigner.tsx
+++ b/karavan-designer/src/designer/route/RouteDesigner.tsx
@@ -14,7 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import React, {createRef, useCallback, useEffect, useRef, useState} from 'react';
+import React, {useCallback, useEffect, useRef} from 'react';
 import {
     Drawer,
     DrawerPanelContent,
@@ -46,19 +46,12 @@ import {CamelDefinitionApi} from "karavan-core/lib/api/CamelDefinitionApi";
 
 export const RouteDesigner = () => {
 
-
-    const {deleteElement, selectElement, moveElement, onShowDeleteConfirmation, onDslSelect, openSelector, createRouteConfiguration, onCommand, handleKeyDown, handleKeyUp} = useRouteDesignerHook();
+    const {openSelector, createRouteConfiguration, onCommand, handleKeyDown, handleKeyUp} = useRouteDesignerHook();
 
     const [integration] = useIntegrationStore((state) => [state.integration], shallow)
-    const [showDeleteConfirmation, deleteMessage , setShowDeleteConfirmation, setPosition, setSelectedUuids, setShiftKeyPressed, setClipboardSteps,
-        clipboardSteps, selectedUuids, width, height, top, left] =
-        useDesignerStore((s) =>
-        [s.showDeleteConfirmation, s.deleteMessage, s.setShowDeleteConfirmation, s.setPosition, s.setSelectedUuids, s.setShiftKeyPressed, s.setClipboardSteps,
-          s.clipboardSteps, s.selectedUuids, s.width, s.height, s.top, s.left], shallow)
+    const [showDeleteConfirmation , setPosition,] = useDesignerStore((s) => [s.showDeleteConfirmation, s.setPosition], shallow)
 
-    const [showSelector, showSteps, parentId, parentDsl, selectorTabIndex, setShowSelector, setSelectorTabIndex] =
-        useSelectorStore((s) =>
-        [s.showSelector, s.showSteps, s.parentId, s.parentDsl, s.selectorTabIndex, s.setShowSelector, s.setSelectorTabIndex], shallow)
+    const [showSelector] = useSelectorStore((s) => [s.showSelector], shallow)
 
     const [clearSteps] = useConnectionsStore((s) => [s.clearSteps])
 
@@ -80,7 +73,6 @@ export const RouteDesigner = () => {
     const flowRef = useRef<HTMLDivElement | null>(null);
 
     useEffect(()=> {
-        console.log(integration)
         // window.addEventListener('resize', changeGraphSize);
         const interval = setInterval(() => {
             changeGraphSize();
@@ -104,10 +96,7 @@ export const RouteDesigner = () => {
 
     function getPropertiesPanel() {
         return (
-            <DrawerPanelContent onResize={(_event, width) => {
-                // setState({key: Math.random().toString()})
-            }}
-                                style={{transform: "initial"}} isResizable hasNoBorder defaultSize={'400px'}
+            <DrawerPanelContent style={{transform: "initial"}} isResizable hasNoBorder defaultSize={'400px'}
                                 maxSize={'800px'} minSize={'300px'}>
                 {/*<DslProperties ref={ref}*/}
                 {/*               integration={integration}*/}
diff --git a/karavan-designer/src/designer/route/useRouteDesignerHook.tsx b/karavan-designer/src/designer/route/useRouteDesignerHook.tsx
index 26c88dd6..d7ed9027 100644
--- a/karavan-designer/src/designer/route/useRouteDesignerHook.tsx
+++ b/karavan-designer/src/designer/route/useRouteDesignerHook.tsx
@@ -316,12 +316,10 @@ export const useRouteDesignerHook = () => {
     }
 
     const addStep = (step: CamelElement, parentId: string, position?: number | undefined) => {
-        const i = CamelDefinitionApiExt.addStepToIntegration(integration, step, parentId, position);
-        const clone = CamelUtil.cloneIntegration(i);
-        EventBus.sendPosition("clean", step, undefined, new DOMRect(), new DOMRect(), 0);
+        const clone = CamelUtil.cloneIntegration(integration);
+        const i = CamelDefinitionApiExt.addStepToIntegration(clone, step, parentId, position);
         const selectedStep = step.dslName === 'RouteDefinition' ? (step as RouteDefinition).from  : step;
-        setIntegration(clone);
-        setShowSelector(false);
+        setIntegration(i);
         setSelectedStep(selectedStep);
         setPropertyOnly(false);
         setSelectedUuids([selectedStep.uuid]);


[camel-karavan] 01/06: Download YAML and PNG #836

Posted by ma...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

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

commit 632230716b514c96454cbde2b613d580d6da0a3b
Author: Marat Gubaidullin <ma...@Marats-MacBook-Pro.local>
AuthorDate: Wed Aug 23 17:24:16 2023 -0400

    Download YAML and PNG #836
---
 karavan-designer/public/example/demo.camel.yaml    | 972 +++++++++++++++++++--
 karavan-designer/src/designer/KaravanDesigner.tsx  |   7 +-
 karavan-designer/src/designer/KaravanStore.ts      |  16 +-
 karavan-designer/src/designer/route/DslElement.tsx |  21 +-
 .../src/designer/route/DslSelector.tsx             |  10 +-
 .../src/designer/route/RouteDesigner.tsx           |  70 +-
 .../src/designer/route/useRouteDesignerHook.tsx    | 206 ++---
 7 files changed, 1065 insertions(+), 237 deletions(-)

diff --git a/karavan-designer/public/example/demo.camel.yaml b/karavan-designer/public/example/demo.camel.yaml
index 4aed6c49..f7df5980 100644
--- a/karavan-designer/public/example/demo.camel.yaml
+++ b/karavan-designer/public/example/demo.camel.yaml
@@ -1,44 +1,186 @@
-#- route:
-#    id: route-f435
-#    from:
-#      uri: kamelet:timer-source
-#      id: from-e52c
-#      steps:
-#        - choice:
-#            when:
-#              - expression: {}
-#                id: when-064f
-#                steps:
-#                  - multicast:
-#                      id: multicast-38ce
-#                      steps:
-#                        - bean:
-#                            id: bean-3b8e
-#                        - log:
-#                            message: ${body}
-#                            id: log-546f
-#                        - loop:
-#                            expression: {}
-#                            id: loop-4635
-#                            steps:
-#                              - convertBodyTo:
-#                                  id: convertBodyTo-1cae
-#            otherwise:
-#              id: otherwise-0b09
-#              steps:
-#                - filter:
-#                    expression: {}
-#                    id: filter-a02b
-#            id: choice-c53c
-#        - doTry:
-#            id: doTry-8fd5
-#            doCatch:
-#              - id: doCatch-1071
-#              - id: doCatch-c38e
-#            steps:
-#              - routingSlip:
-#                  expression: {}
-#                  id: routingSlip-a85a
+- route:
+    id: route-f435
+    from:
+      uri: kamelet:timer-source
+      id: from-e52c
+      steps:
+        - choice:
+            when:
+              - expression: {}
+                id: when-064f
+                steps:
+                  - multicast:
+                      id: multicast-38ce
+                      steps:
+                        - bean:
+                            id: bean-3b8e
+                        - log:
+                            message: ${body}
+                            id: log-546f
+                        - loop:
+                            expression: {}
+                            id: loop-4635
+                            steps:
+                              - convertBodyTo:
+                                  id: convertBodyTo-1cae
+            otherwise:
+              id: otherwise-0b09
+              steps:
+                - filter:
+                    expression: {}
+                    id: filter-a02b
+            id: choice-c53c
+        - doTry:
+            id: doTry-8fd5
+            doCatch:
+              - id: doCatch-1071
+              - id: doCatch-c38e
+            steps:
+              - routingSlip:
+                  expression: {}
+                  id: routingSlip-a85a
+- route:
+    id: route-178a
+    from:
+      uri: kamelet:aws-cloudtrail-source
+      id: from-3e7d
+      steps:
+        - multicast:
+            id: multicast-eef7
+            steps:
+              - bean:
+                  id: bean-a5ef
+              - aggregate:
+                  id: aggregate-f5d8
+              - aggregate:
+                  id: aggregate-b9e7
+              - aggregate:
+                  id: aggregate-5eb8
+              - aggregate:
+                  id: aggregate-c57e
+              - aggregate:
+                  id: aggregate-1cd4
+              - bean:
+                  id: bean-72a1
+              - choice:
+                  when:
+                    - expression: {}
+                      id: when-a56b
+                  otherwise:
+                    id: otherwise-9f31
+                  id: choice-1905
+- route:
+    id: route-f435
+    from:
+      uri: kamelet:timer-source
+      id: from-e52c
+      steps:
+        - choice:
+            when:
+              - expression: {}
+                id: when-064f
+                steps:
+                  - multicast:
+                      id: multicast-38ce
+                      steps:
+                        - bean:
+                            id: bean-3b8e
+                        - log:
+                            message: ${body}
+                            id: log-546f
+                        - loop:
+                            expression: {}
+                            id: loop-4635
+                            steps:
+                              - convertBodyTo:
+                                  id: convertBodyTo-1cae
+            otherwise:
+              id: otherwise-0b09
+              steps:
+                - filter:
+                    expression: {}
+                    id: filter-a02b
+            id: choice-c53c
+        - doTry:
+            id: doTry-8fd5
+            doCatch:
+              - id: doCatch-1071
+              - id: doCatch-c38e
+            steps:
+              - routingSlip:
+                  expression: {}
+                  id: routingSlip-a85a
+- route:
+    id: route-178a
+    from:
+      uri: kamelet:aws-cloudtrail-source
+      id: from-3e7d
+      steps:
+        - multicast:
+            id: multicast-eef7
+            steps:
+              - bean:
+                  id: bean-a5ef
+              - aggregate:
+                  id: aggregate-f5d8
+              - aggregate:
+                  id: aggregate-b9e7
+              - aggregate:
+                  id: aggregate-5eb8
+              - aggregate:
+                  id: aggregate-c57e
+              - aggregate:
+                  id: aggregate-1cd4
+              - bean:
+                  id: bean-72a1
+              - choice:
+                  when:
+                    - expression: {}
+                      id: when-a56b
+                  otherwise:
+                    id: otherwise-9f31
+                  id: choice-1905
+- route:
+    id: route-f435
+    from:
+      uri: kamelet:timer-source
+      id: from-e52c
+      steps:
+        - choice:
+            when:
+              - expression: {}
+                id: when-064f
+                steps:
+                  - multicast:
+                      id: multicast-38ce
+                      steps:
+                        - bean:
+                            id: bean-3b8e
+                        - log:
+                            message: ${body}
+                            id: log-546f
+                        - loop:
+                            expression: {}
+                            id: loop-4635
+                            steps:
+                              - convertBodyTo:
+                                  id: convertBodyTo-1cae
+            otherwise:
+              id: otherwise-0b09
+              steps:
+                - filter:
+                    expression: {}
+                    id: filter-a02b
+            id: choice-c53c
+        - doTry:
+            id: doTry-8fd5
+            doCatch:
+              - id: doCatch-1071
+              - id: doCatch-c38e
+            steps:
+              - routingSlip:
+                  expression: {}
+                  id: routingSlip-a85a
 - route:
     id: route-178a
     from:
@@ -50,22 +192,732 @@
             steps:
               - bean:
                   id: bean-a5ef
-#              - aggregate:
-#                  id: aggregate-f5d8
-#              - aggregate:
-#                  id: aggregate-b9e7
-#              - aggregate:
-#                  id: aggregate-5eb8
-#              - aggregate:
-#                  id: aggregate-c57e
-#              - aggregate:
-#                  id: aggregate-1cd4
-#              - bean:
-#                  id: bean-72a1
-#              - choice:
-#                  when:
-#                    - expression: {}
-#                      id: when-a56b
-#                  otherwise:
-#                    id: otherwise-9f31
-#                  id: choice-1905
+              - aggregate:
+                  id: aggregate-f5d8
+              - aggregate:
+                  id: aggregate-b9e7
+              - aggregate:
+                  id: aggregate-5eb8
+              - aggregate:
+                  id: aggregate-c57e
+              - aggregate:
+                  id: aggregate-1cd4
+              - bean:
+                  id: bean-72a1
+              - choice:
+                  when:
+                    - expression: {}
+                      id: when-a56b
+                  otherwise:
+                    id: otherwise-9f31
+                  id: choice-1905
+- route:
+    id: route-f435
+    from:
+      uri: kamelet:timer-source
+      id: from-e52c
+      steps:
+        - choice:
+            when:
+              - expression: {}
+                id: when-064f
+                steps:
+                  - multicast:
+                      id: multicast-38ce
+                      steps:
+                        - bean:
+                            id: bean-3b8e
+                        - log:
+                            message: ${body}
+                            id: log-546f
+                        - loop:
+                            expression: {}
+                            id: loop-4635
+                            steps:
+                              - convertBodyTo:
+                                  id: convertBodyTo-1cae
+            otherwise:
+              id: otherwise-0b09
+              steps:
+                - filter:
+                    expression: {}
+                    id: filter-a02b
+            id: choice-c53c
+        - doTry:
+            id: doTry-8fd5
+            doCatch:
+              - id: doCatch-1071
+              - id: doCatch-c38e
+            steps:
+              - routingSlip:
+                  expression: {}
+                  id: routingSlip-a85a
+- route:
+    id: route-178a
+    from:
+      uri: kamelet:aws-cloudtrail-source
+      id: from-3e7d
+      steps:
+        - multicast:
+            id: multicast-eef7
+            steps:
+              - bean:
+                  id: bean-a5ef
+              - aggregate:
+                  id: aggregate-f5d8
+              - aggregate:
+                  id: aggregate-b9e7
+              - aggregate:
+                  id: aggregate-5eb8
+              - aggregate:
+                  id: aggregate-c57e
+              - aggregate:
+                  id: aggregate-1cd4
+              - bean:
+                  id: bean-72a1
+              - choice:
+                  when:
+                    - expression: {}
+                      id: when-a56b
+                  otherwise:
+                    id: otherwise-9f31
+                  id: choice-1905
+- route:
+    id: route-f435
+    from:
+      uri: kamelet:timer-source
+      id: from-e52c
+      steps:
+        - choice:
+            when:
+              - expression: {}
+                id: when-064f
+                steps:
+                  - multicast:
+                      id: multicast-38ce
+                      steps:
+                        - bean:
+                            id: bean-3b8e
+                        - log:
+                            message: ${body}
+                            id: log-546f
+                        - loop:
+                            expression: {}
+                            id: loop-4635
+                            steps:
+                              - convertBodyTo:
+                                  id: convertBodyTo-1cae
+            otherwise:
+              id: otherwise-0b09
+              steps:
+                - filter:
+                    expression: {}
+                    id: filter-a02b
+            id: choice-c53c
+        - doTry:
+            id: doTry-8fd5
+            doCatch:
+              - id: doCatch-1071
+              - id: doCatch-c38e
+            steps:
+              - routingSlip:
+                  expression: {}
+                  id: routingSlip-a85a
+- route:
+    id: route-178a
+    from:
+      uri: kamelet:aws-cloudtrail-source
+      id: from-3e7d
+      steps:
+        - multicast:
+            id: multicast-eef7
+            steps:
+              - bean:
+                  id: bean-a5ef
+              - aggregate:
+                  id: aggregate-f5d8
+              - aggregate:
+                  id: aggregate-b9e7
+              - aggregate:
+                  id: aggregate-5eb8
+              - aggregate:
+                  id: aggregate-c57e
+              - aggregate:
+                  id: aggregate-1cd4
+              - bean:
+                  id: bean-72a1
+              - choice:
+                  when:
+                    - expression: {}
+                      id: when-a56b
+                  otherwise:
+                    id: otherwise-9f31
+                  id: choice-1905
+- route:
+    id: route-f435
+    from:
+      uri: kamelet:timer-source
+      id: from-e52c
+      steps:
+        - choice:
+            when:
+              - expression: {}
+                id: when-064f
+                steps:
+                  - multicast:
+                      id: multicast-38ce
+                      steps:
+                        - bean:
+                            id: bean-3b8e
+                        - log:
+                            message: ${body}
+                            id: log-546f
+                        - loop:
+                            expression: {}
+                            id: loop-4635
+                            steps:
+                              - convertBodyTo:
+                                  id: convertBodyTo-1cae
+            otherwise:
+              id: otherwise-0b09
+              steps:
+                - filter:
+                    expression: {}
+                    id: filter-a02b
+            id: choice-c53c
+        - doTry:
+            id: doTry-8fd5
+            doCatch:
+              - id: doCatch-1071
+              - id: doCatch-c38e
+            steps:
+              - routingSlip:
+                  expression: {}
+                  id: routingSlip-a85a
+- route:
+    id: route-178a
+    from:
+      uri: kamelet:aws-cloudtrail-source
+      id: from-3e7d
+      steps:
+        - multicast:
+            id: multicast-eef7
+            steps:
+              - bean:
+                  id: bean-a5ef
+              - aggregate:
+                  id: aggregate-f5d8
+              - aggregate:
+                  id: aggregate-b9e7
+              - aggregate:
+                  id: aggregate-5eb8
+              - aggregate:
+                  id: aggregate-c57e
+              - aggregate:
+                  id: aggregate-1cd4
+              - bean:
+                  id: bean-72a1
+              - choice:
+                  when:
+                    - expression: {}
+                      id: when-a56b
+                  otherwise:
+                    id: otherwise-9f31
+                  id: choice-1905
+- route:
+    id: route-f435
+    from:
+      uri: kamelet:timer-source
+      id: from-e52c
+      steps:
+        - choice:
+            when:
+              - expression: {}
+                id: when-064f
+                steps:
+                  - multicast:
+                      id: multicast-38ce
+                      steps:
+                        - bean:
+                            id: bean-3b8e
+                        - log:
+                            message: ${body}
+                            id: log-546f
+                        - loop:
+                            expression: {}
+                            id: loop-4635
+                            steps:
+                              - convertBodyTo:
+                                  id: convertBodyTo-1cae
+            otherwise:
+              id: otherwise-0b09
+              steps:
+                - filter:
+                    expression: {}
+                    id: filter-a02b
+            id: choice-c53c
+        - doTry:
+            id: doTry-8fd5
+            doCatch:
+              - id: doCatch-1071
+              - id: doCatch-c38e
+            steps:
+              - routingSlip:
+                  expression: {}
+                  id: routingSlip-a85a
+- route:
+    id: route-178a
+    from:
+      uri: kamelet:aws-cloudtrail-source
+      id: from-3e7d
+      steps:
+        - multicast:
+            id: multicast-eef7
+            steps:
+              - bean:
+                  id: bean-a5ef
+              - aggregate:
+                  id: aggregate-f5d8
+              - aggregate:
+                  id: aggregate-b9e7
+              - aggregate:
+                  id: aggregate-5eb8
+              - aggregate:
+                  id: aggregate-c57e
+              - aggregate:
+                  id: aggregate-1cd4
+              - bean:
+                  id: bean-72a1
+              - choice:
+                  when:
+                    - expression: {}
+                      id: when-a56b
+                  otherwise:
+                    id: otherwise-9f31
+                  id: choice-1905
+- route:
+    id: route-f435
+    from:
+      uri: kamelet:timer-source
+      id: from-e52c
+      steps:
+        - choice:
+            when:
+              - expression: {}
+                id: when-064f
+                steps:
+                  - multicast:
+                      id: multicast-38ce
+                      steps:
+                        - bean:
+                            id: bean-3b8e
+                        - log:
+                            message: ${body}
+                            id: log-546f
+                        - loop:
+                            expression: {}
+                            id: loop-4635
+                            steps:
+                              - convertBodyTo:
+                                  id: convertBodyTo-1cae
+            otherwise:
+              id: otherwise-0b09
+              steps:
+                - filter:
+                    expression: {}
+                    id: filter-a02b
+            id: choice-c53c
+        - doTry:
+            id: doTry-8fd5
+            doCatch:
+              - id: doCatch-1071
+              - id: doCatch-c38e
+            steps:
+              - routingSlip:
+                  expression: {}
+                  id: routingSlip-a85a
+- route:
+    id: route-178a
+    from:
+      uri: kamelet:aws-cloudtrail-source
+      id: from-3e7d
+      steps:
+        - multicast:
+            id: multicast-eef7
+            steps:
+              - bean:
+                  id: bean-a5ef
+              - aggregate:
+                  id: aggregate-f5d8
+              - aggregate:
+                  id: aggregate-b9e7
+              - aggregate:
+                  id: aggregate-5eb8
+              - aggregate:
+                  id: aggregate-c57e
+              - aggregate:
+                  id: aggregate-1cd4
+              - bean:
+                  id: bean-72a1
+              - choice:
+                  when:
+                    - expression: {}
+                      id: when-a56b
+                  otherwise:
+                    id: otherwise-9f31
+                  id: choice-1905
+- route:
+    id: route-f435
+    from:
+      uri: kamelet:timer-source
+      id: from-e52c
+      steps:
+        - choice:
+            when:
+              - expression: {}
+                id: when-064f
+                steps:
+                  - multicast:
+                      id: multicast-38ce
+                      steps:
+                        - bean:
+                            id: bean-3b8e
+                        - log:
+                            message: ${body}
+                            id: log-546f
+                        - loop:
+                            expression: {}
+                            id: loop-4635
+                            steps:
+                              - convertBodyTo:
+                                  id: convertBodyTo-1cae
+            otherwise:
+              id: otherwise-0b09
+              steps:
+                - filter:
+                    expression: {}
+                    id: filter-a02b
+            id: choice-c53c
+        - doTry:
+            id: doTry-8fd5
+            doCatch:
+              - id: doCatch-1071
+              - id: doCatch-c38e
+            steps:
+              - routingSlip:
+                  expression: {}
+                  id: routingSlip-a85a
+- route:
+    id: route-178a
+    from:
+      uri: kamelet:aws-cloudtrail-source
+      id: from-3e7d
+      steps:
+        - multicast:
+            id: multicast-eef7
+            steps:
+              - bean:
+                  id: bean-a5ef
+              - aggregate:
+                  id: aggregate-f5d8
+              - aggregate:
+                  id: aggregate-b9e7
+              - aggregate:
+                  id: aggregate-5eb8
+              - aggregate:
+                  id: aggregate-c57e
+              - aggregate:
+                  id: aggregate-1cd4
+              - bean:
+                  id: bean-72a1
+              - choice:
+                  when:
+                    - expression: {}
+                      id: when-a56b
+                  otherwise:
+                    id: otherwise-9f31
+                  id: choice-1905
+- route:
+    id: route-f435
+    from:
+      uri: kamelet:timer-source
+      id: from-e52c
+      steps:
+        - choice:
+            when:
+              - expression: {}
+                id: when-064f
+                steps:
+                  - multicast:
+                      id: multicast-38ce
+                      steps:
+                        - bean:
+                            id: bean-3b8e
+                        - log:
+                            message: ${body}
+                            id: log-546f
+                        - loop:
+                            expression: {}
+                            id: loop-4635
+                            steps:
+                              - convertBodyTo:
+                                  id: convertBodyTo-1cae
+            otherwise:
+              id: otherwise-0b09
+              steps:
+                - filter:
+                    expression: {}
+                    id: filter-a02b
+            id: choice-c53c
+        - doTry:
+            id: doTry-8fd5
+            doCatch:
+              - id: doCatch-1071
+              - id: doCatch-c38e
+            steps:
+              - routingSlip:
+                  expression: {}
+                  id: routingSlip-a85a
+- route:
+    id: route-178a
+    from:
+      uri: kamelet:aws-cloudtrail-source
+      id: from-3e7d
+      steps:
+        - multicast:
+            id: multicast-eef7
+            steps:
+              - bean:
+                  id: bean-a5ef
+              - aggregate:
+                  id: aggregate-f5d8
+              - aggregate:
+                  id: aggregate-b9e7
+              - aggregate:
+                  id: aggregate-5eb8
+              - aggregate:
+                  id: aggregate-c57e
+              - aggregate:
+                  id: aggregate-1cd4
+              - bean:
+                  id: bean-72a1
+              - choice:
+                  when:
+                    - expression: {}
+                      id: when-a56b
+                  otherwise:
+                    id: otherwise-9f31
+                  id: choice-1905
+- route:
+    id: route-f435
+    from:
+      uri: kamelet:timer-source
+      id: from-e52c
+      steps:
+        - choice:
+            when:
+              - expression: {}
+                id: when-064f
+                steps:
+                  - multicast:
+                      id: multicast-38ce
+                      steps:
+                        - bean:
+                            id: bean-3b8e
+                        - log:
+                            message: ${body}
+                            id: log-546f
+                        - loop:
+                            expression: {}
+                            id: loop-4635
+                            steps:
+                              - convertBodyTo:
+                                  id: convertBodyTo-1cae
+            otherwise:
+              id: otherwise-0b09
+              steps:
+                - filter:
+                    expression: {}
+                    id: filter-a02b
+            id: choice-c53c
+        - doTry:
+            id: doTry-8fd5
+            doCatch:
+              - id: doCatch-1071
+              - id: doCatch-c38e
+            steps:
+              - routingSlip:
+                  expression: {}
+                  id: routingSlip-a85a
+- route:
+    id: route-178a
+    from:
+      uri: kamelet:aws-cloudtrail-source
+      id: from-3e7d
+      steps:
+        - multicast:
+            id: multicast-eef7
+            steps:
+              - bean:
+                  id: bean-a5ef
+              - aggregate:
+                  id: aggregate-f5d8
+              - aggregate:
+                  id: aggregate-b9e7
+              - aggregate:
+                  id: aggregate-5eb8
+              - aggregate:
+                  id: aggregate-c57e
+              - aggregate:
+                  id: aggregate-1cd4
+              - bean:
+                  id: bean-72a1
+              - choice:
+                  when:
+                    - expression: {}
+                      id: when-a56b
+                  otherwise:
+                    id: otherwise-9f31
+                  id: choice-1905
+- route:
+    id: route-f435
+    from:
+      uri: kamelet:timer-source
+      id: from-e52c
+      steps:
+        - choice:
+            when:
+              - expression: {}
+                id: when-064f
+                steps:
+                  - multicast:
+                      id: multicast-38ce
+                      steps:
+                        - bean:
+                            id: bean-3b8e
+                        - log:
+                            message: ${body}
+                            id: log-546f
+                        - loop:
+                            expression: {}
+                            id: loop-4635
+                            steps:
+                              - convertBodyTo:
+                                  id: convertBodyTo-1cae
+            otherwise:
+              id: otherwise-0b09
+              steps:
+                - filter:
+                    expression: {}
+                    id: filter-a02b
+            id: choice-c53c
+        - doTry:
+            id: doTry-8fd5
+            doCatch:
+              - id: doCatch-1071
+              - id: doCatch-c38e
+            steps:
+              - routingSlip:
+                  expression: {}
+                  id: routingSlip-a85a
+- route:
+    id: route-178a
+    from:
+      uri: kamelet:aws-cloudtrail-source
+      id: from-3e7d
+      steps:
+        - multicast:
+            id: multicast-eef7
+            steps:
+              - bean:
+                  id: bean-a5ef
+              - aggregate:
+                  id: aggregate-f5d8
+              - aggregate:
+                  id: aggregate-b9e7
+              - aggregate:
+                  id: aggregate-5eb8
+              - aggregate:
+                  id: aggregate-c57e
+              - aggregate:
+                  id: aggregate-1cd4
+              - bean:
+                  id: bean-72a1
+              - choice:
+                  when:
+                    - expression: {}
+                      id: when-a56b
+                  otherwise:
+                    id: otherwise-9f31
+                  id: choice-1905
+- route:
+    id: route-f435
+    from:
+      uri: kamelet:timer-source
+      id: from-e52c
+      steps:
+        - choice:
+            when:
+              - expression: {}
+                id: when-064f
+                steps:
+                  - multicast:
+                      id: multicast-38ce
+                      steps:
+                        - bean:
+                            id: bean-3b8e
+                        - log:
+                            message: ${body}
+                            id: log-546f
+                        - loop:
+                            expression: {}
+                            id: loop-4635
+                            steps:
+                              - convertBodyTo:
+                                  id: convertBodyTo-1cae
+            otherwise:
+              id: otherwise-0b09
+              steps:
+                - filter:
+                    expression: {}
+                    id: filter-a02b
+            id: choice-c53c
+        - doTry:
+            id: doTry-8fd5
+            doCatch:
+              - id: doCatch-1071
+              - id: doCatch-c38e
+            steps:
+              - routingSlip:
+                  expression: {}
+                  id: routingSlip-a85a
+- route:
+    id: route-178a
+    from:
+      uri: kamelet:aws-cloudtrail-source
+      id: from-3e7d
+      steps:
+        - multicast:
+            id: multicast-eef7
+            steps:
+              - bean:
+                  id: bean-a5ef
+              - aggregate:
+                  id: aggregate-f5d8
+              - aggregate:
+                  id: aggregate-b9e7
+              - aggregate:
+                  id: aggregate-5eb8
+              - aggregate:
+                  id: aggregate-c57e
+              - aggregate:
+                  id: aggregate-1cd4
+              - bean:
+                  id: bean-72a1
+              - choice:
+                  when:
+                    - expression: {}
+                      id: when-a56b
+                  otherwise:
+                    id: otherwise-9f31
+                  id: choice-1905
diff --git a/karavan-designer/src/designer/KaravanDesigner.tsx b/karavan-designer/src/designer/KaravanDesigner.tsx
index 5f25fe91..940df7e2 100644
--- a/karavan-designer/src/designer/KaravanDesigner.tsx
+++ b/karavan-designer/src/designer/KaravanDesigner.tsx
@@ -66,13 +66,14 @@ export class KaravanInstance {
 export const KaravanDesigner = (props: Props) => {
 
     const [tab, setTab] = useState<string>('routes');
-    const [propertyOnly, setPropertyOnly] = useDesignerStore((state) =>
-        [state.propertyOnly, state.setPropertyOnly], shallow )
+    const [propertyOnly, setPropertyOnly, setDark] = useDesignerStore((state) =>
+        [state.propertyOnly, state.setPropertyOnly, state.setDark], shallow )
     const [integration, setIntegration] = useIntegrationStore((state) => 
         [state.integration, state.setIntegration], shallow )
 
     useEffect(() => {
         setIntegration(makeIntegration(props.yaml, props.filename));
+        setDark(props.dark);
     }, []);
 
     function makeIntegration  (yaml: string, filename: string): Integration {
@@ -131,7 +132,7 @@ export const KaravanDesigner = (props: Props) => {
             {tab === 'routes' && <RouteDesigner
                 // integration={integration}
                                                 // onSave={(integration, propertyOnly) => save(integration, propertyOnly)}
-                                                dark={props.dark}/>}
+                                                />}
             {/*{tab === 'rest' && <RestDesigner integration={integration}*/}
             {/*                                 onSave={(integration, propertyOnly) => save(integration, propertyOnly)}*/}
             {/*                                 dark={props.dark}/>}*/}
diff --git a/karavan-designer/src/designer/KaravanStore.ts b/karavan-designer/src/designer/KaravanStore.ts
index 4dc570ed..3bd2ed91 100644
--- a/karavan-designer/src/designer/KaravanStore.ts
+++ b/karavan-designer/src/designer/KaravanStore.ts
@@ -136,7 +136,10 @@ export const useConnectionsStore = createWithEqualityFn<ConnectionsState>((set)
 }), shallow)
 
 interface DesignerState {
+    dark: boolean;
+    setDark: (dark: boolean) => void;
     shiftKeyPressed: boolean;
+    setShiftKeyPressed: (shiftKeyPressed: boolean) => void;
     showDeleteConfirmation: boolean;
     setShowDeleteConfirmation: (showDeleteConfirmation: boolean) => void;
     showMoveConfirmation: boolean;
@@ -149,8 +152,8 @@ interface DesignerState {
     setSelectedStep: (selectedStep?: CamelElement) => void;
     selectedUuids: string[];
     setSelectedUuids: (selectedUuids: string[]) => void;
-    clipboardSteps: string[];
-    setClipboardSteps: (clipboardSteps: string[]) => void;
+    clipboardSteps: CamelElement[];
+    setClipboardSteps: (clipboardSteps: CamelElement[]) => void;
     width: number,
     height: number,
     top: number,
@@ -159,6 +162,7 @@ interface DesignerState {
 }
 
 export const useDesignerStore = createWithEqualityFn<DesignerState>((set) => ({
+    dark: false,
     shiftKeyPressed: false,
     showDeleteConfirmation: false,
     showMoveConfirmation: false,
@@ -166,6 +170,12 @@ export const useDesignerStore = createWithEqualityFn<DesignerState>((set) => ({
     selectedUuids: [],
     clipboardSteps: [],
     propertyOnly: false,
+    setDark: (dark: boolean) => {
+        set({dark: dark})
+    },
+    setShiftKeyPressed: (shiftKeyPressed: boolean) => {
+        set({shiftKeyPressed: shiftKeyPressed})
+    },
     setSelectedStep: (selectedStep?: CamelElement) => {
         set({selectedStep: selectedStep})
     },
@@ -184,7 +194,7 @@ export const useDesignerStore = createWithEqualityFn<DesignerState>((set) => ({
     setSelectedUuids: (selectedUuids: string[]) => {
         set({selectedUuids: selectedUuids})
     },
-    setClipboardSteps: (clipboardSteps: string[]) => {
+    setClipboardSteps: (clipboardSteps: CamelElement[]) => {
         set({clipboardSteps: clipboardSteps})
     },
     width: 100,
diff --git a/karavan-designer/src/designer/route/DslElement.tsx b/karavan-designer/src/designer/route/DslElement.tsx
index bf246051..19554e03 100644
--- a/karavan-designer/src/designer/route/DslElement.tsx
+++ b/karavan-designer/src/designer/route/DslElement.tsx
@@ -14,7 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import React, {CSSProperties, useEffect, useRef, useState} from 'react';
+import React, {CSSProperties, useEffect, useMemo, useRef, useState} from 'react';
 import {
     Button,
     Flex,
@@ -60,11 +60,6 @@ export const DslElement = (props: Props) => {
     const [isDraggedOver, setIsDraggedOver] = useState<boolean>(false);
     const [moveElements, setMoveElements] = useState<[string | undefined, string | undefined]>([undefined, undefined]);
 
-    // const elementRef = useRef(null);
-
-    // useEffect(() => {
-    // }, [elementRef]);
-
     function onOpenSelector (evt: React.MouseEvent, showSteps: boolean = true, isInsert: boolean = false) {
         evt.stopPropagation();
         if (isInsert && props.parent) {
@@ -76,7 +71,6 @@ export const DslElement = (props: Props) => {
 
     function onDeleteElement (evt: React.MouseEvent) {
         evt.stopPropagation();
-        console.log("!!!!!")
         onShowDeleteConfirmation(props.step.uuid);
     }
 
@@ -200,7 +194,6 @@ export const DslElement = (props: Props) => {
     }
 
     function sendPosition (el: HTMLDivElement | null, isSelected: boolean) {
-        // console.log("sendPosition", props.step)
         const node = el;
         if (node && el) {
             const header = Array.from(node.childNodes.values()).filter((n: any) => n.classList.contains("header"))[0];
@@ -218,11 +211,21 @@ export const DslElement = (props: Props) => {
         }
     }
 
+    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 availableModels = CamelUi.getSelectorModelsForParent(step.dslName, false);
         const showAddButton = !['CatchDefinition', 'RouteDefinition'].includes(step.dslName) && availableModels.length > 0;
         const showInsertButton =
             !['FromDefinition', 'RouteConfigurationDefinition', 'RouteDefinition', 'CatchDefinition', 'FinallyDefinition', 'WhenDefinition', 'OtherwiseDefinition'].includes(step.dslName)
diff --git a/karavan-designer/src/designer/route/DslSelector.tsx b/karavan-designer/src/designer/route/DslSelector.tsx
index 798a6ed6..af0668e2 100644
--- a/karavan-designer/src/designer/route/DslSelector.tsx
+++ b/karavan-designer/src/designer/route/DslSelector.tsx
@@ -29,7 +29,6 @@ import {shallow} from "zustand/shallow";
 import {useRouteDesignerHook} from "./useRouteDesignerHook";
 
 interface Props {
-    dark: boolean,
     tabIndex?: string | number
 }
 
@@ -41,11 +40,8 @@ export const DslSelector = (props: Props) => {
             [s.showSelector, s.showSteps, s.parentId, s.parentDsl, s.selectorTabIndex, s.setShowSelector, s.setSelectorTabIndex,
                 s.selectedPosition, s.selectedLabels, s.setSelectedLabels, s.addSelectedLabel, s.deleteSelectedLabel], shallow)
 
-    //
-    // const [showSelector, showDeleteConfirmation, showSteps, deleteMessage, parentId, parentDsl, selectedPosition, selectorTabIndex,
-    //     setShowSelector, setShowDeleteConfirmation, setPosition, setSelectorTabIndex, width, height, top, left] = useDesignerStore((s) =>
-    //     [s.showSelector, s.showDeleteConfirmation, s.showSteps, s.deleteMessage, s.parentId, s.parentDsl, s.selectedPosition, s.selectorTabIndex,
-    //         s.setShowSelector, s.setShowDeleteConfirmation, s.setPosition, s.setSelectorTabIndex, s.width, s.height, s.top, s.left], shallow)
+
+    const [dark] = useDesignerStore((s) => [s.dark], shallow)
 
     const {deleteElement, selectElement, moveElement, onShowDeleteConfirmation, onDslSelect, openSelector, createRouteConfiguration} = useRouteDesignerHook();
 
@@ -174,7 +170,7 @@ export const DslSelector = (props: Props) => {
                 </Flex>
             }
             actions={{}}>
-            <PageSection padding={{default: "noPadding"}} variant={props.dark ? "darker" : "light"}>
+            <PageSection padding={{default: "noPadding"}} variant={dark ? "darker" : "light"}>
                 {isEip && <ToggleGroup aria-label="Labels" isCompact>
                     {eipLabels.map(eipLabel => <ToggleGroupItem
                         key={eipLabel}
diff --git a/karavan-designer/src/designer/route/RouteDesigner.tsx b/karavan-designer/src/designer/route/RouteDesigner.tsx
index 878d806c..d605044a 100644
--- a/karavan-designer/src/designer/route/RouteDesigner.tsx
+++ b/karavan-designer/src/designer/route/RouteDesigner.tsx
@@ -39,22 +39,14 @@ import {CamelUtil} from "karavan-core/lib/api/CamelUtil";
 import {RouteConfigurationDefinition} from "karavan-core/lib/model/CamelDefinition";
 import {CamelDefinitionApiExt} from "karavan-core/lib/api/CamelDefinitionApiExt";
 import useResizeObserver from "./useResizeObserver";
-import {DslPosition} from "../utils/EventBus";
+import {Command, DslPosition, EventBus} from "../utils/EventBus";
 import useMutationsObserver from "./useDrawerMutationsObserver";
 import {DeleteConfirmation} from "./DeleteConfirmation";
 
-interface Props {
-    // onSave?: (integration: Integration, propertyOnly: boolean) => void
-    // integration: Integration
-    dark: boolean
-}
+export const RouteDesigner = () => {
 
-export const RouteDesigner = (props: Props) => {
 
-    const printerRef = React.createRef();
-    const contentRef: React.RefObject<HTMLDivElement> = useRef(null);
-
-    const {deleteElement, selectElement, moveElement, onShowDeleteConfirmation, onDslSelect, openSelector, createRouteConfiguration} = useRouteDesignerHook();
+    const {deleteElement, selectElement, moveElement, onShowDeleteConfirmation, onDslSelect, openSelector, createRouteConfiguration, handleKeyDown, handleKeyUp, onCommand} = useRouteDesignerHook();
 
     const [integration] = useIntegrationStore((state) => [state.integration], shallow)
     const [showDeleteConfirmation, deleteMessage , setShowDeleteConfirmation, setPosition, width, height, top, left] =
@@ -67,14 +59,6 @@ export const RouteDesigner = (props: Props) => {
 
     const [clearSteps] = useConnectionsStore((s) => [s.clearSteps])
 
-    useEffect(() => {
-        if (flowRef.current === null) {
-            clearSteps();
-        } else {
-            changeGraphSize();
-        }
-    }, [showSelector]);
-
     const onChangeGraphSize = useCallback((target: HTMLDivElement)  => {
         changeGraphSize();
     }, [])
@@ -90,32 +74,27 @@ export const RouteDesigner = (props: Props) => {
 
     const firstRef = useResizeObserver(onChangeGraphSize);
     const secondRef = useMutationsObserver(onChangeGraphSize);
+    const printerRef = useRef<HTMLDivElement | null>(null);
     const flowRef = useRef<HTMLDivElement | null>(null);
+    const contentRef: React.RefObject<HTMLDivElement> = useRef(null);
 
-    // function componentDidMount() {
-    //     logic.componentDidMount();
-    // }
-    //
-    // function componentWillUnmount() {
-    //     logic.componentWillUnmount();
-    // }
-    //
-    // function handleResize = (event: any) => {
-    //     return logic.handleResize(event);
-    // }
-    //
-    // function handleKeyDown = (event: KeyboardEvent) => {
-    //     return logic.handleKeyDown(event);
-    // }
-    //
-    // function handleKeyUp = (event: KeyboardEvent) => {
-    //     return logic.handleKeyUp(event);
-    // }
-    //
-    // function componentDidUpdate = (prevProps: Readonly<Props>, prevState: Readonly<RouteDesignerState>, snapshot?: any) => {
-    //     return logic.componentDidUpdate(prevState, snapshot);
-    // }
-
+    useEffect(()=>{
+        window.addEventListener('resize', changeGraphSize);
+        window.addEventListener('keydown', handleKeyDown);
+        window.addEventListener('keyup', handleKeyUp);
+        const commandSub = EventBus.onCommand()?.subscribe((command: Command) => onCommand(command, printerRef));
+        if (flowRef.current === null) {
+            clearSteps();
+        } else {
+            changeGraphSize();
+        }
+        return ()=> {
+            window.removeEventListener('resize', changeGraphSize);
+            window.removeEventListener('keydown', handleKeyDown);
+            window.removeEventListener('keyup', handleKeyUp);
+            commandSub?.unsubscribe();
+        }
+    }, [showSelector])
 
     function getDeleteConfirmation() {
         let htmlContent: string = deleteMessage;
@@ -158,12 +137,11 @@ export const RouteDesigner = (props: Props) => {
         const routes = CamelUi.getRoutes(integration);
         const routeConfigurations = CamelUi.getRouteConfigurations(integration);
         return (
-            <div className="graph">
+            <div className="graph" ref={printerRef}>
                 <DslConnections/>
                 <div id="flows" className="flows" data-click="FLOWS" onClick={event => {
                     // logic.unselectElement(event)
                 }}
-
                      ref={flowRef}
                 >
                     {routeConfigurations?.map((routeConfiguration, index: number) => (
@@ -212,7 +190,7 @@ export const RouteDesigner = (props: Props) => {
                     </DrawerContent>
                 </Drawer>
             </div>
-            {showSelector && <DslSelector dark={props.dark}/>}
+            {showSelector && <DslSelector/>}
             {showDeleteConfirmation && <DeleteConfirmation/>}
         </div>
     )
diff --git a/karavan-designer/src/designer/route/useRouteDesignerHook.tsx b/karavan-designer/src/designer/route/useRouteDesignerHook.tsx
index f2c995da..4a69a10d 100644
--- a/karavan-designer/src/designer/route/useRouteDesignerHook.tsx
+++ b/karavan-designer/src/designer/route/useRouteDesignerHook.tsx
@@ -34,15 +34,16 @@ import {useDesignerStore, useIntegrationStore, useSelectorStore} from "../Karava
 import {shallow} from "zustand/shallow";
 import {setTabIndex} from "@patternfly/react-core";
 
+
 export const useRouteDesignerHook = () => {
 
     const [integration, setIntegration] = useIntegrationStore((state) => [state.integration, state.setIntegration], shallow)
     const [ showDeleteConfirmation, propertyOnly,deleteMessage, selectedUuids,clipboardSteps,selectedStep,shiftKeyPressed,
-        setShowDeleteConfirmation,setPropertyOnly, setDeleteMessage, setSelectedStep, setSelectedUuids, setClipboardSteps, setPosition,
-        width, height, top, left] = useDesignerStore((s) =>
+        setShowDeleteConfirmation,setPropertyOnly, setDeleteMessage, setSelectedStep, setSelectedUuids, setClipboardSteps, setPosition, setShiftKeyPressed,
+        width, height, top, left, dark] = useDesignerStore((s) =>
         [s.showDeleteConfirmation, s.propertyOnly,s.deleteMessage, s.selectedUuids,s.clipboardSteps, s.selectedStep, s.shiftKeyPressed,
-             s.setShowDeleteConfirmation,s.setPropertyOnly,s.setDeleteMessage, s.setSelectedStep, s.setSelectedUuids, s.setClipboardSteps, s.setPosition,
-            s.width, s.height, s.top, s.left], shallow)
+             s.setShowDeleteConfirmation,s.setPropertyOnly,s.setDeleteMessage, s.setSelectedStep, s.setSelectedUuids, s.setClipboardSteps, s.setPosition, s.setShiftKeyPressed,
+            s.width, s.height, s.top, s.left, s.dark], shallow)
     const [setParentId, setShowSelector, setSelectorTabIndex, setParentDsl, setShowSteps, setSelectedPosition] = useSelectorStore((s) =>
         [s.setParentId, s.setShowSelector, s.setSelectorTabIndex, s.setParentDsl, s.setShowSteps, s.setSelectedPosition], shallow)
 
@@ -78,80 +79,69 @@ export const useRouteDesignerHook = () => {
     //     this.routeDesigner.setState({key: Math.random().toString()});
     // }
     //
-    // function handleKeyDown = (event: KeyboardEvent) => {
-    //     if ((event.shiftKey)) {
-    //         this.routeDesigner.setState({shiftKeyPressed: true});
-    //     }
-    //     if (window.document.hasFocus() && window.document.activeElement) {
-    //         if (['BODY', 'MAIN'].includes(window.document.activeElement.tagName)) {
-    //             let charCode = String.fromCharCode(event.which).toLowerCase();
-    //             if ((event.ctrlKey || event.metaKey) && charCode === 'c') {
-    //                 this.copyToClipboard();
-    //             } else if ((event.ctrlKey || event.metaKey) && charCode === 'v') {
-    //                 this.pasteFromClipboard();
-    //             }
-    //         }
-    //     } else {
-    //         if (event.repeat) {
-    //             window.dispatchEvent(event);
-    //         }
-    //     }
-    // }
-    //
-    // function handleKeyUp = (event: KeyboardEvent) => {
-    //     this.routeDesigner.setState({shiftKeyPressed: false});
-    //     if (event.repeat) {
-    //         window.dispatchEvent(event);
-    //     }
-    // }
-    //
-    // function componentDidUpdate = (prevState: Readonly<RouteDesignerState>, snapshot?: any) => {
-    //     if (prevState.key !== this.routeDesigner.state.key) {
-    //         this.routeDesigner.props.onSave?.call(this, this.routeDesigner.state.integration, this.routeDesigner.state.propertyOnly);
-    //     }
-    // }
-    //
-    // copyToClipboard = (): void => {
-    //     const {integration, selectedUuids} = this.routeDesigner.state;
-    //     const steps: CamelElement[] = []
-    //     selectedUuids.forEach(selectedUuid => {
-    //         const selectedElement = CamelDefinitionApiExt.findElementInIntegration(integration, selectedUuid);
-    //         if (selectedElement) {
-    //             steps.push(selectedElement);
-    //         }
-    //     })
-    //     if (steps.length >0) {
-    //         this.routeDesigner.setState(prevState => ({
-    //             key: Math.random().toString(),
-    //             clipboardSteps: [...steps]
-    //         }));
-    //     }
-    // }
-    // function pasteFromClipboard = (): void => {
-    //     const {integration, selectedUuids, clipboardSteps} = this.routeDesigner.state;
-    //     if (clipboardSteps.length === 1 && clipboardSteps[0]?.dslName === 'FromDefinition') {
-    //         const clone = CamelUtil.cloneStep(clipboardSteps[0], true);
-    //         const route = CamelDefinitionApi.createRouteDefinition({from: clone});
-    //         this.addStep(route, '', 0)
-    //     } else if (clipboardSteps.length === 1 && clipboardSteps[0]?.dslName === 'RouteDefinition') {
-    //         const clone = CamelUtil.cloneStep(clipboardSteps[0], true);
-    //         this.addStep(clone, '', 0)
-    //     } else if (selectedUuids.length === 1) {
-    //         const targetMeta = CamelDefinitionApiExt.findElementMetaInIntegration(integration, selectedUuids[0]);
-    //         clipboardSteps.reverse().forEach(clipboardStep => {
-    //             if (clipboardStep && targetMeta.parentUuid) {
-    //                 const clone = CamelUtil.cloneStep(clipboardStep, true);
-    //                 this.addStep(clone, targetMeta.parentUuid, targetMeta.position);
-    //             }
-    //         })
-    //     }
-    // }
+    function handleKeyDown (event: KeyboardEvent) {
+        if ((event.shiftKey)) {
+            setShiftKeyPressed(true);
+        }
+        if (window.document.hasFocus() && window.document.activeElement) {
+            if (['BODY', 'MAIN'].includes(window.document.activeElement.tagName)) {
+                let charCode = String.fromCharCode(event.which).toLowerCase();
+                if ((event.ctrlKey || event.metaKey) && charCode === 'c') {
+                    copyToClipboard();
+                } else if ((event.ctrlKey || event.metaKey) && charCode === 'v') {
+                    pasteFromClipboard();
+                }
+            }
+        } else {
+            if (event.repeat) {
+                window.dispatchEvent(event);
+            }
+        }
+    }
+
+    function handleKeyUp (event: KeyboardEvent) {
+        setShiftKeyPressed(false);
+        if (event.repeat) {
+            window.dispatchEvent(event);
+        }
+    }
+
+    function copyToClipboard  (): void {
+        const steps: CamelElement[] = []
+        selectedUuids.forEach(selectedUuid => {
+            const selectedElement = CamelDefinitionApiExt.findElementInIntegration(integration, selectedUuid);
+            if (selectedElement) {
+                steps.push(selectedElement);
+            }
+        })
+        if (steps.length > 0) {
+            setClipboardSteps(steps);
+        }
+    }
+    function pasteFromClipboard (): void {
+        if (clipboardSteps.length === 1 && clipboardSteps[0]?.dslName === 'FromDefinition') {
+            const clone = CamelUtil.cloneStep(clipboardSteps[0], true);
+            const route = CamelDefinitionApi.createRouteDefinition({from: clone});
+            addStep(route, '', 0)
+        } else if (clipboardSteps.length === 1 && clipboardSteps[0]?.dslName === 'RouteDefinition') {
+            const clone = CamelUtil.cloneStep(clipboardSteps[0], true);
+            addStep(clone, '', 0)
+        } else if (selectedUuids.length === 1) {
+            const targetMeta = CamelDefinitionApiExt.findElementMetaInIntegration(integration, selectedUuids[0]);
+            clipboardSteps.reverse().forEach(clipboardStep => {
+                if (clipboardStep && targetMeta.parentUuid) {
+                    const clone = CamelUtil.cloneStep(clipboardStep, true);
+                    addStep(clone, targetMeta.parentUuid, targetMeta.position);
+                }
+            })
+        }
+    }
     //
-    // function  onCommand = (command: Command) => {
-    //     switch (command.command){
-    //         case "downloadImage": this.integrationImageDownload()
-    //     }
-    // }
+    function  onCommand (command: Command, printerRef: React.MutableRefObject<HTMLDivElement | null>) {
+        switch (command.command){
+            case "downloadImage": integrationImageDownload(printerRef);
+        }
+    }
     //
     // function onPropertyUpdate = debounce((element: CamelElement, newRoute?: RouteToCreate) => {
     //     if (newRoute) {
@@ -230,6 +220,7 @@ export const useRouteDesignerHook = () => {
         }
         const add = shiftKeyPressed && !selectedUuids.includes(element.uuid);
         const remove = shiftKeyPressed && selectedUuids.includes(element.uuid);
+        // TODO: do we need to change Intgration just for select????
         const i = CamelDisplayUtil.setIntegrationVisibility(integration, element.uuid);
 
         if (remove) {
@@ -243,7 +234,6 @@ export const useRouteDesignerHook = () => {
 
         setIntegration(i);
         setSelectedStep(selectedElement);
-        setShowSelector(false);
         setSelectedUuids(shiftKeyPressed ? [...selectedUuids] : [element.uuid])
     }
     //
@@ -349,37 +339,35 @@ export const useRouteDesignerHook = () => {
         setPropertyOnly(false);
         setSelectedUuids([source]);
     }
-    //
 
-    //
-    // function downloadIntegrationImage(dataUrl: string) {
-    //     const a = document.createElement('a');
-    //     a.setAttribute('download', 'karavan-routes.png');
-    //     a.setAttribute('href', dataUrl);
-    //     a.click();
-    // }
-    //
-    // function  integrationImageDownloadFilter = (node: HTMLElement) => {
-    //     const exclusionClasses = ['add-flow'];
-    //     return !exclusionClasses.some(classname => {
-    //         return node.classList === undefined ? false : node.classList.contains(classname);
-    //     });
-    // }
-    //
-    // function integrationImageDownload() {
-    //     if (this.routeDesigner.state.printerRef.current === null) {
-    //         return
-    //     }
-    //     toPng(this.routeDesigner.state.printerRef.current, {
-    //         style: {overflow: 'hidden'}, cacheBust: true, filter: this.integrationImageDownloadFilter,
-    //         height: this.routeDesigner.state.height, width: this.routeDesigner.state.width, backgroundColor: this.routeDesigner.props.dark ? "black" : "white"
-    //     }).then(v => {
-    //         toPng(this.routeDesigner.state.printerRef.current, {
-    //             style: {overflow: 'hidden'}, cacheBust: true, filter: this.integrationImageDownloadFilter,
-    //             height: this.routeDesigner.state.height, width: this.routeDesigner.state.width, backgroundColor: this.routeDesigner.props.dark ? "black" : "white"
-    //         }).then(this.downloadIntegrationImage);
-    //     })
-    // }
+    function downloadIntegrationImage(dataUrl: string) {
+        const a = document.createElement('a');
+        a.setAttribute('download', 'karavan-routes.png');
+        a.setAttribute('href', dataUrl);
+        a.click();
+    }
+
+    function  integrationImageDownloadFilter (node: HTMLElement) {
+        const exclusionClasses = ['add-flow'];
+        return !exclusionClasses.some(classname => {
+            return node.classList === undefined ? false : node.classList.contains(classname);
+        });
+    }
+
+    function integrationImageDownload(printerRef: React.MutableRefObject<HTMLDivElement | null>) {
+        const ref = printerRef.current;
+        if (ref !== null) {
+            toPng(ref, {
+                style: {overflow: 'hidden'}, cacheBust: true, filter: integrationImageDownloadFilter,
+                height: height, width: width, backgroundColor: dark ? "black" : "white"
+            }).then(v => {
+                toPng(ref, {
+                    style: {overflow: 'hidden'}, cacheBust: true, filter: integrationImageDownloadFilter,
+                    height: height, width: width, backgroundColor: dark ? "black" : "white"
+                }).then(downloadIntegrationImage);
+            })
+        }
+    }
 
-    return { deleteElement, selectElement, moveElement, onShowDeleteConfirmation, onDslSelect, openSelector, createRouteConfiguration}
+    return { deleteElement, selectElement, moveElement, onShowDeleteConfirmation, onDslSelect, openSelector, createRouteConfiguration, handleKeyDown, handleKeyUp, onCommand}
 }
\ No newline at end of file


[camel-karavan] 03/06: Copy and Paste for #836

Posted by ma...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

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

commit f432faaf7d88a93b9bf4474ee5a6866814a46cd2
Author: Marat Gubaidullin <ma...@Marats-MacBook-Pro.local>
AuthorDate: Wed Aug 23 19:13:18 2023 -0400

    Copy and Paste for #836
---
 karavan-designer/src/designer/KaravanStore.ts      |  12 +-
 .../src/designer/route/RouteDesigner.tsx           |  45 ++------
 .../src/designer/route/useRouteDesignerHook.tsx    | 123 ++++++++++-----------
 3 files changed, 82 insertions(+), 98 deletions(-)

diff --git a/karavan-designer/src/designer/KaravanStore.ts b/karavan-designer/src/designer/KaravanStore.ts
index 3bd2ed91..daae7f4f 100644
--- a/karavan-designer/src/designer/KaravanStore.ts
+++ b/karavan-designer/src/designer/KaravanStore.ts
@@ -192,10 +192,18 @@ export const useDesignerStore = createWithEqualityFn<DesignerState>((set) => ({
         set({deleteMessage: deleteMessage})
     },
     setSelectedUuids: (selectedUuids: string[]) => {
-        set({selectedUuids: selectedUuids})
+        set((state: DesignerState) => {
+            state.selectedUuids.length = 0;
+            state.selectedUuids.push(...selectedUuids);
+            return state;
+        })
     },
     setClipboardSteps: (clipboardSteps: CamelElement[]) => {
-        set({clipboardSteps: clipboardSteps})
+        set((state: DesignerState) => {
+            state.clipboardSteps.length = 0;
+            state.clipboardSteps.push(...clipboardSteps);
+            return state;
+        })
     },
     width: 100,
     height: 100,
diff --git a/karavan-designer/src/designer/route/RouteDesigner.tsx b/karavan-designer/src/designer/route/RouteDesigner.tsx
index 5e373f0d..ce79383e 100644
--- a/karavan-designer/src/designer/route/RouteDesigner.tsx
+++ b/karavan-designer/src/designer/route/RouteDesigner.tsx
@@ -39,19 +39,22 @@ import {CamelUtil} from "karavan-core/lib/api/CamelUtil";
 import {RouteConfigurationDefinition} from "karavan-core/lib/model/CamelDefinition";
 import {CamelDefinitionApiExt} from "karavan-core/lib/api/CamelDefinitionApiExt";
 import useResizeObserver from "./useResizeObserver";
-import {Command, DslPosition, EventBus} from "../utils/EventBus";
+import {Command, EventBus} from "../utils/EventBus";
 import useMutationsObserver from "./useDrawerMutationsObserver";
 import {DeleteConfirmation} from "./DeleteConfirmation";
+import {CamelDefinitionApi} from "karavan-core/lib/api/CamelDefinitionApi";
 
 export const RouteDesigner = () => {
 
 
-    const {deleteElement, selectElement, moveElement, onShowDeleteConfirmation, onDslSelect, openSelector, createRouteConfiguration, handleKeyDown, handleKeyUp, onCommand} = useRouteDesignerHook();
+    const {deleteElement, selectElement, moveElement, onShowDeleteConfirmation, onDslSelect, openSelector, createRouteConfiguration, onCommand, handleKeyDown, handleKeyUp} = useRouteDesignerHook();
 
     const [integration] = useIntegrationStore((state) => [state.integration], shallow)
-    const [showDeleteConfirmation, deleteMessage , setShowDeleteConfirmation, setPosition, width, height, top, left] =
+    const [showDeleteConfirmation, deleteMessage , setShowDeleteConfirmation, setPosition, setSelectedUuids, setShiftKeyPressed, setClipboardSteps,
+        clipboardSteps, selectedUuids, width, height, top, left] =
         useDesignerStore((s) =>
-        [s.showDeleteConfirmation, s.deleteMessage, s.setShowDeleteConfirmation, s.setPosition, s.width, s.height, s.top, s.left], shallow)
+        [s.showDeleteConfirmation, s.deleteMessage, s.setShowDeleteConfirmation, s.setPosition, s.setSelectedUuids, s.setShiftKeyPressed, s.setClipboardSteps,
+          s.clipboardSteps, s.selectedUuids, s.width, s.height, s.top, s.left], shallow)
 
     const [showSelector, showSteps, parentId, parentDsl, selectorTabIndex, setShowSelector, setSelectorTabIndex] =
         useSelectorStore((s) =>
@@ -64,18 +67,10 @@ export const RouteDesigner = () => {
     }, [])
 
     function changeGraphSize ()  {
-        console.log("changeGraphSize")
         if (flowRef && flowRef.current) {
             const el = flowRef.current;
             const rect = el.getBoundingClientRect();
-            // if (width !== el.scrollWidth || height !== el.scrollHeight || top !== rect.top || left !== rect.left) {
-            if (width !== rect.width || height !== rect?.height || top !== rect.top || left !== rect.left) {
-                console.log("State", width, height, top, left)
-                console.log("Graph", rect.width, rect.height, rect.top, rect.left)
-                setPosition(rect.width, rect.height, rect.top, rect.left)
-                // setPosition(el?.scrollWidth, el?.scrollHeight, el?.scrollTop, el?.scrollLeft)
-                // setPosition(el?.scrollWidth, el?.scrollHeight, rect.top, rect.left)
-            }
+            setPosition(rect.width, rect.height, rect.top, rect.left)
         }
     }
 
@@ -84,7 +79,8 @@ export const RouteDesigner = () => {
     const printerRef = useRef<HTMLDivElement | null>(null);
     const flowRef = useRef<HTMLDivElement | null>(null);
 
-    useEffect(()=>{
+    useEffect(()=> {
+        console.log(integration)
         // window.addEventListener('resize', changeGraphSize);
         const interval = setInterval(() => {
             changeGraphSize();
@@ -104,26 +100,7 @@ export const RouteDesigner = () => {
             window.removeEventListener('keyup', handleKeyUp);
             commandSub?.unsubscribe();
         }
-    }, [showSelector])
-
-    function getDeleteConfirmation() {
-        let htmlContent: string = deleteMessage;
-        return (<Modal
-            className="modal-delete"
-            title="Confirmation"
-            isOpen={showDeleteConfirmation}
-            onClose={() => setShowDeleteConfirmation(false)}
-            actions={[
-                <Button key="confirm" variant="primary" onClick={e => deleteElement()}>Delete</Button>,
-                <Button key="cancel" variant="link"
-                        onClick={e => setShowDeleteConfirmation(false)}>Cancel</Button>
-            ]}
-            onEscapePress={e => setShowDeleteConfirmation(false)}>
-            <div>
-                {htmlContent}
-            </div>
-        </Modal>)
-    }
+    }, [showSelector, integration])
 
     function getPropertiesPanel() {
         return (
diff --git a/karavan-designer/src/designer/route/useRouteDesignerHook.tsx b/karavan-designer/src/designer/route/useRouteDesignerHook.tsx
index 993e45ef..26c88dd6 100644
--- a/karavan-designer/src/designer/route/useRouteDesignerHook.tsx
+++ b/karavan-designer/src/designer/route/useRouteDesignerHook.tsx
@@ -79,66 +79,7 @@ export const useRouteDesignerHook = () => {
     //     this.routeDesigner.setState({key: Math.random().toString()});
     // }
     //
-    function handleKeyDown (event: KeyboardEvent) {
-        if ((event.shiftKey)) {
-            setShiftKeyPressed(true);
-        }
-        if (window.document.hasFocus() && window.document.activeElement) {
-            if (['BODY', 'MAIN'].includes(window.document.activeElement.tagName)) {
-                let charCode = String.fromCharCode(event.which).toLowerCase();
-                if ((event.ctrlKey || event.metaKey) && charCode === 'c') {
-                    copyToClipboard();
-                } else if ((event.ctrlKey || event.metaKey) && charCode === 'v') {
-                    pasteFromClipboard();
-                }
-            }
-        } else {
-            if (event.repeat) {
-                window.dispatchEvent(event);
-            }
-        }
-    }
 
-    function handleKeyUp (event: KeyboardEvent) {
-        setShiftKeyPressed(false);
-        if (event.repeat) {
-            window.dispatchEvent(event);
-        }
-    }
-
-    function copyToClipboard  (): void {
-        console.log("copyToClipboard1", selectedUuids)
-        const steps: CamelElement[] = []
-        selectedUuids.forEach(selectedUuid => {
-            const selectedElement = CamelDefinitionApiExt.findElementInIntegration(integration, selectedUuid);
-            if (selectedElement) {
-                steps.push(selectedElement);
-            }
-        })
-        if (steps.length > 0) {
-            console.log("copyToClipboard2", steps)
-            setClipboardSteps(steps);
-        }
-    }
-    function pasteFromClipboard (): void {
-        console.log("pasteFromClipboard")
-        if (clipboardSteps.length === 1 && clipboardSteps[0]?.dslName === 'FromDefinition') {
-            const clone = CamelUtil.cloneStep(clipboardSteps[0], true);
-            const route = CamelDefinitionApi.createRouteDefinition({from: clone});
-            addStep(route, '', 0)
-        } else if (clipboardSteps.length === 1 && clipboardSteps[0]?.dslName === 'RouteDefinition') {
-            const clone = CamelUtil.cloneStep(clipboardSteps[0], true);
-            addStep(clone, '', 0)
-        } else if (selectedUuids.length === 1) {
-            const targetMeta = CamelDefinitionApiExt.findElementMetaInIntegration(integration, selectedUuids[0]);
-            clipboardSteps.reverse().forEach(clipboardStep => {
-                if (clipboardStep && targetMeta.parentUuid) {
-                    const clone = CamelUtil.cloneStep(clipboardStep, true);
-                    addStep(clone, targetMeta.parentUuid, targetMeta.position);
-                }
-            })
-        }
-    }
     //
     function  onCommand (command: Command, printerRef: React.MutableRefObject<HTMLDivElement | null>) {
         switch (command.command){
@@ -215,7 +156,6 @@ export const useRouteDesignerHook = () => {
     }
 
     const selectElement = (element: CamelElement) =>  {
-        // console.log("selectElement", element, selectedUuids)
         const uuids = [...selectedUuids];
         let canNotAdd: boolean = false;
         if (shiftKeyPressed) {
@@ -240,6 +180,65 @@ export const useRouteDesignerHook = () => {
         setSelectedStep(selectedElement);
         setSelectedUuids(shiftKeyPressed ? [...uuids] : [element.uuid])
     }
+
+    function handleKeyDown (event: KeyboardEvent) {
+        if ((event.shiftKey)) {
+            setShiftKeyPressed(true);
+        }
+        if (window.document.hasFocus() && window.document.activeElement) {
+            if (['BODY', 'MAIN'].includes(window.document.activeElement.tagName)) {
+                let charCode = String.fromCharCode(event.which).toLowerCase();
+                if ((event.ctrlKey || event.metaKey) && charCode === 'c') {
+                    copyToClipboard();
+                } else if ((event.ctrlKey || event.metaKey) && charCode === 'v') {
+                    pasteFromClipboard();
+                }
+            }
+        } else {
+            if (event.repeat) {
+                window.dispatchEvent(event);
+            }
+        }
+    }
+
+    function handleKeyUp (event: KeyboardEvent) {
+        setShiftKeyPressed(false);
+        if (event.repeat) {
+            window.dispatchEvent(event);
+        }
+    }
+
+    function copyToClipboard  (): void {
+        const steps: CamelElement[] = []
+        selectedUuids.forEach(selectedUuid => {
+            const selectedElement = CamelDefinitionApiExt.findElementInIntegration(integration, selectedUuid);
+            if (selectedElement) {
+                steps.push(selectedElement);
+            }
+        })
+        if (steps.length > 0) {
+            setClipboardSteps(steps);
+        }
+    }
+    function pasteFromClipboard (): void {
+        if (clipboardSteps.length === 1 && clipboardSteps[0]?.dslName === 'FromDefinition') {
+            const clone = CamelUtil.cloneStep(clipboardSteps[0], true);
+            const route = CamelDefinitionApi.createRouteDefinition({from: clone});
+            addStep(route, '', 0)
+        } else if (clipboardSteps.length === 1 && clipboardSteps[0]?.dslName === 'RouteDefinition') {
+            const clone = CamelUtil.cloneStep(clipboardSteps[0], true);
+            addStep(clone, '', 0)
+        } else if (selectedUuids.length === 1) {
+            const targetMeta = CamelDefinitionApiExt.findElementMetaInIntegration(integration, selectedUuids[0]);
+            clipboardSteps.reverse().forEach(clipboardStep => {
+                if (clipboardStep && targetMeta.parentUuid) {
+                    const clone = CamelUtil.cloneStep(clipboardStep, true);
+                    addStep(clone, targetMeta.parentUuid, targetMeta.position);
+                }
+            })
+        }
+    }
+
     //
     // function unselectElement = (evt: React.MouseEvent<HTMLDivElement, MouseEvent>) => {
     //     if ((evt.target as any).dataset.click === 'FLOWS') {
@@ -293,7 +292,7 @@ export const useRouteDesignerHook = () => {
                 break;
         }
     }
-    //
+
     function setDslDefaults(step: CamelElement): CamelElement {
         if (step.dslName === 'LogDefinition') {
             // eslint-disable-next-line no-template-curly-in-string
@@ -373,5 +372,5 @@ export const useRouteDesignerHook = () => {
         }
     }
 
-    return { deleteElement, selectElement, moveElement, onShowDeleteConfirmation, onDslSelect, openSelector, createRouteConfiguration, handleKeyDown, handleKeyUp, onCommand}
+    return { deleteElement, selectElement, moveElement, onShowDeleteConfirmation, onDslSelect, openSelector, createRouteConfiguration, onCommand, handleKeyDown, handleKeyUp}
 }
\ No newline at end of file


[camel-karavan] 05/06: Properties for #836

Posted by ma...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

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

commit 425975b36e3faa5ca08ffbd3460cec8e73c25ad2
Author: Marat Gubaidullin <ma...@Marats-MacBook-Pro.local>
AuthorDate: Thu Aug 24 15:59:02 2023 -0400

    Properties for #836
---
 karavan-core/src/core/api/CamelUtil.ts             |   2 +-
 karavan-designer/public/example/demo.camel.yaml    | 857 +--------------------
 karavan-designer/src/designer/KaravanDesigner.tsx  |   9 -
 .../src/designer/rest/RestDesigner.tsx             |   9 +-
 .../src/designer/route/DslConnections.tsx          |   1 +
 karavan-designer/src/designer/route/DslElement.tsx |   7 +-
 .../src/designer/route/DslProperties.tsx           | 213 ++---
 .../src/designer/route/RouteDesigner.tsx           |  20 +-
 .../designer/route/property/DataFormatField.tsx    |  15 +-
 .../designer/route/property/DslPropertyField.tsx   | 516 +++++++------
 .../designer/route/property/ExpressionField.tsx    | 181 ++---
 .../src/designer/route/property/ObjectField.tsx    |  12 +-
 .../src/designer/route/usePropertiesHook.tsx       | 103 +++
 .../src/designer/route/useRouteDesignerHook.tsx    | 112 +--
 karavan-designer/src/designer/utils/CamelUi.tsx    |   4 +-
 .../src/designer/utils/KaravanIcons.tsx            |  22 +
 16 files changed, 586 insertions(+), 1497 deletions(-)

diff --git a/karavan-core/src/core/api/CamelUtil.ts b/karavan-core/src/core/api/CamelUtil.ts
index 791ec0fa..65579d59 100644
--- a/karavan-core/src/core/api/CamelUtil.ts
+++ b/karavan-core/src/core/api/CamelUtil.ts
@@ -209,7 +209,7 @@ export class CamelUtil {
                     result[1].push(`${property.displayName} is required`);
                 } else if (property.type === 'ExpressionDefinition') {
                     const expressionMeta = CamelMetadataApi.getCamelModelMetadataByClassName('ExpressionDefinition');
-                    const expressionCheck = expressionMeta?.properties.some(ep => {
+                    const expressionCheck = expressionMeta && value!== undefined && expressionMeta?.properties.some(ep => {
                         const expValue = value[ep.name];
                         if (expValue) {
                             const checkedExpression = CamelUtil.checkRequired(expValue);
diff --git a/karavan-designer/public/example/demo.camel.yaml b/karavan-designer/public/example/demo.camel.yaml
index 14003ed0..46692463 100644
--- a/karavan-designer/public/example/demo.camel.yaml
+++ b/karavan-designer/public/example/demo.camel.yaml
@@ -4,865 +4,16 @@
       uri: kamelet:timer-source
       id: from-e52c
       steps:
-        - choice:
-            when:
-              - expression: {}
-                id: when-064f
-                steps:
-                  - multicast:
-                      id: multicast-38cea
-                      steps:
-                        - bean:
-                            id: bean-3b8e
-                        - log:
-                            message: ${body}
-                            id: log-546f
-                        - loop:
-                            expression: {}
-                            id: loop-4635
-                            steps:
-                              - convertBodyTo:
-                                  id: convertBodyTo-1cae
-            otherwise:
-              id: otherwise-0b09
-              steps:
-                - filter:
-                    expression: {}
-                    id: filter-a02b
-            id: choice-c53c
-        - doTry:
-            id: doTry-8fd5
-            doCatch:
-              - id: doCatch-1071
-              - id: doCatch-c38e
-            steps:
-              - routingSlip:
-                  expression: {}
-                  id: routingSlip-a85a
-- route:
-    id: route-178a
-    from:
-      uri: kamelet:aws-cloudtrail-source
-      id: from-3e7d
-      steps:
-        - multicast:
-            id: multicast-eef7
-            steps:
-              - bean:
-                  id: bean-a5ef
-              - aggregate:
-                  id: aggregate-f5d8
-              - aggregate:
-                  id: aggregate-b9e7
-              - aggregate:
-                  id: aggregate-5eb8
-              - aggregate:
-                  id: aggregate-c57e
-              - aggregate:
-                  id: aggregate-1cd4
-              - bean:
-                  id: bean-72a1
-              - choice:
-                  when:
-                    - expression: {}
-                      id: when-a56b
-                  otherwise:
-                    id: otherwise-9f31
-                  id: choice-1905
-#- route:
-#    id: route-f435
-#    from:
-#      uri: kamelet:timer-source
-#      id: from-e52c
-#      steps:
-#        - choice:
-#            when:
-#              - expression: {}
-#                id: when-064f
-#                steps:
-#                  - multicast:
-#                      id: multicast-38ce
-#                      steps:
-#                        - bean:
-#                            id: bean-3b8e
-#                        - log:
-#                            message: ${body}
-#                            id: log-546f
-#                        - loop:
-#                            expression: {}
-#                            id: loop-4635
-#                            steps:
-#                              - convertBodyTo:
-#                                  id: convertBodyTo-1cae
-#            otherwise:
-#              id: otherwise-0b09
-#              steps:
-#                - filter:
-#                    expression: {}
-#                    id: filter-a02b
-#            id: choice-c53c
-#        - doTry:
-#            id: doTry-8fd5
-#            doCatch:
-#              - id: doCatch-1071
-#              - id: doCatch-c38e
-#            steps:
-#              - routingSlip:
-#                  expression: {}
-#                  id: routingSlip-a85a
-#- route:
-#    id: route-178a
-#    from:
-#      uri: kamelet:aws-cloudtrail-source
-#      id: from-3e7d
-#      steps:
-#        - multicast:
-#            id: multicast-eef7
-#            steps:
-#              - bean:
-#                  id: bean-a5ef
-#              - aggregate:
-#                  id: aggregate-f5d8
-#              - aggregate:
-#                  id: aggregate-b9e7
-#              - aggregate:
-#                  id: aggregate-5eb8
-#              - aggregate:
-#                  id: aggregate-c57e
-#              - aggregate:
-#                  id: aggregate-1cd4
-#              - bean:
-#                  id: bean-72a1
-#              - choice:
-#                  when:
-#                    - expression: {}
-#                      id: when-a56b
-#                  otherwise:
-#                    id: otherwise-9f31
-#                  id: choice-1905
-#- route:
-#    id: route-f435
-#    from:
-#      uri: kamelet:timer-source
-#      id: from-e52c
-#      steps:
-#        - choice:
-#            when:
-#              - expression: {}
-#                id: when-064f
-#                steps:
-#                  - multicast:
-#                      id: multicast-38ce
-#                      steps:
-#                        - bean:
-#                            id: bean-3b8e
-#                        - log:
-#                            message: ${body}
-#                            id: log-546f
-#                        - loop:
-#                            expression: {}
-#                            id: loop-4635
-#                            steps:
-#                              - convertBodyTo:
-#                                  id: convertBodyTo-1cae
-#            otherwise:
-#              id: otherwise-0b09
-#              steps:
-#                - filter:
-#                    expression: {}
-#                    id: filter-a02b
-#            id: choice-c53c
-#        - doTry:
-#            id: doTry-8fd5
-#            doCatch:
-#              - id: doCatch-1071
-#              - id: doCatch-c38e
-#            steps:
-#              - routingSlip:
-#                  expression: {}
-#                  id: routingSlip-a85a
-#- route:
-#    id: route-178a
-#    from:
-#      uri: kamelet:aws-cloudtrail-source
-#      id: from-3e7d
-#      steps:
-#        - multicast:
-#            id: multicast-eef7
-#            steps:
-#              - bean:
-#                  id: bean-a5ef
-#              - aggregate:
-#                  id: aggregate-f5d8
-#              - aggregate:
-#                  id: aggregate-b9e7
-#              - aggregate:
-#                  id: aggregate-5eb8
-#              - aggregate:
-#                  id: aggregate-c57e
-#              - aggregate:
-#                  id: aggregate-1cd4
-#              - bean:
-#                  id: bean-72a1
-#              - choice:
-#                  when:
-#                    - expression: {}
-#                      id: when-a56b
-#                  otherwise:
-#                    id: otherwise-9f31
-#                  id: choice-1905
-#- route:
-#    id: route-f435
-#    from:
-#      uri: kamelet:timer-source
-#      id: from-e52c
-#      steps:
-#        - choice:
-#            when:
-#              - expression: {}
-#                id: when-064f
-#                steps:
-#                  - multicast:
-#                      id: multicast-38ce
-#                      steps:
-#                        - bean:
-#                            id: bean-3b8e
-#                        - log:
-#                            message: ${body}
-#                            id: log-546f
-#                        - loop:
-#                            expression: {}
-#                            id: loop-4635
-#                            steps:
-#                              - convertBodyTo:
-#                                  id: convertBodyTo-1cae
-#            otherwise:
-#              id: otherwise-0b09
-#              steps:
-#                - filter:
-#                    expression: {}
-#                    id: filter-a02b
-#            id: choice-c53c
-#        - doTry:
-#            id: doTry-8fd5
-#            doCatch:
-#              - id: doCatch-1071
-#              - id: doCatch-c38e
-#            steps:
-#              - routingSlip:
-#                  expression: {}
-#                  id: routingSlip-a85a
-#- route:
-#    id: route-178a
-#    from:
-#      uri: kamelet:aws-cloudtrail-source
-#      id: from-3e7d
-#      steps:
-#        - multicast:
-#            id: multicast-eef7
-#            steps:
-#              - bean:
-#                  id: bean-a5ef
-#              - aggregate:
-#                  id: aggregate-f5d8
-#              - aggregate:
-#                  id: aggregate-b9e7
-#              - aggregate:
-#                  id: aggregate-5eb8
-#              - aggregate:
-#                  id: aggregate-c57e
-#              - aggregate:
-#                  id: aggregate-1cd4
-#              - bean:
-#                  id: bean-72a1
-#              - choice:
-#                  when:
-#                    - expression: {}
-#                      id: when-a56b
-#                  otherwise:
-#                    id: otherwise-9f31
-#                  id: choice-1905
-#- route:
-#    id: route-f435
-#    from:
-#      uri: kamelet:timer-source
-#      id: from-e52c
-#      steps:
-#        - choice:
-#            when:
-#              - expression: {}
-#                id: when-064f
-#                steps:
-#                  - multicast:
-#                      id: multicast-38ce
-#                      steps:
-#                        - bean:
-#                            id: bean-3b8e
-#                        - log:
-#                            message: ${body}
-#                            id: log-546f
-#                        - loop:
-#                            expression: {}
-#                            id: loop-4635
-#                            steps:
-#                              - convertBodyTo:
-#                                  id: convertBodyTo-1cae
-#            otherwise:
-#              id: otherwise-0b09
-#              steps:
-#                - filter:
-#                    expression: {}
-#                    id: filter-a02b
-#            id: choice-c53c
-#        - doTry:
-#            id: doTry-8fd5
-#            doCatch:
-#              - id: doCatch-1071
-#              - id: doCatch-c38e
-#            steps:
-#              - routingSlip:
-#                  expression: {}
-#                  id: routingSlip-a85a
-#- route:
-#    id: route-178a
-#    from:
-#      uri: kamelet:aws-cloudtrail-source
-#      id: from-3e7d
-#      steps:
-#        - multicast:
-#            id: multicast-eef7
-#            steps:
-#              - bean:
-#                  id: bean-a5ef
-#              - aggregate:
-#                  id: aggregate-f5d8
-#              - aggregate:
-#                  id: aggregate-b9e7
-#              - aggregate:
-#                  id: aggregate-5eb8
-#              - aggregate:
-#                  id: aggregate-c57e
-#              - aggregate:
-#                  id: aggregate-1cd4
-#              - bean:
-#                  id: bean-72a1
-#              - choice:
-#                  when:
-#                    - expression: {}
-#                      id: when-a56b
-#                  otherwise:
-#                    id: otherwise-9f31
-#                  id: choice-1905
-#- route:
-#    id: route-f435
-#    from:
-#      uri: kamelet:timer-source
-#      id: from-e52c
-#      steps:
-#        - choice:
-#            when:
-#              - expression: {}
-#                id: when-064f
-#                steps:
-#                  - multicast:
-#                      id: multicast-38ce
-#                      steps:
-#                        - bean:
-#                            id: bean-3b8e
-#                        - log:
-#                            message: ${body}
-#                            id: log-546f
-#                        - loop:
-#                            expression: {}
-#                            id: loop-4635
-#                            steps:
-#                              - convertBodyTo:
-#                                  id: convertBodyTo-1cae
-#            otherwise:
-#              id: otherwise-0b09
-#              steps:
-#                - filter:
-#                    expression: {}
-#                    id: filter-a02b
-#            id: choice-c53c
-#        - doTry:
-#            id: doTry-8fd5
-#            doCatch:
-#              - id: doCatch-1071
-#              - id: doCatch-c38e
-#            steps:
-#              - routingSlip:
-#                  expression: {}
-#                  id: routingSlip-a85a
-#- route:
-#    id: route-178a
-#    from:
-#      uri: kamelet:aws-cloudtrail-source
-#      id: from-3e7d
-#      steps:
-#        - multicast:
-#            id: multicast-eef7
-#            steps:
-#              - bean:
-#                  id: bean-a5ef
-#              - aggregate:
-#                  id: aggregate-f5d8
-#              - aggregate:
-#                  id: aggregate-b9e7
-#              - aggregate:
-#                  id: aggregate-5eb8
-#              - aggregate:
-#                  id: aggregate-c57e
-#              - aggregate:
-#                  id: aggregate-1cd4
-#              - bean:
-#                  id: bean-72a1
-#              - choice:
-#                  when:
-#                    - expression: {}
-#                      id: when-a56b
-#                  otherwise:
-#                    id: otherwise-9f31
-#                  id: choice-1905
-#- route:
-#    id: route-f435
-#    from:
-#      uri: kamelet:timer-source
-#      id: from-e52c
-#      steps:
-#        - choice:
-#            when:
-#              - expression: {}
-#                id: when-064f
-#                steps:
-#                  - multicast:
-#                      id: multicast-38ce
-#                      steps:
-#                        - bean:
-#                            id: bean-3b8e
-#                        - log:
-#                            message: ${body}
-#                            id: log-546f
-#                        - loop:
-#                            expression: {}
-#                            id: loop-4635
-#                            steps:
-#                              - convertBodyTo:
-#                                  id: convertBodyTo-1cae
-#            otherwise:
-#              id: otherwise-0b09
-#              steps:
-#                - filter:
-#                    expression: {}
-#                    id: filter-a02b
-#            id: choice-c53c
-#        - doTry:
-#            id: doTry-8fd5
-#            doCatch:
-#              - id: doCatch-1071
-#              - id: doCatch-c38e
-#            steps:
-#              - routingSlip:
-#                  expression: {}
-#                  id: routingSlip-a85a
-#- route:
-#    id: route-178a
-#    from:
-#      uri: kamelet:aws-cloudtrail-source
-#      id: from-3e7d
-#      steps:
-#        - multicast:
-#            id: multicast-eef7
-#            steps:
-#              - bean:
-#                  id: bean-a5ef
-#              - aggregate:
-#                  id: aggregate-f5d8
-#              - aggregate:
-#                  id: aggregate-b9e7
-#              - aggregate:
-#                  id: aggregate-5eb8
-#              - aggregate:
-#                  id: aggregate-c57e
-#              - aggregate:
-#                  id: aggregate-1cd4
-#              - bean:
-#                  id: bean-72a1
-#              - choice:
-#                  when:
-#                    - expression: {}
-#                      id: when-a56b
-#                  otherwise:
-#                    id: otherwise-9f31
-#                  id: choice-1905
-#- route:
-#    id: route-f435
-#    from:
-#      uri: kamelet:timer-source
-#      id: from-e52c
-#      steps:
-#        - choice:
-#            when:
-#              - expression: {}
-#                id: when-064f
-#                steps:
-#                  - multicast:
-#                      id: multicast-38ce
-#                      steps:
-#                        - bean:
-#                            id: bean-3b8e
-#                        - log:
-#                            message: ${body}
-#                            id: log-546f
-#                        - loop:
-#                            expression: {}
-#                            id: loop-4635
-#                            steps:
-#                              - convertBodyTo:
-#                                  id: convertBodyTo-1cae
-#            otherwise:
-#              id: otherwise-0b09
-#              steps:
-#                - filter:
-#                    expression: {}
-#                    id: filter-a02b
-#            id: choice-c53c
-#        - doTry:
-#            id: doTry-8fd5
-#            doCatch:
-#              - id: doCatch-1071
-#              - id: doCatch-c38e
-#            steps:
-#              - routingSlip:
-#                  expression: {}
-#                  id: routingSlip-a85a
-#- route:
-#    id: route-178a
-#    from:
-#      uri: kamelet:aws-cloudtrail-source
-#      id: from-3e7d
-#      steps:
-#        - multicast:
-#            id: multicast-eef7
-#            steps:
-#              - bean:
-#                  id: bean-a5ef
-#              - aggregate:
-#                  id: aggregate-f5d8
-#              - aggregate:
-#                  id: aggregate-b9e7
-#              - aggregate:
-#                  id: aggregate-5eb8
-#              - aggregate:
-#                  id: aggregate-c57e
-#              - aggregate:
-#                  id: aggregate-1cd4
-#              - bean:
-#                  id: bean-72a1
-#              - choice:
-#                  when:
-#                    - expression: {}
-#                      id: when-a56b
-#                  otherwise:
-#                    id: otherwise-9f31
-#                  id: choice-1905
-#- route:
-#    id: route-f435
-#    from:
-#      uri: kamelet:timer-source
-#      id: from-e52c
-#      steps:
-#        - choice:
-#            when:
-#              - expression: {}
-#                id: when-064f
-#                steps:
-#                  - multicast:
-#                      id: multicast-38ce
-#                      steps:
-#                        - bean:
-#                            id: bean-3b8e
-#                        - log:
-#                            message: ${body}
-#                            id: log-546f
-#                        - loop:
-#                            expression: {}
-#                            id: loop-4635
-#                            steps:
-#                              - convertBodyTo:
-#                                  id: convertBodyTo-1cae
-#            otherwise:
-#              id: otherwise-0b09
-#              steps:
-#                - filter:
-#                    expression: {}
-#                    id: filter-a02b
-#            id: choice-c53c
-#        - doTry:
-#            id: doTry-8fd5
-#            doCatch:
-#              - id: doCatch-1071
-#              - id: doCatch-c38e
-#            steps:
-#              - routingSlip:
-#                  expression: {}
-#                  id: routingSlip-a85a
-#- route:
-#    id: route-178a
-#    from:
-#      uri: kamelet:aws-cloudtrail-source
-#      id: from-3e7d
-#      steps:
-#        - multicast:
-#            id: multicast-eef7
-#            steps:
-#              - bean:
-#                  id: bean-a5ef
-#              - aggregate:
-#                  id: aggregate-f5d8
-#              - aggregate:
-#                  id: aggregate-b9e7
-#              - aggregate:
-#                  id: aggregate-5eb8
-#              - aggregate:
-#                  id: aggregate-c57e
-#              - aggregate:
-#                  id: aggregate-1cd4
-#              - bean:
-#                  id: bean-72a1
-#              - choice:
-#                  when:
-#                    - expression: {}
-#                      id: when-a56b
-#                  otherwise:
-#                    id: otherwise-9f31
-#                  id: choice-1905
-#- route:
-#    id: route-f435
-#    from:
-#      uri: kamelet:timer-source
-#      id: from-e52c
-#      steps:
-#        - choice:
-#            when:
-#              - expression: {}
-#                id: when-064f
-#                steps:
-#                  - multicast:
-#                      id: multicast-38ce
-#                      steps:
-#                        - bean:
-#                            id: bean-3b8e
-#                        - log:
-#                            message: ${body}
-#                            id: log-546f
-#                        - loop:
-#                            expression: {}
-#                            id: loop-4635
-#                            steps:
-#                              - convertBodyTo:
-#                                  id: convertBodyTo-1cae
-#            otherwise:
-#              id: otherwise-0b09
-#              steps:
-#                - filter:
-#                    expression: {}
-#                    id: filter-a02b
-#            id: choice-c53c
-#        - doTry:
-#            id: doTry-8fd5
-#            doCatch:
-#              - id: doCatch-1071
-#              - id: doCatch-c38e
-#            steps:
-#              - routingSlip:
-#                  expression: {}
-#                  id: routingSlip-a85a
-#- route:
-#    id: route-178a
-#    from:
-#      uri: kamelet:aws-cloudtrail-source
-#      id: from-3e7d
-#      steps:
-#        - multicast:
-#            id: multicast-eef7
-#            steps:
-#              - bean:
-#                  id: bean-a5ef
-#              - aggregate:
-#                  id: aggregate-f5d8
-#              - aggregate:
-#                  id: aggregate-b9e7
-#              - aggregate:
-#                  id: aggregate-5eb8
-#              - aggregate:
-#                  id: aggregate-c57e
-#              - aggregate:
-#                  id: aggregate-1cd4
-#              - bean:
-#                  id: bean-72a1
-#              - choice:
-#                  when:
-#                    - expression: {}
-#                      id: when-a56b
-#                  otherwise:
-#                    id: otherwise-9f31
-#                  id: choice-1905
-#- route:
-#    id: route-f435
-#    from:
-#      uri: kamelet:timer-source
-#      id: from-e52c
-#      steps:
-#        - choice:
-#            when:
-#              - expression: {}
-#                id: when-064f
-#                steps:
-#                  - multicast:
-#                      id: multicast-38ce
-#                      steps:
-#                        - bean:
-#                            id: bean-3b8e
-#                        - log:
-#                            message: ${body}
-#                            id: log-546f
-#                        - loop:
-#                            expression: {}
-#                            id: loop-4635
-#                            steps:
-#                              - convertBodyTo:
-#                                  id: convertBodyTo-1cae
-#            otherwise:
-#              id: otherwise-0b09
-#              steps:
-#                - filter:
-#                    expression: {}
-#                    id: filter-a02b
-#            id: choice-c53c
-#        - doTry:
-#            id: doTry-8fd5
-#            doCatch:
-#              - id: doCatch-1071
-#              - id: doCatch-c38e
-#            steps:
-#              - routingSlip:
-#                  expression: {}
-#                  id: routingSlip-a85a
-#- route:
-#    id: route-178a
-#    from:
-#      uri: kamelet:aws-cloudtrail-source
-#      id: from-3e7d
-#      steps:
-#        - multicast:
-#            id: multicast-eef7
-#            steps:
-#              - bean:
-#                  id: bean-a5ef
-#              - aggregate:
-#                  id: aggregate-f5d8
-#              - aggregate:
-#                  id: aggregate-b9e7
-#              - aggregate:
-#                  id: aggregate-5eb8
-#              - aggregate:
-#                  id: aggregate-c57e
-#              - aggregate:
-#                  id: aggregate-1cd4
-#              - bean:
-#                  id: bean-72a1
-#              - choice:
-#                  when:
-#                    - expression: {}
-#                      id: when-a56b
-#                  otherwise:
-#                    id: otherwise-9f31
-#                  id: choice-1905
-#- route:
-#    id: route-f435
-#    from:
-#      uri: kamelet:timer-source
-#      id: from-e52c
-#      steps:
-#        - choice:
-#            when:
-#              - expression: {}
-#                id: when-064f
-#                steps:
-#                  - multicast:
-#                      id: multicast-38ce
-#                      steps:
-#                        - bean:
-#                            id: bean-3b8e
-#                        - log:
-#                            message: ${body}
-#                            id: log-546f
-#                        - loop:
-#                            expression: {}
-#                            id: loop-4635
-#                            steps:
-#                              - convertBodyTo:
-#                                  id: convertBodyTo-1cae
-#            otherwise:
-#              id: otherwise-0b09
-#              steps:
-#                - filter:
-#                    expression: {}
-#                    id: filter-a02b
-#            id: choice-c53c
-#        - doTry:
-#            id: doTry-8fd5
-#            doCatch:
-#              - id: doCatch-1071
-#              - id: doCatch-c38e
-#            steps:
-#              - routingSlip:
-#                  expression: {}
-#                  id: routingSlip-a85a
-#- route:
-#    id: route-178a
-#    from:
-#      uri: kamelet:aws-cloudtrail-source
-#      id: from-3e7d
-#      steps:
-#        - multicast:
-#            id: multicast-eef7
-#            steps:
-#              - bean:
-#                  id: bean-a5ef
-#              - aggregate:
-#                  id: aggregate-f5d8
-#              - aggregate:
-#                  id: aggregate-b9e7
-#              - aggregate:
-#                  id: aggregate-5eb8
-#              - aggregate:
-#                  id: aggregate-c57e
-#              - aggregate:
-#                  id: aggregate-1cd4
-#              - bean:
-#                  id: bean-72a1
-#              - choice:
-#                  when:
-#                    - expression: {}
-#                      id: when-a56b
-#                  otherwise:
-#                    id: otherwise-9f31
-#                  id: choice-1905
-#- route:
-#    id: route-f435
-#    from:
-#      uri: kamelet:timer-source
-#      id: from-e52c
-#      steps:
+        - filter:
+              expression: {}
+              id: filter-064f
 #        - choice:
 #            when:
 #              - expression: {}
 #                id: when-064f
 #                steps:
 #                  - multicast:
-#                      id: multicast-38ce
+#                      id: multicast-38cea
 #                      steps:
 #                        - bean:
 #                            id: bean-3b8e
diff --git a/karavan-designer/src/designer/KaravanDesigner.tsx b/karavan-designer/src/designer/KaravanDesigner.tsx
index 3c68baee..bb8a8538 100644
--- a/karavan-designer/src/designer/KaravanDesigner.tsx
+++ b/karavan-designer/src/designer/KaravanDesigner.tsx
@@ -42,15 +42,6 @@ interface Props {
 }
 
 export class KaravanInstance {
-    // static designer: KaravanDesigner;
-
-    // static set(designer: KaravanDesigner): void  {
-    //     KaravanInstance.designer = designer;
-    // }
-    //
-    // static get(): KaravanDesigner {
-    //     return KaravanInstance.designer;
-    // }
 
     static getProps(): Props {
         return {
diff --git a/karavan-designer/src/designer/rest/RestDesigner.tsx b/karavan-designer/src/designer/rest/RestDesigner.tsx
index d13d07e6..0921488b 100644
--- a/karavan-designer/src/designer/rest/RestDesigner.tsx
+++ b/karavan-designer/src/designer/rest/RestDesigner.tsx
@@ -235,14 +235,7 @@ export class RestDesigner extends React.Component<Props, State> {
     getPropertiesPanel() {
         return (
             <DrawerPanelContent isResizable hasNoBorder defaultSize={'400px'} maxSize={'800px'} minSize={'300px'}>
-                <DslProperties
-                    integration={this.props.integration}
-                    step={this.state.selectedStep}
-                    onIntegrationUpdate={this.onIntegrationUpdate}
-                    onPropertyUpdate={this.onPropertyUpdate}
-                    isRouteDesigner={false}
-                    onClone={this.cloneRest}
-                    dark={this.props.dark}/>
+                <DslProperties isRouteDesigner={false}/>
             </DrawerPanelContent>
         )
     }
diff --git a/karavan-designer/src/designer/route/DslConnections.tsx b/karavan-designer/src/designer/route/DslConnections.tsx
index a2505f4f..1002103e 100644
--- a/karavan-designer/src/designer/route/DslConnections.tsx
+++ b/karavan-designer/src/designer/route/DslConnections.tsx
@@ -40,6 +40,7 @@ export const DslConnections = () => {
     }, []);
 
     function setPosition(evt: DslPosition) {
+        console.log("setPosition : ", evt);
         if (evt.command === "add") {
             addStep(evt.step.uuid, evt);
         }
diff --git a/karavan-designer/src/designer/route/DslElement.tsx b/karavan-designer/src/designer/route/DslElement.tsx
index 5cdfabf5..dc73dc68 100644
--- a/karavan-designer/src/designer/route/DslElement.tsx
+++ b/karavan-designer/src/designer/route/DslElement.tsx
@@ -194,10 +194,11 @@ export const DslElement = (props: Props) => {
     }
 
     function sendPosition(el: HTMLDivElement | null, isSelected: boolean) {
-        const node = el;
-        if (node && el) {
-            const header = Array.from(node.childNodes.values()).filter((n: any) => n.classList.contains("header"))[0];
+        // console.log("sendPosition1 : ", selectedStep)
+        if (el) {
+            const header = Array.from(el.childNodes.values()).filter((n: any) => n.classList.contains("header"))[0];
             if (header) {
+                // console.log("sendPosition2 : ", selectedStep, el)
                 const headerIcon: any = Array.from(header.childNodes.values()).filter((n: any) => n.classList.contains("header-icon"))[0];
                 const headerRect = headerIcon.getBoundingClientRect();
                 const rect = el.getBoundingClientRect();
diff --git a/karavan-designer/src/designer/route/DslProperties.tsx b/karavan-designer/src/designer/route/DslProperties.tsx
index 87f93c79..be8c405a 100644
--- a/karavan-designer/src/designer/route/DslProperties.tsx
+++ b/karavan-designer/src/designer/route/DslProperties.tsx
@@ -14,7 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import React from 'react';
+import React, {useState} from 'react';
 import {
     Form,
     Text,
@@ -25,101 +25,33 @@ import '../karavan.css';
 import "@patternfly/patternfly/patternfly.css";
 import {DataFormatField} from "./property/DataFormatField";
 import {DslPropertyField} from "./property/DslPropertyField";
-import {
-    ExpressionDefinition,
-    DataFormatDefinition
-} from "karavan-core/lib/model/CamelDefinition";
-import {Integration, CamelElement} from "karavan-core/lib/model/IntegrationDefinition";
 import {CamelDefinitionApiExt} from "karavan-core/lib/api/CamelDefinitionApiExt";
-import {CamelUtil} from "karavan-core/lib/api/CamelUtil";
-import {CamelUi, RouteToCreate} from "../utils/CamelUi";
+import {CamelUi} from "../utils/CamelUi";
 import {CamelMetadataApi, DataFormats, PropertyMeta} from "karavan-core/lib/model/CamelMetadata";
 import {IntegrationHeader} from "../utils/KaravanComponents";
 import CloneIcon from "@patternfly/react-icons/dist/esm/icons/clone-icon";
+import {useDesignerStore, useIntegrationStore} from "../KaravanStore";
+import {shallow} from "zustand/shallow";
+import {usePropertiesHook} from "./usePropertiesHook";
 
 interface Props {
-    integration: Integration,
-    step?: CamelElement,
-    onIntegrationUpdate?: any,
-    onPropertyUpdate?: (element: CamelElement, newRoute?: RouteToCreate) => void
-    onClone?: (element: CamelElement) => void
     isRouteDesigner: boolean
-    dark: boolean
-}
-
-interface State {
-    step?: CamelElement,
-    selectStatus: Map<string, boolean>
-    isShowAdvanced: boolean
-    isDescriptionExpanded?: boolean
 }
 
-export class DslProperties extends React.Component<Props, State> {
-
-    public state: State = {
-        step: this.props.step,
-        selectStatus: new Map<string, boolean>(),
-        isShowAdvanced: false
-    };
-
-    propertyChanged = (fieldId: string, value: string | number | boolean | any, newRoute?: RouteToCreate) => {
-        if (this.state.step) {
-            const clone = CamelUtil.cloneStep(this.state.step);
-            (clone as any)[fieldId] = value;
-            this.setStep(clone)
-            this.props.onPropertyUpdate?.call(this, clone, newRoute);
-        }
-    }
-
-    dataFormatChanged = (value: DataFormatDefinition) => {
-        value.uuid = this.state.step?.uuid ? this.state.step?.uuid : value.uuid;
-        this.setStep(value);
-        this.props.onPropertyUpdate?.call(this, value);
-    }
-
-    expressionChanged = (propertyName: string, exp: ExpressionDefinition) => {
-        if (this.state.step) {
-            const clone = (CamelUtil.cloneStep(this.state.step));
-            (clone as any)[propertyName] = exp;
-            this.setStep(clone);
-            this.props.onPropertyUpdate?.call(this, clone);
-        }
-    }
+export const DslProperties = (props: Props) => {
 
-    parametersChanged = (parameter: string, value: string | number | boolean | any, pathParameter?: boolean, newRoute?: RouteToCreate) => {
-        if (this.state.step && this.state.step) {
-            const clone = (CamelUtil.cloneStep(this.state.step));
-            const parameters: any = {...(clone as any).parameters};
-            parameters[parameter] = value;
-            (clone as any).parameters = parameters;
-            this.setStep(clone);
-            this.props.onPropertyUpdate?.call(this, clone, newRoute);
-        }
-    }
+    const [integration] = useIntegrationStore((state) => [state.integration], shallow)
 
-    cloneElement = () => {
-        if (this.state.step) {
-            this.props.onClone?.call(this, this.state.step);
-        }
-    }
+    const {cloneElement, onDataFormatChange} = usePropertiesHook();
 
-    componentDidUpdate = (prevProps: Readonly<Props>, prevState: Readonly<State>, snapshot?: any) => {
-        if (prevProps.step !== this.props.step) {
-            this.setStep(this.props.step);
-        }
-    }
+    const [selectedStep, dark] = useDesignerStore((s) => [s.selectedStep, s.dark], shallow)
 
-    setStep = (step?: CamelElement) => {
-        this.setState({
-            step: step,
-            selectStatus: new Map<string, boolean>()
-        });
-    }
+    const [showAdvanced, setShowAdvanced] = useState<boolean>(false);
+    const [isDescriptionExpanded, setIsDescriptionExpanded] = useState<boolean>(false);
 
-    getRouteHeader= (): JSX.Element => {
-        const isDescriptionExpanded = this.state.isDescriptionExpanded;
-        const title = this.state.step && CamelUi.getTitle(this.state.step)
-        const description =  this.state.step &&  CamelUi.getDescription(this.state.step);
+    function getRouteHeader(): JSX.Element {
+        const title = selectedStep && CamelUi.getTitle(selectedStep)
+        const description = selectedStep && CamelUi.getDescription(selectedStep);
         const descriptionLines: string [] = description ? description?.split("\n") : [""];
         return (
             <div className="headers">
@@ -127,40 +59,42 @@ export class DslProperties extends React.Component<Props, State> {
                     <Title headingLevel="h1" size="md">{title}</Title>
                 </div>
                 <Text component={TextVariants.p}>{descriptionLines.at(0)}</Text>
-                {descriptionLines.length > 1 && <ExpandableSection toggleText={isDescriptionExpanded ? 'Show less' : 'Show more'}
-                                                                   onToggle={(_event, isExpanded) => this.setState({isDescriptionExpanded: !isDescriptionExpanded})}
-                                                                   isExpanded={isDescriptionExpanded}>
-                    {descriptionLines.filter((value, index) => index > 0)
-                        .map((desc, index, array) => <Text key={index} component={TextVariants.p}>{desc}</Text>)}
-                </ExpandableSection>}
+                {descriptionLines.length > 1 &&
+                    <ExpandableSection toggleText={isDescriptionExpanded ? 'Show less' : 'Show more'}
+                                       onToggle={(_event, isExpanded) => setIsDescriptionExpanded(!isDescriptionExpanded)}
+                                       isExpanded={isDescriptionExpanded}>
+                        {descriptionLines.filter((value, index) => index > 0)
+                            .map((desc, index, array) => <Text key={index} component={TextVariants.p}>{desc}</Text>)}
+                    </ExpandableSection>}
             </div>
         )
     }
 
-    getClonableElementHeader = (): JSX.Element => {
-        const title = this.state.step && CamelUi.getTitle(this.state.step);
-        const description = this.state.step?.dslName ? CamelMetadataApi.getCamelModelMetadataByClassName(this.state.step?.dslName)?.description : title;
+    function getClonableElementHeader(): JSX.Element {
+        const title = selectedStep && CamelUi.getTitle(selectedStep);
+        const description = selectedStep?.dslName ? CamelMetadataApi.getCamelModelMetadataByClassName(selectedStep?.dslName)?.description : title;
         const descriptionLines: string [] = description ? description?.split("\n") : [""];
         return (
             <div className="headers">
                 <div className="top">
                     <Title headingLevel="h1" size="md">{title}</Title>
                     <Tooltip content="Clone element" position="bottom">
-                        <Button variant="link" onClick={() => this.cloneElement()} icon={<CloneIcon/>}/>
+                        <Button variant="link" onClick={() => cloneElement()} icon={<CloneIcon/>}/>
                     </Tooltip>
                 </div>
-                {descriptionLines.map((desc, index, array) => <Text key={index} component={TextVariants.p}>{desc}</Text>)}
+                {descriptionLines.map((desc, index, array) => <Text key={index}
+                                                                    component={TextVariants.p}>{desc}</Text>)}
             </div>
         )
     }
 
-    getComponentHeader = (): JSX.Element => {
-        if (this.props.isRouteDesigner) return this.getRouteHeader()
-        else return this.getClonableElementHeader();
+    function getComponentHeader(): JSX.Element {
+        if (props.isRouteDesigner) return getRouteHeader()
+        else return getClonableElementHeader();
     }
 
-    getProperties = (): PropertyMeta[] => {
-        const dslName = this.state.step?.dslName;
+    function getProperties(): PropertyMeta[] {
+        const dslName = selectedStep?.dslName;
         return CamelDefinitionApiExt.getElementProperties(dslName)
             // .filter((p: PropertyMeta) => (showAdvanced && p.label.includes('advanced')) || (!showAdvanced && !p.label.includes('advanced')))
             .filter((p: PropertyMeta) => !p.isObject || (p.isObject && !CamelUi.dslHasSteps(p.type)) || (dslName === 'CatchDefinition' && p.name === 'onWhen'))
@@ -168,58 +102,49 @@ export class DslProperties extends React.Component<Props, State> {
         // .filter((p: PropertyMeta) => dslName && !(['RestDefinition', 'GetDefinition', 'PostDefinition', 'PutDefinition', 'PatchDefinition', 'DeleteDefinition', 'HeadDefinition'].includes(dslName) && ['param', 'responseMessage'].includes(p.name))) // TODO: configure this properties
     }
 
-    getPropertyFields = (properties: PropertyMeta[]) => {
+    function getPropertyFields(properties: PropertyMeta[]) {
         return (<>
             {properties.map((property: PropertyMeta) =>
                 <DslPropertyField key={property.name}
-                                  integration={this.props.integration}
                                   property={property}
-                                  element={this.state.step}
-                                  value={this.state.step ? (this.state.step as any)[property.name] : undefined}
-                                  onExpressionChange={this.expressionChanged}
-                                  onParameterChange={this.parametersChanged}
-                                  onDataFormatChange={this.dataFormatChanged}
-                                  onChange={this.propertyChanged}
-                                  dark={this.props.dark}/>
+                                  value={selectedStep ? (selectedStep as any)[property.name] : undefined}/>
             )}
         </>)
     }
 
-    render() {
-        const dataFormats = DataFormats.map(value => value[0]);
-        const dataFormatElement = this.state.step !== undefined && ['MarshalDefinition', 'UnmarshalDefinition'].includes(this.state.step.dslName);
-        const properties = !dataFormatElement
-                    ? this.getProperties()
-                    : this.getProperties().filter(p => !dataFormats.includes(p.name));
-        const propertiesMain = properties.filter(p => !p.label.includes("advanced"));
-        const propertiesAdvanced = properties.filter(p => p.label.includes("advanced"));
-        return (
-            <div key={this.state.step ? this.state.step.uuid : 'integration'}
-                 className='properties'>
-                <Form autoComplete="off" onSubmit={event => event.preventDefault()}>
-                    {this.state.step === undefined && <IntegrationHeader integration={this.props.integration}/>}
-                    {this.state.step && this.getComponentHeader()}
-                    {this.getPropertyFields(propertiesMain)}
-                    {this.state.step && !['MarshalDefinition', 'UnmarshalDefinition'].includes(this.state.step.dslName)
-                        && propertiesAdvanced.length > 0 &&
-                        <ExpandableSection
-                            toggleText={'Advanced properties'}
-                            onToggle={(_event, isExpanded) => this.setState({isShowAdvanced: !this.state.isShowAdvanced})}
-                            isExpanded={this.state.isShowAdvanced}>
-                            <div className="parameters">
-                                {this.getPropertyFields(propertiesAdvanced)}
-                            </div>
-                        </ExpandableSection>}
-                    {this.state.step && ['MarshalDefinition', 'UnmarshalDefinition'].includes(this.state.step.dslName) &&
-                        <DataFormatField
-                            integration={this.props.integration}
-                            dslName={this.state.step.dslName}
-                            value={this.state.step}
-                            onDataFormatChange={this.dataFormatChanged}
-                            dark={this.props.dark}/>
-                    }
-                </Form>
-            </div>
-        )
-    }
+    const dataFormats = DataFormats.map(value => value[0]);
+    const dataFormatElement = selectedStep !== undefined && ['MarshalDefinition', 'UnmarshalDefinition'].includes(selectedStep.dslName);
+    const properties = !dataFormatElement
+        ? getProperties()
+        : getProperties().filter(p => !dataFormats.includes(p.name));
+    const propertiesMain = properties.filter(p => !p.label.includes("advanced"));
+    const propertiesAdvanced = properties.filter(p => p.label.includes("advanced"));
+    return (
+        <div key={selectedStep ? selectedStep.uuid : 'integration'}
+             className='properties'>
+            <Form autoComplete="off" onSubmit={event => event.preventDefault()}>
+                {selectedStep === undefined && <IntegrationHeader integration={integration}/>}
+                {selectedStep && getComponentHeader()}
+                {getPropertyFields(propertiesMain)}
+                {selectedStep && !['MarshalDefinition', 'UnmarshalDefinition'].includes(selectedStep.dslName)
+                    && propertiesAdvanced.length > 0 &&
+                    <ExpandableSection
+                        toggleText={'Advanced properties'}
+                        onToggle={(_event, isExpanded) => setShowAdvanced(!showAdvanced)}
+                        isExpanded={showAdvanced}>
+                        <div className="parameters">
+                            {getPropertyFields(propertiesAdvanced)}
+                        </div>
+                    </ExpandableSection>}
+                {selectedStep && ['MarshalDefinition', 'UnmarshalDefinition'].includes(selectedStep.dslName) &&
+                    <DataFormatField
+                        integration={integration}
+                        dslName={selectedStep.dslName}
+                        value={selectedStep}
+                        onDataFormatChange={onDataFormatChange}
+                        dark={dark}/>
+                }
+            </Form>
+        </div>
+    )
 }
diff --git a/karavan-designer/src/designer/route/RouteDesigner.tsx b/karavan-designer/src/designer/route/RouteDesigner.tsx
index a69105cb..8614418c 100644
--- a/karavan-designer/src/designer/route/RouteDesigner.tsx
+++ b/karavan-designer/src/designer/route/RouteDesigner.tsx
@@ -46,7 +46,7 @@ import {CamelDefinitionApi} from "karavan-core/lib/api/CamelDefinitionApi";
 
 export const RouteDesigner = () => {
 
-    const {openSelector, createRouteConfiguration, onCommand, handleKeyDown, handleKeyUp} = useRouteDesignerHook();
+    const {openSelector, createRouteConfiguration, onCommand, handleKeyDown, handleKeyUp, unselectElement} = useRouteDesignerHook();
 
     const [integration] = useIntegrationStore((state) => [state.integration], shallow)
     const [showDeleteConfirmation , setPosition,] = useDesignerStore((s) => [s.showDeleteConfirmation, s.setPosition], shallow)
@@ -98,13 +98,7 @@ export const RouteDesigner = () => {
         return (
             <DrawerPanelContent style={{transform: "initial"}} isResizable hasNoBorder defaultSize={'400px'}
                                 maxSize={'800px'} minSize={'300px'}>
-                {/*<DslProperties ref={ref}*/}
-                {/*               integration={integration}*/}
-                {/*               step={selectedStep}*/}
-                {/*               onIntegrationUpdate={logic.onIntegrationUpdate}*/}
-                {/*               onPropertyUpdate={logic.onPropertyUpdate}*/}
-                {/*               isRouteDesigner={true}*/}
-                {/*               dark={props.dark}/>*/}
+                <DslProperties isRouteDesigner={true}/>
             </DrawerPanelContent>
         )
     }
@@ -115,11 +109,11 @@ export const RouteDesigner = () => {
         return (
             <div className="graph" ref={printerRef}>
                 <DslConnections/>
-                <div id="flows" className="flows" data-click="FLOWS" onClick={event => {
-                    // logic.unselectElement(event)
-                }}
-                     ref={flowRef}
-                >
+                <div id="flows"
+                     className="flows"
+                     data-click="FLOWS"
+                     onClick={event => {unselectElement(event)}}
+                     ref={flowRef}>
                     {routeConfigurations?.map((routeConfiguration, index: number) => (
                         <DslElement key={routeConfiguration.uuid}
                                     inSteps={false}
diff --git a/karavan-designer/src/designer/route/property/DataFormatField.tsx b/karavan-designer/src/designer/route/property/DataFormatField.tsx
index ef447b6f..474fc6cf 100644
--- a/karavan-designer/src/designer/route/property/DataFormatField.tsx
+++ b/karavan-designer/src/designer/route/property/DataFormatField.tsx
@@ -106,20 +106,7 @@ export class DataFormatField extends React.Component<Props, State> {
     getPropertyFields = (value: any, properties: PropertyMeta[]) => {
         return (<>
             {value && properties?.map((property: PropertyMeta) =>
-                <DslPropertyField key={property.name} property={property}
-                                  integration={this.props.integration}
-                                  element={value}
-                                  value={value ? (value as any)[property.name] : undefined}
-                                  onExpressionChange={exp => {
-                                  }}
-                                  onParameterChange={parameter => {
-                                      console.log(parameter)
-                                  }}
-                                  onDataFormatChange={dataFormat => {
-                                      console.log(dataFormat)
-                                  }}
-                                  dark={this.props.dark}
-                                  onChange={this.propertyChanged}/>
+                <DslPropertyField key={property.name} property={property} value={value ? (value as any)[property.name] : undefined}/>
             )}
         </>)
     }
diff --git a/karavan-designer/src/designer/route/property/DslPropertyField.tsx b/karavan-designer/src/designer/route/property/DslPropertyField.tsx
index 0dca7151..7bfe7f9b 100644
--- a/karavan-designer/src/designer/route/property/DslPropertyField.tsx
+++ b/karavan-designer/src/designer/route/property/DslPropertyField.tsx
@@ -14,7 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import React from 'react';
+import React, {useRef, useState} from 'react';
 import {
 	FormGroup,
 	TextInput,
@@ -50,10 +50,8 @@ import {CamelDefinitionApiExt} from "karavan-core/lib/api/CamelDefinitionApiExt"
 import {ExpressionField} from "./ExpressionField";
 import {CamelUi, RouteToCreate} from "../../utils/CamelUi";
 import {ComponentParameterField} from "./ComponentParameterField";
-import {DataFormatDefinition} from "karavan-core/lib/model/CamelDefinition";
-import {Integration, CamelElement} from "karavan-core/lib/model/IntegrationDefinition";
+import {CamelElement} from "karavan-core/lib/model/IntegrationDefinition";
 import {KameletPropertyField} from "./KameletPropertyField";
-import {ExpressionDefinition} from "karavan-core/lib/model/CamelDefinition";
 import PlusIcon from "@patternfly/react-icons/dist/esm/icons/plus-icon";
 import {ObjectField} from "./ObjectField";
 import {CamelDefinitionApi} from "karavan-core/lib/api/CamelDefinitionApi";
@@ -66,95 +64,94 @@ import KubernetesIcon from "@patternfly/react-icons/dist/js/icons/openshift-icon
 import {InfrastructureSelector} from "./InfrastructureSelector";
 import {InfrastructureAPI} from "../../utils/InfrastructureAPI";
 import EditorIcon from "@patternfly/react-icons/dist/js/icons/code-icon";
-import {TemplateApi} from "karavan-core/lib/api/TemplateApi";
 import {ModalEditor} from "./ModalEditor";
-import {KaravanInstance} from "../../KaravanDesigner";
 import DockerIcon from "@patternfly/react-icons/dist/js/icons/docker-icon";
+import {useDesignerStore, useIntegrationStore} from "../../KaravanStore";
+import {shallow} from "zustand/shallow";
+import {usePropertiesHook} from "../usePropertiesHook";
 
 interface Props {
     property: PropertyMeta,
+    element?: CamelElement,
     value: any,
-    onChange?: (fieldId: string, value: string | number | boolean | any, newRoute?: RouteToCreate) => void,
-    onExpressionChange?: (propertyName: string, exp: ExpressionDefinition) => void,
-    onDataFormatChange?: (value: DataFormatDefinition) => void,
-    onParameterChange?: (parameter: string, value: string | number | boolean | any, pathParameter?: boolean, newRoute?: RouteToCreate) => void,
-    element?: CamelElement
-    integration: Integration,
+    expressionTextChanged?: (fieldId: string, value: string | number | boolean | any) => void,
     hideLabel?: boolean,
     dslLanguage?: [string, string, string],
-    dark: boolean
 }
 
-interface State {
-    selectStatus: Map<string, boolean>,
-    isShowAdvanced: Map<string, boolean>,
-    arrayValues: Map<string, string>,
-    showEditor: boolean
-    infrastructureSelector: boolean
-    infrastructureSelectorProperty?: string
-    customCode?: string
-    ref: any
-}
+export const DslPropertyField = (props: Props) => {
+
+    const [integration] = useIntegrationStore((state) => [state.integration], shallow)
+    const [dark] = useDesignerStore((s) => [s.dark], shallow)
+    const {onPropertyChange, onParametersChange} = usePropertiesHook();
 
-export class DslPropertyField extends React.Component<Props, State> {
+    const [isShowAdvanced, setIsShowAdvanced] = useState<Map<string, boolean>>(new Map<string, boolean>());
+    const [arrayValues, setArrayValues] = useState<Map<string, string>>(new Map<string, string>());
+    const [selectStatus, setSelectStatus] = useState<Map<string, boolean>>(new Map<string, boolean>());
+    const [showEditor, setShowEditor] = useState<boolean>(false);
+    const [infrastructureSelector, setInfrastructureSelector] = useState<boolean>(false);
+    const [infrastructureSelectorProperty, setInfrastructureSelectorProperty] = useState<string | undefined>(undefined);
+    const [customCode, setCustomCode] = useState<string | undefined>(undefined);
 
-    public state: State = {
-        selectStatus: new Map<string, boolean>(),
-        arrayValues: new Map<string, string>(),
-        isShowAdvanced: new Map<string, boolean>(),
-        showEditor: false,
-        infrastructureSelector: false,
-        ref: React.createRef(),
-    };
+    const ref = useRef<any>(null);
 
-    openSelect = (propertyName: string, isExpanded: boolean) => {
-        this.setState({selectStatus: new Map<string, boolean>([[propertyName, isExpanded]])});
+    function openSelect (propertyName: string, isExpanded: boolean ) {
+        setSelectStatus(prevState => {
+            prevState.set(propertyName, isExpanded);
+            return prevState;
+        })
+        // setState({selectStatus: new Map<string, boolean>([[propertyName, isExpanded]])});
     }
 
-    clearSelection = (propertyName: string) => {
-        this.setState({selectStatus: new Map<string, boolean>([[propertyName, false]])});
-    };
+    function clearSelection (propertyName: string ) {
+        setSelectStatus(prevState => {
+            prevState.set(propertyName, false);
+            return prevState;
+        })
+        // setState({selectStatus: new Map<string, boolean>([[propertyName, false]])});
+    }
 
-    isSelectOpen = (propertyName: string): boolean => {
-        return this.state.selectStatus.has(propertyName) && this.state.selectStatus.get(propertyName) === true;
+    function isSelectOpen (propertyName: string): boolean {
+        return selectStatus.has(propertyName) && selectStatus.get(propertyName) === true;
     }
 
-    propertyChanged = (fieldId: string, value: string | number | boolean | any, newRoute?: RouteToCreate) => {
-        if (fieldId === 'id' && CamelDefinitionApiExt.hasElementWithId(this.props.integration, value)) {
-            value = this.props.value;
+    function propertyChanged (fieldId: string, value: string | number | boolean | any, newRoute?: RouteToCreate ) {
+        if (fieldId === 'id' && CamelDefinitionApiExt.hasElementWithId(integration, value)) {
+            value = props.element;
         }
-        this.props.onChange?.call(this, fieldId, value, newRoute);
-        this.setState({selectStatus: new Map<string, boolean>([[fieldId, false]])});
+        onPropertyChange(fieldId, value, newRoute);
+        clearSelection(fieldId);
     }
 
-    arrayChanged = (fieldId: string, value: string) => {
-        const tv = this.state.arrayValues;
-        tv.set(fieldId, value);
-        this.setState({arrayValues: tv});
+    function arrayChanged (fieldId: string, value: string ) {
+        setArrayValues(prevState => {
+            prevState.set(fieldId, value);
+            return prevState;
+        })
     }
 
-    arrayDeleteValue = (fieldId: string, element: string) => {
-        const property: PropertyMeta = this.props.property;
-        let value = this.props.value;
+    function arrayDeleteValue (fieldId: string, element: string ) {
+        const property: PropertyMeta = props.property;
+        let value = props.value;
         if (property.isArray && property.type === 'string') {
-            this.propertyChanged(fieldId, (value as any).filter((x: string) => x !== element))
+            propertyChanged(fieldId, (value as any).filter((x: string) => x !== element))
         }
     }
 
-    arraySave = (fieldId: string) => {
-        const newValue = this.state.arrayValues.get(fieldId);
-        const property: PropertyMeta = this.props.property;
-        let value = this.props.value;
+    function arraySave (fieldId: string ) {
+        const newValue = arrayValues.get(fieldId);
+        const property: PropertyMeta = props.property;
+        let value = props.value;
         if (newValue !== undefined && newValue.length > 0 && property.isArray && property.type === 'string') {
             if (value) (value as any).push(newValue)
             else value = [newValue];
         }
-        this.propertyChanged(fieldId, value);
-        this.arrayChanged(fieldId, "");
+        propertyChanged(fieldId, value);
+        arrayChanged(fieldId, "");
     }
 
-    getLabel = (property: PropertyMeta, value: any) => {
-        if (!this.isMultiValueField(property) && property.isObject && !property.isArray && !["ExpressionDefinition"].includes(property.type)) {
+    function getLabel (property: PropertyMeta, value: any ) {
+        if (!isMultiValueField(property) && property.isObject && !property.isArray && !["ExpressionDefinition"].includes(property.type)) {
             const tooltip = value ? "Delete " + property.name : "Add " + property.name;
             const className = value ? "change-button delete-button" : "change-button add-button";
             const x = value ? undefined : CamelDefinitionApi.createStep(property.type, {});
@@ -163,7 +160,7 @@ export class DslPropertyField extends React.Component<Props, State> {
                 <div style={{display: "flex"}}>
                     <Text>{property.displayName} </Text>
                     <Tooltip position={"top"} content={<div>{tooltip}</div>}>
-                        <button className={className} onClick={e => this.props.onChange?.call(this, property.name, x)} aria-label="Add element">
+                        <button className={className} onClick={e => onPropertyChange(property.name, x)} aria-label="Add element">
                             {icon}
                         </button>
                     </Tooltip>
@@ -174,80 +171,83 @@ export class DslPropertyField extends React.Component<Props, State> {
         }
     }
 
-    isUriReadOnly = (property: PropertyMeta): boolean => {
-        const dslName: string = this.props.element?.dslName || '';
+    function isUriReadOnly (property: PropertyMeta): boolean {
+        const dslName: string = props.element?.dslName || '';
         return property.name === 'uri' && !['ToDynamicDefinition', 'WireTapDefinition'].includes(dslName)
     }
 
-    selectInfrastructure = (value: string) => {
+    function selectInfrastructure (value: string ) {
         // check if there is a selection
-        const textVal = this.state.ref.current;
-        const cursorStart = textVal.selectionStart;
-        const cursorEnd = textVal.selectionEnd;
-        if (cursorStart !== cursorEnd) {
-            const prevValue = this.props.value;
-            const selectedText = prevValue.substring(cursorStart, cursorEnd)
-            value = prevValue.replace(selectedText, value);
-        }
-        const propertyName = this.state.infrastructureSelectorProperty;
-        if (propertyName) {
-            if (value.startsWith("config") || value.startsWith("secret")) value = "{{" + value + "}}";
-            this.propertyChanged(propertyName, value);
-            this.setState({infrastructureSelector: false, infrastructureSelectorProperty: undefined})
+        const textVal = ref.current;
+        if(textVal != null) {
+            const cursorStart = textVal.selectionStart;
+            const cursorEnd = textVal.selectionEnd;
+            if (cursorStart !== cursorEnd) {
+                const prevValue = props.value;
+                const selectedText = prevValue.substring(cursorStart, cursorEnd)
+                value = prevValue.replace(selectedText, value);
+            }
+            const propertyName = infrastructureSelectorProperty;
+            if (propertyName) {
+                if (value.startsWith("config") || value.startsWith("secret")) value = "{{" + value + "}}";
+                propertyChanged(propertyName, value);
+                setInfrastructureSelector(false);
+                setInfrastructureSelectorProperty(undefined);
+            }
         }
     }
 
-    openInfrastructureSelector = (propertyName: string) => {
-        this.setState({infrastructureSelectorProperty: propertyName, infrastructureSelector: true});
+    function openInfrastructureSelector (propertyName: string ) {
+        setInfrastructureSelector(true);
+        setInfrastructureSelectorProperty(propertyName);
     }
 
-    closeInfrastructureSelector = () => {
-        this.setState({infrastructureSelector: false})
+    function closeInfrastructureSelector ( ) {
+        setInfrastructureSelector(false);
     }
 
-    getInfrastructureSelectorModal() {
+    function getInfrastructureSelectorModal() {
         return (
             <InfrastructureSelector
                 dark={false}
-                isOpen={this.state.infrastructureSelector}
-                onClose={() => this.closeInfrastructureSelector()}
-                onSelect={this.selectInfrastructure}/>)
+                isOpen={infrastructureSelector}
+                onClose={() => closeInfrastructureSelector()}
+                onSelect={selectInfrastructure}/>)
     }
 
-    getStringInput = (property: PropertyMeta, value: any) => {
-        const showEditor = this.state.showEditor;
+    function getStringInput (property: PropertyMeta, value: any ) {
         const inInfrastructure = InfrastructureAPI.infrastructure !== 'local';
         const noInfraSelectorButton = ["uri", "id", "description", "group"].includes(property.name);
         const icon = InfrastructureAPI.infrastructure === 'kubernetes' ? <KubernetesIcon/> : <DockerIcon/>
         return (<InputGroup>
             {inInfrastructure && !showEditor && !noInfraSelectorButton &&
                 <Tooltip position="bottom-end" content={"Select from " + capitalize(InfrastructureAPI.infrastructure)}>
-                    <Button variant="control" onClick={e => this.openInfrastructureSelector(property.name)}>
+                    <Button variant="control" onClick={e => openInfrastructureSelector(property.name)}>
                         {icon}
                     </Button>
                 </Tooltip>}
             {(!showEditor || property.secret) && <TextInput
-                ref={this.state.ref}
+                ref={ref}
                 className="text-field" isRequired 
                 type={['integer', 'number'].includes(property.type) ? 'number' : (property.secret ? "password" : "text")}
                 id={property.name} name={property.name}
                 value={value?.toString()}
-                onChange={(_, v) => this.propertyChanged(property.name, ['integer', 'number'].includes(property.type) ? Number(v) : v)}
-                readOnlyVariant={this.isUriReadOnly(property) ? "default" : undefined}/>
+                onChange={(_, v) => propertyChanged(property.name, ['integer', 'number'].includes(property.type) ? Number(v) : v)}
+                readOnlyVariant={isUriReadOnly(property) ? "default" : undefined}/>
             }
             {showEditor && !property.secret && <TextArea
-                ref={this.state.ref}
+                ref={ref}
                 autoResize={true}
                 className="text-field" isRequired 
                 type="text"
                 id={property.name} name={property.name}
                 value={value?.toString()}
-                onChange={(_, v) => this.propertyChanged(property.name, ['integer', 'number'].includes(property.type) ? Number(v) : v)}
-                readOnlyVariant={this.isUriReadOnly(property) ? "default" : undefined}/>
+                onChange={(_, v) => propertyChanged(property.name, ['integer', 'number'].includes(property.type) ? Number(v) : v)}
+                readOnlyVariant={isUriReadOnly(property) ? "default" : undefined}/>
             }
             {!property.secret &&
                 <Tooltip position="bottom-end" content={showEditor ? "Change to TextField" : "Change to Text Area"}>
-                    <Button variant="control" onClick={e => this.setState({showEditor: !showEditor})}>
+                    <Button variant="control" onClick={e => setShowEditor(!showEditor)}>
                         {showEditor ? <CompressIcon/> : <ExpandIcon/>}
                     </Button>
                 </Tooltip>
@@ -255,36 +255,38 @@ export class DslPropertyField extends React.Component<Props, State> {
         </InputGroup>)
     }
 
-    showCode = (name: string, javaType: string) => {
-        const {property} = this.props;
-        KaravanInstance.getProps().onGetCustomCode.call(this, name, property.javaType).then(value => {
-            if (value === undefined) {
-                const code = TemplateApi.generateCode(property.javaType, name);
-                this.setState({customCode: code, showEditor: true})
-            } else {
-                this.setState({customCode: value, showEditor: true})
-            }
-        }).catch((reason: any) => console.log(reason))
+    function showCode (name: string, javaType: string ) {
+        const {property} = props;
+        // TODO: 
+        // KaravanInstance.getProps().onGetCustomCode.call(this, name, property.javaType).then(value => {
+        //     if (value === undefined) {
+        //         const code = TemplateApi.generateCode(property.javaType, name);
+        //         setShowEditor(true);
+        //         setCustomCode(code);
+        //     } else {
+        //         setShowEditor(true);
+        //         setCustomCode(value);
+        //     }
+        // }).catch((reason: any) => console.log(reason))
     }
 
-    getJavaTypeGeneratedInput = (property: PropertyMeta, value: any) => {
-        const {dslLanguage, dark} = this.props;
-        const {showEditor, customCode} = this.state;
+    function getJavaTypeGeneratedInput (property: PropertyMeta, value: any ) {
+        const {dslLanguage} = props;
         return (<InputGroup>
             <InputGroupItem isFill >
                 <TextInput
-                    ref={this.state.ref}
+                    ref={ref}
                     className="text-field" isRequired 
                     type="text"
                     id={property.name} name={property.name}
                     value={value?.toString()}
                     onChange={(_, value) => {
-                        this.propertyChanged(property.name, CamelUtil.capitalizeName(value?.replace(/\s/g, '')))
+                        propertyChanged(property.name, CamelUtil.capitalizeName(value?.replace(/\s/g, '')))
                     }}
-                    readOnlyVariant={this.isUriReadOnly(property) ? "default" : undefined}/>
+                    readOnlyVariant={isUriReadOnly(property) ? "default" : undefined}/>
             </InputGroupItem>
             <InputGroupItem><Tooltip position="bottom-end" content={"Create Java Class"}>
-                <Button isDisabled={value?.length === 0} variant="control" onClick={e => this.showCode(value, property.javaType)}>
+                <Button isDisabled={value?.length === 0} variant="control" onClick={e => showCode(value, property.javaType)}>
                     <PlusIcon/>
                 </Button>
             </Tooltip></InputGroupItem>
@@ -294,18 +296,26 @@ export class DslPropertyField extends React.Component<Props, State> {
                          dark={dark}
                          dslLanguage={dslLanguage}
                          title="Java Class"
-                         onClose={() => this.setState({showEditor: false})}
+                         onClose={() => setShowEditor(false)}
                          onSave={(fieldId, value1) => {
-                             this.propertyChanged(fieldId, value);
-                             KaravanInstance.getProps().onSaveCustomCode?.call(this, value, value1);
-                             this.setState({showEditor: false});
+                             propertyChanged(fieldId, value);
+                             // TODO: 
+                             // KaravanInstance.getProps().onSaveCustomCode?.call(this, value, value1);
+                             setShowEditor(false)
                          }}/></InputGroupItem>
         </InputGroup>)
     }
 
-    getTextArea = (property: PropertyMeta, value: any) => {
-        const {dslLanguage, dark} = this.props;
-        const {showEditor} = this.state;
+    function onTextAreaPropertyChange (fieldId: string, value: string | number | boolean | any) {
+        if (props.expressionTextChanged !== undefined) {
+            props.expressionTextChanged(fieldId, value);
+        } else {
+            propertyChanged(fieldId, value);
+        }
+    }
+
+    function getTextArea (property: PropertyMeta, value: any ) {
+        const {dslLanguage} = props;
         return (
             <InputGroup>
                 <InputGroupItem isFill ><TextArea
@@ -316,9 +326,9 @@ export class DslPropertyField extends React.Component<Props, State> {
                     name={property.name}
                     height={"100px"}
                     value={value?.toString()}
-                    onChange={(_, v) => this.propertyChanged(property.name, v)}/></InputGroupItem>
+                    onChange={(_, v) => onTextAreaPropertyChange(property.name, v)}/></InputGroupItem>
                 <InputGroupItem><Tooltip position="bottom-end" content={"Show Editor"}>
-                    <Button variant="control" onClick={e => this.setState({showEditor: !showEditor})}>
+                    <Button variant="control" onClick={e => setShowEditor(!showEditor)}>
                         <EditorIcon/>
                     </Button>
                 </Tooltip></InputGroupItem>
@@ -328,40 +338,36 @@ export class DslPropertyField extends React.Component<Props, State> {
                              dark={dark}
                              dslLanguage={dslLanguage}
                              title={`Expression (${dslLanguage?.[0]})`}
-                             onClose={() => this.setState({showEditor: false})}
+                             onClose={() => setShowEditor(false)}
                              onSave={(fieldId, value1) => {
-                                 this.propertyChanged(fieldId, value1);
-                                 this.setState({showEditor: false});
+                                 onTextAreaPropertyChange(fieldId, value1);
+                                 setShowEditor(false);
                              }}/></InputGroupItem>
             </InputGroup>
         )
     }
 
-    getExpressionField = (property: PropertyMeta, value: any) => {
+    function getExpressionField (property: PropertyMeta, value: any ) {
         return (
             <div className="expression">
-                <ExpressionField property={property}
-                                 value={value}
-                                 onExpressionChange={this.props.onExpressionChange}
-                                 integration={this.props.integration}
-                                 dark={this.props.dark}/>
+                <ExpressionField property={property} value={value}/>
             </div>
         )
     }
 
-    getObjectField = (property: PropertyMeta, value: any) => {
+    function getObjectField (property: PropertyMeta, value: any ) {
         return (
             <div className="object">
                 {value && <ObjectField property={property}
                                        value={value}
-                                       onPropertyUpdate={this.props.onChange}
-                                       integration={this.props.integration}
-                                       dark={this.props.dark}/>}
+                                       onPropertyUpdate={propertyChanged}
+                                       integration={integration}
+                                       dark={dark}/>}
             </div>
         )
     }
 
-    getSwitch = (property: PropertyMeta, value: any) => {
+    function getSwitch (property: PropertyMeta, value: any ) {
         const isChecked = value !== undefined ? Boolean(value) : Boolean(property.defaultValue !== undefined && property.defaultValue === 'true');
         return (
             <Switch
@@ -369,13 +375,13 @@ export class DslPropertyField extends React.Component<Props, State> {
                 value={value?.toString()}
                 aria-label={property.name}
                 isChecked={isChecked}
-                onChange={(_, v) => this.propertyChanged(property.name, v)}/>
+                onChange={(_, v) => propertyChanged(property.name, v)}/>
         )
     }
 
-    getSelectBean = (property: PropertyMeta, value: any) => {
+    function getSelectBean (property: PropertyMeta, value: any ) {
         const selectOptions: JSX.Element[] = [];
-        const beans = CamelUi.getBeans(this.props.integration);
+        const beans = CamelUi.getBeans(integration);
         if (beans) {
             selectOptions.push(<SelectOption key={0} value={"Select..."} isPlaceholder/>);
             selectOptions.push(...beans.map((bean) => <SelectOption key={bean.name} value={bean.name} description={bean.type}/>));
@@ -385,11 +391,11 @@ export class DslPropertyField extends React.Component<Props, State> {
                 variant={SelectVariant.single}
                 aria-label={property.name}
                 onToggle={(_event, isExpanded) => {
-                    this.openSelect(property.name, isExpanded)
+                    openSelect(property.name, isExpanded)
                 }}
-                onSelect={(e, value, isPlaceholder) => this.propertyChanged(property.name, (!isPlaceholder ? value : undefined))}
+                onSelect={(e, value, isPlaceholder) => propertyChanged(property.name, (!isPlaceholder ? value : undefined))}
                 selections={value}
-                isOpen={this.isSelectOpen(property.name)}
+                isOpen={isSelectOpen(property.name)}
                 aria-labelledby={property.name}
                 direction={SelectDirection.down}
             >
@@ -398,7 +404,7 @@ export class DslPropertyField extends React.Component<Props, State> {
         )
     }
 
-    getSelect = (property: PropertyMeta, value: any) => {
+    function getSelect (property: PropertyMeta, value: any ) {
         const selectOptions: JSX.Element[] = []
         if (property.enumVals && property.enumVals.length > 0) {
             selectOptions.push(<SelectOption key={0} value={"Select " + property.name} isPlaceholder/>);
@@ -410,11 +416,11 @@ export class DslPropertyField extends React.Component<Props, State> {
                 variant={SelectVariant.single}
                 aria-label={property.name}
                 onToggle={(_event, isExpanded) => {
-                    this.openSelect(property.name, isExpanded)
+                    openSelect(property.name, isExpanded)
                 }}
-                onSelect={(e, value, isPlaceholder) => this.propertyChanged(property.name, (!isPlaceholder ? value : undefined))}
+                onSelect={(e, value, isPlaceholder) => propertyChanged(property.name, (!isPlaceholder ? value : undefined))}
                 selections={value}
-                isOpen={this.isSelectOpen(property.name)}
+                isOpen={isSelectOpen(property.name)}
                 id={property.name}
                 aria-labelledby={property.name}
                 direction={SelectDirection.down}
@@ -424,7 +430,7 @@ export class DslPropertyField extends React.Component<Props, State> {
         )
     }
 
-    getMediaTypeSelectOptions(filter?: string): JSX.Element [] {
+    function getMediaTypeSelectOptions(filter?: string): JSX.Element [] {
         const options: JSX.Element [] = [
             <SelectOption key={0} value="Select Media Type" isPlaceholder />
         ];
@@ -437,30 +443,30 @@ export class DslPropertyField extends React.Component<Props, State> {
         return options;
     }
 
-    getMediaTypeSelect = (property: PropertyMeta, value: any) => {
+    function getMediaTypeSelect (property: PropertyMeta, value: any ) {
         return (
             <Select
                 placeholderText="Select Media Type"
                 variant={SelectVariant.typeahead}
                 aria-label={property.name}
                 onToggle={(_event, isExpanded) => {
-                    this.openSelect(property.name, isExpanded)
+                    openSelect(property.name, isExpanded)
                 }}
-                onSelect={(e, value, isPlaceholder) => this.propertyChanged(property.name, (!isPlaceholder ? value : undefined))}
+                onSelect={(e, value, isPlaceholder) => propertyChanged(property.name, (!isPlaceholder ? value : undefined))}
                 selections={value}
-                isOpen={this.isSelectOpen(property.name)}
+                isOpen={isSelectOpen(property.name)}
                 isCreatable={false}
                 isInputFilterPersisted={false}
-                onFilter={(e, text) => this.getMediaTypeSelectOptions(text)}
+                onFilter={(e, text) => getMediaTypeSelectOptions(text)}
                 aria-labelledby={property.name}
                 direction={SelectDirection.down}
             >
-                {this.getMediaTypeSelectOptions()}
+                {getMediaTypeSelectOptions()}
             </Select>
         )
     }
 
-    canBeInternalUri = (property: PropertyMeta, element?: CamelElement): boolean => {
+    function canBeInternalUri (property: PropertyMeta, element?: CamelElement): boolean {
         if (element?.dslName === 'WireTapDefinition' && property.name === 'uri') {
             return true;
         } else if (element?.dslName === 'SagaDefinition' && ['compensation', 'completion'].includes(property.name)) {
@@ -472,9 +478,9 @@ export class DslPropertyField extends React.Component<Props, State> {
         }
     }
 
-    canBeMediaType = (property: PropertyMeta, element?: CamelElement): boolean => {
+    function canBeMediaType (property: PropertyMeta, element?: CamelElement): boolean {
         if (element
-            && ['RestDefinition', 'GetDefinition', 'PostDefinition', 'PutDefinition', 'PatchDefinition', 'DeleteDefinition', 'HeadDefinition'].includes(element?.dslName)
+            && ['RestDefinition', 'GetDefinition', 'PostDefinition', 'PutDefinition', 'PatchDefinition', 'DeleteDefinition', 'HeadDefinition'].includes(element.dslName)
             && ['consumes', 'produces'].includes(property.name)) {
             return true;
         } else {
@@ -482,14 +488,14 @@ export class DslPropertyField extends React.Component<Props, State> {
         }
     }
 
-    javaTypeGenerated = (property: PropertyMeta): boolean => {
+    function javaTypeGenerated (property: PropertyMeta): boolean {
         return property.javaType.length !== 0;
     }
 
-    getInternalUriSelect = (property: PropertyMeta, value: any) => {
+    function getInternalUriSelect (property: PropertyMeta, value: any ) {
         const selectOptions: JSX.Element[] = [];
-        const urls = CamelUi.getInternalRouteUris(this.props.integration, "direct");
-        urls.push(...CamelUi.getInternalRouteUris(this.props.integration, "seda"));
+        const urls = CamelUi.getInternalRouteUris(integration, "direct");
+        urls.push(...CamelUi.getInternalRouteUris(integration, "seda"));
         if (urls && urls.length > 0) {
             selectOptions.push(...urls.map((value: string) =>
                 <SelectOption key={value} value={value.trim()}/>));
@@ -499,17 +505,17 @@ export class DslPropertyField extends React.Component<Props, State> {
                 placeholderText="Select or type an URI"
                 variant={SelectVariant.typeahead}
                 aria-label={property.name}
-                onClear={event => this.clearSelection(property.name)}
+                onClear={event => clearSelection(property.name)}
                 onToggle={(_event, isExpanded) => {
-                    this.openSelect(property.name, isExpanded)
+                    openSelect(property.name, isExpanded)
                 }}
                 onSelect={(e, value, isPlaceholder) => {
                     const url = value.toString().split(":");
                     const newRoute = !urls.includes(value.toString()) && (['direct', 'seda'].includes(url[0])) ? new RouteToCreate(url[0], url[1]) : undefined;
-                    this.propertyChanged(property.name, (!isPlaceholder ? value : undefined), newRoute)
+                    propertyChanged(property.name, (!isPlaceholder ? value : undefined), newRoute)
                 }}
                 selections={value}
-                isOpen={this.isSelectOpen(property.name)}
+                isOpen={isSelectOpen(property.name)}
                 isCreatable={true}
                 isInputFilterPersisted={true}
                 aria-labelledby={property.name}
@@ -520,70 +526,70 @@ export class DslPropertyField extends React.Component<Props, State> {
         )
     }
 
-    onMultiValueObjectUpdate = (index: number, fieldId: string, value: CamelElement) => {
-        const mValue = [...this.props.value];
+    function onMultiValueObjectUpdate (index: number, fieldId: string, value: CamelElement ) {
+        const mValue = [...props.value];
         mValue[index] = value;
-        this.props.onChange?.call(this, fieldId, mValue);
+        onPropertyChange(fieldId, mValue);
     }
 
-    isKeyValueObject(property: PropertyMeta) {
+    function isKeyValueObject(property: PropertyMeta) {
         const props = CamelDefinitionApiExt.getElementProperties(property.type);
         return props.length === 2 && props.filter(p => p.name === 'key').length === 1 && props.filter(p => p.name === 'value').length === 1;
     }
 
-    getMultiObjectFieldProps(property: PropertyMeta, value: any, v: any, index: number, hideLabel: boolean = false) {
+    function getMultiObjectFieldProps(property: PropertyMeta, value: any, v: any, index: number, hideLabel: boolean = false) {
         return (<>
             <div className="object">
                 {value && <ObjectField property={property}
                                        hideLabel={hideLabel}
                                        value={v}
-                                       onPropertyUpdate={(f, v) => this.onMultiValueObjectUpdate(index, f, v)}
-                                       integration={this.props.integration}
-                                       dark={this.props.dark}/>}
+                                       onPropertyUpdate={(f, v) => onMultiValueObjectUpdate(index, f, v)}
+                                       integration={integration}
+                                       dark={dark}/>}
             </div>
             <Button variant="link" className="delete-button" onClick={e => {
                 const v = Array.from(value);
                 v.splice(index, 1);
-                this.propertyChanged(property.name, v);
+                propertyChanged(property.name, v);
             }}><DeleteIcon/></Button>
         </>)
     }
 
-    getMultiValueObjectField = (property: PropertyMeta, value: any) => {
-        const isKeyValue = this.isKeyValueObject(property);
+    function getMultiValueObjectField (property: PropertyMeta, value: any ) {
+        const isKeyValue = isKeyValueObject(property);
         return (
             <div>
                 {value && Array.from(value).map((v: any, index: number) => {
                     if (isKeyValue)
                         return <div key={property + "-" + index} className="object-key-value">
-                            {this.getMultiObjectFieldProps(property, value, v, index, index > 0)}
+                            {getMultiObjectFieldProps(property, value, v, index, index > 0)}
                         </div>
                     else
                         return <Card key={property + "-" + index} className="object-value">
-                            {this.getMultiObjectFieldProps(property, value, v, index)}
+                            {getMultiObjectFieldProps(property, value, v, index)}
                         </Card>
                 })}
                 <Button variant="link" className="add-button"
-                        onClick={e => this.propertyChanged(property.name, [...value, CamelDefinitionApi.createStep(property.type, {})])}><AddIcon/>{"Add " + property.displayName}
+                        onClick={e => propertyChanged(property.name, [...value, CamelDefinitionApi.createStep(property.type, {})])}><AddIcon/>{"Add " + property.displayName}
                 </Button>
             </div>
         )
     }
 
-    getMultiValueField = (property: PropertyMeta, value: any) => {
+    function getMultiValueField (property: PropertyMeta, value: any ) {
         return (
             <div>
                 <TextInputGroup className="input-group">
-                    <TextInputGroupMain value={this.state.arrayValues.get(property.name)} onChange={(e, v) => this.arrayChanged(property.name, v)} onKeyUp={e => {
-                        if (e.key === 'Enter') this.arraySave(property.name)
+                    <TextInputGroupMain value={arrayValues.get(property.name)} onChange={(e, v) => arrayChanged(property.name, v)} onKeyUp={e => {
+                        if (e.key === 'Enter') arraySave(property.name)
                     }}>
                         <ChipGroup>
                             {value && Array.from(value).map((v: any, index: number) => (
-                                <Chip key={"chip-" + index} className="chip" onClick={() => this.arrayDeleteValue(property.name, v)}>{v.toString()}</Chip>))}
+                                <Chip key={"chip-" + index} className="chip" onClick={() => arrayDeleteValue(property.name, v)}>{v.toString()}</Chip>))}
                         </ChipGroup>
                     </TextInputGroupMain>
                     <TextInputGroupUtilities>
-                        <Button variant="plain" onClick={e => this.arraySave(property.name)} aria-label="Add element">
+                        <Button variant="plain" onClick={e => arraySave(property.name)} aria-label="Add element">
                             <PlusIcon/>
                         </Button>
                     </TextInputGroupUtilities>
@@ -592,8 +598,8 @@ export class DslPropertyField extends React.Component<Props, State> {
         )
     }
 
-    getKameletParameters = () => {
-        const element = this.props.element;
+    function getKameletParameters () {
+        const element = props.element;
         const requiredParameters = CamelUtil.getKameletRequiredParameters(element);
         return (
             <div className="parameters">
@@ -603,49 +609,51 @@ export class DslPropertyField extends React.Component<Props, State> {
                         property={property}
                         value={CamelDefinitionApiExt.getParametersValue(element, property.id)}
                         required={requiredParameters?.includes(property.id)}
-                        onParameterChange={this.props.onParameterChange}
+                        onParameterChange={onParametersChange}
                     />)}
             </div>
         )
     }
 
-    getMainComponentParameters = (properties: ComponentProperty[]) => {
+    function getMainComponentParameters (properties: ComponentProperty[] ) {
+        const element = props.element;
         return (
             <div className="parameters">
                 {properties.map(kp => {
-                    const value = CamelDefinitionApiExt.getParametersValue(this.props.element, kp.name, kp.kind === 'path');
+                    const value = CamelDefinitionApiExt.getParametersValue(element, kp.name, kp.kind === 'path');
                     return (<ComponentParameterField
                         key={kp.name}
                         property={kp}
-                        element={this.props.element}
-                        integration={this.props.integration}
+                        element={element}
+                        integration={integration}
                         value={value}
-                        onParameterChange={this.props.onParameterChange}
+                        onParameterChange={onParametersChange}
                     />)
                 })}
             </div>
         )
     }
 
-    getExpandableComponentParameters = (properties: ComponentProperty[], label: string) => {
+    function getExpandableComponentParameters (properties: ComponentProperty[], label: string ) {
+        const element = props.element;
         return (
             <ExpandableSection
                 toggleText={label}
                 onToggle={(_event, isExpanded) => {
-                    this.setState(state => {
-                        state.isShowAdvanced.set(label, !state.isShowAdvanced.get(label));
-                        return {isShowAdvanced: state.isShowAdvanced};
+                    setIsShowAdvanced(prevState => {
+                        prevState.set(label, !prevState.get(label));
+                        return prevState;
                     })
                 }}
-                isExpanded={this.state.isShowAdvanced.has(label) && this.state.isShowAdvanced.get(label)}>
+                isExpanded={isShowAdvanced.has(label) && isShowAdvanced.get(label)}>
                 <div className="parameters">
                     {properties.map(kp =>
                         <ComponentParameterField
                             key={kp.name}
                             property={kp}
-                            integration={this.props.integration}
-                            value={CamelDefinitionApiExt.getParametersValue(this.props.element, kp.name, kp.kind === 'path')}
-                            onParameterChange={this.props.onParameterChange}
+                            integration={integration}
+                            value={CamelDefinitionApiExt.getParametersValue(element, kp.name, kp.kind === 'path')}
+                            onParameterChange={onParametersChange}
                         />
                     )}
                 </div>
@@ -653,7 +661,7 @@ export class DslPropertyField extends React.Component<Props, State> {
         )
     }
 
-    getLabelIcon = (property: PropertyMeta) => {
+    function getLabelIcon (property: PropertyMeta ) {
         return (
             property.description
                 ? <Popover
@@ -678,72 +686,72 @@ export class DslPropertyField extends React.Component<Props, State> {
     }
 
 
-    isMultiValueField = (property: PropertyMeta): boolean => {
+    function isMultiValueField (property: PropertyMeta): boolean {
         return ['string'].includes(property.type) && property.name !== 'expression' && property.isArray && !property.enumVals;
     }
 
-    getComponentParameters(property: PropertyMeta) {
-        const properties = CamelUtil.getComponentProperties(this.props.element);
+    function getComponentParameters(property: PropertyMeta) {
+        const element = props.element;
+        const properties = CamelUtil.getComponentProperties(element);
         const propertiesMain = properties.filter(p => !p.label.includes("advanced") && !p.label.includes("security") && !p.label.includes("scheduler"));
         const propertiesAdvanced = properties.filter(p => p.label.includes("advanced"));
         const propertiesScheduler = properties.filter(p => p.label.includes("scheduler"));
         const propertiesSecurity = properties.filter(p => p.label.includes("security"));
         return (
             <>
-                {property.name === 'parameters' && this.getMainComponentParameters(propertiesMain)}
-                {property.name === 'parameters' && this.props.element && propertiesScheduler.length > 0
-                    && this.getExpandableComponentParameters(propertiesScheduler, "Scheduler parameters")}
-                {property.name === 'parameters' && this.props.element && propertiesSecurity.length > 0
-                    && this.getExpandableComponentParameters(propertiesSecurity, "Security parameters")}
-                {property.name === 'parameters' && this.props.element && propertiesAdvanced.length > 0
-                    && this.getExpandableComponentParameters(propertiesAdvanced, "Advanced parameters")}
+                {property.name === 'parameters' && getMainComponentParameters(propertiesMain)}
+                {property.name === 'parameters' && element && propertiesScheduler.length > 0
+                    && getExpandableComponentParameters(propertiesScheduler, "Scheduler parameters")}
+                {property.name === 'parameters' && element && propertiesSecurity.length > 0
+                    && getExpandableComponentParameters(propertiesSecurity, "Security parameters")}
+                {property.name === 'parameters' && element && propertiesAdvanced.length > 0
+                    && getExpandableComponentParameters(propertiesAdvanced, "Advanced parameters")}
             </>
         )
     }
 
-    render() {
-        const isKamelet = CamelUtil.isKameletComponent(this.props.element);
-        const property: PropertyMeta = this.props.property;
-        const value = this.props.value;
-        return (
-            <div>
-                <FormGroup
-                    label={this.props.hideLabel ? undefined : this.getLabel(property, value)}
-                    isRequired={property.required}
-                    labelIcon={this.getLabelIcon(property)}>
-                    {value && ["ExpressionDefinition", "ExpressionSubElementDefinition"].includes(property.type)
-                        && this.getExpressionField(property, value)}
-                    {property.isObject && !property.isArray && !["ExpressionDefinition", "ExpressionSubElementDefinition"].includes(property.type)
-                        && this.getObjectField(property, value)}
-                    {property.isObject && property.isArray && !this.isMultiValueField(property)
-                        && this.getMultiValueObjectField(property, value)}
-                    {property.name === 'expression' && property.type === "string" && !property.isArray
-                        && this.getTextArea(property, value)}
-                    {this.canBeInternalUri(property, this.props.element)
-                        && this.getInternalUriSelect(property, value)}
-                    {this.canBeMediaType(property, this.props.element)
-                        && this.getMediaTypeSelect(property, value)}
-                    {this.javaTypeGenerated(property)
-                        && this.getJavaTypeGeneratedInput(property, value)}
-                    {['string', 'duration', 'integer', 'number'].includes(property.type) && property.name !== 'expression' && !property.name.endsWith("Ref")
-                        && !property.isArray && !property.enumVals
-                        && !this.canBeInternalUri(property, this.props.element)
-                        && !this.canBeMediaType(property, this.props.element)
-                        && !this.javaTypeGenerated(property)
-                        && this.getStringInput(property, value)}
-                    {['string'].includes(property.type) && property.name.endsWith("Ref") && !property.isArray && !property.enumVals
-                        && this.getSelectBean(property, value)}
-                    {this.isMultiValueField(property)
-                        && this.getMultiValueField(property, value)}
-                    {property.type === 'boolean'
-                        && this.getSwitch(property, value)}
-                    {property.enumVals
-                        && this.getSelect(property, value)}
-                    {isKamelet && property.name === 'parameters' && this.getKameletParameters()}
-                    {!isKamelet && property.name === 'parameters' && this.getComponentParameters(property)}
-                </FormGroup>
-                {this.getInfrastructureSelectorModal()}
-            </div>
-        )
-    }
+    const element = props.element;
+    const isKamelet = CamelUtil.isKameletComponent(element);
+    const property: PropertyMeta = props.property;
+    const value = props.value;
+    return (
+        <div>
+            <FormGroup
+                label={props.hideLabel ? undefined : getLabel(property, value)}
+                isRequired={property.required}
+                labelIcon={getLabelIcon(property)}>
+                {value && ["ExpressionDefinition", "ExpressionSubElementDefinition"].includes(property.type)
+                    && getExpressionField(property, value)}
+                {property.isObject && !property.isArray && !["ExpressionDefinition", "ExpressionSubElementDefinition"].includes(property.type)
+                    && getObjectField(property, value)}
+                {property.isObject && property.isArray && !isMultiValueField(property)
+                    && getMultiValueObjectField(property, value)}
+                {property.name === 'expression' && property.type === "string" && !property.isArray
+                    && getTextArea(property, value)}
+                {canBeInternalUri(property, element)
+                    && getInternalUriSelect(property, value)}
+                {canBeMediaType(property, element)
+                    && getMediaTypeSelect(property, value)}
+                {javaTypeGenerated(property)
+                    && getJavaTypeGeneratedInput(property, value)}
+                {['string', 'duration', 'integer', 'number'].includes(property.type) && property.name !== 'expression' && !property.name.endsWith("Ref")
+                    && !property.isArray && !property.enumVals
+                    && !canBeInternalUri(property, element)
+                    && !canBeMediaType(property, element)
+                    && !javaTypeGenerated(property)
+                    && getStringInput(property, value)}
+                {['string'].includes(property.type) && property.name.endsWith("Ref") && !property.isArray && !property.enumVals
+                    && getSelectBean(property, value)}
+                {isMultiValueField(property)
+                    && getMultiValueField(property, value)}
+                {property.type === 'boolean'
+                    && getSwitch(property, value)}
+                {property.enumVals
+                    && getSelect(property, value)}
+                {isKamelet && property.name === 'parameters' && getKameletParameters()}
+                {!isKamelet && property.name === 'parameters' && getComponentParameters(property)}
+            </FormGroup>
+            {getInfrastructureSelectorModal()}
+        </div>
+    )
 }
diff --git a/karavan-designer/src/designer/route/property/ExpressionField.tsx b/karavan-designer/src/designer/route/property/ExpressionField.tsx
index 135c11fe..9a341d41 100644
--- a/karavan-designer/src/designer/route/property/ExpressionField.tsx
+++ b/karavan-designer/src/designer/route/property/ExpressionField.tsx
@@ -14,7 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import React from 'react';
+import React, {useState} from 'react';
 import {
 	FormGroup,
 	Popover
@@ -31,137 +31,124 @@ import HelpIcon from "@patternfly/react-icons/dist/js/icons/help-icon";
 import {CamelMetadataApi, Languages, PropertyMeta} from "karavan-core/lib/model/CamelMetadata";
 import {CamelDefinitionApiExt} from "karavan-core/lib/api/CamelDefinitionApiExt";
 import {ExpressionDefinition} from "karavan-core/lib/model/CamelDefinition";
-import {Integration, CamelElement} from "karavan-core/lib/model/IntegrationDefinition";
+import {CamelElement} from "karavan-core/lib/model/IntegrationDefinition";
 import {CamelDefinitionApi} from "karavan-core/lib/api/CamelDefinitionApi";
 import {DslPropertyField} from "./DslPropertyField";
 import {CamelUi} from "../../utils/CamelUi";
+import {usePropertiesHook} from "../usePropertiesHook";
 
 interface Props {
     property: PropertyMeta,
     value: CamelElement,
-    onExpressionChange?: (propertyName: string, exp: ExpressionDefinition) => void
-    integration: Integration,
-    dark: boolean,
 }
 
-interface State {
-    selectIsOpen: boolean;
-}
-
-export class ExpressionField extends React.Component<Props, State> {
+export const ExpressionField = (props: Props) => {
 
-    public state: State = {
-        selectIsOpen: false,
-    }
+    const {onExpressionChange} = usePropertiesHook();
+    const [selectIsOpen, setSelectIsOpen] = useState<boolean>(false);
 
-    openSelect = (isExpanded: boolean) => {
-        this.setState({selectIsOpen: isExpanded});
+    function openSelect (isExpanded: boolean) {
+        setSelectIsOpen(isExpanded);
     }
 
-    expressionChanged = (language: string, value: CamelElement) => {
+    function expressionChanged (language: string, value: CamelElement) {
         if (language !== (value as any).expressionName) {
             const className = CamelMetadataApi.getCamelLanguageMetadataByName(language)?.className;
             value = CamelDefinitionApi.createExpression(className || '', {expression: (value as any).expression}); // perhaps copy other similar fields later
         }
         const exp = new ExpressionDefinition();
         (exp as any)[language] = value;
-        if (this.props.value) (exp as any).uuid = this.props.value.uuid;
-        this.props.onExpressionChange?.call(this, this.props.property.name, exp);
-        this.setState({selectIsOpen: false});
+        if (props.value) (exp as any).uuid = props.value.uuid;
+        onExpressionChange(props.property.name, exp);
+        setSelectIsOpen(false);
     }
 
-    propertyChanged = (fieldId: string, value: string | number | boolean | any) => {
-        const expression = this.getExpressionValue();
+    function expressionTextChanged (fieldId: string, value: string | number | boolean | any) {
+        const expression = getExpressionValue();
         if (expression) {
             (expression as any)[fieldId] = value;
-            this.expressionChanged(this.getValueLanguage(), expression);
+            expressionChanged(getValueLanguage(), expression);
         }
     }
 
-    getValueClassName = (): string => {
-        return CamelDefinitionApiExt.getExpressionLanguageClassName(this.props.value) || 'SimpleExpression';
+    function getValueClassName (): string {
+        return CamelDefinitionApiExt.getExpressionLanguageClassName(props.value) || 'SimpleExpression';
     }
 
-    getValueLanguage = (): string => {
-        return CamelDefinitionApiExt.getExpressionLanguageName(this.props.value) || 'simple';
+    function getValueLanguage (): string {
+        return CamelDefinitionApiExt.getExpressionLanguageName(props.value) || 'simple';
     }
 
-    getExpressionValue = (): CamelElement => {
-        const language = this.getValueLanguage();
-        return this.props.value && (this.props.value as any)[language]
-            ? (this.props.value as any)[language]
-            : CamelDefinitionApi.createExpression(this.getValueClassName(), this.props.value);
+    function getExpressionValue (): CamelElement {
+        const language = getValueLanguage();
+        return props.value && (props.value as any)[language]
+            ? (props.value as any)[language]
+            : CamelDefinitionApi.createExpression(getValueClassName(), props.value);
     }
 
-    getProps = (): PropertyMeta[] => {
-        const dslName = this.getValueClassName();
+    function getProps (): PropertyMeta[] {
+        const dslName = getValueClassName();
         return CamelDefinitionApiExt.getElementProperties(dslName)
             .filter(p => p.name !== 'id')
             .filter(p => !p.isObject || (p.isObject && !CamelUi.dslHasSteps(p.type)) || (dslName === 'CatchDefinition' && p.name === 'onWhen'));
     }
 
-    render() {
-        const property: PropertyMeta = this.props.property;
-        const value = this.getExpressionValue();
-        const dslLanguage = Languages.find((l: [string, string, string]) => l[0] === this.getValueLanguage());
-        const selectOptions: JSX.Element[] = []
-        Languages.forEach((lang: [string, string, string]) => {
-            const s = <SelectOption key={lang[0]} value={lang[0]} description={lang[2]}/>;
-            selectOptions.push(s);
-        })
-        return (
-            <div>
-                <label className="pf-v5-c-form__label" htmlFor="expression">
-                    <span className="pf-v5-c-form__label-text">Language</span>
-                    <span className="pf-v5-c-form__label-required" aria-hidden="true"> *</span>
-                </label>
-                <Select
-                    variant={SelectVariant.typeahead}
-                    aria-label={property.name}
-                    onToggle={(_event, isExpanded) => {
-                        this.openSelect(isExpanded)
-                    }}
-                    onSelect={(e, lang, isPlaceholder) => {
-                        this.expressionChanged(lang.toString(), value);
-                    }}
-                    selections={dslLanguage}
-                    isOpen={this.state.selectIsOpen}
-                    aria-labelledby={property.name}
-                    direction={SelectDirection.down}
-                >
-                    {selectOptions}
-                </Select>
-                <FormGroup
-                    key={property.name}
-                    fieldId={property.name}
-                    labelIcon={property.description ?
-                        <Popover
-                            position={"left"}
-                            headerContent={property.displayName}
-                            bodyContent={property.description}>
-                            <button type="button" aria-label="More info" onClick={e => {
-                                e.preventDefault();
-                                e.stopPropagation();
-                            }}
-                                    className="pf-v5-c-form__group-label-help">
-                                <HelpIcon />
-                            </button>
-                        </Popover> : <div></div>
-                    }>
-                    {value && this.getProps().map((property: PropertyMeta) =>
-                        <DslPropertyField key={property.name + this.props.value?.uuid} property={property}
-                                          integration={this.props.integration}
-                                          element={value}
-                                          value={value ? (value as any)[property.name] : undefined}
-                                          onExpressionChange={exp => {}}
-                                          onParameterChange={parameter => {console.log(parameter)}}
-                                          onDataFormatChange={dataFormat => {console.log(dataFormat)}}
-                                          onChange={this.propertyChanged}
-                                          dark={this.props.dark}
-                                          dslLanguage={dslLanguage}/>
-                    )}
-                </FormGroup>
-            </div>
-        )
-    }
+    const property: PropertyMeta = props.property;
+    const value = getExpressionValue();
+    const dslLanguage = Languages.find((l: [string, string, string]) => l[0] === getValueLanguage());
+    const selectOptions: JSX.Element[] = []
+    Languages.forEach((lang: [string, string, string]) => {
+        const s = <SelectOption key={lang[0]} value={lang[0]} description={lang[2]}/>;
+        selectOptions.push(s);
+    })
+    return (
+        <div>
+            <label className="pf-v5-c-form__label" htmlFor="expression">
+                <span className="pf-v5-c-form__label-text">Language</span>
+                <span className="pf-v5-c-form__label-required" aria-hidden="true"> *</span>
+            </label>
+            <Select
+                variant={SelectVariant.typeahead}
+                aria-label={property.name}
+                onToggle={(_event, isExpanded) => {
+                    openSelect(isExpanded)
+                }}
+                onSelect={(e, lang, isPlaceholder) => {
+                    expressionChanged(lang.toString(), value);
+                }}
+                selections={dslLanguage}
+                isOpen={selectIsOpen}
+                aria-labelledby={property.name}
+                direction={SelectDirection.down}
+            >
+                {selectOptions}
+            </Select>
+            <FormGroup
+                key={property.name}
+                fieldId={property.name}
+                labelIcon={property.description ?
+                    <Popover
+                        position={"left"}
+                        headerContent={property.displayName}
+                        bodyContent={property.description}>
+                        <button type="button" aria-label="More info" onClick={e => {
+                            e.preventDefault();
+                            e.stopPropagation();
+                        }}
+                                className="pf-v5-c-form__group-label-help">
+                            <HelpIcon />
+                        </button>
+                    </Popover> : <div></div>
+                }>
+                {value && getProps().map((property: PropertyMeta) =>
+                    <DslPropertyField key={property.name + props.value?.uuid}
+                                      property={property}
+                                      element={value}
+                                      value={value ? (value as any)[property.name] : undefined}
+                                      dslLanguage={dslLanguage}
+                                      expressionTextChanged={expressionTextChanged}/>
+                )}
+            </FormGroup>
+        </div>
+    )
 }
\ No newline at end of file
diff --git a/karavan-designer/src/designer/route/property/ObjectField.tsx b/karavan-designer/src/designer/route/property/ObjectField.tsx
index a72019b5..1ebc93f2 100644
--- a/karavan-designer/src/designer/route/property/ObjectField.tsx
+++ b/karavan-designer/src/designer/route/property/ObjectField.tsx
@@ -76,17 +76,7 @@ export class ObjectField extends React.Component<Props, State> {
         return (
                 <div className="object-field">
                     {value && CamelDefinitionApiExt.getElementProperties(value.dslName).map((property: PropertyMeta)  =>
-                        <DslPropertyField key={property.name}
-                                          hideLabel={this.props.hideLabel}
-                                          integration={this.props.integration}
-                                          property={property}
-                                          element={value}
-                                          value={value ? (value as any)[property.name] : undefined}
-                                          onExpressionChange={this.expressionChanged}
-                                          onParameterChange={(parameter, value) => this.propertyChanged(property.name, value)}
-                                          onDataFormatChange={value1 => {}}
-                                          onChange={(fieldId, value) => this.propertyChanged(property.name, value)}
-                                          dark={this.props.dark}/>
+                        <DslPropertyField key={property.name} property={property} value={value ? (value as any)[property.name] : undefined}/>
                     )}
                 </div>
         )
diff --git a/karavan-designer/src/designer/route/usePropertiesHook.tsx b/karavan-designer/src/designer/route/usePropertiesHook.tsx
new file mode 100644
index 00000000..a3e4e5cd
--- /dev/null
+++ b/karavan-designer/src/designer/route/usePropertiesHook.tsx
@@ -0,0 +1,103 @@
+/*
+ * 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 '../karavan.css';
+import {CamelUtil} from "karavan-core/lib/api/CamelUtil";
+import {
+    DataFormatDefinition, ExpressionDefinition,
+} from "karavan-core/lib/model/CamelDefinition";
+import {CamelElement} from "karavan-core/lib/model/IntegrationDefinition";
+import {CamelDefinitionApiExt} from "karavan-core/lib/api/CamelDefinitionApiExt";
+import {CamelDefinitionApi} from "karavan-core/lib/api/CamelDefinitionApi";
+import {RouteToCreate} from "../utils/CamelUi";
+import {useDesignerStore, useIntegrationStore} from "../KaravanStore";
+import {shallow} from "zustand/shallow";
+
+
+export const usePropertiesHook = () => {
+
+    const [integration, setIntegration] = useIntegrationStore((state) => [state.integration, state.setIntegration], shallow)
+    const [ selectedStep,setPropertyOnly, setSelectedStep, setSelectedUuids] = useDesignerStore((s) =>
+        [s.selectedStep,s.setPropertyOnly, s.setSelectedStep, s.setSelectedUuids], shallow)
+
+    function onPropertyUpdate (element: CamelElement, newRoute?: RouteToCreate) {
+        console.log("-> onPropertyUpdate", element, newRoute)
+        if (newRoute) {
+            let i = CamelDefinitionApiExt.updateIntegrationRouteElement(integration, element);
+            const f = CamelDefinitionApi.createFromDefinition({uri: newRoute.componentName, parameters: {name: newRoute.name}});
+            const r = CamelDefinitionApi.createRouteDefinition({from: f, id: newRoute.name})
+            i = CamelDefinitionApiExt.addStepToIntegration(i, r, '');
+            const clone = CamelUtil.cloneIntegration(i);
+            setIntegration(clone);
+            setSelectedStep(element);
+            setPropertyOnly(false);
+            setSelectedUuids([element.uuid]);
+        } else {
+            const clone = CamelUtil.cloneIntegration(integration);
+            const i = CamelDefinitionApiExt.updateIntegrationRouteElement(clone, element);
+            setIntegration(i);
+            setPropertyOnly(true);
+        }
+    }
+
+    function onPropertyChange (fieldId: string, value: string | number | boolean | any, newRoute?: RouteToCreate){
+        console.log("onPropertyChange", fieldId, value,);
+        console.log("onPropertyChange", selectedStep);
+        if (selectedStep) {
+            const clone = CamelUtil.cloneStep(selectedStep);
+            (clone as any)[fieldId] = value;
+            setSelectedStep(clone)
+            console.log("onPropertyChange", clone);
+            onPropertyUpdate(clone, newRoute);
+        }
+    }
+
+    function onDataFormatChange (value: DataFormatDefinition)   {
+        value.uuid = selectedStep?.uuid ? selectedStep?.uuid : value.uuid;
+        setSelectedStep(value);
+        onPropertyUpdate(value);
+    }
+
+    function onExpressionChange (propertyName: string, exp: ExpressionDefinition)   {
+        console.log("onExpressionChange", propertyName, exp);
+        console.log("onExpressionChange", selectedStep);
+        if (selectedStep) {
+            const clone = (CamelUtil.cloneStep(selectedStep));
+            (clone as any)[propertyName] = exp;
+            setSelectedStep(clone);
+            console.log("onExpressionChange", clone);
+            onPropertyUpdate(clone);
+        }
+    }
+
+    function onParametersChange (parameter: string, value: string | number | boolean | any, pathParameter?: boolean, newRoute?: RouteToCreate)   {
+        console.log("onParametersChange", parameter, value);
+        if (selectedStep && selectedStep) {
+            const clone = (CamelUtil.cloneStep(selectedStep));
+            const parameters: any = {...(clone as any).parameters};
+            parameters[parameter] = value;
+            (clone as any).parameters = parameters;
+            setSelectedStep(clone);
+            onPropertyUpdate(clone, newRoute);
+        }
+    }
+
+    function cloneElement ()   {
+        // TODO:
+    }
+
+    return {onPropertyChange, onParametersChange, cloneElement, onDataFormatChange, onExpressionChange}
+}
\ No newline at end of file
diff --git a/karavan-designer/src/designer/route/useRouteDesignerHook.tsx b/karavan-designer/src/designer/route/useRouteDesignerHook.tsx
index d7ed9027..61208dfa 100644
--- a/karavan-designer/src/designer/route/useRouteDesignerHook.tsx
+++ b/karavan-designer/src/designer/route/useRouteDesignerHook.tsx
@@ -38,77 +38,21 @@ import {setTabIndex} from "@patternfly/react-core";
 export const useRouteDesignerHook = () => {
 
     const [integration, setIntegration] = useIntegrationStore((state) => [state.integration, state.setIntegration], shallow)
-    const [ showDeleteConfirmation, propertyOnly,deleteMessage, selectedUuids,clipboardSteps,selectedStep,shiftKeyPressed,
-        setShowDeleteConfirmation,setPropertyOnly, setDeleteMessage, setSelectedStep, setSelectedUuids, setClipboardSteps, setPosition, setShiftKeyPressed,
-        width, height, top, left, dark] = useDesignerStore((s) =>
-        [s.showDeleteConfirmation, s.propertyOnly,s.deleteMessage, s.selectedUuids,s.clipboardSteps, s.selectedStep, s.shiftKeyPressed,
-             s.setShowDeleteConfirmation,s.setPropertyOnly,s.setDeleteMessage, s.setSelectedStep, s.setSelectedUuids, s.setClipboardSteps, s.setPosition, s.setShiftKeyPressed,
-            s.width, s.height, s.top, s.left, s.dark], shallow)
+    const [selectedUuids,clipboardSteps,shiftKeyPressed,
+        setShowDeleteConfirmation,setPropertyOnly, setDeleteMessage, setSelectedStep, setSelectedUuids, setClipboardSteps, setShiftKeyPressed,
+        width, height, dark] = useDesignerStore((s) =>
+        [s.selectedUuids,s.clipboardSteps, s.shiftKeyPressed,
+            s.setShowDeleteConfirmation,s.setPropertyOnly,s.setDeleteMessage, s.setSelectedStep, s.setSelectedUuids, s.setClipboardSteps, s.setShiftKeyPressed,
+            s.width, s.height, s.dark], shallow)
     const [setParentId, setShowSelector, setSelectorTabIndex, setParentDsl, setShowSteps, setSelectedPosition] = useSelectorStore((s) =>
         [s.setParentId, s.setShowSelector, s.setSelectorTabIndex, s.setParentDsl, s.setShowSteps, s.setSelectedPosition], shallow)
 
-    // const commandSub = EventBus.onCommand()?.subscribe((command: Command) => onCommand(command));
-    //
-    //
-    // function componentDidMount() {
-    //     window.addEventListener('resize', this.routeDesigner.handleResize);
-    //     window.addEventListener('keydown', this.routeDesigner.handleKeyDown);
-    //     window.addEventListener('keyup', this.routeDesigner.handleKeyUp);
-    //     const element = findDOMNode(this.routeDesigner.state.ref.current)?.parentElement?.parentElement;
-    //     const checkResize = (mutations: any) => {
-    //         const el = mutations[0].target;
-    //         const w = el.clientWidth;
-    //         const isChange = mutations.map((m: any) => `${m.oldValue}`).some((prev: any) => prev.indexOf(`width: ${w}px`) === -1);
-    //         if (isChange) this.routeDesigner.setState({key: Math.random().toString()});
-    //     }
-    //     if (element) {
-    //         const observer = new MutationObserver(checkResize);
-    //         observer.observe(element, {attributes: true, attributeOldValue: true, attributeFilter: ['style']});
-    //     }
-    //     this.commandSub = EventBus.onCommand()?.subscribe((command: Command) => this.onCommand(command));
-    // }
-    //
-    // function componentWillUnmount() {
-    //     window.removeEventListener('resize', this.routeDesigner.handleResize);
-    //     window.removeEventListener('keydown', this.routeDesigner.handleKeyDown);
-    //     window.removeEventListener('keyup', this.routeDesigner.handleKeyUp);
-    //     this.commandSub?.unsubscribe();
-    // }
-    //
-    // function handleResize = (event: any) => {
-    //     this.routeDesigner.setState({key: Math.random().toString()});
-    // }
-    //
-
-    //
     function  onCommand (command: Command, printerRef: React.MutableRefObject<HTMLDivElement | null>) {
         switch (command.command){
             case "downloadImage": integrationImageDownload(printerRef);
         }
     }
-    //
-    // function onPropertyUpdate = debounce((element: CamelElement, newRoute?: RouteToCreate) => {
-    //     if (newRoute) {
-    //         let i = CamelDefinitionApiExt.updateIntegrationRouteElement(this.routeDesigner.state.integration, element);
-    //         const f = CamelDefinitionApi.createFromDefinition({uri: newRoute.componentName, parameters: {name: newRoute.name}});
-    //         const r = CamelDefinitionApi.createRouteDefinition({from: f, id: newRoute.name})
-    //         i = CamelDefinitionApiExt.addStepToIntegration(i, r, '');
-    //         const clone = CamelUtil.cloneIntegration(i);
-    //         this.routeDesigner.setState(prevState => ({
-    //             integration: clone,
-    //             key: Math.random().toString(),
-    //             showSelector: false,
-    //             selectedStep: element,
-    //             propertyOnly: false,
-    //             selectedUuids: [element.uuid]
-    //         }));
-    //     } else {
-    //         const clone = CamelUtil.cloneIntegration(this.routeDesigner.state.integration);
-    //         const i = CamelDefinitionApiExt.updateIntegrationRouteElement(clone, element);
-    //         this.routeDesigner.setState({integration: i, propertyOnly: true, key: Math.random().toString()});
-    //     }
-    // }, 300, {leading: true})
-    //
+
     const onShowDeleteConfirmation = (id: string) => {
         let message: string;
         const uuidsToDelete:string [] = [id];
@@ -239,21 +183,17 @@ export const useRouteDesignerHook = () => {
         }
     }
 
-    //
-    // function unselectElement = (evt: React.MouseEvent<HTMLDivElement, MouseEvent>) => {
-    //     if ((evt.target as any).dataset.click === 'FLOWS') {
-    //         evt.stopPropagation()
-    //         const i = CamelDisplayUtil.setIntegrationVisibility(this.routeDesigner.state.integration, undefined);
-    //         this.routeDesigner.setState(prevState => ({
-    //             integration: i,
-    //             selectedStep: undefined,
-    //             showSelector: false,
-    //             selectedPosition: undefined,
-    //             selectedUuids: [],
-    //         }));
-    //     }
-    // }
-    //
+    function unselectElement (evt: React.MouseEvent<HTMLDivElement, MouseEvent>) {
+        if ((evt.target as any).dataset.click === 'FLOWS') {
+            evt.stopPropagation()
+            const i = CamelDisplayUtil.setIntegrationVisibility(integration, undefined);
+            setIntegration(i);
+            setSelectedStep(undefined);
+            setSelectedPosition(undefined);
+            setSelectedUuids([]);
+        }
+    }
+
     const openSelector = (parentId: string | undefined, parentDsl: string | undefined, showSteps: boolean = true, position?: number | undefined, selectorTabIndex?: string | number) => {
         setShowSelector(true);
         setParentId(parentId || '');
@@ -262,11 +202,7 @@ export const useRouteDesignerHook = () => {
         setSelectedPosition(position);
         setSelectorTabIndex((parentId === undefined && parentDsl === undefined) ? 'kamelet' : 'eip');
     }
-    //
-    // function closeDslSelector = () => {
-    //     this.routeDesigner.setState({showSelector: false})
-    // }
-    //
+
     const onDslSelect = (dsl: DslMetaModel, parentId: string, position?: number | undefined) => {
         switch (dsl.dsl) {
             case 'FromDefinition' :
@@ -324,11 +260,8 @@ export const useRouteDesignerHook = () => {
         setPropertyOnly(false);
         setSelectedUuids([selectedStep.uuid]);
     }
-    //
-    // function onIntegrationUpdate = (i: Integration) => {
-    //     this.routeDesigner.setState({integration: i, propertyOnly: false, showSelector: false, key: Math.random().toString()});
-    // }
-    //
+
+
     const moveElement = (source: string, target: string, asChild: boolean) => {
         const i = CamelDefinitionApiExt.moveRouteElement(integration, source, target, asChild);
         const clone = CamelUtil.cloneIntegration(i);
@@ -370,5 +303,6 @@ export const useRouteDesignerHook = () => {
         }
     }
 
-    return { deleteElement, selectElement, moveElement, onShowDeleteConfirmation, onDslSelect, openSelector, createRouteConfiguration, onCommand, handleKeyDown, handleKeyUp}
+    return { deleteElement, selectElement, moveElement, onShowDeleteConfirmation, onDslSelect, openSelector,
+        createRouteConfiguration, onCommand, handleKeyDown, handleKeyUp, unselectElement}
 }
\ No newline at end of file
diff --git a/karavan-designer/src/designer/utils/CamelUi.tsx b/karavan-designer/src/designer/utils/CamelUi.tsx
index 8d4157c4..cd041e9e 100644
--- a/karavan-designer/src/designer/utils/CamelUi.tsx
+++ b/karavan-designer/src/designer/utils/CamelUi.tsx
@@ -81,7 +81,7 @@ import {
     SplitIcon,
     SpringIcon,
     TerminalIcon,
-    TestingIcon,
+    TestingIcon, ToIcon,
     TransformationIcon,
     ValidationIcon,
     WebserviceIcon,
@@ -705,6 +705,8 @@ export class CamelUi {
         switch (dslName) {
             case 'AggregateDefinition':
                 return <AggregateIcon/>;
+            case 'ToDefinition':
+                return <ToIcon/>;
             case 'ChoiceDefinition' :
                 return <ChoiceIcon/>;
             case 'SplitDefinition' :
diff --git a/karavan-designer/src/designer/utils/KaravanIcons.tsx b/karavan-designer/src/designer/utils/KaravanIcons.tsx
index b7228640..23cc7a69 100644
--- a/karavan-designer/src/designer/utils/KaravanIcons.tsx
+++ b/karavan-designer/src/designer/utils/KaravanIcons.tsx
@@ -495,6 +495,28 @@ export function AggregateIcon() {
     );
 }
 
+export function ToIcon() {
+    return (
+            <svg
+                xmlns="http://www.w3.org/2000/svg"
+                width={800}
+                height={800}
+                viewBox="0 0 32 32"
+                className="icon"
+            >
+                <title>{"plug"}</title>
+                <path d="M24 22v-1h6v-2h-6v-6h6v-2h-6v-1a2 2 0 0 0-2-2h-6a8.007 8.007 0 0 0-7.93 7H2v2h6.07A8.007 8.007 0 0 0 16 24h6a2 2 0 0 0 2-2zm-8 0a6 6 0 0 1 0-12h6v12z" />
+                <path
+                    d="M0 0h32v32H0z"
+                    data-name="&lt;Transparent Rectangle&gt;"
+                    style={{
+                        fill: "none",
+                    }}
+                />
+            </svg>
+    );
+}
+
 export function ChoiceIcon() {
     return (
         <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 700 700" className="icon">


[camel-karavan] 02/06: Fix connections issues #836

Posted by ma...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

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

commit 64a8c66a2da57cabbeab525aa048814b1b703e02
Author: Marat Gubaidullin <ma...@Marats-MacBook-Pro.local>
AuthorDate: Wed Aug 23 18:36:22 2023 -0400

    Fix connections issues #836
---
 karavan-designer/public/example/demo.camel.yaml    | 1706 ++++++++++----------
 .../src/designer/route/DslConnections.tsx          |    2 -
 karavan-designer/src/designer/route/DslElement.tsx |  100 +-
 .../src/designer/route/RouteDesigner.tsx           |   22 +-
 .../src/designer/route/useRouteDesignerHook.tsx    |   24 +-
 5 files changed, 935 insertions(+), 919 deletions(-)

diff --git a/karavan-designer/public/example/demo.camel.yaml b/karavan-designer/public/example/demo.camel.yaml
index f7df5980..14003ed0 100644
--- a/karavan-designer/public/example/demo.camel.yaml
+++ b/karavan-designer/public/example/demo.camel.yaml
@@ -10,859 +10,7 @@
                 id: when-064f
                 steps:
                   - multicast:
-                      id: multicast-38ce
-                      steps:
-                        - bean:
-                            id: bean-3b8e
-                        - log:
-                            message: ${body}
-                            id: log-546f
-                        - loop:
-                            expression: {}
-                            id: loop-4635
-                            steps:
-                              - convertBodyTo:
-                                  id: convertBodyTo-1cae
-            otherwise:
-              id: otherwise-0b09
-              steps:
-                - filter:
-                    expression: {}
-                    id: filter-a02b
-            id: choice-c53c
-        - doTry:
-            id: doTry-8fd5
-            doCatch:
-              - id: doCatch-1071
-              - id: doCatch-c38e
-            steps:
-              - routingSlip:
-                  expression: {}
-                  id: routingSlip-a85a
-- route:
-    id: route-178a
-    from:
-      uri: kamelet:aws-cloudtrail-source
-      id: from-3e7d
-      steps:
-        - multicast:
-            id: multicast-eef7
-            steps:
-              - bean:
-                  id: bean-a5ef
-              - aggregate:
-                  id: aggregate-f5d8
-              - aggregate:
-                  id: aggregate-b9e7
-              - aggregate:
-                  id: aggregate-5eb8
-              - aggregate:
-                  id: aggregate-c57e
-              - aggregate:
-                  id: aggregate-1cd4
-              - bean:
-                  id: bean-72a1
-              - choice:
-                  when:
-                    - expression: {}
-                      id: when-a56b
-                  otherwise:
-                    id: otherwise-9f31
-                  id: choice-1905
-- route:
-    id: route-f435
-    from:
-      uri: kamelet:timer-source
-      id: from-e52c
-      steps:
-        - choice:
-            when:
-              - expression: {}
-                id: when-064f
-                steps:
-                  - multicast:
-                      id: multicast-38ce
-                      steps:
-                        - bean:
-                            id: bean-3b8e
-                        - log:
-                            message: ${body}
-                            id: log-546f
-                        - loop:
-                            expression: {}
-                            id: loop-4635
-                            steps:
-                              - convertBodyTo:
-                                  id: convertBodyTo-1cae
-            otherwise:
-              id: otherwise-0b09
-              steps:
-                - filter:
-                    expression: {}
-                    id: filter-a02b
-            id: choice-c53c
-        - doTry:
-            id: doTry-8fd5
-            doCatch:
-              - id: doCatch-1071
-              - id: doCatch-c38e
-            steps:
-              - routingSlip:
-                  expression: {}
-                  id: routingSlip-a85a
-- route:
-    id: route-178a
-    from:
-      uri: kamelet:aws-cloudtrail-source
-      id: from-3e7d
-      steps:
-        - multicast:
-            id: multicast-eef7
-            steps:
-              - bean:
-                  id: bean-a5ef
-              - aggregate:
-                  id: aggregate-f5d8
-              - aggregate:
-                  id: aggregate-b9e7
-              - aggregate:
-                  id: aggregate-5eb8
-              - aggregate:
-                  id: aggregate-c57e
-              - aggregate:
-                  id: aggregate-1cd4
-              - bean:
-                  id: bean-72a1
-              - choice:
-                  when:
-                    - expression: {}
-                      id: when-a56b
-                  otherwise:
-                    id: otherwise-9f31
-                  id: choice-1905
-- route:
-    id: route-f435
-    from:
-      uri: kamelet:timer-source
-      id: from-e52c
-      steps:
-        - choice:
-            when:
-              - expression: {}
-                id: when-064f
-                steps:
-                  - multicast:
-                      id: multicast-38ce
-                      steps:
-                        - bean:
-                            id: bean-3b8e
-                        - log:
-                            message: ${body}
-                            id: log-546f
-                        - loop:
-                            expression: {}
-                            id: loop-4635
-                            steps:
-                              - convertBodyTo:
-                                  id: convertBodyTo-1cae
-            otherwise:
-              id: otherwise-0b09
-              steps:
-                - filter:
-                    expression: {}
-                    id: filter-a02b
-            id: choice-c53c
-        - doTry:
-            id: doTry-8fd5
-            doCatch:
-              - id: doCatch-1071
-              - id: doCatch-c38e
-            steps:
-              - routingSlip:
-                  expression: {}
-                  id: routingSlip-a85a
-- route:
-    id: route-178a
-    from:
-      uri: kamelet:aws-cloudtrail-source
-      id: from-3e7d
-      steps:
-        - multicast:
-            id: multicast-eef7
-            steps:
-              - bean:
-                  id: bean-a5ef
-              - aggregate:
-                  id: aggregate-f5d8
-              - aggregate:
-                  id: aggregate-b9e7
-              - aggregate:
-                  id: aggregate-5eb8
-              - aggregate:
-                  id: aggregate-c57e
-              - aggregate:
-                  id: aggregate-1cd4
-              - bean:
-                  id: bean-72a1
-              - choice:
-                  when:
-                    - expression: {}
-                      id: when-a56b
-                  otherwise:
-                    id: otherwise-9f31
-                  id: choice-1905
-- route:
-    id: route-f435
-    from:
-      uri: kamelet:timer-source
-      id: from-e52c
-      steps:
-        - choice:
-            when:
-              - expression: {}
-                id: when-064f
-                steps:
-                  - multicast:
-                      id: multicast-38ce
-                      steps:
-                        - bean:
-                            id: bean-3b8e
-                        - log:
-                            message: ${body}
-                            id: log-546f
-                        - loop:
-                            expression: {}
-                            id: loop-4635
-                            steps:
-                              - convertBodyTo:
-                                  id: convertBodyTo-1cae
-            otherwise:
-              id: otherwise-0b09
-              steps:
-                - filter:
-                    expression: {}
-                    id: filter-a02b
-            id: choice-c53c
-        - doTry:
-            id: doTry-8fd5
-            doCatch:
-              - id: doCatch-1071
-              - id: doCatch-c38e
-            steps:
-              - routingSlip:
-                  expression: {}
-                  id: routingSlip-a85a
-- route:
-    id: route-178a
-    from:
-      uri: kamelet:aws-cloudtrail-source
-      id: from-3e7d
-      steps:
-        - multicast:
-            id: multicast-eef7
-            steps:
-              - bean:
-                  id: bean-a5ef
-              - aggregate:
-                  id: aggregate-f5d8
-              - aggregate:
-                  id: aggregate-b9e7
-              - aggregate:
-                  id: aggregate-5eb8
-              - aggregate:
-                  id: aggregate-c57e
-              - aggregate:
-                  id: aggregate-1cd4
-              - bean:
-                  id: bean-72a1
-              - choice:
-                  when:
-                    - expression: {}
-                      id: when-a56b
-                  otherwise:
-                    id: otherwise-9f31
-                  id: choice-1905
-- route:
-    id: route-f435
-    from:
-      uri: kamelet:timer-source
-      id: from-e52c
-      steps:
-        - choice:
-            when:
-              - expression: {}
-                id: when-064f
-                steps:
-                  - multicast:
-                      id: multicast-38ce
-                      steps:
-                        - bean:
-                            id: bean-3b8e
-                        - log:
-                            message: ${body}
-                            id: log-546f
-                        - loop:
-                            expression: {}
-                            id: loop-4635
-                            steps:
-                              - convertBodyTo:
-                                  id: convertBodyTo-1cae
-            otherwise:
-              id: otherwise-0b09
-              steps:
-                - filter:
-                    expression: {}
-                    id: filter-a02b
-            id: choice-c53c
-        - doTry:
-            id: doTry-8fd5
-            doCatch:
-              - id: doCatch-1071
-              - id: doCatch-c38e
-            steps:
-              - routingSlip:
-                  expression: {}
-                  id: routingSlip-a85a
-- route:
-    id: route-178a
-    from:
-      uri: kamelet:aws-cloudtrail-source
-      id: from-3e7d
-      steps:
-        - multicast:
-            id: multicast-eef7
-            steps:
-              - bean:
-                  id: bean-a5ef
-              - aggregate:
-                  id: aggregate-f5d8
-              - aggregate:
-                  id: aggregate-b9e7
-              - aggregate:
-                  id: aggregate-5eb8
-              - aggregate:
-                  id: aggregate-c57e
-              - aggregate:
-                  id: aggregate-1cd4
-              - bean:
-                  id: bean-72a1
-              - choice:
-                  when:
-                    - expression: {}
-                      id: when-a56b
-                  otherwise:
-                    id: otherwise-9f31
-                  id: choice-1905
-- route:
-    id: route-f435
-    from:
-      uri: kamelet:timer-source
-      id: from-e52c
-      steps:
-        - choice:
-            when:
-              - expression: {}
-                id: when-064f
-                steps:
-                  - multicast:
-                      id: multicast-38ce
-                      steps:
-                        - bean:
-                            id: bean-3b8e
-                        - log:
-                            message: ${body}
-                            id: log-546f
-                        - loop:
-                            expression: {}
-                            id: loop-4635
-                            steps:
-                              - convertBodyTo:
-                                  id: convertBodyTo-1cae
-            otherwise:
-              id: otherwise-0b09
-              steps:
-                - filter:
-                    expression: {}
-                    id: filter-a02b
-            id: choice-c53c
-        - doTry:
-            id: doTry-8fd5
-            doCatch:
-              - id: doCatch-1071
-              - id: doCatch-c38e
-            steps:
-              - routingSlip:
-                  expression: {}
-                  id: routingSlip-a85a
-- route:
-    id: route-178a
-    from:
-      uri: kamelet:aws-cloudtrail-source
-      id: from-3e7d
-      steps:
-        - multicast:
-            id: multicast-eef7
-            steps:
-              - bean:
-                  id: bean-a5ef
-              - aggregate:
-                  id: aggregate-f5d8
-              - aggregate:
-                  id: aggregate-b9e7
-              - aggregate:
-                  id: aggregate-5eb8
-              - aggregate:
-                  id: aggregate-c57e
-              - aggregate:
-                  id: aggregate-1cd4
-              - bean:
-                  id: bean-72a1
-              - choice:
-                  when:
-                    - expression: {}
-                      id: when-a56b
-                  otherwise:
-                    id: otherwise-9f31
-                  id: choice-1905
-- route:
-    id: route-f435
-    from:
-      uri: kamelet:timer-source
-      id: from-e52c
-      steps:
-        - choice:
-            when:
-              - expression: {}
-                id: when-064f
-                steps:
-                  - multicast:
-                      id: multicast-38ce
-                      steps:
-                        - bean:
-                            id: bean-3b8e
-                        - log:
-                            message: ${body}
-                            id: log-546f
-                        - loop:
-                            expression: {}
-                            id: loop-4635
-                            steps:
-                              - convertBodyTo:
-                                  id: convertBodyTo-1cae
-            otherwise:
-              id: otherwise-0b09
-              steps:
-                - filter:
-                    expression: {}
-                    id: filter-a02b
-            id: choice-c53c
-        - doTry:
-            id: doTry-8fd5
-            doCatch:
-              - id: doCatch-1071
-              - id: doCatch-c38e
-            steps:
-              - routingSlip:
-                  expression: {}
-                  id: routingSlip-a85a
-- route:
-    id: route-178a
-    from:
-      uri: kamelet:aws-cloudtrail-source
-      id: from-3e7d
-      steps:
-        - multicast:
-            id: multicast-eef7
-            steps:
-              - bean:
-                  id: bean-a5ef
-              - aggregate:
-                  id: aggregate-f5d8
-              - aggregate:
-                  id: aggregate-b9e7
-              - aggregate:
-                  id: aggregate-5eb8
-              - aggregate:
-                  id: aggregate-c57e
-              - aggregate:
-                  id: aggregate-1cd4
-              - bean:
-                  id: bean-72a1
-              - choice:
-                  when:
-                    - expression: {}
-                      id: when-a56b
-                  otherwise:
-                    id: otherwise-9f31
-                  id: choice-1905
-- route:
-    id: route-f435
-    from:
-      uri: kamelet:timer-source
-      id: from-e52c
-      steps:
-        - choice:
-            when:
-              - expression: {}
-                id: when-064f
-                steps:
-                  - multicast:
-                      id: multicast-38ce
-                      steps:
-                        - bean:
-                            id: bean-3b8e
-                        - log:
-                            message: ${body}
-                            id: log-546f
-                        - loop:
-                            expression: {}
-                            id: loop-4635
-                            steps:
-                              - convertBodyTo:
-                                  id: convertBodyTo-1cae
-            otherwise:
-              id: otherwise-0b09
-              steps:
-                - filter:
-                    expression: {}
-                    id: filter-a02b
-            id: choice-c53c
-        - doTry:
-            id: doTry-8fd5
-            doCatch:
-              - id: doCatch-1071
-              - id: doCatch-c38e
-            steps:
-              - routingSlip:
-                  expression: {}
-                  id: routingSlip-a85a
-- route:
-    id: route-178a
-    from:
-      uri: kamelet:aws-cloudtrail-source
-      id: from-3e7d
-      steps:
-        - multicast:
-            id: multicast-eef7
-            steps:
-              - bean:
-                  id: bean-a5ef
-              - aggregate:
-                  id: aggregate-f5d8
-              - aggregate:
-                  id: aggregate-b9e7
-              - aggregate:
-                  id: aggregate-5eb8
-              - aggregate:
-                  id: aggregate-c57e
-              - aggregate:
-                  id: aggregate-1cd4
-              - bean:
-                  id: bean-72a1
-              - choice:
-                  when:
-                    - expression: {}
-                      id: when-a56b
-                  otherwise:
-                    id: otherwise-9f31
-                  id: choice-1905
-- route:
-    id: route-f435
-    from:
-      uri: kamelet:timer-source
-      id: from-e52c
-      steps:
-        - choice:
-            when:
-              - expression: {}
-                id: when-064f
-                steps:
-                  - multicast:
-                      id: multicast-38ce
-                      steps:
-                        - bean:
-                            id: bean-3b8e
-                        - log:
-                            message: ${body}
-                            id: log-546f
-                        - loop:
-                            expression: {}
-                            id: loop-4635
-                            steps:
-                              - convertBodyTo:
-                                  id: convertBodyTo-1cae
-            otherwise:
-              id: otherwise-0b09
-              steps:
-                - filter:
-                    expression: {}
-                    id: filter-a02b
-            id: choice-c53c
-        - doTry:
-            id: doTry-8fd5
-            doCatch:
-              - id: doCatch-1071
-              - id: doCatch-c38e
-            steps:
-              - routingSlip:
-                  expression: {}
-                  id: routingSlip-a85a
-- route:
-    id: route-178a
-    from:
-      uri: kamelet:aws-cloudtrail-source
-      id: from-3e7d
-      steps:
-        - multicast:
-            id: multicast-eef7
-            steps:
-              - bean:
-                  id: bean-a5ef
-              - aggregate:
-                  id: aggregate-f5d8
-              - aggregate:
-                  id: aggregate-b9e7
-              - aggregate:
-                  id: aggregate-5eb8
-              - aggregate:
-                  id: aggregate-c57e
-              - aggregate:
-                  id: aggregate-1cd4
-              - bean:
-                  id: bean-72a1
-              - choice:
-                  when:
-                    - expression: {}
-                      id: when-a56b
-                  otherwise:
-                    id: otherwise-9f31
-                  id: choice-1905
-- route:
-    id: route-f435
-    from:
-      uri: kamelet:timer-source
-      id: from-e52c
-      steps:
-        - choice:
-            when:
-              - expression: {}
-                id: when-064f
-                steps:
-                  - multicast:
-                      id: multicast-38ce
-                      steps:
-                        - bean:
-                            id: bean-3b8e
-                        - log:
-                            message: ${body}
-                            id: log-546f
-                        - loop:
-                            expression: {}
-                            id: loop-4635
-                            steps:
-                              - convertBodyTo:
-                                  id: convertBodyTo-1cae
-            otherwise:
-              id: otherwise-0b09
-              steps:
-                - filter:
-                    expression: {}
-                    id: filter-a02b
-            id: choice-c53c
-        - doTry:
-            id: doTry-8fd5
-            doCatch:
-              - id: doCatch-1071
-              - id: doCatch-c38e
-            steps:
-              - routingSlip:
-                  expression: {}
-                  id: routingSlip-a85a
-- route:
-    id: route-178a
-    from:
-      uri: kamelet:aws-cloudtrail-source
-      id: from-3e7d
-      steps:
-        - multicast:
-            id: multicast-eef7
-            steps:
-              - bean:
-                  id: bean-a5ef
-              - aggregate:
-                  id: aggregate-f5d8
-              - aggregate:
-                  id: aggregate-b9e7
-              - aggregate:
-                  id: aggregate-5eb8
-              - aggregate:
-                  id: aggregate-c57e
-              - aggregate:
-                  id: aggregate-1cd4
-              - bean:
-                  id: bean-72a1
-              - choice:
-                  when:
-                    - expression: {}
-                      id: when-a56b
-                  otherwise:
-                    id: otherwise-9f31
-                  id: choice-1905
-- route:
-    id: route-f435
-    from:
-      uri: kamelet:timer-source
-      id: from-e52c
-      steps:
-        - choice:
-            when:
-              - expression: {}
-                id: when-064f
-                steps:
-                  - multicast:
-                      id: multicast-38ce
-                      steps:
-                        - bean:
-                            id: bean-3b8e
-                        - log:
-                            message: ${body}
-                            id: log-546f
-                        - loop:
-                            expression: {}
-                            id: loop-4635
-                            steps:
-                              - convertBodyTo:
-                                  id: convertBodyTo-1cae
-            otherwise:
-              id: otherwise-0b09
-              steps:
-                - filter:
-                    expression: {}
-                    id: filter-a02b
-            id: choice-c53c
-        - doTry:
-            id: doTry-8fd5
-            doCatch:
-              - id: doCatch-1071
-              - id: doCatch-c38e
-            steps:
-              - routingSlip:
-                  expression: {}
-                  id: routingSlip-a85a
-- route:
-    id: route-178a
-    from:
-      uri: kamelet:aws-cloudtrail-source
-      id: from-3e7d
-      steps:
-        - multicast:
-            id: multicast-eef7
-            steps:
-              - bean:
-                  id: bean-a5ef
-              - aggregate:
-                  id: aggregate-f5d8
-              - aggregate:
-                  id: aggregate-b9e7
-              - aggregate:
-                  id: aggregate-5eb8
-              - aggregate:
-                  id: aggregate-c57e
-              - aggregate:
-                  id: aggregate-1cd4
-              - bean:
-                  id: bean-72a1
-              - choice:
-                  when:
-                    - expression: {}
-                      id: when-a56b
-                  otherwise:
-                    id: otherwise-9f31
-                  id: choice-1905
-- route:
-    id: route-f435
-    from:
-      uri: kamelet:timer-source
-      id: from-e52c
-      steps:
-        - choice:
-            when:
-              - expression: {}
-                id: when-064f
-                steps:
-                  - multicast:
-                      id: multicast-38ce
-                      steps:
-                        - bean:
-                            id: bean-3b8e
-                        - log:
-                            message: ${body}
-                            id: log-546f
-                        - loop:
-                            expression: {}
-                            id: loop-4635
-                            steps:
-                              - convertBodyTo:
-                                  id: convertBodyTo-1cae
-            otherwise:
-              id: otherwise-0b09
-              steps:
-                - filter:
-                    expression: {}
-                    id: filter-a02b
-            id: choice-c53c
-        - doTry:
-            id: doTry-8fd5
-            doCatch:
-              - id: doCatch-1071
-              - id: doCatch-c38e
-            steps:
-              - routingSlip:
-                  expression: {}
-                  id: routingSlip-a85a
-- route:
-    id: route-178a
-    from:
-      uri: kamelet:aws-cloudtrail-source
-      id: from-3e7d
-      steps:
-        - multicast:
-            id: multicast-eef7
-            steps:
-              - bean:
-                  id: bean-a5ef
-              - aggregate:
-                  id: aggregate-f5d8
-              - aggregate:
-                  id: aggregate-b9e7
-              - aggregate:
-                  id: aggregate-5eb8
-              - aggregate:
-                  id: aggregate-c57e
-              - aggregate:
-                  id: aggregate-1cd4
-              - bean:
-                  id: bean-72a1
-              - choice:
-                  when:
-                    - expression: {}
-                      id: when-a56b
-                  otherwise:
-                    id: otherwise-9f31
-                  id: choice-1905
-- route:
-    id: route-f435
-    from:
-      uri: kamelet:timer-source
-      id: from-e52c
-      steps:
-        - choice:
-            when:
-              - expression: {}
-                id: when-064f
-                steps:
-                  - multicast:
-                      id: multicast-38ce
+                      id: multicast-38cea
                       steps:
                         - bean:
                             id: bean-3b8e
@@ -921,3 +69,855 @@
                   otherwise:
                     id: otherwise-9f31
                   id: choice-1905
+#- route:
+#    id: route-f435
+#    from:
+#      uri: kamelet:timer-source
+#      id: from-e52c
+#      steps:
+#        - choice:
+#            when:
+#              - expression: {}
+#                id: when-064f
+#                steps:
+#                  - multicast:
+#                      id: multicast-38ce
+#                      steps:
+#                        - bean:
+#                            id: bean-3b8e
+#                        - log:
+#                            message: ${body}
+#                            id: log-546f
+#                        - loop:
+#                            expression: {}
+#                            id: loop-4635
+#                            steps:
+#                              - convertBodyTo:
+#                                  id: convertBodyTo-1cae
+#            otherwise:
+#              id: otherwise-0b09
+#              steps:
+#                - filter:
+#                    expression: {}
+#                    id: filter-a02b
+#            id: choice-c53c
+#        - doTry:
+#            id: doTry-8fd5
+#            doCatch:
+#              - id: doCatch-1071
+#              - id: doCatch-c38e
+#            steps:
+#              - routingSlip:
+#                  expression: {}
+#                  id: routingSlip-a85a
+#- route:
+#    id: route-178a
+#    from:
+#      uri: kamelet:aws-cloudtrail-source
+#      id: from-3e7d
+#      steps:
+#        - multicast:
+#            id: multicast-eef7
+#            steps:
+#              - bean:
+#                  id: bean-a5ef
+#              - aggregate:
+#                  id: aggregate-f5d8
+#              - aggregate:
+#                  id: aggregate-b9e7
+#              - aggregate:
+#                  id: aggregate-5eb8
+#              - aggregate:
+#                  id: aggregate-c57e
+#              - aggregate:
+#                  id: aggregate-1cd4
+#              - bean:
+#                  id: bean-72a1
+#              - choice:
+#                  when:
+#                    - expression: {}
+#                      id: when-a56b
+#                  otherwise:
+#                    id: otherwise-9f31
+#                  id: choice-1905
+#- route:
+#    id: route-f435
+#    from:
+#      uri: kamelet:timer-source
+#      id: from-e52c
+#      steps:
+#        - choice:
+#            when:
+#              - expression: {}
+#                id: when-064f
+#                steps:
+#                  - multicast:
+#                      id: multicast-38ce
+#                      steps:
+#                        - bean:
+#                            id: bean-3b8e
+#                        - log:
+#                            message: ${body}
+#                            id: log-546f
+#                        - loop:
+#                            expression: {}
+#                            id: loop-4635
+#                            steps:
+#                              - convertBodyTo:
+#                                  id: convertBodyTo-1cae
+#            otherwise:
+#              id: otherwise-0b09
+#              steps:
+#                - filter:
+#                    expression: {}
+#                    id: filter-a02b
+#            id: choice-c53c
+#        - doTry:
+#            id: doTry-8fd5
+#            doCatch:
+#              - id: doCatch-1071
+#              - id: doCatch-c38e
+#            steps:
+#              - routingSlip:
+#                  expression: {}
+#                  id: routingSlip-a85a
+#- route:
+#    id: route-178a
+#    from:
+#      uri: kamelet:aws-cloudtrail-source
+#      id: from-3e7d
+#      steps:
+#        - multicast:
+#            id: multicast-eef7
+#            steps:
+#              - bean:
+#                  id: bean-a5ef
+#              - aggregate:
+#                  id: aggregate-f5d8
+#              - aggregate:
+#                  id: aggregate-b9e7
+#              - aggregate:
+#                  id: aggregate-5eb8
+#              - aggregate:
+#                  id: aggregate-c57e
+#              - aggregate:
+#                  id: aggregate-1cd4
+#              - bean:
+#                  id: bean-72a1
+#              - choice:
+#                  when:
+#                    - expression: {}
+#                      id: when-a56b
+#                  otherwise:
+#                    id: otherwise-9f31
+#                  id: choice-1905
+#- route:
+#    id: route-f435
+#    from:
+#      uri: kamelet:timer-source
+#      id: from-e52c
+#      steps:
+#        - choice:
+#            when:
+#              - expression: {}
+#                id: when-064f
+#                steps:
+#                  - multicast:
+#                      id: multicast-38ce
+#                      steps:
+#                        - bean:
+#                            id: bean-3b8e
+#                        - log:
+#                            message: ${body}
+#                            id: log-546f
+#                        - loop:
+#                            expression: {}
+#                            id: loop-4635
+#                            steps:
+#                              - convertBodyTo:
+#                                  id: convertBodyTo-1cae
+#            otherwise:
+#              id: otherwise-0b09
+#              steps:
+#                - filter:
+#                    expression: {}
+#                    id: filter-a02b
+#            id: choice-c53c
+#        - doTry:
+#            id: doTry-8fd5
+#            doCatch:
+#              - id: doCatch-1071
+#              - id: doCatch-c38e
+#            steps:
+#              - routingSlip:
+#                  expression: {}
+#                  id: routingSlip-a85a
+#- route:
+#    id: route-178a
+#    from:
+#      uri: kamelet:aws-cloudtrail-source
+#      id: from-3e7d
+#      steps:
+#        - multicast:
+#            id: multicast-eef7
+#            steps:
+#              - bean:
+#                  id: bean-a5ef
+#              - aggregate:
+#                  id: aggregate-f5d8
+#              - aggregate:
+#                  id: aggregate-b9e7
+#              - aggregate:
+#                  id: aggregate-5eb8
+#              - aggregate:
+#                  id: aggregate-c57e
+#              - aggregate:
+#                  id: aggregate-1cd4
+#              - bean:
+#                  id: bean-72a1
+#              - choice:
+#                  when:
+#                    - expression: {}
+#                      id: when-a56b
+#                  otherwise:
+#                    id: otherwise-9f31
+#                  id: choice-1905
+#- route:
+#    id: route-f435
+#    from:
+#      uri: kamelet:timer-source
+#      id: from-e52c
+#      steps:
+#        - choice:
+#            when:
+#              - expression: {}
+#                id: when-064f
+#                steps:
+#                  - multicast:
+#                      id: multicast-38ce
+#                      steps:
+#                        - bean:
+#                            id: bean-3b8e
+#                        - log:
+#                            message: ${body}
+#                            id: log-546f
+#                        - loop:
+#                            expression: {}
+#                            id: loop-4635
+#                            steps:
+#                              - convertBodyTo:
+#                                  id: convertBodyTo-1cae
+#            otherwise:
+#              id: otherwise-0b09
+#              steps:
+#                - filter:
+#                    expression: {}
+#                    id: filter-a02b
+#            id: choice-c53c
+#        - doTry:
+#            id: doTry-8fd5
+#            doCatch:
+#              - id: doCatch-1071
+#              - id: doCatch-c38e
+#            steps:
+#              - routingSlip:
+#                  expression: {}
+#                  id: routingSlip-a85a
+#- route:
+#    id: route-178a
+#    from:
+#      uri: kamelet:aws-cloudtrail-source
+#      id: from-3e7d
+#      steps:
+#        - multicast:
+#            id: multicast-eef7
+#            steps:
+#              - bean:
+#                  id: bean-a5ef
+#              - aggregate:
+#                  id: aggregate-f5d8
+#              - aggregate:
+#                  id: aggregate-b9e7
+#              - aggregate:
+#                  id: aggregate-5eb8
+#              - aggregate:
+#                  id: aggregate-c57e
+#              - aggregate:
+#                  id: aggregate-1cd4
+#              - bean:
+#                  id: bean-72a1
+#              - choice:
+#                  when:
+#                    - expression: {}
+#                      id: when-a56b
+#                  otherwise:
+#                    id: otherwise-9f31
+#                  id: choice-1905
+#- route:
+#    id: route-f435
+#    from:
+#      uri: kamelet:timer-source
+#      id: from-e52c
+#      steps:
+#        - choice:
+#            when:
+#              - expression: {}
+#                id: when-064f
+#                steps:
+#                  - multicast:
+#                      id: multicast-38ce
+#                      steps:
+#                        - bean:
+#                            id: bean-3b8e
+#                        - log:
+#                            message: ${body}
+#                            id: log-546f
+#                        - loop:
+#                            expression: {}
+#                            id: loop-4635
+#                            steps:
+#                              - convertBodyTo:
+#                                  id: convertBodyTo-1cae
+#            otherwise:
+#              id: otherwise-0b09
+#              steps:
+#                - filter:
+#                    expression: {}
+#                    id: filter-a02b
+#            id: choice-c53c
+#        - doTry:
+#            id: doTry-8fd5
+#            doCatch:
+#              - id: doCatch-1071
+#              - id: doCatch-c38e
+#            steps:
+#              - routingSlip:
+#                  expression: {}
+#                  id: routingSlip-a85a
+#- route:
+#    id: route-178a
+#    from:
+#      uri: kamelet:aws-cloudtrail-source
+#      id: from-3e7d
+#      steps:
+#        - multicast:
+#            id: multicast-eef7
+#            steps:
+#              - bean:
+#                  id: bean-a5ef
+#              - aggregate:
+#                  id: aggregate-f5d8
+#              - aggregate:
+#                  id: aggregate-b9e7
+#              - aggregate:
+#                  id: aggregate-5eb8
+#              - aggregate:
+#                  id: aggregate-c57e
+#              - aggregate:
+#                  id: aggregate-1cd4
+#              - bean:
+#                  id: bean-72a1
+#              - choice:
+#                  when:
+#                    - expression: {}
+#                      id: when-a56b
+#                  otherwise:
+#                    id: otherwise-9f31
+#                  id: choice-1905
+#- route:
+#    id: route-f435
+#    from:
+#      uri: kamelet:timer-source
+#      id: from-e52c
+#      steps:
+#        - choice:
+#            when:
+#              - expression: {}
+#                id: when-064f
+#                steps:
+#                  - multicast:
+#                      id: multicast-38ce
+#                      steps:
+#                        - bean:
+#                            id: bean-3b8e
+#                        - log:
+#                            message: ${body}
+#                            id: log-546f
+#                        - loop:
+#                            expression: {}
+#                            id: loop-4635
+#                            steps:
+#                              - convertBodyTo:
+#                                  id: convertBodyTo-1cae
+#            otherwise:
+#              id: otherwise-0b09
+#              steps:
+#                - filter:
+#                    expression: {}
+#                    id: filter-a02b
+#            id: choice-c53c
+#        - doTry:
+#            id: doTry-8fd5
+#            doCatch:
+#              - id: doCatch-1071
+#              - id: doCatch-c38e
+#            steps:
+#              - routingSlip:
+#                  expression: {}
+#                  id: routingSlip-a85a
+#- route:
+#    id: route-178a
+#    from:
+#      uri: kamelet:aws-cloudtrail-source
+#      id: from-3e7d
+#      steps:
+#        - multicast:
+#            id: multicast-eef7
+#            steps:
+#              - bean:
+#                  id: bean-a5ef
+#              - aggregate:
+#                  id: aggregate-f5d8
+#              - aggregate:
+#                  id: aggregate-b9e7
+#              - aggregate:
+#                  id: aggregate-5eb8
+#              - aggregate:
+#                  id: aggregate-c57e
+#              - aggregate:
+#                  id: aggregate-1cd4
+#              - bean:
+#                  id: bean-72a1
+#              - choice:
+#                  when:
+#                    - expression: {}
+#                      id: when-a56b
+#                  otherwise:
+#                    id: otherwise-9f31
+#                  id: choice-1905
+#- route:
+#    id: route-f435
+#    from:
+#      uri: kamelet:timer-source
+#      id: from-e52c
+#      steps:
+#        - choice:
+#            when:
+#              - expression: {}
+#                id: when-064f
+#                steps:
+#                  - multicast:
+#                      id: multicast-38ce
+#                      steps:
+#                        - bean:
+#                            id: bean-3b8e
+#                        - log:
+#                            message: ${body}
+#                            id: log-546f
+#                        - loop:
+#                            expression: {}
+#                            id: loop-4635
+#                            steps:
+#                              - convertBodyTo:
+#                                  id: convertBodyTo-1cae
+#            otherwise:
+#              id: otherwise-0b09
+#              steps:
+#                - filter:
+#                    expression: {}
+#                    id: filter-a02b
+#            id: choice-c53c
+#        - doTry:
+#            id: doTry-8fd5
+#            doCatch:
+#              - id: doCatch-1071
+#              - id: doCatch-c38e
+#            steps:
+#              - routingSlip:
+#                  expression: {}
+#                  id: routingSlip-a85a
+#- route:
+#    id: route-178a
+#    from:
+#      uri: kamelet:aws-cloudtrail-source
+#      id: from-3e7d
+#      steps:
+#        - multicast:
+#            id: multicast-eef7
+#            steps:
+#              - bean:
+#                  id: bean-a5ef
+#              - aggregate:
+#                  id: aggregate-f5d8
+#              - aggregate:
+#                  id: aggregate-b9e7
+#              - aggregate:
+#                  id: aggregate-5eb8
+#              - aggregate:
+#                  id: aggregate-c57e
+#              - aggregate:
+#                  id: aggregate-1cd4
+#              - bean:
+#                  id: bean-72a1
+#              - choice:
+#                  when:
+#                    - expression: {}
+#                      id: when-a56b
+#                  otherwise:
+#                    id: otherwise-9f31
+#                  id: choice-1905
+#- route:
+#    id: route-f435
+#    from:
+#      uri: kamelet:timer-source
+#      id: from-e52c
+#      steps:
+#        - choice:
+#            when:
+#              - expression: {}
+#                id: when-064f
+#                steps:
+#                  - multicast:
+#                      id: multicast-38ce
+#                      steps:
+#                        - bean:
+#                            id: bean-3b8e
+#                        - log:
+#                            message: ${body}
+#                            id: log-546f
+#                        - loop:
+#                            expression: {}
+#                            id: loop-4635
+#                            steps:
+#                              - convertBodyTo:
+#                                  id: convertBodyTo-1cae
+#            otherwise:
+#              id: otherwise-0b09
+#              steps:
+#                - filter:
+#                    expression: {}
+#                    id: filter-a02b
+#            id: choice-c53c
+#        - doTry:
+#            id: doTry-8fd5
+#            doCatch:
+#              - id: doCatch-1071
+#              - id: doCatch-c38e
+#            steps:
+#              - routingSlip:
+#                  expression: {}
+#                  id: routingSlip-a85a
+#- route:
+#    id: route-178a
+#    from:
+#      uri: kamelet:aws-cloudtrail-source
+#      id: from-3e7d
+#      steps:
+#        - multicast:
+#            id: multicast-eef7
+#            steps:
+#              - bean:
+#                  id: bean-a5ef
+#              - aggregate:
+#                  id: aggregate-f5d8
+#              - aggregate:
+#                  id: aggregate-b9e7
+#              - aggregate:
+#                  id: aggregate-5eb8
+#              - aggregate:
+#                  id: aggregate-c57e
+#              - aggregate:
+#                  id: aggregate-1cd4
+#              - bean:
+#                  id: bean-72a1
+#              - choice:
+#                  when:
+#                    - expression: {}
+#                      id: when-a56b
+#                  otherwise:
+#                    id: otherwise-9f31
+#                  id: choice-1905
+#- route:
+#    id: route-f435
+#    from:
+#      uri: kamelet:timer-source
+#      id: from-e52c
+#      steps:
+#        - choice:
+#            when:
+#              - expression: {}
+#                id: when-064f
+#                steps:
+#                  - multicast:
+#                      id: multicast-38ce
+#                      steps:
+#                        - bean:
+#                            id: bean-3b8e
+#                        - log:
+#                            message: ${body}
+#                            id: log-546f
+#                        - loop:
+#                            expression: {}
+#                            id: loop-4635
+#                            steps:
+#                              - convertBodyTo:
+#                                  id: convertBodyTo-1cae
+#            otherwise:
+#              id: otherwise-0b09
+#              steps:
+#                - filter:
+#                    expression: {}
+#                    id: filter-a02b
+#            id: choice-c53c
+#        - doTry:
+#            id: doTry-8fd5
+#            doCatch:
+#              - id: doCatch-1071
+#              - id: doCatch-c38e
+#            steps:
+#              - routingSlip:
+#                  expression: {}
+#                  id: routingSlip-a85a
+#- route:
+#    id: route-178a
+#    from:
+#      uri: kamelet:aws-cloudtrail-source
+#      id: from-3e7d
+#      steps:
+#        - multicast:
+#            id: multicast-eef7
+#            steps:
+#              - bean:
+#                  id: bean-a5ef
+#              - aggregate:
+#                  id: aggregate-f5d8
+#              - aggregate:
+#                  id: aggregate-b9e7
+#              - aggregate:
+#                  id: aggregate-5eb8
+#              - aggregate:
+#                  id: aggregate-c57e
+#              - aggregate:
+#                  id: aggregate-1cd4
+#              - bean:
+#                  id: bean-72a1
+#              - choice:
+#                  when:
+#                    - expression: {}
+#                      id: when-a56b
+#                  otherwise:
+#                    id: otherwise-9f31
+#                  id: choice-1905
+#- route:
+#    id: route-f435
+#    from:
+#      uri: kamelet:timer-source
+#      id: from-e52c
+#      steps:
+#        - choice:
+#            when:
+#              - expression: {}
+#                id: when-064f
+#                steps:
+#                  - multicast:
+#                      id: multicast-38ce
+#                      steps:
+#                        - bean:
+#                            id: bean-3b8e
+#                        - log:
+#                            message: ${body}
+#                            id: log-546f
+#                        - loop:
+#                            expression: {}
+#                            id: loop-4635
+#                            steps:
+#                              - convertBodyTo:
+#                                  id: convertBodyTo-1cae
+#            otherwise:
+#              id: otherwise-0b09
+#              steps:
+#                - filter:
+#                    expression: {}
+#                    id: filter-a02b
+#            id: choice-c53c
+#        - doTry:
+#            id: doTry-8fd5
+#            doCatch:
+#              - id: doCatch-1071
+#              - id: doCatch-c38e
+#            steps:
+#              - routingSlip:
+#                  expression: {}
+#                  id: routingSlip-a85a
+#- route:
+#    id: route-178a
+#    from:
+#      uri: kamelet:aws-cloudtrail-source
+#      id: from-3e7d
+#      steps:
+#        - multicast:
+#            id: multicast-eef7
+#            steps:
+#              - bean:
+#                  id: bean-a5ef
+#              - aggregate:
+#                  id: aggregate-f5d8
+#              - aggregate:
+#                  id: aggregate-b9e7
+#              - aggregate:
+#                  id: aggregate-5eb8
+#              - aggregate:
+#                  id: aggregate-c57e
+#              - aggregate:
+#                  id: aggregate-1cd4
+#              - bean:
+#                  id: bean-72a1
+#              - choice:
+#                  when:
+#                    - expression: {}
+#                      id: when-a56b
+#                  otherwise:
+#                    id: otherwise-9f31
+#                  id: choice-1905
+#- route:
+#    id: route-f435
+#    from:
+#      uri: kamelet:timer-source
+#      id: from-e52c
+#      steps:
+#        - choice:
+#            when:
+#              - expression: {}
+#                id: when-064f
+#                steps:
+#                  - multicast:
+#                      id: multicast-38ce
+#                      steps:
+#                        - bean:
+#                            id: bean-3b8e
+#                        - log:
+#                            message: ${body}
+#                            id: log-546f
+#                        - loop:
+#                            expression: {}
+#                            id: loop-4635
+#                            steps:
+#                              - convertBodyTo:
+#                                  id: convertBodyTo-1cae
+#            otherwise:
+#              id: otherwise-0b09
+#              steps:
+#                - filter:
+#                    expression: {}
+#                    id: filter-a02b
+#            id: choice-c53c
+#        - doTry:
+#            id: doTry-8fd5
+#            doCatch:
+#              - id: doCatch-1071
+#              - id: doCatch-c38e
+#            steps:
+#              - routingSlip:
+#                  expression: {}
+#                  id: routingSlip-a85a
+#- route:
+#    id: route-178a
+#    from:
+#      uri: kamelet:aws-cloudtrail-source
+#      id: from-3e7d
+#      steps:
+#        - multicast:
+#            id: multicast-eef7
+#            steps:
+#              - bean:
+#                  id: bean-a5ef
+#              - aggregate:
+#                  id: aggregate-f5d8
+#              - aggregate:
+#                  id: aggregate-b9e7
+#              - aggregate:
+#                  id: aggregate-5eb8
+#              - aggregate:
+#                  id: aggregate-c57e
+#              - aggregate:
+#                  id: aggregate-1cd4
+#              - bean:
+#                  id: bean-72a1
+#              - choice:
+#                  when:
+#                    - expression: {}
+#                      id: when-a56b
+#                  otherwise:
+#                    id: otherwise-9f31
+#                  id: choice-1905
+#- route:
+#    id: route-f435
+#    from:
+#      uri: kamelet:timer-source
+#      id: from-e52c
+#      steps:
+#        - choice:
+#            when:
+#              - expression: {}
+#                id: when-064f
+#                steps:
+#                  - multicast:
+#                      id: multicast-38ce
+#                      steps:
+#                        - bean:
+#                            id: bean-3b8e
+#                        - log:
+#                            message: ${body}
+#                            id: log-546f
+#                        - loop:
+#                            expression: {}
+#                            id: loop-4635
+#                            steps:
+#                              - convertBodyTo:
+#                                  id: convertBodyTo-1cae
+#            otherwise:
+#              id: otherwise-0b09
+#              steps:
+#                - filter:
+#                    expression: {}
+#                    id: filter-a02b
+#            id: choice-c53c
+#        - doTry:
+#            id: doTry-8fd5
+#            doCatch:
+#              - id: doCatch-1071
+#              - id: doCatch-c38e
+#            steps:
+#              - routingSlip:
+#                  expression: {}
+#                  id: routingSlip-a85a
+#- route:
+#    id: route-178a
+#    from:
+#      uri: kamelet:aws-cloudtrail-source
+#      id: from-3e7d
+#      steps:
+#        - multicast:
+#            id: multicast-eef7
+#            steps:
+#              - bean:
+#                  id: bean-a5ef
+#              - aggregate:
+#                  id: aggregate-f5d8
+#              - aggregate:
+#                  id: aggregate-b9e7
+#              - aggregate:
+#                  id: aggregate-5eb8
+#              - aggregate:
+#                  id: aggregate-c57e
+#              - aggregate:
+#                  id: aggregate-1cd4
+#              - bean:
+#                  id: bean-72a1
+#              - choice:
+#                  when:
+#                    - expression: {}
+#                      id: when-a56b
+#                  otherwise:
+#                    id: otherwise-9f31
+#                  id: choice-1905
diff --git a/karavan-designer/src/designer/route/DslConnections.tsx b/karavan-designer/src/designer/route/DslConnections.tsx
index 7a58e30f..a2505f4f 100644
--- a/karavan-designer/src/designer/route/DslConnections.tsx
+++ b/karavan-designer/src/designer/route/DslConnections.tsx
@@ -81,8 +81,6 @@ export const DslConnections = () => {
             const lineX2 = fromX - r * 2 + 7;
             const lineY2 = fromY;
 
-            const imageX = incomingX - r + 5;
-            const imageY = fromY - r + 5;
             return (
                 <g key={pos.step.uuid + "-incoming"}>
                     <circle cx={incomingX} cy={fromY} r={r} className="circle-incoming"/>
diff --git a/karavan-designer/src/designer/route/DslElement.tsx b/karavan-designer/src/designer/route/DslElement.tsx
index 19554e03..5cdfabf5 100644
--- a/karavan-designer/src/designer/route/DslElement.tsx
+++ b/karavan-designer/src/designer/route/DslElement.tsx
@@ -45,22 +45,22 @@ interface Props {
 export const DslElement = (props: Props) => {
 
     const ref = React.createRef();
-    const { deleteElement, selectElement, moveElement, onShowDeleteConfirmation, openSelector } = useRouteDesignerHook();
+    const {deleteElement, selectElement, moveElement, onShowDeleteConfirmation, openSelector} = useRouteDesignerHook();
 
     const [integration, setIntegration] = useIntegrationStore((s) => [s.integration, s.setIntegration], shallow)
 
-    const [showDeleteConfirmation, propertyOnly,deleteMessage, selectedUuids,clipboardSteps,selectedStep,showMoveConfirmation,
-        setShowDeleteConfirmation,setPropertyOnly,setDeleteMessage, setSelectedUuids, setClipboardSteps,setPosition,setShowMoveConfirmation,
+    const [showDeleteConfirmation, propertyOnly, deleteMessage, selectedUuids, clipboardSteps, selectedStep, showMoveConfirmation,
+        setShowDeleteConfirmation, setPropertyOnly, setDeleteMessage, setSelectedUuids, setClipboardSteps, setPosition, setShowMoveConfirmation,
         width, height, top, left] = useDesignerStore((s) =>
-        [s.showDeleteConfirmation, s.propertyOnly,s.deleteMessage, s.selectedUuids,s.clipboardSteps, s.selectedStep, s.showMoveConfirmation,
-            s.setShowDeleteConfirmation,s.setPropertyOnly,s.setDeleteMessage, s.setSelectedUuids, s.setClipboardSteps, s.setPosition, s.setShowMoveConfirmation,
+        [s.showDeleteConfirmation, s.propertyOnly, s.deleteMessage, s.selectedUuids, s.clipboardSteps, s.selectedStep, s.showMoveConfirmation,
+            s.setShowDeleteConfirmation, s.setPropertyOnly, s.setDeleteMessage, s.setSelectedUuids, s.setClipboardSteps, s.setPosition, s.setShowMoveConfirmation,
             s.width, s.height, s.top, s.left], shallow)
     const [isDragging, setIsDragging] = useState<boolean>(false);
 
     const [isDraggedOver, setIsDraggedOver] = useState<boolean>(false);
     const [moveElements, setMoveElements] = useState<[string | undefined, string | undefined]>([undefined, undefined]);
 
-    function onOpenSelector (evt: React.MouseEvent, showSteps: boolean = true, isInsert: boolean = false) {
+    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);
@@ -69,24 +69,24 @@ export const DslElement = (props: Props) => {
         }
     }
 
-    function onDeleteElement (evt: React.MouseEvent) {
+    function onDeleteElement(evt: React.MouseEvent) {
         evt.stopPropagation();
         onShowDeleteConfirmation(props.step.uuid);
     }
 
-    function onSelectElement (evt: React.MouseEvent) {
+    function onSelectElement(evt: React.MouseEvent) {
         evt.stopPropagation();
         selectElement(props.step);
     }
 
-    function dragElement (event: React.DragEvent<HTMLDivElement>, element: CamelElement) {
+    function dragElement(event: React.DragEvent<HTMLDivElement>, element: CamelElement) {
         event.preventDefault();
         event.stopPropagation();
         setIsDraggedOver(false);
         const sourceUuid = event.dataTransfer.getData("text/plain");
         const targetUuid = element.uuid;
         if (sourceUuid !== targetUuid) {
-            if (element.hasSteps()){
+            if (element.hasSteps()) {
                 setShowMoveConfirmation(true);
                 setMoveElements([sourceUuid, targetUuid])
             } else {
@@ -95,7 +95,7 @@ export const DslElement = (props: Props) => {
         }
     }
 
-    function confirmMove (asChild: boolean) {
+    function confirmMove(asChild: boolean) {
         const sourceUuid = moveElements[0];
         const targetUuid = moveElements[1];
         if (sourceUuid && targetUuid && sourceUuid !== targetUuid) {
@@ -104,16 +104,16 @@ export const DslElement = (props: Props) => {
         }
     }
 
-    function cancelMove () {
+    function cancelMove() {
         setShowMoveConfirmation(false);
         setMoveElements([undefined, undefined]);
     }
 
-    function isSelected (): boolean {
+    function isSelected(): boolean {
         return selectedUuids.includes(props.step.uuid);
     }
 
-    function hasBorder (): boolean {
+    function hasBorder(): boolean {
         return (props.step?.hasSteps() && !['FromDefinition'].includes(props.step.dslName))
             || ['RouteConfigurationDefinition',
                 'RouteDefinition',
@@ -122,35 +122,35 @@ export const DslElement = (props: Props) => {
                 'SwitchDefinition'].includes(props.step.dslName);
     }
 
-    function isNotDraggable (): boolean {
+    function isNotDraggable(): boolean {
         return ['FromDefinition', 'RouteConfigurationDefinition', 'RouteDefinition', 'WhenDefinition', 'OtherwiseDefinition'].includes(props.step.dslName);
     }
 
-    function isWide (): boolean {
+    function isWide(): boolean {
         return ['RouteConfigurationDefinition', 'RouteDefinition', 'ChoiceDefinition', 'SwitchDefinition', 'MulticastDefinition', 'TryDefinition', 'CircuitBreakerDefinition']
             .includes(props.step.dslName);
     }
 
-    function isAddStepButtonLeft (): boolean {
+    function isAddStepButtonLeft(): boolean {
         return ['MulticastDefinition']
             .includes(props.step.dslName);
     }
 
-    function isHorizontal (): boolean {
+    function isHorizontal(): boolean {
         return ['MulticastDefinition'].includes(props.step.dslName);
     }
 
-    function isRoot (): boolean {
+    function isRoot(): boolean {
         return ['RouteConfigurationDefinition', 'RouteDefinition'].includes(props.step?.dslName);
     }
 
-    function isInStepWithChildren () {
+    function isInStepWithChildren() {
         const step: CamelElement = props.step;
         const children = CamelDefinitionApiExt.getElementChildrenDefinition(step.dslName);
         return children.filter((c: ChildElement) => c.name === 'steps' || c.multiple).length > 0 && props.inSteps;
     }
 
-    function getChildrenInfo (step: CamelElement): [boolean, number, boolean, number, number] {
+    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
@@ -170,7 +170,7 @@ export const DslElement = (props: Props) => {
         return [hasStepsField, stepsChildrenCount, hasNonStepsFields, nonStepChildrenCount, childrenCount]
     }
 
-    function hasWideChildrenElement  ()  {
+    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;
@@ -179,13 +179,13 @@ export const DslElement = (props: Props) => {
         else return false;
     }
 
-    function hasBorderOverSteps  (step: CamelElement)  {
+    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 () {
+    function getHeaderStyle() {
         const style: CSSProperties = {
             width: isWide() ? "100%" : "",
             fontWeight: isSelected() ? "bold" : "normal",
@@ -193,7 +193,7 @@ export const DslElement = (props: Props) => {
         return style;
     }
 
-    function sendPosition (el: HTMLDivElement | null, isSelected: boolean) {
+    function sendPosition(el: HTMLDivElement | null, isSelected: boolean) {
         const node = el;
         if (node && el) {
             const header = Array.from(node.childNodes.values()).filter((n: any) => n.classList.contains("header"))[0];
@@ -201,7 +201,6 @@ export const DslElement = (props: Props) => {
                 const headerIcon: any = Array.from(header.childNodes.values()).filter((n: any) => n.classList.contains("header-icon"))[0];
                 const headerRect = headerIcon.getBoundingClientRect();
                 const rect = el.getBoundingClientRect();
-                // if ()
                 if (props.step.show) {
                     EventBus.sendPosition("add", props.step, props.parent, rect, headerRect, props.position, props.inSteps, isSelected);
                 } else {
@@ -211,7 +210,7 @@ export const DslElement = (props: Props) => {
         }
     }
 
-    function getAvailableModels () { // TODO: make static list-of-values instead
+    function getAvailableModels() { // TODO: make static list-of-values instead
         const step: CamelElement = props.step
         return CamelUi.getSelectorModelsForParent(step.dslName, false);
     }
@@ -222,7 +221,7 @@ export const DslElement = (props: Props) => {
     );
 
 
-    function getHeader () {
+    function getHeader() {
         const step: CamelElement = props.step;
         const parent = props.parent;
         const inRouteConfiguration = parent !== undefined && parent.dslName === 'RouteConfigurationDefinition';
@@ -236,9 +235,9 @@ export const DslElement = (props: Props) => {
             <div className={headerClasses} style={getHeaderStyle()}>
                 {!['RouteConfigurationDefinition', 'RouteDefinition'].includes(props.step.dslName) &&
                     <div
-                        // ref={el => sendPosition(el, isSelected())}
-                         className={"header-icon"}
-                         style={isWide() ? {width: ""} : {}}>
+                        ref={el => sendPosition(el, isSelected())}
+                        className={"header-icon"}
+                        style={isWide() ? {width: ""} : {}}>
                         {CamelUi.getIconForElement(step)}
                     </div>
                 }
@@ -253,7 +252,7 @@ export const DslElement = (props: Props) => {
         )
     }
 
-    function getHeaderTextWithTooltip (step: CamelElement) {
+    function getHeaderTextWithTooltip(step: CamelElement) {
         const checkRequired = CamelUtil.checkRequired(step);
         const title = (step as any).description ? (step as any).description : CamelUi.getElementTitle(props.step);
         let className = hasWideChildrenElement() ? "text text-right" : "text text-bottom";
@@ -261,13 +260,13 @@ export const DslElement = (props: Props) => {
         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>))}>
+                     content={checkRequired[1].map((text, i) => (<div key={i}>{text}</div>))}>
                 <Text className={className}>{title}</Text>
             </Tooltip>
         )
     }
 
-    function getHeaderWithTooltip (tooltip: string | undefined) {
+    function getHeaderWithTooltip(tooltip: string | undefined) {
         return (
             <Tooltip position={"left"}
                      content={<div>{tooltip}</div>}>
@@ -276,13 +275,13 @@ export const DslElement = (props: Props) => {
         )
     }
 
-    function getHeaderTooltip (): string | undefined {
+    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 () {
+    function getElementHeader() {
         const tooltip = getHeaderTooltip();
         if (tooltip !== undefined && !isDragging) {
             return getHeaderWithTooltip(tooltip);
@@ -290,7 +289,7 @@ export const DslElement = (props: Props) => {
         return getHeader();
     }
 
-    function getChildrenStyle ()  {
+    function getChildrenStyle() {
         const style: CSSProperties = {
             display: "flex",
             flexDirection: "row",
@@ -298,7 +297,7 @@ export const DslElement = (props: Props) => {
         return style;
     }
 
-    function getChildrenElementsStyle (child: ChildElement, notOnlySteps: boolean)  {
+    function getChildrenElementsStyle(child: ChildElement, notOnlySteps: boolean) {
         const step = props.step;
         const isBorder = child.name === 'steps' && hasBorderOverSteps(step);
         const style: CSSProperties = {
@@ -312,7 +311,7 @@ export const DslElement = (props: Props) => {
         return style;
     }
 
-    function getChildElements () {
+    function getChildElements() {
         const step: CamelElement = props.step;
         let children: ChildElement[] = CamelDefinitionApiExt.getElementChildrenDefinition(step.dslName);
         const notOnlySteps = children.filter(c => c.name === 'steps').length === 1
@@ -334,12 +333,13 @@ export const DslElement = (props: Props) => {
         )
     }
 
-    function getChildDslElements (child: ChildElement, index: number, notOnlySteps: boolean) {
+    function getChildDslElements(child: ChildElement, index: number, notOnlySteps: boolean) {
         const step = props.step;
         const children: CamelElement[] = CamelDefinitionApiExt.getElementChildren(step, child);
         if (children.length > 0) {
             return (
-                <div className={child.name + " has-child"} style={getChildrenElementsStyle(child, notOnlySteps)} key={step.uuid + "-child-" + index}>
+                <div className={child.name + " has-child"} style={getChildrenElementsStyle(child, notOnlySteps)}
+                     key={step.uuid + "-child-" + index}>
                     {children.map((element, index) => (
                         <div key={step.uuid + child.className + index}>
                             <DslElement
@@ -354,7 +354,8 @@ export const DslElement = (props: Props) => {
             )
         } else if (child.name === 'steps') {
             return (
-                <div className={child.name + " has-child"} style={getChildrenElementsStyle(child, notOnlySteps)} key={step.uuid + "-child-" + index}>
+                <div className={child.name + " has-child"} style={getChildrenElementsStyle(child, notOnlySteps)}
+                     key={step.uuid + "-child-" + index}>
                     {getAddStepButton()}
                 </div>
             )
@@ -362,7 +363,7 @@ export const DslElement = (props: Props) => {
     }
 
     function getAddStepButton() {
-        const { step} = props;
+        const {step} = props;
         const hideAddButton = step.dslName === 'StepDefinition' && !CamelDisplayUtil.isStepDefinitionExpanded(integration, step.uuid, selectedUuids.at(0));
         if (hideAddButton) return (<></>)
         else return (
@@ -370,7 +371,7 @@ export const DslElement = (props: Props) => {
                      content={<div>{"Add step to " + CamelUi.getTitle(step)}</div>}>
                 <button type="button" aria-label="Add" onClick={e => onOpenSelector(e)}
                         className={isAddStepButtonLeft() ? "add-button add-button-left" : "add-button add-button-bottom"}>
-                    <AddIcon />
+                    <AddIcon/>
                 </button>
             </Tooltip>
         )
@@ -384,7 +385,7 @@ export const DslElement = (props: Props) => {
                     aria-label="Add"
                     onClick={e => onOpenSelector(e, false)}
                     className={"add-element-button"}>
-                    <AddIcon />
+                    <AddIcon/>
                 </button>
             </Tooltip>
         )
@@ -393,7 +394,8 @@ export const DslElement = (props: Props) => {
     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"}><InsertIcon />
+                <button type="button" aria-label="Insert" onClick={e => onOpenSelector(e, true, true)}
+                        className={"insert-element-button"}><InsertIcon/>
                 </button>
             </Tooltip>
         )
@@ -402,7 +404,8 @@ export const DslElement = (props: Props) => {
     function getDeleteButton() {
         return (
             <Tooltip position={"right"} content={<div>{"Delete element"}</div>}>
-                <button type="button" aria-label="Delete" onClick={e => onDeleteElement(e)} className="delete-button"><DeleteIcon /></button>
+                <button type="button" aria-label="Delete" onClick={e => onDeleteElement(e)} className="delete-button">
+                    <DeleteIcon/></button>
             </Tooltip>
         )
     }
@@ -417,7 +420,8 @@ export const DslElement = (props: Props) => {
             ><Flex direction={{default: "column"}}>
                 <div>Select move type:</div>
                 <Button key="place" variant="primary" onClick={event => confirmMove(false)}>Shift (target down)</Button>
-                <Button key="child" variant="secondary" onClick={event => confirmMove(true)}>Move as target step</Button>
+                <Button key="child" variant="secondary" onClick={event => confirmMove(true)}>Move as target
+                    step</Button>
                 <Button key="cancel" variant="tertiary" onClick={event => cancelMove()}>Cancel</Button>
             </Flex>
 
diff --git a/karavan-designer/src/designer/route/RouteDesigner.tsx b/karavan-designer/src/designer/route/RouteDesigner.tsx
index d605044a..5e373f0d 100644
--- a/karavan-designer/src/designer/route/RouteDesigner.tsx
+++ b/karavan-designer/src/designer/route/RouteDesigner.tsx
@@ -64,10 +64,17 @@ export const RouteDesigner = () => {
     }, [])
 
     function changeGraphSize ()  {
+        console.log("changeGraphSize")
         if (flowRef && flowRef.current) {
-            const el = flowRef.current.getBoundingClientRect();
-            if (width !== el.width || height !== el.height || top !== el.top || left !== el.left) {
-                setPosition(el.width, el.height, el.top, el.left)
+            const el = flowRef.current;
+            const rect = el.getBoundingClientRect();
+            // if (width !== el.scrollWidth || height !== el.scrollHeight || top !== rect.top || left !== rect.left) {
+            if (width !== rect.width || height !== rect?.height || top !== rect.top || left !== rect.left) {
+                console.log("State", width, height, top, left)
+                console.log("Graph", rect.width, rect.height, rect.top, rect.left)
+                setPosition(rect.width, rect.height, rect.top, rect.left)
+                // setPosition(el?.scrollWidth, el?.scrollHeight, el?.scrollTop, el?.scrollLeft)
+                // setPosition(el?.scrollWidth, el?.scrollHeight, rect.top, rect.left)
             }
         }
     }
@@ -76,10 +83,12 @@ export const RouteDesigner = () => {
     const secondRef = useMutationsObserver(onChangeGraphSize);
     const printerRef = useRef<HTMLDivElement | null>(null);
     const flowRef = useRef<HTMLDivElement | null>(null);
-    const contentRef: React.RefObject<HTMLDivElement> = useRef(null);
 
     useEffect(()=>{
-        window.addEventListener('resize', changeGraphSize);
+        // window.addEventListener('resize', changeGraphSize);
+        const interval = setInterval(() => {
+            changeGraphSize();
+        }, 500);
         window.addEventListener('keydown', handleKeyDown);
         window.addEventListener('keyup', handleKeyUp);
         const commandSub = EventBus.onCommand()?.subscribe((command: Command) => onCommand(command, printerRef));
@@ -89,7 +98,8 @@ export const RouteDesigner = () => {
             changeGraphSize();
         }
         return ()=> {
-            window.removeEventListener('resize', changeGraphSize);
+            clearInterval(interval)
+            // window.removeEventListener('resize', changeGraphSize);
             window.removeEventListener('keydown', handleKeyDown);
             window.removeEventListener('keyup', handleKeyUp);
             commandSub?.unsubscribe();
diff --git a/karavan-designer/src/designer/route/useRouteDesignerHook.tsx b/karavan-designer/src/designer/route/useRouteDesignerHook.tsx
index 4a69a10d..993e45ef 100644
--- a/karavan-designer/src/designer/route/useRouteDesignerHook.tsx
+++ b/karavan-designer/src/designer/route/useRouteDesignerHook.tsx
@@ -107,6 +107,7 @@ export const useRouteDesignerHook = () => {
     }
 
     function copyToClipboard  (): void {
+        console.log("copyToClipboard1", selectedUuids)
         const steps: CamelElement[] = []
         selectedUuids.forEach(selectedUuid => {
             const selectedElement = CamelDefinitionApiExt.findElementInIntegration(integration, selectedUuid);
@@ -115,10 +116,12 @@ export const useRouteDesignerHook = () => {
             }
         })
         if (steps.length > 0) {
+            console.log("copyToClipboard2", steps)
             setClipboardSteps(steps);
         }
     }
     function pasteFromClipboard (): void {
+        console.log("pasteFromClipboard")
         if (clipboardSteps.length === 1 && clipboardSteps[0]?.dslName === 'FromDefinition') {
             const clone = CamelUtil.cloneStep(clipboardSteps[0], true);
             const route = CamelDefinitionApi.createRouteDefinition({from: clone});
@@ -212,29 +215,30 @@ export const useRouteDesignerHook = () => {
     }
 
     const selectElement = (element: CamelElement) =>  {
-        // const {shiftKeyPressed, selectedUuids, integration} = this.routeDesigner.state;
+        // console.log("selectElement", element, selectedUuids)
+        const uuids = [...selectedUuids];
         let canNotAdd: boolean = false;
         if (shiftKeyPressed) {
-            const hasFrom = selectedUuids.map(e => CamelDefinitionApiExt.findElementInIntegration(integration, e)?.dslName === 'FromDefinition').filter(r => r).length > 0;
-            canNotAdd = hasFrom || (selectedUuids.length > 0 && element.dslName === 'FromDefinition');
+            const hasFrom = uuids.map(e => CamelDefinitionApiExt.findElementInIntegration(integration, e)?.dslName === 'FromDefinition').filter(r => r).length > 0;
+            canNotAdd = hasFrom || (uuids.length > 0 && element.dslName === 'FromDefinition');
         }
-        const add = shiftKeyPressed && !selectedUuids.includes(element.uuid);
-        const remove = shiftKeyPressed && selectedUuids.includes(element.uuid);
+        const add = shiftKeyPressed && !uuids.includes(element.uuid);
+        const remove = shiftKeyPressed && uuids.includes(element.uuid);
         // TODO: do we need to change Intgration just for select????
         const i = CamelDisplayUtil.setIntegrationVisibility(integration, element.uuid);
 
         if (remove) {
-            const index = selectedUuids.indexOf(element.uuid);
-            selectedUuids.splice(index, 1);
+            const index = uuids.indexOf(element.uuid);
+            uuids.splice(index, 1);
         } else if (add && !canNotAdd) {
-            selectedUuids.push(element.uuid);
+            uuids.push(element.uuid);
         }
-        const uuid: string = selectedUuids.includes(element.uuid) ? element.uuid : selectedUuids.at(0) || '';
+        const uuid: string = uuids.includes(element.uuid) ? element.uuid : uuids.at(0) || '';
         const selectedElement = shiftKeyPressed ? CamelDefinitionApiExt.findElementInIntegration(integration, uuid) : element;
 
         setIntegration(i);
         setSelectedStep(selectedElement);
-        setSelectedUuids(shiftKeyPressed ? [...selectedUuids] : [element.uuid])
+        setSelectedUuids(shiftKeyPressed ? [...uuids] : [element.uuid])
     }
     //
     // function unselectElement = (evt: React.MouseEvent<HTMLDivElement, MouseEvent>) => {


[camel-karavan] 06/06: Fixed issue with Expressions for #836

Posted by ma...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

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

commit 4f8a93e1392c64fc36c863316bd30af4c7b1fe42
Author: Marat Gubaidullin <ma...@Marats-MacBook-Pro.local>
AuthorDate: Thu Aug 24 19:26:02 2023 -0400

    Fixed issue with Expressions for #836
---
 karavan-designer/src/designer/route/DslConnections.tsx    |  1 -
 karavan-designer/src/designer/route/DslElement.tsx        | 13 ++++++-------
 karavan-designer/src/designer/route/usePropertiesHook.tsx |  9 ---------
 3 files changed, 6 insertions(+), 17 deletions(-)

diff --git a/karavan-designer/src/designer/route/DslConnections.tsx b/karavan-designer/src/designer/route/DslConnections.tsx
index 1002103e..a2505f4f 100644
--- a/karavan-designer/src/designer/route/DslConnections.tsx
+++ b/karavan-designer/src/designer/route/DslConnections.tsx
@@ -40,7 +40,6 @@ export const DslConnections = () => {
     }, []);
 
     function setPosition(evt: DslPosition) {
-        console.log("setPosition : ", evt);
         if (evt.command === "add") {
             addStep(evt.step.uuid, evt);
         }
diff --git a/karavan-designer/src/designer/route/DslElement.tsx b/karavan-designer/src/designer/route/DslElement.tsx
index dc73dc68..383700b0 100644
--- a/karavan-designer/src/designer/route/DslElement.tsx
+++ b/karavan-designer/src/designer/route/DslElement.tsx
@@ -44,7 +44,7 @@ interface Props {
 
 export const DslElement = (props: Props) => {
 
-    const ref = React.createRef();
+    const headerRef = React.useRef<HTMLDivElement>(null);
     const {deleteElement, selectElement, moveElement, onShowDeleteConfirmation, openSelector} = useRouteDesignerHook();
 
     const [integration, setIntegration] = useIntegrationStore((s) => [s.integration, s.setIntegration], shallow)
@@ -194,11 +194,9 @@ export const DslElement = (props: Props) => {
     }
 
     function sendPosition(el: HTMLDivElement | null, isSelected: boolean) {
-        // console.log("sendPosition1 : ", selectedStep)
         if (el) {
             const header = Array.from(el.childNodes.values()).filter((n: any) => n.classList.contains("header"))[0];
             if (header) {
-                // console.log("sendPosition2 : ", selectedStep, el)
                 const headerIcon: any = Array.from(header.childNodes.values()).filter((n: any) => n.classList.contains("header-icon"))[0];
                 const headerRect = headerIcon.getBoundingClientRect();
                 const rect = el.getBoundingClientRect();
@@ -233,7 +231,7 @@ export const DslElement = (props: Props) => {
         const headerClass = ['RouteConfigurationDefinition', 'RouteDefinition'].includes(step.dslName) ? "header-route" : "header"
         const headerClasses = isSelected() ? headerClass + " selected" : headerClass;
         return (
-            <div className={headerClasses} style={getHeaderStyle()}>
+            <div className={headerClasses} style={getHeaderStyle()} ref={headerRef}>
                 {!['RouteConfigurationDefinition', 'RouteDefinition'].includes(props.step.dslName) &&
                     <div
                         ref={el => sendPosition(el, isSelected())}
@@ -269,10 +267,11 @@ export const DslElement = (props: Props) => {
 
     function getHeaderWithTooltip(tooltip: string | undefined) {
         return (
-            <Tooltip position={"left"}
-                     content={<div>{tooltip}</div>}>
+            <>
                 {getHeader()}
-            </Tooltip>
+                <Tooltip triggerRef={headerRef} position={"left"} content={<div>{tooltip}</div>}/>
+            </>
+
         )
     }
 
diff --git a/karavan-designer/src/designer/route/usePropertiesHook.tsx b/karavan-designer/src/designer/route/usePropertiesHook.tsx
index a3e4e5cd..56c5d13d 100644
--- a/karavan-designer/src/designer/route/usePropertiesHook.tsx
+++ b/karavan-designer/src/designer/route/usePropertiesHook.tsx
@@ -26,7 +26,6 @@ import {RouteToCreate} from "../utils/CamelUi";
 import {useDesignerStore, useIntegrationStore} from "../KaravanStore";
 import {shallow} from "zustand/shallow";
 
-
 export const usePropertiesHook = () => {
 
     const [integration, setIntegration] = useIntegrationStore((state) => [state.integration, state.setIntegration], shallow)
@@ -34,7 +33,6 @@ export const usePropertiesHook = () => {
         [s.selectedStep,s.setPropertyOnly, s.setSelectedStep, s.setSelectedUuids], shallow)
 
     function onPropertyUpdate (element: CamelElement, newRoute?: RouteToCreate) {
-        console.log("-> onPropertyUpdate", element, newRoute)
         if (newRoute) {
             let i = CamelDefinitionApiExt.updateIntegrationRouteElement(integration, element);
             const f = CamelDefinitionApi.createFromDefinition({uri: newRoute.componentName, parameters: {name: newRoute.name}});
@@ -54,13 +52,10 @@ export const usePropertiesHook = () => {
     }
 
     function onPropertyChange (fieldId: string, value: string | number | boolean | any, newRoute?: RouteToCreate){
-        console.log("onPropertyChange", fieldId, value,);
-        console.log("onPropertyChange", selectedStep);
         if (selectedStep) {
             const clone = CamelUtil.cloneStep(selectedStep);
             (clone as any)[fieldId] = value;
             setSelectedStep(clone)
-            console.log("onPropertyChange", clone);
             onPropertyUpdate(clone, newRoute);
         }
     }
@@ -72,19 +67,15 @@ export const usePropertiesHook = () => {
     }
 
     function onExpressionChange (propertyName: string, exp: ExpressionDefinition)   {
-        console.log("onExpressionChange", propertyName, exp);
-        console.log("onExpressionChange", selectedStep);
         if (selectedStep) {
             const clone = (CamelUtil.cloneStep(selectedStep));
             (clone as any)[propertyName] = exp;
             setSelectedStep(clone);
-            console.log("onExpressionChange", clone);
             onPropertyUpdate(clone);
         }
     }
 
     function onParametersChange (parameter: string, value: string | number | boolean | any, pathParameter?: boolean, newRoute?: RouteToCreate)   {
-        console.log("onParametersChange", parameter, value);
         if (selectedStep && selectedStep) {
             const clone = (CamelUtil.cloneStep(selectedStep));
             const parameters: any = {...(clone as any).parameters};