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/01/23 15:55:59 UTC

[camel-karavan] branch main updated (41d6643d -> 504673cf)

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

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


    from 41d6643d Apply last changes to space
     new 5af51e09 Separate logic in RouteDesigner
     new 9d8b7d0d Separate logic in RouteDesigner
     new 504673cf Copy/Paste Route

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


Summary of changes:
 karavan-designer/src/DesignerPage.tsx              |   8 +-
 karavan-designer/src/designer/KaravanDesigner.tsx  |  11 +-
 .../src/designer/route/RouteDesigner.tsx           | 370 ++-------------------
 .../src/designer/route/RouteDesignerLogic.tsx      | 346 ++++++-------------
 karavan-designer/src/designer/utils/EventBus.ts    |  14 +
 5 files changed, 144 insertions(+), 605 deletions(-)
 copy karavan-space/src/designer/route/RouteDesigner.tsx => karavan-designer/src/designer/route/RouteDesignerLogic.tsx (50%)


[camel-karavan] 01/03: Separate logic in RouteDesigner

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

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

commit 5af51e0901df891bd2c7e9df812244f6d06c243e
Author: Marat Gubaidullin <ma...@gmail.com>
AuthorDate: Mon Jan 23 10:31:44 2023 -0500

    Separate logic in RouteDesigner
---
 .../src/designer/route/RouteDesigner.tsx           | 370 ++-------------------
 .../{RouteDesigner.tsx => RouteDesignerLogic.tsx}  | 283 ++++------------
 2 files changed, 87 insertions(+), 566 deletions(-)

diff --git a/karavan-designer/src/designer/route/RouteDesigner.tsx b/karavan-designer/src/designer/route/RouteDesigner.tsx
index 67779963..dc0ab805 100644
--- a/karavan-designer/src/designer/route/RouteDesigner.tsx
+++ b/karavan-designer/src/designer/route/RouteDesigner.tsx
@@ -25,21 +25,14 @@ import {
 } from '@patternfly/react-core';
 import '../karavan.css';
 import {DslSelector} from "./DslSelector";
-import {DslMetaModel} from "../utils/DslMetaModel";
 import {DslProperties} from "./DslProperties";
