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/08/28 22:47:59 UTC
[camel-karavan] 06/07: karavan space for #836
This is an automated email from the ASF dual-hosted git repository.
marat pushed a commit to branch feature-836
in repository https://gitbox.apache.org/repos/asf/camel-karavan.git
commit afd976294f798c9b07bb08739147803d98ab10db
Author: Marat Gubaidullin <ma...@talismancloud.io>
AuthorDate: Mon Aug 28 18:31:42 2023 -0400
karavan space for #836
---
karavan-space/package-lock.json | 46 ++-
karavan-space/package.json | 3 +-
.../src/designer/route/RouteDesignerLogic.tsx | 396 ---------------------
karavan-space/src/space/SpacePage.tsx | 4 +-
4 files changed, 45 insertions(+), 404 deletions(-)
diff --git a/karavan-space/package-lock.json b/karavan-space/package-lock.json
index e6696d93..65a33740 100644
--- a/karavan-space/package-lock.json
+++ b/karavan-space/package-lock.json
@@ -27,7 +27,8 @@
"react-dom": "18.2.0",
"react-scripts": "5.0.1",
"rxjs": "7.8.1",
- "uuid": "9.0.0"
+ "uuid": "9.0.0",
+ "zustand": "^4.4.1"
},
"devDependencies": {
"@svgr/webpack": "^7.0.0",
@@ -4098,7 +4099,7 @@
"version": "15.7.5",
"resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.5.tgz",
"integrity": "sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w==",
- "dev": true
+ "devOptional": true
},
"node_modules/@types/qs": {
"version": "6.9.7",
@@ -4114,7 +4115,7 @@
"version": "18.2.0",
"resolved": "https://registry.npmjs.org/@types/react/-/react-18.2.0.tgz",
"integrity": "sha512-0FLj93y5USLHdnhIhABk83rm8XEGA7kH3cr+YUlvxoUGp1xNt/DINUMvqPxLyOQMzLmZe8i4RTHbvb8MC7NmrA==",
- "dev": true,
+ "devOptional": true,
"dependencies": {
"@types/prop-types": "*",
"@types/scheduler": "*",
@@ -4147,7 +4148,7 @@
"version": "0.16.3",
"resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.3.tgz",
"integrity": "sha512-5cJ8CB4yAx7BH1oMvdU0Jh9lrEXyPkar6F9G/ERswkCuvP4KQZfZkSjcMbAICCpQTN4OuZn8tz0HiKv9TGZgrQ==",
- "dev": true
+ "devOptional": true
},
"node_modules/@types/semver": {
"version": "7.3.13",
@@ -6464,7 +6465,7 @@
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.2.tgz",
"integrity": "sha512-I7K1Uu0MBPzaFKg4nI5Q7Vs2t+3gWWW648spaF+Rg7pI9ds18Ugn+lvg4SHczUdKlHI5LWBXyqfS8+DufyBsgQ==",
- "dev": true
+ "devOptional": true
},
"node_modules/dagre": {
"version": "0.8.5",
@@ -16680,6 +16681,14 @@
"requires-port": "^1.0.0"
}
},
+ "node_modules/use-sync-external-store": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz",
+ "integrity": "sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA==",
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.0 || ^18.0.0"
+ }
+ },
"node_modules/util-deprecate": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
@@ -17646,6 +17655,33 @@
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
+ },
+ "node_modules/zustand": {
+ "version": "4.4.1",
+ "resolved": "https://registry.npmjs.org/zustand/-/zustand-4.4.1.tgz",
+ "integrity": "sha512-QCPfstAS4EBiTQzlaGP1gmorkh/UL1Leaj2tdj+zZCZ/9bm0WS7sI2wnfD5lpOszFqWJ1DcPnGoY8RDL61uokw==",
+ "dependencies": {
+ "use-sync-external-store": "1.2.0"
+ },
+ "engines": {
+ "node": ">=12.7.0"
+ },
+ "peerDependencies": {
+ "@types/react": ">=16.8",
+ "immer": ">=9.0",
+ "react": ">=16.8"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ },
+ "immer": {
+ "optional": true
+ },
+ "react": {
+ "optional": true
+ }
+ }
}
}
}
diff --git a/karavan-space/package.json b/karavan-space/package.json
index 0a7e0fe7..d2f69f78 100644
--- a/karavan-space/package.json
+++ b/karavan-space/package.json
@@ -49,7 +49,8 @@
"react-dom": "18.2.0",
"react-scripts": "5.0.1",
"rxjs": "7.8.1",
- "uuid": "9.0.0"
+ "uuid": "9.0.0",
+ "zustand": "^4.4.1"
},
"devDependencies": {
"@svgr/webpack": "^7.0.0",
diff --git a/karavan-space/src/designer/route/RouteDesignerLogic.tsx b/karavan-space/src/designer/route/RouteDesignerLogic.tsx
deleted file mode 100644
index b352a056..00000000
--- a/karavan-space/src/designer/route/RouteDesignerLogic.tsx
+++ /dev/null
@@ -1,396 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-import React from 'react';
-import '../karavan.css';
-import {DslMetaModel} from "../utils/DslMetaModel";
-import {CamelUtil} from "karavan-core/lib/api/CamelUtil";
-import {ChoiceDefinition, FromDefinition, LogDefinition, 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 {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";
-import debounce from 'lodash.debounce';
-
-export class RouteDesignerLogic {
-
- routeDesigner: RouteDesigner
- commandSub?: Subscription
-
- constructor(routeDesigner: RouteDesigner) {
- this.routeDesigner = routeDesigner;
- }
-
- componentDidMount() {
- 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.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.routeDesigner.handleResize);
- window.removeEventListener('keydown', this.routeDesigner.handleKeyDown);
- window.removeEventListener('keyup', this.routeDesigner.handleKeyUp);
- this.commandSub?.unsubscribe();
- }
-
- handleResize = (event: any) => {
- this.routeDesigner.setState({key: Math.random().toString()});
- }
-
- handleKeyDown = (event: KeyboardEvent) => {
- if ((event.shiftKey)) {
- 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') {
- this.copyToClipboard();
- } else if ((event.ctrlKey || event.metaKey) && charCode === 'v') {
- this.pasteFromClipboard();
- }
- }
- } else {
- if (event.repeat) {
- window.dispatchEvent(event);
- }
- }
- }
-
- handleKeyUp = (event: KeyboardEvent) => {
- this.routeDesigner.setState({shiftKeyPressed: false});
- if (event.repeat) {
- window.dispatchEvent(event);
- }
- }
-
- 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);
- }
- }
-
- copyToClipboard = (): void => {
- const {integration, selectedUuids} = this.routeDesigner.state;
- const steps: CamelElement[] = []
- selectedUuids.forEach(selectedUuid => {
- const selectedElement = CamelDefinitionApiExt.findElementInIntegration(integration, selectedUuid);
- if (selectedElement) {
- steps.push(selectedElement);
- }
- })
- if (steps.length >0) {
- this.routeDesigner.setState(prevState => ({
- key: Math.random().toString(),
- 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 = debounce((element: CamelElement, newRoute?: RouteToCreate) => {
- if (newRoute) {
- let i = CamelDefinitionApiExt.updateIntegrationRouteElement(this.routeDesigner.state.integration, element);
- const f = CamelDefinitionApi.createFromDefinition({uri: newRoute.componentName, parameters: {name: newRoute.name}});
- const r = CamelDefinitionApi.createRouteDefinition({from: f, id: newRoute.name})
- i = CamelDefinitionApiExt.addStepToIntegration(i, r, '');
- const clone = CamelUtil.cloneIntegration(i);
- this.routeDesigner.setState(prevState => ({
- integration: clone,
- key: Math.random().toString(),
- showSelector: false,
- selectedStep: element,
- propertyOnly: false,
- selectedUuids: [element.uuid]
- }));
- } else {
- const clone = CamelUtil.cloneIntegration(this.routeDesigner.state.integration);
- const i = CamelDefinitionApiExt.updateIntegrationRouteElement(clone, element);
- this.routeDesigner.setState({integration: i, propertyOnly: true, key: Math.random().toString()});
- }
- }, 300, {leading: true})
-
- showDeleteConfirmation = (id: string) => {
- let message: string;
- const uuidsToDelete:string [] = [id];
- let ce: CamelElement;
- 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];
- if (routeDefinition.from.uuid === id) {
- uuidsToDelete.push(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?';
- } else {
- message = 'Delete element from route?';
- }
- this.routeDesigner.setState(prevState => ({
- showSelector: false,
- showDeleteConfirmation: true,
- deleteMessage: message,
- selectedUuids: uuidsToDelete,
- }));
- }
-
- deleteElement = () => {
- this.routeDesigner.state.selectedUuids.forEach(uuidToDelete => {
- const i = CamelDefinitionApiExt.deleteStepFromIntegration(this.routeDesigner.state.integration, uuidToDelete);
- this.routeDesigner.setState(prevState => ({
- integration: i,
- showSelector: false,
- showDeleteConfirmation: false,
- deleteMessage: '',
- key: Math.random().toString(),
- selectedStep: undefined,
- propertyOnly: false,
- selectedUuids: [uuidToDelete],
- }));
- const el = new CamelElement("");
- el.uuid = uuidToDelete;
- EventBus.sendPosition("delete", el, undefined, new DOMRect(), new DOMRect(), 0);
- });
- }
-
- selectElement = (element: CamelElement) => {
- 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;
- 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.routeDesigner.state.integration, element.uuid);
- this.routeDesigner.setState((prevState: RouteDesignerState) => {
- 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.routeDesigner.state.integration, undefined);
- this.routeDesigner.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.routeDesigner.setState({
- showSelector: true,
- parentId: parentId || '',
- parentDsl: parentDsl,
- showSteps: showSteps,
- selectedPosition: position,
- selectorTabIndex: selectorTabIndex
- })
- }
-
- closeDslSelector = () => {
- this.routeDesigner.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);
- const augmentedStep = this.setDslDefaults(step);
- this.addStep(augmentedStep, parentId, position)
- break;
- }
- }
-
- setDslDefaults(step: CamelElement): CamelElement {
- if (step.dslName === 'LogDefinition') {
- // eslint-disable-next-line no-template-curly-in-string
- (step as LogDefinition).message = "${body}";
- }
- if (step.dslName === 'ChoiceDefinition') {
- (step as ChoiceDefinition).when?.push(CamelDefinitionApi.createStep('WhenDefinition', undefined));
- (step as ChoiceDefinition).otherwise = CamelDefinitionApi.createStep('OtherwiseDefinition', undefined);
- }
- return step;
- }
-
- createRouteConfiguration = () => {
- const clone = CamelUtil.cloneIntegration(this.routeDesigner.state.integration);
- const routeConfiguration = new RouteConfigurationDefinition();
- const i = CamelDefinitionApiExt.addRouteConfigurationToIntegration(clone, routeConfiguration);
- this.routeDesigner.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.routeDesigner.state.integration, step, parentId, position);
- const clone = CamelUtil.cloneIntegration(i);
- EventBus.sendPosition("clean", step, undefined, new DOMRect(), new DOMRect(), 0);
- const selectedStep = step.dslName === 'RouteDefinition' ? (step as RouteDefinition).from : step;
- this.routeDesigner.setState(prevState => ({
- integration: clone,
- key: Math.random().toString(),
- showSelector: false,
- selectedStep: selectedStep,
- propertyOnly: false,
- selectedUuids: [selectedStep.uuid],
- }));
- }
-
- onIntegrationUpdate = (i: Integration) => {
- 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.routeDesigner.state.integration, source, target, asChild);
- const clone = CamelUtil.cloneIntegration(i);
- const selectedStep = CamelDefinitionApiExt.findElementInIntegration(clone, source);
- this.routeDesigner.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.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})
- }
- }
-
- 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.routeDesigner.state.printerRef.current === null) {
- return
- }
- toPng(this.routeDesigner.state.printerRef.current, {
- style: {overflow: 'hidden'}, cacheBust: true, filter: this.integrationImageDownloadFilter,
- height: this.routeDesigner.state.height, width: this.routeDesigner.state.width, backgroundColor: this.routeDesigner.props.dark ? "black" : "white"
- }).then(v => {
- toPng(this.routeDesigner.state.printerRef.current, {
- style: {overflow: 'hidden'}, cacheBust: true, filter: this.integrationImageDownloadFilter,
- height: this.routeDesigner.state.height, width: this.routeDesigner.state.width, backgroundColor: this.routeDesigner.props.dark ? "black" : "white"
- }).then(this.downloadIntegrationImage);
- })
- }
-}
\ No newline at end of file
diff --git a/karavan-space/src/space/SpacePage.tsx b/karavan-space/src/space/SpacePage.tsx
index 0f71ff2e..2916aff1 100644
--- a/karavan-space/src/space/SpacePage.tsx
+++ b/karavan-space/src/space/SpacePage.tsx
@@ -99,7 +99,7 @@ export class SpacePage extends React.Component<Props, State> {
<KaravanDesigner
key={this.state.key}
dark={this.props.dark}
- ref={this.state.karavanDesignerRef}
+ // ref={this.state.karavanDesignerRef}
filename={name}
yaml={yaml}
onSave={(filename, yaml, propertyOnly) => this.save(filename, yaml, propertyOnly)}
@@ -137,7 +137,7 @@ export class SpacePage extends React.Component<Props, State> {
return (
<PageSection className="kamelet-section designer-page" padding={{default: 'noPadding'}}>
<PageSection className="tools-section" padding={{default: 'noPadding'}}
- style={{backgroundColor:"transparent", paddingLeft: "var(--pf-v5-c-page__main-section--PaddingLeft)"}}>
+ style={{ paddingLeft: "var(--pf-v5-c-page__main-section--PaddingLeft)"}}>
<Flex className="tools" justifyContent={{default: 'justifyContentSpaceBetween'}}>
<FlexItem>
<Flex>