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 202aea455f77ff40fcd23707daa362dc818da942
Author: Marat Gubaidullin <ma...@talismancloud.io>
AuthorDate: Thu Feb 1 17:00:03 2024 -0500

    Space #1094
---
 karavan-space/src/designer/DesignerStore.ts        | 15 +++-
 karavan-space/src/designer/KaravanDesigner.tsx     |  9 +-
 .../property/property/ComponentPropertyField.tsx   | 12 ++-
 .../ComponentPropertyPlaceholderDropdown.css       | 13 +++
 .../ComponentPropertyPlaceholderDropdown.tsx       | 96 ++++++++++++++++++----
 karavan-space/src/designer/route/RouteDesigner.tsx |  8 +-
 .../src/designer/utils/InfrastructureAPI.ts        |  5 ++
 karavan-space/src/space/SpacePage.tsx              |  2 +
 8 files changed, 126 insertions(+), 34 deletions(-)

diff --git a/karavan-space/src/designer/DesignerStore.ts 
b/karavan-space/src/designer/DesignerStore.ts
index fe198a7f..8a836e5f 100644
--- a/karavan-space/src/designer/DesignerStore.ts
+++ b/karavan-space/src/designer/DesignerStore.ts
@@ -19,6 +19,7 @@ import {CamelElement, Integration} from 
"karavan-core/lib/model/IntegrationDefin
 import {DslPosition, EventBus} from "./utils/EventBus";
 import {createWithEqualityFn} from "zustand/traditional";
 import {shallow} from "zustand/shallow";
+import {RegistryBeanDefinition} from 
"karavan-core/src/core/model/CamelDefinition";
 
 interface IntegrationState {
     integration: Integration;
@@ -169,7 +170,8 @@ type DesignerState = {
     top: number,
     left: number,
     moveElements: [string | undefined, string | undefined],
-    propertyPlaceholders: string[];
+    propertyPlaceholders: string[],
+    beans: RegistryBeanDefinition[]
 }
 
 const designerState: DesignerState = {
@@ -188,7 +190,8 @@ const designerState: DesignerState = {
     top: 0,
     left: 0,
     moveElements: [undefined, undefined],
-    propertyPlaceholders: []
+    propertyPlaceholders: [],
+    beans: []
 };
 
 type DesignerAction = {
@@ -206,6 +209,7 @@ type DesignerAction = {
     setNotification: (notificationBadge: boolean, notificationMessage: 
[string, string]) => void;
     setMoveElements: (moveElements: [string | undefined, string | undefined]) 
=> void;
     setPropertyPlaceholders: (propertyPlaceholders: string[]) => void;
+    setBeans: (beans: RegistryBeanDefinition[]) => void;
 }
 
 export const useDesignerStore = createWithEqualityFn<DesignerState & 
DesignerAction>((set) => ({
@@ -268,4 +272,11 @@ export const useDesignerStore = 
createWithEqualityFn<DesignerState & DesignerAct
             return state;
         })
     },
+    setBeans: (beans: RegistryBeanDefinition[]) => {
+        set((state: DesignerState) => {
+            state.beans.length = 0;
+            state.beans.push(...beans);
+            return state;
+        })
+    },
 }), shallow)
\ No newline at end of file
diff --git a/karavan-space/src/designer/KaravanDesigner.tsx 
b/karavan-space/src/designer/KaravanDesigner.tsx
index 349ae548..11bf8b6c 100644
--- a/karavan-space/src/designer/KaravanDesigner.tsx
+++ b/karavan-space/src/designer/KaravanDesigner.tsx
@@ -41,11 +41,13 @@ import {BeansDesigner} from "./beans/BeansDesigner";
 import {CodeEditor} from "./editor/CodeEditor";
 import BellIcon from '@patternfly/react-icons/dist/esm/icons/bell-icon';
 import {KameletDesigner} from "./kamelet/KameletDesigner";
