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 <marat.gubaidul...@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