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 c0b7c67e3aa13a3decd6acd667d54e2f397ccab2 Author: Marat Gubaidullin <[email protected]> AuthorDate: Thu Sep 21 15:55:46 2023 -0400 Fix #900 --- karavan-space/src/designer/beans/BeanCard.tsx | 21 +- karavan-space/src/designer/beans/BeansDesigner.tsx | 77 +++--- karavan-space/src/designer/beans/bean.css | 109 ++++++++ karavan-space/src/designer/karavan.css | 208 ++-------------- karavan-space/src/designer/rest/RestCard.tsx | 1 + .../src/designer/rest/RestConfigurationCard.tsx | 1 + karavan-space/src/designer/rest/RestDesigner.tsx | 104 ++++---- karavan-space/src/designer/rest/rest.css | 273 +++++++++++++++++++++ .../src/designer/route/DslConnections.tsx | 6 +- karavan-space/src/designer/route/RouteDesigner.tsx | 9 +- .../src/main/webui/src/designer/beans/BeanCard.tsx | 21 +- .../webui/src/designer/beans/BeansDesigner.tsx | 77 +++--- .../src/main/webui/src/designer/beans/bean.css | 109 ++++++++ .../src/main/webui/src/designer/karavan.css | 208 ++-------------- .../src/main/webui/src/designer/rest/RestCard.tsx | 1 + .../src/designer/rest/RestConfigurationCard.tsx | 1 + .../main/webui/src/designer/rest/RestDesigner.tsx | 104 ++++---- .../src/main/webui/src/designer/rest/rest.css | 273 +++++++++++++++++++++ .../webui/src/designer/route/DslConnections.tsx | 6 +- .../webui/src/designer/route/RouteDesigner.tsx | 9 +- 20 files changed, 1062 insertions(+), 556 deletions(-) diff --git a/karavan-space/src/designer/beans/BeanCard.tsx b/karavan-space/src/designer/beans/BeanCard.tsx index 74dd884b..e9f1187d 100644 --- a/karavan-space/src/designer/beans/BeanCard.tsx +++ b/karavan-space/src/designer/beans/BeanCard.tsx @@ -16,9 +16,9 @@ */ import React from 'react'; import { - Button + Button, Flex, FlexItem } from '@patternfly/react-core'; -import '../karavan.css'; +import './bean.css'; import {RegistryBeanDefinition} from "karavan-core/lib/model/CamelDefinition"; import DeleteIcon from "@patternfly/react-icons/dist/js/icons/times-circle-icon"; import {useDesignerStore} from "../KaravanStore"; @@ -46,15 +46,18 @@ export function BeanCard (props: Props) { const bean = props.bean; return ( - <div className={selectedStep?.uuid === bean.uuid ? "rest-card rest-card-selected" : "rest-card rest-card-unselected"} onClick={e => selectElement(e)}> - <div className="header"> - <div className="title">Bean</div> - <div className="title">{bean.name}</div> - <div className="description">{bean.type}</div> + <Flex direction={{default: "row"}} + className={selectedStep?.uuid === bean.uuid ? "bean-card bean-card-selected" : "bean-card bean-card-unselected"} + onClick={e => selectElement(e)} + > + <FlexItem flex={{default:"flex_1"}} className="title">Bean</FlexItem> + <FlexItem flex={{default:"flex_2"}} className="title">{bean.name}</FlexItem> + <FlexItem flex={{default:"flex_3"}} align={{default: "alignRight"}} className="description">{bean.type}</FlexItem> + <FlexItem> <Button variant="link" className="delete-button" onClick={e => onDelete(e)}> <DeleteIcon/> </Button> - </div> - </div> + </FlexItem> + </Flex> ) } diff --git a/karavan-space/src/designer/beans/BeansDesigner.tsx b/karavan-space/src/designer/beans/BeansDesigner.tsx index 687c4ec2..8262b7bf 100644 --- a/karavan-space/src/designer/beans/BeansDesigner.tsx +++ b/karavan-space/src/designer/beans/BeansDesigner.tsx @@ -16,9 +16,16 @@ */ import React from 'react'; import { - Button, Drawer, DrawerContent, DrawerContentBody, DrawerPanelContent, Modal, PageSection + Button, + Drawer, + DrawerContent, + DrawerContentBody, + DrawerPanelContent, Flex, FlexItem, Gallery, GalleryItem, + Modal, + PageSection, } from '@patternfly/react-core'; import '../karavan.css'; +import './bean.css'; import {RegistryBeanDefinition} from "karavan-core/lib/model/CamelDefinition"; import {CamelUi} from "../utils/CamelUi"; import PlusIcon from "@patternfly/react-icons/dist/esm/icons/plus-icon"; @@ -29,26 +36,26 @@ import {BeanCard} from "./BeanCard"; import {useDesignerStore, useIntegrationStore} from "../KaravanStore"; import {shallow} from "zustand/shallow"; -export function BeansDesigner () { +export function BeansDesigner() { const [integration, setIntegration] = useIntegrationStore((s) => [s.integration, s.setIntegration], shallow) const [dark, selectedStep, showDeleteConfirmation, setShowDeleteConfirmation, setSelectedStep] = useDesignerStore((s) => [s.dark, s.selectedStep, s.showDeleteConfirmation, s.setShowDeleteConfirmation, s.setSelectedStep], shallow) - function onShowDeleteConfirmation (bean: RegistryBeanDefinition) { + function onShowDeleteConfirmation(bean: RegistryBeanDefinition) { setSelectedStep(bean); setShowDeleteConfirmation(true); } - function deleteBean () { + function deleteBean() { const i = CamelDefinitionApiExt.deleteBeanFromIntegration(integration, selectedStep); setIntegration(i, false); setShowDeleteConfirmation(false); setSelectedStep(undefined); } - function changeBean (bean: RegistryBeanDefinition) { + function changeBean(bean: RegistryBeanDefinition) { const clone = CamelUtil.cloneIntegration(integration); const i = CamelDefinitionApiExt.addBeanToIntegration(clone, bean); setIntegration(i, false); @@ -73,24 +80,28 @@ export function BeansDesigner () { </Modal>) } - function selectBean (bean?: RegistryBeanDefinition) { + function selectBean(bean?: RegistryBeanDefinition) { setSelectedStep(bean); } - function unselectBean (evt: React.MouseEvent<HTMLDivElement, MouseEvent>) { + function unselectBean(evt: React.MouseEvent<HTMLDivElement, MouseEvent>) { if ((evt.target as any).dataset.click === 'BEANS') { evt.stopPropagation() setSelectedStep(undefined); } }; - function createBean () { + function createBean() { changeBean(new RegistryBeanDefinition()); } function getPropertiesPanel() { return ( - <DrawerPanelContent isResizable hasNoBorder defaultSize={'400px'} maxSize={'800px'} minSize={'300px'}> + <DrawerPanelContent isResizable + hasNoBorder + defaultSize={'400px'} + maxSize={'800px'} + minSize={'400px'}> <BeanProperties integration={integration} dark={dark} onChange={changeBean} @@ -101,31 +112,41 @@ export function BeansDesigner () { const beans = CamelUi.getBeans(integration); return ( - <PageSection className="rest-page" isFilled padding={{default: 'noPadding'}}> - <div className="rest-page-columns"> - <Drawer isExpanded isInline> - <DrawerContent panelContent={getPropertiesPanel()}> - <DrawerContentBody> - <div className="graph" data-click="REST" onClick={event => unselectBean(event)}> - <div className="flows"> - {beans?.map((bean, index) => <BeanCard key={bean.uuid + index} - bean={bean} - selectElement={selectBean} - deleteElement={onShowDeleteConfirmation}/>)} - <div className="add-rest"> + <PageSection className="bean-designer" isFilled padding={{default: 'noPadding'}}> + <Drawer isExpanded isInline> + <DrawerContent panelContent={getPropertiesPanel()}> + <DrawerContentBody> + <Gallery className="gallery" + hasGutter + maxWidths={{ + default: '100%', + }} + > + {beans?.map((bean, index) => ( + <GalleryItem> + <BeanCard key={bean.uuid + index} + bean={bean} + selectElement={selectBean} + deleteElement={onShowDeleteConfirmation} + /> + </GalleryItem> + ))} + <GalleryItem> + <Flex direction={{default: "row"}} justifyContent={{default: "justifyContentCenter"}}> + <FlexItem> <Button variant={beans?.length === 0 ? "primary" : "secondary"} data-click="ADD_REST" icon={<PlusIcon/>} onClick={e => createBean()}>Create bean </Button> - </div> - </div> - </div> - </DrawerContentBody> - </DrawerContent> - </Drawer> - </div> + </FlexItem> + </Flex> + </GalleryItem> + </Gallery> + </DrawerContentBody> + </DrawerContent> + </Drawer> {getDeleteConfirmation()} </PageSection> ) diff --git a/karavan-space/src/designer/beans/bean.css b/karavan-space/src/designer/beans/bean.css new file mode 100644 index 00000000..38b8fb51 --- /dev/null +++ b/karavan-space/src/designer/beans/bean.css @@ -0,0 +1,109 @@ +/* + * 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 .bean-card { + /*max-width: 800px;*/ + border-style: dotted; + border-radius: 4px; + border-width: 1px; + padding: 16px 6px 16px 16px; + margin-right: 80px; + margin-left: 80px; + position: relative; +} + +.karavan .bean-card-unselected { + border-color: var(--pf-v5-global--Color--200); + background-color: transparent; +} + +.karavan .bean-card-selected { + border-color: var(--pf-v5-global--primary-color--100); +} + +.karavan .bean-card .title { + margin: auto 0 auto 0; + font-weight: bold; + white-space: nowrap; +} + +.karavan .bean-card .description { + margin: auto 0 auto 0; + min-width: 200px; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} + +.karavan .bean-card .delete-button { + position: absolute; + top: 3px; + right: 3px; + line-height: 1; + border: 0; + padding: 0; + margin: 0; + background: transparent; + color: #909090; + visibility: hidden; + z-index: 100; +} + +.karavan .bean-card:hover .delete-button { + visibility: visible; +} + +.karavan .bean-designer { + display: block; + height: 100vh; + width: 100%; + overflow-y: auto; + padding-bottom: 106px; +} + +.karavan .bean-designer .gallery { + padding-top: 16px; + padding-bottom: 16px; +} + +.karavan .bean-designer .properties { + padding: 10px 10px 10px 10px; + background: transparent; + width: 100%; + height: 100%; + overflow: auto; + display: flex; + flex-direction: column; + justify-content: space-between; +} +/*Beans*/ +.karavan .bean-designer .properties .bean-properties .pf-v5-c-form__group-control { + display: flex; + flex-direction: column; + gap: 6px; +} + +.karavan .bean-designer .properties .bean-property { + display: flex; + flex-direction: row; + gap: 3px; +} + +.karavan .bean-designer .properties .bean-property .delete-button { + padding: 3px; + color: #b1b1b7; +} \ No newline at end of file diff --git a/karavan-space/src/designer/karavan.css b/karavan-space/src/designer/karavan.css index f1fd43b4..fb04c267 100644 --- a/karavan-space/src/designer/karavan.css +++ b/karavan-space/src/designer/karavan.css @@ -23,6 +23,7 @@ padding-bottom: 0px; padding-right: 0; border-bottom: 1px solid #eee; + background-color: white; } .karavan .tools-section .dsl-title { @@ -34,6 +35,16 @@ margin-right: 16px; } +.karavan .tools-section .tools .header { + display: flex; + flex-direction: row; +} + +.karavan .tools-section .tools .header .labels { + height: fit-content; + margin-left: 3px; +} + .karavan .brand { height: 36px; } @@ -234,7 +245,7 @@ } .karavan .page { - height: 100%; + height: 100vh; width: 100%; overflow: hidden; display: flex; @@ -254,6 +265,7 @@ display: block; height: 100%; background: #fafafa; + padding-bottom: 66px; } .karavan .top-icon { @@ -321,17 +333,16 @@ } /*Properties*/ -.karavan .properties { - border: 1px solid #eee; +.karavan .dsl-page .properties { padding: 10px 10px 10px 10px; - background: #fcfcfc; + background: transparent; width: 100%; height: 100%; overflow: auto; display: flex; flex-direction: column; justify-content: space-between; - margin-bottom: 70px; + margin-bottom: 20px; } .karavan .pf-v5-c-drawer__splitter { @@ -591,7 +602,7 @@ .karavan .dsl-page .flows { width: 100%; position: absolute; - /*margin-bottom: 80px;*/ + padding-bottom: 66px; } .karavan .dsl-page .flows .add-flow { @@ -1044,173 +1055,6 @@ overflow-wrap: anywhere; } -/*REST Page*/ -.karavan .rest-page { - /*flex: 1;*/ - /*overflow: auto;*/ - /*height: 100%;*/ -} - -.karavan .rest-page .rest-page-columns { - display: block; - height: 100%; - background: #fafafa; -} - -.karavan .rest-page .graph { - display: block; - flex-direction: column; - height: 100%; - width: 100%; - position: relative; - overflow-y: auto; -} - -.karavan .rest-page .flows { - /*width: 800px;*/ - margin: 0 auto 80px auto; -} - -.karavan .rest-page .flows .add-flow { - margin-top: 16px; - display: flex; - justify-content: center; -} - -.karavan .rest-page .rest-config-card, -.karavan .rest-page .rest-card, -.karavan .rest-page .method-card { - border-style: dotted; - border-radius: 4px; - border-width: 1px; - padding: 0 6px 0 6px; - margin-bottom: 6px; - position: relative; -} - -.karavan .rest-page .method-card .rest-method-desc { - display: flex; - flex-direction: column; -} - -.karavan .rest-page .rest-card-unselected, -.karavan .rest-page .rest-config-card-unselected, -.karavan .rest-page .method-card-unselected { - border-color: var(--pf-v5-global--Color--200); - background-color: transparent; -} - -.karavan .rest-page .rest-card-selected, -.karavan .rest-page .rest-config-card-selected, -.karavan .rest-page .method-card-selected { - border-color: var(--pf-v5-global--primary-color--100); -} - -.karavan .rest-page .rest-card-selected .title { - color: var(--pf-v5-global--primary-color--100); -} - -.karavan .rest-page .rest-config-card, -.karavan .rest-page .rest-card { - display: flex; - flex-direction: column; - margin-top: 16px; -} - -.karavan .rest-page .header { - display: flex; - flex-direction: row; - gap: 16px; - height: 44px; - margin-left: 6px; - cursor: pointer; - justify-content: space-between; -} - -.karavan .rest-page .rest-config-card, -.karavan .rest-page .method-card { - display: flex; - flex-direction: row; - gap: 16px; - height: 44px; - cursor: pointer; -} - -.karavan .rest-page .method-card .method { - margin: auto 0 auto 0; - border-radius: 3px; - color: #fff; - font-family: sans-serif; - font-weight: 700; - min-width: 80px; - padding: 6px 0; - text-align: center; - text-shadow: 0 1px 0 rgb(0 0 0 / 10%); -} - -.karavan .rest-page .method-card-unselected .method { - background: var(--pf-v5-global--Color--400); -} - -.karavan .rest-page .method-card-selected .method { - background: var(--pf-v5-global--primary-color--100); -} - -.karavan .rest-page .rest-card .title, -.karavan .rest-page .rest-config-card .title, -.karavan .rest-page .method-card .title { - margin: auto 0 auto 0; - font-weight: bold; - white-space: nowrap; -} - -.karavan .rest-page .rest-card .description, -.karavan .rest-page .rest-config-card .description, -.karavan .rest-page .method-card .description { - margin: auto 0 auto 0; - min-width: 200px; - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; -} - -.karavan .rest-page .rest-config-card .delete-button, -.karavan .rest-page .rest-card .delete-button, -.karavan .rest-page .method-card .delete-button { - position: absolute; - top: 3px; - right: 3px; - line-height: 1; - border: 0; - padding: 0; - margin: 0; - background: transparent; - color: #909090; - visibility: hidden; - z-index: 100; -} - -.karavan .rest-page .rest-config-card:hover .delete-button, -.karavan .rest-page .rest-card:hover .delete-button, -.karavan .rest-page .method-card:hover .delete-button { - visibility: visible; -} - -.karavan .rest-page .rest-card .add-button { - font-size: 15px; - border: 0; - background: transparent; - color: var(--pf-v5-global--primary-color--100); - z-index: 100; -} - -.karavan .rest-page .add-rest { - display: flex; - flex-direction: row; - justify-content: center; - margin-top: 16px; - gap: 6px; -} /*Beans*/ .karavan .rest-page .properties .bean-properties .pf-v5-c-form__group-control { @@ -1265,24 +1109,6 @@ margin-right: auto; } - -.karavan .tools-section { - background-color: white; -} -.karavan .tools-section .tools .header { - display: flex; - flex-direction: row; -} - -.karavan .tools-section .tools .header .labels { - height: fit-content; - margin-left: 3px; -} - -.karavan .tools-section .knowledge-tabs { - background-color: white; -} - /* Project Tools */ .karavan .project-builder { height: 100%; diff --git a/karavan-space/src/designer/rest/RestCard.tsx b/karavan-space/src/designer/rest/RestCard.tsx index 5177f41c..ae6a24d2 100644 --- a/karavan-space/src/designer/rest/RestCard.tsx +++ b/karavan-space/src/designer/rest/RestCard.tsx @@ -18,6 +18,7 @@ import React from 'react'; import { Button, Tooltip } from '@patternfly/react-core'; +import './rest.css'; import '../karavan.css'; import {CamelElement, Integration} from "karavan-core/lib/model/IntegrationDefinition"; import {GetDefinition, RestDefinition} from "karavan-core/lib/model/CamelDefinition"; diff --git a/karavan-space/src/designer/rest/RestConfigurationCard.tsx b/karavan-space/src/designer/rest/RestConfigurationCard.tsx index 8c7ba2ff..3c11153d 100644 --- a/karavan-space/src/designer/rest/RestConfigurationCard.tsx +++ b/karavan-space/src/designer/rest/RestConfigurationCard.tsx @@ -16,6 +16,7 @@ */ import React from 'react'; import {Button} from '@patternfly/react-core'; +import './rest.css'; import '../karavan.css'; import {CamelElement} from "karavan-core/lib/model/IntegrationDefinition"; import DeleteIcon from "@patternfly/react-icons/dist/js/icons/times-circle-icon"; diff --git a/karavan-space/src/designer/rest/RestDesigner.tsx b/karavan-space/src/designer/rest/RestDesigner.tsx index 78ed4be8..4a607cad 100644 --- a/karavan-space/src/designer/rest/RestDesigner.tsx +++ b/karavan-space/src/designer/rest/RestDesigner.tsx @@ -16,15 +16,20 @@ */ import React from 'react'; import { - Button, Drawer, DrawerContent, DrawerContentBody, DrawerPanelContent, Modal, + Button, Drawer, DrawerContent, DrawerContentBody, DrawerPanelContent, Flex, FlexItem, Gallery, GalleryItem, Modal, PageSection } from '@patternfly/react-core'; +import './rest.css'; import '../karavan.css'; import {CamelElement} from "karavan-core/lib/model/IntegrationDefinition"; import {DslProperties} from "../route/DslProperties"; import {RestCard} from "./RestCard"; import PlusIcon from "@patternfly/react-icons/dist/esm/icons/plus-icon"; -import {RestConfigurationDefinition, RestContextRefDefinition, RestDefinition} from "karavan-core/lib/model/CamelDefinition"; +import { + RestConfigurationDefinition, + RestContextRefDefinition, + RestDefinition +} from "karavan-core/lib/model/CamelDefinition"; import {CamelUtil} from "karavan-core/lib/api/CamelUtil"; import {CamelDefinitionApiExt} from "karavan-core/lib/api/CamelDefinitionApiExt"; import {RestMethodSelector} from "./RestMethodSelector"; @@ -42,39 +47,39 @@ export function RestDesigner() { [s.dark, s.selectedStep, s.showDeleteConfirmation, s.setShowDeleteConfirmation, s.setPosition, s.width, s.height, s.top, s.left, s.hideLogDSL, s.setSelectedStep], shallow) const [showSelector, setShowSelector] = useSelectorStore((s) => [s.showSelector, s.setShowSelector], shallow) - - function selectElement (element: CamelElement) { + + function selectElement(element: CamelElement) { setSelectedStep(element); } - function unselectElement (evt: React.MouseEvent<HTMLDivElement, MouseEvent>) { + function unselectElement(evt: React.MouseEvent<HTMLDivElement, MouseEvent>) { if ((evt.target as any).dataset.click === 'REST') { evt.stopPropagation() setSelectedStep(undefined); } } - function addRest (rest: RestDefinition) { + function addRest(rest: RestDefinition) { const clone = CamelUtil.cloneIntegration(integration); const i = CamelDefinitionApiExt.addRestToIntegration(clone, rest); setIntegration(i, false); setSelectedStep(rest); } - function createRest () { + function createRest() { addRest(new RestDefinition()); } - function createRestConfiguration () { + function createRestConfiguration() { addRest(new RestConfigurationDefinition()); } - function onShowDeleteConfirmation (element: CamelElement) { + function onShowDeleteConfirmation(element: CamelElement) { setSelectedStep(element); setShowDeleteConfirmation(true); } - function deleteElement () { + function deleteElement() { if (selectedStep) { let i; if (selectedStep.dslName === 'RestDefinition') i = CamelDefinitionApiExt.deleteRestFromIntegration(integration, selectedStep.uuid); @@ -104,11 +109,11 @@ export function RestDesigner() { </Modal>) } - function closeMethodSelector () { + function closeMethodSelector() { setShowSelector(false); } - function onMethodSelect (method: DslMetaModel) { + function onMethodSelect(method: DslMetaModel) { if (selectedStep) { const clone = CamelUtil.cloneIntegration(integration); const m = CamelDefinitionApi.createStep(method.dsl, {}); @@ -119,8 +124,8 @@ export function RestDesigner() { } } - function cloneRest (rest: CamelElement) { - if (rest.dslName === 'RestDefinition'){ + function cloneRest(rest: CamelElement) { + if (rest.dslName === 'RestDefinition') { const cloneRest = CamelUtil.cloneStep(rest); cloneRest.uuid = uuidv4(); const cloneIntegration = CamelUtil.cloneIntegration(integration); @@ -131,7 +136,7 @@ export function RestDesigner() { // could be only one RestConfigurationDefinition } else if (selectedStep) { const parentId = CamelDefinitionApiExt.findRestMethodParent(integration, rest); - if (parentId){ + if (parentId) { const cloneRest = CamelUtil.cloneStep(rest); cloneRest.uuid = uuidv4(); const cloneIntegration = CamelUtil.cloneIntegration(integration); @@ -142,7 +147,7 @@ export function RestDesigner() { } } - function selectMethod (element: CamelElement) { + function selectMethod(element: CamelElement) { setSelectedStep(element); setShowSelector(true); } @@ -174,12 +179,12 @@ export function RestDesigner() { return (<> {data?.map((rest, index) => <RestCard key={rest.uuid + index} - selectedStep={selectedStep} - rest={rest} - integration={integration} - selectMethod={selectMethod} - selectElement={selectElement} - deleteElement={onShowDeleteConfirmation} + selectedStep={selectedStep} + rest={rest} + integration={integration} + selectMethod={selectMethod} + selectElement={selectElement} + deleteElement={onShowDeleteConfirmation} /> )} </>) @@ -198,37 +203,46 @@ export function RestDesigner() { const configData = integration.spec.flows?.filter(f => f.dslName === 'RestConfigurationDefinition'); const config = configData && Array.isArray(configData) ? configData[0] : undefined; return ( - <PageSection className="rest-page" isFilled padding={{default: 'noPadding'}}> - <div className="rest-page-columns"> - <Drawer isExpanded isInline> - <DrawerContent panelContent={getPropertiesPanel()}> - <DrawerContentBody> - <div className="graph" data-click="REST" onClick={event => unselectElement(event)}> - <div className="flows"> - {config && getRestConfigurationCard(config)} - {data && getRestCards(data)} - <div className="add-rest"> + <PageSection className="rest-designer" isFilled padding={{default: 'noPadding'}}> + <Drawer isExpanded isInline> + <DrawerContent panelContent={getPropertiesPanel()}> + <DrawerContentBody> + <Gallery className="gallery" + hasGutter + maxWidths={{ + default: '100%', + }} + > + {config && getRestConfigurationCard(config)} + {data && getRestCards(data)} + <GalleryItem> + <Flex direction={{default: "row"}} justifyContent={{default: "justifyContentCenter"}}> + <FlexItem> <Button variant={data?.length === 0 ? "primary" : "secondary"} data-click="ADD_REST" icon={<PlusIcon/>} onClick={e => createRest()}>Create service </Button> + </FlexItem> + <FlexItem> {config === undefined && - <Button - variant="secondary" - data-click="ADD_REST_REST_CONFIG" - icon={<PlusIcon/>} - onClick={e => createRestConfiguration()}>Create configuration - </Button> + <GalleryItem> + <Button + variant="secondary" + data-click="ADD_REST_REST_CONFIG" + icon={<PlusIcon/>} + onClick={e => createRestConfiguration()}>Create configuration + </Button> + </GalleryItem> } - </div> - </div> - </div> - </DrawerContentBody> - </DrawerContent> - </Drawer> - </div> + </FlexItem> + </Flex> + </GalleryItem> + </Gallery> + </DrawerContentBody> + </DrawerContent> + </Drawer> {getSelectorModal()} {getDeleteConfirmation()} </PageSection> diff --git a/karavan-space/src/designer/rest/rest.css b/karavan-space/src/designer/rest/rest.css new file mode 100644 index 00000000..25012e2d --- /dev/null +++ b/karavan-space/src/designer/rest/rest.css @@ -0,0 +1,273 @@ +/* + * 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 .rest-designer .rest-card, +.karavan .rest-designer .rest-config-card, +.karavan .rest-designer .method-card { + border-style: dotted; + border-radius: 4px; + border-width: 1px; +} + +.karavan .rest-designer .rest-card { + padding: 16px 16px 6px 16px; + margin-right: 80px; + margin-left: 80px; +} + +.karavan .rest-designer .rest-config-card { + padding: 16px 16px 34px 16px; + margin-right: 80px; + margin-left: 80px; +} + +.karavan .rest-designer .method-card { + padding: 0 0 0 6px; + margin-bottom: 6px; +} + +.karavan .rest-card-unselected { + border-color: var(--pf-v5-global--Color--200); + background-color: transparent; +} + +.karavan .rest-card-selected { + border-color: var(--pf-v5-global--primary-color--100); +} + +.karavan .rest-card .title { + margin: auto 0 auto 0; + font-weight: bold; + white-space: nowrap; +} + +.karavan .rest-card .description { + margin: auto 0 auto 0; + min-width: 200px; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} + +.karavan .rest-card .delete-button { + position: absolute; + top: 3px; + right: 3px; + line-height: 1; + border: 0; + padding: 0; + margin: 0; + background: transparent; + color: #909090; + visibility: hidden; + z-index: 100; +} + +.karavan .rest-card:hover .delete-button { + visibility: visible; +} + +.karavan .rest-designer { + display: block; + height: 100vh; + width: 100%; + overflow-y: auto; +} + +.karavan .rest-designer .gallery { + padding-top: 16px; + padding-bottom: 116px; +} + +.karavan .rest-designer .properties { + padding: 10px 10px 10px 10px; + background: transparent; + width: 100%; + height: 100%; + overflow: auto; + display: flex; + flex-direction: column; + justify-content: space-between; + margin-bottom: 106px; +} +/*rests*/ +.karavan .rest-designer .properties .rest-properties .pf-v5-c-form__group-control { + display: flex; + flex-direction: column; + gap: 6px; +} + +.karavan .rest-designer .properties .rest-property { + display: flex; + flex-direction: row; + gap: 3px; +} + +.karavan .rest-designer .properties .rest-property .delete-button { + padding: 3px; + color: #b1b1b7; +} + +.karavan .rest-designer .rest-designer-columns { + display: block; + height: 100%; + background: #fafafa; +} + +.karavan .rest-designer .graph { + display: block; + flex-direction: column; + height: 100%; + width: 100%; + position: relative; + overflow-y: auto; +} + +.karavan .rest-designer .flows { + /*width: 800px;*/ + margin: 0 auto 80px auto; +} + +.karavan .rest-designer .flows .add-flow { + margin-top: 16px; + display: flex; + justify-content: center; +} + +.karavan .rest-designer .method-card .rest-method-desc { + display: flex; + flex-direction: column; +} + +.karavan .rest-designer .rest-card-unselected, +.karavan .rest-designer .rest-config-card-unselected, +.karavan .rest-designer .method-card-unselected { + border-color: var(--pf-v5-global--Color--200); + background-color: transparent; +} + +.karavan .rest-designer .rest-card-selected, +.karavan .rest-designer .rest-config-card-selected, +.karavan .rest-designer .method-card-selected { + border-color: var(--pf-v5-global--primary-color--100); +} + +.karavan .rest-designer .rest-card-selected .title { + color: var(--pf-v5-global--primary-color--100); +} + +.karavan .rest-designer .rest-config-card, +.karavan .rest-designer .rest-card { + display: flex; + flex-direction: column; + margin-top: 16px; +} + +.karavan .rest-designer .header { + display: flex; + flex-direction: row; + gap: 16px; + height: 44px; + margin-left: 6px; + cursor: pointer; + justify-content: space-between; +} + +.karavan .rest-designer .rest-config-card, +.karavan .rest-designer .method-card { + display: flex; + flex-direction: row; + gap: 16px; + height: 44px; + cursor: pointer; +} + +.karavan .rest-designer .method-card .method { + margin: auto 0 auto 0; + border-radius: 3px; + color: #fff; + font-family: sans-serif; + font-weight: 700; + min-width: 80px; + padding: 6px 0; + text-align: center; + text-shadow: 0 1px 0 rgb(0 0 0 / 10%); +} + +.karavan .rest-designer .method-card-unselected .method { + background: var(--pf-v5-global--Color--400); +} + +.karavan .rest-designer .method-card-selected .method { + background: var(--pf-v5-global--primary-color--100); +} + +.karavan .rest-designer .rest-card .title, +.karavan .rest-designer .rest-config-card .title, +.karavan .rest-designer .method-card .title { + margin: auto 0 auto 0; + font-weight: bold; + white-space: nowrap; +} + +.karavan .rest-designer .rest-card .description, +.karavan .rest-designer .rest-config-card .description, +.karavan .rest-designer .method-card .description { + margin: auto 0 auto 0; + min-width: 200px; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} + +.karavan .rest-designer .rest-config-card .delete-button, +.karavan .rest-designer .rest-card .delete-button, +.karavan .rest-designer .method-card .delete-button { + position: absolute; + top: 3px; + right: 3px; + line-height: 1; + border: 0; + padding: 0; + margin: 0; + background: transparent; + color: #909090; + visibility: hidden; + z-index: 100; +} + +.karavan .rest-designer .rest-config-card:hover .delete-button, +.karavan .rest-designer .rest-card:hover .delete-button, +.karavan .rest-designer .method-card:hover .delete-button { + visibility: visible; +} + +.karavan .rest-designer .rest-card .add-button { + font-size: 15px; + border: 0; + background: transparent; + color: var(--pf-v5-global--primary-color--100); + z-index: 100; +} + +.karavan .rest-designer .add-rest { + display: flex; + flex-direction: row; + justify-content: center; + margin-top: 16px; + gap: 6px; +} diff --git a/karavan-space/src/designer/route/DslConnections.tsx b/karavan-space/src/designer/route/DslConnections.tsx index 44f23042..f39a1aa8 100644 --- a/karavan-space/src/designer/route/DslConnections.tsx +++ b/karavan-space/src/designer/route/DslConnections.tsx @@ -433,8 +433,8 @@ export function DslConnections() { const stepsArray = Array.from(steps.values()); return ( <svg - style={{width: width, height: height + 80, position: "absolute", left: 0, top: 0}} - viewBox={"0 0 " + (width) + " " + (height + 80)}> + style={{width: width, height: height, position: "absolute", left: 0, top: 0}} + viewBox={"0 0 " + (width) + " " + (height)}> <defs> <marker id="arrowhead" markerWidth="9" markerHeight="6" refX="0" refY="3" orient="auto" className="arrow"> <polygon points="0 0, 9 3, 0 6"/> @@ -450,7 +450,7 @@ export function DslConnections() { } return ( - <div id="connections" className="connections" style={{ width: width, height: height + 80}}> + <div id="connections" className="connections" style={{ width: width, height: height}}> {getSvg()} {getIncomings().map(p => getIncomingIcons(p))} {getOutgoings().map(p => getOutgoingIcons(p))} diff --git a/karavan-space/src/designer/route/RouteDesigner.tsx b/karavan-space/src/designer/route/RouteDesigner.tsx index c81f6c30..a78a4eed 100644 --- a/karavan-space/src/designer/route/RouteDesigner.tsx +++ b/karavan-space/src/designer/route/RouteDesigner.tsx @@ -90,8 +90,13 @@ export function RouteDesigner() { function getPropertiesPanel() { return ( - <DrawerPanelContent style={{transform: "initial"}} isResizable hasNoBorder defaultSize={'400px'} - maxSize={'800px'} minSize={'300px'}> + <DrawerPanelContent style={{transform: "initial"}} + isResizable + hasNoBorder + defaultSize={'400px'} + maxSize={'800px'} + minSize={'400px'} + > <DslProperties isRouteDesigner={true}/> </DrawerPanelContent> ) diff --git a/karavan-web/karavan-app/src/main/webui/src/designer/beans/BeanCard.tsx b/karavan-web/karavan-app/src/main/webui/src/designer/beans/BeanCard.tsx index 74dd884b..e9f1187d 100644 --- a/karavan-web/karavan-app/src/main/webui/src/designer/beans/BeanCard.tsx +++ b/karavan-web/karavan-app/src/main/webui/src/designer/beans/BeanCard.tsx @@ -16,9 +16,9 @@ */ import React from 'react'; import { - Button + Button, Flex, FlexItem } from '@patternfly/react-core'; -import '../karavan.css'; +import './bean.css'; import {RegistryBeanDefinition} from "karavan-core/lib/model/CamelDefinition"; import DeleteIcon from "@patternfly/react-icons/dist/js/icons/times-circle-icon"; import {useDesignerStore} from "../KaravanStore"; @@ -46,15 +46,18 @@ export function BeanCard (props: Props) { const bean = props.bean; return ( - <div className={selectedStep?.uuid === bean.uuid ? "rest-card rest-card-selected" : "rest-card rest-card-unselected"} onClick={e => selectElement(e)}> - <div className="header"> - <div className="title">Bean</div> - <div className="title">{bean.name}</div> - <div className="description">{bean.type}</div> + <Flex direction={{default: "row"}} + className={selectedStep?.uuid === bean.uuid ? "bean-card bean-card-selected" : "bean-card bean-card-unselected"} + onClick={e => selectElement(e)} + > + <FlexItem flex={{default:"flex_1"}} className="title">Bean</FlexItem> + <FlexItem flex={{default:"flex_2"}} className="title">{bean.name}</FlexItem> + <FlexItem flex={{default:"flex_3"}} align={{default: "alignRight"}} className="description">{bean.type}</FlexItem> + <FlexItem> <Button variant="link" className="delete-button" onClick={e => onDelete(e)}> <DeleteIcon/> </Button> - </div> - </div> + </FlexItem> + </Flex> ) } diff --git a/karavan-web/karavan-app/src/main/webui/src/designer/beans/BeansDesigner.tsx b/karavan-web/karavan-app/src/main/webui/src/designer/beans/BeansDesigner.tsx index 687c4ec2..8262b7bf 100644 --- a/karavan-web/karavan-app/src/main/webui/src/designer/beans/BeansDesigner.tsx +++ b/karavan-web/karavan-app/src/main/webui/src/designer/beans/BeansDesigner.tsx @@ -16,9 +16,16 @@ */ import React from 'react'; import { - Button, Drawer, DrawerContent, DrawerContentBody, DrawerPanelContent, Modal, PageSection + Button, + Drawer, + DrawerContent, + DrawerContentBody, + DrawerPanelContent, Flex, FlexItem, Gallery, GalleryItem, + Modal, + PageSection, } from '@patternfly/react-core'; import '../karavan.css'; +import './bean.css'; import {RegistryBeanDefinition} from "karavan-core/lib/model/CamelDefinition"; import {CamelUi} from "../utils/CamelUi"; import PlusIcon from "@patternfly/react-icons/dist/esm/icons/plus-icon"; @@ -29,26 +36,26 @@ import {BeanCard} from "./BeanCard"; import {useDesignerStore, useIntegrationStore} from "../KaravanStore"; import {shallow} from "zustand/shallow"; -export function BeansDesigner () { +export function BeansDesigner() { const [integration, setIntegration] = useIntegrationStore((s) => [s.integration, s.setIntegration], shallow) const [dark, selectedStep, showDeleteConfirmation, setShowDeleteConfirmation, setSelectedStep] = useDesignerStore((s) => [s.dark, s.selectedStep, s.showDeleteConfirmation, s.setShowDeleteConfirmation, s.setSelectedStep], shallow) - function onShowDeleteConfirmation (bean: RegistryBeanDefinition) { + function onShowDeleteConfirmation(bean: RegistryBeanDefinition) { setSelectedStep(bean); setShowDeleteConfirmation(true); } - function deleteBean () { + function deleteBean() { const i = CamelDefinitionApiExt.deleteBeanFromIntegration(integration, selectedStep); setIntegration(i, false); setShowDeleteConfirmation(false); setSelectedStep(undefined); } - function changeBean (bean: RegistryBeanDefinition) { + function changeBean(bean: RegistryBeanDefinition) { const clone = CamelUtil.cloneIntegration(integration); const i = CamelDefinitionApiExt.addBeanToIntegration(clone, bean); setIntegration(i, false); @@ -73,24 +80,28 @@ export function BeansDesigner () { </Modal>) } - function selectBean (bean?: RegistryBeanDefinition) { + function selectBean(bean?: RegistryBeanDefinition) { setSelectedStep(bean); } - function unselectBean (evt: React.MouseEvent<HTMLDivElement, MouseEvent>) { + function unselectBean(evt: React.MouseEvent<HTMLDivElement, MouseEvent>) { if ((evt.target as any).dataset.click === 'BEANS') { evt.stopPropagation() setSelectedStep(undefined); } }; - function createBean () { + function createBean() { changeBean(new RegistryBeanDefinition()); } function getPropertiesPanel() { return ( - <DrawerPanelContent isResizable hasNoBorder defaultSize={'400px'} maxSize={'800px'} minSize={'300px'}> + <DrawerPanelContent isResizable + hasNoBorder + defaultSize={'400px'} + maxSize={'800px'} + minSize={'400px'}> <BeanProperties integration={integration} dark={dark} onChange={changeBean} @@ -101,31 +112,41 @@ export function BeansDesigner () { const beans = CamelUi.getBeans(integration); return ( - <PageSection className="rest-page" isFilled padding={{default: 'noPadding'}}> - <div className="rest-page-columns"> - <Drawer isExpanded isInline> - <DrawerContent panelContent={getPropertiesPanel()}> - <DrawerContentBody> - <div className="graph" data-click="REST" onClick={event => unselectBean(event)}> - <div className="flows"> - {beans?.map((bean, index) => <BeanCard key={bean.uuid + index} - bean={bean} - selectElement={selectBean} - deleteElement={onShowDeleteConfirmation}/>)} - <div className="add-rest"> + <PageSection className="bean-designer" isFilled padding={{default: 'noPadding'}}> + <Drawer isExpanded isInline> + <DrawerContent panelContent={getPropertiesPanel()}> + <DrawerContentBody> + <Gallery className="gallery" + hasGutter + maxWidths={{ + default: '100%', + }} + > + {beans?.map((bean, index) => ( + <GalleryItem> + <BeanCard key={bean.uuid + index} + bean={bean} + selectElement={selectBean} + deleteElement={onShowDeleteConfirmation} + /> + </GalleryItem> + ))} + <GalleryItem> + <Flex direction={{default: "row"}} justifyContent={{default: "justifyContentCenter"}}> + <FlexItem> <Button variant={beans?.length === 0 ? "primary" : "secondary"} data-click="ADD_REST" icon={<PlusIcon/>} onClick={e => createBean()}>Create bean </Button> - </div> - </div> - </div> - </DrawerContentBody> - </DrawerContent> - </Drawer> - </div> + </FlexItem> + </Flex> + </GalleryItem> + </Gallery> + </DrawerContentBody> + </DrawerContent> + </Drawer> {getDeleteConfirmation()} </PageSection> ) diff --git a/karavan-web/karavan-app/src/main/webui/src/designer/beans/bean.css b/karavan-web/karavan-app/src/main/webui/src/designer/beans/bean.css new file mode 100644 index 00000000..38b8fb51 --- /dev/null +++ b/karavan-web/karavan-app/src/main/webui/src/designer/beans/bean.css @@ -0,0 +1,109 @@ +/* + * 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 .bean-card { + /*max-width: 800px;*/ + border-style: dotted; + border-radius: 4px; + border-width: 1px; + padding: 16px 6px 16px 16px; + margin-right: 80px; + margin-left: 80px; + position: relative; +} + +.karavan .bean-card-unselected { + border-color: var(--pf-v5-global--Color--200); + background-color: transparent; +} + +.karavan .bean-card-selected { + border-color: var(--pf-v5-global--primary-color--100); +} + +.karavan .bean-card .title { + margin: auto 0 auto 0; + font-weight: bold; + white-space: nowrap; +} + +.karavan .bean-card .description { + margin: auto 0 auto 0; + min-width: 200px; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} + +.karavan .bean-card .delete-button { + position: absolute; + top: 3px; + right: 3px; + line-height: 1; + border: 0; + padding: 0; + margin: 0; + background: transparent; + color: #909090; + visibility: hidden; + z-index: 100; +} + +.karavan .bean-card:hover .delete-button { + visibility: visible; +} + +.karavan .bean-designer { + display: block; + height: 100vh; + width: 100%; + overflow-y: auto; + padding-bottom: 106px; +} + +.karavan .bean-designer .gallery { + padding-top: 16px; + padding-bottom: 16px; +} + +.karavan .bean-designer .properties { + padding: 10px 10px 10px 10px; + background: transparent; + width: 100%; + height: 100%; + overflow: auto; + display: flex; + flex-direction: column; + justify-content: space-between; +} +/*Beans*/ +.karavan .bean-designer .properties .bean-properties .pf-v5-c-form__group-control { + display: flex; + flex-direction: column; + gap: 6px; +} + +.karavan .bean-designer .properties .bean-property { + display: flex; + flex-direction: row; + gap: 3px; +} + +.karavan .bean-designer .properties .bean-property .delete-button { + padding: 3px; + color: #b1b1b7; +} \ No newline at end of file diff --git a/karavan-web/karavan-app/src/main/webui/src/designer/karavan.css b/karavan-web/karavan-app/src/main/webui/src/designer/karavan.css index f1fd43b4..fb04c267 100644 --- a/karavan-web/karavan-app/src/main/webui/src/designer/karavan.css +++ b/karavan-web/karavan-app/src/main/webui/src/designer/karavan.css @@ -23,6 +23,7 @@ padding-bottom: 0px; padding-right: 0; border-bottom: 1px solid #eee; + background-color: white; } .karavan .tools-section .dsl-title { @@ -34,6 +35,16 @@ margin-right: 16px; } +.karavan .tools-section .tools .header { + display: flex; + flex-direction: row; +} + +.karavan .tools-section .tools .header .labels { + height: fit-content; + margin-left: 3px; +} + .karavan .brand { height: 36px; } @@ -234,7 +245,7 @@ } .karavan .page { - height: 100%; + height: 100vh; width: 100%; overflow: hidden; display: flex; @@ -254,6 +265,7 @@ display: block; height: 100%; background: #fafafa; + padding-bottom: 66px; } .karavan .top-icon { @@ -321,17 +333,16 @@ } /*Properties*/ -.karavan .properties { - border: 1px solid #eee; +.karavan .dsl-page .properties { padding: 10px 10px 10px 10px; - background: #fcfcfc; + background: transparent; width: 100%; height: 100%; overflow: auto; display: flex; flex-direction: column; justify-content: space-between; - margin-bottom: 70px; + margin-bottom: 20px; } .karavan .pf-v5-c-drawer__splitter { @@ -591,7 +602,7 @@ .karavan .dsl-page .flows { width: 100%; position: absolute; - /*margin-bottom: 80px;*/ + padding-bottom: 66px; } .karavan .dsl-page .flows .add-flow { @@ -1044,173 +1055,6 @@ overflow-wrap: anywhere; } -/*REST Page*/ -.karavan .rest-page { - /*flex: 1;*/ - /*overflow: auto;*/ - /*height: 100%;*/ -} - -.karavan .rest-page .rest-page-columns { - display: block; - height: 100%; - background: #fafafa; -} - -.karavan .rest-page .graph { - display: block; - flex-direction: column; - height: 100%; - width: 100%; - position: relative; - overflow-y: auto; -} - -.karavan .rest-page .flows { - /*width: 800px;*/ - margin: 0 auto 80px auto; -} - -.karavan .rest-page .flows .add-flow { - margin-top: 16px; - display: flex; - justify-content: center; -} - -.karavan .rest-page .rest-config-card, -.karavan .rest-page .rest-card, -.karavan .rest-page .method-card { - border-style: dotted; - border-radius: 4px; - border-width: 1px; - padding: 0 6px 0 6px; - margin-bottom: 6px; - position: relative; -} - -.karavan .rest-page .method-card .rest-method-desc { - display: flex; - flex-direction: column; -} - -.karavan .rest-page .rest-card-unselected, -.karavan .rest-page .rest-config-card-unselected, -.karavan .rest-page .method-card-unselected { - border-color: var(--pf-v5-global--Color--200); - background-color: transparent; -} - -.karavan .rest-page .rest-card-selected, -.karavan .rest-page .rest-config-card-selected, -.karavan .rest-page .method-card-selected { - border-color: var(--pf-v5-global--primary-color--100); -} - -.karavan .rest-page .rest-card-selected .title { - color: var(--pf-v5-global--primary-color--100); -} - -.karavan .rest-page .rest-config-card, -.karavan .rest-page .rest-card { - display: flex; - flex-direction: column; - margin-top: 16px; -} - -.karavan .rest-page .header { - display: flex; - flex-direction: row; - gap: 16px; - height: 44px; - margin-left: 6px; - cursor: pointer; - justify-content: space-between; -} - -.karavan .rest-page .rest-config-card, -.karavan .rest-page .method-card { - display: flex; - flex-direction: row; - gap: 16px; - height: 44px; - cursor: pointer; -} - -.karavan .rest-page .method-card .method { - margin: auto 0 auto 0; - border-radius: 3px; - color: #fff; - font-family: sans-serif; - font-weight: 700; - min-width: 80px; - padding: 6px 0; - text-align: center; - text-shadow: 0 1px 0 rgb(0 0 0 / 10%); -} - -.karavan .rest-page .method-card-unselected .method { - background: var(--pf-v5-global--Color--400); -} - -.karavan .rest-page .method-card-selected .method { - background: var(--pf-v5-global--primary-color--100); -} - -.karavan .rest-page .rest-card .title, -.karavan .rest-page .rest-config-card .title, -.karavan .rest-page .method-card .title { - margin: auto 0 auto 0; - font-weight: bold; - white-space: nowrap; -} - -.karavan .rest-page .rest-card .description, -.karavan .rest-page .rest-config-card .description, -.karavan .rest-page .method-card .description { - margin: auto 0 auto 0; - min-width: 200px; - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; -} - -.karavan .rest-page .rest-config-card .delete-button, -.karavan .rest-page .rest-card .delete-button, -.karavan .rest-page .method-card .delete-button { - position: absolute; - top: 3px; - right: 3px; - line-height: 1; - border: 0; - padding: 0; - margin: 0; - background: transparent; - color: #909090; - visibility: hidden; - z-index: 100; -} - -.karavan .rest-page .rest-config-card:hover .delete-button, -.karavan .rest-page .rest-card:hover .delete-button, -.karavan .rest-page .method-card:hover .delete-button { - visibility: visible; -} - -.karavan .rest-page .rest-card .add-button { - font-size: 15px; - border: 0; - background: transparent; - color: var(--pf-v5-global--primary-color--100); - z-index: 100; -} - -.karavan .rest-page .add-rest { - display: flex; - flex-direction: row; - justify-content: center; - margin-top: 16px; - gap: 6px; -} /*Beans*/ .karavan .rest-page .properties .bean-properties .pf-v5-c-form__group-control { @@ -1265,24 +1109,6 @@ margin-right: auto; } - -.karavan .tools-section { - background-color: white; -} -.karavan .tools-section .tools .header { - display: flex; - flex-direction: row; -} - -.karavan .tools-section .tools .header .labels { - height: fit-content; - margin-left: 3px; -} - -.karavan .tools-section .knowledge-tabs { - background-color: white; -} - /* Project Tools */ .karavan .project-builder { height: 100%; diff --git a/karavan-web/karavan-app/src/main/webui/src/designer/rest/RestCard.tsx b/karavan-web/karavan-app/src/main/webui/src/designer/rest/RestCard.tsx index 5177f41c..ae6a24d2 100644 --- a/karavan-web/karavan-app/src/main/webui/src/designer/rest/RestCard.tsx +++ b/karavan-web/karavan-app/src/main/webui/src/designer/rest/RestCard.tsx @@ -18,6 +18,7 @@ import React from 'react'; import { Button, Tooltip } from '@patternfly/react-core'; +import './rest.css'; import '../karavan.css'; import {CamelElement, Integration} from "karavan-core/lib/model/IntegrationDefinition"; import {GetDefinition, RestDefinition} from "karavan-core/lib/model/CamelDefinition"; diff --git a/karavan-web/karavan-app/src/main/webui/src/designer/rest/RestConfigurationCard.tsx b/karavan-web/karavan-app/src/main/webui/src/designer/rest/RestConfigurationCard.tsx index 8c7ba2ff..3c11153d 100644 --- a/karavan-web/karavan-app/src/main/webui/src/designer/rest/RestConfigurationCard.tsx +++ b/karavan-web/karavan-app/src/main/webui/src/designer/rest/RestConfigurationCard.tsx @@ -16,6 +16,7 @@ */ import React from 'react'; import {Button} from '@patternfly/react-core'; +import './rest.css'; import '../karavan.css'; import {CamelElement} from "karavan-core/lib/model/IntegrationDefinition"; import DeleteIcon from "@patternfly/react-icons/dist/js/icons/times-circle-icon"; diff --git a/karavan-web/karavan-app/src/main/webui/src/designer/rest/RestDesigner.tsx b/karavan-web/karavan-app/src/main/webui/src/designer/rest/RestDesigner.tsx index 78ed4be8..4a607cad 100644 --- a/karavan-web/karavan-app/src/main/webui/src/designer/rest/RestDesigner.tsx +++ b/karavan-web/karavan-app/src/main/webui/src/designer/rest/RestDesigner.tsx @@ -16,15 +16,20 @@ */ import React from 'react'; import { - Button, Drawer, DrawerContent, DrawerContentBody, DrawerPanelContent, Modal, + Button, Drawer, DrawerContent, DrawerContentBody, DrawerPanelContent, Flex, FlexItem, Gallery, GalleryItem, Modal, PageSection } from '@patternfly/react-core'; +import './rest.css'; import '../karavan.css'; import {CamelElement} from "karavan-core/lib/model/IntegrationDefinition"; import {DslProperties} from "../route/DslProperties"; import {RestCard} from "./RestCard"; import PlusIcon from "@patternfly/react-icons/dist/esm/icons/plus-icon"; -import {RestConfigurationDefinition, RestContextRefDefinition, RestDefinition} from "karavan-core/lib/model/CamelDefinition"; +import { + RestConfigurationDefinition, + RestContextRefDefinition, + RestDefinition +} from "karavan-core/lib/model/CamelDefinition"; import {CamelUtil} from "karavan-core/lib/api/CamelUtil"; import {CamelDefinitionApiExt} from "karavan-core/lib/api/CamelDefinitionApiExt"; import {RestMethodSelector} from "./RestMethodSelector"; @@ -42,39 +47,39 @@ export function RestDesigner() { [s.dark, s.selectedStep, s.showDeleteConfirmation, s.setShowDeleteConfirmation, s.setPosition, s.width, s.height, s.top, s.left, s.hideLogDSL, s.setSelectedStep], shallow) const [showSelector, setShowSelector] = useSelectorStore((s) => [s.showSelector, s.setShowSelector], shallow) - - function selectElement (element: CamelElement) { + + function selectElement(element: CamelElement) { setSelectedStep(element); } - function unselectElement (evt: React.MouseEvent<HTMLDivElement, MouseEvent>) { + function unselectElement(evt: React.MouseEvent<HTMLDivElement, MouseEvent>) { if ((evt.target as any).dataset.click === 'REST') { evt.stopPropagation() setSelectedStep(undefined); } } - function addRest (rest: RestDefinition) { + function addRest(rest: RestDefinition) { const clone = CamelUtil.cloneIntegration(integration); const i = CamelDefinitionApiExt.addRestToIntegration(clone, rest); setIntegration(i, false); setSelectedStep(rest); } - function createRest () { + function createRest() { addRest(new RestDefinition()); } - function createRestConfiguration () { + function createRestConfiguration() { addRest(new RestConfigurationDefinition()); } - function onShowDeleteConfirmation (element: CamelElement) { + function onShowDeleteConfirmation(element: CamelElement) { setSelectedStep(element); setShowDeleteConfirmation(true); } - function deleteElement () { + function deleteElement() { if (selectedStep) { let i; if (selectedStep.dslName === 'RestDefinition') i = CamelDefinitionApiExt.deleteRestFromIntegration(integration, selectedStep.uuid); @@ -104,11 +109,11 @@ export function RestDesigner() { </Modal>) } - function closeMethodSelector () { + function closeMethodSelector() { setShowSelector(false); } - function onMethodSelect (method: DslMetaModel) { + function onMethodSelect(method: DslMetaModel) { if (selectedStep) { const clone = CamelUtil.cloneIntegration(integration); const m = CamelDefinitionApi.createStep(method.dsl, {}); @@ -119,8 +124,8 @@ export function RestDesigner() { } } - function cloneRest (rest: CamelElement) { - if (rest.dslName === 'RestDefinition'){ + function cloneRest(rest: CamelElement) { + if (rest.dslName === 'RestDefinition') { const cloneRest = CamelUtil.cloneStep(rest); cloneRest.uuid = uuidv4(); const cloneIntegration = CamelUtil.cloneIntegration(integration); @@ -131,7 +136,7 @@ export function RestDesigner() { // could be only one RestConfigurationDefinition } else if (selectedStep) { const parentId = CamelDefinitionApiExt.findRestMethodParent(integration, rest); - if (parentId){ + if (parentId) { const cloneRest = CamelUtil.cloneStep(rest); cloneRest.uuid = uuidv4(); const cloneIntegration = CamelUtil.cloneIntegration(integration); @@ -142,7 +147,7 @@ export function RestDesigner() { } } - function selectMethod (element: CamelElement) { + function selectMethod(element: CamelElement) { setSelectedStep(element); setShowSelector(true); } @@ -174,12 +179,12 @@ export function RestDesigner() { return (<> {data?.map((rest, index) => <RestCard key={rest.uuid + index} - selectedStep={selectedStep} - rest={rest} - integration={integration} - selectMethod={selectMethod} - selectElement={selectElement} - deleteElement={onShowDeleteConfirmation} + selectedStep={selectedStep} + rest={rest} + integration={integration} + selectMethod={selectMethod} + selectElement={selectElement} + deleteElement={onShowDeleteConfirmation} /> )} </>) @@ -198,37 +203,46 @@ export function RestDesigner() { const configData = integration.spec.flows?.filter(f => f.dslName === 'RestConfigurationDefinition'); const config = configData && Array.isArray(configData) ? configData[0] : undefined; return ( - <PageSection className="rest-page" isFilled padding={{default: 'noPadding'}}> - <div className="rest-page-columns"> - <Drawer isExpanded isInline> - <DrawerContent panelContent={getPropertiesPanel()}> - <DrawerContentBody> - <div className="graph" data-click="REST" onClick={event => unselectElement(event)}> - <div className="flows"> - {config && getRestConfigurationCard(config)} - {data && getRestCards(data)} - <div className="add-rest"> + <PageSection className="rest-designer" isFilled padding={{default: 'noPadding'}}> + <Drawer isExpanded isInline> + <DrawerContent panelContent={getPropertiesPanel()}> + <DrawerContentBody> + <Gallery className="gallery" + hasGutter + maxWidths={{ + default: '100%', + }} + > + {config && getRestConfigurationCard(config)} + {data && getRestCards(data)} + <GalleryItem> + <Flex direction={{default: "row"}} justifyContent={{default: "justifyContentCenter"}}> + <FlexItem> <Button variant={data?.length === 0 ? "primary" : "secondary"} data-click="ADD_REST" icon={<PlusIcon/>} onClick={e => createRest()}>Create service </Button> + </FlexItem> + <FlexItem> {config === undefined && - <Button - variant="secondary" - data-click="ADD_REST_REST_CONFIG" - icon={<PlusIcon/>} - onClick={e => createRestConfiguration()}>Create configuration - </Button> + <GalleryItem> + <Button + variant="secondary" + data-click="ADD_REST_REST_CONFIG" + icon={<PlusIcon/>} + onClick={e => createRestConfiguration()}>Create configuration + </Button> + </GalleryItem> } - </div> - </div> - </div> - </DrawerContentBody> - </DrawerContent> - </Drawer> - </div> + </FlexItem> + </Flex> + </GalleryItem> + </Gallery> + </DrawerContentBody> + </DrawerContent> + </Drawer> {getSelectorModal()} {getDeleteConfirmation()} </PageSection> diff --git a/karavan-web/karavan-app/src/main/webui/src/designer/rest/rest.css b/karavan-web/karavan-app/src/main/webui/src/designer/rest/rest.css new file mode 100644 index 00000000..25012e2d --- /dev/null +++ b/karavan-web/karavan-app/src/main/webui/src/designer/rest/rest.css @@ -0,0 +1,273 @@ +/* + * 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 .rest-designer .rest-card, +.karavan .rest-designer .rest-config-card, +.karavan .rest-designer .method-card { + border-style: dotted; + border-radius: 4px; + border-width: 1px; +} + +.karavan .rest-designer .rest-card { + padding: 16px 16px 6px 16px; + margin-right: 80px; + margin-left: 80px; +} + +.karavan .rest-designer .rest-config-card { + padding: 16px 16px 34px 16px; + margin-right: 80px; + margin-left: 80px; +} + +.karavan .rest-designer .method-card { + padding: 0 0 0 6px; + margin-bottom: 6px; +} + +.karavan .rest-card-unselected { + border-color: var(--pf-v5-global--Color--200); + background-color: transparent; +} + +.karavan .rest-card-selected { + border-color: var(--pf-v5-global--primary-color--100); +} + +.karavan .rest-card .title { + margin: auto 0 auto 0; + font-weight: bold; + white-space: nowrap; +} + +.karavan .rest-card .description { + margin: auto 0 auto 0; + min-width: 200px; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} + +.karavan .rest-card .delete-button { + position: absolute; + top: 3px; + right: 3px; + line-height: 1; + border: 0; + padding: 0; + margin: 0; + background: transparent; + color: #909090; + visibility: hidden; + z-index: 100; +} + +.karavan .rest-card:hover .delete-button { + visibility: visible; +} + +.karavan .rest-designer { + display: block; + height: 100vh; + width: 100%; + overflow-y: auto; +} + +.karavan .rest-designer .gallery { + padding-top: 16px; + padding-bottom: 116px; +} + +.karavan .rest-designer .properties { + padding: 10px 10px 10px 10px; + background: transparent; + width: 100%; + height: 100%; + overflow: auto; + display: flex; + flex-direction: column; + justify-content: space-between; + margin-bottom: 106px; +} +/*rests*/ +.karavan .rest-designer .properties .rest-properties .pf-v5-c-form__group-control { + display: flex; + flex-direction: column; + gap: 6px; +} + +.karavan .rest-designer .properties .rest-property { + display: flex; + flex-direction: row; + gap: 3px; +} + +.karavan .rest-designer .properties .rest-property .delete-button { + padding: 3px; + color: #b1b1b7; +} + +.karavan .rest-designer .rest-designer-columns { + display: block; + height: 100%; + background: #fafafa; +} + +.karavan .rest-designer .graph { + display: block; + flex-direction: column; + height: 100%; + width: 100%; + position: relative; + overflow-y: auto; +} + +.karavan .rest-designer .flows { + /*width: 800px;*/ + margin: 0 auto 80px auto; +} + +.karavan .rest-designer .flows .add-flow { + margin-top: 16px; + display: flex; + justify-content: center; +} + +.karavan .rest-designer .method-card .rest-method-desc { + display: flex; + flex-direction: column; +} + +.karavan .rest-designer .rest-card-unselected, +.karavan .rest-designer .rest-config-card-unselected, +.karavan .rest-designer .method-card-unselected { + border-color: var(--pf-v5-global--Color--200); + background-color: transparent; +} + +.karavan .rest-designer .rest-card-selected, +.karavan .rest-designer .rest-config-card-selected, +.karavan .rest-designer .method-card-selected { + border-color: var(--pf-v5-global--primary-color--100); +} + +.karavan .rest-designer .rest-card-selected .title { + color: var(--pf-v5-global--primary-color--100); +} + +.karavan .rest-designer .rest-config-card, +.karavan .rest-designer .rest-card { + display: flex; + flex-direction: column; + margin-top: 16px; +} + +.karavan .rest-designer .header { + display: flex; + flex-direction: row; + gap: 16px; + height: 44px; + margin-left: 6px; + cursor: pointer; + justify-content: space-between; +} + +.karavan .rest-designer .rest-config-card, +.karavan .rest-designer .method-card { + display: flex; + flex-direction: row; + gap: 16px; + height: 44px; + cursor: pointer; +} + +.karavan .rest-designer .method-card .method { + margin: auto 0 auto 0; + border-radius: 3px; + color: #fff; + font-family: sans-serif; + font-weight: 700; + min-width: 80px; + padding: 6px 0; + text-align: center; + text-shadow: 0 1px 0 rgb(0 0 0 / 10%); +} + +.karavan .rest-designer .method-card-unselected .method { + background: var(--pf-v5-global--Color--400); +} + +.karavan .rest-designer .method-card-selected .method { + background: var(--pf-v5-global--primary-color--100); +} + +.karavan .rest-designer .rest-card .title, +.karavan .rest-designer .rest-config-card .title, +.karavan .rest-designer .method-card .title { + margin: auto 0 auto 0; + font-weight: bold; + white-space: nowrap; +} + +.karavan .rest-designer .rest-card .description, +.karavan .rest-designer .rest-config-card .description, +.karavan .rest-designer .method-card .description { + margin: auto 0 auto 0; + min-width: 200px; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} + +.karavan .rest-designer .rest-config-card .delete-button, +.karavan .rest-designer .rest-card .delete-button, +.karavan .rest-designer .method-card .delete-button { + position: absolute; + top: 3px; + right: 3px; + line-height: 1; + border: 0; + padding: 0; + margin: 0; + background: transparent; + color: #909090; + visibility: hidden; + z-index: 100; +} + +.karavan .rest-designer .rest-config-card:hover .delete-button, +.karavan .rest-designer .rest-card:hover .delete-button, +.karavan .rest-designer .method-card:hover .delete-button { + visibility: visible; +} + +.karavan .rest-designer .rest-card .add-button { + font-size: 15px; + border: 0; + background: transparent; + color: var(--pf-v5-global--primary-color--100); + z-index: 100; +} + +.karavan .rest-designer .add-rest { + display: flex; + flex-direction: row; + justify-content: center; + margin-top: 16px; + gap: 6px; +} diff --git a/karavan-web/karavan-app/src/main/webui/src/designer/route/DslConnections.tsx b/karavan-web/karavan-app/src/main/webui/src/designer/route/DslConnections.tsx index 44f23042..f39a1aa8 100644 --- a/karavan-web/karavan-app/src/main/webui/src/designer/route/DslConnections.tsx +++ b/karavan-web/karavan-app/src/main/webui/src/designer/route/DslConnections.tsx @@ -433,8 +433,8 @@ export function DslConnections() { const stepsArray = Array.from(steps.values()); return ( <svg - style={{width: width, height: height + 80, position: "absolute", left: 0, top: 0}} - viewBox={"0 0 " + (width) + " " + (height + 80)}> + style={{width: width, height: height, position: "absolute", left: 0, top: 0}} + viewBox={"0 0 " + (width) + " " + (height)}> <defs> <marker id="arrowhead" markerWidth="9" markerHeight="6" refX="0" refY="3" orient="auto" className="arrow"> <polygon points="0 0, 9 3, 0 6"/> @@ -450,7 +450,7 @@ export function DslConnections() { } return ( - <div id="connections" className="connections" style={{ width: width, height: height + 80}}> + <div id="connections" className="connections" style={{ width: width, height: height}}> {getSvg()} {getIncomings().map(p => getIncomingIcons(p))} {getOutgoings().map(p => getOutgoingIcons(p))} diff --git a/karavan-web/karavan-app/src/main/webui/src/designer/route/RouteDesigner.tsx b/karavan-web/karavan-app/src/main/webui/src/designer/route/RouteDesigner.tsx index c81f6c30..a78a4eed 100644 --- a/karavan-web/karavan-app/src/main/webui/src/designer/route/RouteDesigner.tsx +++ b/karavan-web/karavan-app/src/main/webui/src/designer/route/RouteDesigner.tsx @@ -90,8 +90,13 @@ export function RouteDesigner() { function getPropertiesPanel() { return ( - <DrawerPanelContent style={{transform: "initial"}} isResizable hasNoBorder defaultSize={'400px'} - maxSize={'800px'} minSize={'300px'}> + <DrawerPanelContent style={{transform: "initial"}} + isResizable + hasNoBorder + defaultSize={'400px'} + maxSize={'800px'} + minSize={'400px'} + > <DslProperties isRouteDesigner={true}/> </DrawerPanelContent> )
