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 2024/01/31 22:02:27 UTC

(camel-karavan) 03/04: #1091 in Designer

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 e1119cbe5f0703dea77c5c88fd01ef08ec7fb836
Author: Marat Gubaidullin <ma...@talismancloud.io>
AuthorDate: Wed Jan 31 16:34:01 2024 -0500

    #1091 in Designer
---
 karavan-designer/src/DesignerPage.tsx              |   4 +
 karavan-designer/src/designer/DesignerStore.ts     |  16 ++-
 karavan-designer/src/designer/KaravanDesigner.tsx  |   8 +-
 .../src/designer/property/DslProperties.css        |   7 +-
 .../src/designer/property/DslProperties.tsx        |  10 +-
 .../property/property/ComponentPropertyField.tsx   |  49 ++++++---
 .../ComponentPropertyPlaceholderDropdown.css       |  24 +++++
 .../ComponentPropertyPlaceholderDropdown.tsx       |  81 +++++++++++++++
 .../property/property/DslPropertyField.tsx         |   1 +
 karavan-space/src/designer/DesignerStore.ts        |  16 ++-
 karavan-space/src/designer/KaravanDesigner.tsx     |   8 +-
 .../src/designer/property/DslProperties.css        |  12 +++
 .../src/designer/property/DslProperties.tsx        |  16 +--
 .../property/property/ComponentPropertyField.tsx   | 113 ++++++++++++++-------
 .../ComponentPropertyPlaceholderDropdown.css       |  24 +++++
 .../ComponentPropertyPlaceholderDropdown.tsx       |  81 +++++++++++++++
 .../property/property/DslPropertyField.tsx         |  70 ++++++-------
 .../src/designer/property/property/ModalEditor.tsx |   5 +-
 karavan-space/src/space/SpacePage.tsx              |   1 +
 .../webui/src/designer/property/DslProperties.css  |   7 ++
 .../webui/src/designer/property/DslProperties.tsx  |   8 +-
 .../property/property/ComponentPropertyField.tsx   |  74 +++++++++-----
 .../property/property/DslPropertyField.tsx         |  69 ++++++-------
 .../src/designer/property/property/ModalEditor.tsx |   5 +-
 24 files changed, 532 insertions(+), 177 deletions(-)

diff --git a/karavan-designer/src/DesignerPage.tsx b/karavan-designer/src/DesignerPage.tsx
index 909b15c5..125e3626 100644
--- a/karavan-designer/src/DesignerPage.tsx
+++ b/karavan-designer/src/DesignerPage.tsx
@@ -77,6 +77,10 @@ export const DesignerPage = (props: Props) => {
                 onSaveCustomCode={(name1, code) => {
                     console.log(name1, code)
                 }}
+                propertyPlaceholders={[
+                    "timer.delay",
+                    "sql.query"
+                ]}
             />
         )
     }
diff --git a/karavan-designer/src/designer/DesignerStore.ts b/karavan-designer/src/designer/DesignerStore.ts
index 407713a7..fe198a7f 100644
--- a/karavan-designer/src/designer/DesignerStore.ts
+++ b/karavan-designer/src/designer/DesignerStore.ts
@@ -168,7 +168,8 @@ type DesignerState = {
     height: number,
     top: number,
     left: number,
-    moveElements: [string | undefined, string | undefined]
+    moveElements: [string | undefined, string | undefined],
+    propertyPlaceholders: string[];
 }
 
 const designerState: DesignerState = {
@@ -186,7 +187,8 @@ const designerState: DesignerState = {
     height: 0,
     top: 0,
     left: 0,
-    moveElements: [undefined, undefined]
+    moveElements: [undefined, undefined],
+    propertyPlaceholders: []
 };
 
 type DesignerAction = {
@@ -203,6 +205,7 @@ type DesignerAction = {
     reset: () => void;
     setNotification: (notificationBadge: boolean, notificationMessage: [string, string]) => void;
     setMoveElements: (moveElements: [string | undefined, string | undefined]) => void;
+    setPropertyPlaceholders: (propertyPlaceholders: string[]) => void;
 }
 
 export const useDesignerStore = createWithEqualityFn<DesignerState & DesignerAction>((set) => ({
@@ -257,5 +260,12 @@ export const useDesignerStore = createWithEqualityFn<DesignerState & DesignerAct
     },
     setMoveElements: (moveElements: [string | undefined, string | undefined]) => {
         set({moveElements: moveElements})
-    }
+    },
+    setPropertyPlaceholders: (propertyPlaceholders: string[]) => {
+        set((state: DesignerState) => {
+            state.propertyPlaceholders.length = 0;
+            state.propertyPlaceholders.push(...propertyPlaceholders);
+            return state;
+        })
+    },
 }), shallow)
