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/21 22:22:28 UTC

[camel-karavan] 01/03: First try

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 e4e70e8fddf9343e021c7105e7f7d7a0340ba9ff
Author: Marat Gubaidullin <ma...@Marats-MacBook-Pro.local>
AuthorDate: Mon Aug 21 10:09:21 2023 -0400

    First try
---
 karavan-designer/package-lock.json                 |  64 +++-
 karavan-designer/package.json                      |   3 +-
 karavan-designer/public/example/demo.camel.yaml    |  41 +++
 karavan-designer/src/App.tsx                       |   7 +-
 karavan-designer/src/designer/KaravanDesigner.tsx  | 133 +++----
 karavan-designer/src/designer/KaravanStore.ts      | 127 +++++++
 .../src/designer/route/DslConnections.tsx          | 250 +++++++------
 karavan-designer/src/designer/route/DslElement.tsx | 398 ++++++++++-----------
 .../src/designer/route/ElementResizeListener.tsx   |  75 ++++
 .../src/designer/route/RouteDesigner.tsx           | 260 +++++++-------
 .../src/designer/route/RouteDesignerLogic.tsx      | 396 --------------------
 .../designer/route/property/DslPropertyField.tsx   |   2 +-
 .../src/designer/route/useRouteDesignerHook.tsx    | 384 ++++++++++++++++++++
 karavan-designer/src/index.tsx                     |   8 +-
 14 files changed, 1206 insertions(+), 942 deletions(-)

diff --git a/karavan-designer/package-lock.json b/karavan-designer/package-lock.json
index a9956967..d6fbc8b5 100644
--- a/karavan-designer/package-lock.json
+++ b/karavan-designer/package-lock.json
@@ -9,7 +9,7 @@
       "version": "4.0.0-RC2",
       "license": "Apache-2.0",
       "dependencies": {
-        "@monaco-editor/react": "4.5.0",
+        "@monaco-editor/react": "4.5.1",
         "@patternfly/patternfly": "^5.0.2",
         "@patternfly/react-core": "^5.0.0",
         "@patternfly/react-table": "^5.0.0",
@@ -24,7 +24,8 @@
         "react": "18.2.0",
         "react-dom": "18.2.0",
         "rxjs": "7.8.1",
-        "uuid": "9.0.0"
+        "uuid": "9.0.0",
+        "zustand": "^4.4.1"
       },
       "devDependencies": {
         "@svgr/webpack": "^7.0.0",
@@ -36,7 +37,7 @@
         "@typescript-eslint/eslint-plugin": "^5.59.2",
         "@typescript-eslint/parser": "^5.59.2",
         "eslint": "^8.39.0",
-        "monaco-editor": "0.38.0",
+        "monaco-editor": "0.41.0",
         "react-scripts": "5.0.1",
         "typescript": "^4.9.5"
       }
@@ -3198,9 +3199,9 @@
       }
     },
     "node_modules/@monaco-editor/react": {
-      "version": "4.5.0",
-      "resolved": "https://registry.npmjs.org/@monaco-editor/react/-/react-4.5.0.tgz",
-      "integrity": "sha512-VJMkp5Fe1+w8pLEq8tZPHZKu8zDXQIA1FtiDTSNccg1D3wg1YIZaH2es2Qpvop1k62g3c/YySRb3bnGXu2XwYQ==",
+      "version": "4.5.1",
+      "resolved": "https://registry.npmjs.org/@monaco-editor/react/-/react-4.5.1.tgz",
+      "integrity": "sha512-NNDFdP+2HojtNhCkRfE6/D6ro6pBNihaOzMbGK84lNWzRu+CfBjwzGt4jmnqimLuqp5yE5viHS2vi+QOAnD5FQ==",
       "dependencies": {
         "@monaco-editor/loader": "^1.3.3"
       },
@@ -3996,7 +3997,7 @@
       "version": "15.7.5",
       "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.5.tgz",
       "integrity": "sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w==",
-      "dev": true
+      "devOptional": true
     },
     "node_modules/@types/qs": {
       "version": "6.9.7",
@@ -4014,7 +4015,7 @@
       "version": "18.2.0",
       "resolved": "https://registry.npmjs.org/@types/react/-/react-18.2.0.tgz",
       "integrity": "sha512-0FLj93y5USLHdnhIhABk83rm8XEGA7kH3cr+YUlvxoUGp1xNt/DINUMvqPxLyOQMzLmZe8i4RTHbvb8MC7NmrA==",
-      "dev": true,
+      "devOptional": true,
       "dependencies": {
         "@types/prop-types": "*",
         "@types/scheduler": "*",
@@ -4049,7 +4050,7 @@
       "version": "0.16.3",
       "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.3.tgz",
       "integrity": "sha512-5cJ8CB4yAx7BH1oMvdU0Jh9lrEXyPkar6F9G/ERswkCuvP4KQZfZkSjcMbAICCpQTN4OuZn8tz0HiKv9TGZgrQ==",
-      "dev": true
+      "devOptional": true
     },
     "node_modules/@types/semver": {
       "version": "7.3.13",
@@ -6530,7 +6531,7 @@
       "version": "3.1.2",
       "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.2.tgz",
       "integrity": "sha512-I7K1Uu0MBPzaFKg4nI5Q7Vs2t+3gWWW648spaF+Rg7pI9ds18Ugn+lvg4SHczUdKlHI5LWBXyqfS8+DufyBsgQ==",
-      "dev": true
+      "devOptional": true
     },
     "node_modules/dagre": {
       "version": "0.8.5",
@@ -9388,7 +9389,7 @@
       "version": "9.0.21",
       "resolved": "https://registry.npmjs.org/immer/-/immer-9.0.21.tgz",
       "integrity": "sha512-bc4NBHqOqSfRW7POMkHd51LvClaeMXpm8dx0e8oE2GORbq5aRK7Bxl4FyzVLdGtLmvLKL7BTDBG5ACQm4HWjTA==",
-      "dev": true,
+      "devOptional": true,
       "funding": {
         "type": "opencollective",
         "url": "https://opencollective.com/immer"
@@ -12777,9 +12778,9 @@
       }
     },
     "node_modules/monaco-editor": {
-      "version": "0.38.0",
-      "resolved": "https://registry.npmjs.org/monaco-editor/-/monaco-editor-0.38.0.tgz",
-      "integrity": "sha512-11Fkh6yzEmwx7O0YoLxeae0qEGFwmyPRlVxpg7oF9czOOCB/iCjdJrG5I67da5WiXK3YJCxoz9TJFE8Tfq/v9A=="
+      "version": "0.41.0",
+      "resolved": "https://registry.npmjs.org/monaco-editor/-/monaco-editor-0.41.0.tgz",
+      "integrity": "sha512-1o4olnZJsiLmv5pwLEAmzHTE/5geLKQ07BrGxlF4Ri/AXAc2yyDGZwHjiTqD8D/ROKUZmwMA28A+yEowLNOEcA=="
     },
     "node_modules/ms": {
       "version": "2.1.2",
@@ -17384,6 +17385,14 @@
         "requires-port": "^1.0.0"
       }
     },
+    "node_modules/use-sync-external-store": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz",
+      "integrity": "sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA==",
+      "peerDependencies": {
+        "react": "^16.8.0 || ^17.0.0 || ^18.0.0"
+      }
+    },
     "node_modules/util-deprecate": {
       "version": "1.0.2",
       "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
@@ -18430,6 +18439,33 @@
       "funding": {
         "url": "https://github.com/sponsors/sindresorhus"
       }
+    },
+    "node_modules/zustand": {
+      "version": "4.4.1",
+      "resolved": "https://registry.npmjs.org/zustand/-/zustand-4.4.1.tgz",
+      "integrity": "sha512-QCPfstAS4EBiTQzlaGP1gmorkh/UL1Leaj2tdj+zZCZ/9bm0WS7sI2wnfD5lpOszFqWJ1DcPnGoY8RDL61uokw==",
+      "dependencies": {
+        "use-sync-external-store": "1.2.0"
+      },
+      "engines": {
+        "node": ">=12.7.0"
+      },
+      "peerDependencies": {
+        "@types/react": ">=16.8",
+        "immer": ">=9.0",
+        "react": ">=16.8"
+      },
+      "peerDependenciesMeta": {
+        "@types/react": {
+          "optional": true
+        },
+        "immer": {
+          "optional": true
+        },
+        "react": {
+          "optional": true
+        }
+      }
     }
   }
 }
diff --git a/karavan-designer/package.json b/karavan-designer/package.json
index 548304dd..a89a6b7e 100644
--- a/karavan-designer/package.json
+++ b/karavan-designer/package.json
@@ -41,7 +41,8 @@
     "react": "18.2.0",
     "react-dom": "18.2.0",
     "rxjs": "7.8.1",
