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