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/21 22:22:28 UTC
[camel-karavan] 01/03: First try
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 e4e70e8fddf9343e021c7105e7f7d7a0340ba9ff
Author: Marat Gubaidullin <ma...@Marats-MacBook-Pro.local>
AuthorDate: Mon Aug 21 10:09:21 2023 -0400
First try
---
karavan-designer/package-lock.json | 64 +++-
karavan-designer/package.json | 3 +-
karavan-designer/public/example/demo.camel.yaml | 41 +++
karavan-designer/src/App.tsx | 7 +-
karavan-designer/src/designer/KaravanDesigner.tsx | 133 +++----
karavan-designer/src/designer/KaravanStore.ts | 127 +++++++
.../src/designer/route/DslConnections.tsx | 250 +++++++------
karavan-designer/src/designer/route/DslElement.tsx | 398 ++++++++++-----------
.../src/designer/route/ElementResizeListener.tsx | 75 ++++
.../src/designer/route/RouteDesigner.tsx | 260 +++++++-------
.../src/designer/route/RouteDesignerLogic.tsx | 396 --------------------
.../designer/route/property/DslPropertyField.tsx | 2 +-
.../src/designer/route/useRouteDesignerHook.tsx | 384 ++++++++++++++++++++
karavan-designer/src/index.tsx | 8 +-
14 files changed, 1206 insertions(+), 942 deletions(-)
diff --git a/karavan-designer/package-lock.json b/karavan-designer/package-lock.json
index a9956967..d6fbc8b5 100644
--- a/karavan-designer/package-lock.json
+++ b/karavan-designer/package-lock.json
@@ -9,7 +9,7 @@
"version": "4.0.0-RC2",
"license": "Apache-2.0",
"dependencies": {
- "@monaco-editor/react": "4.5.0",
+ "@monaco-editor/react": "4.5.1",
"@patternfly/patternfly": "^5.0.2",
"@patternfly/react-core": "^5.0.0",
"@patternfly/react-table": "^5.0.0",
@@ -24,7 +24,8 @@
"react": "18.2.0",
"react-dom": "18.2.0",
"rxjs": "7.8.1",
- "uuid": "9.0.0"
+ "uuid": "9.0.0",
+ "zustand": "^4.4.1"
},
"devDependencies": {
"@svgr/webpack": "^7.0.0",
@@ -36,7 +37,7 @@
"@typescript-eslint/eslint-plugin": "^5.59.2",
"@typescript-eslint/parser": "^5.59.2",
"eslint": "^8.39.0",
- "monaco-editor": "0.38.0",
+ "monaco-editor": "0.41.0",
"react-scripts": "5.0.1",
"typescript": "^4.9.5"
}
@@ -3198,9 +3199,9 @@
}
},
"node_modules/@monaco-editor/react": {
- "version": "4.5.0",
- "resolved": "https://registry.npmjs.org/@monaco-editor/react/-/react-4.5.0.tgz",
- "integrity": "sha512-VJMkp5Fe1+w8pLEq8tZPHZKu8zDXQIA1FtiDTSNccg1D3wg1YIZaH2es2Qpvop1k62g3c/YySRb3bnGXu2XwYQ==",
+ "version": "4.5.1",
+ "resolved": "https://registry.npmjs.org/@monaco-editor/react/-/react-4.5.1.tgz",
+ "integrity": "sha512-NNDFdP+2HojtNhCkRfE6/D6ro6pBNihaOzMbGK84lNWzRu+CfBjwzGt4jmnqimLuqp5yE5viHS2vi+QOAnD5FQ==",
"dependencies": {
"@monaco-editor/loader": "^1.3.3"
},
@@ -3996,7 +3997,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",
@@ -4014,7 +4015,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": "*",
@@ -4049,7 +4050,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",
@@ -6530,7 +6531,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",
@@ -9388,7 +9389,7 @@
"version": "9.0.21",
"resolved": "https://registry.npmjs.org/immer/-/immer-9.0.21.tgz",
"integrity": "sha512-bc4NBHqOqSfRW7POMkHd51LvClaeMXpm8dx0e8oE2GORbq5aRK7Bxl4FyzVLdGtLmvLKL7BTDBG5ACQm4HWjTA==",
- "dev": true,
+ "devOptional": true,
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/immer"
@@ -12777,9 +12778,9 @@
}
},
"node_modules/monaco-editor": {
- "version": "0.38.0",
- "resolved": "https://registry.npmjs.org/monaco-editor/-/monaco-editor-0.38.0.tgz",
- "integrity": "sha512-11Fkh6yzEmwx7O0YoLxeae0qEGFwmyPRlVxpg7oF9czOOCB/iCjdJrG5I67da5WiXK3YJCxoz9TJFE8Tfq/v9A=="
+ "version": "0.41.0",
+ "resolved": "https://registry.npmjs.org/monaco-editor/-/monaco-editor-0.41.0.tgz",
+ "integrity": "sha512-1o4olnZJsiLmv5pwLEAmzHTE/5geLKQ07BrGxlF4Ri/AXAc2yyDGZwHjiTqD8D/ROKUZmwMA28A+yEowLNOEcA=="
},
"node_modules/ms": {
"version": "2.1.2",
@@ -17384,6 +17385,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",
@@ -18430,6 +18439,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-designer/package.json b/karavan-designer/package.json
index 548304dd..a89a6b7e 100644
--- a/karavan-designer/package.json
+++ b/karavan-designer/package.json
@@ -41,7 +41,8 @@
"react": "18.2.0",
"react-dom": "18.2.0",
"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-designer/public/example/demo.camel.yaml b/karavan-designer/public/example/demo.camel.yaml
new file mode 100644
index 00000000..2db92f8d
--- /dev/null
+++ b/karavan-designer/public/example/demo.camel.yaml
@@ -0,0 +1,41 @@
+- route:
+ id: route-f435
+ from:
+ uri: kamelet:timer-source
+ id: from-e52c
+ steps:
+ - choice:
+ when:
+ - expression: {}
+ id: when-064f
+ steps:
+ - multicast:
+ id: multicast-38ce
+ steps:
+ - bean:
+ id: bean-3b8e
+ - log:
+ message: ${body}
+ id: log-546f
+ - loop:
+ expression: {}
+ id: loop-4635
+ steps:
+ - convertBodyTo:
+ id: convertBodyTo-1cae
+ otherwise:
+ id: otherwise-0b09
+ steps:
+ - filter:
+ expression: {}
+ id: filter-a02b
+ id: choice-c53c
+ - doTry:
+ id: doTry-8fd5
+ doCatch:
+ - id: doCatch-1071
+ - id: doCatch-c38e
+ steps:
+ - routingSlip:
+ expression: {}
+ id: routingSlip-a85a
diff --git a/karavan-designer/src/App.tsx b/karavan-designer/src/App.tsx
index b9cae192..3e98dc50 100644
--- a/karavan-designer/src/App.tsx
+++ b/karavan-designer/src/App.tsx
@@ -94,7 +94,8 @@ class App extends React.Component<Props, State> {
fetch("kamelets/kamelets.yaml"),
fetch("components/components.json"),
fetch("snippets/org.apache.camel.AggregationStrategy"),
- fetch("snippets/org.apache.camel.Processor")
+ fetch("snippets/org.apache.camel.Processor"),
+ fetch("example/demo.camel.yaml")
// fetch("components/supported-components.json"),
]).then(responses =>
Promise.all(responses.map(response => response.text()))
@@ -113,6 +114,10 @@ class App extends React.Component<Props, State> {
TemplateApi.saveTemplate("org.apache.camel.Processor", data[3]);
if (data[4]) {
+ this.setState({yaml: data[4], name: "demo.camel.yaml"})
+ }
+
+ if (data[5]) {
ComponentApi.saveSupportedComponents(data[4]);
ComponentApi.setSupportedOnly(true);
}
diff --git a/karavan-designer/src/designer/KaravanDesigner.tsx b/karavan-designer/src/designer/KaravanDesigner.tsx
index a2556b2a..b65a66f1 100644
--- a/karavan-designer/src/designer/KaravanDesigner.tsx
+++ b/karavan-designer/src/designer/KaravanDesigner.tsx
@@ -14,7 +14,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-import React from 'react';
+import React, {useEffect, useState} from 'react';
import {
Badge,
PageSection, PageSectionVariants, Tab, Tabs, TabTitleIcon, TabTitleText, Tooltip,
@@ -27,6 +27,8 @@ import {CamelUtil} from "karavan-core/lib/api/CamelUtil";
import {CamelUi} from "./utils/CamelUi";
import {BeansDesigner} from "./beans/BeansDesigner";
import {RestDesigner} from "./rest/RestDesigner";
+import {useDesignerStore, useIntegrationStore} from "./KaravanStore";
+import {shallow} from "zustand/shallow";
import {getDesignerIcon} from "./utils/KaravanIcons";
interface Props {
@@ -39,67 +41,70 @@ interface Props {
tab?: string
}
-interface State {
- tab: string
- integration: Integration
- key: string
- propertyOnly: boolean
-}
-
export class KaravanInstance {
- static designer: KaravanDesigner;
+ // static designer: KaravanDesigner;
- static set(designer: KaravanDesigner): void {
- KaravanInstance.designer = designer;
- }
-
- static get(): KaravanDesigner {
- return KaravanInstance.designer;
- }
+ // static set(designer: KaravanDesigner): void {
+ // KaravanInstance.designer = designer;
+ // }
+ //
+ // static get(): KaravanDesigner {
+ // return KaravanInstance.designer;
+ // }
static getProps(): Props {
- return KaravanInstance.designer?.props;
+ return {
+ dark: false, filename: "", onGetCustomCode(name: string, javaType: string): Promise<string | undefined> {
+ return Promise.resolve(undefined);
+ }, onSave(filename: string, yaml: string, propertyOnly: boolean): void {
+ }, onSaveCustomCode(name: string, code: string): void {
+ }, yaml: ""
+ };
}
}
-export class KaravanDesigner extends React.Component<Props, State> {
+export const KaravanDesigner = (props: Props) => {
- getIntegration = (yaml: string, filename: string): Integration => {
+ const [tab, setTab] = useState<string>('routes');
+ const [propertyOnly, setPropertyOnly] = useDesignerStore((state) =>
+ [state.propertyOnly, state.setPropertyOnly], shallow )
+ const [integration, setIntegration] = useIntegrationStore((state) =>
+ [state.integration, state.setIntegration], shallow )
+
+ useEffect(() => {
+ console.log("useEffect");
+ setIntegration(makeIntegration(props.yaml, props.filename));
+ }, []);
+
+ function makeIntegration (yaml: string, filename: string): Integration {
if (yaml && CamelDefinitionYaml.yamlIsIntegration(yaml)) {
- return CamelDefinitionYaml.yamlToIntegration(this.props.filename, this.props.yaml)
+ return CamelDefinitionYaml.yamlToIntegration(props.filename, props.yaml)
} else {
return Integration.createNew(filename, 'plain');
}
}
- public state: State = {
- tab: this.props.tab ? this.props.tab : 'routes',
- integration: this.getIntegration(this.props.yaml, this.props.filename),
- key: "",
- propertyOnly: false,
- }
-
- componentDidMount() {
- KaravanInstance.set(this);
- }
-
- componentDidUpdate = (prevProps: Readonly<Props>, prevState: Readonly<State>, snapshot?: any) => {
- if (prevState.key !== this.state.key) {
- this.props.onSave?.call(this, this.props.filename, this.getCode(this.state.integration), this.state.propertyOnly);
- }
- }
-
- save = (integration: Integration, propertyOnly: boolean): void => {
- this.setState({key: Math.random().toString(), integration: integration, propertyOnly: propertyOnly});
- }
-
- getCode = (integration: Integration): string => {
+ // componentDidMount() {
+ // KaravanInstance.set(this);
+ // }
+ //
+ // componentDidUpdate = (prevProps: Readonly<Props>, prevState: Readonly<State>, snapshot?: any) => {
+ // if (prevState.key !== key) {
+ // props.onSave?.call(this, props.filename, this.getCode(integration), propertyOnly);
+ // }
+ // }
+
+ // save = (integration: Integration, propertyOnly: boolean): void => {
+ // this.setState({key: Math.random().toString(), integration: integration, propertyOnly: propertyOnly});
+ // }
+
+ function getCode (integration: Integration): string {
const clone = CamelUtil.cloneIntegration(integration);
return CamelDefinitionYaml.integrationToYaml(clone);
}
- getTab(title: string, tooltip: string, icon: string) {
- const counts = CamelUi.getFlowCounts(this.state.integration);
+ function getTab(title: string, tooltip: string, icon: string) {
+ const counts = CamelUi.getFlowCounts(integration);
const count = counts.has(icon) && counts.get(icon) ? counts.get(icon) : undefined;
const showCount = count && count > 0;
return (
@@ -115,25 +120,23 @@ export class KaravanDesigner extends React.Component<Props, State> {
)
}
- render() {
- const tab = this.state.tab;
- return (
- <PageSection variant={this.props.dark ? PageSectionVariants.darker : PageSectionVariants.light} className="page" isFilled padding={{default: 'noPadding'}}>
- <Tabs className="main-tabs" activeKey={tab} onSelect={(event, tabIndex) => this.setState({tab: tabIndex.toString()})} style={{width: "100%"}}>
- <Tab eventKey='routes' title={this.getTab("Routes", "Integration flows", "routes")}></Tab>
- <Tab eventKey='rest' title={this.getTab("REST", "REST services", "rest")}></Tab>
- <Tab eventKey='beans' title={this.getTab("Beans", "Beans Configuration", "beans")}></Tab>
- </Tabs>
- {tab === 'routes' && <RouteDesigner integration={this.state.integration}
- onSave={(integration, propertyOnly) => this.save(integration, propertyOnly)}
- dark={this.props.dark}/>}
- {tab === 'rest' && <RestDesigner integration={this.state.integration}
- onSave={(integration, propertyOnly) => this.save(integration, propertyOnly)}
- dark={this.props.dark}/>}
- {tab === 'beans' && <BeansDesigner integration={this.state.integration}
- onSave={(integration, propertyOnly) => this.save(integration, propertyOnly)}
- dark={this.props.dark}/>}
- </PageSection>
- )
- }
+ return (
+ <PageSection variant={props.dark ? PageSectionVariants.darker : PageSectionVariants.light} className="page" isFilled padding={{default: 'noPadding'}}>
+ <Tabs className="main-tabs" activeKey={tab} onSelect={(event, tabIndex) => setTab(tabIndex.toString())} style={{width: "100%"}}>
+ <Tab eventKey='routes' title={getTab("Routes", "Integration flows", "routes")}></Tab>
+ <Tab eventKey='rest' title={getTab("REST", "REST services", "rest")}></Tab>
+ <Tab eventKey='beans' title={getTab("Beans", "Beans Configuration", "beans")}></Tab>
+ </Tabs>
+ {tab === 'routes' && <RouteDesigner
+ // integration={integration}
+ // onSave={(integration, propertyOnly) => save(integration, propertyOnly)}
+ dark={props.dark}/>}
+ {/*{tab === 'rest' && <RestDesigner integration={integration}*/}
+ {/* onSave={(integration, propertyOnly) => save(integration, propertyOnly)}*/}
+ {/* dark={props.dark}/>}*/}
+ {/*{tab === 'beans' && <BeansDesigner integration={integration}*/}
+ {/* onSave={(integration, propertyOnly) => save(integration, propertyOnly)}*/}
+ {/* dark={props.dark}/>}*/}
+ </PageSection>
+ )
}
\ No newline at end of file
diff --git a/karavan-designer/src/designer/KaravanStore.ts b/karavan-designer/src/designer/KaravanStore.ts
new file mode 100644
index 00000000..4e710e61
--- /dev/null
+++ b/karavan-designer/src/designer/KaravanStore.ts
@@ -0,0 +1,127 @@
+/*
+ * 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 {create} from 'zustand'
+import {CamelElement, Integration} from "karavan-core/lib/model/IntegrationDefinition";
+import {createWithEqualityFn} from "zustand/traditional";
+import {shallow} from "zustand/shallow";
+
+interface IntegrationState {
+ integration: Integration;
+ setIntegration: (integration: Integration) => void;
+}
+
+export const useIntegrationStore = createWithEqualityFn<IntegrationState>((set) => ({
+ integration: Integration.createNew("demo", "plain"),
+ setIntegration: (integration: Integration) => {
+ set({integration: integration})
+ },
+}), shallow)
+
+interface DesignerState {
+ shiftKeyPressed: boolean;
+ showSelector: boolean;
+ setShowSelector: (showSelector: boolean) => void;
+ showDeleteConfirmation: boolean;
+ setShowDeleteConfirmation: (showDeleteConfirmation: boolean) => void;
+ showMoveConfirmation: boolean;
+ setShowMoveConfirmation: (showMoveConfirmation: boolean) => void;
+ propertyOnly: boolean;
+ setPropertyOnly: (propertyOnly: boolean) => void;
+ showSteps: boolean;
+ setShowSteps: (showSteps: boolean) => void;
+ deleteMessage: string;
+ setDeleteMessage: (deleteMessage: string) => void;
+ parentDsl?: string;
+ setParentDsl: (parentDsl?: string) => void;
+ selectedPosition?: number;
+ setSelectedPosition: (selectedPosition?: number) => void;
+ selectedStep?: CamelElement;
+ setSelectedStep: (selectedStep?: CamelElement) => void;
+ parentId: string;
+ setParentId: (parentId: string) => void;
+ selectedUuids: string[];
+ setSelectedUuids: (selectedUuids: string[]) => void;
+ clipboardSteps: string[];
+ setClipboardSteps: (clipboardSteps: string[]) => void;
+ selectorTabIndex?: string | number
+ setSelectorTabIndex: (selectorTabIndex?: string | number) => void;
+ width: number,
+ height: number,
+ top: number,
+ left: number,
+ setPosition: (width: number, height: number, top: number, left: number) => void;
+}
+
+export const useDesignerStore = createWithEqualityFn<DesignerState>((set) => ({
+ shiftKeyPressed: false,
+ showSelector: false,
+ showDeleteConfirmation: false,
+ showMoveConfirmation: false,
+ deleteMessage: '',
+ parentId: '',
+ showSteps: true,
+ selectedUuids: [],
+ clipboardSteps: [],
+ propertyOnly: false,
+ setSelectorTabIndex: (selectorTabIndex?: string | number) => {
+ set({selectorTabIndex: selectorTabIndex})
+ },
+ setSelectedStep: (selectedStep?: CamelElement) => {
+ set({selectedStep: selectedStep})
+ },
+ setSelectedPosition: (selectedPosition?: number) => {
+ set({selectedPosition: selectedPosition})
+ },
+ setParentDsl: (parentDsl?: string) => {
+ set({parentDsl: parentDsl})
+ },
+ setShowSelector: (showSelector: boolean) => {
+ set({showSelector: showSelector})
+ },
+ setShowDeleteConfirmation: (showDeleteConfirmation: boolean) => {
+ set({showDeleteConfirmation: showDeleteConfirmation})
+ },
+ setShowMoveConfirmation: (showMoveConfirmation: boolean) => {
+ set({showMoveConfirmation: showMoveConfirmation})
+ },
+ setPropertyOnly: (propertyOnly: boolean) => {
+ set({propertyOnly: propertyOnly})
+ },
+ setShowSteps: (showSteps: boolean) => {
+ set({showSteps: showSteps})
+ },
+ setDeleteMessage: (deleteMessage: string) => {
+ set({deleteMessage: deleteMessage})
+ },
+ setParentId: (parentId: string) => {
+ set({parentId: parentId})
+ },
+ setSelectedUuids: (selectedUuids: string[]) => {
+ set({selectedUuids: selectedUuids})
+ },
+ setClipboardSteps: (clipboardSteps: string[]) => {
+ set({clipboardSteps: clipboardSteps})
+ },
+ width: 1000,
+ height: 1000,
+ top: 0,
+ left: 0,
+ setPosition: (width: number, height: number, top: number, left: number) => {
+ set({width: width, height: height, top: top, left: left})
+ },
+}), shallow)
\ No newline at end of file
diff --git a/karavan-designer/src/designer/route/DslConnections.tsx b/karavan-designer/src/designer/route/DslConnections.tsx
index 938042d4..db7cd2dd 100644
--- a/karavan-designer/src/designer/route/DslConnections.tsx
+++ b/karavan-designer/src/designer/route/DslConnections.tsx
@@ -14,67 +14,61 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-import React from 'react';
+import React, {useEffect, useState} from 'react';
import '../karavan.css';
-import {Integration, CamelElement} from "karavan-core/lib/model/IntegrationDefinition";
+import {CamelElement} from "karavan-core/lib/model/IntegrationDefinition";
import {DslPosition, EventBus} from "../utils/EventBus";
import {CamelUi} from "../utils/CamelUi";
import {Subscription} from "rxjs";
import {SagaDefinition} from "karavan-core/lib/model/CamelDefinition";
+import {useDesignerStore} from "../KaravanStore";
-interface Props {
- integration: Integration
- width: number
- height: number
- top: number
- left: number
-}
-
-interface State {
- integration: Integration
- steps: Map<string, DslPosition>
-}
const overlapGap: number = 40;
const outgoingDefinitions: string[] = ['ToDefinition', 'KameletDefinition', 'ToDynamicDefinition', "PollEnrichDefinition", "EnrichDefinition", "WireTapDefinition", "SagaDefinition"];
+export const DslConnections = () => {
-export class DslConnections extends React.Component<Props, State> {
-
- public state: State = {
- integration: this.props.integration,
- steps: new Map<string, DslPosition>(),
- };
- sub?: Subscription;
+ const [ width, height, top, left] = useDesignerStore((s) =>
+ [s.width, s.height, s.top, s.left])
+ const [steps, setSteps] = useState<Map<string, DslPosition>>(new Map<string, DslPosition>());
- componentDidMount() {
- this.sub = EventBus.onPosition()?.subscribe((evt: DslPosition) => this.setPosition(evt));
- }
-
- componentWillUnmount() {
- this.sub?.unsubscribe();
- }
+ useEffect(() => {
+ console.log("DslConnections Start", width, height, top, left);
+ const sub = EventBus.onPosition()?.subscribe((evt: DslPosition) => setPosition(evt));
+ return () => {
+ console.log("DslConnections Stop");
+ sub?.unsubscribe();
+ };
+ }, [width, height, top, left]);
- setPosition(evt: DslPosition) {
+ function setPosition(evt: DslPosition) {
if (evt.command === "add") {
- this.setState(prevState => ({steps: prevState.steps.set(evt.step.uuid, evt)}));
+ setSteps(prevSteps => {
+ prevSteps.set(evt.step.uuid, evt);
+ return prevSteps;
+ })
+ }
+ else if (evt.command === "delete") {
+ setSteps(prevSteps => {
+ prevSteps.clear();
+ Array.from(prevSteps.entries())
+ .filter(value => value[1]?.parent?.uuid !== evt.step.uuid)
+ .forEach(value => prevSteps.set(value[0], value[1]));
+ prevSteps.delete(evt.step.uuid);
+ return prevSteps;
+ })
+ }
+ else if (evt.command === "clean") {
+ setSteps(prevSteps => {
+ prevSteps.clear();
+ return prevSteps;
+ })
}
- else if (evt.command === "delete") this.setState(prevState => {
- prevState.steps.clear();
- Array.from(prevState.steps.entries())
- .filter(value => value[1]?.parent?.uuid !== evt.step.uuid)
- .forEach(value => prevState.steps.set(value[0], value[1]));
- 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() {
- let outs: [string, number][] = Array.from(this.state.steps.values())
+ function getIncomings() {
+ let outs: [string, number][] = Array.from(steps.values())
.filter(pos => ["FromDefinition"].includes(pos.step.dslName))
.filter(pos => !CamelUi.isElementInternalComponent(pos.step))
.filter(pos => !(pos.step.dslName === 'FromDefinition' && CamelUi.hasInternalUri(pos.step)))
@@ -84,17 +78,17 @@ export class DslConnections extends React.Component<Props, State> {
return y1 > y2 ? 1 : -1
})
.map(pos => [pos.step.uuid, pos.headerRect.y]);
- while (this.hasOverlap(outs)) {
- outs = this.addGap(outs);
+ while (hasOverlap(outs)) {
+ outs = addGap(outs);
}
return outs;
}
- getIncoming(data: [string, number]) {
- const pos = this.state.steps.get(data[0]);
+ function getIncoming(data: [string, number]) {
+ const pos = steps.get(data[0]);
if (pos) {
- const fromX = pos.headerRect.x + pos.headerRect.width / 2 - this.props.left;
- const fromY = pos.headerRect.y + pos.headerRect.height / 2 - this.props.top;
+ const fromX = pos.headerRect.x + pos.headerRect.width / 2 - left;
+ const fromY = pos.headerRect.y + pos.headerRect.height / 2 - top;
const r = pos.headerRect.height / 2;
const incomingX = 20;
@@ -117,10 +111,10 @@ export class DslConnections extends React.Component<Props, State> {
}
}
- getIncomingIcons(data: [string, number]) {
- const pos = this.state.steps.get(data[0]);
+ function getIncomingIcons(data: [string, number]) {
+ const pos = steps.get(data[0]);
if (pos) {
- const fromY = pos.headerRect.y + pos.headerRect.height / 2 - this.props.top;
+ const fromY = pos.headerRect.y + pos.headerRect.height / 2 - top;
const r = pos.headerRect.height / 2;
const incomingX = 20;
const imageX = incomingX - r + 5;
@@ -133,7 +127,7 @@ export class DslConnections extends React.Component<Props, State> {
}
}
- hasOverlap(data: [string, number][]): boolean {
+ function hasOverlap(data: [string, number][]): boolean {
let result = false;
data.forEach((d, i, arr) => {
if (i > 0 && d[1] - arr[i - 1][1] < overlapGap) result = true;
@@ -141,7 +135,7 @@ export class DslConnections extends React.Component<Props, State> {
return result;
}
- addGap(data: [string, number][]): [string, number][] {
+ function addGap(data: [string, number][]): [string, number][] {
const result: [string, number][] = [];
data.forEach((d, i, arr) => {
if (i > 0 && d[1] - arr[i - 1][1] < overlapGap) result.push([d[0], d[1] + overlapGap])
@@ -151,8 +145,8 @@ export class DslConnections extends React.Component<Props, State> {
}
- getOutgoings(): [string, number][] {
- let outs: [string, number][] = Array.from(this.state.steps.values())
+ function getOutgoings(): [string, number][] {
+ let outs: [string, number][] = Array.from(steps.values())
.filter(pos => outgoingDefinitions.includes(pos.step.dslName))
.filter(pos => pos.step.dslName !== 'KameletDefinition' || (pos.step.dslName === 'KameletDefinition' && !CamelUi.isActionKamelet(pos.step)))
.filter(pos => pos.step.dslName === 'ToDefinition' && !CamelUi.isActionKamelet(pos.step) && !CamelUi.isElementInternalComponent(pos.step))
@@ -163,21 +157,21 @@ export class DslConnections extends React.Component<Props, State> {
const y2 = pos2.headerRect.y + pos2.headerRect.height / 2;
return y1 > y2 ? 1 : -1
})
- .map(pos => [pos.step.uuid, pos.headerRect.y - this.props.top]);
- while (this.hasOverlap(outs)) {
- outs = this.addGap(outs);
+ .map(pos => [pos.step.uuid, pos.headerRect.y - top]);
+ while (hasOverlap(outs)) {
+ outs = addGap(outs);
}
return outs;
}
- getOutgoing(data: [string, number]) {
- const pos = this.state.steps.get(data[0]);
+ function getOutgoing(data: [string, number]) {
+ const pos = steps.get(data[0]);
if (pos) {
- const fromX = pos.headerRect.x + pos.headerRect.width / 2 - this.props.left;
- const fromY = pos.headerRect.y + pos.headerRect.height / 2 - this.props.top;
+ const fromX = pos.headerRect.x + pos.headerRect.width / 2 - left;
+ const fromY = pos.headerRect.y + pos.headerRect.height / 2 - top;
const r = pos.headerRect.height / 2;
- const outgoingX = this.props.width - 20;
+ const outgoingX = width - 20;
const outgoingY = data[1] + 15;
const lineX1 = fromX + r;
@@ -203,11 +197,11 @@ export class DslConnections extends React.Component<Props, State> {
}
}
- getOutgoingIcons(data: [string, number]) {
- const pos = this.state.steps.get(data[0]);
+ function getOutgoingIcons(data: [string, number]) {
+ const pos = steps.get(data[0]);
if (pos) {
const r = pos.headerRect.height / 2;
- const outgoingX = this.props.width - 20;
+ const outgoingX = width - 20;
const outgoingY = data[1] + 15;
const imageX = outgoingX - r + 5;
const imageY = outgoingY - r + 5;
@@ -219,55 +213,55 @@ export class DslConnections extends React.Component<Props, State> {
}
}
- getInternals(): [string, number, boolean][] {
- let outs: [string, number, boolean][] = Array.from(this.state.steps.values())
+ function getInternals(): [string, number, boolean][] {
+ let outs: [string, number, boolean][] = Array.from(steps.values())
.filter(pos => outgoingDefinitions.includes(pos.step.dslName) && CamelUi.hasInternalUri(pos.step))
.sort((pos1: DslPosition, pos2: DslPosition) => {
const y1 = pos1.headerRect.y + pos1.headerRect.height / 2;
const y2 = pos2.headerRect.y + pos2.headerRect.height / 2;
return y1 > y2 ? 1 : -1
})
- .map(pos => [pos.step.uuid, pos.headerRect.y - this.props.top, pos.isSelected]);
+ .map(pos => [pos.step.uuid, pos.headerRect.y - top, pos.isSelected]);
return outs;
}
- getInternalLines(data: [string, number, boolean]) {
- const pos = this.state.steps.get(data[0]);
+ function getInternalLines(data: [string, number, boolean]) {
+ const pos = steps.get(data[0]);
const uri = (pos?.step as any).uri;
if (uri && uri.length && pos) {
const key = pos.step.uuid + "-outgoing"
- const fromX = pos.headerRect.x + pos.headerRect.width / 2 - this.props.left;
- const fromY = pos.headerRect.y + pos.headerRect.height / 2 - this.props.top;
+ const fromX = pos.headerRect.x + pos.headerRect.width / 2 - left;
+ const fromY = pos.headerRect.y + pos.headerRect.height / 2 - top;
const r = pos.headerRect.height / 2;
const className = (CamelUi.hasDirectUri(pos.step) ? "path-direct" : "path-seda") + (data[2] ? "-selected" : "");
- return this.getInternalLine(uri, key, className, fromX, fromY, r, data[1]);
+ return getInternalLine(uri, key, className, fromX, fromY, r, data[1]);
} else if (pos?.step.dslName === 'SagaDefinition'){
const saga = (pos?.step as SagaDefinition);
- const fromX = pos.headerRect.x + pos.headerRect.width / 2 - this.props.left;
- const fromY = pos.headerRect.y + pos.headerRect.height / 2 - this.props.top;
+ const fromX = pos.headerRect.x + pos.headerRect.width / 2 - left;
+ const fromY = pos.headerRect.y + pos.headerRect.height / 2 - top;
const r = pos.headerRect.height / 2;
const result:any[] = [];
if (saga.completion && (saga.completion.startsWith("direct") || saga.completion.startsWith("seda"))){
const key = pos.step.uuid + "-completion"
const className = saga.completion.startsWith("direct") ? "path-direct" : "path-seda";
- result.push(this.getInternalLine(saga.completion, key, className, fromX, fromY, r, data[1]));
+ result.push(getInternalLine(saga.completion, key, className, fromX, fromY, r, data[1]));
}
if (saga.compensation && (saga.compensation.startsWith("direct") || saga.compensation.startsWith("seda"))){
const key = pos.step.uuid + "-compensation"
const className = saga.compensation.startsWith("direct") ? "path-direct" : "path-seda";
- result.push(this.getInternalLine(saga.compensation, key, className, fromX, fromY, r, data[1]));
+ result.push(getInternalLine(saga.compensation, key, className, fromX, fromY, r, data[1]));
}
return result;
}
}
- getInternalLine(uri: string, key: string, className: string, fromX: number, fromY: number, r: number, i: number) {
- const target = Array.from(this.state.steps.values())
+ function getInternalLine(uri: string, key: string, className: string, fromX: number, fromY: number, r: number, i: number) {
+ const target = Array.from(steps.values())
.filter(s => s.step.dslName === 'FromDefinition')
.filter(s => (s.step as any).uri && (s.step as any).uri === uri)[0];
if (target) {
- const targetX = target.headerRect.x + target.headerRect.width / 2 - this.props.left;
- const targetY = target.headerRect.y + target.headerRect.height / 2 - this.props.top;
+ const targetX = target.headerRect.x + target.headerRect.width / 2 - left;
+ const targetY = target.headerRect.y + target.headerRect.height / 2 - top;
const gap = 100;
const add = 0.2;
@@ -294,7 +288,7 @@ export class DslConnections extends React.Component<Props, State> {
const pointX4 = pointLX + coefX;
const pointY4 = endY;
- return this.getInternalPath(key, className, startX, startY, pointX1, pointY1, pointX2, pointY2, pointLX, pointLY, pointX3, pointY3, pointX4, pointY4, endX, endY);
+ return getInternalPath(key, className, startX, startY, pointX1, pointY1, pointX2, pointY2, pointLX, pointLY, pointX3, pointY3, pointX4, pointY4, endX, endY);
} else if (targetX > fromX && targetX - fromX < gap) {
const startX = fromX - r;
const startY = fromY;
@@ -317,7 +311,7 @@ export class DslConnections extends React.Component<Props, State> {
const pointX4 = pointLX - coefX/2;
const pointY4 = endY;
- return this.getInternalPath(key, className, startX, startY, pointX1, pointY1, pointX2, pointY2, pointLX, pointLY, pointX3, pointY3, pointX4, pointY4, endX, endY);
+ return getInternalPath(key, className, startX, startY, pointX1, pointY1, pointX2, pointY2, pointLX, pointLY, pointX3, pointY3, pointX4, pointY4, endX, endY);
} else if (targetX <= fromX && fromX - targetX < gap) {
const startX = fromX + r;
const startY = fromY;
@@ -340,7 +334,7 @@ export class DslConnections extends React.Component<Props, State> {
const pointX4 = pointLX - coefX/2;
const pointY4 = endY;
- return this.getInternalPath(key, className, startX, startY, pointX1, pointY1, pointX2, pointY2, pointLX, pointLY, pointX3, pointY3, pointX4, pointY4, endX, endY);
+ return getInternalPath(key, className, startX, startY, pointX1, pointY1, pointX2, pointY2, pointLX, pointLY, pointX3, pointY3, pointX4, pointY4, endX, endY);
} else {
const startX = fromX - r;
const startY = fromY;
@@ -363,12 +357,12 @@ export class DslConnections extends React.Component<Props, State> {
const pointX4 = pointLX + coefX;
const pointY4 = endY;
- return this.getInternalPath(key, className, startX, startY, pointX1, pointY1, pointX2, pointY2, pointLX, pointLY, pointX3, pointY3, pointX4, pointY4, endX, endY);
+ return getInternalPath(key, className, startX, startY, pointX1, pointY1, pointX2, pointY2, pointLX, pointLY, pointX3, pointY3, pointX4, pointY4, endX, endY);
}
}
}
- getInternalPath(key: string, className: string, startX: number, startY: number, pointX1: number, pointY1: number, pointX2: number, pointY2: number, pointLX: number, pointLY: number,
+ function getInternalPath(key: string, className: string, startX: number, startY: number, pointX1: number, pointY1: number, pointX2: number, pointY2: number, pointLX: number, pointLY: number,
pointX3: number, pointY3: number, pointX4: number, pointY4: number, endX: number, endY: number) {
return (
<g key={key}>
@@ -380,35 +374,35 @@ export class DslConnections extends React.Component<Props, State> {
)
}
- getCircle(pos: DslPosition) {
- const cx = pos.headerRect.x + pos.headerRect.width / 2 - this.props.left;
- const cy = pos.headerRect.y + pos.headerRect.height / 2 - this.props.top;
+ function getCircle(pos: DslPosition) {
+ const cx = pos.headerRect.x + pos.headerRect.width / 2 - left;
+ const cy = pos.headerRect.y + pos.headerRect.height / 2 - top;
const r = pos.headerRect.height / 2;
return (
<circle cx={cx} cy={cy} r={r} stroke="transparent" strokeWidth="3" fill="transparent" key={pos.step.uuid + "-circle"}/>
)
}
- hasSteps = (step: CamelElement): boolean => {
+ function hasSteps (step: CamelElement): boolean {
return (step.hasSteps() && !['FromDefinition'].includes(step.dslName))
|| ['RouteDefinition', 'TryDefinition', 'ChoiceDefinition', 'SwitchDefinition'].includes(step.dslName);
}
- getPreviousStep(pos: DslPosition) {
- return Array.from(this.state.steps.values())
+ function getPreviousStep(pos: DslPosition) {
+ return Array.from(steps.values())
.filter(p => pos.parent?.uuid === p.parent?.uuid)
.filter(p => p.inSteps)
.filter(p => p.position === pos.position - 1)[0];
}
- getArrow(pos: DslPosition) {
- const endX = pos.headerRect.x + pos.headerRect.width / 2 - this.props.left;
- const endY = pos.headerRect.y - 9 - this.props.top;
+ function getArrow(pos: DslPosition) {
+ const endX = pos.headerRect.x + pos.headerRect.width / 2 - left;
+ const endY = pos.headerRect.y - 9 - top;
if (pos.parent) {
- const parent = this.state.steps.get(pos.parent.uuid);
+ const parent = steps.get(pos.parent.uuid);
if (parent) {
- const startX = parent.headerRect.x + parent.headerRect.width / 2 - this.props.left;
- const startY = parent.headerRect.y + parent.headerRect.height - this.props.top;
+ const startX = parent.headerRect.x + parent.headerRect.width / 2 - left;
+ const startY = parent.headerRect.y + parent.headerRect.height - top;
if ((!pos.inSteps || (pos.inSteps && pos.position === 0)) && parent.step.dslName !== 'MulticastDefinition') {
return (
<path name={pos.step.dslName} d={`M ${startX},${startY} C ${startX},${endY} ${endX},${startY} ${endX},${endY}`}
@@ -419,22 +413,22 @@ export class DslConnections extends React.Component<Props, State> {
<path d={`M ${startX},${startY} C ${startX},${endY} ${endX},${startY} ${endX},${endY}`}
className="path" key={pos.step.uuid} markerEnd="url(#arrowhead)"/>
)
- } else if (pos.inSteps && pos.position > 0 && !this.hasSteps(pos.step)) {
- const prev = this.getPreviousStep(pos);
+ } else if (pos.inSteps && pos.position > 0 && !hasSteps(pos.step)) {
+ const prev = getPreviousStep(pos);
if (prev) {
- const r = this.hasSteps(prev.step) ? prev.rect : prev.headerRect;
- const prevX = r.x + r.width / 2 - this.props.left;
- const prevY = r.y + r.height - this.props.top;
+ const r = hasSteps(prev.step) ? prev.rect : prev.headerRect;
+ const prevX = r.x + r.width / 2 - left;
+ const prevY = r.y + r.height - top;
return (
<line x1={prevX} y1={prevY} x2={endX} y2={endY} className="path" key={pos.step.uuid} markerEnd="url(#arrowhead)"/>
)
}
- } else if (pos.inSteps && pos.position > 0 && this.hasSteps(pos.step)) {
- const prev = this.getPreviousStep(pos);
+ } else if (pos.inSteps && pos.position > 0 && hasSteps(pos.step)) {
+ const prev = getPreviousStep(pos);
if (prev) {
- const r = this.hasSteps(prev.step) ? prev.rect : prev.headerRect;
- const prevX = r.x + r.width / 2 - this.props.left;
- const prevY = r.y + r.height - this.props.top;
+ const r = hasSteps(prev.step) ? prev.rect : prev.headerRect;
+ const prevX = r.x + r.width / 2 - left;
+ const prevY = r.y + r.height - top;
return (
<line x1={prevX} y1={prevY} x2={endX} y2={endY} className="path" key={pos.step.uuid} markerEnd="url(#arrowhead)"/>
)
@@ -444,33 +438,31 @@ export class DslConnections extends React.Component<Props, State> {
}
}
- getSvg() {
- const steps = Array.from(this.state.steps.values());
+ function getSvg() {
+ const stepsArray = Array.from(steps.values());
return (
<svg
- style={{width: this.props.width, height: this.props.height, position: "absolute", left: 0, top: 0}}
- viewBox={"0 0 " + this.props.width + " " + this.props.height}>
+ style={{width: width, height: height, position: "absolute", left: 0, top: 0}}
+ viewBox={"0 0 " + width + " " + height}>
<defs>
<marker id="arrowhead" markerWidth="9" markerHeight="6" refX="0" refY="3" orient="auto" className="arrow">
<polygon points="0 0, 9 3, 0 6"/>
</marker>
</defs>
- {steps.map(pos => this.getCircle(pos))}
- {steps.map(pos => this.getArrow(pos))}
- {this.getIncomings().map(p => this.getIncoming(p))}
- {this.getOutgoings().map(p => this.getOutgoing(p))}
- {/*{this.getInternals().map((p) => this.getInternalLines(p)).flat()}*/}
+ {stepsArray.map(pos => getCircle(pos))}
+ {stepsArray.map(pos => getArrow(pos))}
+ {getIncomings().map(p => getIncoming(p))}
+ {getOutgoings().map(p => getOutgoing(p))}
+ {/*{getInternals().map((p) => getInternalLines(p)).flat()}*/}
</svg>
)
}
- render() {
- return (
- <div className="connections" style={{width: this.props.width, height: this.props.height, marginTop: "8px"}}>
- {this.getSvg()}
- {this.getIncomings().map(p => this.getIncomingIcons(p))}
- {this.getOutgoings().map(p => this.getOutgoingIcons(p))}
- </div>
- );
- }
+ return (
+ <div className="connections" style={{width: width, height: height, marginTop: "0px"}}>
+ {getSvg()}
+ {getIncomings().map(p => getIncomingIcons(p))}
+ {getOutgoings().map(p => getOutgoingIcons(p))}
+ </div>
+ )
}
diff --git a/karavan-designer/src/designer/route/DslElement.tsx b/karavan-designer/src/designer/route/DslElement.tsx
index 2ea1aaa2..2b498d02 100644
--- a/karavan-designer/src/designer/route/DslElement.tsx
+++ b/karavan-designer/src/designer/route/DslElement.tsx
@@ -14,7 +14,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-import React, {CSSProperties} from 'react';
+import React, {createRef, CSSProperties, useState} from 'react';
import {
Button,
Flex,
@@ -27,145 +27,149 @@ import DeleteIcon from "@patternfly/react-icons/dist/js/icons/times-circle-icon"
import InsertIcon from "@patternfly/react-icons/dist/js/icons/arrow-alt-circle-right-icon";
import {CamelElement, Integration} from "karavan-core/lib/model/IntegrationDefinition";
import {CamelUi} from "../utils/CamelUi";
-import {EventBus} from "../utils/EventBus";
+import {DslPosition, EventBus} from "../utils/EventBus";
import {ChildElement, CamelDefinitionApiExt} from "karavan-core/lib/api/CamelDefinitionApiExt";
import ReactDOM from "react-dom";
import {CamelUtil} from "karavan-core/lib/api/CamelUtil";
import {CamelDisplayUtil} from "karavan-core/lib/api/CamelDisplayUtil";
+import {useDesignerStore, useIntegrationStore} from "../KaravanStore";
+import {shallow} from "zustand/shallow";
+import {useRouteDesignerHook} from "./useRouteDesignerHook";
interface Props {
- integration: Integration,
+ // integration: Integration,
step: CamelElement,
parent: CamelElement | undefined,
- deleteElement: any
- selectElement: any
- openSelector: (parentId: string | undefined, parentDsl: string | undefined, showSteps: boolean, position?: number | undefined) => void
- moveElement: (source: string, target: string, asChild: boolean) => void
- selectedUuid: string []
+ // deleteElement: any
+ // selectElement: any
+ // openSelector: (parentId: string | undefined, parentDsl: string | undefined, showSteps: boolean, position?: number | undefined) => void
+ // moveElement: (source: string, target: string, asChild: boolean) => void
+ // selectedUuid: string []
inSteps: boolean
position: number
}
-interface State {
- showSelector: boolean
- showMoveConfirmation: boolean
- moveElements: [string | undefined, string | undefined]
- tabIndex: string | number
- isDragging: boolean
- isDraggedOver: boolean
-}
+export const DslElement = (props: Props) => {
+
+ const ref = React.createRef();
+ const { deleteElement, selectElement, moveElement, onShowDeleteConfirmation, openSelector } = useRouteDesignerHook();
-export class DslElement extends React.Component<Props, State> {
+ const [integration, setIntegration] = useIntegrationStore((s) => [s.integration, s.setIntegration], shallow)
- public state: State = {
- showSelector: false,
- showMoveConfirmation: false,
- moveElements: [undefined, undefined],
- tabIndex: 0,
- isDragging: false,
- isDraggedOver: false,
- };
+ const [showSelector, showDeleteConfirmation, propertyOnly, showSteps,deleteMessage,parentId, selectedUuids,clipboardSteps, parentDsl,selectedPosition,selectedStep,selectorTabIndex,showMoveConfirmation,
+ setShowSelector, setShowDeleteConfirmation,setPropertyOnly,setShowSteps,setDeleteMessage, setParentId, setParentDsl, setSelectedPosition, setSelectedUuids, setClipboardSteps,setPosition,setShowMoveConfirmation,
+ width, height, top, left] = useDesignerStore((s) =>
+ [s.showSelector, s.showDeleteConfirmation, s.propertyOnly, s.showSteps,s.deleteMessage,s.parentId, s.selectedUuids,s.clipboardSteps, s.parentDsl, s.selectedPosition, s.selectedStep,s.selectorTabIndex, s.showMoveConfirmation,
+ s.setShowSelector, s.setShowDeleteConfirmation,s.setPropertyOnly,s.setShowSteps,s.setDeleteMessage, s.setParentId, s.setParentDsl, s.setSelectedPosition, s.setSelectedUuids, s.setClipboardSteps, s.setPosition, s.setShowMoveConfirmation,
+ s.width, s.height, s.top, s.left], shallow)
+ const [isDragging, setIsDragging] = useState<boolean>(false);
+
+ const [isDraggedOver, setIsDraggedOver] = useState<boolean>(false);
+ const [moveElements, setMoveElements] = useState<[string | undefined, string | undefined]>([undefined, undefined]);
//
// componentDidUpdate = (prevProps: Readonly<Props>, prevState: Readonly<State>, snapshot?: any) => {
- // if (prevState.selectedUuid !== this.props.selectedUuid) {
- // this.setState({selectedUuid: this.props.selectedUuid});
+ // if (prevselectedUuid !== props.selectedUuid) {
+ // setState({selectedUuid: props.selectedUuid});
// }
// }
- openSelector = (evt: React.MouseEvent, showSteps: boolean = true, isInsert: boolean = false) => {
+ function onOpenSelector (evt: React.MouseEvent, showSteps: boolean = true, isInsert: boolean = false) {
evt.stopPropagation();
- if (isInsert && this.props.parent) {
- this.props.openSelector.call(this, this.props.parent.uuid, this.props.parent.dslName, showSteps, this.props.position);
+ if (isInsert && props.parent) {
+ openSelector(props.parent.uuid, props.parent.dslName, showSteps, props.position);
} else {
- this.props.openSelector.call(this, this.props.step.uuid, this.props.step.dslName, showSteps);
+ openSelector(props.step.uuid, props.step.dslName, showSteps);
}
}
- closeDslSelector = () => {
- this.setState({showSelector: false})
+ function closeDslSelector () {
+ // setState({showSelector: false})
+ setShowSelector(false);
}
- delete = (evt: React.MouseEvent) => {
+ function onDeleteElement (evt: React.MouseEvent) {
evt.stopPropagation();
- this.props.deleteElement.call(this, this.props.step.uuid);
+ onShowDeleteConfirmation(props.step.uuid);
}
- selectElement = (evt: React.MouseEvent) => {
+ function onSelectElement (evt: React.MouseEvent) {
evt.stopPropagation();
- this.props.selectElement.call(this, this.props.step);
+ selectElement(props.step);
}
- dragElement = (event: React.DragEvent<HTMLDivElement>, element: CamelElement) => {
+ function dragElement (event: React.DragEvent<HTMLDivElement>, element: CamelElement) {
event.preventDefault();
event.stopPropagation();
- this.setState({isDraggedOver: false});
+ setIsDraggedOver(false);
const sourceUuid = event.dataTransfer.getData("text/plain");
const targetUuid = element.uuid;
if (sourceUuid !== targetUuid) {
if (element.hasSteps()){
- this.setState({showMoveConfirmation: true, moveElements: [sourceUuid, targetUuid]});
+ setShowMoveConfirmation(true);
+ setMoveElements([sourceUuid, targetUuid])
} else {
- this.props.moveElement?.call(this, sourceUuid, targetUuid, false);
+ moveElement(sourceUuid, targetUuid, false);
}
}
}
- confirmMove = (asChild: boolean) => {
- const sourceUuid = this.state.moveElements[0];
- const targetUuid = this.state.moveElements[1];
+ function confirmMove (asChild: boolean) {
+ const sourceUuid = moveElements[0];
+ const targetUuid = moveElements[1];
if (sourceUuid && targetUuid && sourceUuid !== targetUuid) {
- this.props.moveElement?.call(this, sourceUuid, targetUuid, asChild);
- this.setState({showMoveConfirmation: false, moveElements: [undefined, undefined]})
+ moveElement(sourceUuid, targetUuid, asChild);
+ cancelMove();
}
}
- cancelMove = () => {
- this.setState({showMoveConfirmation: false, moveElements: [undefined, undefined]})
+ function cancelMove () {
+ setShowMoveConfirmation(false);
+ setMoveElements([undefined, undefined]);
}
- isSelected = (): boolean => {
- return this.props.selectedUuid.includes(this.props.step.uuid);
+ function isSelected (): boolean {
+ return selectedUuids.includes(props.step.uuid);
}
- hasBorder = (): boolean => {
- return (this.props.step?.hasSteps() && !['FromDefinition'].includes(this.props.step.dslName))
+ function hasBorder (): boolean {
+ return (props.step?.hasSteps() && !['FromDefinition'].includes(props.step.dslName))
|| ['RouteConfigurationDefinition',
'RouteDefinition',
'TryDefinition',
'ChoiceDefinition',
- 'SwitchDefinition'].includes(this.props.step.dslName);
+ 'SwitchDefinition'].includes(props.step.dslName);
}
- isNotDraggable = (): boolean => {
- return ['FromDefinition', 'RouteConfigurationDefinition', 'RouteDefinition', 'WhenDefinition', 'OtherwiseDefinition'].includes(this.props.step.dslName);
+ function isNotDraggable (): boolean {
+ return ['FromDefinition', 'RouteConfigurationDefinition', 'RouteDefinition', 'WhenDefinition', 'OtherwiseDefinition'].includes(props.step.dslName);
}
- isWide = (): boolean => {
+ function isWide (): boolean {
return ['RouteConfigurationDefinition', 'RouteDefinition', 'ChoiceDefinition', 'SwitchDefinition', 'MulticastDefinition', 'TryDefinition', 'CircuitBreakerDefinition']
- .includes(this.props.step.dslName);
+ .includes(props.step.dslName);
}
- isAddStepButtonLeft = (): boolean => {
+ function isAddStepButtonLeft (): boolean {
return ['MulticastDefinition']
- .includes(this.props.step.dslName);
+ .includes(props.step.dslName);
}
- isHorizontal = (): boolean => {
- return ['MulticastDefinition'].includes(this.props.step.dslName);
+ function isHorizontal (): boolean {
+ return ['MulticastDefinition'].includes(props.step.dslName);
}
- isRoot = (): boolean => {
- return ['RouteConfigurationDefinition', 'RouteDefinition'].includes(this.props.step?.dslName);
+ function isRoot (): boolean {
+ return ['RouteConfigurationDefinition', 'RouteDefinition'].includes(props.step?.dslName);
}
- isInStepWithChildren = () => {
- const step: CamelElement = this.props.step;
+ function isInStepWithChildren () {
+ const step: CamelElement = props.step;
const children = CamelDefinitionApiExt.getElementChildrenDefinition(step.dslName);
- return children.filter((c: ChildElement) => c.name === 'steps' || c.multiple).length > 0 && this.props.inSteps;
+ return children.filter((c: ChildElement) => c.name === 'steps' || c.multiple).length > 0 && props.inSteps;
}
- getChildrenInfo = (step: CamelElement): [boolean, number, boolean, number, number] => {
+ function getChildrenInfo (step: CamelElement): [boolean, number, boolean, number, number] {
const children = CamelDefinitionApiExt.getElementChildrenDefinition(step.dslName);
const hasStepsField = children.filter((c: ChildElement) => c.name === 'steps').length === 1;
const stepsChildrenCount = children
@@ -185,49 +189,50 @@ export class DslElement extends React.Component<Props, State> {
return [hasStepsField, stepsChildrenCount, hasNonStepsFields, nonStepChildrenCount, childrenCount]
}
- hasWideChildrenElement = () => {
- const [hasStepsField, stepsChildrenCount, hasNonStepsFields, nonStepChildrenCount, childrenCount] = this.getChildrenInfo(this.props.step);
- if (this.isHorizontal() && stepsChildrenCount > 1) return true;
+ function hasWideChildrenElement () {
+ const [hasStepsField, stepsChildrenCount, hasNonStepsFields, nonStepChildrenCount, childrenCount] = getChildrenInfo(props.step);
+ if (isHorizontal() && stepsChildrenCount > 1) return true;
else if (hasStepsField && stepsChildrenCount > 0 && hasNonStepsFields && nonStepChildrenCount > 0) return true;
else if (!hasStepsField && hasNonStepsFields && childrenCount > 1) return true;
else if (hasStepsField && stepsChildrenCount > 0 && hasNonStepsFields && childrenCount > 1) return true;
else return false;
}
- hasBorderOverSteps = (step: CamelElement) => {
- const [hasStepsField, stepsChildrenCount, hasNonStepsFields, nonStepChildrenCount] = this.getChildrenInfo(step);
+ function hasBorderOverSteps (step: CamelElement) {
+ const [hasStepsField, stepsChildrenCount, hasNonStepsFields, nonStepChildrenCount] = getChildrenInfo(step);
if (hasStepsField && stepsChildrenCount > 0 && hasNonStepsFields && nonStepChildrenCount > 0) return true;
else return false;
}
- getHeaderStyle = () => {
+ function getHeaderStyle () {
const style: CSSProperties = {
- width: this.isWide() ? "100%" : "",
- fontWeight: this.isSelected() ? "bold" : "normal",
+ width: isWide() ? "100%" : "",
+ fontWeight: isSelected() ? "bold" : "normal",
};
return style;
}
- sendPosition = (el: HTMLDivElement | null, isSelected: boolean) => {
- const node = ReactDOM.findDOMNode(this);
+ function sendPosition (el: HTMLDivElement | null, isSelected: boolean) {
+ // console.log("sendPosition", props.step)
+ const node = el;
if (node && el) {
const header = Array.from(node.childNodes.values()).filter((n: any) => n.classList.contains("header"))[0];
if (header) {
const headerIcon: any = Array.from(header.childNodes.values()).filter((n: any) => n.classList.contains("header-icon"))[0];
const headerRect = headerIcon.getBoundingClientRect();
const rect = el.getBoundingClientRect();
- if (this.props.step.show){
- EventBus.sendPosition("add", this.props.step, this.props.parent, rect, headerRect, this.props.position, this.props.inSteps, isSelected);
+ if (props.step.show){
+ EventBus.sendPosition("add", props.step, props.parent, rect, headerRect, props.position, props.inSteps, isSelected);
} else {
- EventBus.sendPosition("delete", this.props.step, this.props.parent, new DOMRect(), new DOMRect(), 0);
+ EventBus.sendPosition("delete", props.step, props.parent, new DOMRect(), new DOMRect(), 0);
}
}
}
}
- getHeader = () => {
- const step: CamelElement = this.props.step;
- const parent = this.props.parent;
+ function getHeader () {
+ const step: CamelElement = props.step;
+ const parent = props.parent;
const inRouteConfiguration = parent !== undefined && parent.dslName === 'RouteConfigurationDefinition';
const availableModels = CamelUi.getSelectorModelsForParent(step.dslName, false);
const showAddButton = !['CatchDefinition', 'RouteDefinition'].includes(step.dslName) && availableModels.length > 0;
@@ -235,31 +240,31 @@ export class DslElement extends React.Component<Props, State> {
!['FromDefinition', 'RouteConfigurationDefinition', 'RouteDefinition', 'CatchDefinition', 'FinallyDefinition', 'WhenDefinition', 'OtherwiseDefinition'].includes(step.dslName)
&& !inRouteConfiguration;
const headerClass = ['RouteConfigurationDefinition', 'RouteDefinition'].includes(step.dslName) ? "header-route" : "header"
- const headerClasses = this.isSelected() ? headerClass + " selected" : headerClass;
+ const headerClasses = isSelected() ? headerClass + " selected" : headerClass;
return (
- <div className={headerClasses} style={this.getHeaderStyle()}>
- {!['RouteConfigurationDefinition', 'RouteDefinition'].includes(this.props.step.dslName) &&
- <div ref={el => this.sendPosition(el, this.isSelected())}
+ <div className={headerClasses} style={getHeaderStyle()}>
+ {!['RouteConfigurationDefinition', 'RouteDefinition'].includes(props.step.dslName) &&
+ <div ref={el => sendPosition(el, isSelected())}
className={"header-icon"}
- style={this.isWide() ? {width: ""} : {}}>
+ style={isWide() ? {width: ""} : {}}>
{CamelUi.getIconForElement(step)}
</div>
}
- <div className={this.hasWideChildrenElement() ? "header-text" : ""}>
- {this.hasWideChildrenElement() && <div className="spacer"/>}
- {this.getHeaderTextWithTooltip(step)}
+ <div className={hasWideChildrenElement() ? "header-text" : ""}>
+ {hasWideChildrenElement() && <div className="spacer"/>}
+ {getHeaderTextWithTooltip(step)}
</div>
- {showInsertButton && this.getInsertElementButton()}
- {this.getDeleteButton()}
- {showAddButton && this.getAddElementButton()}
+ {showInsertButton && getInsertElementButton()}
+ {getDeleteButton()}
+ {showAddButton && getAddElementButton()}
</div>
)
}
- getHeaderTextWithTooltip = (step: CamelElement) => {
+ function getHeaderTextWithTooltip (step: CamelElement) {
const checkRequired = CamelUtil.checkRequired(step);
- const title = (step as any).description ? (step as any).description : CamelUi.getElementTitle(this.props.step);
- let className = this.hasWideChildrenElement() ? "text text-right" : "text text-bottom";
+ const title = (step as any).description ? (step as any).description : CamelUi.getElementTitle(props.step);
+ let className = hasWideChildrenElement() ? "text text-right" : "text text-bottom";
if (!checkRequired[0]) className = className + " header-text-required";
if (checkRequired[0]) return <Text className={className}>{title}</Text>
else return (
@@ -270,30 +275,30 @@ export class DslElement extends React.Component<Props, State> {
)
}
- getHeaderWithTooltip = (tooltip: string | undefined) => {
+ function getHeaderWithTooltip (tooltip: string | undefined) {
return (
<Tooltip position={"left"}
content={<div>{tooltip}</div>}>
- {this.getHeader()}
+ {getHeader()}
</Tooltip>
)
}
- getHeaderTooltip = (): string | undefined => {
- if (CamelUi.isShowExpressionTooltip(this.props.step)) return CamelUi.getExpressionTooltip(this.props.step);
- if (CamelUi.isShowUriTooltip(this.props.step)) return CamelUi.getUriTooltip(this.props.step);
+ function getHeaderTooltip (): string | undefined {
+ if (CamelUi.isShowExpressionTooltip(props.step)) return CamelUi.getExpressionTooltip(props.step);
+ if (CamelUi.isShowUriTooltip(props.step)) return CamelUi.getUriTooltip(props.step);
return undefined;
}
- getElementHeader = () => {
- const tooltip = this.getHeaderTooltip();
- if (tooltip !== undefined && !this.state.isDragging) {
- return this.getHeaderWithTooltip(tooltip);
+ function getElementHeader () {
+ const tooltip = getHeaderTooltip();
+ if (tooltip !== undefined && !isDragging) {
+ return getHeaderWithTooltip(tooltip);
}
- return this.getHeader();
+ return getHeader();
}
- getChildrenStyle = () => {
+ function getChildrenStyle () {
const style: CSSProperties = {
display: "flex",
flexDirection: "row",
@@ -301,22 +306,22 @@ export class DslElement extends React.Component<Props, State> {
return style;
}
- getChildrenElementsStyle = (child: ChildElement, notOnlySteps: boolean) => {
- const step = this.props.step;
- const isBorder = child.name === 'steps' && this.hasBorderOverSteps(step);
+ function getChildrenElementsStyle (child: ChildElement, notOnlySteps: boolean) {
+ const step = props.step;
+ const isBorder = child.name === 'steps' && hasBorderOverSteps(step);
const style: CSSProperties = {
borderStyle: isBorder ? "dotted" : "none",
borderColor: "var(--step-border-color)",
borderWidth: "1px",
borderRadius: "16px",
- display: this.isHorizontal() || child.name !== 'steps' ? "flex" : "block",
+ display: isHorizontal() || child.name !== 'steps' ? "flex" : "block",
flexDirection: "row",
}
return style;
}
- getChildElements = () => {
- const step: CamelElement = this.props.step;
+ function getChildElements () {
+ const step: CamelElement = props.step;
let children: ChildElement[] = CamelDefinitionApiExt.getElementChildrenDefinition(step.dslName);
const notOnlySteps = children.filter(c => c.name === 'steps').length === 1
&& children.filter(c => c.multiple && c.name !== 'steps').length > 0;
@@ -331,67 +336,61 @@ export class DslElement extends React.Component<Props, State> {
children = children.filter(value => value.name !== 'onWhen')
}
return (
- <div key={step.uuid + "-children"} className="children" style={this.getChildrenStyle()}>
- {children.map((child: ChildElement, index: number) => this.getChildDslElements(child, index, notOnlySteps))}
+ <div key={step.uuid + "-children"} className="children" style={getChildrenStyle()}>
+ {children.map((child: ChildElement, index: number) => getChildDslElements(child, index, notOnlySteps))}
</div>
)
}
- getChildDslElements = (child: ChildElement, index: number, notOnlySteps: boolean) => {
- const step = this.props.step;
+ function getChildDslElements (child: ChildElement, index: number, notOnlySteps: boolean) {
+ const step = props.step;
const children: CamelElement[] = CamelDefinitionApiExt.getElementChildren(step, child);
if (children.length > 0) {
return (
- <div className={child.name + " has-child"} style={this.getChildrenElementsStyle(child, notOnlySteps)} key={step.uuid + "-child-" + index}>
+ <div className={child.name + " has-child"} style={getChildrenElementsStyle(child, notOnlySteps)} key={step.uuid + "-child-" + index}>
{children.map((element, index) => (
<div key={step.uuid + child.className + index}>
<DslElement
- integration={this.props.integration}
- openSelector={this.props.openSelector}
- deleteElement={this.props.deleteElement}
- selectElement={this.props.selectElement}
- moveElement={this.props.moveElement}
- selectedUuid={this.props.selectedUuid}
inSteps={child.name === 'steps'}
position={index}
step={element}
parent={step}/>
</div>
))}
- {child.name === 'steps' && this.getAddStepButton()}
+ {child.name === 'steps' && getAddStepButton()}
</div>
)
} else if (child.name === 'steps') {
return (
- <div className={child.name + " has-child"} style={this.getChildrenElementsStyle(child, notOnlySteps)} key={step.uuid + "-child-" + index}>
- {this.getAddStepButton()}
+ <div className={child.name + " has-child"} style={getChildrenElementsStyle(child, notOnlySteps)} key={step.uuid + "-child-" + index}>
+ {getAddStepButton()}
</div>
)
}
}
- getAddStepButton() {
- const {integration, step, selectedUuid} = this.props;
- const hideAddButton = step.dslName === 'StepDefinition' && !CamelDisplayUtil.isStepDefinitionExpanded(integration, step.uuid, selectedUuid.at(0));
+ function getAddStepButton() {
+ const { step} = props;
+ const hideAddButton = step.dslName === 'StepDefinition' && !CamelDisplayUtil.isStepDefinitionExpanded(integration, step.uuid, selectedUuids.at(0));
if (hideAddButton) return (<></>)
else return (
<Tooltip position={"bottom"}
content={<div>{"Add step to " + CamelUi.getTitle(step)}</div>}>
- <button type="button" aria-label="Add" onClick={e => this.openSelector(e)}
- className={this.isAddStepButtonLeft() ? "add-button add-button-left" : "add-button add-button-bottom"}>
+ <button type="button" aria-label="Add" onClick={e => onOpenSelector(e)}
+ className={isAddStepButtonLeft() ? "add-button add-button-left" : "add-button add-button-bottom"}>
<AddIcon />
</button>
</Tooltip>
)
}
- getAddElementButton() {
+ function getAddElementButton() {
return (
- <Tooltip position={"bottom"} content={<div>{"Add DSL element to " + CamelUi.getTitle(this.props.step)}</div>}>
+ <Tooltip position={"bottom"} content={<div>{"Add DSL element to " + CamelUi.getTitle(props.step)}</div>}>
<button
type="button"
aria-label="Add"
- onClick={e => this.openSelector(e, false)}
+ onClick={e => onOpenSelector(e, false)}
className={"add-element-button"}>
<AddIcon />
</button>
@@ -399,95 +398,92 @@ export class DslElement extends React.Component<Props, State> {
)
}
- getInsertElementButton() {
+ function getInsertElementButton() {
return (
<Tooltip position={"left"} content={<div>{"Insert element before"}</div>}>
- <button type="button" aria-label="Insert" onClick={e => this.openSelector(e, true, true)} className={"insert-element-button"}><InsertIcon />
+ <button type="button" aria-label="Insert" onClick={e => onOpenSelector(e, true, true)} className={"insert-element-button"}><InsertIcon />
</button>
</Tooltip>
)
}
- getDeleteButton() {
+ function getDeleteButton() {
return (
<Tooltip position={"right"} content={<div>{"Delete element"}</div>}>
- <button type="button" aria-label="Delete" onClick={e => this.delete(e)} className="delete-button"><DeleteIcon /></button>
+ <button type="button" aria-label="Delete" onClick={e => onDeleteElement(e)} className="delete-button"><DeleteIcon /></button>
</Tooltip>
)
}
- getMoveConfirmation() {
+ function getMoveConfirmation() {
return (
<Modal
aria-label="title"
className='move-modal'
- isOpen={this.state.showMoveConfirmation}
+ isOpen={showMoveConfirmation}
variant={ModalVariant.small}
><Flex direction={{default: "column"}}>
<div>Select move type:</div>
- <Button key="place" variant="primary" onClick={event => this.confirmMove(false)}>Shift (target down)</Button>
- <Button key="child" variant="secondary" onClick={event => this.confirmMove(true)}>Move as target step</Button>
- <Button key="cancel" variant="tertiary" onClick={event => this.cancelMove()}>Cancel</Button>
+ <Button key="place" variant="primary" onClick={event => confirmMove(false)}>Shift (target down)</Button>
+ <Button key="child" variant="secondary" onClick={event => confirmMove(true)}>Move as target step</Button>
+ <Button key="cancel" variant="tertiary" onClick={event => cancelMove()}>Cancel</Button>
</Flex>
</Modal>
)
}
- render() {
- const element: CamelElement = this.props.step;
- const className = "step-element" + (this.isSelected() ? " step-element-selected" : "")
- + (!this.props.step.show ? " hidden-step" : "");
- return (
- <div key={"root" + element.uuid}
- className={className}
- ref={el => this.sendPosition(el, this.isSelected())}
- style={{
- borderStyle: this.hasBorder() ? "dotted" : "none",
- borderColor: this.isSelected() ? "var(--step-border-color-selected)" : "var(--step-border-color)",
- marginTop: this.isInStepWithChildren() ? "16px" : "8px",
- zIndex: element.dslName === 'ToDefinition' ? 20 : 10,
- boxShadow: this.state.isDraggedOver ? "0px 0px 1px 2px var(--step-border-color-selected)" : "none",
- }}
- onMouseOver={event => event.stopPropagation()}
- onClick={event => this.selectElement(event)}
- onDragStart={event => {
- event.stopPropagation();
- event.dataTransfer.setData("text/plain", element.uuid);
- (event.target as any).style.opacity = .5;
- this.setState({isDragging: true});
- }}
- onDragEnd={event => {
- (event.target as any).style.opacity = '';
- this.setState({isDragging: false});
- }}
- onDragOver={event => {
- event.preventDefault();
- event.stopPropagation();
- if (element.dslName !== 'FromDefinition' && !this.state.isDragging) {
- this.setState({isDraggedOver: true});
- }
- }}
- onDragEnter={event => {
- event.preventDefault();
- event.stopPropagation();
- if (element.dslName !== 'FromDefinition') {
- this.setState({isDraggedOver: true});
- }
- }}
- onDragLeave={event => {
- event.preventDefault();
- event.stopPropagation();
- this.setState({isDraggedOver: false});
-
- }}
- onDrop={event => this.dragElement(event, element)}
- draggable={!this.isNotDraggable()}
- >
- {this.getElementHeader()}
- {this.getChildElements()}
- {this.getMoveConfirmation()}
- </div>
- )
- }
+ const element: CamelElement = props.step;
+ const className = "step-element" + (isSelected() ? " step-element-selected" : "")
+ + (!props.step.show ? " hidden-step" : "");
+ return (
+ <div key={"root" + element.uuid}
+ className={className}
+ ref={el => sendPosition(el, isSelected())}
+ style={{
+ borderStyle: hasBorder() ? "dotted" : "none",
+ borderColor: isSelected() ? "var(--step-border-color-selected)" : "var(--step-border-color)",
+ marginTop: isInStepWithChildren() ? "16px" : "8px",
+ zIndex: element.dslName === 'ToDefinition' ? 20 : 10,
+ boxShadow: isDraggedOver ? "0px 0px 1px 2px var(--step-border-color-selected)" : "none",
+ }}
+ onMouseOver={event => event.stopPropagation()}
+ onClick={event => onSelectElement(event)}
+ onDragStart={event => {
+ event.stopPropagation();
+ event.dataTransfer.setData("text/plain", element.uuid);
+ (event.target as any).style.opacity = .5;
+ setIsDragging(true);
+ }}
+ onDragEnd={event => {
+ (event.target as any).style.opacity = '';
+ setIsDragging(false);
+ }}
+ onDragOver={event => {
+ event.preventDefault();
+ event.stopPropagation();
+ if (element.dslName !== 'FromDefinition' && !isDragging) {
+ setIsDraggedOver(true);
+ }
+ }}
+ onDragEnter={event => {
+ event.preventDefault();
+ event.stopPropagation();
+ if (element.dslName !== 'FromDefinition') {
+ setIsDraggedOver(true);
+ }
+ }}
+ onDragLeave={event => {
+ event.preventDefault();
+ event.stopPropagation();
+ setIsDraggedOver(false);
+ }}
+ onDrop={event => dragElement(event, element)}
+ draggable={!isNotDraggable()}
+ >
+ {getElementHeader()}
+ {getChildElements()}
+ {getMoveConfirmation()}
+ </div>
+ )
}
diff --git a/karavan-designer/src/designer/route/ElementResizeListener.tsx b/karavan-designer/src/designer/route/ElementResizeListener.tsx
new file mode 100644
index 00000000..4ef53c64
--- /dev/null
+++ b/karavan-designer/src/designer/route/ElementResizeListener.tsx
@@ -0,0 +1,75 @@
+/*
+ * 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 {RefObject, useCallback, useEffect, useRef} from "react";
+
+interface Props {
+ onResize: (event: Event) => void;
+};
+export const ElementResizeListener: React.FC<Props> = ({ onResize }) => {
+ const rafRef = useRef(0);
+ const objectRef: RefObject<HTMLObjectElement> = useRef(null);
+ const onResizeRef = useRef(onResize);
+
+ onResizeRef.current = onResize;
+
+ const _onResize = useCallback((e: Event) => {
+ if (rafRef.current) {
+ cancelAnimationFrame(rafRef.current);
+ }
+ rafRef.current = requestAnimationFrame(() => {
+ onResizeRef.current(e);
+ });
+ }, []);
+
+ const onLoad = useCallback(() => {
+ const obj = objectRef.current;
+ if (obj && obj.contentDocument && obj.contentDocument.defaultView) {
+ obj.contentDocument.defaultView.addEventListener('resize', _onResize);
+ }
+ }, []);
+
+ useEffect(() => {
+ return () => {
+ const obj = objectRef.current;
+ if (obj && obj.contentDocument && obj.contentDocument.defaultView) {
+ obj.contentDocument.defaultView.removeEventListener('resize', _onResize);
+ }
+ }
+ }, []);
+
+ return (
+ <object
+ aria-label="object"
+ onLoad={onLoad}
+ ref={objectRef} tabIndex={-1}
+ type={'text/html'}
+ data={'about:blank'}
+ title={''}
+ style={{
+ position: 'absolute',
+ top: 0,
+ left: 0,
+ height: '100%',
+ width: '100%',
+ pointerEvents: 'none',
+ zIndex: -1,
+ opacity: 0,
+ }}
+ />
+ )
+}
diff --git a/karavan-designer/src/designer/route/RouteDesigner.tsx b/karavan-designer/src/designer/route/RouteDesigner.tsx
index f7607b90..101309bb 100644
--- a/karavan-designer/src/designer/route/RouteDesigner.tsx
+++ b/karavan-designer/src/designer/route/RouteDesigner.tsx
@@ -14,7 +14,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-import React from 'react';
+import React, {useCallback, useEffect, useRef, useState} from 'react';
import {
Drawer,
DrawerPanelContent,
@@ -32,164 +32,156 @@ import PlusIcon from "@patternfly/react-icons/dist/esm/icons/plus-icon";
import {DslElement} from "./DslElement";
import {CamelUi} from "../utils/CamelUi";
import {CamelDisplayUtil} from "karavan-core/lib/api/CamelDisplayUtil";
-import {RouteDesignerLogic} from "./RouteDesignerLogic";
+import {useRouteDesignerHook} from "./useRouteDesignerHook";
+import {useDesignerStore, useIntegrationStore} from "../KaravanStore";
+import {shallow} from "zustand/shallow";
+import {CamelUtil} from "karavan-core/lib/api/CamelUtil";
+import {RouteConfigurationDefinition} from "karavan-core/lib/model/CamelDefinition";
+import {CamelDefinitionApiExt} from "karavan-core/lib/api/CamelDefinitionApiExt";
+import {ElementResizeListener} from "./ElementResizeListener";
interface Props {
- onSave?: (integration: Integration, propertyOnly: boolean) => void
- integration: Integration
+ // onSave?: (integration: Integration, propertyOnly: boolean) => void
+ // integration: Integration
dark: boolean
}
-export interface RouteDesignerState {
- logic: RouteDesignerLogic
- 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 const RouteDesigner = (props: Props) => {
-export class RouteDesigner extends React.Component<Props, RouteDesignerState> {
+ const ref = React.createRef();
+ const printerRef = React.createRef()
+ const contentRef: React.RefObject<HTMLDivElement> = useRef(null);
+ const flowRef: React.RefObject<HTMLDivElement> = useRef(null);
- public state: RouteDesignerState = {
- logic: new RouteDesignerLogic(this),
- 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,
- };
+ const { deleteElement, selectElement, moveElement, onShowDeleteConfirmation, onDslSelect, openSelector, createRouteConfiguration} = useRouteDesignerHook();
- componentDidMount() {
- this.state.logic.componentDidMount();
- }
+ const [integration, setIntegration] = useIntegrationStore((state) => [state.integration, state.setIntegration], shallow)
+ const [showSelector, showDeleteConfirmation, propertyOnly, showSteps,deleteMessage,parentId, selectedUuids,clipboardSteps, parentDsl,selectedPosition,selectedStep,selectorTabIndex,
+ setShowSelector, setShowDeleteConfirmation,setPropertyOnly,setShowSteps,setDeleteMessage, setParentId, setSelectedUuids, setClipboardSteps,setPosition,
+ width, height, top, left] = useDesignerStore((s) =>
+ [s.showSelector, s.showDeleteConfirmation, s.propertyOnly, s.showSteps,s.deleteMessage,s.parentId, s.selectedUuids,s.clipboardSteps, s.parentDsl, s.selectedPosition, s.selectedStep,s.selectorTabIndex,
+ s.setShowSelector, s.setShowDeleteConfirmation,s.setPropertyOnly,s.setShowSteps,s.setDeleteMessage, s.setParentId, s.setSelectedUuids, s.setClipboardSteps, s.setPosition,
+ s.width, s.height, s.top, s.left], shallow)
- componentWillUnmount() {
- this.state.logic.componentWillUnmount();
- }
+ useEffect(() => {
+ adaptResize();
+ })
- handleResize = (event: any) => {
- return this.state.logic.handleResize(event);
- }
+ // function componentDidMount() {
+ // logic.componentDidMount();
+ // }
+ //
+ // function componentWillUnmount() {
+ // logic.componentWillUnmount();
+ // }
+ //
+ // function handleResize = (event: any) => {
+ // return logic.handleResize(event);
+ // }
+ //
+ // function handleKeyDown = (event: KeyboardEvent) => {
+ // return logic.handleKeyDown(event);
+ // }
+ //
+ // function handleKeyUp = (event: KeyboardEvent) => {
+ // return logic.handleKeyUp(event);
+ // }
+ //
+ // function componentDidUpdate = (prevProps: Readonly<Props>, prevState: Readonly<RouteDesignerState>, snapshot?: any) => {
+ // return logic.componentDidUpdate(prevState, snapshot);
+ // }
- handleKeyDown = (event: KeyboardEvent) => {
- return this.state.logic.handleKeyDown(event);
- }
-
- handleKeyUp = (event: KeyboardEvent) => {
- return this.state.logic.handleKeyUp(event);
- }
-
- componentDidUpdate = (prevProps: Readonly<Props>, prevState: Readonly<RouteDesignerState>, snapshot?: any) => {
- return this.state.logic.componentDidUpdate(prevState, snapshot);
- }
-
- getSelectorModal() {
+ function getSelectorModal() {
return (
<DslSelector
- isOpen={this.state.showSelector}
- 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.state.logic.onDslSelect}/>)
+ isOpen={showSelector}
+ onClose={() => setShowSelector(false)}
+ dark={props.dark}
+ parentId={parentId}
+ parentDsl={parentDsl}
+ showSteps={showSteps}
+ position={selectedPosition}
+ tabIndex={selectorTabIndex}
+ onDslSelect={(dsl, parentId1) => onDslSelect(dsl, parentId1)}/>)
}
- getDeleteConfirmation() {
- let htmlContent: string = this.state.deleteMessage;
+ function getDeleteConfirmation() {
+ let htmlContent: string = deleteMessage;
return (<Modal
className="modal-delete"
title="Confirmation"
- isOpen={this.state.showDeleteConfirmation}
- onClose={() => this.setState({showDeleteConfirmation: false})}
+ isOpen={showDeleteConfirmation}
+ onClose={() => setShowDeleteConfirmation(false)}
actions={[
- <Button key="confirm" variant="primary" onClick={e => this.state.logic.deleteElement()}>Delete</Button>,
+ <Button key="confirm" variant="primary" onClick={e => deleteElement()}>Delete</Button>,
<Button key="cancel" variant="link"
- onClick={e => this.setState({showDeleteConfirmation: false})}>Cancel</Button>
+ onClick={e => setShowDeleteConfirmation(false)}>Cancel</Button>
]}
- onEscapePress={e => this.setState({showDeleteConfirmation: false})}>
+ onEscapePress={e => setShowDeleteConfirmation(false)}>
<div>
{htmlContent}
</div>
</Modal>)
}
- getPropertiesPanel() {
+ function getPropertiesPanel() {
return (
- <DrawerPanelContent onResize={(_event, width) => this.setState({key: Math.random().toString()})}
+ <DrawerPanelContent onResize={(_event, width) => {
+ // setState({key: Math.random().toString()})
+ }}
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.state.logic.onIntegrationUpdate}
- onPropertyUpdate={this.state.logic.onPropertyUpdate}
- isRouteDesigner={true}
- dark={this.props.dark}/>
+ {/*<DslProperties ref={ref}*/}
+ {/* integration={integration}*/}
+ {/* step={selectedStep}*/}
+ {/* onIntegrationUpdate={logic.onIntegrationUpdate}*/}
+ {/* onPropertyUpdate={logic.onPropertyUpdate}*/}
+ {/* isRouteDesigner={true}*/}
+ {/* dark={props.dark}/>*/}
</DrawerPanelContent>
)
}
- getGraph() {
- const {selectedUuids, integration, key, width, height, top, left} = this.state;
+ // function onResizePage(el: HTMLDivElement | null) {
+ // console.log("onResizePage", el)
+ // const rect = el?.getBoundingClientRect();
+ // if (el && rect && (el.scrollWidth !== width || el.scrollHeight !== height || rect.top !== top || rect.left !== left)) {
+ // setPosition(el.scrollWidth, el.scrollHeight, rect.top, rect.left)
+ // }
+ // }
+
+ const adaptResize = useCallback(() => {
+ if (contentRef.current && flowRef.current) {
+ const el = contentRef.current.getBoundingClientRect();
+ // flowRef.current.textContent = `width: ${elmRect.width}`;
+ console.log("elmRect", el)
+ setPosition(el.width, el.height, el.top, el.left)
+ }
+ }, []);
+
+
+ function getGraph() {
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.state.logic.unselectElement(event)}
- ref={el => this.state.logic.onResizePage(el)}>
+ <div className="graph" ref={contentRef}>
+ <DslConnections/>
+ <ElementResizeListener onResize={adaptResize}/>
+ <div id="flows" className="flows" data-click="FLOWS" onClick={event => {
+ // logic.unselectElement(event)
+ }}
+ // ref={el => onResizePage(el)}
+ ref={flowRef}
+ >
{routeConfigurations?.map((routeConfiguration, index: number) => (
- <DslElement key={routeConfiguration.uuid + key}
- integration={integration}
- openSelector={this.state.logic.openSelector}
- deleteElement={this.state.logic.showDeleteConfirmation}
- selectElement={this.state.logic.selectElement}
- moveElement={this.state.logic.moveElement}
- selectedUuid={selectedUuids}
+ <DslElement key={routeConfiguration.uuid}
inSteps={false}
position={index}
step={routeConfiguration}
parent={undefined}/>
))}
{routes?.map((route: any, index: number) => (
- <DslElement key={route.uuid + key}
- integration={integration}
- openSelector={this.state.logic.openSelector}
- deleteElement={this.state.logic.showDeleteConfirmation}
- selectElement={this.state.logic.selectElement}
- moveElement={this.state.logic.moveElement}
- selectedUuid={selectedUuids}
+ <DslElement key={route.uuid}
inSteps={false}
position={index}
step={route}
@@ -199,31 +191,35 @@ export class RouteDesigner extends React.Component<Props, RouteDesignerState> {
<Button
variant={routes.length === 0 ? "primary" : "secondary"}
icon={<PlusIcon/>}
- onClick={e => this.state.logic.openSelector(undefined, undefined)}>Create route
+ onClick={e => {
+ openSelector(undefined, undefined)
+ }}
+ >
+ Create route
</Button>
<Button
variant="secondary"
icon={<PlusIcon/>}
- onClick={e => this.state.logic.createRouteConfiguration()}>Create configuration
+ onClick={e => 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.state.showSelector === true && this.getSelectorModal()}
- {this.getDeleteConfirmation()}
- </PageSection>
- );
- }
+ return (
+ <PageSection className="dsl-page" isFilled padding={{default: 'noPadding'}}>
+ <div className="dsl-page-columns">
+ <Drawer isExpanded isInline>
+ <DrawerContent panelContent={getPropertiesPanel()}>
+ <DrawerContentBody>{getGraph()}</DrawerContentBody>
+ </DrawerContent>
+ </Drawer>
+ </div>
+ {getSelectorModal()}
+ {getDeleteConfirmation()}
+ </PageSection>
+ );
}
\ No newline at end of file
diff --git a/karavan-designer/src/designer/route/RouteDesignerLogic.tsx b/karavan-designer/src/designer/route/RouteDesignerLogic.tsx
deleted file mode 100644
index b352a056..00000000
--- a/karavan-designer/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-designer/src/designer/route/property/DslPropertyField.tsx b/karavan-designer/src/designer/route/property/DslPropertyField.tsx
index 3021baf2..0dca7151 100644
--- a/karavan-designer/src/designer/route/property/DslPropertyField.tsx
+++ b/karavan-designer/src/designer/route/property/DslPropertyField.tsx
@@ -264,7 +264,7 @@ export class DslPropertyField extends React.Component<Props, State> {
} else {
this.setState({customCode: value, showEditor: true})
}
- }).catch(reason => console.log(reason))
+ }).catch((reason: any) => console.log(reason))
}
getJavaTypeGeneratedInput = (property: PropertyMeta, value: any) => {
diff --git a/karavan-designer/src/designer/route/useRouteDesignerHook.tsx b/karavan-designer/src/designer/route/useRouteDesignerHook.tsx
new file mode 100644
index 00000000..fd97b731
--- /dev/null
+++ b/karavan-designer/src/designer/route/useRouteDesignerHook.tsx
@@ -0,0 +1,384 @@
+/*
+ * 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} from "./RouteDesigner";
+import {findDOMNode} from "react-dom";
+import {Subscription} from "rxjs";
+import debounce from 'lodash.debounce';
+import {useDesignerStore, useIntegrationStore} from "../KaravanStore";
+import {shallow} from "zustand/shallow";
+import {setTabIndex} from "@patternfly/react-core";
+
+export const useRouteDesignerHook = () => {
+
+ const [integration, setIntegration] = useIntegrationStore((state) => [state.integration, state.setIntegration], shallow)
+ const [showSelector, showDeleteConfirmation, propertyOnly, showSteps,deleteMessage,parentId, selectedUuids,clipboardSteps, parentDsl,selectedPosition,selectedStep,selectorTabIndex,shiftKeyPressed,
+ setShowSelector, setShowDeleteConfirmation,setPropertyOnly,setShowSteps, setDeleteMessage, setSelectedStep, setParentId, setParentDsl, setSelectedPosition, setSelectedUuids, setClipboardSteps,setPosition,setSelectorTabIndex,
+ width, height, top, left] = useDesignerStore((s) =>
+ [s.showSelector, s.showDeleteConfirmation, s.propertyOnly, s.showSteps,s.deleteMessage,s.parentId, s.selectedUuids,s.clipboardSteps, s.parentDsl, s.selectedPosition, s.selectedStep,s.selectorTabIndex, s.shiftKeyPressed,
+ s.setShowSelector, s.setShowDeleteConfirmation,s.setPropertyOnly,s.setShowSteps,s.setDeleteMessage, s.setSelectedStep, s.setParentId, s.setParentDsl, s.setSelectedPosition, s.setSelectedUuids, s.setClipboardSteps, s.setPosition,s.setSelectorTabIndex,
+ s.width, s.height, s.top, s.left], shallow)
+
+ // const commandSub = EventBus.onCommand()?.subscribe((command: Command) => onCommand(command));
+ //
+ //
+ // function 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));
+ // }
+ //
+ // function componentWillUnmount() {
+ // window.removeEventListener('resize', this.routeDesigner.handleResize);
+ // window.removeEventListener('keydown', this.routeDesigner.handleKeyDown);
+ // window.removeEventListener('keyup', this.routeDesigner.handleKeyUp);
+ // this.commandSub?.unsubscribe();
+ // }
+ //
+ // function handleResize = (event: any) => {
+ // this.routeDesigner.setState({key: Math.random().toString()});
+ // }
+ //
+ // function 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);
+ // }
+ // }
+ // }
+ //
+ // function handleKeyUp = (event: KeyboardEvent) => {
+ // this.routeDesigner.setState({shiftKeyPressed: false});
+ // if (event.repeat) {
+ // window.dispatchEvent(event);
+ // }
+ // }
+ //
+ // function 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]
+ // }));
+ // }
+ // }
+ // function 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);
+ // }
+ // })
+ // }
+ // }
+ //
+ // function onCommand = (command: Command) => {
+ // switch (command.command){
+ // case "downloadImage": this.integrationImageDownload()
+ // }
+ // }
+ //
+ // function 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})
+ //
+ const onShowDeleteConfirmation = (id: string) => {
+ let message: string;
+ const uuidsToDelete:string [] = [id];
+ let ce: CamelElement;
+ ce = CamelDefinitionApiExt.findElementInIntegration(integration, id)!;
+ if (ce.dslName === 'FromDefinition') { // Get the RouteDefinition for this.routeDesigner. Use its uuid.
+ let flows = 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?';
+ }
+ setShowSelector(false);
+ setShowDeleteConfirmation(false);
+ setDeleteMessage(message);
+ setSelectedUuids(uuidsToDelete);
+ }
+
+ const deleteElement = () => {
+ selectedUuids.forEach(uuidToDelete => {
+ const i = CamelDefinitionApiExt.deleteStepFromIntegration(integration, uuidToDelete);
+ setIntegration(i);
+ setShowSelector(false);
+ setShowDeleteConfirmation(false);
+ setDeleteMessage('');
+ setSelectedStep(undefined);
+ setPropertyOnly(false);
+ setSelectedUuids([uuidToDelete]);
+
+ const el = new CamelElement("");
+ el.uuid = uuidToDelete;
+ EventBus.sendPosition("delete", el, undefined, new DOMRect(), new DOMRect(), 0);
+ });
+ }
+
+ const 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(integration, element.uuid);
+
+ if (remove) {
+ const index = selectedUuids.indexOf(element.uuid);
+ selectedUuids.splice(index, 1);
+ } else if (add && !canNotAdd) {
+ selectedUuids.push(element.uuid);
+ }
+ const uuid: string = selectedUuids.includes(element.uuid) ? element.uuid : selectedUuids.at(0) || '';
+ const selectedElement = shiftKeyPressed ? CamelDefinitionApiExt.findElementInIntegration(integration, uuid) : element;
+
+ setIntegration(i);
+ setSelectedStep(selectedElement);
+ setShowSelector(false);
+ setSelectedUuids(shiftKeyPressed ? [...selectedUuids] : [element.uuid])
+ }
+ //
+ // function 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: [],
+ // }));
+ // }
+ // }
+ //
+ const openSelector = (parentId: string | undefined, parentDsl: string | undefined, showSteps: boolean = true, position?: number | undefined, selectorTabIndex?: string | number) => {
+ setShowSelector(true);
+ setParentId(parentId || '');
+ setParentDsl(parentDsl);
+ setShowSteps(showSteps);
+ setSelectedPosition(position);
+ setSelectorTabIndex(selectorTabIndex);
+ }
+ //
+ // function closeDslSelector = () => {
+ // this.routeDesigner.setState({showSelector: false})
+ // }
+ //
+ const onDslSelect = (dsl: DslMetaModel, parentId: string, position?: number | undefined) => {
+ switch (dsl.dsl) {
+ case 'FromDefinition' :
+ const route = CamelDefinitionApi.createRouteDefinition({from: new FromDefinition({uri: dsl.uri})});
+ addStep(route, parentId, position)
+ break;
+ case 'ToDefinition' :
+ const to = CamelDefinitionApi.createStep(dsl.dsl, {uri: dsl.uri});
+ addStep(to, parentId, position)
+ break;
+ case 'ToDynamicDefinition' :
+ const toD = CamelDefinitionApi.createStep(dsl.dsl, {uri: dsl.uri});
+ addStep(toD, parentId, position)
+ break;
+ case 'KameletDefinition' :
+ const kamelet = CamelDefinitionApi.createStep(dsl.dsl, {name: dsl.name});
+ addStep(kamelet, parentId, position)
+ break;
+ default:
+ const step = CamelDefinitionApi.createStep(dsl.dsl, undefined);
+ const augmentedStep = setDslDefaults(step);
+ addStep(augmentedStep, parentId, position)
+ break;
+ }
+ }
+ //
+ function 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;
+ }
+
+ const createRouteConfiguration = () => {
+ const clone = CamelUtil.cloneIntegration(integration);
+ const routeConfiguration = new RouteConfigurationDefinition();
+ const i = CamelDefinitionApiExt.addRouteConfigurationToIntegration(clone, routeConfiguration);
+ setIntegration(i);
+ setPropertyOnly(false);
+ setSelectedStep(routeConfiguration);
+ setSelectedUuids([routeConfiguration.uuid]);
+ }
+
+ const addStep = (step: CamelElement, parentId: string, position?: number | undefined) => {
+ const i = CamelDefinitionApiExt.addStepToIntegration(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;
+ setIntegration(clone);
+ setShowSelector(false);
+ setSelectedStep(selectedStep);
+ setPropertyOnly(false);
+ setSelectedUuids([selectedStep.uuid]);
+ }
+ //
+ // function onIntegrationUpdate = (i: Integration) => {
+ // this.routeDesigner.setState({integration: i, propertyOnly: false, showSelector: false, key: Math.random().toString()});
+ // }
+ //
+ const moveElement = (source: string, target: string, asChild: boolean) => {
+ const i = CamelDefinitionApiExt.moveRouteElement(integration, source, target, asChild);
+ const clone = CamelUtil.cloneIntegration(i);
+ const selectedStep = CamelDefinitionApiExt.findElementInIntegration(clone, source);
+
+ setIntegration(clone);
+ setShowSelector(false);
+ setSelectedStep(selectedStep);
+ setPropertyOnly(false);
+ setSelectedUuids([source]);
+ }
+ //
+
+ //
+ // function downloadIntegrationImage(dataUrl: string) {
+ // const a = document.createElement('a');
+ // a.setAttribute('download', 'karavan-routes.png');
+ // a.setAttribute('href', dataUrl);
+ // a.click();
+ // }
+ //
+ // function integrationImageDownloadFilter = (node: HTMLElement) => {
+ // const exclusionClasses = ['add-flow'];
+ // return !exclusionClasses.some(classname => {
+ // return node.classList === undefined ? false : node.classList.contains(classname);
+ // });
+ // }
+ //
+ // function 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);
+ // })
+ // }
+
+ return { deleteElement, selectElement, moveElement, onShowDeleteConfirmation, onDslSelect, openSelector, createRouteConfiguration}
+}
\ No newline at end of file
diff --git a/karavan-designer/src/index.tsx b/karavan-designer/src/index.tsx
index 53d84ae5..3219d305 100644
--- a/karavan-designer/src/index.tsx
+++ b/karavan-designer/src/index.tsx
@@ -14,7 +14,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-import React from 'react';
+import React, {StrictMode} from 'react';
import "./index.css";
import "@patternfly/patternfly/patternfly.css";
import App from "./App";
@@ -22,4 +22,8 @@ import {createRoot} from "react-dom/client";
const container = document.getElementById('root');
const root = createRoot(container!);
-root.render(<App/>);
\ No newline at end of file
+root.render(
+ <StrictMode>
+ <App />
+ </StrictMode>
+);
\ No newline at end of file