-    "uuid": "9.0.0"
+    "uuid": "9.0.0",
+    "zustand": "^4.4.1"
   },
   "devDependencies": {
     "@svgr/webpack": "^7.0.0",
diff --git a/karavan-designer/public/example/demo.camel.yaml b/karavan-designer/public/example/demo.camel.yaml
new file mode 100644
index 00000000..2db92f8d
--- /dev/null
+++ b/karavan-designer/public/example/demo.camel.yaml
@@ -0,0 +1,41 @@
+- 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
diff --git a/karavan-designer/src/App.tsx b/karavan-designer/src/App.tsx
index b9cae192..3e98dc50 100644
--- a/karavan-designer/src/App.tsx
+++ b/karavan-designer/src/App.tsx
@@ -94,7 +94,8 @@ class App extends React.Component<Props, State> {
             fetch("kamelets/kamelets.yaml"),
             fetch("components/components.json"),
             fetch("snippets/org.apache.camel.AggregationStrategy"),
-            fetch("snippets/org.apache.camel.Processor")
+            fetch("snippets/org.apache.camel.Processor"),
+            fetch("example/demo.camel.yaml")
             // fetch("components/supported-components.json"),
         ]).then(responses =>
             Promise.all(responses.map(response => response.text()))
@@ -113,6 +114,10 @@ class App extends React.Component<Props, State> {
             TemplateApi.saveTemplate("org.apache.camel.Processor", data[3]);
 
             if (data[4]) {
+                this.setState({yaml: data[4], name: "demo.camel.yaml"})
+            }
+
+            if (data[5]) {
                 ComponentApi.saveSupportedComponents(data[4]);
                 ComponentApi.setSupportedOnly(true);
             }
diff --git a/karavan-designer/src/designer/KaravanDesigner.tsx b/karavan-designer/src/designer/KaravanDesigner.tsx
index a2556b2a..b65a66f1 100644
--- a/karavan-designer/src/designer/KaravanDesigner.tsx
+++ b/karavan-designer/src/designer/KaravanDesigner.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, {useEffect, useState} from 'react';
 import {
     Badge,
     PageSection, PageSectionVariants, Tab, Tabs, TabTitleIcon, TabTitleText, Tooltip,
@@ -27,6 +27,8 @@ import {CamelUtil} from "karavan-core/lib/api/CamelUtil";
 import {CamelUi} from "./utils/CamelUi";
 import {BeansDesigner} from "./beans/BeansDesigner";
 import {RestDesigner} from "./rest/RestDesigner";
+import {useDesignerStore, useIntegrationStore} from "./KaravanStore";
+import {shallow} from "zustand/shallow";
 import {getDesignerIcon} from "./utils/KaravanIcons";
 
 interface Props {
@@ -39,67 +41,70 @@ interface Props {
     tab?: string
 }
 
-interface State {
-    tab: string
-    integration: Integration
-    key: string
-    propertyOnly: boolean
-}
-
 export class KaravanInstance {
-    static designer: KaravanDesigner;
+    // static designer: KaravanDesigner;
 
-    static set(designer: KaravanDesigner): void  {
-        KaravanInstance.designer = designer;
-    }
-
-    static get(): KaravanDesigner {
-        return KaravanInstance.designer;
-    }
+    // static set(designer: KaravanDesigner): void  {
+    //     KaravanInstance.designer = designer;
+    // }
+    //
+    // static get(): KaravanDesigner {
+    //     return KaravanInstance.designer;
+    // }
 
     static getProps(): Props {
-        return KaravanInstance.designer?.props;
+        return {
+            dark: false, filename: "", onGetCustomCode(name: string, javaType: string): Promise<string | undefined> {
+                return Promise.resolve(undefined);
+            }, onSave(filename: string, yaml: string, propertyOnly: boolean): void {
+            }, onSaveCustomCode(name: string, code: string): void {
+            }, yaml: ""
+        };
     }
 }
 
-export class KaravanDesigner extends React.Component<Props, State> {
+export const KaravanDesigner = (props: Props) => {
 
-    getIntegration = (yaml: string, filename: string): Integration => {
+    const [tab, setTab] = useState<string>('routes');
+    const [propertyOnly, setPropertyOnly] = useDesignerStore((state) =>
+        [state.propertyOnly, state.setPropertyOnly], shallow )
+    const [integration, setIntegration] = useIntegrationStore((state) => 
+        [state.integration, state.setIntegration], shallow )
+
+    useEffect(() => {
+        console.log("useEffect");
+        setIntegration(makeIntegration(props.yaml, props.filename));
+    }, []);
+
+    function makeIntegration  (yaml: string, filename: string): Integration {
        if (yaml && CamelDefinitionYaml.yamlIsIntegration(yaml)) {
-           return CamelDefinitionYaml.yamlToIntegration(this.props.filename, this.props.yaml)
+           return CamelDefinitionYaml.yamlToIntegration(props.filename, props.yaml)
        } else {
            return Integration.createNew(filename, 'plain');
        }
     }
 
-    public state: State = {
-        tab: this.props.tab ? this.props.tab : 'routes',
-        integration: this.getIntegration(this.props.yaml, this.props.filename),
-        key: "",
-        propertyOnly: false,
-    }
-
-    componentDidMount() {
-        KaravanInstance.set(this);
-    }
-
-    componentDidUpdate = (prevProps: Readonly<Props>, prevState: Readonly<State>, snapshot?: any) => {
-        if (prevState.key !== this.state.key) {
-            this.props.onSave?.call(this, this.props.filename, this.getCode(this.state.integration), this.state.propertyOnly);
-        }
-    }
-
-    save = (integration: Integration, propertyOnly: boolean): void => {
-        this.setState({key: Math.random().toString(), integration: integration, propertyOnly: propertyOnly});
-    }
-
-    getCode = (integration: Integration): string => {
+    // componentDidMount() {
+    //     KaravanInstance.set(this);
+    // }
+    //
+    // componentDidUpdate = (prevProps: Readonly<Props>, prevState: Readonly<State>, snapshot?: any) => {
+    //     if (prevState.key !== key) {
+    //         props.onSave?.call(this, props.filename, this.getCode(integration), propertyOnly);
+    //     }
+    // }
+
+    // save = (integration: Integration, propertyOnly: boolean): void => {
+    //     this.setState({key: Math.random().toString(), integration: integration, propertyOnly: propertyOnly});
+    // }
+
+    function getCode (integration: Integration): string {
         const clone = CamelUtil.cloneIntegration(integration);
         return CamelDefinitionYaml.integrationToYaml(clone);
     }
 
-    getTab(title: string, tooltip: string, icon: string) {
-        const counts = CamelUi.getFlowCounts(this.state.integration);
+    function  getTab(title: string, tooltip: string, icon: string) {
+        const counts = CamelUi.getFlowCounts(integration);
         const count = counts.has(icon) && counts.get(icon) ? counts.get(icon) : undefined;
         const showCount = count && count > 0;
         return (
@@ -115,25 +120,23 @@ export class KaravanDesigner extends React.Component<Props, State> {
         )
     }
 
-    render() {
-        const tab = this.state.tab;
-        return (
-            <PageSection variant={this.props.dark ? PageSectionVariants.darker : PageSectionVariants.light} className="page" isFilled padding={{default: 'noPadding'}}>
-                <Tabs className="main-tabs" activeKey={tab} onSelect={(event, tabIndex) => this.setState({tab: tabIndex.toString()})} style={{width: "100%"}}>
-                    <Tab eventKey='routes' title={this.getTab("Routes", "Integration flows", "routes")}></Tab>
-                    <Tab eventKey='rest' title={this.getTab("REST", "REST services", "rest")}></Tab>
-                    <Tab eventKey='beans' title={this.getTab("Beans", "Beans Configuration", "beans")}></Tab>
-                </Tabs>
-                    {tab === 'routes' && <RouteDesigner integration={this.state.integration}
-                                                        onSave={(integration, propertyOnly) => this.save(integration, propertyOnly)}
-                                                        dark={this.props.dark}/>}
-                    {tab === 'rest' && <RestDesigner integration={this.state.integration}
-                                                     onSave={(integration, propertyOnly) => this.save(integration, propertyOnly)}
-                                                     dark={this.props.dark}/>}
-                    {tab === 'beans' && <BeansDesigner integration={this.state.integration}
-                                                       onSave={(integration, propertyOnly) => this.save(integration, propertyOnly)}
-                                                       dark={this.props.dark}/>}
-            </PageSection>
-        )
-    }
+    return (
+        <PageSection variant={props.dark ? PageSectionVariants.darker : PageSectionVariants.light} className="page" isFilled padding={{default: 'noPadding'}}>
+            <Tabs className="main-tabs" activeKey={tab} onSelect={(event, tabIndex) => setTab(tabIndex.toString())} style={{width: "100%"}}>
+                <Tab eventKey='routes' title={getTab("Routes", "Integration flows", "routes")}></Tab>
+                <Tab eventKey='rest' title={getTab("REST", "REST services", "rest")}></Tab>
+                <Tab eventKey='beans' title={getTab("Beans", "Beans Configuration", "beans")}></Tab>
+            </Tabs>
+            {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}/>}*/}
+            {/*{tab === 'beans' && <BeansDesigner integration={integration}*/}
+            {/*                                   onSave={(integration, propertyOnly) => save(integration, propertyOnly)}*/}
+            {/*                                   dark={props.dark}/>}*/}
+        </PageSection>
+    )
 }
\ No newline at end of file
diff --git a/karavan-designer/src/designer/KaravanStore.ts b/karavan-designer/src/designer/KaravanStore.ts
new file mode 100644
index 00000000..4e710e61
--- /dev/null
+++ b/karavan-designer/src/designer/KaravanStore.ts
@@ -0,0 +1,127 @@
+/*
+ * 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 {create} from 'zustand'
+import {CamelElement, Integration} from "karavan-core/lib/model/IntegrationDefinition";
+import {createWithEqualityFn} from "zustand/traditional";
+import {shallow} from "zustand/shallow";
+
+interface IntegrationState {
+    integration: Integration;
+    setIntegration: (integration: Integration) => void;
+}
+
+export const useIntegrationStore = createWithEqualityFn<IntegrationState>((set) => ({
+    integration: Integration.createNew("demo", "plain"),
+    setIntegration: (integration: Integration) => {
+        set({integration: integration})
+    },
+}), shallow)
+
+interface DesignerState {
+    shiftKeyPressed: boolean;
+    showSelector: boolean;
+    setShowSelector: (showSelector: boolean) => void;
+    showDeleteConfirmation: boolean;
+    setShowDeleteConfirmation: (showDeleteConfirmation: boolean) => void;
+    showMoveConfirmation: boolean;
+    setShowMoveConfirmation: (showMoveConfirmation: boolean) => void;
+    propertyOnly: boolean;
+    setPropertyOnly: (propertyOnly: boolean) => void;
+    showSteps: boolean;
+    setShowSteps: (showSteps: boolean) => void;
+    deleteMessage: string;
+    setDeleteMessage: (deleteMessage: string) => void;
+    parentDsl?: string;
+    setParentDsl: (parentDsl?: string) => void;
+    selectedPosition?: number;
+    setSelectedPosition: (selectedPosition?: number) => void;
+    selectedStep?: CamelElement;
+    setSelectedStep: (selectedStep?: CamelElement) => void;
+    parentId: string;
+    setParentId: (parentId: string) => void;
+    selectedUuids: string[];
+    setSelectedUuids: (selectedUuids: string[]) => void;
+    clipboardSteps: string[];
+    setClipboardSteps: (clipboardSteps: string[]) => void;
+    selectorTabIndex?: string | number
+    setSelectorTabIndex: (selectorTabIndex?: string | number) => void;
+    width: number,
+    height: number,
+    top: number,
+    left: number,
+    setPosition: (width: number, height: number, top: number, left: number) => void;
+}
+
+export const useDesignerStore = createWithEqualityFn<DesignerState>((set) => ({
+    shiftKeyPressed: false,
+    showSelector: false,
+    showDeleteConfirmation: false,
+    showMoveConfirmation: false,
+    deleteMessage: '',
+    parentId: '',
+    showSteps: true,
+    selectedUuids: [],
+    clipboardSteps: [],
+    propertyOnly: false,
+    setSelectorTabIndex: (selectorTabIndex?: string | number) => {
+        set({selectorTabIndex: selectorTabIndex})
+    },
+    setSelectedStep: (selectedStep?: CamelElement) => {
+        set({selectedStep: selectedStep})
+    },
+    setSelectedPosition: (selectedPosition?: number) => {
+        set({selectedPosition: selectedPosition})
+    },
+    setParentDsl: (parentDsl?: string) => {
+        set({parentDsl: parentDsl})
+    },
+    setShowSelector: (showSelector: boolean) => {
+        set({showSelector: showSelector})
+    },
+    setShowDeleteConfirmation: (showDeleteConfirmation: boolean) => {
+        set({showDeleteConfirmation: showDeleteConfirmation})
+    },
+    setShowMoveConfirmation: (showMoveConfirmation: boolean) => {
+        set({showMoveConfirmation: showMoveConfirmation})
+    },
+    setPropertyOnly: (propertyOnly: boolean) => {
+        set({propertyOnly: propertyOnly})
+    },
+    setShowSteps: (showSteps: boolean) => {
+        set({showSteps: showSteps})
+    },
+    setDeleteMessage: (deleteMessage: string) => {
+        set({deleteMessage: deleteMessage})
+    },
+    setParentId: (parentId: string) => {
+        set({parentId: parentId})
+    },
+    setSelectedUuids: (selectedUuids: string[]) => {
+        set({selectedUuids: selectedUuids})
+    },
+    setClipboardSteps: (clipboardSteps: string[]) => {
+        set({clipboardSteps: clipboardSteps})
+    },
+    width: 1000,
+    height: 1000,
+    top: 0,
+    left: 0,
+    setPosition: (width: number, height: number, top: number, left: number) => {
+        set({width: width, height: height, top: top, left: left})
+    },
+}), shallow)
\ No newline at end of file
diff --git a/karavan-designer/src/designer/route/DslConnections.tsx b/karavan-designer/src/designer/route/DslConnections.tsx
index 938042d4..db7cd2dd 100644
--- a/karavan-designer/src/designer/route/DslConnections.tsx
+++ b/karavan-designer/src/designer/route/DslConnections.tsx
@@ -14,67 +14,61 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import React from 'react';
+import React, {useEffect, useState} from 'react';
 import '../karavan.css';
-import {Integration, CamelElement} from "karavan-core/lib/model/IntegrationDefinition";
+import {CamelElement} from "karavan-core/lib/model/IntegrationDefinition";
 import {DslPosition, EventBus} from "../utils/EventBus";
 import {CamelUi} from "../utils/CamelUi";
 import {Subscription} from "rxjs";
 import {SagaDefinition} from "karavan-core/lib/model/CamelDefinition";
+import {useDesignerStore} from "../KaravanStore";
 
-interface Props {
-    integration: Integration
-    width: number
-    height: number
-    top: number
-    left: number
-}
-
-interface State {
-    integration: Integration
-    steps: Map<string, DslPosition>
-}
 
 const overlapGap: number = 40;
 const outgoingDefinitions: string[] = ['ToDefinition', 'KameletDefinition', 'ToDynamicDefinition', "PollEnrichDefinition", "EnrichDefinition", "WireTapDefinition", "SagaDefinition"];
 
+export const DslConnections = () => {
 
-export class DslConnections extends React.Component<Props, State> {
-
-    public state: State = {
-        integration: this.props.integration,
-        steps: new Map<string, DslPosition>(),
-    };
-    sub?: Subscription;
+    const [ width, height, top, left] = useDesignerStore((s) =>
+        [s.width, s.height, s.top, s.left])
+    const [steps, setSteps] = useState<Map<string, DslPosition>>(new Map<string, DslPosition>());
 
-    componentDidMount() {
-        this.sub = EventBus.onPosition()?.subscribe((evt: DslPosition) => this.setPosition(evt));
-    }
-
-    componentWillUnmount() {
-        this.sub?.unsubscribe();
-    }
+    useEffect(() => {
+        console.log("DslConnections Start", width, height, top, left);
+        const sub = EventBus.onPosition()?.subscribe((evt: DslPosition) => setPosition(evt));
+        return () => {
+            console.log("DslConnections Stop");
+            sub?.unsubscribe();
+        };
+    }, [width, height, top, left]);
 
-    setPosition(evt: DslPosition) {
+    function setPosition(evt: DslPosition) {
         if (evt.command === "add") {
-            this.setState(prevState => ({steps: prevState.steps.set(evt.step.uuid, evt)}));
+            setSteps(prevSteps => {
+                prevSteps.set(evt.step.uuid, evt);
+                return prevSteps;
+            })
+        }
+        else if (evt.command === "delete") {
+            setSteps(prevSteps => {
+                prevSteps.clear();
+                Array.from(prevSteps.entries())
+                    .filter(value => value[1]?.parent?.uuid !== evt.step.uuid)
+                    .forEach(value => prevSteps.set(value[0], value[1]));
+                prevSteps.delete(evt.step.uuid);
+                return prevSteps;
+            })
+        }
+        else if (evt.command === "clean") {
+            setSteps(prevSteps => {
+                prevSteps.clear();
+                return prevSteps;
+            })
         }
-        else if (evt.command === "delete") this.setState(prevState => {
-            prevState.steps.clear();
-            Array.from(prevState.steps.entries())
-                .filter(value => value[1]?.parent?.uuid !== evt.step.uuid)
-                .forEach(value => prevState.steps.set(value[0], value[1]));
-            prevState.steps.delete(evt.step.uuid);
-            return {steps: prevState.steps};
-        });
-        else if (evt.command === "clean") this.setState(prevState => {
-            prevState.steps.clear();
-            return {steps: prevState.steps};
-        });
     }
 
-    getIncomings() {
-        let outs: [string, number][] = Array.from(this.state.steps.values())
+    function getIncomings() {
+        let outs: [string, number][] = Array.from(steps.values())
             .filter(pos => ["FromDefinition"].includes(pos.step.dslName))
             .filter(pos => !CamelUi.isElementInternalComponent(pos.step))
             .filter(pos => !(pos.step.dslName === 'FromDefinition' && CamelUi.hasInternalUri(pos.step)))
@@ -84,17 +78,17 @@ export class DslConnections extends React.Component<Props, State> {
                 return y1 > y2 ? 1 : -1
             })
             .map(pos => [pos.step.uuid, pos.headerRect.y]);
-        while (this.hasOverlap(outs)) {
-            outs = this.addGap(outs);
+        while (hasOverlap(outs)) {
+            outs = addGap(outs);
         }
         return outs;
     }
 
-    getIncoming(data: [string, number]) {
-        const pos = this.state.steps.get(data[0]);
+    function getIncoming(data: [string, number]) {
+        const pos = steps.get(data[0]);
         if (pos) {
-            const fromX = pos.headerRect.x + pos.headerRect.width / 2 - this.props.left;
-            const fromY = pos.headerRect.y + pos.headerRect.height / 2 - this.props.top;
+            const fromX = pos.headerRect.x + pos.headerRect.width / 2 - left;
+            const fromY = pos.headerRect.y + pos.headerRect.height / 2 - top;
             const r = pos.headerRect.height / 2;
 
             const incomingX = 20;
@@ -117,10 +111,10 @@ export class DslConnections extends React.Component<Props, State> {
         }
     }
 
-    getIncomingIcons(data: [string, number]) {
-        const pos = this.state.steps.get(data[0]);
+    function getIncomingIcons(data: [string, number]) {
+        const pos = steps.get(data[0]);
         if (pos) {
-            const fromY = pos.headerRect.y + pos.headerRect.height / 2 - this.props.top;
+            const fromY = pos.headerRect.y + pos.headerRect.height / 2 - top;
             const r = pos.headerRect.height / 2;
             const incomingX = 20;
             const imageX = incomingX - r + 5;
@@ -133,7 +127,7 @@ export class DslConnections extends React.Component<Props, State> {
         }
     }
 
-    hasOverlap(data: [string, number][]): boolean {
+    function hasOverlap(data: [string, number][]): boolean {
         let result = false;
         data.forEach((d, i, arr) => {
             if (i > 0 && d[1] - arr[i - 1][1] < overlapGap) result = true;
@@ -141,7 +135,7 @@ export class DslConnections extends React.Component<Props, State> {
         return result;
     }
 
-    addGap(data: [string, number][]): [string, number][] {
+    function addGap(data: [string, number][]): [string, number][] {
         const result: [string, number][] = [];
         data.forEach((d, i, arr) => {
             if (i > 0 && d[1] - arr[i - 1][1] < overlapGap) result.push([d[0], d[1] + overlapGap])
@@ -151,8 +145,8 @@ export class DslConnections extends React.Component<Props, State> {
     }
 
 
-    getOutgoings(): [string, number][] {
-        let outs: [string, number][] = Array.from(this.state.steps.values())
+    function getOutgoings(): [string, number][] {
+        let outs: [string, number][] = Array.from(steps.values())
             .filter(pos => outgoingDefinitions.includes(pos.step.dslName))
             .filter(pos => pos.step.dslName !== 'KameletDefinition' || (pos.step.dslName === 'KameletDefinition' && !CamelUi.isActionKamelet(pos.step)))
             .filter(pos => pos.step.dslName === 'ToDefinition' && !CamelUi.isActionKamelet(pos.step) && !CamelUi.isElementInternalComponent(pos.step))
@@ -163,21 +157,21 @@ export class DslConnections extends React.Component<Props, State> {
                 const y2 = pos2.headerRect.y + pos2.headerRect.height / 2;
                 return y1 > y2 ? 1 : -1
             })
-            .map(pos => [pos.step.uuid, pos.headerRect.y - this.props.top]);
-        while (this.hasOverlap(outs)) {
-            outs = this.addGap(outs);
+            .map(pos => [pos.step.uuid, pos.headerRect.y - top]);
+        while (hasOverlap(outs)) {
+            outs = addGap(outs);
         }
         return outs;
     }
 
-    getOutgoing(data: [string, number]) {
-        const pos = this.state.steps.get(data[0]);
+    function getOutgoing(data: [string, number]) {
+        const pos = steps.get(data[0]);
         if (pos) {
-            const fromX = pos.headerRect.x + pos.headerRect.width / 2 - this.props.left;
-            const fromY = pos.headerRect.y + pos.headerRect.height / 2 - this.props.top;
+            const fromX = pos.headerRect.x + pos.headerRect.width / 2 - left;
+            const fromY = pos.headerRect.y + pos.headerRect.height / 2 - top;
             const r = pos.headerRect.height / 2;
 
-            const outgoingX = this.props.width - 20;
+            const outgoingX = width - 20;
             const outgoingY = data[1] + 15;
 
             const lineX1 = fromX + r;
@@ -203,11 +197,11 @@ export class DslConnections extends React.Component<Props, State> {
         }
     }
 
-    getOutgoingIcons(data: [string, number]) {
-        const pos = this.state.steps.get(data[0]);
+    function getOutgoingIcons(data: [string, number]) {
+        const pos = steps.get(data[0]);
         if (pos) {
             const r = pos.headerRect.height / 2;
-            const outgoingX = this.props.width - 20;
+            const outgoingX = width - 20;
             const outgoingY = data[1] + 15;
             const imageX = outgoingX - r + 5;
             const imageY = outgoingY - r + 5;
@@ -219,55 +213,55 @@ export class DslConnections extends React.Component<Props, State> {
         }
     }
 
-    getInternals(): [string, number, boolean][] {
-        let outs: [string, number, boolean][] = Array.from(this.state.steps.values())
+    function getInternals(): [string, number, boolean][] {
+        let outs: [string, number, boolean][] = Array.from(steps.values())
             .filter(pos => outgoingDefinitions.includes(pos.step.dslName) && CamelUi.hasInternalUri(pos.step))
             .sort((pos1: DslPosition, pos2: DslPosition) => {
                 const y1 = pos1.headerRect.y + pos1.headerRect.height / 2;
                 const y2 = pos2.headerRect.y + pos2.headerRect.height / 2;
                 return y1 > y2 ? 1 : -1
             })
-            .map(pos => [pos.step.uuid, pos.headerRect.y - this.props.top, pos.isSelected]);
+            .map(pos => [pos.step.uuid, pos.headerRect.y - top, pos.isSelected]);
         return outs;
     }
 
-    getInternalLines(data: [string, number, boolean]) {
-        const pos = this.state.steps.get(data[0]);
+    function getInternalLines(data: [string, number, boolean]) {
+        const pos = steps.get(data[0]);
         const uri = (pos?.step as any).uri;
         if (uri && uri.length && pos) {
             const key = pos.step.uuid + "-outgoing"
-            const fromX = pos.headerRect.x + pos.headerRect.width / 2 - this.props.left;
-            const fromY = pos.headerRect.y + pos.headerRect.height / 2 - this.props.top;
+            const fromX = pos.headerRect.x + pos.headerRect.width / 2 - left;
+            const fromY = pos.headerRect.y + pos.headerRect.height / 2 - top;
             const r = pos.headerRect.height / 2;
             const className = (CamelUi.hasDirectUri(pos.step) ? "path-direct" : "path-seda") + (data[2] ? "-selected" : "");
-            return this.getInternalLine(uri, key, className, fromX, fromY, r, data[1]);
+            return getInternalLine(uri, key, className, fromX, fromY, r, data[1]);
         } else if (pos?.step.dslName === 'SagaDefinition'){
             const saga = (pos?.step as SagaDefinition);
-            const fromX = pos.headerRect.x + pos.headerRect.width / 2 - this.props.left;
-            const fromY = pos.headerRect.y + pos.headerRect.height / 2 - this.props.top;
+            const fromX = pos.headerRect.x + pos.headerRect.width / 2 - left;
+            const fromY = pos.headerRect.y + pos.headerRect.height / 2 - top;
             const r = pos.headerRect.height / 2;
             const result:any[] = [];
             if (saga.completion && (saga.completion.startsWith("direct") || saga.completion.startsWith("seda"))){
                 const key = pos.step.uuid + "-completion"
                 const className = saga.completion.startsWith("direct") ? "path-direct" : "path-seda";
-                result.push(this.getInternalLine(saga.completion, key, className, fromX, fromY, r, data[1]));
+                result.push(getInternalLine(saga.completion, key, className, fromX, fromY, r, data[1]));
             }
             if (saga.compensation && (saga.compensation.startsWith("direct") || saga.compensation.startsWith("seda"))){
                 const key = pos.step.uuid + "-compensation"
                 const className = saga.compensation.startsWith("direct") ? "path-direct" : "path-seda";
-                result.push(this.getInternalLine(saga.compensation, key, className, fromX, fromY, r, data[1]));
+                result.push(getInternalLine(saga.compensation, key, className, fromX, fromY, r, data[1]));
             }
             return result;
         }
     }
 
-    getInternalLine(uri: string, key: string, className: string, fromX: number, fromY: number, r: number, i: number) {
-        const target = Array.from(this.state.steps.values())
+    function getInternalLine(uri: string, key: string, className: string, fromX: number, fromY: number, r: number, i: number) {
+        const target = Array.from(steps.values())
             .filter(s => s.step.dslName === 'FromDefinition')
             .filter(s => (s.step as any).uri && (s.step as any).uri === uri)[0];
         if (target) {
-            const targetX = target.headerRect.x + target.headerRect.width / 2 - this.props.left;
-            const targetY = target.headerRect.y + target.headerRect.height / 2 - this.props.top;
+            const targetX = target.headerRect.x + target.headerRect.width / 2 - left;
+            const targetY = target.headerRect.y + target.headerRect.height / 2 - top;
             const gap = 100;
             const add = 0.2;
 
@@ -294,7 +288,7 @@ export class DslConnections extends React.Component<Props, State> {
                 const pointX4 = pointLX + coefX;
                 const pointY4 = endY;
 
-                return this.getInternalPath(key, className, startX, startY, pointX1, pointY1, pointX2, pointY2, pointLX, pointLY, pointX3, pointY3, pointX4, pointY4, endX, endY);
+                return getInternalPath(key, className, startX, startY, pointX1, pointY1, pointX2, pointY2, pointLX, pointLY, pointX3, pointY3, pointX4, pointY4, endX, endY);
             } else if (targetX > fromX && targetX - fromX < gap) {
                 const startX = fromX - r;
                 const startY = fromY;
@@ -317,7 +311,7 @@ export class DslConnections extends React.Component<Props, State> {
                 const pointX4 = pointLX - coefX/2;
                 const pointY4 = endY;
 
-                return this.getInternalPath(key, className, startX, startY, pointX1, pointY1, pointX2, pointY2, pointLX, pointLY, pointX3, pointY3, pointX4, pointY4, endX, endY);
+                return getInternalPath(key, className, startX, startY, pointX1, pointY1, pointX2, pointY2, pointLX, pointLY, pointX3, pointY3, pointX4, pointY4, endX, endY);
             } else if (targetX <= fromX && fromX - targetX < gap) {
                 const startX = fromX + r;
                 const startY = fromY;
@@ -340,7 +334,7 @@ export class DslConnections extends React.Component<Props, State> {
                 const pointX4 = pointLX - coefX/2;
                 const pointY4 = endY;
 
-                return this.getInternalPath(key, className, startX, startY, pointX1, pointY1, pointX2, pointY2, pointLX, pointLY, pointX3, pointY3, pointX4, pointY4, endX, endY);
+                return getInternalPath(key, className, startX, startY, pointX1, pointY1, pointX2, pointY2, pointLX, pointLY, pointX3, pointY3, pointX4, pointY4, endX, endY);
             } else {
                 const startX = fromX - r;
                 const startY = fromY;
@@ -363,12 +357,12 @@ export class DslConnections extends React.Component<Props, State> {
                 const pointX4 = pointLX + coefX;
                 const pointY4 = endY;
 
-                return this.getInternalPath(key, className, startX, startY, pointX1, pointY1, pointX2, pointY2, pointLX, pointLY, pointX3, pointY3, pointX4, pointY4, endX, endY);
+                return getInternalPath(key, className, startX, startY, pointX1, pointY1, pointX2, pointY2, pointLX, pointLY, pointX3, pointY3, pointX4, pointY4, endX, endY);
             }
         }
     }
 
-    getInternalPath(key: string, className: string, startX: number, startY: number, pointX1: number, pointY1: number, pointX2: number, pointY2: number, pointLX: number, pointLY: number,
+    function getInternalPath(key: string, className: string, startX: number, startY: number, pointX1: number, pointY1: number, pointX2: number, pointY2: number, pointLX: number, pointLY: number,
                     pointX3: number, pointY3: number, pointX4: number, pointY4: number, endX: number, endY: number) {
         return (
             <g key={key}>
@@ -380,35 +374,35 @@ export class DslConnections extends React.Component<Props, State> {
         )
     }
 
-    getCircle(pos: DslPosition) {
-        const cx = pos.headerRect.x + pos.headerRect.width / 2 - this.props.left;
-        const cy = pos.headerRect.y + pos.headerRect.height / 2 - this.props.top;
+    function getCircle(pos: DslPosition) {
+        const cx = pos.headerRect.x + pos.headerRect.width / 2 - left;
+        const cy = pos.headerRect.y + pos.headerRect.height / 2 - top;
         const r = pos.headerRect.height / 2;
         return (
             <circle cx={cx} cy={cy} r={r} stroke="transparent" strokeWidth="3" fill="transparent" key={pos.step.uuid + "-circle"}/>
         )
     }
 
-    hasSteps = (step: CamelElement): boolean => {
+    function hasSteps  (step: CamelElement): boolean  {
         return (step.hasSteps() && !['FromDefinition'].includes(step.dslName))
             || ['RouteDefinition', 'TryDefinition', 'ChoiceDefinition', 'SwitchDefinition'].includes(step.dslName);
     }
 
-    getPreviousStep(pos: DslPosition) {
-        return Array.from(this.state.steps.values())
+    function getPreviousStep(pos: DslPosition) {
+        return Array.from(steps.values())
             .filter(p => pos.parent?.uuid === p.parent?.uuid)
             .filter(p => p.inSteps)
             .filter(p => p.position === pos.position - 1)[0];
     }
 
-    getArrow(pos: DslPosition) {
-        const endX = pos.headerRect.x + pos.headerRect.width / 2 - this.props.left;
-        const endY = pos.headerRect.y - 9 - this.props.top;
+    function getArrow(pos: DslPosition) {
+        const endX = pos.headerRect.x + pos.headerRect.width / 2 - left;
+        const endY = pos.headerRect.y - 9 - top;
         if (pos.parent) {
-            const parent = this.state.steps.get(pos.parent.uuid);
+            const parent = steps.get(pos.parent.uuid);
             if (parent) {
-                const startX = parent.headerRect.x + parent.headerRect.width / 2 - this.props.left;
-                const startY = parent.headerRect.y + parent.headerRect.height - this.props.top;
+                const startX = parent.headerRect.x + parent.headerRect.width / 2 - left;
+                const startY = parent.headerRect.y + parent.headerRect.height - top;
                 if ((!pos.inSteps || (pos.inSteps && pos.position === 0)) && parent.step.dslName !== 'MulticastDefinition') {
                     return (
                         <path name={pos.step.dslName} d={`M ${startX},${startY} C ${startX},${endY} ${endX},${startY}   ${endX},${endY}`}
@@ -419,22 +413,22 @@ export class DslConnections extends React.Component<Props, State> {
                         <path d={`M ${startX},${startY} C ${startX},${endY} ${endX},${startY}   ${endX},${endY}`}
                               className="path" key={pos.step.uuid} markerEnd="url(#arrowhead)"/>
                     )
-                } else if (pos.inSteps && pos.position > 0 && !this.hasSteps(pos.step)) {
-                    const prev = this.getPreviousStep(pos);
+                } else if (pos.inSteps && pos.position > 0 && !hasSteps(pos.step)) {
+                    const prev = getPreviousStep(pos);
                     if (prev) {
-                        const r = this.hasSteps(prev.step) ? prev.rect : prev.headerRect;
-                        const prevX = r.x + r.width / 2 - this.props.left;
-                        const prevY = r.y + r.height - this.props.top;
+                        const r = hasSteps(prev.step) ? prev.rect : prev.headerRect;
+                        const prevX = r.x + r.width / 2 - left;
+                        const prevY = r.y + r.height - top;
                         return (
                             <line x1={prevX} y1={prevY} x2={endX} y2={endY} className="path" key={pos.step.uuid} markerEnd="url(#arrowhead)"/>
                         )
                     }
-                } else if (pos.inSteps && pos.position > 0 && this.hasSteps(pos.step)) {
-                    const prev = this.getPreviousStep(pos);
+                } else if (pos.inSteps && pos.position > 0 && hasSteps(pos.step)) {
+                    const prev = getPreviousStep(pos);
                     if (prev) {
-                        const r = this.hasSteps(prev.step) ? prev.rect : prev.headerRect;
-                        const prevX = r.x + r.width / 2 - this.props.left;
-                        const prevY = r.y + r.height - this.props.top;
+                        const r = hasSteps(prev.step) ? prev.rect : prev.headerRect;
+                        const prevX = r.x + r.width / 2 - left;
+                        const prevY = r.y + r.height - top;
                         return (
                             <line x1={prevX} y1={prevY} x2={endX} y2={endY} className="path" key={pos.step.uuid} markerEnd="url(#arrowhead)"/>
                         )
@@ -444,33 +438,31 @@ export class DslConnections extends React.Component<Props, State> {
         }
     }
 
-    getSvg() {
-        const steps = Array.from(this.state.steps.values());
+    function getSvg() {
+        const stepsArray = Array.from(steps.values());
         return (
             <svg
-                style={{width: this.props.width, height: this.props.height, position: "absolute", left: 0, top: 0}}
-                viewBox={"0 0 " + this.props.width + " " + this.props.height}>
+                style={{width: width, height: height, position: "absolute", left: 0, top: 0}}
+                viewBox={"0 0 " + width + " " + height}>
                 <defs>
                     <marker id="arrowhead" markerWidth="9" markerHeight="6" refX="0" refY="3" orient="auto" className="arrow">
                         <polygon points="0 0, 9 3, 0 6"/>
                     </marker>
                 </defs>
-                {steps.map(pos => this.getCircle(pos))}
-                {steps.map(pos => this.getArrow(pos))}
-                {this.getIncomings().map(p => this.getIncoming(p))}
-                {this.getOutgoings().map(p => this.getOutgoing(p))}
-                {/*{this.getInternals().map((p) => this.getInternalLines(p)).flat()}*/}
+                {stepsArray.map(pos => getCircle(pos))}
+                {stepsArray.map(pos => getArrow(pos))}
+                {getIncomings().map(p => getIncoming(p))}
+                {getOutgoings().map(p => getOutgoing(p))}
+                {/*{getInternals().map((p) => getInternalLines(p)).flat()}*/}
             </svg>
         )
     }
 
-    render() {
-        return (
-            <div className="connections" style={{width: this.props.width, height: this.props.height, marginTop: "8px"}}>
-                {this.getSvg()}
-                {this.getIncomings().map(p => this.getIncomingIcons(p))}
-                {this.getOutgoings().map(p => this.getOutgoingIcons(p))}
-            </div>
-        );
-    }
+    return (
+        <div className="connections" style={{width: width, height: height, marginTop: "0px"}}>
+            {getSvg()}
+            {getIncomings().map(p => getIncomingIcons(p))}
+            {getOutgoings().map(p => getOutgoingIcons(p))}
+        </div>
+    )
 }
diff --git a/karavan-designer/src/designer/route/DslElement.tsx b/karavan-designer/src/designer/route/DslElement.tsx
index 2ea1aaa2..2b498d02 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} from 'react';
+import React, {createRef, CSSProperties, useState} from 'react';
 import {
     Button,
     Flex,
@@ -27,145 +27,149 @@ import DeleteIcon from "@patternfly/react-icons/dist/js/icons/times-circle-icon"
 import InsertIcon from "@patternfly/react-icons/dist/js/icons/arrow-alt-circle-right-icon";
 import {CamelElement, Integration} from "karavan-core/lib/model/IntegrationDefinition";
 import {CamelUi} from "../utils/CamelUi";
-import {EventBus} from "../utils/EventBus";
+import {DslPosition, EventBus} from "../utils/EventBus";
 import {ChildElement, CamelDefinitionApiExt} from "karavan-core/lib/api/CamelDefinitionApiExt";
 import ReactDOM from "react-dom";
 import {CamelUtil} from "karavan-core/lib/api/CamelUtil";
 import {CamelDisplayUtil} from "karavan-core/lib/api/CamelDisplayUtil";
+import {useDesignerStore, useIntegrationStore} from "../KaravanStore";
+import {shallow} from "zustand/shallow";
+import {useRouteDesignerHook} from "./useRouteDesignerHook";
 
 interface Props {
-    integration: Integration,
+    // integration: Integration,
     step: CamelElement,
     parent: CamelElement | undefined,
-    deleteElement: any
-    selectElement: any
-    openSelector: (parentId: string | undefined, parentDsl: string | undefined, showSteps: boolean, position?: number | undefined) => void
-    moveElement: (source: string, target: string, asChild: boolean) => void
-    selectedUuid: string []
+    // deleteElement: any
+    // selectElement: any
+    // openSelector: (parentId: string | undefined, parentDsl: string | undefined, showSteps: boolean, position?: number | undefined) => void
+    // moveElement: (source: string, target: string, asChild: boolean) => void
+    // selectedUuid: string []
     inSteps: boolean
     position: number
 }
 
-interface State {
-    showSelector: boolean
-    showMoveConfirmation: boolean
-    moveElements: [string | undefined, string | undefined]
-    tabIndex: string | number
-    isDragging: boolean
-    isDraggedOver: boolean
-}
+export const DslElement = (props: Props) => {
+
+    const ref = React.createRef();
+    const { deleteElement, selectElement, moveElement, onShowDeleteConfirmation, openSelector } = useRouteDesignerHook();
 
-export class DslElement extends React.Component<Props, State> {
+    const [integration, setIntegration] = useIntegrationStore((s) => [s.integration, s.setIntegration], shallow)
 
-    public state: State = {
-        showSelector: false,
-        showMoveConfirmation: false,
-        moveElements: [undefined, undefined],
-        tabIndex: 0,
-        isDragging: false,
-        isDraggedOver: false,
-    };
+    const [showSelector, showDeleteConfirmation, propertyOnly, showSteps,deleteMessage,parentId, selectedUuids,clipboardSteps, parentDsl,selectedPosition,selectedStep,selectorTabIndex,showMoveConfirmation,
+        setShowSelector, setShowDeleteConfirmation,setPropertyOnly,setShowSteps,setDeleteMessage, setParentId, setParentDsl, setSelectedPosition, setSelectedUuids, setClipboardSteps,setPosition,setShowMoveConfirmation,
+        width, height, top, left] = useDesignerStore((s) =>
+        [s.showSelector, s.showDeleteConfirmation, s.propertyOnly, s.showSteps,s.deleteMessage,s.parentId, s.selectedUuids,s.clipboardSteps, s.parentDsl, s.selectedPosition, s.selectedStep,s.selectorTabIndex, s.showMoveConfirmation,
+            s.setShowSelector, s.setShowDeleteConfirmation,s.setPropertyOnly,s.setShowSteps,s.setDeleteMessage, s.setParentId, s.setParentDsl, s.setSelectedPosition, 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]);
 
     //
     // componentDidUpdate = (prevProps: Readonly<Props>, prevState: Readonly<State>, snapshot?: any) => {
-    //     if (prevState.selectedUuid !== this.props.selectedUuid) {
-    //         this.setState({selectedUuid: this.props.selectedUuid});
+    //     if (prevselectedUuid !== props.selectedUuid) {
+    //         setState({selectedUuid: props.selectedUuid});
     //     }
     // }
 
-    openSelector = (evt: React.MouseEvent, showSteps: boolean = true, isInsert: boolean = false) => {
+    function onOpenSelector (evt: React.MouseEvent, showSteps: boolean = true, isInsert: boolean = false) {
         evt.stopPropagation();
-        if (isInsert && this.props.parent) {
-            this.props.openSelector.call(this, this.props.parent.uuid, this.props.parent.dslName, showSteps, this.props.position);
+        if (isInsert && props.parent) {
+            openSelector(props.parent.uuid, props.parent.dslName, showSteps, props.position);
         } else {
-            this.props.openSelector.call(this, this.props.step.uuid, this.props.step.dslName, showSteps);
+            openSelector(props.step.uuid, props.step.dslName, showSteps);
         }
     }
 
-    closeDslSelector = () => {
-        this.setState({showSelector: false})
+    function closeDslSelector () {
+        // setState({showSelector: false})
+        setShowSelector(false);
     }
 
-    delete = (evt: React.MouseEvent) => {
+    function onDeleteElement (evt: React.MouseEvent) {
         evt.stopPropagation();
-        this.props.deleteElement.call(this, this.props.step.uuid);
+        onShowDeleteConfirmation(props.step.uuid);
     }
 
-    selectElement = (evt: React.MouseEvent) => {
+    function onSelectElement (evt: React.MouseEvent) {
         evt.stopPropagation();
-        this.props.selectElement.call(this, this.props.step);
+        selectElement(props.step);
     }
 
-    dragElement = (event: React.DragEvent<HTMLDivElement>, element: CamelElement) => {
+    function dragElement (event: React.DragEvent<HTMLDivElement>, element: CamelElement) {
         event.preventDefault();
         event.stopPropagation();
-        this.setState({isDraggedOver: false});
+        setIsDraggedOver(false);
         const sourceUuid = event.dataTransfer.getData("text/plain");
         const targetUuid = element.uuid;
         if (sourceUuid !== targetUuid) {
             if (element.hasSteps()){
-                this.setState({showMoveConfirmation: true, moveElements: [sourceUuid, targetUuid]});
+                setShowMoveConfirmation(true);
+                setMoveElements([sourceUuid, targetUuid])
             } else {
-                this.props.moveElement?.call(this, sourceUuid, targetUuid, false);
+                moveElement(sourceUuid, targetUuid, false);
             }
         }
     }
 
-    confirmMove = (asChild: boolean) => {
-        const sourceUuid = this.state.moveElements[0];
-        const targetUuid = this.state.moveElements[1];
+    function confirmMove (asChild: boolean) {
+        const sourceUuid = moveElements[0];
+        const targetUuid = moveElements[1];
         if (sourceUuid && targetUuid && sourceUuid !== targetUuid) {
-            this.props.moveElement?.call(this, sourceUuid, targetUuid, asChild);
-            this.setState({showMoveConfirmation: false, moveElements: [undefined, undefined]})
+            moveElement(sourceUuid, targetUuid, asChild);
+            cancelMove();
         }
     }
 
-    cancelMove = () => {
-        this.setState({showMoveConfirmation: false, moveElements: [undefined, undefined]})
+    function cancelMove () {
+        setShowMoveConfirmation(false);
+        setMoveElements([undefined, undefined]);
     }
 
-    isSelected = (): boolean => {
-        return this.props.selectedUuid.includes(this.props.step.uuid);
+    function isSelected (): boolean {
+        return selectedUuids.includes(props.step.uuid);
     }
 
-    hasBorder = (): boolean => {
-        return (this.props.step?.hasSteps() && !['FromDefinition'].includes(this.props.step.dslName))
+    function hasBorder (): boolean {
+        return (props.step?.hasSteps() && !['FromDefinition'].includes(props.step.dslName))
             || ['RouteConfigurationDefinition',
                 'RouteDefinition',
                 'TryDefinition',
                 'ChoiceDefinition',
-                'SwitchDefinition'].includes(this.props.step.dslName);
+                'SwitchDefinition'].includes(props.step.dslName);
     }
 
-    isNotDraggable = (): boolean => {
-        return ['FromDefinition', 'RouteConfigurationDefinition', 'RouteDefinition', 'WhenDefinition', 'OtherwiseDefinition'].includes(this.props.step.dslName);
+    function isNotDraggable (): boolean {
+        return ['FromDefinition', 'RouteConfigurationDefinition', 'RouteDefinition', 'WhenDefinition', 'OtherwiseDefinition'].includes(props.step.dslName);
     }
 
-    isWide = (): boolean => {
+    function isWide (): boolean {
         return ['RouteConfigurationDefinition', 'RouteDefinition', 'ChoiceDefinition', 'SwitchDefinition', 'MulticastDefinition', 'TryDefinition', 'CircuitBreakerDefinition']
-            .includes(this.props.step.dslName);
+            .includes(props.step.dslName);
     }
 
-    isAddStepButtonLeft = (): boolean => {
+    function isAddStepButtonLeft (): boolean {
         return ['MulticastDefinition']
-            .includes(this.props.step.dslName);
+            .includes(props.step.dslName);
     }
 
-    isHorizontal = (): boolean => {
-        return ['MulticastDefinition'].includes(this.props.step.dslName);
+    function isHorizontal (): boolean {
+        return ['MulticastDefinition'].includes(props.step.dslName);
     }
 
-    isRoot = (): boolean => {
-        return ['RouteConfigurationDefinition', 'RouteDefinition'].includes(this.props.step?.dslName);
+    function isRoot (): boolean {
+        return ['RouteConfigurationDefinition', 'RouteDefinition'].includes(props.step?.dslName);
     }
 
-    isInStepWithChildren = () => {
-        const step: CamelElement = this.props.step;
+    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 && this.props.inSteps;
+        return children.filter((c: ChildElement) => c.name === 'steps' || c.multiple).length > 0 && props.inSteps;
     }
 
-    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
@@ -185,49 +189,50 @@ export class DslElement extends React.Component<Props, State> {
         return [hasStepsField, stepsChildrenCount, hasNonStepsFields, nonStepChildrenCount, childrenCount]
     }
 
-    hasWideChildrenElement = () => {
-        const [hasStepsField, stepsChildrenCount, hasNonStepsFields, nonStepChildrenCount, childrenCount] = this.getChildrenInfo(this.props.step);
-        if (this.isHorizontal() && stepsChildrenCount > 1) return true;
+    function hasWideChildrenElement  ()  {
+        const [hasStepsField, stepsChildrenCount, hasNonStepsFields, nonStepChildrenCount, childrenCount] = getChildrenInfo(props.step);
+        if (isHorizontal() && stepsChildrenCount > 1) return true;
         else if (hasStepsField && stepsChildrenCount > 0 && hasNonStepsFields && nonStepChildrenCount > 0) return true;
         else if (!hasStepsField && hasNonStepsFields && childrenCount > 1) return true;
         else if (hasStepsField && stepsChildrenCount > 0 && hasNonStepsFields && childrenCount > 1) return true;
         else return false;
     }
 
-    hasBorderOverSteps = (step: CamelElement) => {
-        const [hasStepsField, stepsChildrenCount, hasNonStepsFields, nonStepChildrenCount] = this.getChildrenInfo(step);
+    function hasBorderOverSteps  (step: CamelElement)  {
+        const [hasStepsField, stepsChildrenCount, hasNonStepsFields, nonStepChildrenCount] = getChildrenInfo(step);
         if (hasStepsField && stepsChildrenCount > 0 && hasNonStepsFields && nonStepChildrenCount > 0) return true;
         else return false;
     }
 
-    getHeaderStyle = () => {
+    function getHeaderStyle () {
         const style: CSSProperties = {
-            width: this.isWide() ? "100%" : "",
-            fontWeight: this.isSelected() ? "bold" : "normal",
+            width: isWide() ? "100%" : "",
+            fontWeight: isSelected() ? "bold" : "normal",
         };
         return style;
     }
 
-    sendPosition = (el: HTMLDivElement | null, isSelected: boolean) => {
-        const node = ReactDOM.findDOMNode(this);
+    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];
             if (header) {
                 const headerIcon: any = Array.from(header.childNodes.values()).filter((n: any) => n.classList.contains("header-icon"))[0];
                 const headerRect = headerIcon.getBoundingClientRect();
                 const rect = el.getBoundingClientRect();
-                if (this.props.step.show){
-                    EventBus.sendPosition("add", this.props.step, this.props.parent, rect, headerRect, this.props.position, this.props.inSteps, isSelected);
+                if (props.step.show){
+                    EventBus.sendPosition("add", props.step, props.parent, rect, headerRect, props.position, props.inSteps, isSelected);
                 } else {
-                    EventBus.sendPosition("delete", this.props.step, this.props.parent, new DOMRect(), new DOMRect(), 0);
+                    EventBus.sendPosition("delete", props.step, props.parent, new DOMRect(), new DOMRect(), 0);
                 }
             }
         }
     }
 
-    getHeader = () => {
-        const step: CamelElement = this.props.step;
-        const parent = this.props.parent;
+    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;
@@ -235,31 +240,31 @@ export class DslElement extends React.Component<Props, State> {
             !['FromDefinition', 'RouteConfigurationDefinition', 'RouteDefinition', 'CatchDefinition', 'FinallyDefinition', 'WhenDefinition', 'OtherwiseDefinition'].includes(step.dslName)
             && !inRouteConfiguration;
         const headerClass = ['RouteConfigurationDefinition', 'RouteDefinition'].includes(step.dslName) ? "header-route" : "header"
-        const headerClasses = this.isSelected() ? headerClass + " selected" : headerClass;
+        const headerClasses = isSelected() ? headerClass + " selected" : headerClass;
         return (
-            <div className={headerClasses} style={this.getHeaderStyle()}>
-                {!['RouteConfigurationDefinition', 'RouteDefinition'].includes(this.props.step.dslName) &&
-                    <div ref={el => this.sendPosition(el, this.isSelected())}
+            <div className={headerClasses} style={getHeaderStyle()}>
+                {!['RouteConfigurationDefinition', 'RouteDefinition'].includes(props.step.dslName) &&
+                    <div ref={el => sendPosition(el, isSelected())}
                          className={"header-icon"}
-                         style={this.isWide() ? {width: ""} : {}}>
+                         style={isWide() ? {width: ""} : {}}>
                         {CamelUi.getIconForElement(step)}
                     </div>
                 }
-                <div className={this.hasWideChildrenElement() ? "header-text" : ""}>
-                    {this.hasWideChildrenElement() && <div className="spacer"/>}
-                    {this.getHeaderTextWithTooltip(step)}
+                <div className={hasWideChildrenElement() ? "header-text" : ""}>
+                    {hasWideChildrenElement() && <div className="spacer"/>}
+                    {getHeaderTextWithTooltip(step)}
                 </div>
-                {showInsertButton && this.getInsertElementButton()}
-                {this.getDeleteButton()}
-                {showAddButton && this.getAddElementButton()}
+                {showInsertButton && getInsertElementButton()}
+                {getDeleteButton()}
+                {showAddButton && getAddElementButton()}
             </div>
         )
     }
 
-    getHeaderTextWithTooltip = (step: CamelElement) => {
+    function getHeaderTextWithTooltip (step: CamelElement) {
         const checkRequired = CamelUtil.checkRequired(step);
-        const title = (step as any).description ? (step as any).description : CamelUi.getElementTitle(this.props.step);
-        let className = this.hasWideChildrenElement() ? "text text-right" : "text text-bottom";
+        const title = (step as any).description ? (step as any).description : CamelUi.getElementTitle(props.step);
+        let className = hasWideChildrenElement() ? "text text-right" : "text text-bottom";
         if (!checkRequired[0]) className = className + " header-text-required";
         if (checkRequired[0]) return <Text className={className}>{title}</Text>
         else return (
@@ -270,30 +275,30 @@ export class DslElement extends React.Component<Props, State> {
         )
     }
 
-    getHeaderWithTooltip = (tooltip: string | undefined) => {
+    function getHeaderWithTooltip (tooltip: string | undefined) {
         return (
             <Tooltip position={"left"}
                      content={<div>{tooltip}</div>}>
-                {this.getHeader()}
+                {getHeader()}
             </Tooltip>
         )
     }
 
-    getHeaderTooltip = (): string | undefined => {
-        if (CamelUi.isShowExpressionTooltip(this.props.step)) return CamelUi.getExpressionTooltip(this.props.step);
-        if (CamelUi.isShowUriTooltip(this.props.step)) return CamelUi.getUriTooltip(this.props.step);
+    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;
     }
 
-    getElementHeader = () => {
-        const tooltip = this.getHeaderTooltip();
-        if (tooltip !== undefined && !this.state.isDragging) {
-            return this.getHeaderWithTooltip(tooltip);
+    function getElementHeader () {
+        const tooltip = getHeaderTooltip();
+        if (tooltip !== undefined && !isDragging) {
+            return getHeaderWithTooltip(tooltip);
         }
-        return this.getHeader();
+        return getHeader();
     }
 
-    getChildrenStyle = () => {
+    function getChildrenStyle ()  {
         const style: CSSProperties = {
             display: "flex",
             flexDirection: "row",
@@ -301,22 +306,22 @@ export class DslElement extends React.Component<Props, State> {
         return style;
     }
 
-    getChildrenElementsStyle = (child: ChildElement, notOnlySteps: boolean) => {
-        const step = this.props.step;
-        const isBorder = child.name === 'steps' && this.hasBorderOverSteps(step);
+    function getChildrenElementsStyle (child: ChildElement, notOnlySteps: boolean)  {
+        const step = props.step;
+        const isBorder = child.name === 'steps' && hasBorderOverSteps(step);
         const style: CSSProperties = {
             borderStyle: isBorder ? "dotted" : "none",
             borderColor: "var(--step-border-color)",
             borderWidth: "1px",
             borderRadius: "16px",
-            display: this.isHorizontal() || child.name !== 'steps' ? "flex" : "block",
+            display: isHorizontal() || child.name !== 'steps' ? "flex" : "block",
             flexDirection: "row",
         }
         return style;
     }
 
-    getChildElements = () => {
-        const step: CamelElement = this.props.step;
+    function getChildElements () {
+        const step: CamelElement = props.step;
         let children: ChildElement[] = CamelDefinitionApiExt.getElementChildrenDefinition(step.dslName);
         const notOnlySteps = children.filter(c => c.name === 'steps').length === 1
             && children.filter(c => c.multiple && c.name !== 'steps').length > 0;
@@ -331,67 +336,61 @@ export class DslElement extends React.Component<Props, State> {
             children = children.filter(value => value.name !== 'onWhen')
         }
         return (
-            <div key={step.uuid + "-children"} className="children" style={this.getChildrenStyle()}>
-                {children.map((child: ChildElement, index: number) => this.getChildDslElements(child, index, notOnlySteps))}
+            <div key={step.uuid + "-children"} className="children" style={getChildrenStyle()}>
+                {children.map((child: ChildElement, index: number) => getChildDslElements(child, index, notOnlySteps))}
             </div>
         )
     }
 
-    getChildDslElements = (child: ChildElement, index: number, notOnlySteps: boolean) => {
-        const step = this.props.step;
+    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={this.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
-                                integration={this.props.integration}
-                                openSelector={this.props.openSelector}
-                                deleteElement={this.props.deleteElement}
-                                selectElement={this.props.selectElement}
-                                moveElement={this.props.moveElement}
-                                selectedUuid={this.props.selectedUuid}
                                 inSteps={child.name === 'steps'}
                                 position={index}
                                 step={element}
                                 parent={step}/>
                         </div>
                     ))}
-                    {child.name === 'steps' && this.getAddStepButton()}
+                    {child.name === 'steps' && getAddStepButton()}
                 </div>
             )
         } else if (child.name === 'steps') {
             return (
-                <div className={child.name + " has-child"} style={this.getChildrenElementsStyle(child, notOnlySteps)} key={step.uuid + "-child-" + index}>
-                    {this.getAddStepButton()}
+                <div className={child.name + " has-child"} style={getChildrenElementsStyle(child, notOnlySteps)} key={step.uuid + "-child-" + index}>
+                    {getAddStepButton()}
                 </div>
             )
         }
     }
 
-    getAddStepButton() {
-        const {integration, step, selectedUuid} = this.props;
-        const hideAddButton = step.dslName === 'StepDefinition' && !CamelDisplayUtil.isStepDefinitionExpanded(integration, step.uuid, selectedUuid.at(0));
+    function getAddStepButton() {
+        const { step} = props;
+        const hideAddButton = step.dslName === 'StepDefinition' && !CamelDisplayUtil.isStepDefinitionExpanded(integration, step.uuid, selectedUuids.at(0));
         if (hideAddButton) return (<></>)
         else return (
             <Tooltip position={"bottom"}
                      content={<div>{"Add step to " + CamelUi.getTitle(step)}</div>}>
-                <button type="button" aria-label="Add" onClick={e => this.openSelector(e)}
-                        className={this.isAddStepButtonLeft() ? "add-button add-button-left" : "add-button add-button-bottom"}>
+                <button type="button" aria-label="Add" onClick={e => onOpenSelector(e)}
+                        className={isAddStepButtonLeft() ? "add-button add-button-left" : "add-button add-button-bottom"}>
                     <AddIcon />
                 </button>
             </Tooltip>
         )
     }
 
-    getAddElementButton() {
+    function getAddElementButton() {
         return (
-            <Tooltip position={"bottom"} content={<div>{"Add DSL element to " + CamelUi.getTitle(this.props.step)}</div>}>
+            <Tooltip position={"bottom"} content={<div>{"Add DSL element to " + CamelUi.getTitle(props.step)}</div>}>
                 <button
                     type="button"
                     aria-label="Add"
-                    onClick={e => this.openSelector(e, false)}
+                    onClick={e => onOpenSelector(e, false)}
                     className={"add-element-button"}>
                     <AddIcon />
                 </button>
@@ -399,95 +398,92 @@ export class DslElement extends React.Component<Props, State> {
         )
     }
 
-    getInsertElementButton() {
+    function getInsertElementButton() {
         return (
             <Tooltip position={"left"} content={<div>{"Insert element before"}</div>}>
-                <button type="button" aria-label="Insert" onClick={e => this.openSelector(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>
         )
     }
 
-    getDeleteButton() {
+    function getDeleteButton() {
         return (
             <Tooltip position={"right"} content={<div>{"Delete element"}</div>}>
-                <button type="button" aria-label="Delete" onClick={e => this.delete(e)} className="delete-button"><DeleteIcon /></button>
+                <button type="button" aria-label="Delete" onClick={e => onDeleteElement(e)} className="delete-button"><DeleteIcon /></button>
             </Tooltip>
         )
     }
 
-    getMoveConfirmation() {
+    function getMoveConfirmation() {
         return (
             <Modal
                 aria-label="title"
                 className='move-modal'
-                isOpen={this.state.showMoveConfirmation}
+                isOpen={showMoveConfirmation}
                 variant={ModalVariant.small}
             ><Flex direction={{default: "column"}}>
                 <div>Select move type:</div>
-                <Button key="place" variant="primary" onClick={event => this.confirmMove(false)}>Shift (target down)</Button>
-                <Button key="child" variant="secondary" onClick={event => this.confirmMove(true)}>Move as target step</Button>
-                <Button key="cancel" variant="tertiary" onClick={event => this.cancelMove()}>Cancel</Button>
+                <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="cancel" variant="tertiary" onClick={event => cancelMove()}>Cancel</Button>
             </Flex>
 
             </Modal>
         )
     }
 
-    render() {
-        const element: CamelElement = this.props.step;
-        const className = "step-element" + (this.isSelected() ? " step-element-selected" : "")
-            + (!this.props.step.show ? " hidden-step" : "");
-        return (
-            <div key={"root" + element.uuid}
-                 className={className}
-                 ref={el => this.sendPosition(el, this.isSelected())}
-                 style={{
-                     borderStyle: this.hasBorder() ? "dotted" : "none",
-                     borderColor: this.isSelected() ? "var(--step-border-color-selected)" : "var(--step-border-color)",
-                     marginTop: this.isInStepWithChildren() ? "16px" : "8px",
-                     zIndex: element.dslName === 'ToDefinition' ? 20 : 10,
-                     boxShadow: this.state.isDraggedOver ? "0px 0px 1px 2px var(--step-border-color-selected)" : "none",
-                 }}
-                 onMouseOver={event => event.stopPropagation()}
-                 onClick={event => this.selectElement(event)}
-                 onDragStart={event => {
-                     event.stopPropagation();
-                     event.dataTransfer.setData("text/plain", element.uuid);
-                     (event.target as any).style.opacity = .5;
-                     this.setState({isDragging: true});
-                 }}
-                 onDragEnd={event => {
-                     (event.target as any).style.opacity = '';
-                     this.setState({isDragging: false});
-                 }}
-                 onDragOver={event => {
-                     event.preventDefault();
-                     event.stopPropagation();
-                     if (element.dslName !== 'FromDefinition' && !this.state.isDragging) {
-                         this.setState({isDraggedOver: true});
-                     }
-                 }}
-                 onDragEnter={event => {
-                     event.preventDefault();
-                     event.stopPropagation();
-                     if (element.dslName !== 'FromDefinition') {
-                         this.setState({isDraggedOver: true});
-                     }
-                 }}
-                 onDragLeave={event => {
-                     event.preventDefault();
-                     event.stopPropagation();
-                     this.setState({isDraggedOver: false});
-
-                 }}
-                 onDrop={event => this.dragElement(event, element)}
-                 draggable={!this.isNotDraggable()}
-            >
-                {this.getElementHeader()}
-                {this.getChildElements()}
-                {this.getMoveConfirmation()}
-            </div>
-        )
-    }
+    const element: CamelElement = props.step;
+    const className = "step-element" + (isSelected() ? " step-element-selected" : "")
+        + (!props.step.show ? " hidden-step" : "");
+    return (
+        <div key={"root" + element.uuid}
+             className={className}
+             ref={el => sendPosition(el, isSelected())}
+             style={{
+                 borderStyle: hasBorder() ? "dotted" : "none",
+                 borderColor: isSelected() ? "var(--step-border-color-selected)" : "var(--step-border-color)",
+                 marginTop: isInStepWithChildren() ? "16px" : "8px",
+                 zIndex: element.dslName === 'ToDefinition' ? 20 : 10,
+                 boxShadow: isDraggedOver ? "0px 0px 1px 2px var(--step-border-color-selected)" : "none",
+             }}
+             onMouseOver={event => event.stopPropagation()}
+             onClick={event => onSelectElement(event)}
+             onDragStart={event => {
+                 event.stopPropagation();
+                 event.dataTransfer.setData("text/plain", element.uuid);
+                 (event.target as any).style.opacity = .5;
+                 setIsDragging(true);
+             }}
+             onDragEnd={event => {
+                 (event.target as any).style.opacity = '';
+                 setIsDragging(false);
+             }}
+             onDragOver={event => {
+                 event.preventDefault();
+                 event.stopPropagation();
+                 if (element.dslName !== 'FromDefinition' && !isDragging) {
+                     setIsDraggedOver(true);
+                 }
+             }}
+             onDragEnter={event => {
+                 event.preventDefault();
+                 event.stopPropagation();
+                 if (element.dslName !== 'FromDefinition') {
+                     setIsDraggedOver(true);
+                 }
+             }}
+             onDragLeave={event => {
+                 event.preventDefault();
+                 event.stopPropagation();
+                 setIsDraggedOver(false);
+             }}
+             onDrop={event => dragElement(event, element)}
+             draggable={!isNotDraggable()}
+        >
+            {getElementHeader()}
+            {getChildElements()}
+            {getMoveConfirmation()}
+        </div>
+    )
 }
diff --git a/karavan-designer/src/designer/route/ElementResizeListener.tsx b/karavan-designer/src/designer/route/ElementResizeListener.tsx
new file mode 100644
index 00000000..4ef53c64
--- /dev/null
+++ b/karavan-designer/src/designer/route/ElementResizeListener.tsx
@@ -0,0 +1,75 @@
+/*
+ * 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 {RefObject, useCallback, useEffect, useRef} from "react";
+
+interface Props {
+    onResize: (event: Event) => void;
+};
+export const ElementResizeListener: React.FC<Props> = ({ onResize }) => {
+    const rafRef = useRef(0);
+    const objectRef: RefObject<HTMLObjectElement> = useRef(null);
+    const onResizeRef = useRef(onResize);
+
+    onResizeRef.current = onResize;
+
+    const _onResize = useCallback((e: Event) => {
+        if (rafRef.current) {
+            cancelAnimationFrame(rafRef.current);
+        }
+        rafRef.current = requestAnimationFrame(() => {
+            onResizeRef.current(e);
+        });
+    }, []);
+
+    const onLoad = useCallback(() => {
+        const obj = objectRef.current;
+        if (obj && obj.contentDocument && obj.contentDocument.defaultView) {
+            obj.contentDocument.defaultView.addEventListener('resize', _onResize);
+        }
+    }, []);
+
+    useEffect(() => {
+        return () => {
+            const obj = objectRef.current;
+            if (obj && obj.contentDocument && obj.contentDocument.defaultView) {
+                obj.contentDocument.defaultView.removeEventListener('resize', _onResize);
+            }
+        }
+    }, []);
+
+    return (
+        <object
+            aria-label="object"
+            onLoad={onLoad}
+            ref={objectRef} tabIndex={-1}
+            type={'text/html'}
+            data={'about:blank'}
+            title={''}
+            style={{
+                position: 'absolute',
+                top: 0,
+                left: 0,
+                height: '100%',
+                width: '100%',
+                pointerEvents: 'none',
+                zIndex: -1,
+                opacity: 0,
+            }}
+        />
+    )
+}
diff --git a/karavan-designer/src/designer/route/RouteDesigner.tsx b/karavan-designer/src/designer/route/RouteDesigner.tsx
index f7607b90..101309bb 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 from 'react';
+import React, {useCallback, useEffect, useRef, useState} from 'react';
 import {
     Drawer,
     DrawerPanelContent,
@@ -32,164 +32,156 @@ import PlusIcon from "@patternfly/react-icons/dist/esm/icons/plus-icon";
 import {DslElement} from "./DslElement";
 import {CamelUi} from "../utils/CamelUi";
 import {CamelDisplayUtil} from "karavan-core/lib/api/CamelDisplayUtil";
-import {RouteDesignerLogic} from "./RouteDesignerLogic";
+import {useRouteDesignerHook} from "./useRouteDesignerHook";
+import {useDesignerStore, useIntegrationStore} from "../KaravanStore";
+import {shallow} from "zustand/shallow";
+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 {ElementResizeListener} from "./ElementResizeListener";
 
 interface Props {
-    onSave?: (integration: Integration, propertyOnly: boolean) => void
-    integration: Integration
+    // onSave?: (integration: Integration, propertyOnly: boolean) => void
+    // integration: Integration
     dark: boolean
 }
 
-export interface RouteDesignerState {
-    logic: RouteDesignerLogic
-    integration: Integration
-    selectedStep?: CamelElement
-    showSelector: boolean
-    showDeleteConfirmation: boolean
-    deleteMessage: string
-    parentId: string
-    parentDsl?: string
-    selectedPosition?: number
-    showSteps: boolean
-    selectedUuids: string []
-    key: string
-    width: number
-    height: number
-    top: number
-    left: number
-    clipboardSteps: CamelElement[]
-    shiftKeyPressed?: boolean
-    ref?: any
-    printerRef?: any
-    propertyOnly: boolean
-    selectorTabIndex?: string | number
-}
+export const RouteDesigner = (props: Props) => {
 
-export class RouteDesigner extends React.Component<Props, RouteDesignerState> {
+    const ref = React.createRef();
+    const printerRef = React.createRef()
+    const contentRef: React.RefObject<HTMLDivElement> = useRef(null);
+    const flowRef: React.RefObject<HTMLDivElement> = useRef(null);
 
-    public state: RouteDesignerState = {
-        logic: new RouteDesignerLogic(this),
-        integration: CamelDisplayUtil.setIntegrationVisibility(this.props.integration, undefined),
-        showSelector: false,
-        showDeleteConfirmation: false,
-        deleteMessage: '',
-        parentId: '',
-        showSteps: true,
-        selectedUuids: [],
-        clipboardSteps: [],
-        key: "",
-        width: 1000,
-        height: 1000,
-        top: 0,
-        left: 0,
-        ref: React.createRef(),
-        printerRef: React.createRef(),
-        propertyOnly: false,
-    };
+    const { deleteElement, selectElement, moveElement, onShowDeleteConfirmation, onDslSelect, openSelector, createRouteConfiguration} = useRouteDesignerHook();
 
-    componentDidMount() {
-        this.state.logic.componentDidMount();
-    }
+    const [integration, setIntegration] = useIntegrationStore((state) => [state.integration, state.setIntegration], shallow)
+    const [showSelector, showDeleteConfirmation, propertyOnly, showSteps,deleteMessage,parentId, selectedUuids,clipboardSteps, parentDsl,selectedPosition,selectedStep,selectorTabIndex,
+        setShowSelector, setShowDeleteConfirmation,setPropertyOnly,setShowSteps,setDeleteMessage, setParentId, setSelectedUuids, setClipboardSteps,setPosition,
+        width, height, top, left] = useDesignerStore((s) =>
+        [s.showSelector, s.showDeleteConfirmation, s.propertyOnly, s.showSteps,s.deleteMessage,s.parentId, s.selectedUuids,s.clipboardSteps, s.parentDsl, s.selectedPosition, s.selectedStep,s.selectorTabIndex,
+            s.setShowSelector, s.setShowDeleteConfirmation,s.setPropertyOnly,s.setShowSteps,s.setDeleteMessage, s.setParentId, s.setSelectedUuids, s.setClipboardSteps, s.setPosition,
+            s.width, s.height, s.top, s.left], shallow)
 
-    componentWillUnmount() {
-        this.state.logic.componentWillUnmount();
-    }
+    useEffect(() => {
+        adaptResize();
+    })
 
-    handleResize = (event: any) => {
-        return this.state.logic.handleResize(event);
-    }
+    // 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);
+    // }
 
-    handleKeyDown = (event: KeyboardEvent) => {
-        return this.state.logic.handleKeyDown(event);
-    }
-
-    handleKeyUp = (event: KeyboardEvent) => {
-        return this.state.logic.handleKeyUp(event);
-    }
-
-    componentDidUpdate = (prevProps: Readonly<Props>, prevState: Readonly<RouteDesignerState>, snapshot?: any) => {
-        return this.state.logic.componentDidUpdate(prevState, snapshot);
-    }
-
-    getSelectorModal() {
+    function getSelectorModal() {
         return (
             <DslSelector
-                isOpen={this.state.showSelector}
-                onClose={() => this.state.logic.closeDslSelector()}
-                dark={this.props.dark}
-                parentId={this.state.parentId}
-                parentDsl={this.state.parentDsl}
-                showSteps={this.state.showSteps}
-                position={this.state.selectedPosition}
-                tabIndex={this.state.selectorTabIndex}
-                onDslSelect={this.state.logic.onDslSelect}/>)
+                isOpen={showSelector}
+                onClose={() => setShowSelector(false)}
+                dark={props.dark}
+                parentId={parentId}
+                parentDsl={parentDsl}
+                showSteps={showSteps}
+                position={selectedPosition}
+                tabIndex={selectorTabIndex}
+                onDslSelect={(dsl, parentId1) => onDslSelect(dsl, parentId1)}/>)
     }
 
-    getDeleteConfirmation() {
-        let htmlContent: string = this.state.deleteMessage;
+    function getDeleteConfirmation() {
+        let htmlContent: string = deleteMessage;
         return (<Modal
             className="modal-delete"
             title="Confirmation"
-            isOpen={this.state.showDeleteConfirmation}
-            onClose={() => this.setState({showDeleteConfirmation: false})}
+            isOpen={showDeleteConfirmation}
+            onClose={() => setShowDeleteConfirmation(false)}
             actions={[
-                <Button key="confirm" variant="primary" onClick={e => this.state.logic.deleteElement()}>Delete</Button>,
+                <Button key="confirm" variant="primary" onClick={e => deleteElement()}>Delete</Button>,
                 <Button key="cancel" variant="link"
-                        onClick={e => this.setState({showDeleteConfirmation: false})}>Cancel</Button>
+                        onClick={e => setShowDeleteConfirmation(false)}>Cancel</Button>
             ]}
-            onEscapePress={e => this.setState({showDeleteConfirmation: false})}>
+            onEscapePress={e => setShowDeleteConfirmation(false)}>
             <div>
                 {htmlContent}
             </div>
         </Modal>)
     }
 
-    getPropertiesPanel() {
+    function getPropertiesPanel() {
         return (
-            <DrawerPanelContent onResize={(_event, width) => this.setState({key: Math.random().toString()})}
+            <DrawerPanelContent onResize={(_event, width) => {
+                // setState({key: Math.random().toString()})
+            }}
                                 style={{transform: "initial"}} isResizable hasNoBorder defaultSize={'400px'}
                                 maxSize={'800px'} minSize={'300px'}>
-                <DslProperties ref={this.state.ref}
-                               integration={this.state.integration}
-                               step={this.state.selectedStep}
-                               onIntegrationUpdate={this.state.logic.onIntegrationUpdate}
-                               onPropertyUpdate={this.state.logic.onPropertyUpdate}
-                               isRouteDesigner={true}
-                               dark={this.props.dark}/>
+                {/*<DslProperties ref={ref}*/}
+                {/*               integration={integration}*/}
+                {/*               step={selectedStep}*/}
+                {/*               onIntegrationUpdate={logic.onIntegrationUpdate}*/}
+                {/*               onPropertyUpdate={logic.onPropertyUpdate}*/}
+                {/*               isRouteDesigner={true}*/}
+                {/*               dark={props.dark}/>*/}
             </DrawerPanelContent>
         )
     }
 
-    getGraph() {
-        const {selectedUuids, integration, key, width, height, top, left} = this.state;
+    // function onResizePage(el: HTMLDivElement | null) {
+    //     console.log("onResizePage", el)
+    //     const rect = el?.getBoundingClientRect();
+    //     if (el && rect && (el.scrollWidth !== width || el.scrollHeight !== height || rect.top !== top || rect.left !== left)) {
+    //         setPosition(el.scrollWidth, el.scrollHeight, rect.top, rect.left)
+    //     }
+    // }
+
+    const adaptResize = useCallback(() => {
+        if (contentRef.current && flowRef.current) {
+            const el = contentRef.current.getBoundingClientRect();
+            // flowRef.current.textContent = `width: ${elmRect.width}`;
+            console.log("elmRect", el)
+            setPosition(el.width, el.height, el.top, el.left)
+        }
+    }, []);
+
+
+    function getGraph() {
         const routes = CamelUi.getRoutes(integration);
         const routeConfigurations = CamelUi.getRouteConfigurations(integration);
         return (
-            <div ref={this.state.printerRef} className="graph">
-                <DslConnections height={height} width={width} top={top} left={left} integration={integration}/>
-                <div className="flows" data-click="FLOWS" onClick={event => this.state.logic.unselectElement(event)}
-                     ref={el => this.state.logic.onResizePage(el)}>
+            <div className="graph" ref={contentRef}>
+                <DslConnections/>
+                <ElementResizeListener onResize={adaptResize}/>
+                <div id="flows" className="flows" data-click="FLOWS" onClick={event => {
+                    // logic.unselectElement(event)
+                }}
+                     // ref={el => onResizePage(el)}
+                     ref={flowRef}
+                >
                     {routeConfigurations?.map((routeConfiguration, index: number) => (
-                        <DslElement key={routeConfiguration.uuid + key}
-                                    integration={integration}
-                                    openSelector={this.state.logic.openSelector}
-                                    deleteElement={this.state.logic.showDeleteConfirmation}
-                                    selectElement={this.state.logic.selectElement}
-                                    moveElement={this.state.logic.moveElement}
-                                    selectedUuid={selectedUuids}
+                        <DslElement key={routeConfiguration.uuid}
                                     inSteps={false}
                                     position={index}
                                     step={routeConfiguration}
                                     parent={undefined}/>
                     ))}
                     {routes?.map((route: any, index: number) => (
-                        <DslElement key={route.uuid + key}
-                                    integration={integration}
-                                    openSelector={this.state.logic.openSelector}
-                                    deleteElement={this.state.logic.showDeleteConfirmation}
-                                    selectElement={this.state.logic.selectElement}
-                                    moveElement={this.state.logic.moveElement}
-                                    selectedUuid={selectedUuids}
+                        <DslElement key={route.uuid}
                                     inSteps={false}
                                     position={index}
                                     step={route}
@@ -199,31 +191,35 @@ export class RouteDesigner extends React.Component<Props, RouteDesignerState> {
                         <Button
                             variant={routes.length === 0 ? "primary" : "secondary"}
                             icon={<PlusIcon/>}
-                            onClick={e => this.state.logic.openSelector(undefined, undefined)}>Create route
+                            onClick={e => {
+                                openSelector(undefined, undefined)
+                            }}
+                        >
+                            Create route
                         </Button>
                         <Button
                             variant="secondary"
                             icon={<PlusIcon/>}
-                            onClick={e => this.state.logic.createRouteConfiguration()}>Create configuration
+                            onClick={e => createRouteConfiguration()}
+                        >
+                            Create configuration
                         </Button>
                     </div>
                 </div>
             </div>)
     }
 
-    render() {
-        return (
-            <PageSection className="dsl-page" isFilled padding={{default: 'noPadding'}}>
-                <div className="dsl-page-columns">
-                    <Drawer isExpanded isInline>
-                        <DrawerContent panelContent={this.getPropertiesPanel()}>
-                            <DrawerContentBody>{this.getGraph()}</DrawerContentBody>
-                        </DrawerContent>
-                    </Drawer>
-                </div>
-                {this.state.showSelector === true && this.getSelectorModal()}
-                {this.getDeleteConfirmation()}
-            </PageSection>
-        );
-    }
+    return (
+        <PageSection className="dsl-page" isFilled padding={{default: 'noPadding'}}>
+            <div className="dsl-page-columns">
+                <Drawer isExpanded isInline>
+                    <DrawerContent panelContent={getPropertiesPanel()}>
+                        <DrawerContentBody>{getGraph()}</DrawerContentBody>
+                    </DrawerContent>
+                </Drawer>
+            </div>
+            {getSelectorModal()}
+            {getDeleteConfirmation()}
+        </PageSection>
+    );
 }
\ No newline at end of file
diff --git a/karavan-designer/src/designer/route/RouteDesignerLogic.tsx b/karavan-designer/src/designer/route/RouteDesignerLogic.tsx
deleted file mode 100644
index b352a056..00000000
--- a/karavan-designer/src/designer/route/RouteDesignerLogic.tsx
+++ /dev/null
@@ -1,396 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-import React from 'react';
-import '../karavan.css';
-import {DslMetaModel} from "../utils/DslMetaModel";
-import {CamelUtil} from "karavan-core/lib/api/CamelUtil";
-import {ChoiceDefinition, FromDefinition, LogDefinition, RouteConfigurationDefinition, RouteDefinition} from "karavan-core/lib/model/CamelDefinition";
-import {CamelElement, Integration} from "karavan-core/lib/model/IntegrationDefinition";
-import {CamelDefinitionApiExt} from "karavan-core/lib/api/CamelDefinitionApiExt";
-import {CamelDefinitionApi} from "karavan-core/lib/api/CamelDefinitionApi";
-import {Command, EventBus} from "../utils/EventBus";
-import {RouteToCreate} from "../utils/CamelUi";
-import {CamelDisplayUtil} from "karavan-core/lib/api/CamelDisplayUtil";
-import {toPng} from 'html-to-image';
-import {RouteDesigner, RouteDesignerState} from "./RouteDesigner";
-import {findDOMNode} from "react-dom";
-import {Subscription} from "rxjs";
-import debounce from 'lodash.debounce';
-
-export class RouteDesignerLogic {
-
-    routeDesigner: RouteDesigner
-    commandSub?: Subscription
-
-    constructor(routeDesigner: RouteDesigner) {
-        this.routeDesigner = routeDesigner;
-    }
-
-    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));
-    }
-
-    componentWillUnmount() {
-        window.removeEventListener('resize', this.routeDesigner.handleResize);
-        window.removeEventListener('keydown', this.routeDesigner.handleKeyDown);
-        window.removeEventListener('keyup', this.routeDesigner.handleKeyUp);
-        this.commandSub?.unsubscribe();
-    }
-
-    handleResize = (event: any) => {
-        this.routeDesigner.setState({key: Math.random().toString()});
-    }
-
-    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);
-            }
-        }
-    }
-
-    handleKeyUp = (event: KeyboardEvent) => {
-        this.routeDesigner.setState({shiftKeyPressed: false});
-        if (event.repeat) {
-            window.dispatchEvent(event);
-        }
-    }
-
-    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]
-            }));
-        }
-    }
-    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);
-                }
-            })
-        }
-    }
-
-    onCommand = (command: Command) => {
-        switch (command.command){
-            case "downloadImage": this.integrationImageDownload()
-        }
-    }
-
-    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})
-
-    showDeleteConfirmation = (id: string) => {
-        let message: string;
-        const uuidsToDelete:string [] = [id];
-        let ce: CamelElement;
-        ce = CamelDefinitionApiExt.findElementInIntegration(this.routeDesigner.state.integration, id)!;
-        if (ce.dslName === 'FromDefinition') { // Get the RouteDefinition for this.routeDesigner.  Use its uuid.
-            let flows = this.routeDesigner.state.integration.spec.flows!;
-            for (let i = 0; i < flows.length; i++) {
-                if (flows[i].dslName === 'RouteDefinition') {
-                    let routeDefinition: RouteDefinition = flows[i];
-                    if (routeDefinition.from.uuid === id) {
-                        uuidsToDelete.push(routeDefinition.uuid);
-                        break;
-                    }
-                }
-            }
-            message = 'Deleting the first element will delete the entire route!';
-        } else if (ce.dslName === 'RouteDefinition') {
-            message = 'Delete route?';
-        } else if (ce.dslName === 'RouteConfigurationDefinition') {
-            message = 'Delete route configuration?';
-        } else {
-            message = 'Delete element from route?';
-        }
-        this.routeDesigner.setState(prevState => ({
-            showSelector: false,
-            showDeleteConfirmation: true,
-            deleteMessage: message,
-            selectedUuids: uuidsToDelete,
-        }));
-    }
-
-    deleteElement = () => {
-        this.routeDesigner.state.selectedUuids.forEach(uuidToDelete => {
-            const i = CamelDefinitionApiExt.deleteStepFromIntegration(this.routeDesigner.state.integration, uuidToDelete);
-            this.routeDesigner.setState(prevState => ({
-                integration: i,
-                showSelector: false,
-                showDeleteConfirmation: false,
-                deleteMessage: '',
-                key: Math.random().toString(),
-                selectedStep: undefined,
-                propertyOnly: false,
-                selectedUuids: [uuidToDelete],
-            }));
-            const el = new CamelElement("");
-            el.uuid = uuidToDelete;
-            EventBus.sendPosition("delete", el, undefined, new DOMRect(), new DOMRect(), 0);
-        });
-    }
-
-    selectElement = (element: CamelElement) => {
-        const {shiftKeyPressed, selectedUuids, integration} = this.routeDesigner.state;
-        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 add = shiftKeyPressed && !selectedUuids.includes(element.uuid);
-        const remove = shiftKeyPressed && selectedUuids.includes(element.uuid);
-        const i = CamelDisplayUtil.setIntegrationVisibility(this.routeDesigner.state.integration, element.uuid);
-        this.routeDesigner.setState((prevState: RouteDesignerState) => {
-            if (remove) {
-                const index = prevState.selectedUuids.indexOf(element.uuid);
-                prevState.selectedUuids.splice(index, 1);
-            } else if (add && !canNotAdd) {
-                prevState.selectedUuids.push(element.uuid);
-            }
-            const uuid: string = prevState.selectedUuids.includes(element.uuid) ? element.uuid : prevState.selectedUuids.at(0) || '';
-            const selectedElement = shiftKeyPressed ? CamelDefinitionApiExt.findElementInIntegration(integration, uuid) : element;
-            return {
-                integration: i,
-                selectedStep: selectedElement,
-                showSelector: false,
-                selectedUuids: shiftKeyPressed ? [...prevState.selectedUuids] : [element.uuid],
-            }
-        });
-    }
-
-    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: [],
-            }));
-        }
-    }
-
-    openSelector = (parentId: string | undefined, parentDsl: string | undefined, showSteps: boolean = true, position?: number | undefined, selectorTabIndex?: string | number) => {
-        this.routeDesigner.setState({
-            showSelector: true,
-            parentId: parentId || '',
-            parentDsl: parentDsl,
-            showSteps: showSteps,
-            selectedPosition: position,
-            selectorTabIndex: selectorTabIndex
-        })
-    }
-
-    closeDslSelector = () => {
-        this.routeDesigner.setState({showSelector: false})
-    }
-
-    onDslSelect = (dsl: DslMetaModel, parentId: string, position?: number | undefined) => {
-        switch (dsl.dsl) {
-            case 'FromDefinition' :
-                const route = CamelDefinitionApi.createRouteDefinition({from: new FromDefinition({uri: dsl.uri})});
-                this.addStep(route, parentId, position)
-                break;
-            case 'ToDefinition' :
-                const to = CamelDefinitionApi.createStep(dsl.dsl, {uri: dsl.uri});
-                this.addStep(to, parentId, position)
-                break;
-            case 'ToDynamicDefinition' :
-                const toD = CamelDefinitionApi.createStep(dsl.dsl, {uri: dsl.uri});
-                this.addStep(toD, parentId, position)
-                break;
-            case 'KameletDefinition' :
-                const kamelet = CamelDefinitionApi.createStep(dsl.dsl, {name: dsl.name});
-                this.addStep(kamelet, parentId, position)
-                break;
-            default:
-                const step = CamelDefinitionApi.createStep(dsl.dsl, undefined);
-                const augmentedStep = this.setDslDefaults(step);
-                this.addStep(augmentedStep, parentId, position)
-                break;
-        }
-    }
-
-    setDslDefaults(step: CamelElement): CamelElement {
-        if (step.dslName === 'LogDefinition') {
-            // eslint-disable-next-line no-template-curly-in-string
-            (step as LogDefinition).message = "${body}";
-        }
-        if (step.dslName === 'ChoiceDefinition') {
-            (step as ChoiceDefinition).when?.push(CamelDefinitionApi.createStep('WhenDefinition', undefined));
-            (step as ChoiceDefinition).otherwise = CamelDefinitionApi.createStep('OtherwiseDefinition', undefined);
-        }
-        return step;
-    }
-
-    createRouteConfiguration = () => {
-        const clone = CamelUtil.cloneIntegration(this.routeDesigner.state.integration);
-        const routeConfiguration = new RouteConfigurationDefinition();
-        const i = CamelDefinitionApiExt.addRouteConfigurationToIntegration(clone, routeConfiguration);
-        this.routeDesigner.setState(prevState => ({
-            integration: i,
-            propertyOnly: false,
-            key: Math.random().toString(),
-            selectedStep: routeConfiguration,
-            selectedUuids: [routeConfiguration.uuid],
-        }));
-    }
-
-    addStep = (step: CamelElement, parentId: string, position?: number | undefined) => {
-        const i = CamelDefinitionApiExt.addStepToIntegration(this.routeDesigner.state.integration, step, parentId, position);
-        const clone = CamelUtil.cloneIntegration(i);
-        EventBus.sendPosition("clean", step, undefined, new DOMRect(), new DOMRect(), 0);
-        const selectedStep = step.dslName === 'RouteDefinition' ? (step as RouteDefinition).from  : step;
-        this.routeDesigner.setState(prevState => ({
-            integration: clone,
-            key: Math.random().toString(),
-            showSelector: false,
-            selectedStep: selectedStep,
-            propertyOnly: false,
-            selectedUuids: [selectedStep.uuid],
-        }));
-    }
-
-    onIntegrationUpdate = (i: Integration) => {
-        this.routeDesigner.setState({integration: i, propertyOnly: false, showSelector: false, key: Math.random().toString()});
-    }
-
-    moveElement = (source: string, target: string, asChild: boolean) => {
-        const i = CamelDefinitionApiExt.moveRouteElement(this.routeDesigner.state.integration, source, target, asChild);
-        const clone = CamelUtil.cloneIntegration(i);
-        const selectedStep = CamelDefinitionApiExt.findElementInIntegration(clone, source);
-        this.routeDesigner.setState(prevState => ({
-            integration: clone,
-            key: Math.random().toString(),
-            showSelector: false,
-            selectedStep: selectedStep,
-            propertyOnly: false,
-            selectedUuids: [source],
-        }));
-    }
-
-    onResizePage(el: HTMLDivElement | null) {
-        const rect = el?.getBoundingClientRect();
-        if (el && rect && (el.scrollWidth !== this.routeDesigner.state.width || el.scrollHeight !== this.routeDesigner.state.height || rect.top !== this.routeDesigner.state.top || rect.left !== this.routeDesigner.state.left)) {
-            this.routeDesigner.setState({width: el.scrollWidth, height: el.scrollHeight, top: rect.top, left: rect.left})
-        }
-    }
-
-    downloadIntegrationImage(dataUrl: string) {
-        const a = document.createElement('a');
-        a.setAttribute('download', 'karavan-routes.png');
-        a.setAttribute('href', dataUrl);
-        a.click();
-    }
-
-    integrationImageDownloadFilter = (node: HTMLElement) => {
-        const exclusionClasses = ['add-flow'];
-        return !exclusionClasses.some(classname => {
-            return node.classList === undefined ? false : node.classList.contains(classname);
-        });
-    }
-
-    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);
-        })
-    }
-}
\ No newline at end of file
diff --git a/karavan-designer/src/designer/route/property/DslPropertyField.tsx b/karavan-designer/src/designer/route/property/DslPropertyField.tsx
index 3021baf2..0dca7151 100644
--- a/karavan-designer/src/designer/route/property/DslPropertyField.tsx
+++ b/karavan-designer/src/designer/route/property/DslPropertyField.tsx
@@ -264,7 +264,7 @@ export class DslPropertyField extends React.Component<Props, State> {
             } else {
                 this.setState({customCode: value, showEditor: true})
             }
-        }).catch(reason => console.log(reason))
+        }).catch((reason: any) => console.log(reason))
     }
 
     getJavaTypeGeneratedInput = (property: PropertyMeta, value: any) => {
diff --git a/karavan-designer/src/designer/route/useRouteDesignerHook.tsx b/karavan-designer/src/designer/route/useRouteDesignerHook.tsx
new file mode 100644
index 00000000..fd97b731
--- /dev/null
+++ b/karavan-designer/src/designer/route/useRouteDesignerHook.tsx
@@ -0,0 +1,384 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import React from 'react';
+import '../karavan.css';
+import {DslMetaModel} from "../utils/DslMetaModel";
+import {CamelUtil} from "karavan-core/lib/api/CamelUtil";
+import {ChoiceDefinition, FromDefinition, LogDefinition, RouteConfigurationDefinition, RouteDefinition} from "karavan-core/lib/model/CamelDefinition";
+import {CamelElement, Integration} from "karavan-core/lib/model/IntegrationDefinition";
+import {CamelDefinitionApiExt} from "karavan-core/lib/api/CamelDefinitionApiExt";
+import {CamelDefinitionApi} from "karavan-core/lib/api/CamelDefinitionApi";
+import {Command, EventBus} from "../utils/EventBus";
+import {RouteToCreate} from "../utils/CamelUi";
+import {CamelDisplayUtil} from "karavan-core/lib/api/CamelDisplayUtil";
+import {toPng} from 'html-to-image';
+import {RouteDesigner} from "./RouteDesigner";
+import {findDOMNode} from "react-dom";
+import {Subscription} from "rxjs";
+import debounce from 'lodash.debounce';
+import {useDesignerStore, useIntegrationStore} from "../KaravanStore";
+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 [showSelector, showDeleteConfirmation, propertyOnly, showSteps,deleteMessage,parentId, selectedUuids,clipboardSteps, parentDsl,selectedPosition,selectedStep,selectorTabIndex,shiftKeyPressed,
+        setShowSelector, setShowDeleteConfirmation,setPropertyOnly,setShowSteps, setDeleteMessage, setSelectedStep, setParentId, setParentDsl, setSelectedPosition, setSelectedUuids, setClipboardSteps,setPosition,setSelectorTabIndex,
+        width, height, top, left] = useDesignerStore((s) =>
+        [s.showSelector, s.showDeleteConfirmation, s.propertyOnly, s.showSteps,s.deleteMessage,s.parentId, s.selectedUuids,s.clipboardSteps, s.parentDsl, s.selectedPosition, s.selectedStep,s.selectorTabIndex, s.shiftKeyPressed,
+            s.setShowSelector, s.setShowDeleteConfirmation,s.setPropertyOnly,s.setShowSteps,s.setDeleteMessage, s.setSelectedStep, s.setParentId, s.setParentDsl, s.setSelectedPosition, s.setSelectedUuids, s.setClipboardSteps, s.setPosition,s.setSelectorTabIndex,
+            s.width, s.height, s.top, s.left], 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 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  onCommand = (command: Command) => {
+    //     switch (command.command){
+    //         case "downloadImage": this.integrationImageDownload()
+    //     }
+    // }
+    //
+    // 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];
+        let ce: CamelElement;
+        ce = CamelDefinitionApiExt.findElementInIntegration(integration, id)!;
+        if (ce.dslName === 'FromDefinition') { // Get the RouteDefinition for this.routeDesigner.  Use its uuid.
+            let flows = integration.spec.flows!;
+            for (let i = 0; i < flows.length; i++) {
+                if (flows[i].dslName === 'RouteDefinition') {
+                    let routeDefinition: RouteDefinition = flows[i];
+                    if (routeDefinition.from.uuid === id) {
+                        uuidsToDelete.push(routeDefinition.uuid);
+                        break;
+                    }
+                }
+            }
+            message = 'Deleting the first element will delete the entire route!';
+        } else if (ce.dslName === 'RouteDefinition') {
+            message = 'Delete route?';
+        } else if (ce.dslName === 'RouteConfigurationDefinition') {
+            message = 'Delete route configuration?';
+        } else {
+            message = 'Delete element from route?';
+        }
+        setShowSelector(false);
+        setShowDeleteConfirmation(false);
+        setDeleteMessage(message);
+        setSelectedUuids(uuidsToDelete);
+    }
+
+    const deleteElement = () =>  {
+        selectedUuids.forEach(uuidToDelete => {
+            const i = CamelDefinitionApiExt.deleteStepFromIntegration(integration, uuidToDelete);
+            setIntegration(i);
+            setShowSelector(false);
+            setShowDeleteConfirmation(false);
+            setDeleteMessage('');
+            setSelectedStep(undefined);
+            setPropertyOnly(false);
+            setSelectedUuids([uuidToDelete]);
+
+            const el = new CamelElement("");
+            el.uuid = uuidToDelete;
+            EventBus.sendPosition("delete", el, undefined, new DOMRect(), new DOMRect(), 0);
+        });
+    }
+
+    const selectElement = (element: CamelElement) =>  {
+        // const {shiftKeyPressed, selectedUuids, integration} = this.routeDesigner.state;
+        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 add = shiftKeyPressed && !selectedUuids.includes(element.uuid);
+        const remove = shiftKeyPressed && selectedUuids.includes(element.uuid);
+        const i = CamelDisplayUtil.setIntegrationVisibility(integration, element.uuid);
+
+        if (remove) {
+            const index = selectedUuids.indexOf(element.uuid);
+            selectedUuids.splice(index, 1);
+        } else if (add && !canNotAdd) {
+            selectedUuids.push(element.uuid);
+        }
+        const uuid: string = selectedUuids.includes(element.uuid) ? element.uuid : selectedUuids.at(0) || '';
+        const selectedElement = shiftKeyPressed ? CamelDefinitionApiExt.findElementInIntegration(integration, uuid) : element;
+
+        setIntegration(i);
+        setSelectedStep(selectedElement);
+        setShowSelector(false);
+        setSelectedUuids(shiftKeyPressed ? [...selectedUuids] : [element.uuid])
+    }
+    //
+    // 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: [],
+    //         }));
+    //     }
+    // }
+    //
+    const openSelector = (parentId: string | undefined, parentDsl: string | undefined, showSteps: boolean = true, position?: number | undefined, selectorTabIndex?: string | number) => {
+        setShowSelector(true);
+        setParentId(parentId || '');
+        setParentDsl(parentDsl);
+        setShowSteps(showSteps);
+        setSelectedPosition(position);
+        setSelectorTabIndex(selectorTabIndex);
+    }
+    //
+    // function closeDslSelector = () => {
+    //     this.routeDesigner.setState({showSelector: false})
+    // }
+    //
+    const onDslSelect = (dsl: DslMetaModel, parentId: string, position?: number | undefined) => {
+        switch (dsl.dsl) {
+            case 'FromDefinition' :
+                const route = CamelDefinitionApi.createRouteDefinition({from: new FromDefinition({uri: dsl.uri})});
+                addStep(route, parentId, position)
+                break;
+            case 'ToDefinition' :
+                const to = CamelDefinitionApi.createStep(dsl.dsl, {uri: dsl.uri});
+                addStep(to, parentId, position)
+                break;
+            case 'ToDynamicDefinition' :
+                const toD = CamelDefinitionApi.createStep(dsl.dsl, {uri: dsl.uri});
+                addStep(toD, parentId, position)
+                break;
+            case 'KameletDefinition' :
+                const kamelet = CamelDefinitionApi.createStep(dsl.dsl, {name: dsl.name});
+                addStep(kamelet, parentId, position)
+                break;
+            default:
+                const step = CamelDefinitionApi.createStep(dsl.dsl, undefined);
+                const augmentedStep = setDslDefaults(step);
+                addStep(augmentedStep, parentId, position)
+                break;
+        }
+    }
+    //
+    function setDslDefaults(step: CamelElement): CamelElement {
+        if (step.dslName === 'LogDefinition') {
+            // eslint-disable-next-line no-template-curly-in-string
+            (step as LogDefinition).message = "${body}";
+        }
+        if (step.dslName === 'ChoiceDefinition') {
+            (step as ChoiceDefinition).when?.push(CamelDefinitionApi.createStep('WhenDefinition', undefined));
+            (step as ChoiceDefinition).otherwise = CamelDefinitionApi.createStep('OtherwiseDefinition', undefined);
+        }
+        return step;
+    }
+
+    const createRouteConfiguration = () => {
+        const clone = CamelUtil.cloneIntegration(integration);
+        const routeConfiguration = new RouteConfigurationDefinition();
+        const i = CamelDefinitionApiExt.addRouteConfigurationToIntegration(clone, routeConfiguration);
+        setIntegration(i);
+        setPropertyOnly(false);
+        setSelectedStep(routeConfiguration);
+        setSelectedUuids([routeConfiguration.uuid]);
+    }
+
+    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 selectedStep = step.dslName === 'RouteDefinition' ? (step as RouteDefinition).from  : step;
+        setIntegration(clone);
+        setShowSelector(false);
+        setSelectedStep(selectedStep);
+        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);
+        const selectedStep = CamelDefinitionApiExt.findElementInIntegration(clone, source);
+
+        setIntegration(clone);
+        setShowSelector(false);
+        setSelectedStep(selectedStep);
+        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);
+    //     })
+    // }
+
+    return { deleteElement, selectElement, moveElement, onShowDeleteConfirmation, onDslSelect, openSelector, createRouteConfiguration}
+}
\ No newline at end of file
diff --git a/karavan-designer/src/index.tsx b/karavan-designer/src/index.tsx
index 53d84ae5..3219d305 100644
--- a/karavan-designer/src/index.tsx
+++ b/karavan-designer/src/index.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, {StrictMode} from 'react';
 import "./index.css";
 import "@patternfly/patternfly/patternfly.css";
 import App from "./App";
@@ -22,4 +22,8 @@ import {createRoot} from "react-dom/client";
 
 const container = document.getElementById('root');
 const root = createRoot(container!);
-root.render(<App/>);
\ No newline at end of file
+root.render(
+    <StrictMode>
+        <App />
+    </StrictMode>
+);
\ No newline at end of file