+import {RegistryBeanDefinition} from "karavan-core/lib/model/CamelDefinition";
 
 interface Props {
     onSave: (filename: string, yaml: string, propertyOnly: boolean) => void
     onSaveCustomCode: (name: string, code: string) => void
     onGetCustomCode: (name: string, javaType: string) => Promise<string | 
undefined>
+    onSavePropertyPlaceholder: (key: string, value: string) => void
     filename: string
     yaml: string
     dark: boolean
@@ -53,14 +55,15 @@ interface Props {
     showCodeTab: boolean
     tab?: "routes" | "rest" | "beans"
     propertyPlaceholders: string[]
+    beans: RegistryBeanDefinition[]
 }
 
 export function KaravanDesigner(props: Props) {
 
     const [tab, setTab] = useState<string>('routes');
-    const [setDark, hideLogDSL, setHideLogDSL, setSelectedStep, reset, badge, 
message, setPropertyPlaceholders] =
+    const [setDark, hideLogDSL, setHideLogDSL, setSelectedStep, reset, badge, 
message, setPropertyPlaceholders, setBeans] =
         useDesignerStore((s) =>
-        [s.setDark, s.hideLogDSL, s.setHideLogDSL, s.setSelectedStep, s.reset, 
s.notificationBadge, s.notificationMessage, s.setPropertyPlaceholders], shallow)
+        [s.setDark, s.hideLogDSL, s.setHideLogDSL, s.setSelectedStep, s.reset, 
s.notificationBadge, s.notificationMessage, s.setPropertyPlaceholders, 
s.setBeans], shallow)
     const [integration, setIntegration] = useIntegrationStore((s) =>
         [s.integration, s.setIntegration], shallow)
 
@@ -70,6 +73,7 @@ export function KaravanDesigner(props: Props) {
         InfrastructureAPI.setOnSaveCustomCode(props.onSaveCustomCode);
         InfrastructureAPI.setOnGetCustomCode(props.onGetCustomCode);
         InfrastructureAPI.setOnSave(props.onSave);
+        
InfrastructureAPI.setOnSavePropertyPlaceholder(props.onSavePropertyPlaceholder);
 
         setSelectedStep(undefined);
         const i = makeIntegration(props.yaml, props.filename);
@@ -85,6 +89,7 @@ export function KaravanDesigner(props: Props) {
         reset();
         setDark(props.dark);
         setPropertyPlaceholders(props.propertyPlaceholders)
+        setBeans(props.beans)
         setHideLogDSL(props.hideLogDSL === true);
         return () => {
             sub?.unsubscribe();
diff --git 
a/karavan-space/src/designer/property/property/ComponentPropertyField.tsx 
b/karavan-space/src/designer/property/property/ComponentPropertyField.tsx
index a25acafc..1287c18a 100644
--- a/karavan-space/src/designer/property/property/ComponentPropertyField.tsx
+++ b/karavan-space/src/designer/property/property/ComponentPropertyField.tsx
@@ -71,8 +71,8 @@ export function ComponentPropertyField(props: Props) {
     const {onParametersChange, getInternalComponentName} = usePropertiesHook();
 
     const [integration] = useIntegrationStore((state) => [state.integration], 
shallow)
-    const [dark, setSelectedStep, propertyPlaceholders] = useDesignerStore((s) 
=>
-        [s.dark, s.setSelectedStep, s.propertyPlaceholders], shallow)
+    const [dark, setSelectedStep, beans] = useDesignerStore((s) =>
+        [s.dark, s.setSelectedStep, s.beans], shallow)
 
     const [selectStatus, setSelectStatus] = useState<Map<string, boolean>>(new 
Map<string, boolean>());
     const [showEditor, setShowEditor] = useState<boolean>(false);
@@ -82,7 +82,6 @@ export function ComponentPropertyField(props: Props) {
     const [id, setId] = useState<string>(prefix + "-" + props.property.name);
 
     const ref = useRef<any>(null);
-    const [isOpenPlaceholdersDropdown, setOpenPlaceholdersDropdown] = 
useState<boolean>(false);
 
 
     function parametersChanged(parameter: string, value: string | number | 
boolean | any, pathParameter?: boolean, newRoute?: RouteToCreate) {
@@ -100,7 +99,6 @@ export function ComponentPropertyField(props: Props) {
 
     function getSelectBean(property: ComponentProperty, value: any) {
         const selectOptions: JSX.Element[] = [];
-        const beans = CamelUi.getBeans(integration);
         if (beans) {
             selectOptions.push(<SelectOption key={0} value={"Select..."} 
isPlaceholder/>);
             selectOptions.push(...beans.map((bean) => <SelectOption 
key={bean.name} value={beanPrefix + bean.name}
@@ -269,7 +267,7 @@ export function ComponentPropertyField(props: Props) {
                 </Tooltip>
             }
             <InputGroupItem>
-                <ComponentPropertyPlaceholderDropdown property={property}/>
+                <ComponentPropertyPlaceholderDropdown property={property} 
value={value}/>
             </InputGroupItem>
         </InputGroup>
     }
@@ -289,7 +287,7 @@ export function ComponentPropertyField(props: Props) {
                         }}/>
                 </InputGroupItem>
                 <InputGroupItem>
-                    <ComponentPropertyPlaceholderDropdown property={property}/>
+                    <ComponentPropertyPlaceholderDropdown property={property} 
value={value}/>
                 </InputGroupItem>
             </InputGroup>
         )
@@ -347,7 +345,7 @@ export function ComponentPropertyField(props: Props) {
                     />
                 </InputGroupItem>
                 <InputGroupItem>
-                    <ComponentPropertyPlaceholderDropdown property={property}/>
+                    <ComponentPropertyPlaceholderDropdown property={property} 
value={value}/>
                 </InputGroupItem>
             </TextInputGroup>
         )
diff --git 
a/karavan-space/src/designer/property/property/ComponentPropertyPlaceholderDropdown.css
 
b/karavan-space/src/designer/property/property/ComponentPropertyPlaceholderDropdown.css
index 7d5d2d43..43991634 100644
--- 
a/karavan-space/src/designer/property/property/ComponentPropertyPlaceholderDropdown.css
+++ 
b/karavan-space/src/designer/property/property/ComponentPropertyPlaceholderDropdown.css
@@ -19,6 +19,19 @@
     padding-left: 6px;
     padding-right: 6px;
 }
+
+.karavan .properties .property-placeholder-toggle 
.pf-v5-c-button__icon.pf-m-start {
+    margin-inline-end: 0;
+}
+
 .karavan .properties .property-placeholder-toggle 
.pf-v5-c-menu-toggle__controls {
     display: none;
+}
+
+.pf-v5-c-popover .property-placeholder-toggle-form {
+    width: 300px;
+}
+
+.pf-v5-c-popover .property-placeholder-toggle-form .pf-v5-c-form__group {
+    grid-template-columns: 1fr 2fr;
 }
\ No newline at end of file
diff --git 
a/karavan-space/src/designer/property/property/ComponentPropertyPlaceholderDropdown.tsx
 
b/karavan-space/src/designer/property/property/ComponentPropertyPlaceholderDropdown.tsx
index 270f9549..136f90fb 100644
--- 
a/karavan-space/src/designer/property/property/ComponentPropertyPlaceholderDropdown.tsx
+++ 
b/karavan-space/src/designer/property/property/ComponentPropertyPlaceholderDropdown.tsx
@@ -19,7 +19,7 @@ import {
     Dropdown,
     MenuToggleElement,
     MenuToggle,
-    DropdownList, DropdownItem
+    DropdownList, DropdownItem, Popover, Badge, TextVariants, Text, Flex, 
TextInput, FormGroup, Form, Button, FlexItem
 } from '@patternfly/react-core';
 import '../../karavan.css';
 import './ComponentPropertyPlaceholderDropdown.css';
@@ -30,25 +30,99 @@ import {usePropertiesHook} from "../usePropertiesHook";
 import {useDesignerStore} from "../../DesignerStore";
 import {shallow} from "zustand/shallow";
 import EllipsisVIcon from 
"@patternfly/react-icons/dist/esm/icons/ellipsis-v-icon";
-
+import AddIcon from "@patternfly/react-icons/dist/js/icons/plus-icon";
+import {InfrastructureAPI} from "../../utils/InfrastructureAPI";
 
 interface Props {
     property: ComponentProperty,
+    value: any
 }
 
 export function ComponentPropertyPlaceholderDropdown(props: Props) {
 
     const {onParametersChange} = usePropertiesHook();
-    const [propertyPlaceholders] = useDesignerStore((s) => 
[s.propertyPlaceholders], shallow)
+    const [propertyPlaceholders, setPropertyPlaceholders] = 
useDesignerStore((s) =>
+        [s.propertyPlaceholders, s.setPropertyPlaceholders], shallow)
     const [isOpenPlaceholdersDropdown, setOpenPlaceholdersDropdown] = 
useState<boolean>(false);
+    const [propValue, setPropValue] = useState<string>('');
+    const [isVisible, setIsVisible] = React.useState(false);
+
+    const {property, value} = props;
+    const valueIsPlaceholder: boolean = value && 
value.toString().startsWith('{{') && value.toString().endsWith('}}');
+    const placeholderValue = valueIsPlaceholder ? 
value.toString().replace('{{', '').replace('}}', '') : undefined;
+    const showAddButton = valueIsPlaceholder && 
!propertyPlaceholders.includes(placeholderValue);
+    const popoverId = "popover-selector-" + property.name;
 
     function parametersChanged(parameter: string, value: string | number | 
boolean | any, pathParameter?: boolean, newRoute?: RouteToCreate) {
         onParametersChange(parameter, value, pathParameter, newRoute);
     }
 
-    const property: ComponentProperty = props.property;
+    function onMenuToggleClick() {
+        if (!showAddButton) {
+            setOpenPlaceholdersDropdown(!isOpenPlaceholdersDropdown)
+        }
+    }
+
+    function saveProperty() {
+        InfrastructureAPI.onSavePropertyPlaceholder(placeholderValue, 
propValue);
+        setIsVisible(false);
+        const p = [...propertyPlaceholders]
+        p.push(placeholderValue);
+        setPropertyPlaceholders(p);
+    }
+
+    function getPopover() {
+        return (
+            <Popover
+                isVisible={isVisible}
+                shouldOpen={(_event, _fn) => setIsVisible(true)}
+                shouldClose={(_event, _fn) => setIsVisible(false)}
+                aria-label="Add property"
+                headerContent={"Add property"}
+                bodyContent={
+                    <Form isHorizontal 
className="property-placeholder-toggle-form" autoComplete="off">
+                        <FormGroup isInline label="Property" isRequired 
fieldId="property">
+                            <TextInput id="property" readOnly 
value={placeholderValue}/>
+                        </FormGroup>
+                        <FormGroup isInline label="Value" isRequired 
fieldId="value">
+                            <TextInput id="value" isRequired value={propValue}
+                                       onChange={(_, value) => 
setPropValue(value)}/>
+                        </FormGroup>
+                    </Form>
+                }
+                footerContent={
+                    <Flex>
+                        <FlexItem align={{default: "alignRight"}}>
+                            <Button
+                                onClick={() => saveProperty()}>
+                                Save
+                            </Button>
+                        </FlexItem>
+                    </Flex>
+                }
+                triggerRef={() => document.getElementById(popoverId) as 
HTMLButtonElement}
+            />
+        )
+    }
+
+    function getToggle(toggleRef: React.Ref<MenuToggleElement>) {
+        return (
+            <MenuToggle className="property-placeholder-toggle"
+                        id={popoverId}
+                        ref={toggleRef}
+                        aria-label="placeholder menu"
+                        variant="default"
+                        onClick={() => onMenuToggleClick()}
+                        isExpanded={isOpenPlaceholdersDropdown}
+            >
+                {showAddButton ? <AddIcon/> : <EllipsisVIcon/>}
+                {showAddButton && getPopover()}
+            </MenuToggle>
+        )
+    }
+
     return (
-        propertyPlaceholders && propertyPlaceholders.length > 0 ?
+        (propertyPlaceholders && propertyPlaceholders.length > 0 ) || 
showAddButton ?
             <Dropdown
                 popperProps={{position: "end"}}
                 isOpen={isOpenPlaceholdersDropdown}
@@ -57,17 +131,7 @@ export function ComponentPropertyPlaceholderDropdown(props: 
Props) {
                     setOpenPlaceholdersDropdown(false);
                 }}
                 onOpenChange={(isOpen: boolean) => 
setOpenPlaceholdersDropdown(isOpen)}
-                toggle={(toggleRef: React.Ref<MenuToggleElement>) => (
-                    <MenuToggle className="property-placeholder-toggle"
-                                ref={toggleRef}
-                                aria-label="placeholder menu"
-                                variant="default"
-                                onClick={() => 
setOpenPlaceholdersDropdown(!isOpenPlaceholdersDropdown)}
-                                isExpanded={isOpenPlaceholdersDropdown}
-                    >
-                        <EllipsisVIcon/>
-                    </MenuToggle>
-                )}
+                toggle={(toggleRef: React.Ref<MenuToggleElement>) => 
getToggle(toggleRef)}
                 shouldFocusToggleOnSelect
             >
                 <DropdownList>
diff --git a/karavan-space/src/designer/route/RouteDesigner.tsx 
b/karavan-space/src/designer/route/RouteDesigner.tsx
index c2ebf292..7a36f814 100644
--- a/karavan-space/src/designer/route/RouteDesigner.tsx
+++ b/karavan-space/src/designer/route/RouteDesigner.tsx
@@ -40,7 +40,7 @@ import {DslElementMoveModal} from 
"./element/DslElementMoveModal";
 
 export function RouteDesigner() {
 
-    const {openSelector, createRouteConfiguration, onCommand, handleKeyDown, 
handleKeyUp, unselectElement, onDslSelect,
+    const {openSelector, createRouteConfiguration, onCommand, unselectElement, 
onDslSelect,
         isSourceKamelet, isActionKamelet, isKamelet, isSinkKamelet} = 
useRouteDesignerHook();
 
     const [integration] = useIntegrationStore((state) => [state.integration], 
shallow)
@@ -72,12 +72,9 @@ export function RouteDesigner() {
     const flowRef = useRef<HTMLDivElement | null>(null);
 
     useEffect(()=> {
-        // window.addEventListener('resize', changeGraphSize);
         const interval = setInterval(() => {
             changeGraphSize();
         }, 500);
-        window.addEventListener('keydown', handleKeyDown);
-        window.addEventListener('keyup', handleKeyUp);
         const commandSub = EventBus.onCommand()?.subscribe((command: Command) 
=> onCommand(command, printerRef));
         if (flowRef.current === null) {
             clearSteps();
@@ -86,9 +83,6 @@ export function RouteDesigner() {
         }
         return ()=> {
             clearInterval(interval)
-            // window.removeEventListener('resize', changeGraphSize);
-            window.removeEventListener('keydown', handleKeyDown);
-            window.removeEventListener('keyup', handleKeyUp);
             commandSub?.unsubscribe();
         }
     }, [showSelector, integration])
diff --git a/karavan-space/src/designer/utils/InfrastructureAPI.ts 
b/karavan-space/src/designer/utils/InfrastructureAPI.ts
index f0d8faef..c2d5ca7c 100644
--- a/karavan-space/src/designer/utils/InfrastructureAPI.ts
+++ b/karavan-space/src/designer/utils/InfrastructureAPI.ts
@@ -20,6 +20,7 @@ export class InfrastructureAPI {
     static onGetCustomCode: (name: string, javaType: string) => Promise<string 
| undefined>;
     static onSaveCustomCode: (name: string, code: string) => void;
     static onSave: (filename: string, yaml: string, propertyOnly: boolean) => 
void;
+    static onSavePropertyPlaceholder: (key: string, value: string) => void;
 
     static setOnGetCustomCode(onGetCustomCode: (name: string, javaType: 
string) => Promise<string | undefined>){
         this.onGetCustomCode = onGetCustomCode
@@ -33,6 +34,10 @@ export class InfrastructureAPI {
         this.onSave = onSave
     }
 
+    static setOnSavePropertyPlaceholder(onSavePropertyPlaceholder:(key: 
string, value: string) => void){
+        this.onSavePropertyPlaceholder = onSavePropertyPlaceholder
+    }
+
     // Kubernetes/Docker API
     static infrastructure: 'kubernetes' | 'docker' | 'local' = 'local';
     static configMaps: string[] = [];
diff --git a/karavan-space/src/space/SpacePage.tsx 
b/karavan-space/src/space/SpacePage.tsx
index e878383a..f3004565 100644
--- a/karavan-space/src/space/SpacePage.tsx
+++ b/karavan-space/src/space/SpacePage.tsx
@@ -109,6 +109,8 @@ export class SpacePage extends React.Component<Props, 
State> {
                     console.log(name1, code)
                 }}
                 propertyPlaceholders={[]}
+                beans={[]}
+                onSavePropertyPlaceholder={(key, value) => {}}
             />
         )
     }

Reply via email to