This is an automated email from the ASF dual-hosted git repository. marat pushed a commit to branch main in repository https://gitbox.apache.org/repos/asf/camel-karavan.git
The following commit(s) were added to refs/heads/main by this push: new ee454172 Implement #622 ee454172 is described below commit ee454172d18c94a85e96638c4a0cac8dccf19921 Author: Marat Gubaidullin <marat.gubaidul...@gmail.com> AuthorDate: Fri Jan 20 16:40:41 2023 -0500 Implement #622 --- karavan-core/src/core/api/CamelUtil.ts | 11 ++++- .../src/designer/rest/RestDesigner.tsx | 1 - .../src/designer/route/DslConnections.tsx | 8 +++- karavan-designer/src/designer/route/DslElement.tsx | 17 ++++++- .../src/designer/route/DslProperties.tsx | 19 -------- .../src/designer/route/RouteDesigner.tsx | 56 +++++++++++++++++----- karavan-designer/src/designer/utils/EventBus.ts | 6 +-- 7 files changed, 80 insertions(+), 38 deletions(-) diff --git a/karavan-core/src/core/api/CamelUtil.ts b/karavan-core/src/core/api/CamelUtil.ts index dca89363..170d267a 100644 --- a/karavan-core/src/core/api/CamelUtil.ts +++ b/karavan-core/src/core/api/CamelUtil.ts @@ -26,6 +26,7 @@ import {ComponentProperty} from "../model/ComponentModels"; import {ComponentApi} from "./ComponentApi"; import {CamelMetadataApi} from "../model/CamelMetadata"; import {CamelDefinitionApiExt} from "./CamelDefinitionApiExt"; +import {v4 as uuidv4} from 'uuid'; export class CamelUtil { @@ -50,8 +51,14 @@ export class CamelUtil { return int; } - static cloneStep = (step: CamelElement): CamelElement => { - const clone = JSON.parse(JSON.stringify(step)); + static cloneStep = (step: CamelElement, generateUuids: boolean = false): CamelElement => { + const clone = JSON.parse(JSON.stringify(step, (key, value) => { + if (generateUuids && key === 'uuid'){ + return uuidv4(); + } else { + return value; + } + })); return CamelDefinitionApi.createStep(step.dslName, clone, true); } diff --git a/karavan-designer/src/designer/rest/RestDesigner.tsx b/karavan-designer/src/designer/rest/RestDesigner.tsx index f91c5129..d13d07e6 100644 --- a/karavan-designer/src/designer/rest/RestDesigner.tsx +++ b/karavan-designer/src/designer/rest/RestDesigner.tsx @@ -240,7 +240,6 @@ export class RestDesigner extends React.Component<Props, State> { step={this.state.selectedStep} onIntegrationUpdate={this.onIntegrationUpdate} onPropertyUpdate={this.onPropertyUpdate} - clipboardStep={undefined} isRouteDesigner={false} onClone={this.cloneRest} dark={this.props.dark}/> diff --git a/karavan-designer/src/designer/route/DslConnections.tsx b/karavan-designer/src/designer/route/DslConnections.tsx index 0806761e..6dc1ced3 100644 --- a/karavan-designer/src/designer/route/DslConnections.tsx +++ b/karavan-designer/src/designer/route/DslConnections.tsx @@ -57,12 +57,18 @@ export class DslConnections extends React.Component<Props, State> { } setPosition(evt: DslPosition) { - if (evt.command === "add") this.setState(prevState => ({steps: prevState.steps.set(evt.step.uuid, evt)})); + if (evt.command === "add") { + this.setState(prevState => ({steps: prevState.steps.set(evt.step.uuid, evt)})); + } else if (evt.command === "delete") this.setState(prevState => { // prevState.steps.clear(); prevState.steps.delete(evt.step.uuid); return {steps: prevState.steps}; }); + else if (evt.command === "clean") this.setState(prevState => { + prevState.steps.clear(); + return {steps: prevState.steps}; + }); } getIncomings() { diff --git a/karavan-designer/src/designer/route/DslElement.tsx b/karavan-designer/src/designer/route/DslElement.tsx index 868f3e2c..07222d84 100644 --- a/karavan-designer/src/designer/route/DslElement.tsx +++ b/karavan-designer/src/designer/route/DslElement.tsx @@ -65,9 +65,22 @@ export class DslElement extends React.Component<Props, State> { tabIndex: 0, selectedUuid: this.props.selectedUuid, isDragging: false, - isDraggedOver: false + isDraggedOver: false, }; + handleKeyDown = (event: React.KeyboardEvent) =>{ + // event.preventDefault(); + // console.log(event); + // let charCode = String.fromCharCode(event.which).toLowerCase(); + // if((event.ctrlKey || event.metaKey) && charCode === 's') { + // alert("CTRL+S Pressed"); + // }else if((event.ctrlKey || event.metaKey) && charCode === 'c') { + // alert("CTRL+C Pressed"); + // }else if((event.ctrlKey || event.metaKey) && charCode === 'v') { + // alert("CTRL+V Pressed"); + // } + } + componentDidUpdate = (prevProps: Readonly<Props>, prevState: Readonly<State>, snapshot?: any) => { if (prevState.selectedUuid !== this.props.selectedUuid) { this.setState({selectedUuid: this.props.selectedUuid}); @@ -484,6 +497,8 @@ export class DslElement extends React.Component<Props, State> { }} onDrop={event => this.dragElement(event, element)} draggable={!this.isNotDraggable()} + // tabIndex={0} + onKeyDown={this.handleKeyDown} > {this.getElementHeader()} {this.getChildElements()} diff --git a/karavan-designer/src/designer/route/DslProperties.tsx b/karavan-designer/src/designer/route/DslProperties.tsx index 0359bb36..f0000bc1 100644 --- a/karavan-designer/src/designer/route/DslProperties.tsx +++ b/karavan-designer/src/designer/route/DslProperties.tsx @@ -36,8 +36,6 @@ import {CamelUtil} from "karavan-core/lib/api/CamelUtil"; import {CamelUi, RouteToCreate} from "../utils/CamelUi"; import {CamelMetadataApi, PropertyMeta} from "karavan-core/lib/model/CamelMetadata"; import {IntegrationHeader} from "../utils/KaravanComponents"; -import CopyIcon from '@patternfly/react-icons/dist/esm/icons/copy-icon' -import PasteIcon from '@patternfly/react-icons/dist/esm/icons/paste-icon' import CloneIcon from "@patternfly/react-icons/dist/esm/icons/clone-icon"; interface Props { @@ -45,8 +43,6 @@ interface Props { step?: CamelElement, onIntegrationUpdate?: any, onPropertyUpdate?: (element: CamelElement, newRoute?: RouteToCreate) => void - clipboardStep?: CamelElement - onSaveClipboardStep?: (element?: CamelElement) => void onClone?: (element: CamelElement) => void isRouteDesigner: boolean dark: boolean @@ -76,15 +72,6 @@ export class DslProperties extends React.Component<Props, State> { } } - pasteClipboardStep = () => { - if (this.props.clipboardStep && this.state.step) { - const clone = CamelUtil.cloneStep(this.props.clipboardStep); - clone.uuid = this.state.step.uuid; - this.setStep(clone) - this.props.onPropertyUpdate?.call(this, clone); - } - } - dataFormatChanged = (value: DataFormatDefinition) => { value.uuid = this.state.step?.uuid ? this.state.step?.uuid : value.uuid; this.setStep(value); @@ -144,12 +131,6 @@ export class DslProperties extends React.Component<Props, State> { <div className="headers"> <div className="top"> <Title headingLevel="h1" size="md">{title}</Title> - <Tooltip content="Copy step" position="bottom"> - <Button variant="link" onClick={() => this.props.onSaveClipboardStep?.call(this, this.state.step)} icon={<CopyIcon/>}/> - </Tooltip> - <Tooltip content="Paste step" position="bottom"> - <Button variant="link" onClick={() => this.pasteClipboardStep()} icon={<PasteIcon/>}/> - </Tooltip> </div> <Text component={TextVariants.p}>{descriptionLines.at(0)}</Text> {descriptionLines.length > 1 && <ExpandableSection toggleText={isDescriptionExpanded ? 'Show less' : 'Show more'} diff --git a/karavan-designer/src/designer/route/RouteDesigner.tsx b/karavan-designer/src/designer/route/RouteDesigner.tsx index 949295ea..10a44ca6 100644 --- a/karavan-designer/src/designer/route/RouteDesigner.tsx +++ b/karavan-designer/src/designer/route/RouteDesigner.tsx @@ -92,6 +92,7 @@ export class RouteDesigner extends React.Component<Props, State> { componentDidMount() { window.addEventListener('resize', this.handleResize); + window.addEventListener('keydown', this.handleKeyDown); const element = findDOMNode(this.state.ref.current)?.parentElement?.parentElement; const checkResize = (mutations: any) => { const el = mutations[0].target; @@ -107,12 +108,42 @@ export class RouteDesigner extends React.Component<Props, State> { componentWillUnmount() { window.removeEventListener('resize', this.handleResize); + window.removeEventListener('keydown', this.handleKeyDown); } handleResize = (event: any) => { this.setState({key: Math.random().toString()}); } + handleKeyDown = (event: KeyboardEvent) => { + const {integration, selectedUuid, clipboardStep} = this.state; + if (window.document.hasFocus() && window.document.activeElement && selectedUuid && selectedUuid !== '') { + if (['BODY', 'MAIN'].includes(window.document.activeElement.tagName)) { + let charCode = String.fromCharCode(event.which).toLowerCase(); + if ((event.ctrlKey || event.metaKey) && charCode === 'c') { + const selectedElement = CamelDefinitionApiExt.findElementInIntegration(integration, selectedUuid); + this.saveToClipboard(selectedElement); + } else if ((event.ctrlKey || event.metaKey) && charCode === 'v') { + if (clipboardStep?.dslName === 'FromDefinition') { + const clone = CamelUtil.cloneStep(clipboardStep, true); + const route = CamelDefinitionApi.createRouteDefinition({from: clone}); + this.addStep(route, '', 0) + } else { + const meta = CamelDefinitionApiExt.findElementMetaInIntegration(integration, selectedUuid); + if (clipboardStep && meta.parentUuid) { + const clone = CamelUtil.cloneStep(clipboardStep, true); + this.addStep(clone, meta.parentUuid, meta.position); + } + } + } + } + } else { + if (event.repeat) { + window.dispatchEvent(event); + } + } + } + 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); @@ -222,8 +253,8 @@ export class RouteDesigner extends React.Component<Props, State> { onDslSelect = (dsl: DslMetaModel, parentId: string, position?: number | undefined) => { switch (dsl.dsl) { case 'FromDefinition' : - const from = CamelDefinitionApi.createRouteDefinition({from: new FromDefinition({uri: dsl.uri})}); - this.addStep(from, parentId, position) + const route = CamelDefinitionApi.createRouteDefinition({from: new FromDefinition({uri: dsl.uri})}); + this.addStep(route, parentId, position) break; case 'ToDefinition' : const to = CamelDefinitionApi.createStep(dsl.dsl, {uri: dsl.uri}); @@ -260,6 +291,7 @@ 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 clone = CamelUtil.cloneIntegration(i); + EventBus.sendPosition("clean", step, undefined, new DOMRect(), new DOMRect(), 0); this.setState({ integration: clone, key: Math.random().toString(), @@ -338,9 +370,7 @@ export class RouteDesigner extends React.Component<Props, State> { step={this.state.selectedStep} onIntegrationUpdate={this.onIntegrationUpdate} onPropertyUpdate={this.onPropertyUpdate} - clipboardStep={this.state.clipboardStep} isRouteDesigner={true} - onSaveClipboardStep={this.saveToClipboard} dark={this.props.dark}/> </DrawerPanelContent> ) @@ -356,7 +386,7 @@ export class RouteDesigner extends React.Component<Props, State> { integrationImageDownloadFilter = (node: HTMLElement) => { const exclusionClasses = ['add-flow']; return !exclusionClasses.some(classname => { - return node.classList === undefined ? false: node.classList.contains(classname); + return node.classList === undefined ? false : node.classList.contains(classname); }); } @@ -364,11 +394,15 @@ export class RouteDesigner extends React.Component<Props, State> { if (this.state.printerRef.current === null) { return } - toPng(this.state.printerRef.current, { style:{overflow:'hidden'}, cacheBust: true, filter: this.integrationImageDownloadFilter, - height:this.state.height,width:this.state.width, backgroundColor: this.props.dark?"black":"white" }).then(v => { - toPng(this.state.printerRef.current, { style:{overflow:'hidden'}, cacheBust: true, filter: this.integrationImageDownloadFilter, - height:this.state.height,width:this.state.width, backgroundColor: this.props.dark?"black":"white" }).then(this.downloadIntegrationImage); - }) + toPng(this.state.printerRef.current, { + style: {overflow: 'hidden'}, cacheBust: true, filter: this.integrationImageDownloadFilter, + height: this.state.height, width: this.state.width, backgroundColor: this.props.dark ? "black" : "white" + }).then(v => { + toPng(this.state.printerRef.current, { + style: {overflow: 'hidden'}, cacheBust: true, filter: this.integrationImageDownloadFilter, + height: this.state.height, width: this.state.width, backgroundColor: this.props.dark ? "black" : "white" + }).then(this.downloadIntegrationImage); + }) } getGraph() { @@ -380,7 +414,7 @@ export class RouteDesigner extends React.Component<Props, State> { <DslConnections height={height} width={width} top={top} left={left} integration={integration}/> <div className="flows" data-click="FLOWS" onClick={event => this.unselectElement(event)} ref={el => this.onResizePage(el)}> - {routeConfigurations?.map((routeConfiguration , index: number) => ( + {routeConfigurations?.map((routeConfiguration, index: number) => ( <DslElement key={routeConfiguration.uuid + key} integration={integration} openSelector={this.openSelector} diff --git a/karavan-designer/src/designer/utils/EventBus.ts b/karavan-designer/src/designer/utils/EventBus.ts index 98e573d7..2df867f4 100644 --- a/karavan-designer/src/designer/utils/EventBus.ts +++ b/karavan-designer/src/designer/utils/EventBus.ts @@ -27,9 +27,9 @@ export class DslPosition { position: number = 0; rect: DOMRect = new DOMRect(); headerRect: DOMRect = new DOMRect(); - command: "add" | "delete" = "add"; + command: "add" | "delete" | "clean" = "add"; - constructor(command: "add" | "delete", + constructor(command: "add" | "delete" | "clean", step: CamelElement, parent:CamelElement | undefined, rect: DOMRect, @@ -49,7 +49,7 @@ export class DslPosition { } export const EventBus = { - sendPosition: (command: "add" | "delete", + sendPosition: (command: "add" | "delete" | "clean", step: CamelElement, parent: CamelElement | undefined, rect: DOMRect,