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
The following commit(s) were added to refs/heads/main by this push: new 25449b1f Fix #1319 25449b1f is described below commit 25449b1fc4577e012882a176d44f638089456e85 Author: Marat Gubaidullin <ma...@talismancloud.io> AuthorDate: Fri Jun 14 10:31:20 2024 -0400 Fix #1319 --- .../property/property/DslPropertyField.tsx | 69 +++++++++++-- .../property/property/KameletPropertyField.tsx | 99 +++++++++---------- .../property/PropertyPlaceholderDropdown.tsx | 9 +- karavan-designer/public/example/demo.camel.yaml | 109 ++++----------------- .../property/property/DslPropertyField.tsx | 69 +++++++++++-- .../property/property/KameletPropertyField.tsx | 99 +++++++++---------- .../property/PropertyPlaceholderDropdown.tsx | 9 +- .../property/property/DslPropertyField.tsx | 69 +++++++++++-- .../property/property/KameletPropertyField.tsx | 99 +++++++++---------- .../property/PropertyPlaceholderDropdown.tsx | 9 +- 10 files changed, 362 insertions(+), 278 deletions(-) diff --git a/karavan-app/src/main/webui/src/designer/property/property/DslPropertyField.tsx b/karavan-app/src/main/webui/src/designer/property/property/DslPropertyField.tsx index 67de1077..f669aa84 100644 --- a/karavan-app/src/main/webui/src/designer/property/property/DslPropertyField.tsx +++ b/karavan-app/src/main/webui/src/designer/property/property/DslPropertyField.tsx @@ -275,7 +275,7 @@ export function DslPropertyField(props: Props) { return <InputGroup> <InputGroupItem> <ToggleGroup aria-label="Variable type"> - <ToggleGroupItem text="global:" key='global' buttonId={"global-variable-"+ property.name} + <ToggleGroupItem text="global:" key='global' buttonId={"global-variable-" + property.name} isSelected={variableType === GLOBAL} onChange={(_, selected) => { if (selected) { @@ -287,7 +287,7 @@ export function DslPropertyField(props: Props) { } }} /> - <ToggleGroupItem text="route:" key='route' buttonId={"route-variable"+ property.name} + <ToggleGroupItem text="route:" key='route' buttonId={"route-variable" + property.name} className='route-variable' isSelected={variableType === ROUTE} onChange={(_, selected) => { @@ -331,8 +331,47 @@ export function DslPropertyField(props: Props) { </InputGroup> } - function isNumeric (num: any) { - return (typeof(num) === 'number' || typeof(num) === "string" && num.trim() !== '') && !isNaN(num as number); + function isNumeric(num: any) { + return (typeof (num) === 'number' || typeof (num) === "string" && num.trim() !== '') && !isNaN(num as number); + } + + function getSpecialStringInput(property: PropertyMeta) { + return ( + <InputGroup> + <InputGroupItem isFill> + <TextInput + ref={ref} + className="text-field" isRequired + type={property.secret ? "password" : "text"} + id={property.name} name={property.name} + value={textValue?.toString()} + customIcon={property.type !== 'string' ? + <Text component={TextVariants.p}>{property.type}</Text> : undefined} + onBlur={_ => { + if (isNumeric((textValue))) { + propertyChanged(property.name, Number(textValue)) + } else { + propertyChanged(property.name, textValue) + } + }} + onFocus={_ => setCheckChanges(true)} + onChange={(_, v) => { + setTextValue(v); + setCheckChanges(true); + }} + /> + </InputGroupItem> + <InputGroupItem> + <PropertyPlaceholderDropdown + property={property} value={value} + onDslPropertyChange={(_, v, newRoute) => { + setTextValue(v); + propertyChanged(property.name, v) + setCheckChanges(true); + }}/> + </InputGroupItem> + </InputGroup> + ) } function getStringInput(property: PropertyMeta) { @@ -365,13 +404,11 @@ export function DslPropertyField(props: Props) { if (isNumber && isNumeric((textValue))) { propertyChanged(property.name, Number(textValue)) } else if (!isNumber) { - console.log("onBlur", property.name, textValue) propertyChanged(property.name, textValue) } }} onFocus={_ => setCheckChanges(true)} onChange={(_, v) => { - if (isNumber && isNumeric(v)) { setTextValue(v); setCheckChanges(true); @@ -403,6 +440,15 @@ export function DslPropertyField(props: Props) { setShowEditor(false); }}/> </InputGroupItem>} + <InputGroupItem> + <PropertyPlaceholderDropdown + property={property} value={value} + onDslPropertyChange={(_, v, newRoute) => { + setTextValue(v); + propertyChanged(property.name, v) + setCheckChanges(true); + }}/> + </InputGroupItem> </InputGroup> } @@ -985,7 +1031,16 @@ export function DslPropertyField(props: Props) { && getMediaTypeSelect(property, value)} {javaTypeGenerated(property) && getJavaTypeGeneratedInput(property, value)} - {['string', 'duration', 'integer', 'number'].includes(property.type) + {['duration', 'integer', 'number'].includes(property.type) + && !isVariable + && property.name !== 'expression' + && !property.name.endsWith("Ref") + && !property.isArray && !property.enumVals + && !canBeInternalUri(property, element) + && !canBeMediaType(property, element) + && !javaTypeGenerated(property) + && getSpecialStringInput(property)} + {['string'].includes(property.type) && !isVariable && property.name !== 'expression' && !property.name.endsWith("Ref") diff --git a/karavan-app/src/main/webui/src/designer/property/property/KameletPropertyField.tsx b/karavan-app/src/main/webui/src/designer/property/property/KameletPropertyField.tsx index 0bbda26b..1523c720 100644 --- a/karavan-app/src/main/webui/src/designer/property/property/KameletPropertyField.tsx +++ b/karavan-app/src/main/webui/src/designer/property/property/KameletPropertyField.tsx @@ -19,13 +19,11 @@ import { FormGroup, TextInput, Popover, - Switch, InputGroup, Button, TextArea, Tooltip, capitalize, Text, TextVariants + Switch, InputGroup, Button, Tooltip, capitalize, Text, TextVariants, InputGroupItem } from '@patternfly/react-core'; import '../../karavan.css'; import "@patternfly/patternfly/patternfly.css"; import HelpIcon from "@patternfly/react-icons/dist/js/icons/help-icon"; -import ExpandIcon from "@patternfly/react-icons/dist/js/icons/expand-icon"; -import CompressIcon from "@patternfly/react-icons/dist/js/icons/compress-icon"; import {Property} from "karavan-core/lib/model/KameletModels"; import {InfrastructureSelector} from "./InfrastructureSelector"; import {InfrastructureAPI} from "../../utils/InfrastructureAPI"; @@ -35,6 +33,11 @@ import DockerIcon from "@patternfly/react-icons/dist/js/icons/docker-icon"; import {usePropertiesHook} from "../usePropertiesHook"; import {Select, SelectDirection, SelectOption, SelectVariant} from "@patternfly/react-core/deprecated"; import {KubernetesIcon} from "../../icons/ComponentIcons"; +import {PropertyPlaceholderDropdown} from "./PropertyPlaceholderDropdown"; +import EditorIcon from "@patternfly/react-icons/dist/js/icons/code-icon"; +import {ExpressionModalEditor} from "../../../expression/ExpressionModalEditor"; +import {useDesignerStore} from "../../DesignerStore"; +import {shallow} from "zustand/shallow"; interface Props { property: Property, @@ -46,7 +49,7 @@ export function KameletPropertyField(props: Props) { const {onParametersChange} = usePropertiesHook(); - const [selectIsOpen, setSelectIsOpen] = useState<boolean>(false); + const [dark] = useDesignerStore((s) => [s.dark], shallow) const [showEditor, setShowEditor] = useState<boolean>(false); const [showPassword, setShowPassword] = useState<boolean>(false); const [infrastructureSelector, setInfrastructureSelector] = useState<boolean>(false); @@ -74,7 +77,6 @@ export function KameletPropertyField(props: Props) { function parametersChanged (parameter: string, value: string | number | boolean | any, pathParameter?: boolean) { setCheckChanges(false); onParametersChange(parameter, value, pathParameter); - setSelectIsOpen(false); setSelectStatus(new Map<string, boolean>([[parameter, false]])) } @@ -120,7 +122,7 @@ export function KameletPropertyField(props: Props) { onSelect={selectInfrastructure}/>) } - function getStringInput() { + function getSpecialStringInput() { const {property, value} = props; const prefix = "parameters"; const id = prefix + "-" + property.id; @@ -128,6 +130,7 @@ export function KameletPropertyField(props: Props) { const noInfraSelectorButton = ["uri", "id", "description", "group"].includes(property.id); const icon = InfrastructureAPI.infrastructure === 'kubernetes' ? KubernetesIcon("infra-button") : <DockerIcon/> const showInfraSelectorButton = inInfrastructure && !showEditor && !noInfraSelectorButton; + const showEditorButton = property.type === 'string' && property.format !== "password"; const selectFromList: boolean = property.enum !== undefined && property?.enum?.length > 0; const selectOptions: JSX.Element[] = []; if (selectFromList && property.enum) { @@ -171,33 +174,51 @@ export function KameletPropertyField(props: Props) { type={property.format && !showPassword ? "password" : "text"} id={id} name={id} value={textValue} - onBlur={_ => parametersChanged(property.id, textValue)} + onBlur={_ => { + if (isNumeric((textValue))) { + parametersChanged(property.id, Number(textValue)) + } else { + parametersChanged(property.id, textValue) + } + }} onChange={(_, v) => { setTextValue(v); setCheckChanges(true); }} + customIcon={property.type !== 'string' ? + <Text component={TextVariants.p}>{property.type}</Text> : undefined} /> } - {(!selectFromList && showEditor) && property.format !== "password" && - <TextArea autoResize={true} - className="text-field" isRequired - type="text" - id={id} name={id} - value={textValue} - onBlur={_ => parametersChanged(property.id, textValue)} - onChange={(_, v) => { - setTextValue(v); - setCheckChanges(true); - }} - /> - } - {property.format !== "password" && - <Tooltip position="bottom-end" content={showEditor ? "Change to TextField" : "Change to Text Area"}> + {showEditorButton && <InputGroupItem> + <Tooltip position="bottom-end" content={"Show Editor"}> <Button variant="control" onClick={e => setShowEditor(!showEditor)}> - {showEditor ? <CompressIcon/> : <ExpandIcon/>} + <EditorIcon/> </Button> </Tooltip> - } + </InputGroupItem>} + {showEditor && <InputGroupItem> + <ExpressionModalEditor name={property.id} + customCode={value} + showEditor={showEditor} + dark={dark} + dslLanguage={undefined} + title={property.title} + onClose={() => setShowEditor(false)} + onSave={(fieldId, value1) => { + parametersChanged(property.id, value1) + setTextValue(value1); + setShowEditor(false); + }}/> + </InputGroupItem>} + <InputGroupItem> + <PropertyPlaceholderDropdown + property={property} value={value} + onDslPropertyChange={(_, v, newRoute) => { + setTextValue(v); + parametersChanged(property.id, v) + setCheckChanges(true); + }}/> + </InputGroupItem> {property.format === "password" && <Tooltip position="bottom-end" content={showPassword ? "Hide" : "Show"}> <Button variant="control" onClick={e => setShowPassword(!showPassword)}> @@ -212,32 +233,6 @@ export function KameletPropertyField(props: Props) { return (typeof(num) === 'number' || (typeof(num) === "string" && num.trim() !== '')) && !isNaN(num as number); } - function getNumberInput() { - return ( - <TextInput id={id} - name={id} - className="text-field" - isRequired - // type='number' - value={textValue?.toString()} - customIcon={<Text component={TextVariants.p}>{property.type}</Text>} - onBlur={_ => { - if (isNumeric((textValue))) { - parametersChanged(property.id, Number(textValue)) - } - }} - onChange={(_, v) => { - if (isNumeric(v)) { - setTextValue(v); - setCheckChanges(true); - } else { - setTextValue(textValue); - } - }} - /> - ) - } - const property = props.property; const value = props.value; const prefix = "parameters"; @@ -267,8 +262,8 @@ export function KameletPropertyField(props: Props) { </button> </Popover> }> - {property.type === 'string' && getStringInput()} - {['integer', 'int', 'number'].includes(property.type) && getNumberInput()} + {/*{property.type === 'string' && getStringInput()}*/} + {['string','integer', 'int', 'number'].includes(property.type) && getSpecialStringInput()} {property.type === 'boolean' && <Switch id={id} name={id} value={value?.toString()} diff --git a/karavan-app/src/main/webui/src/designer/property/property/PropertyPlaceholderDropdown.tsx b/karavan-app/src/main/webui/src/designer/property/property/PropertyPlaceholderDropdown.tsx index 7dc134df..e920ce0a 100644 --- a/karavan-app/src/main/webui/src/designer/property/property/PropertyPlaceholderDropdown.tsx +++ b/karavan-app/src/main/webui/src/designer/property/property/PropertyPlaceholderDropdown.tsx @@ -41,6 +41,7 @@ import AddIcon from "@patternfly/react-icons/dist/js/icons/plus-icon"; import {InfrastructureAPI} from "../../utils/InfrastructureAPI"; import {PropertyMeta} from "karavan-core/lib/model/CamelMetadata"; import {RouteToCreate} from "../../utils/CamelUi"; +import {Property} from "karavan-core/lib/model/KameletModels"; const SYNTAX_EXAMPLES = [ {key: 'property:', value: 'group.property', description: 'Application property'}, @@ -50,7 +51,7 @@ const SYNTAX_EXAMPLES = [ ] interface Props { - property: ComponentProperty | PropertyMeta, + property: ComponentProperty | PropertyMeta | Property, value: any, onDslPropertyChange?: (fieldId: string, value: string | number | boolean | any, newRoute?: RouteToCreate) => void, onComponentPropertyChange?: (parameter: string, value: string | number | boolean | any, pathParameter?: boolean, newRoute?: RouteToCreate) => void @@ -75,15 +76,17 @@ export function PropertyPlaceholderDropdown(props: Props) { && !propertyPlaceholders.includes(placeholderValue) && !SYNTAX_EXAMPLES.map(se=> se.value).includes(removeBrackets(placeholderValue)) && SYNTAX_EXAMPLES.findIndex(se=> removeBrackets(placeholderValue).startsWith(se.key)) === -1; - const popoverId = "popover-selector-" + property.name; + const popoverId = "popover-selector-" + property.hasOwnProperty('name') ? (property as any).name : (property as any).id; const hasPlaceholders = (propertyPlaceholders && propertyPlaceholders.length > 0 ); function parametersChanged(value: string | number | boolean | any) { if (property instanceof ComponentProperty) { props.onComponentPropertyChange?.(property.name, `{{${value}}}`, property.kind === 'path'); - } else { + } else if (property instanceof PropertyMeta) { props.onDslPropertyChange?.(property.name, `{{${value}}}`); + } else { + props.onDslPropertyChange?.((property as Property).id, `{{${value}}}`); } } diff --git a/karavan-designer/public/example/demo.camel.yaml b/karavan-designer/public/example/demo.camel.yaml index 1ac491b4..873e4e37 100644 --- a/karavan-designer/public/example/demo.camel.yaml +++ b/karavan-designer/public/example/demo.camel.yaml @@ -1,98 +1,23 @@ -- rest: - id: rest-328e - description: >- - It has a broken design for long endpoint descriptions and a dual topology - view rendering - get: - - id: get-5ab7 - to: direct:hello - route: - id: route-0dc7 - description: Audit Start - nodePrefixId: route-972 + id: route-b0b7 + nodePrefixId: route-dfc from: - id: from-846a - description: Audit Start - uri: direct + id: from-70b1 + uri: kamelet:timer-source parameters: - name: start + message: Hello + period: 1 steps: + - aggregate: + id: aggregate-46cb + completionSize: 1 - to: - id: to-3597 - uri: kafka - parameters: - topic: audit -- route: - id: route-a54e - description: Audit Finish - nodePrefixId: route-1d1 - from: - id: from-47d5 - description: Audit Finish - uri: direct - parameters: - name: finish - steps: - - to: - id: to-3cf2 - uri: kafka - parameters: - topic: audit -- route: - id: route-07ed - description: Audit Step - nodePrefixId: route-833 - from: - id: from-e007 - uri: direct - parameters: - name: step - steps: - - to: - id: to-fa9e - uri: kafka - parameters: - topic: audit -- route: - id: hello - from: - id: from-3f49 - uri: direct - parameters: - name: hello - steps: - - to: - id: to-428d - uri: activemq + id: to-df39 + uri: amqp + - aggregate: + id: aggregate-1193 + completionSize: 121212 + completionTimeout: "{{bean:beanName.method}}" - to: - id: to-fed7 - uri: kafka -- routeConfiguration: - id: auditedRoute - intercept: - - intercept: - id: intercept-0deb - steps: - - to: - id: to-b470 - uri: direct - parameters: - name: step - interceptFrom: - - interceptFrom: - id: interceptFrom-4041 - steps: - - to: - id: to-6861 - uri: direct - parameters: - name: start - onCompletion: - - onCompletion: - id: onCompletion-3dab - steps: - - to: - id: to-dd4e - uri: direct - parameters: - name: finish + id: to-1600 + uri: kamelet:kafka-sink diff --git a/karavan-designer/src/designer/property/property/DslPropertyField.tsx b/karavan-designer/src/designer/property/property/DslPropertyField.tsx index 67de1077..f669aa84 100644 --- a/karavan-designer/src/designer/property/property/DslPropertyField.tsx +++ b/karavan-designer/src/designer/property/property/DslPropertyField.tsx @@ -275,7 +275,7 @@ export function DslPropertyField(props: Props) { return <InputGroup> <InputGroupItem> <ToggleGroup aria-label="Variable type"> - <ToggleGroupItem text="global:" key='global' buttonId={"global-variable-"+ property.name} + <ToggleGroupItem text="global:" key='global' buttonId={"global-variable-" + property.name} isSelected={variableType === GLOBAL} onChange={(_, selected) => { if (selected) { @@ -287,7 +287,7 @@ export function DslPropertyField(props: Props) { } }} /> - <ToggleGroupItem text="route:" key='route' buttonId={"route-variable"+ property.name} + <ToggleGroupItem text="route:" key='route' buttonId={"route-variable" + property.name} className='route-variable' isSelected={variableType === ROUTE} onChange={(_, selected) => { @@ -331,8 +331,47 @@ export function DslPropertyField(props: Props) { </InputGroup> } - function isNumeric (num: any) { - return (typeof(num) === 'number' || typeof(num) === "string" && num.trim() !== '') && !isNaN(num as number); + function isNumeric(num: any) { + return (typeof (num) === 'number' || typeof (num) === "string" && num.trim() !== '') && !isNaN(num as number); + } + + function getSpecialStringInput(property: PropertyMeta) { + return ( + <InputGroup> + <InputGroupItem isFill> + <TextInput + ref={ref} + className="text-field" isRequired + type={property.secret ? "password" : "text"} + id={property.name} name={property.name} + value={textValue?.toString()} + customIcon={property.type !== 'string' ? + <Text component={TextVariants.p}>{property.type}</Text> : undefined} + onBlur={_ => { + if (isNumeric((textValue))) { + propertyChanged(property.name, Number(textValue)) + } else { + propertyChanged(property.name, textValue) + } + }} + onFocus={_ => setCheckChanges(true)} + onChange={(_, v) => { + setTextValue(v); + setCheckChanges(true); + }} + /> + </InputGroupItem> + <InputGroupItem> + <PropertyPlaceholderDropdown + property={property} value={value} + onDslPropertyChange={(_, v, newRoute) => { + setTextValue(v); + propertyChanged(property.name, v) + setCheckChanges(true); + }}/> + </InputGroupItem> + </InputGroup> + ) } function getStringInput(property: PropertyMeta) { @@ -365,13 +404,11 @@ export function DslPropertyField(props: Props) { if (isNumber && isNumeric((textValue))) { propertyChanged(property.name, Number(textValue)) } else if (!isNumber) { - console.log("onBlur", property.name, textValue) propertyChanged(property.name, textValue) } }} onFocus={_ => setCheckChanges(true)} onChange={(_, v) => { - if (isNumber && isNumeric(v)) { setTextValue(v); setCheckChanges(true); @@ -403,6 +440,15 @@ export function DslPropertyField(props: Props) { setShowEditor(false); }}/> </InputGroupItem>} + <InputGroupItem> + <PropertyPlaceholderDropdown + property={property} value={value} + onDslPropertyChange={(_, v, newRoute) => { + setTextValue(v); + propertyChanged(property.name, v) + setCheckChanges(true); + }}/> + </InputGroupItem> </InputGroup> } @@ -985,7 +1031,16 @@ export function DslPropertyField(props: Props) { && getMediaTypeSelect(property, value)} {javaTypeGenerated(property) && getJavaTypeGeneratedInput(property, value)} - {['string', 'duration', 'integer', 'number'].includes(property.type) + {['duration', 'integer', 'number'].includes(property.type) + && !isVariable + && property.name !== 'expression' + && !property.name.endsWith("Ref") + && !property.isArray && !property.enumVals + && !canBeInternalUri(property, element) + && !canBeMediaType(property, element) + && !javaTypeGenerated(property) + && getSpecialStringInput(property)} + {['string'].includes(property.type) && !isVariable && property.name !== 'expression' && !property.name.endsWith("Ref") diff --git a/karavan-designer/src/designer/property/property/KameletPropertyField.tsx b/karavan-designer/src/designer/property/property/KameletPropertyField.tsx index 0bbda26b..1523c720 100644 --- a/karavan-designer/src/designer/property/property/KameletPropertyField.tsx +++ b/karavan-designer/src/designer/property/property/KameletPropertyField.tsx @@ -19,13 +19,11 @@ import { FormGroup, TextInput, Popover, - Switch, InputGroup, Button, TextArea, Tooltip, capitalize, Text, TextVariants + Switch, InputGroup, Button, Tooltip, capitalize, Text, TextVariants, InputGroupItem } from '@patternfly/react-core'; import '../../karavan.css'; import "@patternfly/patternfly/patternfly.css"; import HelpIcon from "@patternfly/react-icons/dist/js/icons/help-icon"; -import ExpandIcon from "@patternfly/react-icons/dist/js/icons/expand-icon"; -import CompressIcon from "@patternfly/react-icons/dist/js/icons/compress-icon"; import {Property} from "karavan-core/lib/model/KameletModels"; import {InfrastructureSelector} from "./InfrastructureSelector"; import {InfrastructureAPI} from "../../utils/InfrastructureAPI"; @@ -35,6 +33,11 @@ import DockerIcon from "@patternfly/react-icons/dist/js/icons/docker-icon"; import {usePropertiesHook} from "../usePropertiesHook"; import {Select, SelectDirection, SelectOption, SelectVariant} from "@patternfly/react-core/deprecated"; import {KubernetesIcon} from "../../icons/ComponentIcons"; +import {PropertyPlaceholderDropdown} from "./PropertyPlaceholderDropdown"; +import EditorIcon from "@patternfly/react-icons/dist/js/icons/code-icon"; +import {ExpressionModalEditor} from "../../../expression/ExpressionModalEditor"; +import {useDesignerStore} from "../../DesignerStore"; +import {shallow} from "zustand/shallow"; interface Props { property: Property, @@ -46,7 +49,7 @@ export function KameletPropertyField(props: Props) { const {onParametersChange} = usePropertiesHook(); - const [selectIsOpen, setSelectIsOpen] = useState<boolean>(false); + const [dark] = useDesignerStore((s) => [s.dark], shallow) const [showEditor, setShowEditor] = useState<boolean>(false); const [showPassword, setShowPassword] = useState<boolean>(false); const [infrastructureSelector, setInfrastructureSelector] = useState<boolean>(false); @@ -74,7 +77,6 @@ export function KameletPropertyField(props: Props) { function parametersChanged (parameter: string, value: string | number | boolean | any, pathParameter?: boolean) { setCheckChanges(false); onParametersChange(parameter, value, pathParameter); - setSelectIsOpen(false); setSelectStatus(new Map<string, boolean>([[parameter, false]])) } @@ -120,7 +122,7 @@ export function KameletPropertyField(props: Props) { onSelect={selectInfrastructure}/>) } - function getStringInput() { + function getSpecialStringInput() { const {property, value} = props; const prefix = "parameters"; const id = prefix + "-" + property.id; @@ -128,6 +130,7 @@ export function KameletPropertyField(props: Props) { const noInfraSelectorButton = ["uri", "id", "description", "group"].includes(property.id); const icon = InfrastructureAPI.infrastructure === 'kubernetes' ? KubernetesIcon("infra-button") : <DockerIcon/> const showInfraSelectorButton = inInfrastructure && !showEditor && !noInfraSelectorButton; + const showEditorButton = property.type === 'string' && property.format !== "password"; const selectFromList: boolean = property.enum !== undefined && property?.enum?.length > 0; const selectOptions: JSX.Element[] = []; if (selectFromList && property.enum) { @@ -171,33 +174,51 @@ export function KameletPropertyField(props: Props) { type={property.format && !showPassword ? "password" : "text"} id={id} name={id} value={textValue} - onBlur={_ => parametersChanged(property.id, textValue)} + onBlur={_ => { + if (isNumeric((textValue))) { + parametersChanged(property.id, Number(textValue)) + } else { + parametersChanged(property.id, textValue) + } + }} onChange={(_, v) => { setTextValue(v); setCheckChanges(true); }} + customIcon={property.type !== 'string' ? + <Text component={TextVariants.p}>{property.type}</Text> : undefined} /> } - {(!selectFromList && showEditor) && property.format !== "password" && - <TextArea autoResize={true} - className="text-field" isRequired - type="text" - id={id} name={id} - value={textValue} - onBlur={_ => parametersChanged(property.id, textValue)} - onChange={(_, v) => { - setTextValue(v); - setCheckChanges(true); - }} - /> - } - {property.format !== "password" && - <Tooltip position="bottom-end" content={showEditor ? "Change to TextField" : "Change to Text Area"}> + {showEditorButton && <InputGroupItem> + <Tooltip position="bottom-end" content={"Show Editor"}> <Button variant="control" onClick={e => setShowEditor(!showEditor)}> - {showEditor ? <CompressIcon/> : <ExpandIcon/>} + <EditorIcon/> </Button> </Tooltip> - } + </InputGroupItem>} + {showEditor && <InputGroupItem> + <ExpressionModalEditor name={property.id} + customCode={value} + showEditor={showEditor} + dark={dark} + dslLanguage={undefined} + title={property.title} + onClose={() => setShowEditor(false)} + onSave={(fieldId, value1) => { + parametersChanged(property.id, value1) + setTextValue(value1); + setShowEditor(false); + }}/> + </InputGroupItem>} + <InputGroupItem> + <PropertyPlaceholderDropdown + property={property} value={value} + onDslPropertyChange={(_, v, newRoute) => { + setTextValue(v); + parametersChanged(property.id, v) + setCheckChanges(true); + }}/> + </InputGroupItem> {property.format === "password" && <Tooltip position="bottom-end" content={showPassword ? "Hide" : "Show"}> <Button variant="control" onClick={e => setShowPassword(!showPassword)}> @@ -212,32 +233,6 @@ export function KameletPropertyField(props: Props) { return (typeof(num) === 'number' || (typeof(num) === "string" && num.trim() !== '')) && !isNaN(num as number); } - function getNumberInput() { - return ( - <TextInput id={id} - name={id} - className="text-field" - isRequired - // type='number' - value={textValue?.toString()} - customIcon={<Text component={TextVariants.p}>{property.type}</Text>} - onBlur={_ => { - if (isNumeric((textValue))) { - parametersChanged(property.id, Number(textValue)) - } - }} - onChange={(_, v) => { - if (isNumeric(v)) { - setTextValue(v); - setCheckChanges(true); - } else { - setTextValue(textValue); - } - }} - /> - ) - } - const property = props.property; const value = props.value; const prefix = "parameters"; @@ -267,8 +262,8 @@ export function KameletPropertyField(props: Props) { </button> </Popover> }> - {property.type === 'string' && getStringInput()} - {['integer', 'int', 'number'].includes(property.type) && getNumberInput()} + {/*{property.type === 'string' && getStringInput()}*/} + {['string','integer', 'int', 'number'].includes(property.type) && getSpecialStringInput()} {property.type === 'boolean' && <Switch id={id} name={id} value={value?.toString()} diff --git a/karavan-designer/src/designer/property/property/PropertyPlaceholderDropdown.tsx b/karavan-designer/src/designer/property/property/PropertyPlaceholderDropdown.tsx index 7dc134df..e920ce0a 100644 --- a/karavan-designer/src/designer/property/property/PropertyPlaceholderDropdown.tsx +++ b/karavan-designer/src/designer/property/property/PropertyPlaceholderDropdown.tsx @@ -41,6 +41,7 @@ import AddIcon from "@patternfly/react-icons/dist/js/icons/plus-icon"; import {InfrastructureAPI} from "../../utils/InfrastructureAPI"; import {PropertyMeta} from "karavan-core/lib/model/CamelMetadata"; import {RouteToCreate} from "../../utils/CamelUi"; +import {Property} from "karavan-core/lib/model/KameletModels"; const SYNTAX_EXAMPLES = [ {key: 'property:', value: 'group.property', description: 'Application property'}, @@ -50,7 +51,7 @@ const SYNTAX_EXAMPLES = [ ] interface Props { - property: ComponentProperty | PropertyMeta, + property: ComponentProperty | PropertyMeta | Property, value: any, onDslPropertyChange?: (fieldId: string, value: string | number | boolean | any, newRoute?: RouteToCreate) => void, onComponentPropertyChange?: (parameter: string, value: string | number | boolean | any, pathParameter?: boolean, newRoute?: RouteToCreate) => void @@ -75,15 +76,17 @@ export function PropertyPlaceholderDropdown(props: Props) { && !propertyPlaceholders.includes(placeholderValue) && !SYNTAX_EXAMPLES.map(se=> se.value).includes(removeBrackets(placeholderValue)) && SYNTAX_EXAMPLES.findIndex(se=> removeBrackets(placeholderValue).startsWith(se.key)) === -1; - const popoverId = "popover-selector-" + property.name; + const popoverId = "popover-selector-" + property.hasOwnProperty('name') ? (property as any).name : (property as any).id; const hasPlaceholders = (propertyPlaceholders && propertyPlaceholders.length > 0 ); function parametersChanged(value: string | number | boolean | any) { if (property instanceof ComponentProperty) { props.onComponentPropertyChange?.(property.name, `{{${value}}}`, property.kind === 'path'); - } else { + } else if (property instanceof PropertyMeta) { props.onDslPropertyChange?.(property.name, `{{${value}}}`); + } else { + props.onDslPropertyChange?.((property as Property).id, `{{${value}}}`); } } diff --git a/karavan-space/src/designer/property/property/DslPropertyField.tsx b/karavan-space/src/designer/property/property/DslPropertyField.tsx index 67de1077..f669aa84 100644 --- a/karavan-space/src/designer/property/property/DslPropertyField.tsx +++ b/karavan-space/src/designer/property/property/DslPropertyField.tsx @@ -275,7 +275,7 @@ export function DslPropertyField(props: Props) { return <InputGroup> <InputGroupItem> <ToggleGroup aria-label="Variable type"> - <ToggleGroupItem text="global:" key='global' buttonId={"global-variable-"+ property.name} + <ToggleGroupItem text="global:" key='global' buttonId={"global-variable-" + property.name} isSelected={variableType === GLOBAL} onChange={(_, selected) => { if (selected) { @@ -287,7 +287,7 @@ export function DslPropertyField(props: Props) { } }} /> - <ToggleGroupItem text="route:" key='route' buttonId={"route-variable"+ property.name} + <ToggleGroupItem text="route:" key='route' buttonId={"route-variable" + property.name} className='route-variable' isSelected={variableType === ROUTE} onChange={(_, selected) => { @@ -331,8 +331,47 @@ export function DslPropertyField(props: Props) { </InputGroup> } - function isNumeric (num: any) { - return (typeof(num) === 'number' || typeof(num) === "string" && num.trim() !== '') && !isNaN(num as number); + function isNumeric(num: any) { + return (typeof (num) === 'number' || typeof (num) === "string" && num.trim() !== '') && !isNaN(num as number); + } + + function getSpecialStringInput(property: PropertyMeta) { + return ( + <InputGroup> + <InputGroupItem isFill> + <TextInput + ref={ref} + className="text-field" isRequired + type={property.secret ? "password" : "text"} + id={property.name} name={property.name} + value={textValue?.toString()} + customIcon={property.type !== 'string' ? + <Text component={TextVariants.p}>{property.type}</Text> : undefined} + onBlur={_ => { + if (isNumeric((textValue))) { + propertyChanged(property.name, Number(textValue)) + } else { + propertyChanged(property.name, textValue) + } + }} + onFocus={_ => setCheckChanges(true)} + onChange={(_, v) => { + setTextValue(v); + setCheckChanges(true); + }} + /> + </InputGroupItem> + <InputGroupItem> + <PropertyPlaceholderDropdown + property={property} value={value} + onDslPropertyChange={(_, v, newRoute) => { + setTextValue(v); + propertyChanged(property.name, v) + setCheckChanges(true); + }}/> + </InputGroupItem> + </InputGroup> + ) } function getStringInput(property: PropertyMeta) { @@ -365,13 +404,11 @@ export function DslPropertyField(props: Props) { if (isNumber && isNumeric((textValue))) { propertyChanged(property.name, Number(textValue)) } else if (!isNumber) { - console.log("onBlur", property.name, textValue) propertyChanged(property.name, textValue) } }} onFocus={_ => setCheckChanges(true)} onChange={(_, v) => { - if (isNumber && isNumeric(v)) { setTextValue(v); setCheckChanges(true); @@ -403,6 +440,15 @@ export function DslPropertyField(props: Props) { setShowEditor(false); }}/> </InputGroupItem>} + <InputGroupItem> + <PropertyPlaceholderDropdown + property={property} value={value} + onDslPropertyChange={(_, v, newRoute) => { + setTextValue(v); + propertyChanged(property.name, v) + setCheckChanges(true); + }}/> + </InputGroupItem> </InputGroup> } @@ -985,7 +1031,16 @@ export function DslPropertyField(props: Props) { && getMediaTypeSelect(property, value)} {javaTypeGenerated(property) && getJavaTypeGeneratedInput(property, value)} - {['string', 'duration', 'integer', 'number'].includes(property.type) + {['duration', 'integer', 'number'].includes(property.type) + && !isVariable + && property.name !== 'expression' + && !property.name.endsWith("Ref") + && !property.isArray && !property.enumVals + && !canBeInternalUri(property, element) + && !canBeMediaType(property, element) + && !javaTypeGenerated(property) + && getSpecialStringInput(property)} + {['string'].includes(property.type) && !isVariable && property.name !== 'expression' && !property.name.endsWith("Ref") diff --git a/karavan-space/src/designer/property/property/KameletPropertyField.tsx b/karavan-space/src/designer/property/property/KameletPropertyField.tsx index 0bbda26b..1523c720 100644 --- a/karavan-space/src/designer/property/property/KameletPropertyField.tsx +++ b/karavan-space/src/designer/property/property/KameletPropertyField.tsx @@ -19,13 +19,11 @@ import { FormGroup, TextInput, Popover, - Switch, InputGroup, Button, TextArea, Tooltip, capitalize, Text, TextVariants + Switch, InputGroup, Button, Tooltip, capitalize, Text, TextVariants, InputGroupItem } from '@patternfly/react-core'; import '../../karavan.css'; import "@patternfly/patternfly/patternfly.css"; import HelpIcon from "@patternfly/react-icons/dist/js/icons/help-icon"; -import ExpandIcon from "@patternfly/react-icons/dist/js/icons/expand-icon"; -import CompressIcon from "@patternfly/react-icons/dist/js/icons/compress-icon"; import {Property} from "karavan-core/lib/model/KameletModels"; import {InfrastructureSelector} from "./InfrastructureSelector"; import {InfrastructureAPI} from "../../utils/InfrastructureAPI"; @@ -35,6 +33,11 @@ import DockerIcon from "@patternfly/react-icons/dist/js/icons/docker-icon"; import {usePropertiesHook} from "../usePropertiesHook"; import {Select, SelectDirection, SelectOption, SelectVariant} from "@patternfly/react-core/deprecated"; import {KubernetesIcon} from "../../icons/ComponentIcons"; +import {PropertyPlaceholderDropdown} from "./PropertyPlaceholderDropdown"; +import EditorIcon from "@patternfly/react-icons/dist/js/icons/code-icon"; +import {ExpressionModalEditor} from "../../../expression/ExpressionModalEditor"; +import {useDesignerStore} from "../../DesignerStore"; +import {shallow} from "zustand/shallow"; interface Props { property: Property, @@ -46,7 +49,7 @@ export function KameletPropertyField(props: Props) { const {onParametersChange} = usePropertiesHook(); - const [selectIsOpen, setSelectIsOpen] = useState<boolean>(false); + const [dark] = useDesignerStore((s) => [s.dark], shallow) const [showEditor, setShowEditor] = useState<boolean>(false); const [showPassword, setShowPassword] = useState<boolean>(false); const [infrastructureSelector, setInfrastructureSelector] = useState<boolean>(false); @@ -74,7 +77,6 @@ export function KameletPropertyField(props: Props) { function parametersChanged (parameter: string, value: string | number | boolean | any, pathParameter?: boolean) { setCheckChanges(false); onParametersChange(parameter, value, pathParameter); - setSelectIsOpen(false); setSelectStatus(new Map<string, boolean>([[parameter, false]])) } @@ -120,7 +122,7 @@ export function KameletPropertyField(props: Props) { onSelect={selectInfrastructure}/>) } - function getStringInput() { + function getSpecialStringInput() { const {property, value} = props; const prefix = "parameters"; const id = prefix + "-" + property.id; @@ -128,6 +130,7 @@ export function KameletPropertyField(props: Props) { const noInfraSelectorButton = ["uri", "id", "description", "group"].includes(property.id); const icon = InfrastructureAPI.infrastructure === 'kubernetes' ? KubernetesIcon("infra-button") : <DockerIcon/> const showInfraSelectorButton = inInfrastructure && !showEditor && !noInfraSelectorButton; + const showEditorButton = property.type === 'string' && property.format !== "password"; const selectFromList: boolean = property.enum !== undefined && property?.enum?.length > 0; const selectOptions: JSX.Element[] = []; if (selectFromList && property.enum) { @@ -171,33 +174,51 @@ export function KameletPropertyField(props: Props) { type={property.format && !showPassword ? "password" : "text"} id={id} name={id} value={textValue} - onBlur={_ => parametersChanged(property.id, textValue)} + onBlur={_ => { + if (isNumeric((textValue))) { + parametersChanged(property.id, Number(textValue)) + } else { + parametersChanged(property.id, textValue) + } + }} onChange={(_, v) => { setTextValue(v); setCheckChanges(true); }} + customIcon={property.type !== 'string' ? + <Text component={TextVariants.p}>{property.type}</Text> : undefined} /> } - {(!selectFromList && showEditor) && property.format !== "password" && - <TextArea autoResize={true} - className="text-field" isRequired - type="text" - id={id} name={id} - value={textValue} - onBlur={_ => parametersChanged(property.id, textValue)} - onChange={(_, v) => { - setTextValue(v); - setCheckChanges(true); - }} - /> - } - {property.format !== "password" && - <Tooltip position="bottom-end" content={showEditor ? "Change to TextField" : "Change to Text Area"}> + {showEditorButton && <InputGroupItem> + <Tooltip position="bottom-end" content={"Show Editor"}> <Button variant="control" onClick={e => setShowEditor(!showEditor)}> - {showEditor ? <CompressIcon/> : <ExpandIcon/>} + <EditorIcon/> </Button> </Tooltip> - } + </InputGroupItem>} + {showEditor && <InputGroupItem> + <ExpressionModalEditor name={property.id} + customCode={value} + showEditor={showEditor} + dark={dark} + dslLanguage={undefined} + title={property.title} + onClose={() => setShowEditor(false)} + onSave={(fieldId, value1) => { + parametersChanged(property.id, value1) + setTextValue(value1); + setShowEditor(false); + }}/> + </InputGroupItem>} + <InputGroupItem> + <PropertyPlaceholderDropdown + property={property} value={value} + onDslPropertyChange={(_, v, newRoute) => { + setTextValue(v); + parametersChanged(property.id, v) + setCheckChanges(true); + }}/> + </InputGroupItem> {property.format === "password" && <Tooltip position="bottom-end" content={showPassword ? "Hide" : "Show"}> <Button variant="control" onClick={e => setShowPassword(!showPassword)}> @@ -212,32 +233,6 @@ export function KameletPropertyField(props: Props) { return (typeof(num) === 'number' || (typeof(num) === "string" && num.trim() !== '')) && !isNaN(num as number); } - function getNumberInput() { - return ( - <TextInput id={id} - name={id} - className="text-field" - isRequired - // type='number' - value={textValue?.toString()} - customIcon={<Text component={TextVariants.p}>{property.type}</Text>} - onBlur={_ => { - if (isNumeric((textValue))) { - parametersChanged(property.id, Number(textValue)) - } - }} - onChange={(_, v) => { - if (isNumeric(v)) { - setTextValue(v); - setCheckChanges(true); - } else { - setTextValue(textValue); - } - }} - /> - ) - } - const property = props.property; const value = props.value; const prefix = "parameters"; @@ -267,8 +262,8 @@ export function KameletPropertyField(props: Props) { </button> </Popover> }> - {property.type === 'string' && getStringInput()} - {['integer', 'int', 'number'].includes(property.type) && getNumberInput()} + {/*{property.type === 'string' && getStringInput()}*/} + {['string','integer', 'int', 'number'].includes(property.type) && getSpecialStringInput()} {property.type === 'boolean' && <Switch id={id} name={id} value={value?.toString()} diff --git a/karavan-space/src/designer/property/property/PropertyPlaceholderDropdown.tsx b/karavan-space/src/designer/property/property/PropertyPlaceholderDropdown.tsx index 7dc134df..e920ce0a 100644 --- a/karavan-space/src/designer/property/property/PropertyPlaceholderDropdown.tsx +++ b/karavan-space/src/designer/property/property/PropertyPlaceholderDropdown.tsx @@ -41,6 +41,7 @@ import AddIcon from "@patternfly/react-icons/dist/js/icons/plus-icon"; import {InfrastructureAPI} from "../../utils/InfrastructureAPI"; import {PropertyMeta} from "karavan-core/lib/model/CamelMetadata"; import {RouteToCreate} from "../../utils/CamelUi"; +import {Property} from "karavan-core/lib/model/KameletModels"; const SYNTAX_EXAMPLES = [ {key: 'property:', value: 'group.property', description: 'Application property'}, @@ -50,7 +51,7 @@ const SYNTAX_EXAMPLES = [ ] interface Props { - property: ComponentProperty | PropertyMeta, + property: ComponentProperty | PropertyMeta | Property, value: any, onDslPropertyChange?: (fieldId: string, value: string | number | boolean | any, newRoute?: RouteToCreate) => void, onComponentPropertyChange?: (parameter: string, value: string | number | boolean | any, pathParameter?: boolean, newRoute?: RouteToCreate) => void @@ -75,15 +76,17 @@ export function PropertyPlaceholderDropdown(props: Props) { && !propertyPlaceholders.includes(placeholderValue) && !SYNTAX_EXAMPLES.map(se=> se.value).includes(removeBrackets(placeholderValue)) && SYNTAX_EXAMPLES.findIndex(se=> removeBrackets(placeholderValue).startsWith(se.key)) === -1; - const popoverId = "popover-selector-" + property.name; + const popoverId = "popover-selector-" + property.hasOwnProperty('name') ? (property as any).name : (property as any).id; const hasPlaceholders = (propertyPlaceholders && propertyPlaceholders.length > 0 ); function parametersChanged(value: string | number | boolean | any) { if (property instanceof ComponentProperty) { props.onComponentPropertyChange?.(property.name, `{{${value}}}`, property.kind === 'path'); - } else { + } else if (property instanceof PropertyMeta) { props.onDslPropertyChange?.(property.name, `{{${value}}}`); + } else { + props.onDslPropertyChange?.((property as Property).id, `{{${value}}}`); } }