-import {CamelUtil} from "karavan-core/lib/api/CamelUtil";
-import {FromDefinition, 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 {DslConnections} from "./DslConnections";
 import PlusIcon from "@patternfly/react-icons/dist/esm/icons/plus-icon";
 import {DslElement} from "./DslElement";
-import {EventBus} from "../utils/EventBus";
-import {CamelUi, RouteToCreate} from "../utils/CamelUi";
-import {findDOMNode} from "react-dom";
+import {CamelUi} from "../utils/CamelUi";
 import {CamelDisplayUtil} from "karavan-core/lib/api/CamelDisplayUtil";
-import {toPng} from 'html-to-image';
+import {RouteDesignerLogic} from "./RouteDesignerLogic";
 
 interface Props {
     onSave?: (integration: Integration, propertyOnly: boolean) => void
@@ -47,7 +40,8 @@ interface Props {
     dark: boolean
 }
 
-interface State {
+export interface RouteDesignerState {
+    logic: RouteDesignerLogic
     integration: Integration
     selectedStep?: CamelElement
     showSelector: boolean
@@ -71,9 +65,10 @@ interface State {
     selectorTabIndex?: string | number
 }
 
-export class RouteDesigner extends React.Component<Props, State> {
+export class RouteDesigner extends React.Component<Props, RouteDesignerState> {
 
-    public state: State = {
+    public state: RouteDesignerState = {
+        logic: new RouteDesignerLogic(this),
         integration: CamelDisplayUtil.setIntegrationVisibility(this.props.integration, undefined),
         showSelector: false,
         showDeleteConfirmation: false,
@@ -93,315 +88,41 @@ 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;
-            const w = el.clientWidth;
-            const isChange = mutations.map((m: any) => `${m.oldValue}`).some((prev: any) => prev.indexOf(`width: ${w}px`) === -1);
-            if (isChange) this.setState({key: Math.random().toString()});
-        }
-        if (element) {
-            const observer = new MutationObserver(checkResize);
-            observer.observe(element, {attributes: true, attributeOldValue: true, attributeFilter: ['style']});
-        }
+        this.state.logic.componentDidMount();
     }
 
     componentWillUnmount() {
-        window.removeEventListener('resize', this.handleResize);
-        window.removeEventListener('keydown', this.handleKeyDown);
-        window.removeEventListener('keyup', this.handleKeyUp);
+        this.state.logic.componentWillUnmount();
     }
 
     handleResize = (event: any) => {
-        this.setState({key: Math.random().toString()});
+        return this.state.logic.handleResize(event);
     }
 
     handleKeyDown = (event: KeyboardEvent) => {
-        const {integration, selectedUuids, clipboardSteps} = this.state;
-        if ((event.shiftKey)) {
-            this.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') {
-                    const steps: CamelElement[] = []
-                    selectedUuids.forEach(selectedUuid => {
-                        const selectedElement = CamelDefinitionApiExt.findElementInIntegration(integration, selectedUuid);
-                        if (selectedElement) {
-                            steps.push(selectedElement);
-                        }
-                    })
-                    this.saveToClipboard(steps);
-                } else if ((event.ctrlKey || event.metaKey) && charCode === 'v') {
-                    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 (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);
-                            }
-                        })
-                    }
-                }
-            }
-        } else {
-            if (event.repeat) {
-                window.dispatchEvent(event);
-            }
-        }
+        return this.state.logic.handleKeyDown(event);
     }
 
     handleKeyUp = (event: KeyboardEvent) => {
-        this.setState({shiftKeyPressed: false});
-        if (event.repeat) {
-            window.dispatchEvent(event);
-        }
+        return this.state.logic.handleKeyUp(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);
-        }
-    }
-
-    saveToClipboard = (steps: CamelElement[]): void => {
-        if (steps.length >0) {
-            this.setState(prevState => ({
-                key: Math.random().toString(),
-                clipboardSteps: [...steps]
-            }));
-        }
-    }
-
-    onPropertyUpdate = (element: CamelElement, newRoute?: RouteToCreate) => {
-        if (newRoute) {
-            let i = CamelDefinitionApiExt.updateIntegrationRouteElement(this.state.integration, element);
-            const f = CamelDefinitionApi.createFromDefinition({uri: newRoute.componentName + ":" + newRoute.name})
-            const r = CamelDefinitionApi.createRouteDefinition({from: f, id: newRoute.name})
-            i = CamelDefinitionApiExt.addStepToIntegration(i, r, '');
-            const clone = CamelUtil.cloneIntegration(i);
-            this.setState(prevState => ({
-                integration: clone,
-                key: Math.random().toString(),
-                showSelector: false,
-                selectedStep: element,
-                propertyOnly: false,
-                selectedUuids: [element.uuid]
-            }));
-        } else {
-            const clone = CamelUtil.cloneIntegration(this.state.integration);
-            const i = CamelDefinitionApiExt.updateIntegrationRouteElement(clone, element);
-            this.setState({integration: i, propertyOnly: true, key: Math.random().toString()});
-        }
-    }
-
-    showDeleteConfirmation = (id: string) => {
-        let message: string;
-        let ce: CamelElement;
-        let isRouteConfiguration: boolean = false;
-        ce = CamelDefinitionApiExt.findElementInIntegration(this.state.integration, id)!;
-        if (ce.dslName === 'FromDefinition') { // Get the RouteDefinition for this.  Use its uuid.
-            let flows = this.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) {
-                        id = 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?';
-            isRouteConfiguration = true;
-        } else {
-            message = 'Delete element from route?';
-        }
-        this.setState(prevState => ({
-            showSelector: false,
-            showDeleteConfirmation: true,
-            deleteMessage: message,
-            selectedUuids: [id],
-        }));
-    }
-
-    deleteElement = () => {
-        const id = this.state.selectedUuids.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,
-                selectedUuids: [id],
-            }));
-            const el = new CamelElement("");
-            el.uuid = id;
-            EventBus.sendPosition("delete", el, undefined, new DOMRect(), new DOMRect(), 0);
-        }
-    }
-
-    selectElement = (element: CamelElement) => {
-        const {shiftKeyPressed, selectedUuids, integration} = this.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.state.integration, element.uuid);
-        this.setState((prevState: State) => {
-            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.state.integration, undefined);
-            this.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.setState({
-            showSelector: true,
-            parentId: parentId || '',
-            parentDsl: parentDsl,
-            showSteps: showSteps,
-            selectedPosition: position,
-            selectorTabIndex: selectorTabIndex
-        })
-    }
-
-    closeDslSelector = () => {
-        this.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);
-                this.addStep(step, parentId, position)
-                break;
-        }
-    }
-
-    createRouteConfiguration = () => {
-        const clone = CamelUtil.cloneIntegration(this.state.integration);
-        const routeConfiguration = new RouteConfigurationDefinition();
-        const i = CamelDefinitionApiExt.addRouteConfigurationToIntegration(clone, routeConfiguration);
-        this.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.state.integration, step, parentId, position);
-        const clone = CamelUtil.cloneIntegration(i);
-        EventBus.sendPosition("clean", step, undefined, new DOMRect(), new DOMRect(), 0);
-        this.setState(prevState => ({
-            integration: clone,
-            key: Math.random().toString(),
-            showSelector: false,
-            selectedStep: step,
-            propertyOnly: false,
-            selectedUuids: [step.uuid],
-        }));
-    }
-
-    onIntegrationUpdate = (i: Integration) => {
-        this.setState({integration: i, propertyOnly: false, showSelector: false, key: Math.random().toString()});
-    }
-
-    moveElement = (source: string, target: string, asChild: boolean) => {
-        const i = CamelDefinitionApiExt.moveRouteElement(this.state.integration, source, target, asChild);
-        const clone = CamelUtil.cloneIntegration(i);
-        const selectedStep = CamelDefinitionApiExt.findElementInIntegration(clone, source);
-        this.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.state.width || el.scrollHeight !== this.state.height || rect.top !== this.state.top || rect.left !== this.state.left)) {
-            this.setState({width: el.scrollWidth, height: el.scrollHeight, top: rect.top, left: rect.left})
-        }
+    componentDidUpdate = (prevProps: Readonly<Props>, prevState: Readonly<RouteDesignerState>, snapshot?: any) => {
+        return this.state.logic.componentDidUpdate(prevState, snapshot);
     }
 
     getSelectorModal() {
         return (
             <DslSelector
                 isOpen={this.state.showSelector}
-                onClose={() => this.closeDslSelector()}
+                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.onDslSelect}/>)
+                onDslSelect={this.state.logic.onDslSelect}/>)
     }
 
     getDeleteConfirmation() {
@@ -412,7 +133,7 @@ export class RouteDesigner extends React.Component<Props, State> {
             isOpen={this.state.showDeleteConfirmation}
             onClose={() => this.setState({showDeleteConfirmation: false})}
             actions={[
-                <Button key="confirm" variant="primary" onClick={e => this.deleteElement()}>Delete</Button>,
+                <Button key="confirm" variant="primary" onClick={e => this.state.logic.deleteElement()}>Delete</Button>,
                 <Button key="cancel" variant="link"
                         onClick={e => this.setState({showDeleteConfirmation: false})}>Cancel</Button>
             ]}
@@ -431,43 +152,14 @@ export class RouteDesigner extends React.Component<Props, State> {
                 <DslProperties ref={this.state.ref}
                                integration={this.state.integration}
                                step={this.state.selectedStep}
-                               onIntegrationUpdate={this.onIntegrationUpdate}
-                               onPropertyUpdate={this.onPropertyUpdate}
+                               onIntegrationUpdate={this.state.logic.onIntegrationUpdate}
+                               onPropertyUpdate={this.state.logic.onPropertyUpdate}
                                isRouteDesigner={true}
                                dark={this.props.dark}/>
             </DrawerPanelContent>
         )
     }
 
-    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.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);
-        })
-    }
-
     getGraph() {
         const {selectedUuids, integration, key, width, height, top, left} = this.state;
         const routes = CamelUi.getRoutes(integration);
@@ -475,15 +167,15 @@ export class RouteDesigner extends React.Component<Props, State> {
         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.unselectElement(event)}
-                     ref={el => this.onResizePage(el)}>
+                <div className="flows" data-click="FLOWS" onClick={event => this.state.logic.unselectElement(event)}
+                     ref={el => this.state.logic.onResizePage(el)}>
                     {routeConfigurations?.map((routeConfiguration, index: number) => (
                         <DslElement key={routeConfiguration.uuid + key}
                                     integration={integration}
-                                    openSelector={this.openSelector}
-                                    deleteElement={this.showDeleteConfirmation}
-                                    selectElement={this.selectElement}
-                                    moveElement={this.moveElement}
+                                    openSelector={this.state.logic.openSelector}
+                                    deleteElement={this.state.logic.showDeleteConfirmation}
+                                    selectElement={this.state.logic.selectElement}
+                                    moveElement={this.state.logic.moveElement}
                                     selectedUuid={selectedUuids}
                                     inSteps={false}
                                     position={index}
@@ -493,10 +185,10 @@ export class RouteDesigner extends React.Component<Props, State> {
                     {routes?.map((route: any, index: number) => (
                         <DslElement key={route.uuid + key}
                                     integration={integration}
-                                    openSelector={this.openSelector}
-                                    deleteElement={this.showDeleteConfirmation}
-                                    selectElement={this.selectElement}
-                                    moveElement={this.moveElement}
+                                    openSelector={this.state.logic.openSelector}
+                                    deleteElement={this.state.logic.showDeleteConfirmation}
+                                    selectElement={this.state.logic.selectElement}
+                                    moveElement={this.state.logic.moveElement}
                                     selectedUuid={selectedUuids}
                                     inSteps={false}
                                     position={index}
@@ -507,12 +199,12 @@ export class RouteDesigner extends React.Component<Props, State> {
                         <Button
                             variant={routes.length === 0 ? "primary" : "secondary"}
                             icon={<PlusIcon/>}
-                            onClick={e => this.openSelector(undefined, undefined)}>Create route
+                            onClick={e => this.state.logic.openSelector(undefined, undefined)}>Create route
                         </Button>
                         <Button
                             variant="secondary"
                             icon={<PlusIcon/>}
-                            onClick={e => this.createRouteConfiguration()}>Create configuration
+                            onClick={e => this.state.logic.createRouteConfiguration()}>Create configuration
                         </Button>
                     </div>
                 </div>
diff --git a/karavan-designer/src/designer/route/RouteDesigner.tsx b/karavan-designer/src/designer/route/RouteDesignerLogic.tsx
similarity index 56%
copy from karavan-designer/src/designer/route/RouteDesigner.tsx
copy to karavan-designer/src/designer/route/RouteDesignerLogic.tsx
index 67779963..0bc1eba8 100644
--- a/karavan-designer/src/designer/route/RouteDesigner.tsx
+++ b/karavan-designer/src/designer/route/RouteDesignerLogic.tsx
@@ -15,93 +15,38 @@
  * limitations under the License.
  */
 import React from 'react';
-import {
-    Drawer,
-    DrawerPanelContent,
-    DrawerContent,
-    DrawerContentBody,
-    Button, Modal,
-    PageSection,
-} from '@patternfly/react-core';
 import '../karavan.css';
-import {DslSelector} from "./DslSelector";
 import {DslMetaModel} from "../utils/DslMetaModel";
-import {DslProperties} from "./DslProperties";
 import {CamelUtil} from "karavan-core/lib/api/CamelUtil";
 import {FromDefinition, 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 {DslConnections} from "./DslConnections";
-import PlusIcon from "@patternfly/react-icons/dist/esm/icons/plus-icon";
-import {DslElement} from "./DslElement";
 import {EventBus} from "../utils/EventBus";
-import {CamelUi, RouteToCreate} from "../utils/CamelUi";
-import {findDOMNode} from "react-dom";
+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";
 
-interface Props {
-    onSave?: (integration: Integration, propertyOnly: boolean) => void
-    integration: Integration
-    dark: boolean
-}
-
-interface State {
-    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 class RouteDesignerLogic {
 
-export class RouteDesigner extends React.Component<Props, State> {
+    routeDesigner: RouteDesigner
 
-    public state: State = {
-        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,
-    };
+    constructor(routeDesigner: RouteDesigner) {
+        this.routeDesigner = routeDesigner;
+    }
 
     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;
+        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.setState({key: Math.random().toString()});
+            if (isChange) this.routeDesigner.setState({key: Math.random().toString()});
         }
         if (element) {
             const observer = new MutationObserver(checkResize);
@@ -110,19 +55,19 @@ export class RouteDesigner extends React.Component<Props, State> {
     }
 
     componentWillUnmount() {
-        window.removeEventListener('resize', this.handleResize);
-        window.removeEventListener('keydown', this.handleKeyDown);
-        window.removeEventListener('keyup', this.handleKeyUp);
+        window.removeEventListener('resize', this.routeDesigner.handleResize);
+        window.removeEventListener('keydown', this.routeDesigner.handleKeyDown);
+        window.removeEventListener('keyup', this.routeDesigner.handleKeyUp);
     }
 
     handleResize = (event: any) => {
-        this.setState({key: Math.random().toString()});
+        this.routeDesigner.setState({key: Math.random().toString()});
     }
 
     handleKeyDown = (event: KeyboardEvent) => {
-        const {integration, selectedUuids, clipboardSteps} = this.state;
+        const {integration, selectedUuids, clipboardSteps} = this.routeDesigner.state;
         if ((event.shiftKey)) {
-            this.setState({shiftKeyPressed: true});
+            this.routeDesigner.setState({shiftKeyPressed: true});
         }
         if (window.document.hasFocus() && window.document.activeElement) {
             if (['BODY', 'MAIN'].includes(window.document.activeElement.tagName)) {
@@ -160,21 +105,21 @@ export class RouteDesigner extends React.Component<Props, State> {
     }
 
     handleKeyUp = (event: KeyboardEvent) => {
-        this.setState({shiftKeyPressed: false});
+        this.routeDesigner.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);
+    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);
         }
     }
 
     saveToClipboard = (steps: CamelElement[]): void => {
         if (steps.length >0) {
-            this.setState(prevState => ({
+            this.routeDesigner.setState(prevState => ({
                 key: Math.random().toString(),
                 clipboardSteps: [...steps]
             }));
@@ -183,12 +128,12 @@ export class RouteDesigner extends React.Component<Props, State> {
 
     onPropertyUpdate = (element: CamelElement, newRoute?: RouteToCreate) => {
         if (newRoute) {
-            let i = CamelDefinitionApiExt.updateIntegrationRouteElement(this.state.integration, element);
+            let i = CamelDefinitionApiExt.updateIntegrationRouteElement(this.routeDesigner.state.integration, element);
             const f = CamelDefinitionApi.createFromDefinition({uri: newRoute.componentName + ":" + newRoute.name})
             const r = CamelDefinitionApi.createRouteDefinition({from: f, id: newRoute.name})
             i = CamelDefinitionApiExt.addStepToIntegration(i, r, '');
             const clone = CamelUtil.cloneIntegration(i);
-            this.setState(prevState => ({
+            this.routeDesigner.setState(prevState => ({
                 integration: clone,
                 key: Math.random().toString(),
                 showSelector: false,
@@ -197,9 +142,9 @@ export class RouteDesigner extends React.Component<Props, State> {
                 selectedUuids: [element.uuid]
             }));
         } else {
-            const clone = CamelUtil.cloneIntegration(this.state.integration);
+            const clone = CamelUtil.cloneIntegration(this.routeDesigner.state.integration);
             const i = CamelDefinitionApiExt.updateIntegrationRouteElement(clone, element);
-            this.setState({integration: i, propertyOnly: true, key: Math.random().toString()});
+            this.routeDesigner.setState({integration: i, propertyOnly: true, key: Math.random().toString()});
         }
     }
 
@@ -207,9 +152,9 @@ export class RouteDesigner extends React.Component<Props, State> {
         let message: string;
         let ce: CamelElement;
         let isRouteConfiguration: boolean = false;
-        ce = CamelDefinitionApiExt.findElementInIntegration(this.state.integration, id)!;
-        if (ce.dslName === 'FromDefinition') { // Get the RouteDefinition for this.  Use its uuid.
-            let flows = this.state.integration.spec.flows!;
+        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];
@@ -228,7 +173,7 @@ export class RouteDesigner extends React.Component<Props, State> {
         } else {
             message = 'Delete element from route?';
         }
-        this.setState(prevState => ({
+        this.routeDesigner.setState(prevState => ({
             showSelector: false,
             showDeleteConfirmation: true,
             deleteMessage: message,
@@ -237,10 +182,10 @@ export class RouteDesigner extends React.Component<Props, State> {
     }
 
     deleteElement = () => {
-        const id = this.state.selectedUuids.at(0);
+        const id = this.routeDesigner.state.selectedUuids.at(0);
         if (id) {
-            const i = CamelDefinitionApiExt.deleteStepFromIntegration(this.state.integration, id);
-            this.setState(prevState => ({
+            const i = CamelDefinitionApiExt.deleteStepFromIntegration(this.routeDesigner.state.integration, id);
+            this.routeDesigner.setState(prevState => ({
                 integration: i,
                 showSelector: false,
                 showDeleteConfirmation: false,
@@ -257,7 +202,7 @@ export class RouteDesigner extends React.Component<Props, State> {
     }
 
     selectElement = (element: CamelElement) => {
-        const {shiftKeyPressed, selectedUuids, integration} = this.state;
+        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;
@@ -265,8 +210,8 @@ export class RouteDesigner extends React.Component<Props, State> {
         }
         const add = shiftKeyPressed && !selectedUuids.includes(element.uuid);
         const remove = shiftKeyPressed && selectedUuids.includes(element.uuid);
-        const i = CamelDisplayUtil.setIntegrationVisibility(this.state.integration, element.uuid);
-        this.setState((prevState: State) => {
+        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);
@@ -287,8 +232,8 @@ export class RouteDesigner extends React.Component<Props, State> {
     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(prevState => ({
+            const i = CamelDisplayUtil.setIntegrationVisibility(this.routeDesigner.state.integration, undefined);
+            this.routeDesigner.setState(prevState => ({
                 integration: i,
                 selectedStep: undefined,
                 showSelector: false,
@@ -299,7 +244,7 @@ export class RouteDesigner extends React.Component<Props, State> {
     }
 
     openSelector = (parentId: string | undefined, parentDsl: string | undefined, showSteps: boolean = true, position?: number | undefined, selectorTabIndex?: string | number) => {
-        this.setState({
+        this.routeDesigner.setState({
             showSelector: true,
             parentId: parentId || '',
             parentDsl: parentDsl,
@@ -310,7 +255,7 @@ export class RouteDesigner extends React.Component<Props, State> {
     }
 
     closeDslSelector = () => {
-        this.setState({showSelector: false})
+        this.routeDesigner.setState({showSelector: false})
     }
 
     onDslSelect = (dsl: DslMetaModel, parentId: string, position?: number | undefined) => {
@@ -339,10 +284,10 @@ export class RouteDesigner extends React.Component<Props, State> {
     }
 
     createRouteConfiguration = () => {
-        const clone = CamelUtil.cloneIntegration(this.state.integration);
+        const clone = CamelUtil.cloneIntegration(this.routeDesigner.state.integration);
         const routeConfiguration = new RouteConfigurationDefinition();
         const i = CamelDefinitionApiExt.addRouteConfigurationToIntegration(clone, routeConfiguration);
-        this.setState(prevState => ({
+        this.routeDesigner.setState(prevState => ({
             integration: i,
             propertyOnly: false,
             key: Math.random().toString(),
@@ -352,10 +297,10 @@ export class RouteDesigner extends React.Component<Props, State> {
     }
 
     addStep = (step: CamelElement, parentId: string, position?: number | undefined) => {
-        const i = CamelDefinitionApiExt.addStepToIntegration(this.state.integration, step, parentId, position);
+        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);
-        this.setState(prevState => ({
+        this.routeDesigner.setState(prevState => ({
             integration: clone,
             key: Math.random().toString(),
             showSelector: false,
@@ -366,14 +311,14 @@ export class RouteDesigner extends React.Component<Props, State> {
     }
 
     onIntegrationUpdate = (i: Integration) => {
-        this.setState({integration: i, propertyOnly: false, showSelector: false, key: Math.random().toString()});
+        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.state.integration, source, target, asChild);
+        const i = CamelDefinitionApiExt.moveRouteElement(this.routeDesigner.state.integration, source, target, asChild);
         const clone = CamelUtil.cloneIntegration(i);
         const selectedStep = CamelDefinitionApiExt.findElementInIntegration(clone, source);
-        this.setState(prevState => ({
+        this.routeDesigner.setState(prevState => ({
             integration: clone,
             key: Math.random().toString(),
             showSelector: false,
@@ -385,60 +330,11 @@ export class RouteDesigner extends React.Component<Props, State> {
 
     onResizePage(el: HTMLDivElement | null) {
         const rect = el?.getBoundingClientRect();
-        if (el && rect && (el.scrollWidth !== this.state.width || el.scrollHeight !== this.state.height || rect.top !== this.state.top || rect.left !== this.state.left)) {
-            this.setState({width: el.scrollWidth, height: el.scrollHeight, top: rect.top, left: rect.left})
+        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})
         }
     }
 
-    getSelectorModal() {
-        return (
-            <DslSelector
-                isOpen={this.state.showSelector}
-                onClose={() => this.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.onDslSelect}/>)
-    }
-
-    getDeleteConfirmation() {
-        let htmlContent: string = this.state.deleteMessage;
-        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.deleteElement()}>Delete</Button>,
-                <Button key="cancel" variant="link"
-                        onClick={e => this.setState({showDeleteConfirmation: false})}>Cancel</Button>
-            ]}
-            onEscapePress={e => this.setState({showDeleteConfirmation: false})}>
-            <div>
-                {htmlContent}
-            </div>
-        </Modal>)
-    }
-
-    getPropertiesPanel() {
-        return (
-            <DrawerPanelContent onResize={width => this.setState({key: Math.random().toString(1)})}
-                                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.onIntegrationUpdate}
-                               onPropertyUpdate={this.onPropertyUpdate}
-                               isRouteDesigner={true}
-                               dark={this.props.dark}/>
-            </DrawerPanelContent>
-        )
-    }
-
     downloadIntegrationImage(dataUrl: string) {
         const a = document.createElement('a');
         a.setAttribute('download', 'karavan-routes.png');
@@ -454,84 +350,17 @@ export class RouteDesigner extends React.Component<Props, State> {
     }
 
     integrationImageDownload() {
-        if (this.state.printerRef.current === null) {
+        if (this.routeDesigner.state.printerRef.current === null) {
             return
         }
-        toPng(this.state.printerRef.current, {
+        toPng(this.routeDesigner.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"
+            height: this.routeDesigner.state.height, width: this.routeDesigner.state.width, backgroundColor: this.routeDesigner.props.dark ? "black" : "white"
         }).then(v => {
-            toPng(this.state.printerRef.current, {
+            toPng(this.routeDesigner.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"
+                height: this.routeDesigner.state.height, width: this.routeDesigner.state.width, backgroundColor: this.routeDesigner.props.dark ? "black" : "white"
             }).then(this.downloadIntegrationImage);
         })
     }
-
-    getGraph() {
-        const {selectedUuids, integration, key, width, height, top, left} = this.state;
-        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.unselectElement(event)}
-                     ref={el => this.onResizePage(el)}>
-                    {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={selectedUuids}
-                                    inSteps={false}
-                                    position={index}
-                                    step={routeConfiguration}
-                                    parent={undefined}/>
-                    ))}
-                    {routes?.map((route: any, index: number) => (
-                        <DslElement key={route.uuid + key}
-                                    integration={integration}
-                                    openSelector={this.openSelector}
-                                    deleteElement={this.showDeleteConfirmation}
-                                    selectElement={this.selectElement}
-                                    moveElement={this.moveElement}
-                                    selectedUuid={selectedUuids}
-                                    inSteps={false}
-                                    position={index}
-                                    step={route}
-                                    parent={undefined}/>
-                    ))}
-                    <div className="add-flow">
-                        <Button
-                            variant={routes.length === 0 ? "primary" : "secondary"}
-                            icon={<PlusIcon/>}
-                            onClick={e => this.openSelector(undefined, undefined)}>Create route
-                        </Button>
-                        <Button
-                            variant="secondary"
-                            icon={<PlusIcon/>}
-                            onClick={e => this.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.getSelectorModal()}
-                {this.getDeleteConfirmation()}
-            </PageSection>
-        );
-    }
 }
\ No newline at end of file


[camel-karavan] 02/03: Separate logic in RouteDesigner

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

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

commit 9d8b7d0de25537bf054989763d06d8ad59693fa4
Author: Marat Gubaidullin <ma...@gmail.com>
AuthorDate: Mon Jan 23 10:48:06 2023 -0500

    Separate logic in RouteDesigner
---
 karavan-designer/src/DesignerPage.tsx                      |  8 ++------
 karavan-designer/src/designer/KaravanDesigner.tsx          | 11 +----------
 karavan-designer/src/designer/route/RouteDesignerLogic.tsx | 12 +++++++++++-
 karavan-designer/src/designer/utils/EventBus.ts            | 14 ++++++++++++++
 4 files changed, 28 insertions(+), 17 deletions(-)

diff --git a/karavan-designer/src/DesignerPage.tsx b/karavan-designer/src/DesignerPage.tsx
index aa6530c4..c3ca5d7f 100644
--- a/karavan-designer/src/DesignerPage.tsx
+++ b/karavan-designer/src/DesignerPage.tsx
@@ -26,6 +26,7 @@ import DownloadIcon from "@patternfly/react-icons/dist/esm/icons/download-icon";
 import DownloadImageIcon from "@patternfly/react-icons/dist/esm/icons/image-icon";
 import {KaravanDesigner} from "./designer/KaravanDesigner";
 import Editor from "@monaco-editor/react";
+import {EventBus} from "./designer/utils/EventBus";
 
 interface Props {
     name: string,
@@ -35,7 +36,6 @@ interface Props {
 }
 
 interface State {
-    karavanDesignerRef: any,
     mode: "design" | "code",
 }
 
@@ -43,7 +43,6 @@ export class DesignerPage extends React.Component<Props, State> {
 
     public state: State = {
         mode: 'design',
-        karavanDesignerRef: React.createRef(),
     };
 
     componentDidMount() {
@@ -64,9 +63,7 @@ export class DesignerPage extends React.Component<Props, State> {
     }
 
     downloadImage = () => {
-        if (this.state.karavanDesignerRef) {
-            this.state.karavanDesignerRef.current.downloadImage();
-        }
+        EventBus.sendCommand("downloadImage");
     }
 
     getDesigner = () => {
@@ -74,7 +71,6 @@ export class DesignerPage extends React.Component<Props, State> {
         return (
             <KaravanDesigner
                 dark={this.props.dark}
-                ref={this.state.karavanDesignerRef}
                 filename={name}
                 yaml={yaml}
                 onSave={(filename, yaml, propertyOnly) => this.save(filename, yaml, propertyOnly)}
diff --git a/karavan-designer/src/designer/KaravanDesigner.tsx b/karavan-designer/src/designer/KaravanDesigner.tsx
index e7f975b8..a2556b2a 100644
--- a/karavan-designer/src/designer/KaravanDesigner.tsx
+++ b/karavan-designer/src/designer/KaravanDesigner.tsx
@@ -44,7 +44,6 @@ interface State {
     integration: Integration
     key: string
     propertyOnly: boolean
-    routeDesignerRef?: any
 }
 
 export class KaravanInstance {
@@ -78,7 +77,6 @@ export class KaravanDesigner extends React.Component<Props, State> {
         integration: this.getIntegration(this.props.yaml, this.props.filename),
         key: "",
         propertyOnly: false,
-        routeDesignerRef: React.createRef(),
     }
 
     componentDidMount() {
@@ -117,12 +115,6 @@ export class KaravanDesigner extends React.Component<Props, State> {
         )
     }
 
-    downloadImage(){
-        if(this.state.routeDesignerRef){
-            this.state.routeDesignerRef.current.integrationImageDownload();
-         }
-    }
-
     render() {
         const tab = this.state.tab;
         return (
@@ -134,8 +126,7 @@ export class KaravanDesigner extends React.Component<Props, State> {
                 </Tabs>
                     {tab === 'routes' && <RouteDesigner integration={this.state.integration}
                                                         onSave={(integration, propertyOnly) => this.save(integration, propertyOnly)}
-                                                        dark={this.props.dark}
-                                                        ref={this.state.routeDesignerRef}/>}
+                                                        dark={this.props.dark}/>}
                     {tab === 'rest' && <RestDesigner integration={this.state.integration}
                                                      onSave={(integration, propertyOnly) => this.save(integration, propertyOnly)}
                                                      dark={this.props.dark}/>}
diff --git a/karavan-designer/src/designer/route/RouteDesignerLogic.tsx b/karavan-designer/src/designer/route/RouteDesignerLogic.tsx
index 0bc1eba8..168112c0 100644
--- a/karavan-designer/src/designer/route/RouteDesignerLogic.tsx
+++ b/karavan-designer/src/designer/route/RouteDesignerLogic.tsx
@@ -22,16 +22,18 @@ import {FromDefinition, RouteConfigurationDefinition, RouteDefinition} from "kar
 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 {EventBus} from "../utils/EventBus";
+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";
 
 export class RouteDesignerLogic {
 
     routeDesigner: RouteDesigner
+    commandSub?: Subscription
 
     constructor(routeDesigner: RouteDesigner) {
         this.routeDesigner = routeDesigner;
@@ -52,12 +54,14 @@ export class RouteDesignerLogic {
             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) => {
@@ -126,6 +130,12 @@ export class RouteDesignerLogic {
         }
     }
 
+    onCommand = (command: Command) => {
+        switch (command.command){
+            case "downloadImage": this.integrationImageDownload()
+        }
+    }
+
     onPropertyUpdate = (element: CamelElement, newRoute?: RouteToCreate) => {
         if (newRoute) {
             let i = CamelDefinitionApiExt.updateIntegrationRouteElement(this.routeDesigner.state.integration, element);
diff --git a/karavan-designer/src/designer/utils/EventBus.ts b/karavan-designer/src/designer/utils/EventBus.ts
index 2df867f4..aeae6ff7 100644
--- a/karavan-designer/src/designer/utils/EventBus.ts
+++ b/karavan-designer/src/designer/utils/EventBus.ts
@@ -48,6 +48,17 @@ export class DslPosition {
     }
 }
 
+const commands = new Subject<Command>();
+export class Command {
+    command: string;
+    data: any;
+
+    constructor(command: string, data: any) {
+        this.command = command;
+        this.data = data;
+    }
+}
+
 export const EventBus = {
     sendPosition: (command: "add" | "delete" | "clean",
                    step: CamelElement,
@@ -58,4 +69,7 @@ export const EventBus = {
                    inSteps: boolean = false,
                    isSelected: boolean = false) => positions.next(new DslPosition(command, step, parent, rect, headerRect, position, inSteps, isSelected)),
     onPosition: () => positions.asObservable(),
+
+    sendCommand: (command: string, data?: any) => commands.next(new Command(command, data)),
+    onCommand: () => commands.asObservable(),
 }


[camel-karavan] 03/03: Copy/Paste Route

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

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

commit 504673cf8b77aaba3b1be2652828d7f14c935429
Author: Marat Gubaidullin <ma...@gmail.com>
AuthorDate: Mon Jan 23 10:55:47 2023 -0500

    Copy/Paste Route
---
 .../src/designer/route/RouteDesignerLogic.tsx      | 53 ++++++++++++----------
 1 file changed, 30 insertions(+), 23 deletions(-)

diff --git a/karavan-designer/src/designer/route/RouteDesignerLogic.tsx b/karavan-designer/src/designer/route/RouteDesignerLogic.tsx
index 168112c0..18b2ce3c 100644
--- a/karavan-designer/src/designer/route/RouteDesignerLogic.tsx
+++ b/karavan-designer/src/designer/route/RouteDesignerLogic.tsx
@@ -69,7 +69,6 @@ export class RouteDesignerLogic {
     }
 
     handleKeyDown = (event: KeyboardEvent) => {
-        const {integration, selectedUuids, clipboardSteps} = this.routeDesigner.state;
         if ((event.shiftKey)) {
             this.routeDesigner.setState({shiftKeyPressed: true});
         }
@@ -77,28 +76,9 @@ export class RouteDesignerLogic {
             if (['BODY', 'MAIN'].includes(window.document.activeElement.tagName)) {
                 let charCode = String.fromCharCode(event.which).toLowerCase();
                 if ((event.ctrlKey || event.metaKey) && charCode === 'c') {
-                    const steps: CamelElement[] = []
-                    selectedUuids.forEach(selectedUuid => {
-                        const selectedElement = CamelDefinitionApiExt.findElementInIntegration(integration, selectedUuid);
-                        if (selectedElement) {
-                            steps.push(selectedElement);
-                        }
-                    })
-                    this.saveToClipboard(steps);
+                    this.copyToClipboard();
                 } else if ((event.ctrlKey || event.metaKey) && charCode === 'v') {
-                    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 (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);
-                            }
-                        })
-                    }
+                    this.pasteFromClipboard();
                 }
             }
         } else {
@@ -121,7 +101,15 @@ export class RouteDesignerLogic {
         }
     }
 
-    saveToClipboard = (steps: CamelElement[]): void => {
+    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(),
@@ -129,6 +117,25 @@ export class RouteDesignerLogic {
             }));
         }
     }
+    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){