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

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


The following commit(s) were added to refs/heads/main by this push:
     new aed9731c Implement #622
aed9731c is described below

commit aed9731c0fa71bea2f96a4fc13ceb9bb3fc394f7
Author: Marat Gubaidullin <marat.gubaidul...@gmail.com>
AuthorDate: Fri Jan 20 17:30:53 2023 -0500

    Implement #622
---
 .../src/main/resources/application.properties      |   2 +-
 karavan-app/src/main/webui/package-lock.json       |  43 +++--
 .../configuration/RouteConfigurationCard.tsx       |  58 -------
 .../configuration/RouteConfigurationDesigner.tsx   | 174 ---------------------
 .../main/webui/src/designer/rest/RestDesigner.tsx  |   1 -
 .../webui/src/designer/route/DslConnections.tsx    |   8 +-
 .../main/webui/src/designer/route/DslElement.tsx   |  17 +-
 .../webui/src/designer/route/DslProperties.tsx     |  19 ---
 .../webui/src/designer/route/RouteDesigner.tsx     | 163 +++++++++++++------
 .../src/main/webui/src/designer/utils/EventBus.ts  |   6 +-
 .../main/webui/src/projects/CreateFileModal.tsx    |   2 -
 11 files changed, 175 insertions(+), 318 deletions(-)

diff --git a/karavan-app/src/main/resources/application.properties 
b/karavan-app/src/main/resources/application.properties
index d3413d23..d5bf15b9 100644
--- a/karavan-app/src/main/resources/application.properties
+++ b/karavan-app/src/main/resources/application.properties
@@ -92,7 +92,7 @@ quarkus.kubernetes-client.request-timeout=10000
 quarkus.swagger-ui.always-include=true
 
 quarkus.quinoa.frozen-lockfile=false
-quarkus.quinoa.package-manager-install=true
+quarkus.quinoa.package-manager-install=false
 quarkus.quinoa.package-manager-install.node-version=18.12.1
 quarkus.quinoa.dev-server.port=3000
 quarkus.quinoa.dev-server.check-timeout=60000
\ No newline at end of file
diff --git a/karavan-app/src/main/webui/package-lock.json 
b/karavan-app/src/main/webui/package-lock.json
index 4122dfc4..65dcddc8 100644
--- a/karavan-app/src/main/webui/package-lock.json
+++ b/karavan-app/src/main/webui/package-lock.json
@@ -39,6 +39,27 @@
         "monaco-editor": "0.29.1"
       }
     },
+    "../../../../karavan-core": {
+      "version": "3.18.6",
+      "license": "Apache-2.0",
+      "dependencies": {
+        "@types/js-yaml": "^4.0.5",
+        "@types/uuid": "^8.3.4",
+        "typescript": "^4.9.4",
+        "uuid": "8.3.2"
+      },
+      "devDependencies": {
+        "@types/chai": "^4.3.0",
+        "@types/dagre": "^0.7.47",
+        "@types/mocha": "^9.1.0",
+        "@types/node": "^18.11.18",
+        "chai": "^4.3.4",
+        "cross-env": "^7.0.3",
+        "fs": "^0.0.1-security",
+        "mocha": "^9.2.0",
+        "ts-node": "^10.4.0"
+      }
+    },
     "../karavan-core": {
       "extraneous": true
     },
