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/12/06 00:51:46 UTC
(camel-karavan) 02/04: First example of new Designer #1012
This is an automated email from the ASF dual-hosted git repository.
marat pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/camel-karavan.git
commit b4fbb2925156e811bcff97b1324ec8893d00dfef
Author: Marat Gubaidullin <ma...@talismancloud.io>
AuthorDate: Tue Dec 5 19:07:19 2023 -0500
First example of new Designer #1012
---
karavan-designer/public/example/demo.camel.yaml | 92 +++++
karavan-designer/src/designer/DesignerStore.ts | 30 +-
karavan-designer/src/designer/karavan.css | 409 +--------------------
.../src/designer/route/DslConnections.tsx | 280 +++++---------
karavan-designer/src/designer/route/DslElement.css | 18 -
.../src/designer/route/DslProperties.css | 250 +++++++++++++
.../src/designer/route/DslProperties.tsx | 21 +-
.../src/designer/route/RouteDesigner.tsx | 24 +-
.../src/designer/route/element/DslElement.css | 220 +++++++++++
.../designer/route/{ => element}/DslElement.tsx | 169 +++++----
.../src/designer/route/element/DslElementIcons.tsx | 80 ++++
.../route/{ => element}/DslElementMoveModal.tsx | 6 +-
.../src/designer/route/usePropertiesHook.tsx | 11 +-
.../src/designer/route/useRouteDesignerHook.tsx | 3 +-
karavan-designer/src/designer/utils/CamelUi.tsx | 9 +
karavan-designer/src/designer/utils/EventBus.ts | 32 +-
16 files changed, 950 insertions(+), 704 deletions(-)
diff --git a/karavan-designer/public/example/demo.camel.yaml b/karavan-designer/public/example/demo.camel.yaml
index 56bd06fc..40e3c45b 100644
--- a/karavan-designer/public/example/demo.camel.yaml
+++ b/karavan-designer/public/example/demo.camel.yaml
@@ -4,3 +4,95 @@
from:
uri: timer
id: from-cfa5
+ steps:
+ - choice:
+ when:
+ - expression:
+ simple:
+ id: simple-99bf
+ id: when-ab5e
+ steps:
+ - to:
+ uri: arangodb
+ id: to-f70a
+ - removeProperties:
+ id: removeProperties-344a
+ - expression:
+ simple:
+ id: simple-d199
+ id: when-37cd
+ steps:
+ - to:
+ uri: amqp
+ id: to-fbfe
+ otherwise:
+ id: otherwise-382c
+ steps:
+ - log:
+ message: ${body}
+ id: log-6831
+ id: choice-c1db
+ - multicast:
+ id: multicast-6a53
+ steps:
+ - log:
+ message: ${body}
+ id: log-799d
+ - log:
+ message: ${body}
+ id: log-fc8e
+ - log:
+ message: ${body}
+ id: log-1e42
+ - filter:
+ expression:
+ simple:
+ id: simple-7ff9
+ id: filter-8c99
+ steps:
+ - process:
+ id: process-e1c1
+ description: Call cutom java bean
+ - delay:
+ expression:
+ simple:
+ id: simple-64a6
+ id: delay-b1ec
+ - doTry:
+ id: doTry-46cd
+ doCatch:
+ - id: doCatch-c6e7
+ steps:
+ - log:
+ message: ${body}
+ id: log-77df
+ - wireTap:
+ id: wireTap-a25e
+ doFinally:
+ id: doFinally-0a65
+ steps:
+ - log:
+ message: ${body}
+ id: log-f4fa
+ - log:
+ message: ${body}
+ id: log-cd30
+ steps:
+ - pollEnrich:
+ expression:
+ simple:
+ id: simple-6181
+ id: pollEnrich-a41b
+ - filter:
+ expression:
+ simple:
+ id: simple-a69b
+ id: filter-07cf
+ steps:
+ - setBody:
+ expression:
+ simple:
+ id: simple-f0dc
+ id: setBody-3c0c
+ - circuitBreaker:
+ id: circuitBreaker-4af8
diff --git a/karavan-designer/src/designer/DesignerStore.ts b/karavan-designer/src/designer/DesignerStore.ts
index 4831855f..f9bc38d1 100644
--- a/karavan-designer/src/designer/DesignerStore.ts
+++ b/karavan-designer/src/designer/DesignerStore.ts
@@ -16,7 +16,7 @@
*/
import {CamelElement, Integration} from "karavan-core/lib/model/IntegrationDefinition";
-import {DslPosition, EventBus} from "./utils/EventBus";
+import {ButtonPosition, DslPosition, EventBus} from "./utils/EventBus";
import {createWithEqualityFn} from "zustand/traditional";
import {shallow} from "zustand/shallow";
@@ -120,6 +120,10 @@ interface ConnectionsState {
deleteStep: (uuid: string) => void;
clearSteps: () => void;
setSteps: (steps: Map<string, DslPosition>) => void;
+ buttons: ButtonPosition[];
+ addButton: (button: ButtonPosition) => void;
+ deleteButton: (button: ButtonPosition) => void;
+ clearButtons: () => void;
}
export const useConnectionsStore = createWithEqualityFn<ConnectionsState>((set) => ({
@@ -148,6 +152,30 @@ export const useConnectionsStore = createWithEqualityFn<ConnectionsState>((set)
setSteps: (steps: Map<string, DslPosition>) => {
set({steps: steps})
},
+ buttons: [],
+ addButton: (button: ButtonPosition) => {
+ set((state: ConnectionsState) => {
+ const index = state.buttons.findIndex(b => b.uuid === button.uuid);
+ if (index !== -1) {
+ state.buttons.splice(index, 1);
+ }
+ state.buttons.push(button);
+ return state;
+ })
+ },
+ clearButtons: () => {
+ set((state: ConnectionsState) => {
+ state.buttons.length = 0;
+ return state;
+ })
+ },
+ deleteButton: (button: ButtonPosition) => {
+ set((state: ConnectionsState) => {
+ const index = state.buttons.findIndex(b => b.uuid === button.uuid);
+ state.buttons.splice(index, 1);
+ return state;
+ })
+ },
}), shallow)
type DesignerState = {
diff --git a/karavan-designer/src/designer/karavan.css b/karavan-designer/src/designer/karavan.css
index 31aa866b..5e558152 100644
--- a/karavan-designer/src/designer/karavan.css
+++ b/karavan-designer/src/designer/karavan.css
@@ -355,241 +355,6 @@
display: none;
}
-.karavan .properties .headers {
- grid-row-gap: 10px;
- row-gap: 10px;
- display: contents;
-}
-
-.karavan .properties .headers .top {
- width: 100%;
- display: flex;
- flex-direction: row;
-}
-
-.karavan .properties .headers .top h1 {
- width: 100%;
- margin-top: auto;
- margin-bottom: auto;
-}
-
-.karavan .properties .footer {
- height: 100%;
- display: contents;
-}
-
-.karavan .properties .pf-v5-c-form {
- row-gap: 10px;
-}
-
-.karavan .properties .pf-v5-c-form__group-label {
- padding-bottom: 3px;
- display: flex;
- justify-content: space-between;
-}
-
-.karavan .properties .pf-v5-c-form-control:focus-within {
- --pf-v5-c-form-control--after--BorderBottomColor: var(--pf-v5-c-form-control--after--BorderBottomColor);
- --pf-v5-c-form-control--after--BorderBottomWidth: var(--pf-v5-c-form-control--after--BorderBottomWidth);
-}
-
-.karavan .properties .pf-v5-c-select {
- --pf-v5-c-select__toggle--FontSize: 14px;
- --pf-v5-c-select__menu-item--FontSize: 14px;
-}
-
-.karavan .properties .input-group .pf-v5-c-text-input-group__text {
- width: 100%;
-}
-
-.karavan .properties .input-group .pf-v5-c-chip-group,
-.karavan .properties .input-group .pf-v5-c-text-input-group__main,
-.karavan .properties .input-group .pf-v5-c-chip-group .pf-v5-c-chip-group__list,
-.karavan .properties .input-group .pf-v5-c-chip-group .pf-v5-c-chip-group__list .pf-v5-c-chip-group__list-item,
-.karavan .properties .input-group .pf-v5-c-chip-group .pf-v5-c-chip-group__main {
- display: block;
-}
-
-.karavan .properties .input-group .pf-v5-c-chip-group {
- margin-left: 0;
-}
-
-.karavan .properties .input-group .pf-v5-c-chip-group .pf-v5-c-chip .pf-v5-c-chip__text {
- max-width: inherit;
-}
-
-.karavan .properties .input-group .pf-v5-c-chip-group .pf-v5-c-chip {
- width: 100%;
-}
-
-.karavan .properties .input-group .pf-v5-c-text-input-group__utilities {
- align-items: end;
- margin-top: auto;
-}
-
-.karavan .properties .chip .pf-v5-c-button {
- position: absolute;
- right: 0;
-}
-
-.karavan .properties .expression-title {
- font-size: 17px;
- font-weight: bold;
-}
-
-.karavan .properties .text-area {
- font-size: 13px;
-}
-
-.karavan .properties .pf-v5-c-select__menu-search {
- padding: 0px 6px 6px 6px;
-}
-
-.karavan .properties .pf-v5-c-select__toggle-typeahead {
- height: auto;
-}
-
-.karavan .properties .pf-v5-c-select__menu-item {
- /*width: 280px;*/
-}
-
-.karavan .properties .pf-v5-c-select__menu-item-description {
- overflow-wrap: anywhere;
-}
-
-.karavan .properties .number {
- display: flex;
- justify-content: space-between;
-}
-
-.karavan .properties .number .number-property {
- width: 100%;
-}
-
-.karavan .properties .number .clear-button {
- color: #b1b1b7;
- --pf-v5-c-button--BorderRadius: var(--pf-v5-c-button--m-control--BorderRadius);
- --pf-v5-c-button--disabled--BackgroundColor: var(--pf-v5-c-button--m-control--disabled--BackgroundColor);
- --pf-v5-c-button--after--BorderWidth: var(--pf-v5-c-button--m-control--after--BorderWidth);
- --pf-v5-c-button--after--BorderColor: var(--pf-v5-c-button--m-control--after--BorderTopColor) var(--pf-v5-c-button--m-control--after--BorderRightColor) var(--pf-v5-c-button--m-control--after--BorderBottomColor) var(--pf-v5-c-button--m-control--after--BorderLeftColor);
- color: var(--pf-v5-c-button--m-control--Color);
- background-color: var(--pf-v5-c-button--m-control--BackgroundColor);
- padding-left: 5px;
- padding-right: 5px;
-}
-
-.karavan .properties .help-button {
- font-size: 12px;
-}
-
-.karavan .properties .component-selector {
- border-width: var(--pf-v5-global--BorderWidth--sm);
- border-top-color: var(--pf-v5-global--BorderColor--300);
- border-right-color: var(--pf-v5-global--BorderColor--300);
- border-bottom-color: var(--pf-v5-global--BorderColor--200);
- border-left-color: var(--pf-v5-global--BorderColor--300);
- border-style: solid;
-}
-
-.karavan .properties .object-value {
- display: flex;
- flex-direction: row;
- margin-bottom: 3px;
-}
-
-.karavan .properties .object-key-value .object {
- padding-top: 0;
-}
-
-.karavan .properties .object-value .delete-button {
- margin: 0;
- padding: 5px 3px 0px 6px;
- height: 16px;
- color: #909090;
-}
-
-.karavan .properties .object-key-value,
-.karavan .properties .object-key-value .object-field {
- display: flex;
- flex-direction: row;
- gap: 3px;
-}
-
-.karavan .properties .object-key-value .delete-button {
- margin: auto 0 22px 0;
- padding: 0px 0px 0px 3px;
- height: 16px;
- color: #909090;
-}
-
-.karavan .properties .expression,
-.karavan .properties .object,
-.karavan .properties .dataformat,
-.karavan .properties .parameters {
- padding-top: 6px;
- padding-left: 16px;
- row-gap: 6px;
- display: grid;
- width: 100%;
-}
-
-.karavan .properties .expression .pf-v5-c-form__group-label,
-.karavan .properties .object .pf-v5-c-form__group-label,
-.karavan .properties .dataformat .pf-v5-c-form__group-label,
-.karavan .properties .parameters .pf-v5-c-form__group-label {
- font-weight: 100;
-}
-
-.karavan .properties .expression .pf-v5-c-form__group,
-.karavan .properties .object .pf-v5-c-form__group,
-.karavan .properties .dataformat .pf-v5-c-form__group,
-.karavan .properties .parameters .pf-v5-c-form__group {
- margin-bottom: 10px;
-}
-
-.karavan .properties .expression .pf-v5-c-select__menu-wrapper,
-.karavan .properties .object .pf-v5-c-select__menu-wrapper,
-.karavan .properties .dataformat .pf-v5-c-select__menu-wrapper,
-.karavan .properties .parameters .pf-v5-c-select__menu-wrapper {
- width: 350px;
-}
-
-.karavan .properties .change-button {
- font-size: 15px;
- height: 15px;
- line-height: 1;
- border: 0;
- padding: 0;
- margin: auto auto auto 6px;
- background: transparent;
-}
-
-.karavan .properties .change-button svg {
- margin-right: 6px;
-}
-
-.karavan .properties .add-button {
- color: var(--pf-v5-global--active-color--100);
-}
-
-.karavan .properties .delete-button {
- color: #909090;
-}
-
-.karavan .properties .pf-v5-c-expandable-section__toggle {
- margin: 0;
- padding: 0;
-}
-
-.karavan .properties .pf-v5-c-expandable-section__content {
- margin: 0;
-}
-
-.karavan .properties .pf-v5-c-expandable-section__content p {
- min-height: 6px;
-}
-
-
/*Graph*/
.karavan .dsl-page .graph {
display: block;
@@ -691,7 +456,7 @@
flex-direction: column;
width: fit-content;
border-color: var(--pf-v5-global--Color--200);
- border-radius: 16px;
+ border-radius: 42px;
border-width: 1px;
min-width: 120px;
padding: 3px 4px 4px 4px;
@@ -718,178 +483,6 @@
gap: 6px;
}
-.karavan .dsl-page .flows .step-element .header-route {
- display: block;
- border: none;
- background: transparent;
- padding: 0px 0px 0px 0px;
- margin-bottom: 10px;
- min-width: 160px;
- z-index: 101;
-}
-
-.karavan .step-element .header-route .delete-button {
- position: absolute;
- top: 4px;
- right: 4px;
- line-height: 1;
- border: 0;
- padding: 0;
- margin: 0;
- background: transparent;
- color: #909090;
- visibility: hidden;
-}
-
-.karavan .step-element .header .delete-button,
-.element-builder .header .delete-button {
- position: absolute;
- top: -5px;
- line-height: 1;
- border: 0;
- padding: 0;
- margin: 0 0 0 10px;
- background: transparent;
- color: #909090;
- visibility: hidden;
-}
-
-/*.karavan .step-element:hover .delete-button,*/
-/*.karavan .step-element:hover .delete-button,*/
-.karavan .step-element .header:hover .delete-button,
-.karavan .step-element .header-route:hover .delete-button,
-.element-builder .header:hover .delete-button {
- visibility: visible;
-}
-
-.modal-delete {
- width: 350px !important;
-}
-
-.karavan .step-element .header {
- height: 50px;
-}
-
-.karavan .step-element-selected {
- background-color: rgba(var(--pf-v5-global--palette--blue-50), 1);
-}
-
-.karavan .step-element .selected .header-icon {
- border-color: var(--pf-v5-global--primary-color--100);
- background-color: var(--pf-v5-global--palette--blue-50);
- border-width: 2px;
-}
-
-.karavan .step-element .header .header-text {
- position: absolute;
- top: 8px;
- left: 0;
- width: 100%;
- display: flex;
- flex-direction: row;
-}
-
-.karavan .step-element .header .spacer {
- width: 50%
-}
-
-.karavan .step-element .header .text-bottom {
- background-color: rgba(255, 255, 255, 0.5);
-}
-
-.karavan .step-element .header .text-right {
- padding-left: 17px;
- width: 50%;
- text-align: start;
-}
-
-.karavan .step-element .header .header-text-required {
- color: var(--pf-v5-global--danger-color--100);
- font-weight: bold;
-}
-
-.karavan .step-element .header-icon {
- border-color: var(--pf-v5-global--Color--200);
- border-style: solid;
- border-radius: 30px;
- border-width: 1px;
- background: white;
- width: 30px;
- height: 30px;
- margin: auto;
- display: flex;
- justify-content: center;
- align-items: center;
-}
-
-.karavan .step-element .header .icon,
-.element-builder .header .icon {
- height: 20px;
- width: auto;
- border: none;
- -webkit-user-select: none;
- -khtml-user-select: none;
- -moz-user-select: none;
- -o-user-select: none;
- user-select: none;
-}
-
-.karavan .step-element .add-element-button {
- top: 5px;
- left: 5px;
- font-size: 15px;
- height: 15px;
- line-height: 1;
- border: 0;
- padding: 0;
- background: transparent;
- color: var(--pf-v5-global--primary-color--100);
- visibility: hidden;
- z-index: 100;
- position: absolute;
-}
-
-.karavan .step-element .insert-element-button {
- position: absolute;
- top: -5px;
- line-height: 1;
- border: 0;
- padding: 0;
- margin: 0 0 0 -25px;
- background: transparent;
- background: transparent;
- color: var(--pf-v5-global--primary-color--100);
- visibility: hidden;
- z-index: 100;
-}
-
-.karavan .step-element .header:hover .insert-element-button {
- visibility: visible;
-}
-
-.karavan .step-element .add-button-bottom {
- position: relative;
-}
-
-.karavan .step-element .add-button-left {
- position: absolute;
- top: 4px;
- left: 4px;
-}
-
-.karavan .step-element .add-button {
- font-size: 15px;
- height: 15px;
- line-height: 1;
- border: 0;
- padding: 0;
- margin: 0;
- background: transparent;
- color: var(--pf-v5-global--primary-color--100);
- visibility: hidden;
- z-index: 100;
-}
-
.karavan .dsl-page .flows .hidden-step {
display: none;
}
diff --git a/karavan-designer/src/designer/route/DslConnections.tsx b/karavan-designer/src/designer/route/DslConnections.tsx
index a636339e..6bb318c2 100644
--- a/karavan-designer/src/designer/route/DslConnections.tsx
+++ b/karavan-designer/src/designer/route/DslConnections.tsx
@@ -17,9 +17,8 @@
import React, {useEffect} from 'react';
import '../karavan.css';
import {CamelElement} from "karavan-core/lib/model/IntegrationDefinition";
-import {DslPosition, EventBus} from "../utils/EventBus";
+import {ButtonPosition, DslPosition, EventBus} from "../utils/EventBus";
import {CamelUi} from "../utils/CamelUi";
-import {SagaDefinition} from "karavan-core/lib/model/CamelDefinition";
import {useConnectionsStore, useDesignerStore, useIntegrationStore} from "../DesignerStore";
import {shallow} from "zustand/shallow";
import {CamelDefinitionApiExt} from "karavan-core/lib/api/CamelDefinitionApiExt";
@@ -32,12 +31,16 @@ export function DslConnections() {
const [integration] = useIntegrationStore((state) => [state.integration], shallow)
const [width, height, top, left, hideLogDSL] = useDesignerStore((s) =>
[s.width, s.height, s.top, s.left, s.hideLogDSL], shallow)
- const [ steps, addStep, deleteStep, clearSteps] = useConnectionsStore((s) => [s.steps, s.addStep, s.deleteStep, s.clearSteps], shallow)
+ const [steps, addStep, deleteStep, clearSteps, buttons, addButton, clearButtons, deleteButton] =
+ useConnectionsStore((s) => [s.steps, s.addStep, s.deleteStep, s.clearSteps,
+ s.buttons, s.addButton, s.clearButtons, s.deleteButton], shallow)
useEffect(() => {
- const sub = EventBus.onPosition()?.subscribe((evt: DslPosition) => setPosition(evt));
+ const sub1 = EventBus.onPosition()?.subscribe((evt: DslPosition) => setPosition(evt));
+ const sub2 = EventBus.onButtonPosition()?.subscribe((btn: ButtonPosition) => setButtonPosition(btn));
return () => {
- sub?.unsubscribe();
+ sub1?.unsubscribe();
+ sub2?.unsubscribe();
};
});
@@ -46,14 +49,22 @@ export function DslConnections() {
toDelete.forEach(key => deleteStep(key));
}, [integration]);
+ function setButtonPosition(btn: ButtonPosition) {
+ if (btn.command === "add") {
+ addButton(btn);
+ } else if (btn.command === "delete") {
+ deleteButton(btn);
+ } else if (btn.command === "clean") {
+ clearButtons();
+ }
+ }
+
function setPosition(evt: DslPosition) {
if (evt.command === "add") {
addStep(evt.step.uuid, evt);
- }
- else if (evt.command === "delete") {
+ } else if (evt.command === "delete") {
deleteStep(evt.step.uuid);
- }
- else if (evt.command === "clean") {
+ } else if (evt.command === "clean") {
clearSteps();
}
}
@@ -110,7 +121,8 @@ export function DslConnections() {
const imageX = incomingX - r + 5;
const imageY = fromY - r + 5;
return (
- <div key={pos.step.uuid + "-icon"} style={{display: "block", position: "absolute", top: imageY, left: imageX}}>
+ <div key={pos.step.uuid + "-icon"}
+ style={{display: "block", position: "absolute", top: imageY, left: imageX}}>
{CamelUi.getConnectionIcon(pos.step)}
</div>
)
@@ -174,16 +186,14 @@ export function DslConnections() {
const lineXi = lineX1 + 40;
const lineYi = lineY2;
- let image = CamelUi.getConnectionIconString(pos.step);
- const imageX = outgoingX - r + 5;
- const imageY = outgoingY - r + 5;
return (
<g key={pos.step.uuid + "-outgoing"}>
<circle cx={outgoingX} cy={outgoingY} r={r} className="circle-outgoing"/>
{/*<image x={imageX} y={imageY} href={image} className="icon"/>*/}
{/*<text x={imageX + 25} y={imageY + 40} className="caption" textAnchor="end">{CamelUi.getOutgoingTitle(pos.step)}</text>*/}
- <path d={`M ${lineX1},${lineY1} C ${lineXi - 20}, ${lineY1} ${lineX1 - 15},${lineYi} ${lineXi},${lineYi} L ${lineX2},${lineY2}`}
- className="path-incoming" markerEnd="url(#arrowhead)"/>
+ <path
+ d={`M ${lineX1},${lineY1} C ${lineXi - 20}, ${lineY1} ${lineX1 - 15},${lineYi} ${lineXi},${lineYi} L ${lineX2},${lineY2}`}
+ className="path-incoming" markerEnd="url(#arrowhead)"/>
</g>
)
}
@@ -198,185 +208,25 @@ export function DslConnections() {
const imageX = outgoingX - r + 5;
const imageY = outgoingY - r + 5;
return (
- <div key={pos.step.uuid + "-icon"} style={{display: "block", position: "absolute", top: imageY, left: imageX}}>
+ <div key={pos.step.uuid + "-icon"}
+ style={{display: "block", position: "absolute", top: imageY, left: imageX}}>
{CamelUi.getConnectionIcon(pos.step)}
</div>
)
}
}
- function getInternals(): [string, number, boolean][] {
- const outgoingDefinitions = TopologyUtils.getOutgoingDefinitions();
- let outs: [string, number, boolean][] = Array.from(steps.values())
- .filter(pos => outgoingDefinitions.includes(pos.step.dslName) && TopologyUtils.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 - top, pos.isSelected]);
- return outs;
- }
-
- 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 - left;
- const fromY = pos.headerRect.y + pos.headerRect.height / 2 - top;
- const r = pos.headerRect.height / 2;
- const className = (TopologyUtils.hasDirectUri(pos.step) ? "path-direct" : "path-seda") + (data[2] ? "-selected" : "");
- 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 - 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(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(getInternalLine(saga.compensation, key, className, fromX, fromY, r, data[1]));
- }
- return result;
- }
- }
-
- 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 - left;
- const targetY = target.headerRect.y + target.headerRect.height / 2 - top;
- const gap = 100;
- const add = 0.2;
-
- // right
- if (targetX - fromX >= gap) {
- const startX = fromX + r;
- const startY = fromY;
- const endX = targetX - r * 2 + 4;
- const endY = targetY;
-
- const coefX = 24 + (i * add);
- const coefY = (targetY > fromY) ? 24 : -24;
-
- const pointX1 = startX + coefX;
- const pointY1 = startY;
- const pointX2 = startX + coefX;
- const pointY2 = startY + coefY;
-
- const pointLX = pointX1;
- const pointLY = targetY - coefY;
-
- const pointX3 = pointLX;
- const pointY3 = endY;
- const pointX4 = pointLX + coefX;
- const pointY4 = 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;
- const endX = targetX - r * 2 + 4;
- const endY = targetY;
-
- const coefX = -24 - (i * add);
- const coefY = (targetY > fromY) ? 24 : -24;
-
- const pointX1 = startX + coefX;
- const pointY1 = startY;
- const pointX2 = startX + coefX;
- const pointY2 = startY + coefY;
-
- const pointLX = pointX1;
- const pointLY = targetY - coefY;
-
- const pointX3 = pointLX;
- const pointY3 = endY;
- const pointX4 = pointLX - coefX/2;
- const pointY4 = 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;
- const endX = targetX + r * 2 - 4;
- const endY = targetY;
-
- const coefX = 24 + (i * add);
- const coefY = (targetY > fromY) ? 24 : -24;
-
- const pointX1 = startX + coefX;
- const pointY1 = startY;
- const pointX2 = startX + coefX;
- const pointY2 = startY + coefY;
-
- const pointLX = pointX1;
- const pointLY = targetY - coefY;
-
- const pointX3 = pointLX;
- const pointY3 = endY;
- const pointX4 = pointLX - coefX/2;
- const pointY4 = 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;
- const endX = targetX + r * 2 - 4;
- const endY = targetY;
-
- const coefX = -24 - (i * add);
- const coefY = (targetY > fromY) ? 24 : -24;
-
- const pointX1 = startX + coefX;
- const pointY1 = startY;
- const pointX2 = startX + coefX;
- const pointY2 = startY + coefY;
-
- const pointLX = pointX1;
- const pointLY = targetY - coefY;
-
- const pointX3 = pointLX;
- const pointY3 = endY;
- const pointX4 = pointLX + coefX;
- const pointY4 = endY;
-
- return getInternalPath(key, className, startX, startY, pointX1, pointY1, pointX2, pointY2, pointLX, pointLY, pointX3, pointY3, pointX4, pointY4, endX, endY);
- }
- }
- }
-
- 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}>
- <path d={`M ${startX} ${startY}
- Q ${pointX1} ${pointY1} ${pointX2} ${pointY2} L ${pointLX},${pointLY}
- Q ${pointX3} ${pointY3} ${pointX4} ${pointY4} L ${endX},${endY}`}
- className={className} markerEnd="url(#arrowhead)"/>
- </g>
- )
- }
-
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"}/>
+ <circle cx={cx} cy={cy} r={r} stroke="transparent" strokeWidth="3" fill="transparent"
+ key={pos.step.uuid + "-circle"}/>
)
}
- function hasSteps (step: CamelElement): boolean {
+ function hasSteps(step: CamelElement): boolean {
return (step.hasSteps() && !['FromDefinition'].includes(step.dslName))
|| ['RouteDefinition', 'TryDefinition', 'ChoiceDefinition', 'SwitchDefinition'].includes(step.dslName);
}
@@ -393,18 +243,21 @@ export function DslConnections() {
const endY = pos.headerRect.y - 9 - top;
if (pos.parent) {
const parent = steps.get(pos.parent.uuid);
- if (parent) {
+ const showArrow = pos.prevStep !== undefined && !['TryDefinition', 'ChoiceDefinition'].includes(pos.prevStep.dslName);
+ const name = pos.prevStep?.dslName;
+ if (parent && showArrow) {
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}`}
+ <path name={pos.step.dslName}
+ d={`M ${startX},${startY} C ${startX},${endY} ${endX},${startY} ${endX},${endY}`}
className="path" key={pos.step.uuid} markerEnd="url(#arrowhead)"/>
)
} else if (parent.step.dslName === 'MulticastDefinition' && pos.inSteps) {
return (
<path d={`M ${startX},${startY} C ${startX},${endY} ${endX},${startY} ${endX},${endY}`}
- className="path" key={pos.step.uuid} markerEnd="url(#arrowhead)"/>
+ name={name} className="path" key={pos.step.uuid} markerEnd="url(#arrowhead)"/>
)
} else if (pos.inSteps && pos.position > 0 && !hasSteps(pos.step)) {
const prev = getPreviousStep(pos);
@@ -413,7 +266,8 @@ export function DslConnections() {
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)"/>
+ <line name={name} x1={prevX} y1={prevY} x2={endX} y2={endY} className="path"
+ key={pos.step.uuid} markerEnd="url(#arrowhead)"/>
)
}
} else if (pos.inSteps && pos.position > 0 && hasSteps(pos.step)) {
@@ -423,7 +277,8 @@ export function DslConnections() {
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)"/>
+ <line name={name} x1={prevX} y1={prevY} x2={endX} y2={endY} className="path"
+ key={pos.step.uuid} markerEnd="url(#arrowhead)"/>
)
}
}
@@ -431,6 +286,55 @@ export function DslConnections() {
}
}
+ function getButtonArrow(btn: ButtonPosition) {
+ const rect1 = btn.rect;
+ const uuid = btn.nextstep.uuid;
+ const nextStep = steps.get(uuid);
+ const rect2 = nextStep?.rect;
+ if (rect1 && rect2) {
+ const startX = rect1.x + rect1.width / 2 - left;
+ const startY = rect1.y + rect1.height - top - 2;
+ const endX = rect2.x + rect2.width / 2 - left;
+ const endTempY = rect2.y - top - 9;
+
+ const gapX = Math.abs(endX - startX);
+ const gapY = Math.abs(endTempY - startY);
+
+ const radX = gapX > 30 ? 20 : gapX/2;
+ const radY = gapY > 30 ? 20 : gapY/2;
+ const endY = rect2.y - top - 9 - radY;
+
+ const iRadX = startX > endX ? -1 * radX : radX;
+ const iRadY = startY > endY ? -1 * radY : radY;
+
+ const LX1 = startX;
+ const LY1 = endY - radY;
+
+ const Q1_X1 = startX;
+ const Q1_Y1 = LY1 + radY;
+ const Q1_X2 = startX + iRadX;
+ const Q1_Y2 = LY1 + radY;
+
+ const LX2 = startX + (endX - startX) - iRadX;
+ const LY2 = LY1 + radY;
+
+ const Q2_X1 = LX2 + iRadX;
+ const Q2_Y1 = endY;
+ const Q2_X2 = LX2 + iRadX;
+ const Q2_Y2 = endY + radY;
+
+ const path = `M ${startX} ${startY}`
+ + ` L ${LX1} ${LY1} `
+ + ` Q ${Q1_X1} ${Q1_Y1} ${Q1_X2} ${Q1_Y2}`
+ + ` L ${LX2} ${LY2}`
+ + ` Q ${Q2_X1} ${Q2_Y1} ${Q2_X2} ${Q2_Y2}`
+ // + ` L ${endX} ${endY}`;
+ return (
+ <path key={btn.uuid} d={path} className="path" markerEnd="url(#arrowhead)"/>
+ )
+ }
+ }
+
function getSvg() {
const stepsArray = Array.from(steps.values());
return (
@@ -438,12 +342,14 @@ export function DslConnections() {
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">
+ <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>
{stepsArray.map(pos => getCircle(pos))}
{stepsArray.map(pos => getArrow(pos))}
+ {buttons.map(btn => getButtonArrow(btn)).filter(b => b !== undefined)}
{getIncomings().map(p => getIncoming(p))}
{getOutgoings().map(p => getOutgoing(p))}
{/*{getInternals().map((p) => getInternalLines(p)).flat()}*/}
@@ -452,7 +358,7 @@ export function DslConnections() {
}
return (
- <div id="connections" className="connections" style={{ width: width, height: height}}>
+ <div id="connections" className="connections" style={{width: width, height: height}}>
{getSvg()}
{getIncomings().map(p => getIncomingIcons(p))}
{getOutgoings().map(p => getOutgoingIcons(p))}
diff --git a/karavan-designer/src/designer/route/DslElement.css b/karavan-designer/src/designer/route/DslElement.css
deleted file mode 100644
index d3ed7177..00000000
--- a/karavan-designer/src/designer/route/DslElement.css
+++ /dev/null
@@ -1,18 +0,0 @@
-.disabled {
- opacity: 0.5;
-}
-.menu-button {
- position: absolute;
- top: 26px;
- line-height: 1;
- border: 0;
- padding: 0;
- margin: 0 0 0 10px;
- background: transparent;
- color: var(--pf-v5-global--primary-color--100);
- visibility: hidden;
-}
-
-.dsl-element:hover .menu-button {
- visibility: visible;
-}
diff --git a/karavan-designer/src/designer/route/DslProperties.css b/karavan-designer/src/designer/route/DslProperties.css
new file mode 100644
index 00000000..7040fd40
--- /dev/null
+++ b/karavan-designer/src/designer/route/DslProperties.css
@@ -0,0 +1,250 @@
+/*
+ * 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.
+ */
+.karavan .properties .headers {
+ grid-row-gap: 10px;
+ row-gap: 10px;
+ display: contents;
+}
+
+.karavan .properties .headers .top {
+ width: 100%;
+ display: flex;
+ flex-direction: row;
+}
+
+.karavan .properties .headers .top h1 {
+ width: 100%;
+ margin-top: auto;
+ margin-bottom: auto;
+}
+
+.karavan .properties .footer {
+ height: 100%;
+ display: contents;
+}
+
+.karavan .properties .pf-v5-c-form {
+ row-gap: 10px;
+}
+
+.karavan .properties .pf-v5-c-form__group-label {
+ padding-bottom: 3px;
+ display: flex;
+ justify-content: space-between;
+}
+
+.karavan .properties .pf-v5-c-form-control:focus-within {
+ --pf-v5-c-form-control--after--BorderBottomColor: var(--pf-v5-c-form-control--after--BorderBottomColor);
+ --pf-v5-c-form-control--after--BorderBottomWidth: var(--pf-v5-c-form-control--after--BorderBottomWidth);
+}
+
+.karavan .properties .pf-v5-c-select {
+ --pf-v5-c-select__toggle--FontSize: 14px;
+ --pf-v5-c-select__menu-item--FontSize: 14px;
+}
+
+.karavan .properties .input-group .pf-v5-c-text-input-group__text {
+ width: 100%;
+}
+
+.karavan .properties .input-group .pf-v5-c-chip-group,
+.karavan .properties .input-group .pf-v5-c-text-input-group__main,
+.karavan .properties .input-group .pf-v5-c-chip-group .pf-v5-c-chip-group__list,
+.karavan .properties .input-group .pf-v5-c-chip-group .pf-v5-c-chip-group__list .pf-v5-c-chip-group__list-item,
+.karavan .properties .input-group .pf-v5-c-chip-group .pf-v5-c-chip-group__main {
+ display: block;
+}
+
+.karavan .properties .input-group .pf-v5-c-chip-group {
+ margin-left: 0;
+}
+
+.karavan .properties .input-group .pf-v5-c-chip-group .pf-v5-c-chip .pf-v5-c-chip__text {
+ max-width: inherit;
+}
+
+.karavan .properties .input-group .pf-v5-c-chip-group .pf-v5-c-chip {
+ width: 100%;
+}
+
+.karavan .properties .input-group .pf-v5-c-text-input-group__utilities {
+ align-items: end;
+ margin-top: auto;
+}
+
+.karavan .properties .chip .pf-v5-c-button {
+ position: absolute;
+ right: 0;
+}
+
+.karavan .properties .expression-title {
+ font-size: 17px;
+ font-weight: bold;
+}
+
+.karavan .properties .text-area {
+ font-size: 13px;
+}
+
+.karavan .properties .pf-v5-c-select__menu-search {
+ padding: 0px 6px 6px 6px;
+}
+
+.karavan .properties .pf-v5-c-select__toggle-typeahead {
+ height: auto;
+}
+
+.karavan .properties .pf-v5-c-select__menu-item {
+ /*width: 280px;*/
+}
+
+.karavan .properties .pf-v5-c-select__menu-item-description {
+ overflow-wrap: anywhere;
+}
+
+.karavan .properties .number {
+ display: flex;
+ justify-content: space-between;
+}
+
+.karavan .properties .number .number-property {
+ width: 100%;
+}
+
+.karavan .properties .number .clear-button {
+ color: #b1b1b7;
+ --pf-v5-c-button--BorderRadius: var(--pf-v5-c-button--m-control--BorderRadius);
+ --pf-v5-c-button--disabled--BackgroundColor: var(--pf-v5-c-button--m-control--disabled--BackgroundColor);
+ --pf-v5-c-button--after--BorderWidth: var(--pf-v5-c-button--m-control--after--BorderWidth);
+ --pf-v5-c-button--after--BorderColor: var(--pf-v5-c-button--m-control--after--BorderTopColor) var(--pf-v5-c-button--m-control--after--BorderRightColor) var(--pf-v5-c-button--m-control--after--BorderBottomColor) var(--pf-v5-c-button--m-control--after--BorderLeftColor);
+ color: var(--pf-v5-c-button--m-control--Color);
+ background-color: var(--pf-v5-c-button--m-control--BackgroundColor);
+ padding-left: 5px;
+ padding-right: 5px;
+}
+
+.karavan .properties .help-button {
+ font-size: 12px;
+}
+
+.karavan .properties .component-selector {
+ border-width: var(--pf-v5-global--BorderWidth--sm);
+ border-top-color: var(--pf-v5-global--BorderColor--300);
+ border-right-color: var(--pf-v5-global--BorderColor--300);
+ border-bottom-color: var(--pf-v5-global--BorderColor--200);
+ border-left-color: var(--pf-v5-global--BorderColor--300);
+ border-style: solid;
+}
+
+.karavan .properties .object-value {
+ display: flex;
+ flex-direction: row;
+ margin-bottom: 3px;
+}
+
+.karavan .properties .object-key-value .object {
+ padding-top: 0;
+}
+
+.karavan .properties .object-value .delete-button {
+ margin: 0;
+ padding: 5px 3px 0px 6px;
+ height: 16px;
+ color: #909090;
+}
+
+.karavan .properties .object-key-value,
+.karavan .properties .object-key-value .object-field {
+ display: flex;
+ flex-direction: row;
+ gap: 3px;
+}
+
+.karavan .properties .object-key-value .delete-button {
+ margin: auto 0 22px 0;
+ padding: 0px 0px 0px 3px;
+ height: 16px;
+ color: #909090;
+}
+
+.karavan .properties .expression,
+.karavan .properties .object,
+.karavan .properties .dataformat,
+.karavan .properties .parameters {
+ padding-top: 6px;
+ padding-left: 16px;
+ row-gap: 6px;
+ display: grid;
+ width: 100%;
+}
+
+.karavan .properties .expression .pf-v5-c-form__group-label,
+.karavan .properties .object .pf-v5-c-form__group-label,
+.karavan .properties .dataformat .pf-v5-c-form__group-label,
+.karavan .properties .parameters .pf-v5-c-form__group-label {
+ font-weight: 100;
+}
+
+.karavan .properties .expression .pf-v5-c-form__group,
+.karavan .properties .object .pf-v5-c-form__group,
+.karavan .properties .dataformat .pf-v5-c-form__group,
+.karavan .properties .parameters .pf-v5-c-form__group {
+ margin-bottom: 10px;
+}
+
+.karavan .properties .expression .pf-v5-c-select__menu-wrapper,
+.karavan .properties .object .pf-v5-c-select__menu-wrapper,
+.karavan .properties .dataformat .pf-v5-c-select__menu-wrapper,
+.karavan .properties .parameters .pf-v5-c-select__menu-wrapper {
+ width: 350px;
+}
+
+.karavan .properties .change-button {
+ font-size: 15px;
+ height: 15px;
+ line-height: 1;
+ border: 0;
+ padding: 0;
+ margin: auto auto auto 6px;
+ background: transparent;
+}
+
+.karavan .properties .change-button svg {
+ margin-right: 6px;
+}
+
+.karavan .properties .add-button {
+ color: var(--pf-v5-global--active-color--100);
+}
+
+.karavan .properties .delete-button {
+ color: #909090;
+}
+
+.karavan .properties .pf-v5-c-expandable-section__toggle {
+ margin: 0;
+ padding: 0;
+}
+
+.karavan .properties .pf-v5-c-expandable-section__content {
+ margin: 0;
+}
+
+.karavan .properties .pf-v5-c-expandable-section__content p {
+ min-height: 6px;
+}
+
diff --git a/karavan-designer/src/designer/route/DslProperties.tsx b/karavan-designer/src/designer/route/DslProperties.tsx
index 8231759c..b958b2b3 100644
--- a/karavan-designer/src/designer/route/DslProperties.tsx
+++ b/karavan-designer/src/designer/route/DslProperties.tsx
@@ -22,6 +22,7 @@ import {
TextVariants, ExpandableSection, Button, Tooltip,
} from '@patternfly/react-core';
import '../karavan.css';
+import './DslProperties.css';
import "@patternfly/patternfly/patternfly.css";
import {DataFormatField} from "./property/DataFormatField";
import {DslPropertyField} from "./property/DslPropertyField";
@@ -30,6 +31,7 @@ import {CamelUi} from "../utils/CamelUi";
import {CamelMetadataApi, DataFormats, PropertyMeta} from "karavan-core/lib/model/CamelMetadata";
import {IntegrationHeader} from "../utils/IntegrationHeader";
import CloneIcon from "@patternfly/react-icons/dist/esm/icons/clone-icon";
+import ConvertIcon from "@patternfly/react-icons/dist/esm/icons/optimize-icon";
import {useDesignerStore, useIntegrationStore} from "../DesignerStore";
import {shallow} from "zustand/shallow";
import {usePropertiesHook} from "./usePropertiesHook";
@@ -43,7 +45,8 @@ export function DslProperties(props: Props) {
const [integration] = useIntegrationStore((state) => [state.integration], shallow)
- const {cloneElement, onDataFormatChange, onPropertyChange, onParametersChange, onExpressionChange} = usePropertiesHook(props.isRouteDesigner);
+ const {convertStep, cloneElement, onDataFormatChange, onPropertyChange, onParametersChange, onExpressionChange} =
+ usePropertiesHook(props.isRouteDesigner);
const [selectedStep, dark] = useDesignerStore((s) => [s.selectedStep, s.dark], shallow)
@@ -54,10 +57,26 @@ export function DslProperties(props: Props) {
const title = selectedStep && CamelDisplayUtil.getTitle(selectedStep)
const description = selectedStep && CamelUi.getDescription(selectedStep);
const descriptionLines: string [] = description ? description?.split("\n") : [""];
+ const targetDsl = CamelUi.getConvertTargetDsl(selectedStep?.dslName);
+ const targetDslTitle = targetDsl?.replace("Definition", "");
return (
<div className="headers">
<div className="top">
<Title headingLevel="h1" size="md">{title}</Title>
+ {targetDsl &&
+ <Button
+ variant={"link"}
+ icon={<ConvertIcon/>}
+ iconPosition={"right"}
+ onClick={event => {
+ if (selectedStep) {
+ convertStep(selectedStep, targetDsl);
+ }
+ }}
+ >
+ Convert to {targetDslTitle}
+ </Button>
+ }
</div>
<Text component={TextVariants.p}>{descriptionLines.at(0)}</Text>
{descriptionLines.length > 1 &&
diff --git a/karavan-designer/src/designer/route/RouteDesigner.tsx b/karavan-designer/src/designer/route/RouteDesigner.tsx
index c57260db..b6b1c942 100644
--- a/karavan-designer/src/designer/route/RouteDesigner.tsx
+++ b/karavan-designer/src/designer/route/RouteDesigner.tsx
@@ -27,7 +27,7 @@ import {DslSelector} from "./DslSelector";
import {DslProperties} from "./DslProperties";
import {DslConnections} from "./DslConnections";
import PlusIcon from "@patternfly/react-icons/dist/esm/icons/plus-icon";
-import {DslElement} from "./DslElement";
+import {DslElement} from "./element/DslElement";
import {CamelUi} from "../utils/CamelUi";
import {useRouteDesignerHook} from "./useRouteDesignerHook";
import {useConnectionsStore, useDesignerStore, useIntegrationStore, useSelectorStore} from "../DesignerStore";
@@ -36,7 +36,7 @@ import useResizeObserver from "./useResizeObserver";
import {Command, EventBus} from "../utils/EventBus";
import useMutationsObserver from "./useDrawerMutationsObserver";
import {DeleteConfirmation} from "./DeleteConfirmation";
-import {DslElementMoveModal} from "./DslElementMoveModal";
+import {DslElementMoveModal} from "./element/DslElementMoveModal";
export function RouteDesigner() {
@@ -152,15 +152,21 @@ export function RouteDesigner() {
inSteps={false}
position={index}
step={routeConfiguration}
+ nextStep={undefined}
+ prevStep={undefined}
parent={undefined}/>
))}
- {routes?.map((route: any, index: number) => (
- <DslElement key={route.uuid}
- inSteps={false}
- position={index}
- step={route}
- parent={undefined}/>
- ))}
+ {routes?.map((route: any, index: number) => {
+ return (
+ <DslElement key={route.uuid}
+ inSteps={false}
+ position={index}
+ step={route}
+ nextStep={undefined}
+ prevStep={undefined}
+ parent={undefined}/>
+ )
+ })}
{getGraphButtons()}
</div>
</div>)
diff --git a/karavan-designer/src/designer/route/element/DslElement.css b/karavan-designer/src/designer/route/element/DslElement.css
new file mode 100644
index 00000000..85f7b46a
--- /dev/null
+++ b/karavan-designer/src/designer/route/element/DslElement.css
@@ -0,0 +1,220 @@
+/*
+ * 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.
+ */
+
+.karavan .dsl-page .flows .step-element .header-route {
+ display: block;
+ border: none;
+ background: transparent;
+ padding: 0px 0px 0px 0px;
+ margin-bottom: 10px;
+ min-width: 160px;
+ z-index: 101;
+}
+
+.karavan .step-element .header-route .delete-button {
+ position: absolute;
+ top: 4px;
+ right: 4px;
+ line-height: 1;
+ border: 0;
+ padding: 0;
+ margin: 0;
+ background: transparent;
+ color: #909090;
+ visibility: hidden;
+}
+
+.karavan .step-element .header .delete-button,
+.element-builder .header .delete-button {
+ position: absolute;
+ top: -8px;
+ line-height: 1;
+ border: 0;
+ padding: 0;
+ margin: 0 0 0 10px;
+ background: transparent;
+ color: #909090;
+ visibility: hidden;
+}
+
+/*.karavan .step-element:hover .delete-button,*/
+/*.karavan .step-element:hover .delete-button,*/
+.karavan .step-element .header:hover .delete-button,
+.karavan .step-element .header-route:hover .delete-button,
+.element-builder .header:hover .delete-button {
+ visibility: visible;
+}
+
+.modal-delete {
+ width: 350px !important;
+}
+
+.karavan .step-element .header {
+ height: 50px;
+}
+
+.karavan .step-element-selected {
+ background-color: rgba(var(--pf-v5-global--palette--blue-50), 1);
+}
+
+.karavan .step-element .selected .header-icon {
+ border-color: var(--pf-v5-global--primary-color--100);
+ background-color: var(--pf-v5-global--palette--blue-50);
+ border-width: 2px;
+}
+
+.karavan .step-element .header .header-text {
+ position: absolute;
+ top: 8px;
+ left: 0;
+ width: 100%;
+ display: flex;
+ flex-direction: row;
+}
+
+.karavan .step-element .header .spacer {
+ width: 50%
+}
+
+.karavan .step-element .header .text-bottom {
+ background-color: rgba(255, 255, 255, 0.5);
+}
+
+.karavan .step-element .header .text-right {
+ padding-left: 17px;
+ width: 50%;
+ text-align: start;
+}
+
+.karavan .step-element .header .header-text-required {
+ color: var(--pf-v5-global--danger-color--100);
+ font-weight: bold;
+}
+
+.karavan .step-element .header-icon {
+ border-color: var(--pf-v5-global--Color--200);
+ border-style: solid;
+ border-radius: 30px;
+ border-width: 1px;
+ background: white;
+ width: 30px;
+ height: 30px;
+ margin: auto;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+}
+
+.karavan .step-element .header .icon,
+.element-builder .header .icon {
+ height: 20px;
+ width: auto;
+ border: none;
+ -webkit-user-select: none;
+ -khtml-user-select: none;
+ -moz-user-select: none;
+ -o-user-select: none;
+ user-select: none;
+}
+
+.karavan .step-element .add-element-button {
+ top: 5px;
+ left: 5px;
+ font-size: 15px;
+ height: 15px;
+ line-height: 1;
+ border: 0;
+ padding: 0;
+ background: transparent;
+ color: var(--pf-v5-global--primary-color--100);
+ visibility: hidden;
+ z-index: 100;
+ position: absolute;
+}
+
+.karavan .step-element .insert-element-button {
+ position: absolute;
+ top: -8px;
+ line-height: 1;
+ border: 0;
+ padding: 0;
+ margin: 0 0 0 -30px;
+ background: transparent;
+ color: var(--pf-v5-global--primary-color--100);
+ visibility: hidden;
+ z-index: 100;
+}
+
+.karavan .step-element .header:hover .insert-element-button {
+ visibility: visible;
+}
+
+.karavan .step-element .add-button-bottom {
+ position: relative;
+}
+
+.karavan .step-element .add-button-left {
+ position: absolute;
+ top: 4px;
+ left: 4px;
+}
+
+.karavan .step-element .add-button {
+ font-size: 15px;
+ height: 15px;
+ line-height: 1;
+ border: 0;
+ padding: 0;
+ margin: 0;
+ background: transparent;
+ color: var(--pf-v5-global--primary-color--100);
+ visibility: hidden;
+ z-index: 100;
+}
+
+.disabled {
+ opacity: 0.5;
+}
+.menu-button {
+ position: absolute;
+ top: 26px;
+ line-height: 1;
+ border: 0;
+ padding: 0;
+ margin: 0 0 0 10px;
+ background: transparent;
+ color: var(--pf-v5-global--primary-color--100);
+ visibility: hidden;
+}
+
+.dsl-element:hover .menu-button {
+ visibility: visible;
+}
+
+.add-button-icon, .insert-button-icon {
+ fill: var(--pf-v5-global--primary-color--100);
+ width: 20px;
+ height: 20px;
+ background: white;
+}
+
+.delete-button-icon {
+ fill: var(--pf-v5-global--danger-color--100);
+ width: 20px;
+ height: 20px;
+ background: white;
+}
\ No newline at end of file
diff --git a/karavan-designer/src/designer/route/DslElement.tsx b/karavan-designer/src/designer/route/element/DslElement.tsx
similarity index 79%
rename from karavan-designer/src/designer/route/DslElement.tsx
rename to karavan-designer/src/designer/route/element/DslElement.tsx
index e3dfdbfc..ce780636 100644
--- a/karavan-designer/src/designer/route/DslElement.tsx
+++ b/karavan-designer/src/designer/route/element/DslElement.tsx
@@ -15,27 +15,25 @@
* limitations under the License.
*/
import React, {CSSProperties, useMemo, useState} from 'react';
-import {Menu, MenuContent, MenuItem, MenuList, Popover, Text, Tooltip,} from '@patternfly/react-core';
-import '../karavan.css';
+import {Text, Tooltip,} from '@patternfly/react-core';
+import '../../karavan.css';
import './DslElement.css';
-import AddIcon from "@patternfly/react-icons/dist/js/icons/plus-circle-icon";
-import DeleteIcon from "@patternfly/react-icons/dist/js/icons/times-circle-icon";
-import SyncIcon from "@patternfly/react-icons/dist/js/icons/sync-icon";
-import TurnIcon from "@patternfly/react-icons/dist/js/icons/chevron-circle-right-icon";
-import InsertIcon from "@patternfly/react-icons/dist/js/icons/arrow-alt-circle-right-icon";
import {CamelElement} from "karavan-core/lib/model/IntegrationDefinition";
-import {CamelUi} from "../utils/CamelUi";
-import {EventBus} from "../utils/EventBus";
+import {CamelUi} from "../../utils/CamelUi";
+import {EventBus} from "../../utils/EventBus";
import {ChildElement, CamelDefinitionApiExt} from "karavan-core/lib/api/CamelDefinitionApiExt";
import {CamelUtil} from "karavan-core/lib/api/CamelUtil";
import {CamelDisplayUtil} from "karavan-core/lib/api/CamelDisplayUtil";
-import {useDesignerStore, useIntegrationStore} from "../DesignerStore";
+import {useDesignerStore, useIntegrationStore} from "../../DesignerStore";
import {shallow} from "zustand/shallow";
-import {useRouteDesignerHook} from "./useRouteDesignerHook";
+import {useRouteDesignerHook} from "../useRouteDesignerHook";
+import {AddElementIcon, DeleteElementIcon, InsertElementIcon} from "./DslElementIcons";
interface Props {
step: CamelElement,
parent: CamelElement | undefined,
+ nextStep: CamelElement | undefined,
+ prevStep: CamelElement | undefined,
inSteps: boolean
position: number
}
@@ -43,7 +41,16 @@ interface Props {
export function DslElement(props: Props) {
const headerRef = React.useRef<HTMLDivElement>(null);
- const {selectElement, moveElement, onShowDeleteConfirmation, openSelector, isKamelet, isSourceKamelet, isActionKamelet} = useRouteDesignerHook();
+ const addButtonRef = React.useRef<HTMLDivElement>(null);
+ const {
+ selectElement,
+ moveElement,
+ onShowDeleteConfirmation,
+ openSelector,
+ isKamelet,
+ isSourceKamelet,
+ isActionKamelet
+ } = useRouteDesignerHook();
const [integration] = useIntegrationStore((s) => [s.integration, s.setIntegration], shallow)
@@ -98,12 +105,20 @@ export function DslElement(props: Props) {
}
function hasBorder(): boolean {
- return (props.step?.hasSteps() && !['FromDefinition'].includes(props.step.dslName))
- || ['RouteConfigurationDefinition',
- 'RouteDefinition',
- 'TryDefinition',
- 'ChoiceDefinition',
- 'SwitchDefinition'].includes(props.step.dslName);
+ const step = props.step;
+ if (['FilterDefinition'].includes(step.dslName)) {
+ return true;
+ }
+ if (['FromDefinition',
+ 'RouteDefinition',
+ 'TryDefinition',
+ 'CatchDefinition', 'FinallyDefinition',
+ 'ChoiceDefinition',
+ 'SwitchDefinition', 'WhenDefinition', 'OtherwiseDefinition'
+ ].includes(step.dslName)) {
+ return false;
+ }
+ return props.step?.hasSteps();
}
function isNotDraggable(): boolean {
@@ -177,7 +192,26 @@ export function DslElement(props: Props) {
return style;
}
+ function sendButtonPosition(el: HTMLButtonElement | null) {
+ const {nextStep, step, parent} = props;
+ let needArrow = !hasBorder() && !['ChoiceDefinition', 'MulticastDefinition', 'TryDefinition'].includes(step.dslName);
+
+ if (parent
+ && ['TryDefinition'].includes(parent.dslName)
+ && !['CatchDefinition', 'FinallyDefinition'].includes(step.dslName)) {
+ needArrow = true;
+ }
+
+ if (el && nextStep && needArrow) {
+ const rect = headerRef.current?.getBoundingClientRect();
+
+ if (rect)
+ EventBus.sendButtonPosition("add", step.uuid, nextStep, rect);
+ }
+ }
+
function sendPosition(el: HTMLDivElement | null) {
+ const {step, prevStep, parent} = props;
const isSelected = isElementSelected();
const isHidden = isElementHidden();
if (el) {
@@ -186,15 +220,14 @@ export function DslElement(props: Props) {
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 (props.step.showChildren) {
+ if (step.showChildren) {
if (isHidden) {
- // EventBus.sendPosition("delete", props.step, props.parent, new DOMRect(), new DOMRect(), 0);
- EventBus.sendPosition("add", props.step, props.parent, rect, headerRect, props.position, props.inSteps, isSelected);
+ EventBus.sendPosition("add", step, prevStep, parent, rect, headerRect, props.position, props.inSteps, isSelected);
} else {
- EventBus.sendPosition("add", props.step, props.parent, rect, headerRect, props.position, props.inSteps, isSelected);
+ EventBus.sendPosition("add", step, prevStep, parent, rect, headerRect, props.position, props.inSteps, isSelected);
}
} else {
- EventBus.sendPosition("delete", props.step, props.parent, new DOMRect(), new DOMRect(), 0);
+ EventBus.sendPosition("delete", step, prevStep, parent, new DOMRect(), new DOMRect(), 0);
}
}
}
@@ -237,7 +270,6 @@ export function DslElement(props: Props) {
</div>
{showInsertButton && getInsertElementButton()}
{getDeleteButton()}
- {/*{getMenuButton()}*/}
{showAddButton && getAddElementButton()}
</div>
)
@@ -304,7 +336,7 @@ export function DslElement(props: Props) {
const step = props.step;
const isBorder = child.name === 'steps' && hasBorderOverSteps(step);
const style: CSSProperties = {
- borderStyle: isBorder ? "dotted" : "none",
+ // borderStyle: isBorder ? "dotted" : "none",
borderColor: "var(--step-border-color)",
borderWidth: "1px",
borderRadius: "16px",
@@ -343,15 +375,25 @@ export function DslElement(props: Props) {
return (
<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
- inSteps={child.name === 'steps'}
- position={index}
- step={element}
- parent={step}/>
- </div>
- ))}
+ {children.map((element, index) => {
+ let prevStep = children.at(index - 1);
+ let nextStep = undefined;
+ if (['TryDefinition', 'ChoiceDefinition'].includes(step.dslName)) {
+ nextStep = props.nextStep;
+ } else {
+ nextStep = children.at(index + 1);
+ }
+ return (<div key={step.uuid + child.className + index}>
+ <DslElement
+ inSteps={child.name === 'steps'}
+ position={index}
+ step={element}
+ nextStep={nextStep}
+ prevStep={prevStep}
+ parent={step}/>
+ </div>)
+ }
+ )}
{child.name === 'steps' && getAddStepButton()}
</div>
)
@@ -366,16 +408,23 @@ export function DslElement(props: Props) {
}
function getAddStepButton() {
- const {step} = props;
+ const {step, nextStep} = 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 " + CamelDisplayUtil.getTitle(step)}</div>}>
- <button type="button" aria-label="Add" onClick={e => onOpenSelector(e)}
- className={isAddStepButtonLeft() ? "add-button add-button-left" : "add-button add-button-bottom"}>
- <AddIcon/>
- </button>
+ content={<div>{nextStep?.dslName.replace("Definition", "")}</div>}
+ // content={<div>{"Add step to " + CamelDisplayUtil.getTitle(step)}</div>}
+ >
+ <div ref={addButtonRef}>
+ <button type="button"
+ ref={el => sendButtonPosition(el)}
+ aria-label="Add"
+ onClick={e => onOpenSelector(e)}
+ className={isAddStepButtonLeft() ? "add-button add-button-left" : "add-button add-button-bottom"}>
+ <AddElementIcon/>
+ </button>
+ </div>
</Tooltip>
)
}
@@ -389,7 +438,7 @@ export function DslElement(props: Props) {
aria-label="Add"
onClick={e => onOpenSelector(e, false)}
className={"add-element-button"}>
- <AddIcon/>
+ <AddElementIcon/>
</button>
</Tooltip>
)
@@ -398,8 +447,11 @@ export function DslElement(props: Props) {
function getInsertElementButton() {
return (
<Tooltip position={"left"} content={<div>{"Insert element before"}</div>}>
- <button type="button" aria-label="Insert" onClick={e => onOpenSelector(e, true, true)}
- className={"insert-element-button"}><InsertIcon/>
+ <button type="button"
+ aria-label="Insert"
+ onClick={e => onOpenSelector(e, true, true)}
+ className={"insert-element-button"}>
+ <InsertElementIcon/>
</button>
</Tooltip>
)
@@ -409,39 +461,12 @@ export function DslElement(props: Props) {
return (
<Tooltip position={"right"} content={<div>{"Delete element"}</div>}>
<button type="button" aria-label="Delete" onClick={e => onDeleteElement(e)} className="delete-button">
- <DeleteIcon/>
+ <DeleteElementIcon/>
</button>
</Tooltip>
)
}
- function getMenuButton() {
- return (
- <Popover
- aria-label="Convert Popover"
- hasNoPadding
- position={"right"}
- hideOnOutsideClick={true}
- showClose={false}
- bodyContent={
- <Menu activeItemId={''} onSelect={event => {}} isPlain>
- <MenuContent>
- <MenuList>
- <MenuItem itemId={0} icon={<SyncIcon aria-hidden />}>Convert to SetHeader</MenuItem>
- {/*<MenuItem itemId={1}>Action</MenuItem>*/}
- {/*<MenuItem itemId={2}>Action</MenuItem>*/}
- </MenuList>
- </MenuContent>
- </Menu>
- }
- >
- <button type="button" aria-label="Menu" onClick={e => {}} className="menu-button">
- <TurnIcon/>
- </button>
- </Popover>
- )
- }
-
const element: CamelElement = props.step;
const className = "step-element"
+ (isElementSelected() ? " step-element-selected" : "") + (!props.step.showChildren ? " hidden-step" : "")
@@ -451,7 +476,7 @@ export function DslElement(props: Props) {
className={className}
ref={el => sendPosition(el)}
style={{
- borderStyle: hasBorder() ? "dotted" : "none",
+ borderStyle: hasBorder() ? "dashed" : "none",
borderColor: isElementSelected() ? "var(--step-border-color-selected)" : "var(--step-border-color)",
marginTop: isInStepWithChildren() ? "16px" : "8px",
zIndex: element.dslName === 'ToDefinition' ? 20 : 10,
diff --git a/karavan-designer/src/designer/route/element/DslElementIcons.tsx b/karavan-designer/src/designer/route/element/DslElementIcons.tsx
new file mode 100644
index 00000000..3b6b984b
--- /dev/null
+++ b/karavan-designer/src/designer/route/element/DslElementIcons.tsx
@@ -0,0 +1,80 @@
+/*
+ * 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 "./DslElement.css"
+
+export function DeleteElementIcon() {
+ return (
+ <svg
+ xmlns="http://www.w3.org/2000/svg"
+ xmlSpace="preserve"
+ viewBox="0 0 32 32"
+ className="delete-button-icon"
+ >
+ <path d="M16 2C8.2 2 2 8.2 2 16s6.2 14 14 14 14-6.2 14-14S23.8 2 16 2zm0 26C9.4 28 4 22.6 4 16S9.4 4 16 4s12 5.4 12 12-5.4 12-12 12z" />
+ <path
+ d="M0 0h32v32H0z"
+ style={{
+ fill: "none",
+ }}
+ />
+ <path d="M21.4 23 16 17.6 10.6 23 9 21.4l5.4-5.4L9 10.6 10.6 9l5.4 5.4L21.4 9l1.6 1.6-5.4 5.4 5.4 5.4z" />
+ </svg>
+ )
+}
+
+export function AddElementIcon() {
+ return (
+ <svg
+ xmlns="http://www.w3.org/2000/svg"
+ xmlSpace="preserve"
+ viewBox="0 0 32 32"
+ className="add-button-icon"
+ >
+ <path
+ d="M16 4c6.6 0 12 5.4 12 12s-5.4 12-12 12S4 22.6 4 16 9.4 4 16 4m0-2C8.3 2 2 8.3 2 16s6.3 14 14 14 14-6.3 14-14S23.7 2 16 2z"/>
+ <path d="M24 15h-7V8h-2v7H8v2h7v7h2v-7h7z"/>
+ <path
+ d="M0 0h32v32H0z"
+ style={{
+ fill: "none",
+ }}
+ />
+ </svg>
+ )
+}
+
+export function InsertElementIcon() {
+ return (
+ <svg
+ xmlns="http://www.w3.org/2000/svg"
+ xmlSpace="preserve"
+ viewBox="0 0 32 32"
+ className="insert-button-icon"
+ >
+ <path d="m16 8-1.43 1.393L20.15 15H8v2h12.15l-5.58 5.573L16 24l8-8-8-8z" />
+ <path d="M16 30a14 14 0 1 1 14-14 14.016 14.016 0 0 1-14 14Zm0-26a12 12 0 1 0 12 12A12.014 12.014 0 0 0 16 4Z" />
+ <path
+ d="M0 0h32v32H0z"
+ data-name="<Transparent Rectangle>"
+ style={{
+ fill: "none",
+ }}
+ />
+ </svg>
+ )
+}
\ No newline at end of file
diff --git a/karavan-designer/src/designer/route/DslElementMoveModal.tsx b/karavan-designer/src/designer/route/element/DslElementMoveModal.tsx
similarity index 94%
rename from karavan-designer/src/designer/route/DslElementMoveModal.tsx
rename to karavan-designer/src/designer/route/element/DslElementMoveModal.tsx
index d1e17ecf..75d4f58f 100644
--- a/karavan-designer/src/designer/route/DslElementMoveModal.tsx
+++ b/karavan-designer/src/designer/route/element/DslElementMoveModal.tsx
@@ -20,10 +20,10 @@ import {
Flex,
Modal, ModalVariant,
} from '@patternfly/react-core';
-import '../karavan.css';
-import {useDesignerStore, useIntegrationStore} from "../DesignerStore";
+import '../../karavan.css';
+import {useDesignerStore, useIntegrationStore} from "../../DesignerStore";
import {shallow} from "zustand/shallow";
-import {useRouteDesignerHook} from "./useRouteDesignerHook";
+import {useRouteDesignerHook} from "../useRouteDesignerHook";
import {CamelDefinitionApiExt} from "karavan-core/lib/api/CamelDefinitionApiExt";
export function DslElementMoveModal() {
diff --git a/karavan-designer/src/designer/route/usePropertiesHook.tsx b/karavan-designer/src/designer/route/usePropertiesHook.tsx
index 87513b18..a9bd9ef9 100644
--- a/karavan-designer/src/designer/route/usePropertiesHook.tsx
+++ b/karavan-designer/src/designer/route/usePropertiesHook.tsx
@@ -126,5 +126,14 @@ export function usePropertiesHook (isRouteDesigner: boolean = true) {
// TODO:
}
- return {cloneElement, onPropertyChange, onParametersChange, onDataFormatChange, onExpressionChange, getInternalComponentName}
+ const convertStep = (step: CamelElement, targetDslName: string ) => {
+ // const clone = CamelUtil.cloneIntegration(integration);
+ // const i = CamelDefinitionApiExt.addStepToIntegration(clone, step, parentId, position);
+ // const selectedStep = step.dslName === 'RouteDefinition' ? (step as RouteDefinition).from : step;
+ // setIntegration(i, false);
+ // setSelectedStep(selectedStep);
+ // setSelectedUuids([selectedStep.uuid]);
+ }
+
+ return {convertStep, cloneElement, onPropertyChange, onParametersChange, onDataFormatChange, onExpressionChange, getInternalComponentName}
}
\ No newline at end of file
diff --git a/karavan-designer/src/designer/route/useRouteDesignerHook.tsx b/karavan-designer/src/designer/route/useRouteDesignerHook.tsx
index f48fc850..b65966a0 100644
--- a/karavan-designer/src/designer/route/useRouteDesignerHook.tsx
+++ b/karavan-designer/src/designer/route/useRouteDesignerHook.tsx
@@ -105,7 +105,8 @@ export function useRouteDesignerHook () {
}
const deleteElement = () => {
- EventBus.sendPosition("clean", new CamelElement(""), undefined, new DOMRect(), new DOMRect(), 0);
+ EventBus.sendPosition("clean", new CamelElement(""), undefined, undefined, new DOMRect(), new DOMRect(), 0);
+ EventBus.sendButtonPosition("clean", '', new CamelElement(""), new DOMRect());
let i = integration;
selectedUuids.forEach(uuidToDelete => {
i = CamelDefinitionApiExt.deleteStepFromIntegration(i, uuidToDelete);
diff --git a/karavan-designer/src/designer/utils/CamelUi.tsx b/karavan-designer/src/designer/utils/CamelUi.tsx
index ec4d5796..83476b1b 100644
--- a/karavan-designer/src/designer/utils/CamelUi.tsx
+++ b/karavan-designer/src/designer/utils/CamelUi.tsx
@@ -166,8 +166,17 @@ export class RouteToCreate {
const INTEGRATION_PATTERNS = 'Integration Patterns';
const connectorNavs = ['routing', "transformation", "error", "configuration", "endpoint", "kamelet", "component"];
+const stepConvertMap = new Map<string, string>([
+ ["SetBodyDefinition", "SetHeaderDefinition"],
+ ["SetHeaderDefinition", "SetBodyDefinition"],
+]);
+
export class CamelUi {
+ static getConvertTargetDsl = (sourceDsl?: string): string | undefined => {
+ return sourceDsl ? stepConvertMap.get(sourceDsl) : undefined;
+ }
+
static createNewInternalRoute = (uri: string): RouteToCreate | undefined => {
const uris = uri.toString().split(":");
const componentName = uris[0];
diff --git a/karavan-designer/src/designer/utils/EventBus.ts b/karavan-designer/src/designer/utils/EventBus.ts
index 0a20d3c6..55906f0f 100644
--- a/karavan-designer/src/designer/utils/EventBus.ts
+++ b/karavan-designer/src/designer/utils/EventBus.ts
@@ -18,10 +18,26 @@ import {Subject} from 'rxjs';
import {CamelElement, Integration} from "karavan-core/lib/model/IntegrationDefinition";
import {v4 as uuidv4} from "uuid";
-const positions = new Subject<DslPosition>();
+export class ButtonPosition {
+ uuid: string = '';
+ nextstep: CamelElement = new CamelElement("");
+ rect: DOMRect = new DOMRect();
+ command: "add" | "delete" | "clean" = "add";
+
+ constructor(command: "add" | "delete" | "clean",
+ uuid: string,
+ nextstep: CamelElement,
+ rect: DOMRect) {
+ this.uuid = uuid;
+ this.command = command;
+ this.nextstep = nextstep;
+ this.rect = rect;
+ }
+}
export class DslPosition {
step: CamelElement = new CamelElement("");
+ prevStep: CamelElement | undefined;
parent: CamelElement | undefined;
inSteps: boolean = false;
isSelected: boolean = false;
@@ -32,6 +48,7 @@ export class DslPosition {
constructor(command: "add" | "delete" | "clean",
step: CamelElement,
+ prevStep: CamelElement | undefined,
parent:CamelElement | undefined,
rect: DOMRect,
headerRect:DOMRect,
@@ -40,6 +57,7 @@ export class DslPosition {
isSelected: boolean = false) {
this.command = command;
this.step = step;
+ this.prevStep = prevStep;
this.parent = parent;
this.rect = rect;
this.headerRect = headerRect;
@@ -85,17 +103,25 @@ export class ToastMessage {
this.variant = variant;
}
}
+const dslPositions = new Subject<DslPosition>();
+const buttonPositions = new Subject<ButtonPosition>();
export const EventBus = {
sendPosition: (command: "add" | "delete" | "clean",
step: CamelElement,
+ prevStep: CamelElement | undefined,
parent: CamelElement | undefined,
rect: DOMRect,
headerRect: DOMRect,
position: number,
inSteps: boolean = false,
- isSelected: boolean = false) => positions.next(new DslPosition(command, step, parent, rect, headerRect, position, inSteps, isSelected)),
- onPosition: () => positions.asObservable(),
+ isSelected: boolean = false) => dslPositions.next(new DslPosition(command, step, prevStep, parent, rect, headerRect, position, inSteps, isSelected)),
+ onPosition: () => dslPositions.asObservable(),
+
+ sendButtonPosition: (command: "add" | "delete" | "clean", uuid: string,
+ nextStep: CamelElement,
+ rect: DOMRect) => buttonPositions.next(new ButtonPosition(command, uuid, nextStep, rect)),
+ onButtonPosition: () => buttonPositions.asObservable(),
sendIntegrationUpdate: (i: Integration, propertyOnly: boolean) => updates.next(new IntegrationUpdate(i, propertyOnly)),
onIntegrationUpdate: () => updates.asObservable(),