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/25 15:30:18 UTC
[camel-karavan] 01/03: Apply last changes to space
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 8392b52d10535ebde3c7ff84672a01466875a1cb
Author: Marat Gubaidullin <ma...@gmail.com>
AuthorDate: Mon Jan 23 11:45:05 2023 -0500
Apply last changes to space
---
karavan-space/src/designer/KaravanDesigner.tsx | 11 +-
karavan-space/src/designer/route/RouteDesigner.tsx | 370 ++-------------------
.../{RouteDesigner.tsx => RouteDesignerLogic.tsx} | 346 ++++++-------------
karavan-space/src/designer/utils/EventBus.ts | 14 +
4 files changed, 142 insertions(+), 599 deletions(-)
diff --git a/karavan-space/src/designer/KaravanDesigner.tsx b/karavan-space/src/designer/KaravanDesigner.tsx
index e7f975b8..a2556b2a 100644
--- a/karavan-space/src/designer/KaravanDesigner.tsx
+++ b/karavan-space/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-space/src/designer/route/RouteDesigner.tsx b/karavan-space/src/designer/route/RouteDesigner.tsx
index 67779963..dc0ab805 100644
--- a/karavan-space/src/designer/route/RouteDesigner.tsx
+++ b/karavan-space/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-space/src/designer/route/RouteDesigner.tsx b/karavan-space/src/designer/route/RouteDesignerLogic.tsx
similarity index 50%
copy from karavan-space/src/designer/route/RouteDesigner.tsx
copy to karavan-space/src/designer/route/RouteDesignerLogic.tsx
index 67779963..18b2ce3c 100644
--- a/karavan-space/src/designer/route/RouteDesigner.tsx
+++ b/karavan-space/src/designer/route/RouteDesignerLogic.tsx
@@ -15,141 +15,70 @@
* 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 {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";
-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
+ commandSub?: Subscription
- 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);
observer.observe(element, {attributes: true, attributeOldValue: true, attributeFilter: ['style']});
}
+ this.commandSub = EventBus.onCommand()?.subscribe((command: Command) => this.onCommand(command));
}
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);
+ this.commandSub?.unsubscribe();
}
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;
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)) {
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 {
@@ -160,35 +89,68 @@ 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 => {
+ 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.setState(prevState => ({
+ this.routeDesigner.setState(prevState => ({
key: Math.random().toString(),
clipboardSteps: [...steps]
}));
}
}
+ pasteFromClipboard = (): void => {
+ const {integration, selectedUuids, clipboardSteps} = this.routeDesigner.state;
+ if (clipboardSteps.length === 1 && clipboardSteps[0]?.dslName === 'FromDefinition') {
+ const clone = CamelUtil.cloneStep(clipboardSteps[0], true);
+ const route = CamelDefinitionApi.createRouteDefinition({from: clone});
+ this.addStep(route, '', 0)
+ } else if (clipboardSteps.length === 1 && clipboardSteps[0]?.dslName === 'RouteDefinition') {
+ const clone = CamelUtil.cloneStep(clipboardSteps[0], true);
+ this.addStep(clone, '', 0)
+ } else if (selectedUuids.length === 1) {
+ const targetMeta = CamelDefinitionApiExt.findElementMetaInIntegration(integration, selectedUuids[0]);
+ clipboardSteps.reverse().forEach(clipboardStep => {
+ if (clipboardStep && targetMeta.parentUuid) {
+ const clone = CamelUtil.cloneStep(clipboardStep, true);
+ this.addStep(clone, targetMeta.parentUuid, targetMeta.position);
+ }
+ })
+ }
+ }
+
+ onCommand = (command: Command) => {
+ switch (command.command){
+ case "downloadImage": this.integrationImageDownload()
+ }
+ }
onPropertyUpdate = (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 +159,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 +169,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 +190,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 +199,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 +219,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 +227,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 +249,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 +261,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 +272,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 +301,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 +314,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 +328,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 +347,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 +367,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
diff --git a/karavan-space/src/designer/utils/EventBus.ts b/karavan-space/src/designer/utils/EventBus.ts
index 2df867f4..aeae6ff7 100644
--- a/karavan-space/src/designer/utils/EventBus.ts
+++ b/karavan-space/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(),
}