@@ -10837,15 +10858,8 @@
       }
     },
     "node_modules/karavan-core": {
-      "version": "3.18.6",
-      "resolved": "file:../../../../karavan-core",
-      "license": "Apache-2.0",
-      "dependencies": {
-        "@types/js-yaml": "^4.0.5",
-        "@types/uuid": "^8.3.4",
-        "typescript": "^4.9.4",
-        "uuid": "8.3.2"
-      }
+      "resolved": "../../../../karavan-core",
+      "link": true
     },
     "node_modules/keycloak-js": {
       "version": "19.0.1",
@@ -24308,10 +24322,19 @@
       }
     },
     "karavan-core": {
-      "version": "3.18.6",
+      "version": "file:../../../../karavan-core",
       "requires": {
+        "@types/chai": "^4.3.0",
+        "@types/dagre": "^0.7.47",
         "@types/js-yaml": "^4.0.5",
+        "@types/mocha": "^9.1.0",
+        "@types/node": "^18.11.18",
         "@types/uuid": "^8.3.4",
+        "chai": "^4.3.4",
+        "cross-env": "^7.0.3",
+        "fs": "^0.0.1-security",
+        "mocha": "^9.2.0",
+        "ts-node": "^10.4.0",
         "typescript": "^4.9.4",
         "uuid": "8.3.2"
       }
diff --git 
a/karavan-app/src/main/webui/src/designer/configuration/RouteConfigurationCard.tsx
 
b/karavan-app/src/main/webui/src/designer/configuration/RouteConfigurationCard.tsx
deleted file mode 100644
index 7b069e3c..00000000
--- 
a/karavan-app/src/main/webui/src/designer/configuration/RouteConfigurationCard.tsx
+++ /dev/null
@@ -1,58 +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 {
-    Button
-} from '@patternfly/react-core';
-import '../karavan.css';
-import {RouteConfigurationDefinition} from 
"karavan-core/lib/model/CamelDefinition";
-import DeleteIcon from 
"@patternfly/react-icons/dist/js/icons/times-circle-icon";
-import {CamelElement} from "karavan-core/lib/model/IntegrationDefinition";
-
-interface Props {
-    routeConfiguration: RouteConfigurationDefinition
-    selectedStep?: CamelElement
-    deleteElement: (element: RouteConfigurationDefinition) => void
-    selectElement: (element: RouteConfigurationDefinition) => void
-}
-
-export class RouteConfigurationCard extends React.Component<Props, any> {
-
-    selectElement = (evt: React.MouseEvent) => {
-        evt.stopPropagation();
-        this.props.selectElement.call(this, this.props.routeConfiguration);
-    }
-
-    delete = (evt: React.MouseEvent) => {
-        evt.stopPropagation();
-        this.props.deleteElement.call(this, this.props.routeConfiguration);
-    }
-
-    render() {
-        const {selectedStep, routeConfiguration} = this.props;
-        return (
-            <div className={selectedStep?.uuid === routeConfiguration.uuid ? 
"rest-card rest-card-selected" : "rest-card rest-card-unselected"}
-                 onClick={e => this.selectElement(e)}>
-                <div className="header">
-                    <div className="title">Route Configuration</div>
-                    <div className="description">Route Configuration</div>
-                    <Button variant="link" className="delete-button" 
onClick={e => this.delete(e)}><DeleteIcon/></Button>
-                </div>
-            </div>
-        );
-    }
-}
diff --git 
a/karavan-app/src/main/webui/src/designer/configuration/RouteConfigurationDesigner.tsx
 
b/karavan-app/src/main/webui/src/designer/configuration/RouteConfigurationDesigner.tsx
deleted file mode 100644
index d7fdb61a..00000000
--- 
a/karavan-app/src/main/webui/src/designer/configuration/RouteConfigurationDesigner.tsx
+++ /dev/null
@@ -1,174 +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 {
-    Button, Drawer, DrawerContent, DrawerContentBody, DrawerPanelContent, 
Modal, PageSection
-} from '@patternfly/react-core';
-import '../karavan.css';
-import {RouteConfigurationDefinition} from 
"karavan-core/lib/model/CamelDefinition";
-import {CamelElement, Integration} from 
"karavan-core/lib/model/IntegrationDefinition";
-import {CamelUi} from "../utils/CamelUi";
-import PlusIcon from "@patternfly/react-icons/dist/esm/icons/plus-icon";
-import {CamelDefinitionApiExt} from 
"karavan-core/lib/api/CamelDefinitionApiExt";
-import {CamelUtil} from "karavan-core/lib/api/CamelUtil";
-import {RouteConfigurationCard} from "./RouteConfigurationCard";
-import {DslProperties} from "../route/DslProperties";
-
-interface Props {
-    onSave?: (integration: Integration, propertyOnly: boolean) => void
-    integration: Integration
-    dark: boolean
-}
-
-interface State {
-    integration: Integration
-    showDeleteConfirmation: boolean
-    routeConfigurations: RouteConfigurationDefinition[]
-    selectedRouteConfiguration?: RouteConfigurationDefinition
-    key: string
-    propertyOnly: boolean
-}
-
-export class RouteConfigurationDesigner extends React.Component<Props, State> {
-
-    public state: State = {
-        integration: this.props.integration,
-        routeConfigurations: [],
-        showDeleteConfirmation: false,
-        key: "",
-        propertyOnly: false
-    }
-
-    componentDidMount() {
-        this.setState({key: Math.random().toString()})
-    }
-
-    componentDidUpdate = (prevProps: Readonly<Props>, prevState: 
Readonly<State>, snapshot?: any) => {
-        if (prevState.key !== this.state.key) {
-            this.props.onSave?.call(this, this.state.integration, 
this.state.propertyOnly);
-        }
-    }
-
-    showDeleteConfirmation = (routeConfiguration: 
RouteConfigurationDefinition) => {
-        this.setState({selectedRouteConfiguration: routeConfiguration, 
showDeleteConfirmation: true});
-    }
-
-    onIntegrationUpdate = (i: Integration) => {
-        this.setState({integration: i, propertyOnly: false, 
showDeleteConfirmation: false, key: Math.random().toString()});
-    }
-
-    deleteRouteConfiguration = () => {
-        const {selectedRouteConfiguration} = this.state;
-        if (selectedRouteConfiguration) {
-            const i = 
CamelDefinitionApiExt.deleteRouteConfigurationFromIntegration(this.state.integration,
 selectedRouteConfiguration);
-            this.setState({
-                integration: i,
-                showDeleteConfirmation: false,
-                key: Math.random().toString(),
-                selectedRouteConfiguration: undefined,
-                propertyOnly: false
-            });
-        }
-    }
-
-    getDeleteConfirmation() {
-        return (<Modal
-            className="modal-delete"
-            title="Confirmation"
-            isOpen={this.state.showDeleteConfirmation}
-            onClose={() => this.setState({showDeleteConfirmation: false})}
-            actions={[
-                <Button key="confirm" variant="primary" onClick={e => 
this.deleteRouteConfiguration()}>Delete</Button>,
-                <Button key="cancel" variant="link"
-                        onClick={e => this.setState({showDeleteConfirmation: 
false})}>Cancel</Button>
-            ]}
-            onEscapePress={e => this.setState({showDeleteConfirmation: 
false})}>
-            <div>
-                Delete Route Configuration from integration?
-            </div>
-        </Modal>)
-    }
-
-    createRouteConfiguration = () => {
-        const clone = CamelUtil.cloneIntegration(this.state.integration);
-        const routeConfiguration = new RouteConfigurationDefinition();
-        const i = 
CamelDefinitionApiExt.addRouteConfigurationToIntegration(clone, 
routeConfiguration);
-        this.setState({integration: i, propertyOnly: false, key: 
Math.random().toString(), selectedRouteConfiguration: routeConfiguration});
-    }
-
-    selectRouteConfiguration = (element: RouteConfigurationDefinition) => {
-        this.setState({selectedRouteConfiguration: element})
-    }
-
-    onPropertyUpdate = (element: CamelElement) => {
-        const clone = CamelUtil.cloneIntegration(this.state.integration);
-        const i = 
CamelDefinitionApiExt.updateRouteConfigurationToIntegration(clone, element);
-        this.setState({integration: i, propertyOnly: true, key: 
Math.random().toString()});
-    }
-
-    getPropertiesPanel() {
-        return (
-            <DrawerPanelContent isResizable hasNoBorder defaultSize={'400px'} 
maxSize={'800px'} minSize={'300px'}>
-                <DslProperties
-                    integration={this.props.integration}
-                    step={this.state.selectedRouteConfiguration}
-                    onIntegrationUpdate={this.onIntegrationUpdate}
-                    onPropertyUpdate={this.onPropertyUpdate}
-                    clipboardStep={undefined}
-                    isRouteDesigner={false}
-                    onClone={element => {}}
-                    dark={this.props.dark}/>
-            </DrawerPanelContent>
-        )
-    }
-
-    render() {
-        const routeConfigurations = 
CamelUi.getRouteConfigurations(this.state.integration);
-        return (
-            <PageSection className="rest-page" isFilled padding={{default: 
'noPadding'}}>
-                <div className="rest-page-columns">
-                    <Drawer isExpanded isInline>
-                        <DrawerContent 
panelContent={this.getPropertiesPanel()}>
-                            <DrawerContentBody>
-                                <div className="graph" data-click="REST">
-                                    <div className="flows">
-                                        
{routeConfigurations?.map(routeConfiguration =>
-                                            <RouteConfigurationCard 
key={routeConfiguration.uuid + this.state.key}
-                                                                    
routeConfiguration={routeConfiguration}
-                                                                    
selectedStep={this.state.selectedRouteConfiguration}
-                                                                    
selectElement={this.selectRouteConfiguration}
-                                                                    
deleteElement={this.showDeleteConfirmation}/>
-                                        )}
-                                        <div className="add-rest">
-                                            <Button
-                                                variant="primary"
-                                                data-click="ADD_REST"
-                                                icon={<PlusIcon/>}
-                                                onClick={e => 
this.createRouteConfiguration()}>Create new configuration
-                                            </Button>
-                                        </div>
-                                    </div>
-                                </div>
-                            </DrawerContentBody>
-                        </DrawerContent>
-                    </Drawer>
-                </div>
-                {this.getDeleteConfirmation()}
-            </PageSection>
-        )
-    }
-}
diff --git a/karavan-app/src/main/webui/src/designer/rest/RestDesigner.tsx 
b/karavan-app/src/main/webui/src/designer/rest/RestDesigner.tsx
index f91c5129..d13d07e6 100644
--- a/karavan-app/src/main/webui/src/designer/rest/RestDesigner.tsx
+++ b/karavan-app/src/main/webui/src/designer/rest/RestDesigner.tsx
@@ -240,7 +240,6 @@ export class RestDesigner extends React.Component<Props, 
State> {
                     step={this.state.selectedStep}
                     onIntegrationUpdate={this.onIntegrationUpdate}
                     onPropertyUpdate={this.onPropertyUpdate}
-                    clipboardStep={undefined}
                     isRouteDesigner={false}
                     onClone={this.cloneRest}
                     dark={this.props.dark}/>
diff --git a/karavan-app/src/main/webui/src/designer/route/DslConnections.tsx 
b/karavan-app/src/main/webui/src/designer/route/DslConnections.tsx
index 0806761e..6dc1ced3 100644
--- a/karavan-app/src/main/webui/src/designer/route/DslConnections.tsx
+++ b/karavan-app/src/main/webui/src/designer/route/DslConnections.tsx
@@ -57,12 +57,18 @@ export class DslConnections extends React.Component<Props, 
State> {
     }
 
     setPosition(evt: DslPosition) {
-        if (evt.command === "add") this.setState(prevState => ({steps: 
prevState.steps.set(evt.step.uuid, evt)}));
+        if (evt.command === "add") {
+            this.setState(prevState => ({steps: 
prevState.steps.set(evt.step.uuid, evt)}));
+        }
         else if (evt.command === "delete") this.setState(prevState => {
             // prevState.steps.clear();
             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() {
diff --git a/karavan-app/src/main/webui/src/designer/route/DslElement.tsx 
b/karavan-app/src/main/webui/src/designer/route/DslElement.tsx
index 868f3e2c..07222d84 100644
--- a/karavan-app/src/main/webui/src/designer/route/DslElement.tsx
+++ b/karavan-app/src/main/webui/src/designer/route/DslElement.tsx
@@ -65,9 +65,22 @@ export class DslElement extends React.Component<Props, 
State> {
         tabIndex: 0,
         selectedUuid: this.props.selectedUuid,
         isDragging: false,
-        isDraggedOver: false
+        isDraggedOver: false,
     };
 
+    handleKeyDown = (event: React.KeyboardEvent) =>{
+        // event.preventDefault();
+        // console.log(event);
+        // let charCode = String.fromCharCode(event.which).toLowerCase();
+        // if((event.ctrlKey || event.metaKey) && charCode === 's') {
+        //     alert("CTRL+S Pressed");
+        // }else if((event.ctrlKey || event.metaKey) && charCode === 'c') {
+        //     alert("CTRL+C Pressed");
+        // }else if((event.ctrlKey || event.metaKey) && charCode === 'v') {
+        //     alert("CTRL+V Pressed");
+        // }
+    }
+
     componentDidUpdate = (prevProps: Readonly<Props>, prevState: 
Readonly<State>, snapshot?: any) => {
         if (prevState.selectedUuid !== this.props.selectedUuid) {
             this.setState({selectedUuid: this.props.selectedUuid});
@@ -484,6 +497,8 @@ export class DslElement extends React.Component<Props, 
State> {
                  }}
                  onDrop={event => this.dragElement(event, element)}
                  draggable={!this.isNotDraggable()}
+                 // tabIndex={0}
+                 onKeyDown={this.handleKeyDown}
             >
                 {this.getElementHeader()}
                 {this.getChildElements()}
diff --git a/karavan-app/src/main/webui/src/designer/route/DslProperties.tsx 
b/karavan-app/src/main/webui/src/designer/route/DslProperties.tsx
index 0359bb36..f0000bc1 100644
--- a/karavan-app/src/main/webui/src/designer/route/DslProperties.tsx
+++ b/karavan-app/src/main/webui/src/designer/route/DslProperties.tsx
@@ -36,8 +36,6 @@ import {CamelUtil} from "karavan-core/lib/api/CamelUtil";
 import {CamelUi, RouteToCreate} from "../utils/CamelUi";
 import {CamelMetadataApi, PropertyMeta} from 
"karavan-core/lib/model/CamelMetadata";
 import {IntegrationHeader} from "../utils/KaravanComponents";
-import CopyIcon from '@patternfly/react-icons/dist/esm/icons/copy-icon'
-import PasteIcon from '@patternfly/react-icons/dist/esm/icons/paste-icon'
 import CloneIcon from "@patternfly/react-icons/dist/esm/icons/clone-icon";
 
 interface Props {
@@ -45,8 +43,6 @@ interface Props {
     step?: CamelElement,
     onIntegrationUpdate?: any,
     onPropertyUpdate?: (element: CamelElement, newRoute?: RouteToCreate) => 
void
-    clipboardStep?: CamelElement
-    onSaveClipboardStep?: (element?: CamelElement) => void
     onClone?: (element: CamelElement) => void
     isRouteDesigner: boolean
     dark: boolean
@@ -76,15 +72,6 @@ export class DslProperties extends React.Component<Props, 
State> {
         }
     }
 
-    pasteClipboardStep = () => {
-        if (this.props.clipboardStep && this.state.step) {
-            const clone = CamelUtil.cloneStep(this.props.clipboardStep);
-            clone.uuid = this.state.step.uuid;
-            this.setStep(clone)
-            this.props.onPropertyUpdate?.call(this, clone);
-        }
-    }
-
     dataFormatChanged = (value: DataFormatDefinition) => {
         value.uuid = this.state.step?.uuid ? this.state.step?.uuid : 
value.uuid;
         this.setStep(value);
@@ -144,12 +131,6 @@ export class DslProperties extends React.Component<Props, 
State> {
             <div className="headers">
                 <div className="top">
                     <Title headingLevel="h1" size="md">{title}</Title>
-                    <Tooltip content="Copy step" position="bottom">
-                        <Button variant="link" onClick={() => 
this.props.onSaveClipboardStep?.call(this, this.state.step)} 
icon={<CopyIcon/>}/>
-                    </Tooltip>
-                    <Tooltip content="Paste step" position="bottom">
-                        <Button variant="link" onClick={() => 
this.pasteClipboardStep()} icon={<PasteIcon/>}/>
-                    </Tooltip>
                 </div>
                 <Text 
component={TextVariants.p}>{descriptionLines.at(0)}</Text>
                 {descriptionLines.length > 1 && <ExpandableSection 
toggleText={isDescriptionExpanded ? 'Show less' : 'Show more'}
diff --git a/karavan-app/src/main/webui/src/designer/route/RouteDesigner.tsx 
b/karavan-app/src/main/webui/src/designer/route/RouteDesigner.tsx
index 949295ea..87584dc1 100644
--- a/karavan-app/src/main/webui/src/designer/route/RouteDesigner.tsx
+++ b/karavan-app/src/main/webui/src/designer/route/RouteDesigner.tsx
@@ -57,13 +57,14 @@ interface State {
     parentDsl?: string
     selectedPosition?: number
     showSteps: boolean
-    selectedUuid: string
+    selectedUuid: string []
     key: string
     width: number
     height: number
     top: number
     left: number
     clipboardStep?: CamelElement
+    shiftKeyPressed?: boolean
     ref?: any
     printerRef?: any
     propertyOnly: boolean
@@ -79,7 +80,7 @@ export class RouteDesigner extends React.Component<Props, 
State> {
         deleteMessage: '',
         parentId: '',
         showSteps: true,
-        selectedUuid: '',
+        selectedUuid: [],
         key: "",
         width: 1000,
         height: 1000,
@@ -92,6 +93,8 @@ export class RouteDesigner extends React.Component<Props, 
State> {
 
     componentDidMount() {
         window.addEventListener('resize', this.handleResize);
+        window.addEventListener('keydown', this.handleKeyDown);
+        window.addEventListener('keyup', this.handleKeyUp);
         const element = 
findDOMNode(this.state.ref.current)?.parentElement?.parentElement;
         const checkResize = (mutations: any) => {
             const el = mutations[0].target;
@@ -107,12 +110,54 @@ export class RouteDesigner extends React.Component<Props, 
State> {
 
     componentWillUnmount() {
         window.removeEventListener('resize', this.handleResize);
+        window.removeEventListener('keydown', this.handleKeyDown);
+        window.removeEventListener('keyup', this.handleKeyUp);
     }
 
     handleResize = (event: any) => {
         this.setState({key: Math.random().toString()});
     }
 
+    handleKeyDown = (event: KeyboardEvent) => {
+        const {integration, selectedUuid, clipboardStep} = this.state;
+        const selectedUUID = selectedUuid.at(0);
+        if ((event.shiftKey)) {
+            this.setState({shiftKeyPressed: true});
+        }
+        if (window.document.hasFocus() && window.document.activeElement && 
selectedUUID) {
+            if (['BODY', 
'MAIN'].includes(window.document.activeElement.tagName)) {
+                let charCode = String.fromCharCode(event.which).toLowerCase();
+                if ((event.ctrlKey || event.metaKey) && charCode === 'c') {
+                    const selectedElement = 
CamelDefinitionApiExt.findElementInIntegration(integration, selectedUUID);
+                    this.saveToClipboard(selectedElement);
+                } else if ((event.ctrlKey || event.metaKey) && charCode === 
'v') {
+                    if (clipboardStep?.dslName === 'FromDefinition') {
+                        const clone = CamelUtil.cloneStep(clipboardStep, true);
+                        const route = 
CamelDefinitionApi.createRouteDefinition({from: clone});
+                        this.addStep(route, '', 0)
+                    } else {
+                        const meta = 
CamelDefinitionApiExt.findElementMetaInIntegration(integration, selectedUUID);
+                        if (clipboardStep && meta.parentUuid) {
+                            const clone = CamelUtil.cloneStep(clipboardStep, 
true);
+                            this.addStep(clone, meta.parentUuid, 
meta.position);
+                        }
+                    }
+                }
+            }
+        } else {
+            if (event.repeat) {
+                window.dispatchEvent(event);
+            }
+        }
+    }
+
+    handleKeyUp = (event: KeyboardEvent) => {
+        this.setState({shiftKeyPressed: false});
+        if (event.repeat) {
+            window.dispatchEvent(event);
+        }
+    }
+
     componentDidUpdate = (prevProps: Readonly<Props>, prevState: 
Readonly<State>, snapshot?: any) => {
         if (prevState.key !== this.state.key) {
             this.props.onSave?.call(this, this.state.integration, 
this.state.propertyOnly);
@@ -130,14 +175,14 @@ export class RouteDesigner extends React.Component<Props, 
State> {
             const r = CamelDefinitionApi.createRouteDefinition({from: f, id: 
newRoute.name})
             i = CamelDefinitionApiExt.addStepToIntegration(i, r, '');
             const clone = CamelUtil.cloneIntegration(i);
-            this.setState({
+            this.setState(prevState => ({
                 integration: clone,
                 key: Math.random().toString(),
                 showSelector: false,
                 selectedStep: element,
-                selectedUuid: element.uuid,
-                propertyOnly: false
-            });
+                propertyOnly: false,
+                selectedUuid: [element.uuid],
+            }));
         } else {
             const clone = CamelUtil.cloneIntegration(this.state.integration);
             const i = 
CamelDefinitionApiExt.updateIntegrationRouteElement(clone, element);
@@ -170,37 +215,56 @@ export class RouteDesigner extends React.Component<Props, 
State> {
         } else {
             message = 'Delete element from route?';
         }
-        this.setState({selectedUuid: id, showSelector: false, 
showDeleteConfirmation: true, deleteMessage: message});
+        this.setState(prevState => ({
+            showSelector: false,
+            showDeleteConfirmation: true,
+            deleteMessage: message,
+            selectedUuid: [id],
+        }));
     }
 
     deleteElement = () => {
-        const id = this.state.selectedUuid;
-        const i = 
CamelDefinitionApiExt.deleteStepFromIntegration(this.state.integration, id);
-        this.setState({
-            integration: i,
-            showSelector: false,
-            showDeleteConfirmation: false,
-            deleteMessage: '',
-            key: Math.random().toString(),
-            selectedStep: undefined,
-            selectedUuid: '',
-            propertyOnly: false
-        });
-        const el = new CamelElement("");
-        el.uuid = id;
-        EventBus.sendPosition("delete", el, undefined, new DOMRect(), new 
DOMRect(), 0);
+        const id = this.state.selectedUuid.at(0);
+        if (id) {
+            const i = 
CamelDefinitionApiExt.deleteStepFromIntegration(this.state.integration, id);
+            this.setState(prevState => ({
+                integration: i,
+                showSelector: false,
+                showDeleteConfirmation: false,
+                deleteMessage: '',
+                key: Math.random().toString(),
+                selectedStep: undefined,
+                propertyOnly: false,
+                selectedUuid: [id],
+            }));
+            const el = new CamelElement("");
+            el.uuid = id;
+            EventBus.sendPosition("delete", el, undefined, new DOMRect(), new 
DOMRect(), 0);
+        }
     }
 
     selectElement = (element: CamelElement) => {
+        console.log(this.state.shiftKeyPressed, element);
         const i = 
CamelDisplayUtil.setIntegrationVisibility(this.state.integration, element.uuid);
-        this.setState({integration: i, selectedStep: element, selectedUuid: 
element.uuid, showSelector: false})
+        this.setState(prevState => ({
+            integration: i,
+            selectedStep: element,
+            showSelector: false,
+            selectedUuid: [element.uuid],
+        }));
     }
 
     unselectElement = (evt: React.MouseEvent<HTMLDivElement, MouseEvent>) => {
         if ((evt.target as any).dataset.click === 'FLOWS') {
             evt.stopPropagation()
             const i = 
CamelDisplayUtil.setIntegrationVisibility(this.state.integration, undefined);
-            this.setState({integration: i, selectedStep: undefined, 
selectedUuid: '', showSelector: false, selectedPosition: undefined})
+            this.setState(prevState => ({
+                integration: i,
+                selectedStep: undefined,
+                showSelector: false,
+                selectedPosition: undefined,
+                selectedUuid: [],
+            }));
         }
     }
 
@@ -222,8 +286,8 @@ export class RouteDesigner extends React.Component<Props, 
State> {
     onDslSelect = (dsl: DslMetaModel, parentId: string, position?: number | 
undefined) => {
         switch (dsl.dsl) {
             case 'FromDefinition' :
-                const from = CamelDefinitionApi.createRouteDefinition({from: 
new FromDefinition({uri: dsl.uri})});
-                this.addStep(from, parentId, position)
+                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});
@@ -248,26 +312,27 @@ export class RouteDesigner extends React.Component<Props, 
State> {
         const clone = CamelUtil.cloneIntegration(this.state.integration);
         const routeConfiguration = new RouteConfigurationDefinition();
         const i = 
CamelDefinitionApiExt.addRouteConfigurationToIntegration(clone, 
routeConfiguration);
-        this.setState({
+        this.setState(prevState => ({
             integration: i,
             propertyOnly: false,
             key: Math.random().toString(),
             selectedStep: routeConfiguration,
-            selectedUuid: routeConfiguration.uuid,
-        });
+            selectedUuid: [routeConfiguration.uuid],
+        }));
     }
 
     addStep = (step: CamelElement, parentId: string, position?: number | 
undefined) => {
         const i = 
CamelDefinitionApiExt.addStepToIntegration(this.state.integration, step, 
parentId, position);
         const clone = CamelUtil.cloneIntegration(i);
-        this.setState({
+        EventBus.sendPosition("clean", step, undefined, new DOMRect(), new 
DOMRect(), 0);
+        this.setState(prevState => ({
             integration: clone,
             key: Math.random().toString(),
             showSelector: false,
             selectedStep: step,
-            selectedUuid: step.uuid,
-            propertyOnly: false
-        });
+            propertyOnly: false,
+            selectedUuid: [step.uuid],
+        }));
     }
 
     onIntegrationUpdate = (i: Integration) => {
@@ -278,14 +343,14 @@ export class RouteDesigner extends React.Component<Props, 
State> {
         const i = 
CamelDefinitionApiExt.moveRouteElement(this.state.integration, source, target, 
asChild);
         const clone = CamelUtil.cloneIntegration(i);
         const selectedStep = 
CamelDefinitionApiExt.findElementInIntegration(clone, source);
-        this.setState({
+        this.setState(prevState => ({
             integration: clone,
             key: Math.random().toString(),
             showSelector: false,
             selectedStep: selectedStep,
-            selectedUuid: source,
-            propertyOnly: false
-        });
+            propertyOnly: false,
+            selectedUuid: [source],
+        }));
     }
 
     onResizePage(el: HTMLDivElement | null) {
@@ -338,9 +403,7 @@ export class RouteDesigner extends React.Component<Props, 
State> {
                                step={this.state.selectedStep}
                                onIntegrationUpdate={this.onIntegrationUpdate}
                                onPropertyUpdate={this.onPropertyUpdate}
-                               clipboardStep={this.state.clipboardStep}
                                isRouteDesigner={true}
-                               onSaveClipboardStep={this.saveToClipboard}
                                dark={this.props.dark}/>
             </DrawerPanelContent>
         )
@@ -356,7 +419,7 @@ export class RouteDesigner extends React.Component<Props, 
State> {
     integrationImageDownloadFilter = (node: HTMLElement) => {
         const exclusionClasses = ['add-flow'];
         return !exclusionClasses.some(classname => {
-            return node.classList === undefined ? false: 
node.classList.contains(classname);
+            return node.classList === undefined ? false : 
node.classList.contains(classname);
         });
     }
 
@@ -364,11 +427,15 @@ export class RouteDesigner extends React.Component<Props, 
State> {
         if (this.state.printerRef.current === null) {
             return
         }
-        toPng(this.state.printerRef.current, { style:{overflow:'hidden'}, 
cacheBust: true, filter: this.integrationImageDownloadFilter,
-                height:this.state.height,width:this.state.width,  
backgroundColor: this.props.dark?"black":"white" }).then(v => {
-                    toPng(this.state.printerRef.current, { 
style:{overflow:'hidden'}, cacheBust: true, filter: 
this.integrationImageDownloadFilter,
-                    height:this.state.height,width:this.state.width,  
backgroundColor: this.props.dark?"black":"white" 
}).then(this.downloadIntegrationImage);
-            })
+        toPng(this.state.printerRef.current, {
+            style: {overflow: 'hidden'}, cacheBust: true, filter: 
this.integrationImageDownloadFilter,
+            height: this.state.height, width: this.state.width, 
backgroundColor: this.props.dark ? "black" : "white"
+        }).then(v => {
+            toPng(this.state.printerRef.current, {
+                style: {overflow: 'hidden'}, cacheBust: true, filter: 
this.integrationImageDownloadFilter,
+                height: this.state.height, width: this.state.width, 
backgroundColor: this.props.dark ? "black" : "white"
+            }).then(this.downloadIntegrationImage);
+        })
     }
 
     getGraph() {
@@ -380,14 +447,14 @@ export class RouteDesigner extends React.Component<Props, 
State> {
                 <DslConnections height={height} width={width} top={top} 
left={left} integration={integration}/>
                 <div className="flows" data-click="FLOWS" onClick={event => 
this.unselectElement(event)}
                      ref={el => this.onResizePage(el)}>
-                    {routeConfigurations?.map((routeConfiguration , index: 
number) => (
+                    {routeConfigurations?.map((routeConfiguration, index: 
number) => (
                         <DslElement key={routeConfiguration.uuid + key}
                                     integration={integration}
                                     openSelector={this.openSelector}
                                     deleteElement={this.showDeleteConfirmation}
                                     selectElement={this.selectElement}
                                     moveElement={this.moveElement}
-                                    selectedUuid={selectedUuid}
+                                    selectedUuid={selectedUuid.at(0) || ''}
                                     inSteps={false}
                                     position={index}
                                     step={routeConfiguration}
@@ -400,7 +467,7 @@ export class RouteDesigner extends React.Component<Props, 
State> {
                                     deleteElement={this.showDeleteConfirmation}
                                     selectElement={this.selectElement}
                                     moveElement={this.moveElement}
-                                    selectedUuid={selectedUuid}
+                                    selectedUuid={selectedUuid.at(0) || ''}
                                     inSteps={false}
                                     position={index}
                                     step={route}
diff --git a/karavan-app/src/main/webui/src/designer/utils/EventBus.ts 
b/karavan-app/src/main/webui/src/designer/utils/EventBus.ts
index 98e573d7..2df867f4 100644
--- a/karavan-app/src/main/webui/src/designer/utils/EventBus.ts
+++ b/karavan-app/src/main/webui/src/designer/utils/EventBus.ts
@@ -27,9 +27,9 @@ export class DslPosition {
     position: number = 0;
     rect: DOMRect = new DOMRect();
     headerRect: DOMRect = new DOMRect();
-    command: "add" | "delete" = "add";
+    command: "add" | "delete" | "clean" = "add";
 
-    constructor(command: "add" | "delete",
+    constructor(command: "add" | "delete" | "clean",
                 step: CamelElement,
                 parent:CamelElement | undefined,
                 rect: DOMRect,
@@ -49,7 +49,7 @@ export class DslPosition {
 }
 
 export const EventBus = {
-    sendPosition: (command: "add" | "delete",
+    sendPosition: (command: "add" | "delete" | "clean",
                    step: CamelElement,
                    parent: CamelElement | undefined,
                    rect: DOMRect,
diff --git a/karavan-app/src/main/webui/src/projects/CreateFileModal.tsx 
b/karavan-app/src/main/webui/src/projects/CreateFileModal.tsx
index 9101d9ac..90183a0f 100644
--- a/karavan-app/src/main/webui/src/projects/CreateFileModal.tsx
+++ b/karavan-app/src/main/webui/src/projects/CreateFileModal.tsx
@@ -61,8 +61,6 @@ export class CreateFileModal extends React.Component<Props, 
State> {
     render() {
         const {fileType} = this.state;
         const {types} = this.props;
-        console.log(fileType)
-        console.log(types)
         const extension = ProjectFileTypes.filter(value => value.name === 
fileType)[0].extension;
         const filename = (extension !== 'java')
             ? CamelUi.nameFromTitle(this.state.name)


Reply via email to