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;