\ No newline at end of file
diff --git a/karavan-designer/src/designer/KaravanDesigner.tsx b/karavan-designer/src/designer/KaravanDesigner.tsx
index 626f984b..349ae548 100644
--- a/karavan-designer/src/designer/KaravanDesigner.tsx
+++ b/karavan-designer/src/designer/KaravanDesigner.tsx
@@ -41,7 +41,6 @@ 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 {v4 as uuidv4} from "uuid";
 
 interface Props {
     onSave: (filename: string, yaml: string, propertyOnly: boolean) => void
@@ -53,13 +52,15 @@ interface Props {
     hideLogDSL?: boolean
     showCodeTab: boolean
     tab?: "routes" | "rest" | "beans"
+    propertyPlaceholders: string[]
 }
 
 export function KaravanDesigner(props: Props) {
 
     const [tab, setTab] = useState<string>('routes');
-    const [setDark, hideLogDSL, setHideLogDSL, setSelectedStep, reset, badge, message] = useDesignerStore((s) =>
-        [s.setDark, s.hideLogDSL, s.setHideLogDSL, s.setSelectedStep, s.reset, s.notificationBadge, s.notificationMessage], shallow)
+    const [setDark, hideLogDSL, setHideLogDSL, setSelectedStep, reset, badge, message, setPropertyPlaceholders] =
+        useDesignerStore((s) =>
+        [s.setDark, s.hideLogDSL, s.setHideLogDSL, s.setSelectedStep, s.reset, s.notificationBadge, s.notificationMessage, s.setPropertyPlaceholders], shallow)
     const [integration, setIntegration] = useIntegrationStore((s) =>
         [s.integration, s.setIntegration], shallow)
 
@@ -83,6 +84,7 @@ export function KaravanDesigner(props: Props) {
         setTab(designerTab || 'routes')
         reset();
         setDark(props.dark);
+        setPropertyPlaceholders(props.propertyPlaceholders)
         setHideLogDSL(props.hideLogDSL === true);
         return () => {
             sub?.unsubscribe();
diff --git a/karavan-designer/src/designer/property/DslProperties.css b/karavan-designer/src/designer/property/DslProperties.css
index dd7cb199..43562bcb 100644
--- a/karavan-designer/src/designer/property/DslProperties.css
+++ b/karavan-designer/src/designer/property/DslProperties.css
@@ -248,10 +248,15 @@
     min-height: 6px;
 }
 
+.karavan .properties .header-menu-toggle {
+    padding-left: 6px;
+    padding-right: 0;
+}
+
 .karavan .properties .component-headers {
     margin-left: 24px;
 }
 
 .karavan .properties .component-headers .pf-v5-c-clipboard-copy.pf-m-inline {
     background-color: transparent;
-}
\ No newline at end of file
+}
diff --git a/karavan-designer/src/designer/property/DslProperties.tsx b/karavan-designer/src/designer/property/DslProperties.tsx
index 6169d656..ed56d2eb 100644
--- a/karavan-designer/src/designer/property/DslProperties.tsx
+++ b/karavan-designer/src/designer/property/DslProperties.tsx
@@ -27,7 +27,7 @@ import {
     MenuToggleElement,
     MenuToggle,
     DropdownList,
-    DropdownItem, Label, Flex, LabelGroup, Popover, FlexItem, Badge, ClipboardCopy, ClipboardCopyAction,
+    DropdownItem, Flex, Popover, FlexItem, Badge, ClipboardCopy,
 } from '@patternfly/react-core';
 import '../karavan.css';
 import './DslProperties.css';
@@ -85,18 +85,16 @@ export function DslProperties(props: Props) {
         const showMenu = hasSteps || targetDsl !== undefined;
         return showMenu ?
             <Dropdown
-                style={{inset: "0px auto auto -70px important!"}}
-                className={"xxx"}
+                popperProps={{position: "end"}}
                 isOpen={isMenuOpen}
                 onSelect={() => {
                 }}
                 onOpenChange={(isOpen: boolean) => setMenuOpen(isOpen)}
                 toggle={(toggleRef: React.Ref<MenuToggleElement>) => (
                     <MenuToggle
-                        style={{width: "240px", display: "flex", flexDirection: "row", justifyContent: "end"}}
-                        className={"zzzz"}
+                        className="header-menu-toggle"
                         ref={toggleRef}
-                        aria-label="kebab dropdown toggle"
+                        aria-label="menu"
                         variant="plain"
                         onClick={() => setMenuOpen(!isMenuOpen)}
                         isExpanded={isMenuOpen}
diff --git a/karavan-designer/src/designer/property/property/ComponentPropertyField.tsx b/karavan-designer/src/designer/property/property/ComponentPropertyField.tsx
index ae63e323..a25acafc 100644
--- a/karavan-designer/src/designer/property/property/ComponentPropertyField.tsx
+++ b/karavan-designer/src/designer/property/property/ComponentPropertyField.tsx
@@ -21,10 +21,13 @@ import {
     Popover,
     Switch,
     InputGroup,
-    TextArea,
     Tooltip,
     Button,
-    capitalize, InputGroupItem, TextInputGroup, TextVariants, Text
+    capitalize,
+    InputGroupItem,
+    TextInputGroup,
+    TextVariants,
+    Text,
 } from '@patternfly/react-core';
 import {
     Select,
@@ -51,6 +54,7 @@ import {shallow} from "zustand/shallow";
 import {KubernetesIcon} from "../../icons/ComponentIcons";
 import EditorIcon from "@patternfly/react-icons/dist/js/icons/code-icon";
 import {ModalEditor} from "./ModalEditor";
+import {ComponentPropertyPlaceholderDropdown} from "./ComponentPropertyPlaceholderDropdown";
 
 const prefix = "parameters";
 const beanPrefix = "#bean:";
@@ -67,16 +71,18 @@ export function ComponentPropertyField(props: Props) {
     const {onParametersChange, getInternalComponentName} = usePropertiesHook();
 
     const [integration] = useIntegrationStore((state) => [state.integration], shallow)
-    const [dark, setSelectedStep] = useDesignerStore((s) => [s.dark, s.setSelectedStep], shallow)
+    const [dark, setSelectedStep, propertyPlaceholders] = useDesignerStore((s) =>
+        [s.dark, s.setSelectedStep, s.propertyPlaceholders], shallow)
 
     const [selectStatus, setSelectStatus] = useState<Map<string, boolean>>(new Map<string, boolean>());
     const [showEditor, setShowEditor] = useState<boolean>(false);
     const [showPassword, setShowPassword] = useState<boolean>(false);
     const [infrastructureSelector, setInfrastructureSelector] = useState<boolean>(false);
     const [infrastructureSelectorProperty, setInfrastructureSelectorProperty] = useState<string | undefined>(undefined);
-
     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) {
@@ -262,20 +268,30 @@ export function ComponentPropertyField(props: Props) {
                     </Button>
                 </Tooltip>
             }
+            <InputGroupItem>
+                <ComponentPropertyPlaceholderDropdown property={property}/>
+            </InputGroupItem>
         </InputGroup>
     }
 
-    function getTextInput(property: ComponentProperty, value: any) {
+    function getSpecialStringInput(property: ComponentProperty, value: any) {
         return (
-            <TextInput
-                className="text-field" isRequired
-                type={(property.secret ? "password" : "text")}
-                id={id} name={id}
-                value={value !== undefined ? value : property.defaultValue}
-                customIcon={<Text component={TextVariants.p}>{property.type}</Text>}
-                onChange={(_, value) => {
-                    parametersChanged(property.name, value, property.kind === 'path')
-                }}/>
+            <InputGroup>
+                <InputGroupItem isFill>
+                    <TextInput
+                        className="text-field" isRequired
+                        type={(property.secret ? "password" : "text")}
+                        id={id} name={id}
+                        value={value !== undefined ? value : property.defaultValue}
+                        customIcon={<Text component={TextVariants.p}>{property.type}</Text>}
+                        onChange={(_, value) => {
+                            parametersChanged(property.name, value, property.kind === 'path')
+                        }}/>
+                </InputGroupItem>
+                <InputGroupItem>
+                    <ComponentPropertyPlaceholderDropdown property={property}/>
+                </InputGroupItem>
+            </InputGroup>
         )
     }
 
@@ -330,6 +346,9 @@ export function ComponentPropertyField(props: Props) {
                         onChange={(_, v) => parametersChanged(property.name, v)}
                     />
                 </InputGroupItem>
+                <InputGroupItem>
+                    <ComponentPropertyPlaceholderDropdown property={property}/>
+                </InputGroupItem>
             </TextInputGroup>
         )
     }
@@ -362,7 +381,7 @@ export function ComponentPropertyField(props: Props) {
             {property.type === 'string' && property.enum === undefined && !canBeInternalUri(property)
                 && getStringInput(property, value)}
             {['duration', 'integer', 'int', 'number'].includes(property.type) && property.enum === undefined && !canBeInternalUri(property)
-                && getTextInput(property, value)}
+                && getSpecialStringInput(property, value)}
             {['object'].includes(property.type) && !property.enum
                 && getSelectBean(property, value)}
             {['string', 'object'].includes(property.type) && property.enum
diff --git a/karavan-designer/src/designer/property/property/ComponentPropertyPlaceholderDropdown.css b/karavan-designer/src/designer/property/property/ComponentPropertyPlaceholderDropdown.css
new file mode 100644
index 00000000..7d5d2d43
--- /dev/null
+++ b/karavan-designer/src/designer/property/property/ComponentPropertyPlaceholderDropdown.css
@@ -0,0 +1,24 @@
+/*
+ * 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 .property-placeholder-toggle {
+    padding-left: 6px;
+    padding-right: 6px;
+}
+.karavan .properties .property-placeholder-toggle .pf-v5-c-menu-toggle__controls {
+    display: none;
+}
\ No newline at end of file
diff --git a/karavan-designer/src/designer/property/property/ComponentPropertyPlaceholderDropdown.tsx b/karavan-designer/src/designer/property/property/ComponentPropertyPlaceholderDropdown.tsx
new file mode 100644
index 00000000..270f9549
--- /dev/null
+++ b/karavan-designer/src/designer/property/property/ComponentPropertyPlaceholderDropdown.tsx
@@ -0,0 +1,81 @@
+/*
+ * 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, {useState} from 'react';
+import {
+    Dropdown,
+    MenuToggleElement,
+    MenuToggle,
+    DropdownList, DropdownItem
+} from '@patternfly/react-core';
+import '../../karavan.css';
+import './ComponentPropertyPlaceholderDropdown.css';
+import "@patternfly/patternfly/patternfly.css";
+import {ComponentProperty} from "karavan-core/lib/model/ComponentModels";
+import {RouteToCreate} from "../../utils/CamelUi";
+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";
+
+
+interface Props {
+    property: ComponentProperty,
+}
+
+export function ComponentPropertyPlaceholderDropdown(props: Props) {
+
+    const {onParametersChange} = usePropertiesHook();
+    const [propertyPlaceholders] = useDesignerStore((s) => [s.propertyPlaceholders], shallow)
+    const [isOpenPlaceholdersDropdown, setOpenPlaceholdersDropdown] = useState<boolean>(false);
+
+    function parametersChanged(parameter: string, value: string | number | boolean | any, pathParameter?: boolean, newRoute?: RouteToCreate) {
+        onParametersChange(parameter, value, pathParameter, newRoute);
+    }
+
+    const property: ComponentProperty = props.property;
+    return (
+        propertyPlaceholders && propertyPlaceholders.length > 0 ?
+            <Dropdown
+                popperProps={{position: "end"}}
+                isOpen={isOpenPlaceholdersDropdown}
+                onSelect={(_, value) => {
+                    parametersChanged(property.name, `{{${value}}}`, property.kind === 'path')
+                    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>
+                )}
+                shouldFocusToggleOnSelect
+            >
+                <DropdownList>
+                    {propertyPlaceholders.map((pp, index) =>
+                        <DropdownItem value={pp} key={index}>{pp}</DropdownItem>
+                    )}
+                </DropdownList>
+            </Dropdown>
+            : <></>
+    )
+}
diff --git a/karavan-designer/src/designer/property/property/DslPropertyField.tsx b/karavan-designer/src/designer/property/property/DslPropertyField.tsx
index aa093618..59066667 100644
--- a/karavan-designer/src/designer/property/property/DslPropertyField.tsx
+++ b/karavan-designer/src/designer/property/property/DslPropertyField.tsx
@@ -74,6 +74,7 @@ import {
 import {TemplateApi} from "karavan-core/lib/api/TemplateApi";
 import {KubernetesIcon} from "../../icons/ComponentIcons";
 import {BeanProperties} from "./BeanProperties";
+import {ComponentPropertyPlaceholderDropdown} from "./ComponentPropertyPlaceholderDropdown";
 
 interface Props {
     property: PropertyMeta,
diff --git a/karavan-space/src/designer/DesignerStore.ts b/karavan-space/src/designer/DesignerStore.ts
index 407713a7..fe198a7f 100644
--- a/karavan-space/src/designer/DesignerStore.ts
+++ b/karavan-space/src/designer/DesignerStore.ts
@@ -168,7 +168,8 @@ type DesignerState = {
     height: number,
     top: number,
     left: number,
-    moveElements: [string | undefined, string | undefined]
+    moveElements: [string | undefined, string | undefined],
+    propertyPlaceholders: string[];
 }
 
 const designerState: DesignerState = {
@@ -186,7 +187,8 @@ const designerState: DesignerState = {
     height: 0,
     top: 0,
     left: 0,
-    moveElements: [undefined, undefined]
+    moveElements: [undefined, undefined],
+    propertyPlaceholders: []
 };
 
 type DesignerAction = {
@@ -203,6 +205,7 @@ type DesignerAction = {
     reset: () => void;
     setNotification: (notificationBadge: boolean, notificationMessage: [string, string]) => void;
     setMoveElements: (moveElements: [string | undefined, string | undefined]) => void;
+    setPropertyPlaceholders: (propertyPlaceholders: string[]) => void;
 }
 
 export const useDesignerStore = createWithEqualityFn<DesignerState & DesignerAction>((set) => ({
@@ -257,5 +260,12 @@ export const useDesignerStore = createWithEqualityFn<DesignerState & DesignerAct
     },
     setMoveElements: (moveElements: [string | undefined, string | undefined]) => {
         set({moveElements: moveElements})
-    }
+    },
+    setPropertyPlaceholders: (propertyPlaceholders: string[]) => {
+        set((state: DesignerState) => {
+            state.propertyPlaceholders.length = 0;
+            state.propertyPlaceholders.push(...propertyPlaceholders);
+            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 626f984b..349ae548 100644
--- a/karavan-space/src/designer/KaravanDesigner.tsx
+++ b/karavan-space/src/designer/KaravanDesigner.tsx
@@ -41,7 +41,6 @@ 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 {v4 as uuidv4} from "uuid";
 
 interface Props {
     onSave: (filename: string, yaml: string, propertyOnly: boolean) => void
@@ -53,13 +52,15 @@ interface Props {
     hideLogDSL?: boolean
     showCodeTab: boolean
     tab?: "routes" | "rest" | "beans"
+    propertyPlaceholders: string[]
 }
 
 export function KaravanDesigner(props: Props) {
 
     const [tab, setTab] = useState<string>('routes');
-    const [setDark, hideLogDSL, setHideLogDSL, setSelectedStep, reset, badge, message] = useDesignerStore((s) =>
-        [s.setDark, s.hideLogDSL, s.setHideLogDSL, s.setSelectedStep, s.reset, s.notificationBadge, s.notificationMessage], shallow)
+    const [setDark, hideLogDSL, setHideLogDSL, setSelectedStep, reset, badge, message, setPropertyPlaceholders] =
+        useDesignerStore((s) =>
+        [s.setDark, s.hideLogDSL, s.setHideLogDSL, s.setSelectedStep, s.reset, s.notificationBadge, s.notificationMessage, s.setPropertyPlaceholders], shallow)
     const [integration, setIntegration] = useIntegrationStore((s) =>
         [s.integration, s.setIntegration], shallow)
 
@@ -83,6 +84,7 @@ export function KaravanDesigner(props: Props) {
         setTab(designerTab || 'routes')
         reset();
         setDark(props.dark);
+        setPropertyPlaceholders(props.propertyPlaceholders)
         setHideLogDSL(props.hideLogDSL === true);
         return () => {
             sub?.unsubscribe();
diff --git a/karavan-space/src/designer/property/DslProperties.css b/karavan-space/src/designer/property/DslProperties.css
index c681098c..43562bcb 100644
--- a/karavan-space/src/designer/property/DslProperties.css
+++ b/karavan-space/src/designer/property/DslProperties.css
@@ -248,3 +248,15 @@
     min-height: 6px;
 }
 
+.karavan .properties .header-menu-toggle {
+    padding-left: 6px;
+    padding-right: 0;
+}
+
+.karavan .properties .component-headers {
+    margin-left: 24px;
+}
+
+.karavan .properties .component-headers .pf-v5-c-clipboard-copy.pf-m-inline {
+    background-color: transparent;
+}
diff --git a/karavan-space/src/designer/property/DslProperties.tsx b/karavan-space/src/designer/property/DslProperties.tsx
index 9325ee05..ed56d2eb 100644
--- a/karavan-space/src/designer/property/DslProperties.tsx
+++ b/karavan-space/src/designer/property/DslProperties.tsx
@@ -27,7 +27,7 @@ import {
     MenuToggleElement,
     MenuToggle,
     DropdownList,
-    DropdownItem, Label, Flex, LabelGroup, Popover, FlexItem, Badge,
+    DropdownItem, Flex, Popover, FlexItem, Badge, ClipboardCopy,
 } from '@patternfly/react-core';
 import '../karavan.css';
 import './DslProperties.css';
@@ -85,18 +85,16 @@ export function DslProperties(props: Props) {
         const showMenu = hasSteps || targetDsl !== undefined;
         return showMenu ?
             <Dropdown
-                style={{inset: "0px auto auto -70px important!"}}
-                className={"xxx"}
+                popperProps={{position: "end"}}
                 isOpen={isMenuOpen}
                 onSelect={() => {
                 }}
                 onOpenChange={(isOpen: boolean) => setMenuOpen(isOpen)}
                 toggle={(toggleRef: React.Ref<MenuToggleElement>) => (
                     <MenuToggle
-                        style={{width: "240px", display: "flex", flexDirection: "row", justifyContent: "end"}}
-                        className={"zzzz"}
+                        className="header-menu-toggle"
                         ref={toggleRef}
-                        aria-label="kebab dropdown toggle"
+                        aria-label="menu"
                         variant="plain"
                         onClick={() => setMenuOpen(!isMenuOpen)}
                         isExpanded={isMenuOpen}
@@ -166,11 +164,13 @@ export function DslProperties(props: Props) {
                     <ExpandableSection toggleText='Headers'
                                        onToggle={(_event, isExpanded) => setIsDescriptionExpanded(!isDescriptionExpanded)}
                                        isExpanded={isDescriptionExpanded}>
-                        <Flex direction={{default:"column"}}>
+                        <Flex className='component-headers' direction={{default:"column"}}>
                             {headers.filter((header) => groups.includes(header.group))
                                 .map((header, index, array) =>
                                     <Flex key={index}>
-                                        <Text style={{marginLeft: "26px"}} component={TextVariants.p}>{header.name}</Text>
+                                        <ClipboardCopy key={index} hoverTip="Copy" clickTip="Copied" variant="inline-compact" isCode>
+                                            {header.name}
+                                        </ClipboardCopy>
                                         <FlexItem align={{default: 'alignRight'}}>
                                             <Popover
                                                 position={"left"}
diff --git a/karavan-space/src/designer/property/property/ComponentPropertyField.tsx b/karavan-space/src/designer/property/property/ComponentPropertyField.tsx
index b98b411d..a25acafc 100644
--- a/karavan-space/src/designer/property/property/ComponentPropertyField.tsx
+++ b/karavan-space/src/designer/property/property/ComponentPropertyField.tsx
@@ -21,10 +21,13 @@ import {
     Popover,
     Switch,
     InputGroup,
-    TextArea,
     Tooltip,
     Button,
-    capitalize, InputGroupItem
+    capitalize,
+    InputGroupItem,
+    TextInputGroup,
+    TextVariants,
+    Text,
 } from '@patternfly/react-core';
 import {
     Select,
@@ -39,8 +42,6 @@ import {ComponentProperty} from "karavan-core/lib/model/ComponentModels";
 import {CamelUi, RouteToCreate} from "../../utils/CamelUi";
 import {CamelElement} from "karavan-core/lib/model/IntegrationDefinition";
 import {ToDefinition} from "karavan-core/lib/model/CamelDefinition";
-import CompressIcon from "@patternfly/react-icons/dist/js/icons/compress-icon";
-import ExpandIcon from "@patternfly/react-icons/dist/js/icons/expand-icon";
 import {InfrastructureSelector} from "./InfrastructureSelector";
 import {InfrastructureAPI} from "../../utils/InfrastructureAPI";
 import DockerIcon from "@patternfly/react-icons/dist/js/icons/docker-icon";
@@ -48,9 +49,12 @@ import ShowIcon from "@patternfly/react-icons/dist/js/icons/eye-icon";
 import HideIcon from "@patternfly/react-icons/dist/js/icons/eye-slash-icon";
 import PlusIcon from "@patternfly/react-icons/dist/esm/icons/plus-icon";
 import {usePropertiesHook} from "../usePropertiesHook";
-import {useIntegrationStore} from "../../DesignerStore";
+import {useDesignerStore, useIntegrationStore} from "../../DesignerStore";
 import {shallow} from "zustand/shallow";
 import {KubernetesIcon} from "../../icons/ComponentIcons";
+import EditorIcon from "@patternfly/react-icons/dist/js/icons/code-icon";
+import {ModalEditor} from "./ModalEditor";
+import {ComponentPropertyPlaceholderDropdown} from "./ComponentPropertyPlaceholderDropdown";
 
 const prefix = "parameters";
 const beanPrefix = "#bean:";
@@ -67,15 +71,18 @@ 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 [selectStatus, setSelectStatus] = useState<Map<string, boolean>>(new Map<string, boolean>());
     const [showEditor, setShowEditor] = useState<boolean>(false);
     const [showPassword, setShowPassword] = useState<boolean>(false);
     const [infrastructureSelector, setInfrastructureSelector] = useState<boolean>(false);
     const [infrastructureSelectorProperty, setInfrastructureSelectorProperty] = useState<string | undefined>(undefined);
-
     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) {
@@ -170,7 +177,7 @@ export function ComponentPropertyField(props: Props) {
                     <Button isDisabled={value === undefined} variant="control" onClick={e => {
                         if (value) {
                             const newRoute = !internalUris.includes(value.toString())
-                                ? CamelUi.createNewInternalRoute(componentName.concat(...':',value.toString()))
+                                ? CamelUi.createNewInternalRoute(componentName.concat(...':', value.toString()))
                                 : undefined;
                             parametersChanged(property.name, value, property.kind === 'path', newRoute);
                         }
@@ -234,20 +241,26 @@ export function ComponentPropertyField(props: Props) {
                            id={id} name={id}
                            value={value !== undefined ? value : property.defaultValue}
                            onChange={(e, value) => parametersChanged(property.name, value, property.kind === 'path')}/>}
-            {showEditor && !property.secret &&
-                <TextArea autoResize={true} ref={ref}
-                          className="text-field" isRequired
-                          type="text"
-                          id={id} name={id}
-                          value={value !== undefined ? value : property.defaultValue}
-                          onChange={(e, value) => parametersChanged(property.name, value, property.kind === 'path')}/>}
-            {!property.secret &&
-                <Tooltip position="bottom-end" content={showEditor ? "Change to TextField" : "Change to Text Area"}>
+            <InputGroupItem>
+                <Tooltip position="bottom-end" content={"Show Editor"}>
                     <Button variant="control" onClick={e => setShowEditor(!showEditor)}>
-                        {showEditor ? <CompressIcon/> : <ExpandIcon/>}
+                        <EditorIcon/>
                     </Button>
                 </Tooltip>
-            }
+            </InputGroupItem>
+            {showEditor && <InputGroupItem>
+                <ModalEditor name={property.name}
+                             customCode={value}
+                             showEditor={showEditor}
+                             dark={dark}
+                             dslLanguage={undefined}
+                             title={property.displayName}
+                             onClose={() => setShowEditor(false)}
+                             onSave={(fieldId, value1) => {
+                                 parametersChanged(property.name, value1, property.kind === 'path')
+                                 setShowEditor(false);
+                             }}/>
+            </InputGroupItem>}
             {property.secret &&
                 <Tooltip position="bottom-end" content={showPassword ? "Hide" : "Show"}>
                     <Button variant="control" onClick={e => setShowPassword(!showPassword)}>
@@ -255,19 +268,30 @@ export function ComponentPropertyField(props: Props) {
                     </Button>
                 </Tooltip>
             }
+            <InputGroupItem>
+                <ComponentPropertyPlaceholderDropdown property={property}/>
+            </InputGroupItem>
         </InputGroup>
     }
 
-    function getTextInput(property: ComponentProperty, value: any) {
+    function getSpecialStringInput(property: ComponentProperty, value: any) {
         return (
-            <TextInput
-                className="text-field" isRequired
-                type={['integer', 'int', 'number'].includes(property.type) ? 'number' : (property.secret ? "password" : "text")}
-                id={id} name={id}
-                value={value !== undefined ? value : property.defaultValue}
-                onChange={(_, value) => {
-                    parametersChanged(property.name, ['integer', 'int', 'number'].includes(property.type) ? Number(value) : value, property.kind === 'path')
-                }}/>
+            <InputGroup>
+                <InputGroupItem isFill>
+                    <TextInput
+                        className="text-field" isRequired
+                        type={(property.secret ? "password" : "text")}
+                        id={id} name={id}
+                        value={value !== undefined ? value : property.defaultValue}
+                        customIcon={<Text component={TextVariants.p}>{property.type}</Text>}
+                        onChange={(_, value) => {
+                            parametersChanged(property.name, value, property.kind === 'path')
+                        }}/>
+                </InputGroupItem>
+                <InputGroupItem>
+                    <ComponentPropertyPlaceholderDropdown property={property}/>
+                </InputGroupItem>
+            </InputGroup>
         )
     }
 
@@ -297,14 +321,35 @@ export function ComponentPropertyField(props: Props) {
     }
 
     function getSwitch(property: ComponentProperty, value: any) {
+        const isValueBoolean = (value === true || value === false);
+        const isDisabled = value !== undefined && !isValueBoolean;
         const isChecked = value !== undefined ? Boolean(value) : (property.defaultValue !== undefined && ['true', true].includes(property.defaultValue))
         return (
-            <Switch
-                id={id} name={id}
-                value={value?.toString()}
-                aria-label={id}
-                isChecked={isChecked}
-                onChange={(e, checked) => parametersChanged(property.name, checked)}/>
+            <TextInputGroup className="input-group">
+                <InputGroupItem>
+                    <Switch
+                        id={id} name={id}
+                        value={value?.toString()}
+                        isDisabled={isDisabled}
+                        className="switch-placeholder"
+                        aria-label={id}
+                        isChecked={isChecked}
+                        onChange={(e, checked) => parametersChanged(property.name, checked)}/>
+                </InputGroupItem>
+                <InputGroupItem isFill>
+                    <TextInput
+                        id={property.name + "-placeholder"}
+                        name={property.name + "-placeholder"}
+                        type="text"
+                        aria-label="placeholder"
+                        value={!isValueBoolean ? value?.toString() : undefined}
+                        onChange={(_, v) => parametersChanged(property.name, v)}
+                    />
+                </InputGroupItem>
+                <InputGroupItem>
+                    <ComponentPropertyPlaceholderDropdown property={property}/>
+                </InputGroupItem>
+            </TextInputGroup>
         )
     }
 
@@ -336,7 +381,7 @@ export function ComponentPropertyField(props: Props) {
             {property.type === 'string' && property.enum === undefined && !canBeInternalUri(property)
                 && getStringInput(property, value)}
             {['duration', 'integer', 'int', 'number'].includes(property.type) && property.enum === undefined && !canBeInternalUri(property)
-                && getTextInput(property, value)}
+                && getSpecialStringInput(property, value)}
             {['object'].includes(property.type) && !property.enum
                 && getSelectBean(property, value)}
             {['string', 'object'].includes(property.type) && property.enum
diff --git a/karavan-space/src/designer/property/property/ComponentPropertyPlaceholderDropdown.css b/karavan-space/src/designer/property/property/ComponentPropertyPlaceholderDropdown.css
new file mode 100644
index 00000000..7d5d2d43
--- /dev/null
+++ b/karavan-space/src/designer/property/property/ComponentPropertyPlaceholderDropdown.css
@@ -0,0 +1,24 @@
+/*
+ * 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 .property-placeholder-toggle {
+    padding-left: 6px;
+    padding-right: 6px;
+}
+.karavan .properties .property-placeholder-toggle .pf-v5-c-menu-toggle__controls {
+    display: none;
+}
\ 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
new file mode 100644
index 00000000..270f9549
--- /dev/null
+++ b/karavan-space/src/designer/property/property/ComponentPropertyPlaceholderDropdown.tsx
@@ -0,0 +1,81 @@
+/*
+ * 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, {useState} from 'react';
+import {
+    Dropdown,
+    MenuToggleElement,
+    MenuToggle,
+    DropdownList, DropdownItem
+} from '@patternfly/react-core';
+import '../../karavan.css';
+import './ComponentPropertyPlaceholderDropdown.css';
+import "@patternfly/patternfly/patternfly.css";
+import {ComponentProperty} from "karavan-core/lib/model/ComponentModels";
+import {RouteToCreate} from "../../utils/CamelUi";
+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";
+
+
+interface Props {
+    property: ComponentProperty,
+}
+
+export function ComponentPropertyPlaceholderDropdown(props: Props) {
+
+    const {onParametersChange} = usePropertiesHook();
+    const [propertyPlaceholders] = useDesignerStore((s) => [s.propertyPlaceholders], shallow)
+    const [isOpenPlaceholdersDropdown, setOpenPlaceholdersDropdown] = useState<boolean>(false);
+
+    function parametersChanged(parameter: string, value: string | number | boolean | any, pathParameter?: boolean, newRoute?: RouteToCreate) {
+        onParametersChange(parameter, value, pathParameter, newRoute);
+    }
+
+    const property: ComponentProperty = props.property;
+    return (
+        propertyPlaceholders && propertyPlaceholders.length > 0 ?
+            <Dropdown
+                popperProps={{position: "end"}}
+                isOpen={isOpenPlaceholdersDropdown}
+                onSelect={(_, value) => {
+                    parametersChanged(property.name, `{{${value}}}`, property.kind === 'path')
+                    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>
+                )}
+                shouldFocusToggleOnSelect
+            >
+                <DropdownList>
+                    {propertyPlaceholders.map((pp, index) =>
+                        <DropdownItem value={pp} key={index}>{pp}</DropdownItem>
+                    )}
+                </DropdownList>
+            </Dropdown>
+            : <></>
+    )
+}
diff --git a/karavan-space/src/designer/property/property/DslPropertyField.tsx b/karavan-space/src/designer/property/property/DslPropertyField.tsx
index b01f6e7c..59066667 100644
--- a/karavan-space/src/designer/property/property/DslPropertyField.tsx
+++ b/karavan-space/src/designer/property/property/DslPropertyField.tsx
@@ -32,7 +32,7 @@ import {
     Tooltip,
     Card,
     InputGroup,
-    capitalize, InputGroupItem
+    capitalize, InputGroupItem, TextVariants
 } from '@patternfly/react-core';
 import {
     Select,
@@ -59,8 +59,6 @@ import {CamelDefinitionApi} from "karavan-core/lib/api/CamelDefinitionApi";
 import AddIcon from "@patternfly/react-icons/dist/js/icons/plus-circle-icon";
 import {MediaTypes} from "../../utils/MediaTypes";
 import {ComponentProperty} from "karavan-core/lib/model/ComponentModels";
-import CompressIcon from "@patternfly/react-icons/dist/js/icons/compress-icon";
-import ExpandIcon from "@patternfly/react-icons/dist/js/icons/expand-icon";
 import {InfrastructureSelector} from "./InfrastructureSelector";
 import {InfrastructureAPI} from "../../utils/InfrastructureAPI";
 import EditorIcon from "@patternfly/react-icons/dist/js/icons/code-icon";
@@ -76,6 +74,7 @@ import {
 import {TemplateApi} from "karavan-core/lib/api/TemplateApi";
 import {KubernetesIcon} from "../../icons/ComponentIcons";
 import {BeanProperties} from "./BeanProperties";
+import {ComponentPropertyPlaceholderDropdown} from "./ComponentPropertyPlaceholderDropdown";
 
 interface Props {
     property: PropertyMeta,
@@ -229,6 +228,9 @@ export function DslPropertyField(props: Props) {
         const inInfrastructure = InfrastructureAPI.infrastructure !== 'local';
         const noInfraSelectorButton = ["uri", "id", "description", "group"].includes(property.name);
         const icon = InfrastructureAPI.infrastructure === 'kubernetes' ? KubernetesIcon("infra-button") : <DockerIcon/>
+        const isNumber = ['integer', 'number', 'duration'].includes(property.type);
+        const uriReadOnly = isUriReadOnly(property);
+        const showEditorButton = !uriReadOnly && !isNumber && !property.secret && !['id', 'description'].includes(property.name);
         return (<InputGroup>
             {inInfrastructure && !showEditor && !noInfraSelectorButton &&
                 <InputGroupItem>
@@ -244,34 +246,35 @@ export function DslPropertyField(props: Props) {
                 <InputGroupItem isFill>
                     <TextInput ref={ref}
                                className="text-field" isRequired
-                               type={['integer', 'number'].includes(property.type) ? 'number' : (property.secret ? "password" : "text")}
+                               type={(property.secret ? "password" : "text")}
                                id={property.name} name={property.name}
                                value={value?.toString()}
-                               onChange={(_, v) => propertyChanged(property.name, ['integer', 'number'].includes(property.type) ? Number(v) : v)}
-                               readOnlyVariant={isUriReadOnly(property) ? "default" : undefined}/>
-                </InputGroupItem>
-            }
-            {showEditor && !property.secret &&
-                <InputGroupItem isFill>
-                    <TextArea ref={ref}
-                              autoResize={true}
-                              className="text-field" isRequired
-                              type="text"
-                              id={property.name} name={property.name}
-                              value={value?.toString()}
-                              onChange={(_, v) => propertyChanged(property.name, ['integer', 'number'].includes(property.type) ? Number(v) : v)}
-                              readOnlyVariant={isUriReadOnly(property) ? "default" : undefined}/>
-                </InputGroupItem>
-            }
-            {!property.secret &&
-                <InputGroupItem>
-                    <Tooltip position="bottom-end" content={showEditor ? "Change to TextField" : "Change to Text Area"}>
-                        <Button variant="control" onClick={e => setShowEditor(!showEditor)}>
-                            {showEditor ? <CompressIcon/> : <ExpandIcon/>}
-                        </Button>
-                    </Tooltip>
+                               customIcon={property.type !== 'string' ? <Text component={TextVariants.p}>{property.type}</Text> : undefined}
+                               onChange={(_, v) =>
+                                   propertyChanged(property.name, v)}
+                               readOnlyVariant={uriReadOnly? "default" : undefined}/>
                 </InputGroupItem>
             }
+            {showEditorButton && <InputGroupItem>
+                <Tooltip position="bottom-end" content={"Show Editor"}>
+                    <Button variant="control" onClick={e => setShowEditor(!showEditor)}>
+                        <EditorIcon/>
+                    </Button>
+                </Tooltip>
+            </InputGroupItem>}
+            {showEditor && <InputGroupItem>
+                <ModalEditor name={property.name}
+                             customCode={value}
+                             showEditor={showEditor}
+                             dark={dark}
+                             dslLanguage={undefined}
+                             title={property.displayName}
+                             onClose={() => setShowEditor(false)}
+                             onSave={(fieldId, value1) => {
+                                 propertyChanged(property.name, value1)
+                                 setShowEditor(false);
+                             }}/>
+            </InputGroupItem>}
         </InputGroup>)
     }
 
@@ -313,7 +316,7 @@ export function DslPropertyField(props: Props) {
                 </Tooltip>
             </InputGroupItem>
             {showEditor && <InputGroupItem>
-                <ModalEditor property={property}
+                <ModalEditor name={property.name}
                              customCode={customCode}
                              showEditor={showEditor}
                              dark={dark}
@@ -351,7 +354,7 @@ export function DslPropertyField(props: Props) {
                     </Tooltip>
                 </InputGroupItem>
                 {showEditor && <InputGroupItem>
-                    <ModalEditor property={property}
+                    <ModalEditor name={property.name}
                                  customCode={value}
                                  showEditor={showEditor}
                                  dark={dark}
@@ -388,7 +391,7 @@ export function DslPropertyField(props: Props) {
         )
     }
 
-    function getSwitch(property: PropertyMeta, value: any) {
+    function getBooleanInput(property: PropertyMeta, value: any) {
         const isValueBoolean = (value === true || value === false);
         const isDisabled = value !== undefined && !isValueBoolean;
         let isChecked = false;
@@ -410,17 +413,16 @@ export function DslPropertyField(props: Props) {
                         isChecked={isChecked}
                         onChange={(_, v) => propertyChanged(property.name, v)}/>
                 </InputGroupItem>
-                {property.placeholder && <InputGroupItem isFill>
+                <InputGroupItem isFill>
                     <TextInput
                         id={property.name + "-placeholder"}
                         name={property.name + "-placeholder"}
                         type="text"
                         aria-label="placeholder"
-                        placeholder="Property placeholder"
                         value={!isValueBoolean ? value?.toString() : undefined}
                         onChange={(_, v) => propertyChanged(property.name, v)}
                     />
-                </InputGroupItem>}
+                </InputGroupItem>
             </TextInputGroup>
         )
     }
@@ -831,7 +833,7 @@ export function DslPropertyField(props: Props) {
                 {isMultiValueField(property)
                     && getMultiValueField(property, value)}
                 {property.type === 'boolean'
-                    && getSwitch(property, value)}
+                    && getBooleanInput(property, value)}
                 {property.enumVals
                     && getSelect(property, value)}
                 {isKamelet && property.name === 'parameters' && getKameletParameters()}
diff --git a/karavan-space/src/designer/property/property/ModalEditor.tsx b/karavan-space/src/designer/property/property/ModalEditor.tsx
index 3b18975b..d3df2a27 100644
--- a/karavan-space/src/designer/property/property/ModalEditor.tsx
+++ b/karavan-space/src/designer/property/property/ModalEditor.tsx
@@ -22,11 +22,10 @@ import {
 } from '@patternfly/react-core';
 import '../../karavan.css';
 import "@patternfly/patternfly/patternfly.css";
-import {PropertyMeta} from "karavan-core/lib/model/CamelMetadata";
 import Editor from "@monaco-editor/react";
 
 interface Props {
-    property: PropertyMeta,
+    name: string,
     customCode: any,
     onSave: (fieldId: string, value: string | number | boolean | any) => void,
     onClose: () => void,
@@ -49,7 +48,7 @@ export function ModalEditor(props: Props) {
     }
 
     function closeAndSave(){
-        props.onSave(props.property.name, customCode);
+        props.onSave(props.name, customCode);
     }
 
     const {dark, dslLanguage, title, showEditor} = props;
diff --git a/karavan-space/src/space/SpacePage.tsx b/karavan-space/src/space/SpacePage.tsx
index 3dea0d2a..e878383a 100644
--- a/karavan-space/src/space/SpacePage.tsx
+++ b/karavan-space/src/space/SpacePage.tsx
@@ -108,6 +108,7 @@ export class SpacePage extends React.Component<Props, State> {
                 onSaveCustomCode={(name1, code) => {
                     console.log(name1, code)
                 }}
+                propertyPlaceholders={[]}
             />
         )
     }
diff --git a/karavan-web/karavan-app/src/main/webui/src/designer/property/DslProperties.css b/karavan-web/karavan-app/src/main/webui/src/designer/property/DslProperties.css
index c681098c..dd7cb199 100644
--- a/karavan-web/karavan-app/src/main/webui/src/designer/property/DslProperties.css
+++ b/karavan-web/karavan-app/src/main/webui/src/designer/property/DslProperties.css
@@ -248,3 +248,10 @@
     min-height: 6px;
 }
 
+.karavan .properties .component-headers {
+    margin-left: 24px;
+}
+
+.karavan .properties .component-headers .pf-v5-c-clipboard-copy.pf-m-inline {
+    background-color: transparent;
+}
\ No newline at end of file
diff --git a/karavan-web/karavan-app/src/main/webui/src/designer/property/DslProperties.tsx b/karavan-web/karavan-app/src/main/webui/src/designer/property/DslProperties.tsx
index 9325ee05..6169d656 100644
--- a/karavan-web/karavan-app/src/main/webui/src/designer/property/DslProperties.tsx
+++ b/karavan-web/karavan-app/src/main/webui/src/designer/property/DslProperties.tsx
@@ -27,7 +27,7 @@ import {
     MenuToggleElement,
     MenuToggle,
     DropdownList,
-    DropdownItem, Label, Flex, LabelGroup, Popover, FlexItem, Badge,
+    DropdownItem, Label, Flex, LabelGroup, Popover, FlexItem, Badge, ClipboardCopy, ClipboardCopyAction,
 } from '@patternfly/react-core';
 import '../karavan.css';
 import './DslProperties.css';
@@ -166,11 +166,13 @@ export function DslProperties(props: Props) {
                     <ExpandableSection toggleText='Headers'
                                        onToggle={(_event, isExpanded) => setIsDescriptionExpanded(!isDescriptionExpanded)}
                                        isExpanded={isDescriptionExpanded}>
-                        <Flex direction={{default:"column"}}>
+                        <Flex className='component-headers' direction={{default:"column"}}>
                             {headers.filter((header) => groups.includes(header.group))
                                 .map((header, index, array) =>
                                     <Flex key={index}>
-                                        <Text style={{marginLeft: "26px"}} component={TextVariants.p}>{header.name}</Text>
+                                        <ClipboardCopy key={index} hoverTip="Copy" clickTip="Copied" variant="inline-compact" isCode>
+                                            {header.name}
+                                        </ClipboardCopy>
                                         <FlexItem align={{default: 'alignRight'}}>
                                             <Popover
                                                 position={"left"}
diff --git a/karavan-web/karavan-app/src/main/webui/src/designer/property/property/ComponentPropertyField.tsx b/karavan-web/karavan-app/src/main/webui/src/designer/property/property/ComponentPropertyField.tsx
index b98b411d..ae63e323 100644
--- a/karavan-web/karavan-app/src/main/webui/src/designer/property/property/ComponentPropertyField.tsx
+++ b/karavan-web/karavan-app/src/main/webui/src/designer/property/property/ComponentPropertyField.tsx
@@ -24,7 +24,7 @@ import {
     TextArea,
     Tooltip,
     Button,
-    capitalize, InputGroupItem
+    capitalize, InputGroupItem, TextInputGroup, TextVariants, Text
 } from '@patternfly/react-core';
 import {
     Select,
@@ -39,8 +39,6 @@ import {ComponentProperty} from "karavan-core/lib/model/ComponentModels";
 import {CamelUi, RouteToCreate} from "../../utils/CamelUi";
 import {CamelElement} from "karavan-core/lib/model/IntegrationDefinition";
 import {ToDefinition} from "karavan-core/lib/model/CamelDefinition";
-import CompressIcon from "@patternfly/react-icons/dist/js/icons/compress-icon";
-import ExpandIcon from "@patternfly/react-icons/dist/js/icons/expand-icon";
 import {InfrastructureSelector} from "./InfrastructureSelector";
 import {InfrastructureAPI} from "../../utils/InfrastructureAPI";
 import DockerIcon from "@patternfly/react-icons/dist/js/icons/docker-icon";
@@ -48,9 +46,11 @@ import ShowIcon from "@patternfly/react-icons/dist/js/icons/eye-icon";
 import HideIcon from "@patternfly/react-icons/dist/js/icons/eye-slash-icon";
 import PlusIcon from "@patternfly/react-icons/dist/esm/icons/plus-icon";
 import {usePropertiesHook} from "../usePropertiesHook";
-import {useIntegrationStore} from "../../DesignerStore";
+import {useDesignerStore, useIntegrationStore} from "../../DesignerStore";
 import {shallow} from "zustand/shallow";
 import {KubernetesIcon} from "../../icons/ComponentIcons";
+import EditorIcon from "@patternfly/react-icons/dist/js/icons/code-icon";
+import {ModalEditor} from "./ModalEditor";
 
 const prefix = "parameters";
 const beanPrefix = "#bean:";
@@ -67,6 +67,7 @@ export function ComponentPropertyField(props: Props) {
     const {onParametersChange, getInternalComponentName} = usePropertiesHook();
 
     const [integration] = useIntegrationStore((state) => [state.integration], shallow)
+    const [dark, setSelectedStep] = useDesignerStore((s) => [s.dark, s.setSelectedStep], shallow)
 
     const [selectStatus, setSelectStatus] = useState<Map<string, boolean>>(new Map<string, boolean>());
     const [showEditor, setShowEditor] = useState<boolean>(false);
@@ -170,7 +171,7 @@ export function ComponentPropertyField(props: Props) {
                     <Button isDisabled={value === undefined} variant="control" onClick={e => {
                         if (value) {
                             const newRoute = !internalUris.includes(value.toString())
-                                ? CamelUi.createNewInternalRoute(componentName.concat(...':',value.toString()))
+                                ? CamelUi.createNewInternalRoute(componentName.concat(...':', value.toString()))
                                 : undefined;
                             parametersChanged(property.name, value, property.kind === 'path', newRoute);
                         }
@@ -234,20 +235,26 @@ export function ComponentPropertyField(props: Props) {
                            id={id} name={id}
                            value={value !== undefined ? value : property.defaultValue}
                            onChange={(e, value) => parametersChanged(property.name, value, property.kind === 'path')}/>}
-            {showEditor && !property.secret &&
-                <TextArea autoResize={true} ref={ref}
-                          className="text-field" isRequired
-                          type="text"
-                          id={id} name={id}
-                          value={value !== undefined ? value : property.defaultValue}
-                          onChange={(e, value) => parametersChanged(property.name, value, property.kind === 'path')}/>}
-            {!property.secret &&
-                <Tooltip position="bottom-end" content={showEditor ? "Change to TextField" : "Change to Text Area"}>
+            <InputGroupItem>
+                <Tooltip position="bottom-end" content={"Show Editor"}>
                     <Button variant="control" onClick={e => setShowEditor(!showEditor)}>
-                        {showEditor ? <CompressIcon/> : <ExpandIcon/>}
+                        <EditorIcon/>
                     </Button>
                 </Tooltip>
-            }
+            </InputGroupItem>
+            {showEditor && <InputGroupItem>
+                <ModalEditor name={property.name}
+                             customCode={value}
+                             showEditor={showEditor}
+                             dark={dark}
+                             dslLanguage={undefined}
+                             title={property.displayName}
+                             onClose={() => setShowEditor(false)}
+                             onSave={(fieldId, value1) => {
+                                 parametersChanged(property.name, value1, property.kind === 'path')
+                                 setShowEditor(false);
+                             }}/>
+            </InputGroupItem>}
             {property.secret &&
                 <Tooltip position="bottom-end" content={showPassword ? "Hide" : "Show"}>
                     <Button variant="control" onClick={e => setShowPassword(!showPassword)}>
@@ -262,11 +269,12 @@ export function ComponentPropertyField(props: Props) {
         return (
             <TextInput
                 className="text-field" isRequired
-                type={['integer', 'int', 'number'].includes(property.type) ? 'number' : (property.secret ? "password" : "text")}
+                type={(property.secret ? "password" : "text")}
                 id={id} name={id}
                 value={value !== undefined ? value : property.defaultValue}
+                customIcon={<Text component={TextVariants.p}>{property.type}</Text>}
                 onChange={(_, value) => {
-                    parametersChanged(property.name, ['integer', 'int', 'number'].includes(property.type) ? Number(value) : value, property.kind === 'path')
+                    parametersChanged(property.name, value, property.kind === 'path')
                 }}/>
         )
     }
@@ -297,14 +305,32 @@ export function ComponentPropertyField(props: Props) {
     }
 
     function getSwitch(property: ComponentProperty, value: any) {
+        const isValueBoolean = (value === true || value === false);
+        const isDisabled = value !== undefined && !isValueBoolean;
         const isChecked = value !== undefined ? Boolean(value) : (property.defaultValue !== undefined && ['true', true].includes(property.defaultValue))
         return (
-            <Switch
-                id={id} name={id}
-                value={value?.toString()}
-                aria-label={id}
-                isChecked={isChecked}
-                onChange={(e, checked) => parametersChanged(property.name, checked)}/>
+            <TextInputGroup className="input-group">
+                <InputGroupItem>
+                    <Switch
+                        id={id} name={id}
+                        value={value?.toString()}
+                        isDisabled={isDisabled}
+                        className="switch-placeholder"
+                        aria-label={id}
+                        isChecked={isChecked}
+                        onChange={(e, checked) => parametersChanged(property.name, checked)}/>
+                </InputGroupItem>
+                <InputGroupItem isFill>
+                    <TextInput
+                        id={property.name + "-placeholder"}
+                        name={property.name + "-placeholder"}
+                        type="text"
+                        aria-label="placeholder"
+                        value={!isValueBoolean ? value?.toString() : undefined}
+                        onChange={(_, v) => parametersChanged(property.name, v)}
+                    />
+                </InputGroupItem>
+            </TextInputGroup>
         )
     }
 
diff --git a/karavan-web/karavan-app/src/main/webui/src/designer/property/property/DslPropertyField.tsx b/karavan-web/karavan-app/src/main/webui/src/designer/property/property/DslPropertyField.tsx
index b01f6e7c..aa093618 100644
--- a/karavan-web/karavan-app/src/main/webui/src/designer/property/property/DslPropertyField.tsx
+++ b/karavan-web/karavan-app/src/main/webui/src/designer/property/property/DslPropertyField.tsx
@@ -32,7 +32,7 @@ import {
     Tooltip,
     Card,
     InputGroup,
-    capitalize, InputGroupItem
+    capitalize, InputGroupItem, TextVariants
 } from '@patternfly/react-core';
 import {
     Select,
@@ -59,8 +59,6 @@ import {CamelDefinitionApi} from "karavan-core/lib/api/CamelDefinitionApi";
 import AddIcon from "@patternfly/react-icons/dist/js/icons/plus-circle-icon";
 import {MediaTypes} from "../../utils/MediaTypes";
 import {ComponentProperty} from "karavan-core/lib/model/ComponentModels";
-import CompressIcon from "@patternfly/react-icons/dist/js/icons/compress-icon";
-import ExpandIcon from "@patternfly/react-icons/dist/js/icons/expand-icon";
 import {InfrastructureSelector} from "./InfrastructureSelector";
 import {InfrastructureAPI} from "../../utils/InfrastructureAPI";
 import EditorIcon from "@patternfly/react-icons/dist/js/icons/code-icon";
@@ -229,6 +227,9 @@ export function DslPropertyField(props: Props) {
         const inInfrastructure = InfrastructureAPI.infrastructure !== 'local';
         const noInfraSelectorButton = ["uri", "id", "description", "group"].includes(property.name);
         const icon = InfrastructureAPI.infrastructure === 'kubernetes' ? KubernetesIcon("infra-button") : <DockerIcon/>
+        const isNumber = ['integer', 'number', 'duration'].includes(property.type);
+        const uriReadOnly = isUriReadOnly(property);
+        const showEditorButton = !uriReadOnly && !isNumber && !property.secret && !['id', 'description'].includes(property.name);
         return (<InputGroup>
             {inInfrastructure && !showEditor && !noInfraSelectorButton &&
                 <InputGroupItem>
@@ -244,34 +245,35 @@ export function DslPropertyField(props: Props) {
                 <InputGroupItem isFill>
                     <TextInput ref={ref}
                                className="text-field" isRequired
-                               type={['integer', 'number'].includes(property.type) ? 'number' : (property.secret ? "password" : "text")}
+                               type={(property.secret ? "password" : "text")}
                                id={property.name} name={property.name}
                                value={value?.toString()}
-                               onChange={(_, v) => propertyChanged(property.name, ['integer', 'number'].includes(property.type) ? Number(v) : v)}
-                               readOnlyVariant={isUriReadOnly(property) ? "default" : undefined}/>
-                </InputGroupItem>
-            }
-            {showEditor && !property.secret &&
-                <InputGroupItem isFill>
-                    <TextArea ref={ref}
-                              autoResize={true}
-                              className="text-field" isRequired
-                              type="text"
-                              id={property.name} name={property.name}
-                              value={value?.toString()}
-                              onChange={(_, v) => propertyChanged(property.name, ['integer', 'number'].includes(property.type) ? Number(v) : v)}
-                              readOnlyVariant={isUriReadOnly(property) ? "default" : undefined}/>
-                </InputGroupItem>
-            }
-            {!property.secret &&
-                <InputGroupItem>
-                    <Tooltip position="bottom-end" content={showEditor ? "Change to TextField" : "Change to Text Area"}>
-                        <Button variant="control" onClick={e => setShowEditor(!showEditor)}>
-                            {showEditor ? <CompressIcon/> : <ExpandIcon/>}
-                        </Button>
-                    </Tooltip>
+                               customIcon={property.type !== 'string' ? <Text component={TextVariants.p}>{property.type}</Text> : undefined}
+                               onChange={(_, v) =>
+                                   propertyChanged(property.name, v)}
+                               readOnlyVariant={uriReadOnly? "default" : undefined}/>
                 </InputGroupItem>
             }
+            {showEditorButton && <InputGroupItem>
+                <Tooltip position="bottom-end" content={"Show Editor"}>
+                    <Button variant="control" onClick={e => setShowEditor(!showEditor)}>
+                        <EditorIcon/>
+                    </Button>
+                </Tooltip>
+            </InputGroupItem>}
+            {showEditor && <InputGroupItem>
+                <ModalEditor name={property.name}
+                             customCode={value}
+                             showEditor={showEditor}
+                             dark={dark}
+                             dslLanguage={undefined}
+                             title={property.displayName}
+                             onClose={() => setShowEditor(false)}
+                             onSave={(fieldId, value1) => {
+                                 propertyChanged(property.name, value1)
+                                 setShowEditor(false);
+                             }}/>
+            </InputGroupItem>}
         </InputGroup>)
     }
 
@@ -313,7 +315,7 @@ export function DslPropertyField(props: Props) {
                 </Tooltip>
             </InputGroupItem>
             {showEditor && <InputGroupItem>
-                <ModalEditor property={property}
+                <ModalEditor name={property.name}
                              customCode={customCode}
                              showEditor={showEditor}
                              dark={dark}
@@ -351,7 +353,7 @@ export function DslPropertyField(props: Props) {
                     </Tooltip>
                 </InputGroupItem>
                 {showEditor && <InputGroupItem>
-                    <ModalEditor property={property}
+                    <ModalEditor name={property.name}
                                  customCode={value}
                                  showEditor={showEditor}
                                  dark={dark}
@@ -388,7 +390,7 @@ export function DslPropertyField(props: Props) {
         )
     }
 
-    function getSwitch(property: PropertyMeta, value: any) {
+    function getBooleanInput(property: PropertyMeta, value: any) {
         const isValueBoolean = (value === true || value === false);
         const isDisabled = value !== undefined && !isValueBoolean;
         let isChecked = false;
@@ -410,17 +412,16 @@ export function DslPropertyField(props: Props) {
                         isChecked={isChecked}
                         onChange={(_, v) => propertyChanged(property.name, v)}/>
                 </InputGroupItem>
-                {property.placeholder && <InputGroupItem isFill>
+                <InputGroupItem isFill>
                     <TextInput
                         id={property.name + "-placeholder"}
                         name={property.name + "-placeholder"}
                         type="text"
                         aria-label="placeholder"
-                        placeholder="Property placeholder"
                         value={!isValueBoolean ? value?.toString() : undefined}
                         onChange={(_, v) => propertyChanged(property.name, v)}
                     />
-                </InputGroupItem>}
+                </InputGroupItem>
             </TextInputGroup>
         )
     }
@@ -831,7 +832,7 @@ export function DslPropertyField(props: Props) {
                 {isMultiValueField(property)
                     && getMultiValueField(property, value)}
                 {property.type === 'boolean'
-                    && getSwitch(property, value)}
+                    && getBooleanInput(property, value)}
                 {property.enumVals
                     && getSelect(property, value)}
                 {isKamelet && property.name === 'parameters' && getKameletParameters()}
diff --git a/karavan-web/karavan-app/src/main/webui/src/designer/property/property/ModalEditor.tsx b/karavan-web/karavan-app/src/main/webui/src/designer/property/property/ModalEditor.tsx
index 3b18975b..d3df2a27 100644
--- a/karavan-web/karavan-app/src/main/webui/src/designer/property/property/ModalEditor.tsx
+++ b/karavan-web/karavan-app/src/main/webui/src/designer/property/property/ModalEditor.tsx
@@ -22,11 +22,10 @@ import {
 } from '@patternfly/react-core';
 import '../../karavan.css';
 import "@patternfly/patternfly/patternfly.css";
-import {PropertyMeta} from "karavan-core/lib/model/CamelMetadata";
 import Editor from "@monaco-editor/react";
 
 interface Props {
-    property: PropertyMeta,
+    name: string,
     customCode: any,
     onSave: (fieldId: string, value: string | number | boolean | any) => void,
     onClose: () => void,
@@ -49,7 +48,7 @@ export function ModalEditor(props: Props) {
     }
 
     function closeAndSave(){
-        props.onSave(props.property.name, customCode);
+        props.onSave(props.name, customCode);
     }
 
     const {dark, dslLanguage, title, showEditor} = props;