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 5e889e62 Fix #1180
5e889e62 is described below

commit 5e889e6290662aa1a8acd92dcb7e288b093689b0
Author: Marat Gubaidullin <ma...@talismancloud.io>
AuthorDate: Fri Mar 15 18:14:44 2024 -0400

    Fix #1180
---
 .../main/webui/src/designer/KaravanDesigner.tsx    |  62 +++++-----
 .../main/webui/src/designer/selector/DslCard.tsx   |  71 +++++++++++
 .../webui/src/designer/selector/DslFastCard.tsx    |  58 +++++++++
 .../webui/src/designer/selector/DslPreferences.tsx |  13 +++
 .../webui/src/designer/selector/DslSelector.css    |  40 ++++++-
 .../webui/src/designer/selector/DslSelector.tsx    | 130 ++++++++-------------
 karavan-designer/public/example/demo.camel.yaml    |   2 -
 karavan-designer/src/designer/selector/DslCard.tsx |  71 +++++++++++
 .../src/designer/selector/DslFastCard.tsx          |  58 +++++++++
 .../src/designer/selector/DslPreferences.tsx       |  13 +++
 .../src/designer/selector/DslSelector.css          |  40 ++++++-
 .../src/designer/selector/DslSelector.tsx          | 130 ++++++++-------------
 karavan-space/src/designer/selector/DslCard.tsx    |  71 +++++++++++
 .../src/designer/selector/DslFastCard.tsx          |  58 +++++++++
 .../src/designer/selector/DslPreferences.tsx       |  13 +++
 .../src/designer/selector/DslSelector.css          |  40 ++++++-
 .../src/designer/selector/DslSelector.tsx          | 130 ++++++++-------------
 17 files changed, 708 insertions(+), 292 deletions(-)

diff --git a/karavan-app/src/main/webui/src/designer/KaravanDesigner.tsx 
b/karavan-app/src/main/webui/src/designer/KaravanDesigner.tsx
index 8e49e054..36b65118 100644
--- a/karavan-app/src/main/webui/src/designer/KaravanDesigner.tsx
+++ b/karavan-app/src/main/webui/src/designer/KaravanDesigner.tsx
@@ -73,30 +73,35 @@ export function KaravanDesigner(props: Props) {
     useEffect(() => {
         const sub = EventBus.onIntegrationUpdate()?.subscribe((update: 
IntegrationUpdate) =>
             save(update.integration, update.propertyOnly));
-        InfrastructureAPI.setOnSaveCustomCode(props.onSaveCustomCode);
-        InfrastructureAPI.setOnGetCustomCode(props.onGetCustomCode);
-        InfrastructureAPI.setOnSave(props.onSave);
-        
InfrastructureAPI.setOnSavePropertyPlaceholder(props.onSavePropertyPlaceholder);
-        
InfrastructureAPI.setOnInternalConsumerClick(props.onInternalConsumerClick);
+        try {
+            InfrastructureAPI.setOnSaveCustomCode(props.onSaveCustomCode);
+            InfrastructureAPI.setOnGetCustomCode(props.onGetCustomCode);
+            InfrastructureAPI.setOnSave(props.onSave);
+            
InfrastructureAPI.setOnSavePropertyPlaceholder(props.onSavePropertyPlaceholder);
+            
InfrastructureAPI.setOnInternalConsumerClick(props.onInternalConsumerClick);
 
-        setSelectedStep(undefined);
-        const i = makeIntegration(props.yaml, props.filename);
-        setIntegration(i, false);
-        let designerTab = i.kind === 'Kamelet' ? 'kamelet' : props.tab;
-        if (designerTab === undefined) {
-            const counts = CamelUi.getFlowCounts(i);
-            designerTab = (counts.get('routes') || 0) > 0 ? 'routes' : 
designerTab;
-            designerTab = (counts.get('rest') || 0) > 0 ? 'rest' : designerTab;
-            designerTab = (counts.get('beans') || 0) > 0 ? 'beans' : 
designerTab;
+            setSelectedStep(undefined);
+            const i = makeIntegration(props.yaml, props.filename);
+            setIntegration(i, false);
+            let designerTab = i.kind === 'Kamelet' ? 'kamelet' : props.tab;
+            if (designerTab === undefined) {
+                const counts = CamelUi.getFlowCounts(i);
+                designerTab = (counts.get('routes') || 0) > 0 ? 'routes' : 
designerTab;
+                designerTab = (counts.get('rest') || 0) > 0 ? 'rest' : 
designerTab;
+                designerTab = (counts.get('beans') || 0) > 0 ? 'beans' : 
designerTab;
+            }
+            setTab(designerTab || 'routes')
+            reset();
+            setDark(props.dark);
+            setPropertyPlaceholders(props.propertyPlaceholders)
+            setVariables(VariableUtil.findVariables(props.files))
+            setBeans(props.beans)
+            resetFiles(props.files)
+            setHideLogDSL(props.hideLogDSL === true);
+        } catch (e: any) {
+            console.log(e)
+            EventBus.sendAlert(' ' + e?.name, '' + e?.message, 'danger');
         }
-        setTab(designerTab || 'routes')
-        reset();
-        setDark(props.dark);
-        setPropertyPlaceholders(props.propertyPlaceholders)
-        setVariables(VariableUtil.findVariables(props.files))
-        setBeans(props.beans)
-        resetFiles(props.files)
-        setHideLogDSL(props.hideLogDSL === true);
         return () => {
             sub?.unsubscribe();
             setSelectedStep(undefined);
@@ -169,19 +174,6 @@ export function KaravanDesigner(props: Props) {
                     <Tab eventKey='beans' title={getTab("Beans", "Beans 
Configuration", "beans")}></Tab>
                     {props.showCodeTab && <Tab eventKey='code' 
title={getTab("YAML", "YAML Code", "code", true)}></Tab>}
                 </Tabs>
-                {/*{tab === 'routes' && <Tooltip content={"Hide Log 
elements"}>*/}
-                {/*    <Switch*/}
-                {/*        isReversed*/}
-                {/*        isChecked={hideLogDSL}*/}
-                {/*        onChange={(_, checked) => {*/}
-                {/*            setHideLogDSL(checked)*/}
-                {/*        }}*/}
-                {/*        aria-label={"Hide Log"}*/}
-                {/*        id="hideLogDSL"*/}
-                {/*        name="hideLogDSL"*/}
-                {/*        className={"hide-log"}*/}
-                {/*    />*/}
-                {/*</Tooltip>}*/}
             </div>
             {tab === 'kamelet' && <KameletDesigner/>}
             {tab === 'routes' && <RouteDesigner/>}
diff --git a/karavan-app/src/main/webui/src/designer/selector/DslCard.tsx 
b/karavan-app/src/main/webui/src/designer/selector/DslCard.tsx
new file mode 100644
index 00000000..1e0b9a3f
--- /dev/null
+++ b/karavan-app/src/main/webui/src/designer/selector/DslCard.tsx
@@ -0,0 +1,71 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import React from 'react';
+import {
+    Badge,
+    Card, CardBody, CardFooter,
+    CardHeader,
+    Text,
+} from '@patternfly/react-core';
+import './DslSelector.css';
+import {CamelUi} from "../utils/CamelUi";
+import {DslMetaModel} from "../utils/DslMetaModel";
+import {KameletApi} from "karavan-core/lib/api/KameletApi";
+
+interface Props {
+    dsl: DslMetaModel,
+    index: number
+    onDslSelect: (evt: React.MouseEvent, dsl: DslMetaModel) => void
+}
+
+export function DslCard (props: Props) {
+
+    function selectDsl(evt: React.MouseEvent, dsl: DslMetaModel) {
+        props.onDslSelect(evt, dsl);
+    }
+
+    const {dsl, index} = props;
+    const labels = dsl.labels !== undefined ? 
dsl.labels.split(",").filter(label => label !== 'eip') : [];
+    const isCustom = KameletApi.getCustomKameletNames().includes(dsl.name);
+    return (
+        <Card key={dsl.dsl + index} isCompact isPlain isFlat isRounded 
className="dsl-card"
+              onClick={event => selectDsl(event, dsl)}>
+            <CardHeader className="header-labels">
+                <Badge isRead className="support-level 
labels">{dsl.supportLevel}</Badge>
+                {['kamelet', 
'component'].includes(dsl.navigation.toLowerCase()) &&
+                    <Badge isRead className="version 
labels">{dsl.version}</Badge>
+                }
+                {isCustom && <Badge className="custom">custom</Badge>}
+            </CardHeader>
+            <CardHeader>
+                {CamelUi.getIconForDsl(dsl)}
+                <Text className='dsl-card-title'>{dsl.title}</Text>
+            </CardHeader>
+            <CardBody>
+                {/*<Text>{dsl.description}</Text>*/}
+                <Text className="pf-v5-u-color-200">{dsl.description}</Text>
+            </CardBody>
+            <CardFooter className="footer-labels">
+                <div style={{display: "flex", flexDirection: "row", 
justifyContent: "start"}}>
+                    {labels.map((label, index) => <Badge key={label + "-" + 
index} isRead
+                                                         
className="labels">{label}</Badge>)}
+                </div>
+
+            </CardFooter>
+        </Card>
+    )
+}
\ No newline at end of file
diff --git a/karavan-app/src/main/webui/src/designer/selector/DslFastCard.tsx 
b/karavan-app/src/main/webui/src/designer/selector/DslFastCard.tsx
new file mode 100644
index 00000000..26bb8dfb
--- /dev/null
+++ b/karavan-app/src/main/webui/src/designer/selector/DslFastCard.tsx
@@ -0,0 +1,58 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import React from 'react';
+import {
+    Button,
+    Card,
+    CardHeader,
+    Text,
+} from '@patternfly/react-core';
+import './DslSelector.css';
+import {CamelUi} from "../utils/CamelUi";
+import {DslMetaModel} from "../utils/DslMetaModel";
+import TimesIcon from 
"@patternfly/react-icons/dist/esm/icons/outlined-times-circle-icon";
+
+interface Props {
+    dsl: DslMetaModel,
+    index: number
+    onDslSelect: (evt: React.MouseEvent, dsl: DslMetaModel) => void
+    onDeleteFast: (evt: React.MouseEvent, dsl: DslMetaModel) => void
+}
+
+export function DslFastCard (props: Props) {
+
+    function selectDsl(evt: React.MouseEvent, dsl: DslMetaModel) {
+        props.onDslSelect(evt, dsl);
+    }
+
+    function deleteFast(evt: React.MouseEvent) {
+        props.onDeleteFast(evt, dsl);
+    }
+
+    const {dsl, index} = props;
+
+    return (
+        <Card key={dsl.dsl + index} isCompact isPlain isFlat isRounded 
className="dsl-card dsl-fast-card"
+              onClick={event => selectDsl(event, dsl)}>
+            <Button className='fast-delete' variant='link' icon={<TimesIcon/>} 
onClick={deleteFast}/>
+            <CardHeader className='header'>
+                {CamelUi.getIconForDsl(dsl)}
+                <Text className='dsl-fast-card-title'>{dsl.title}</Text>
+            </CardHeader>
+        </Card>
+    )
+}
\ No newline at end of file
diff --git 
a/karavan-app/src/main/webui/src/designer/selector/DslPreferences.tsx 
b/karavan-app/src/main/webui/src/designer/selector/DslPreferences.tsx
index 87844b5a..9464b813 100644
--- a/karavan-app/src/main/webui/src/designer/selector/DslPreferences.tsx
+++ b/karavan-app/src/main/webui/src/designer/selector/DslPreferences.tsx
@@ -74,3 +74,16 @@ export function addPreferredElement(type: 'eip' | 
'components' | 'kamelets', dsl
         console.log(e);
     }
 }
+
+export function deletePreferredElement(type: 'eip' | 'components' | 
'kamelets', dsl: DslMetaModel) {
+    try {
+        const dslKey = type === 'eip' ? dsl.dsl : (type === 'components' ? 
dsl.uri : dsl.name);
+        const local = localStorage.getItem(PREFERRED_ELEMENTS_STORAGE_NAME);
+        const pes = local !== null ? new PreferredElements(JSON.parse(local)) 
: new PreferredElements();
+        let list: PreferredElement[] = (pes as any)[type];
+        (pes as any)[type] = [...list.filter(l => l.dslKey !== dslKey)];
+        localStorage.setItem(PREFERRED_ELEMENTS_STORAGE_NAME, 
JSON.stringify(pes));
+    } catch (e) {
+        console.log(e);
+    }
+}
diff --git a/karavan-app/src/main/webui/src/designer/selector/DslSelector.css 
b/karavan-app/src/main/webui/src/designer/selector/DslSelector.css
index 279c1f41..4c54ffa3 100644
--- a/karavan-app/src/main/webui/src/designer/selector/DslSelector.css
+++ b/karavan-app/src/main/webui/src/designer/selector/DslSelector.css
@@ -27,6 +27,8 @@
 .dsl-modal .dsl-card .pf-v5-c-card__header-main {
     display: flex;
     flex-direction: row;
+    justify-content: start;
+    overflow-x: hidden;
 }
 
 .dsl-modal .dsl-card .header-labels .pf-v5-c-card__header-main {
@@ -120,15 +122,23 @@
 .dsl-modal .labels {
     opacity: 0.5;
     font-weight: 200;
-    text-wrap: nowrap;
+    text-wrap: none;
 }
 
-.dsl-modal .dsl-card:hover .labels {
-    opacity: 1;
+.dsl-modal .dsl-card:hover .pf-v5-c-badge {
+    margin-right: 1px;
+}
+
+.dsl-modal .dsl-card:hover {
+    background-color: var(--pf-v5-global--palette--black-200);
+}
+
+.dsl-modal .dsl-card:hover .pf-v5-c-badge.pf-m-read {
+    --pf-v5-c-badge--BackgroundColor: var(--pf-v5-global--palette--black-150);
 }
 
 .dsl-modal .dsl-card:hover p {
-    color: var(--pf-v5-global--Color--100);
+    /*color: var(--pf-v5-global--Color--200);*/
 }
 
 .dsl-modal .dsl-fast-card .header p {
@@ -140,3 +150,25 @@
 .dsl-modal .dsl-fast-card .header {
     padding: 10px;
 }
+
+.dsl-modal .dsl-fast-card .fast-delete {
+    padding: 0;
+    margin: 0;
+    width: 16px;
+    height: 16px;
+    vertical-align: text-bottom;
+    position: absolute;
+    right: 0;
+    top: -1px;
+    visibility: hidden;
+}
+
+.dsl-modal .dsl-fast-card .fast-delete .pf-v5-svg {
+    fill: var(--pf-v5-global--danger-color--100);
+    width: 16px;
+    height: 16px;
+}
+
+.dsl-modal .dsl-fast-card:hover .fast-delete {
+    visibility: visible;
+}
\ No newline at end of file
diff --git a/karavan-app/src/main/webui/src/designer/selector/DslSelector.tsx 
b/karavan-app/src/main/webui/src/designer/selector/DslSelector.tsx
index 9e366515..50424e8c 100644
--- a/karavan-app/src/main/webui/src/designer/selector/DslSelector.tsx
+++ b/karavan-app/src/main/webui/src/designer/selector/DslSelector.tsx
@@ -16,12 +16,7 @@
  */
 import React, {useEffect, useState} from 'react';
 import {
-    Badge,
     Button,
-    Card,
-    CardBody,
-    CardFooter,
-    CardHeader,
     Flex,
     FlexItem,
     Gallery,
@@ -31,7 +26,6 @@ import {
     Tab,
     Tabs,
     TabTitleText,
-    Text,
     TextInputGroup,
     TextInputGroupMain,
     TextInputGroupUtilities,
@@ -47,13 +41,15 @@ import {useRouteDesignerHook} from 
"../route/useRouteDesignerHook";
 import {ComponentApi} from 'karavan-core/lib/api/ComponentApi';
 import {KameletApi} from 'karavan-core/lib/api/KameletApi';
 import TimesIcon from "@patternfly/react-icons/dist/esm/icons/times-icon";
-import {addPreferredElement, getPreferredElements} from "./DslPreferences";
+import {addPreferredElement, deletePreferredElement, getPreferredElements} 
from "./DslPreferences";
+import {DslFastCard} from "./DslFastCard";
+import {DslCard} from "./DslCard";
 
 interface Props {
     tabIndex?: string | number
 }
 
-export function DslSelector (props: Props) {
+export function DslSelector(props: Props) {
 
     const [showSelector, showSteps, parentId, parentDsl, selectorTabIndex, 
setShowSelector, setSelectorTabIndex,
         selectedPosition, selectedLabels, addSelectedLabel, 
deleteSelectedLabel, clearSelectedLabels] =
@@ -72,11 +68,14 @@ export function DslSelector (props: Props) {
     const [preferredKamelets, setPreferredKamelets] = useState<string[]>([]);
 
     useEffect(() => {
+        setPreferences();
+    }, [selectedLabels]);
+
+    function setPreferences() {
         setPreferredEip(getPreferredElements('eip'));
         setPreferredComponents(getPreferredElements('components'));
         setPreferredKamelets(getPreferredElements('kamelets'));
-    }, [selectedLabels]);
-
+    }
 
     function selectTab(evt: React.MouseEvent<HTMLElement, MouseEvent>, 
eventKey: string | number) {
         setSelectorTabIndex(eventKey);
@@ -87,9 +86,13 @@ export function DslSelector (props: Props) {
         setFilter('');
         setShowSelector(false);
         onDslSelect(dsl, parentId, selectedPosition);
-        const type = isEip ? 'eip' : (selectorTabIndex === 'components' ? 
'components' : 'kamelets');
         addPreferredElement(type, dsl)
     }
+    function deleteFast(evt: React.MouseEvent, dsl: DslMetaModel) {
+        evt.stopPropagation();
+        deletePreferredElement(type, dsl);
+        setPreferences();
+    }
 
     function searchInput() {
         return (
@@ -105,11 +108,11 @@ export function DslSelector (props: Props) {
                 <FlexItem>
                     <TextInputGroup>
                         <TextInputGroupMain className="text-field" type="text" 
autoComplete={"off"}
-                               value={filter}
-                               onChange={(_, value) => setFilter(value)}/>
+                                            value={filter}
+                                            onChange={(_, value) => 
setFilter(value)}/>
                         <TextInputGroupUtilities>
                             <Button variant="plain" onClick={_ => 
setFilter('')}>
-                                <TimesIcon />
+                                <TimesIcon/>
                             </Button>
                         </TextInputGroupUtilities>
                     </TextInputGroup>
@@ -118,50 +121,6 @@ export function DslSelector (props: Props) {
         )
     }
 
-    function getCard(dsl: DslMetaModel, index: number) {
-        const labels = dsl.labels !== undefined ? 
dsl.labels.split(",").filter(label => label !== 'eip') : [];
-        const isCustom = KameletApi.getCustomKameletNames().includes(dsl.name);
-        return (
-            <Card key={dsl.dsl + index} isCompact className="dsl-card"
-                  onClick={event => selectDsl(event, dsl)}>
-                <CardHeader className="header-labels">
-                    <Badge isRead className="support-level 
labels">{dsl.supportLevel}</Badge>
-                    {['kamelet', 
'component'].includes(dsl.navigation.toLowerCase()) &&
-                        <Badge isRead className="version 
labels">{dsl.version}</Badge>
-                    }
-                    {isCustom && <Badge className="custom">custom</Badge>}
-                </CardHeader>
-                <CardHeader>
-                    {CamelUi.getIconForDsl(dsl)}
-                    <Text className='dsl-card-title'>{dsl.title}</Text>
-                </CardHeader>
-                <CardBody>
-                    {/*<Text>{dsl.description}</Text>*/}
-                    <Text 
className="pf-v5-u-color-200">{dsl.description}</Text>
-                </CardBody>
-                <CardFooter className="footer-labels">
-                    <div style={{display: "flex", flexDirection: "row", 
justifyContent: "start"}}>
-                        {labels.map((label, index) => <Badge key={label + "-" 
+ index} isRead
-                                                             
className="labels">{label}</Badge>)}
-                    </div>
-
-                </CardFooter>
-            </Card>
-        )
-    }
-
-    function getFastCard(dsl: DslMetaModel, index: number) {
-        return (
-            <Card key={dsl.dsl + index} isCompact className="dsl-card 
dsl-fast-card"
-                  onClick={event => selectDsl(event, dsl)}>
-                <CardHeader className='header'>
-                    {CamelUi.getIconForDsl(dsl)}
-                    <Text className='dsl-fast-card-title'>{dsl.title}</Text>
-                </CardHeader>
-            </Card>
-        )
-    }
-
     function close() {
         setFilter('');
         setShowSelector(false);
@@ -175,7 +134,7 @@ export function DslSelector (props: Props) {
         }
     }
 
-    function filterElements(elements: DslMetaModel[], type: 'eip' | 
'components' | 'kamelets'):DslMetaModel[] {
+    function filterElements(elements: DslMetaModel[], type: 'eip' | 
'components' | 'kamelets'): DslMetaModel[] {
         return elements.filter((dsl: DslMetaModel) => CamelUi.checkFilter(dsl, 
filter))
             .filter((dsl: DslMetaModel) => {
                 if (type !== 'eip' || selectedLabels.length === 0) {
@@ -187,6 +146,7 @@ export function DslSelector (props: Props) {
     }
 
     const isEip = selectorTabIndex === 'eip';
+    const type = isEip ? 'eip' : (selectorTabIndex === 'components' ? 
'components' : 'kamelets');
     const isRouteConfig = parentDsl === 'RouteConfigurationDefinition';
     const title = parentDsl === undefined ? "Select source" : "Select step";
     const navigation: string = selectorTabIndex ? selectorTabIndex.toString() 
: '';
@@ -219,6 +179,17 @@ export function DslSelector (props: Props) {
         ? filteredComponentElements
         : (navigation === 'kamelets' ? filteredKameletElements : 
filteredEipElements);
 
+    const fastElements = elements.filter((d: DslMetaModel) => {
+        if (isEip) {
+            return preferredElements.includes(d.dsl);
+        } else if (navigation === 'components') {
+            return d.uri && preferredElements.includes(d.uri)
+        } else {
+            return preferredElements.includes(d.name)
+        }
+    }).filter((_, i) => i < 7)
+
+
     return (
         <Modal
             aria-label={title}
@@ -237,7 +208,8 @@ export function DslSelector (props: Props) {
                               onSelect={selectTab}>
                             {parentDsl !== undefined &&
                                 <Tab eventKey={"eip"} key={"tab-eip"}
-                                     title={<TabTitleText>{`Integration 
Patterns (${filteredEipElements?.length})`}</TabTitleText>}>
+                                     title={
+                                         <TabTitleText>{`Integration Patterns 
(${filteredEipElements?.length})`}</TabTitleText>}>
                                 </Tab>
                             }
                             {!isRouteConfig &&
@@ -259,30 +231,26 @@ export function DslSelector (props: Props) {
             actions={{}}>
             <PageSection padding={{default: "noPadding"}} variant={dark ? 
"darker" : "light"}>
                 {isEip && <ToggleGroup aria-label="Labels" isCompact>
-                    {eipLabels.map(eipLabel => <ToggleGroupItem
-                        key={eipLabel}
-                        text={eipLabel}
-                        buttonId={eipLabel}
-                        isSelected={selectedLabels.includes(eipLabel)}
-                        onChange={selected => selectLabel(eipLabel)}
-                    />)}
-                    <ToggleGroupItem key='clean' buttonId='clean' 
isSelected={false} onChange={clearSelectedLabels} icon={<TimesIcon/>}/>
+                    {eipLabels.map(eipLabel =>
+                        <ToggleGroupItem key={eipLabel}
+                                         text={eipLabel}
+                                         buttonId={eipLabel}
+                                         
isSelected={selectedLabels.includes(eipLabel)}
+                                         onChange={selected => 
selectLabel(eipLabel)}
+                        />)}
+                    <ToggleGroupItem key='clean' buttonId='clean' 
isSelected={false} onChange={clearSelectedLabels}
+                                     icon={<TimesIcon/>}/>
                 </ToggleGroup>}
-                <Gallery key={"fast-gallery-" + navigation} hasGutter 
className="dsl-gallery" minWidths={{default: '150px'}}>
-                    {showSelector && elements
-                        .filter((d: DslMetaModel) => {
-                            if (isEip) {
-                                return preferredElements.includes(d.dsl);
-                            } else if (navigation === 'components') {
-                                return d.uri && 
preferredElements.includes(d.uri)
-                            } else {
-                                return preferredElements.includes(d.name)
-                            }
-                        })
-                        .filter((_, i) => i < 7).map((dsl: DslMetaModel, 
index: number) => getFastCard(dsl, index))}
+                <Gallery key={"fast-gallery-" + navigation} hasGutter 
className="dsl-gallery"
+                         minWidths={{default: '150px'}}>
+                    {showSelector && fastElements.map((dsl: DslMetaModel, 
index: number) =>
+                        <DslFastCard dsl={dsl} index={index} 
onDslSelect={selectDsl} onDeleteFast={deleteFast}/>
+                    )}
                 </Gallery>
                 <Gallery key={"gallery-" + navigation} hasGutter 
className="dsl-gallery" minWidths={{default: '200px'}}>
-                    {showSelector && filteredElements.map((dsl: DslMetaModel, 
index: number) => getCard(dsl, index))}
+                    {showSelector && filteredElements.map((dsl: DslMetaModel, 
index: number) =>
+                        <DslCard dsl={dsl} index={index} 
onDslSelect={selectDsl}/>
+                    )}
                 </Gallery>
             </PageSection>
         </Modal>
diff --git a/karavan-designer/public/example/demo.camel.yaml 
b/karavan-designer/public/example/demo.camel.yaml
index facfc3d1..b751e86b 100644
--- a/karavan-designer/public/example/demo.camel.yaml
+++ b/karavan-designer/public/example/demo.camel.yaml
@@ -59,5 +59,3 @@
                       Beer battle: ${jq(variable:beer1,.name)}
                       (${variable.alc1}%) is weaker than
                       ${jq(variable:beer2,.name)} (${variable.alc2}%)
-            otherwise:
-              id: otherwise-03ac
\ No newline at end of file
diff --git a/karavan-designer/src/designer/selector/DslCard.tsx 
b/karavan-designer/src/designer/selector/DslCard.tsx
new file mode 100644
index 00000000..1e0b9a3f
--- /dev/null
+++ b/karavan-designer/src/designer/selector/DslCard.tsx
@@ -0,0 +1,71 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import React from 'react';
+import {
+    Badge,
+    Card, CardBody, CardFooter,
+    CardHeader,
+    Text,
+} from '@patternfly/react-core';
+import './DslSelector.css';
+import {CamelUi} from "../utils/CamelUi";
+import {DslMetaModel} from "../utils/DslMetaModel";
+import {KameletApi} from "karavan-core/lib/api/KameletApi";
+
+interface Props {
+    dsl: DslMetaModel,
+    index: number
+    onDslSelect: (evt: React.MouseEvent, dsl: DslMetaModel) => void
+}
+
+export function DslCard (props: Props) {
+
+    function selectDsl(evt: React.MouseEvent, dsl: DslMetaModel) {
+        props.onDslSelect(evt, dsl);
+    }
+
+    const {dsl, index} = props;
+    const labels = dsl.labels !== undefined ? 
dsl.labels.split(",").filter(label => label !== 'eip') : [];
+    const isCustom = KameletApi.getCustomKameletNames().includes(dsl.name);
+    return (
+        <Card key={dsl.dsl + index} isCompact isPlain isFlat isRounded 
className="dsl-card"
+              onClick={event => selectDsl(event, dsl)}>
+            <CardHeader className="header-labels">
+                <Badge isRead className="support-level 
labels">{dsl.supportLevel}</Badge>
+                {['kamelet', 
'component'].includes(dsl.navigation.toLowerCase()) &&
+                    <Badge isRead className="version 
labels">{dsl.version}</Badge>
+                }
+                {isCustom && <Badge className="custom">custom</Badge>}
+            </CardHeader>
+            <CardHeader>
+                {CamelUi.getIconForDsl(dsl)}
+                <Text className='dsl-card-title'>{dsl.title}</Text>
+            </CardHeader>
+            <CardBody>
+                {/*<Text>{dsl.description}</Text>*/}
+                <Text className="pf-v5-u-color-200">{dsl.description}</Text>
+            </CardBody>
+            <CardFooter className="footer-labels">
+                <div style={{display: "flex", flexDirection: "row", 
justifyContent: "start"}}>
+                    {labels.map((label, index) => <Badge key={label + "-" + 
index} isRead
+                                                         
className="labels">{label}</Badge>)}
+                </div>
+
+            </CardFooter>
+        </Card>
+    )
+}
\ No newline at end of file
diff --git a/karavan-designer/src/designer/selector/DslFastCard.tsx 
b/karavan-designer/src/designer/selector/DslFastCard.tsx
new file mode 100644
index 00000000..26bb8dfb
--- /dev/null
+++ b/karavan-designer/src/designer/selector/DslFastCard.tsx
@@ -0,0 +1,58 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import React from 'react';
+import {
+    Button,
+    Card,
+    CardHeader,
+    Text,
+} from '@patternfly/react-core';
+import './DslSelector.css';
+import {CamelUi} from "../utils/CamelUi";
+import {DslMetaModel} from "../utils/DslMetaModel";
+import TimesIcon from 
"@patternfly/react-icons/dist/esm/icons/outlined-times-circle-icon";
+
+interface Props {
+    dsl: DslMetaModel,
+    index: number
+    onDslSelect: (evt: React.MouseEvent, dsl: DslMetaModel) => void
+    onDeleteFast: (evt: React.MouseEvent, dsl: DslMetaModel) => void
+}
+
+export function DslFastCard (props: Props) {
+
+    function selectDsl(evt: React.MouseEvent, dsl: DslMetaModel) {
+        props.onDslSelect(evt, dsl);
+    }
+
+    function deleteFast(evt: React.MouseEvent) {
+        props.onDeleteFast(evt, dsl);
+    }
+
+    const {dsl, index} = props;
+
+    return (
+        <Card key={dsl.dsl + index} isCompact isPlain isFlat isRounded 
className="dsl-card dsl-fast-card"
+              onClick={event => selectDsl(event, dsl)}>
+            <Button className='fast-delete' variant='link' icon={<TimesIcon/>} 
onClick={deleteFast}/>
+            <CardHeader className='header'>
+                {CamelUi.getIconForDsl(dsl)}
+                <Text className='dsl-fast-card-title'>{dsl.title}</Text>
+            </CardHeader>
+        </Card>
+    )
+}
\ No newline at end of file
diff --git a/karavan-designer/src/designer/selector/DslPreferences.tsx 
b/karavan-designer/src/designer/selector/DslPreferences.tsx
index 87844b5a..9464b813 100644
--- a/karavan-designer/src/designer/selector/DslPreferences.tsx
+++ b/karavan-designer/src/designer/selector/DslPreferences.tsx
@@ -74,3 +74,16 @@ export function addPreferredElement(type: 'eip' | 
'components' | 'kamelets', dsl
         console.log(e);
     }
 }
+
+export function deletePreferredElement(type: 'eip' | 'components' | 
'kamelets', dsl: DslMetaModel) {
+    try {
+        const dslKey = type === 'eip' ? dsl.dsl : (type === 'components' ? 
dsl.uri : dsl.name);
+        const local = localStorage.getItem(PREFERRED_ELEMENTS_STORAGE_NAME);
+        const pes = local !== null ? new PreferredElements(JSON.parse(local)) 
: new PreferredElements();
+        let list: PreferredElement[] = (pes as any)[type];
+        (pes as any)[type] = [...list.filter(l => l.dslKey !== dslKey)];
+        localStorage.setItem(PREFERRED_ELEMENTS_STORAGE_NAME, 
JSON.stringify(pes));
+    } catch (e) {
+        console.log(e);
+    }
+}
diff --git a/karavan-designer/src/designer/selector/DslSelector.css 
b/karavan-designer/src/designer/selector/DslSelector.css
index 279c1f41..4c54ffa3 100644
--- a/karavan-designer/src/designer/selector/DslSelector.css
+++ b/karavan-designer/src/designer/selector/DslSelector.css
@@ -27,6 +27,8 @@
 .dsl-modal .dsl-card .pf-v5-c-card__header-main {
     display: flex;
     flex-direction: row;
+    justify-content: start;
+    overflow-x: hidden;
 }
 
 .dsl-modal .dsl-card .header-labels .pf-v5-c-card__header-main {
@@ -120,15 +122,23 @@
 .dsl-modal .labels {
     opacity: 0.5;
     font-weight: 200;
-    text-wrap: nowrap;
+    text-wrap: none;
 }
 
-.dsl-modal .dsl-card:hover .labels {
-    opacity: 1;
+.dsl-modal .dsl-card:hover .pf-v5-c-badge {
+    margin-right: 1px;
+}
+
+.dsl-modal .dsl-card:hover {
+    background-color: var(--pf-v5-global--palette--black-200);
+}
+
+.dsl-modal .dsl-card:hover .pf-v5-c-badge.pf-m-read {
+    --pf-v5-c-badge--BackgroundColor: var(--pf-v5-global--palette--black-150);
 }
 
 .dsl-modal .dsl-card:hover p {
-    color: var(--pf-v5-global--Color--100);
+    /*color: var(--pf-v5-global--Color--200);*/
 }
 
 .dsl-modal .dsl-fast-card .header p {
@@ -140,3 +150,25 @@
 .dsl-modal .dsl-fast-card .header {
     padding: 10px;
 }
+
+.dsl-modal .dsl-fast-card .fast-delete {
+    padding: 0;
+    margin: 0;
+    width: 16px;
+    height: 16px;
+    vertical-align: text-bottom;
+    position: absolute;
+    right: 0;
+    top: -1px;
+    visibility: hidden;
+}
+
+.dsl-modal .dsl-fast-card .fast-delete .pf-v5-svg {
+    fill: var(--pf-v5-global--danger-color--100);
+    width: 16px;
+    height: 16px;
+}
+
+.dsl-modal .dsl-fast-card:hover .fast-delete {
+    visibility: visible;
+}
\ No newline at end of file
diff --git a/karavan-designer/src/designer/selector/DslSelector.tsx 
b/karavan-designer/src/designer/selector/DslSelector.tsx
index 9e366515..50424e8c 100644
--- a/karavan-designer/src/designer/selector/DslSelector.tsx
+++ b/karavan-designer/src/designer/selector/DslSelector.tsx
@@ -16,12 +16,7 @@
  */
 import React, {useEffect, useState} from 'react';
 import {
-    Badge,
     Button,
-    Card,
-    CardBody,
-    CardFooter,
-    CardHeader,
     Flex,
     FlexItem,
     Gallery,
@@ -31,7 +26,6 @@ import {
     Tab,
     Tabs,
     TabTitleText,
-    Text,
     TextInputGroup,
     TextInputGroupMain,
     TextInputGroupUtilities,
@@ -47,13 +41,15 @@ import {useRouteDesignerHook} from 
"../route/useRouteDesignerHook";
 import {ComponentApi} from 'karavan-core/lib/api/ComponentApi';
 import {KameletApi} from 'karavan-core/lib/api/KameletApi';
 import TimesIcon from "@patternfly/react-icons/dist/esm/icons/times-icon";
-import {addPreferredElement, getPreferredElements} from "./DslPreferences";
+import {addPreferredElement, deletePreferredElement, getPreferredElements} 
from "./DslPreferences";
+import {DslFastCard} from "./DslFastCard";
+import {DslCard} from "./DslCard";
 
 interface Props {
     tabIndex?: string | number
 }
 
-export function DslSelector (props: Props) {
+export function DslSelector(props: Props) {
 
     const [showSelector, showSteps, parentId, parentDsl, selectorTabIndex, 
setShowSelector, setSelectorTabIndex,
         selectedPosition, selectedLabels, addSelectedLabel, 
deleteSelectedLabel, clearSelectedLabels] =
@@ -72,11 +68,14 @@ export function DslSelector (props: Props) {
     const [preferredKamelets, setPreferredKamelets] = useState<string[]>([]);
 
     useEffect(() => {
+        setPreferences();
+    }, [selectedLabels]);
+
+    function setPreferences() {
         setPreferredEip(getPreferredElements('eip'));
         setPreferredComponents(getPreferredElements('components'));
         setPreferredKamelets(getPreferredElements('kamelets'));
-    }, [selectedLabels]);
-
+    }
 
     function selectTab(evt: React.MouseEvent<HTMLElement, MouseEvent>, 
eventKey: string | number) {
         setSelectorTabIndex(eventKey);
@@ -87,9 +86,13 @@ export function DslSelector (props: Props) {
         setFilter('');
         setShowSelector(false);
         onDslSelect(dsl, parentId, selectedPosition);
-        const type = isEip ? 'eip' : (selectorTabIndex === 'components' ? 
'components' : 'kamelets');
         addPreferredElement(type, dsl)
     }
+    function deleteFast(evt: React.MouseEvent, dsl: DslMetaModel) {
+        evt.stopPropagation();
+        deletePreferredElement(type, dsl);
+        setPreferences();
+    }
 
     function searchInput() {
         return (
@@ -105,11 +108,11 @@ export function DslSelector (props: Props) {
                 <FlexItem>
                     <TextInputGroup>
                         <TextInputGroupMain className="text-field" type="text" 
autoComplete={"off"}
-                               value={filter}
-                               onChange={(_, value) => setFilter(value)}/>
+                                            value={filter}
+                                            onChange={(_, value) => 
setFilter(value)}/>
                         <TextInputGroupUtilities>
                             <Button variant="plain" onClick={_ => 
setFilter('')}>
-                                <TimesIcon />
+                                <TimesIcon/>
                             </Button>
                         </TextInputGroupUtilities>
                     </TextInputGroup>
@@ -118,50 +121,6 @@ export function DslSelector (props: Props) {
         )
     }
 
-    function getCard(dsl: DslMetaModel, index: number) {
-        const labels = dsl.labels !== undefined ? 
dsl.labels.split(",").filter(label => label !== 'eip') : [];
-        const isCustom = KameletApi.getCustomKameletNames().includes(dsl.name);
-        return (
-            <Card key={dsl.dsl + index} isCompact className="dsl-card"
-                  onClick={event => selectDsl(event, dsl)}>
-                <CardHeader className="header-labels">
-                    <Badge isRead className="support-level 
labels">{dsl.supportLevel}</Badge>
-                    {['kamelet', 
'component'].includes(dsl.navigation.toLowerCase()) &&
-                        <Badge isRead className="version 
labels">{dsl.version}</Badge>
-                    }
-                    {isCustom && <Badge className="custom">custom</Badge>}
-                </CardHeader>
-                <CardHeader>
-                    {CamelUi.getIconForDsl(dsl)}
-                    <Text className='dsl-card-title'>{dsl.title}</Text>
-                </CardHeader>
-                <CardBody>
-                    {/*<Text>{dsl.description}</Text>*/}
-                    <Text 
className="pf-v5-u-color-200">{dsl.description}</Text>
-                </CardBody>
-                <CardFooter className="footer-labels">
-                    <div style={{display: "flex", flexDirection: "row", 
justifyContent: "start"}}>
-                        {labels.map((label, index) => <Badge key={label + "-" 
+ index} isRead
-                                                             
className="labels">{label}</Badge>)}
-                    </div>
-
-                </CardFooter>
-            </Card>
-        )
-    }
-
-    function getFastCard(dsl: DslMetaModel, index: number) {
-        return (
-            <Card key={dsl.dsl + index} isCompact className="dsl-card 
dsl-fast-card"
-                  onClick={event => selectDsl(event, dsl)}>
-                <CardHeader className='header'>
-                    {CamelUi.getIconForDsl(dsl)}
-                    <Text className='dsl-fast-card-title'>{dsl.title}</Text>
-                </CardHeader>
-            </Card>
-        )
-    }
-
     function close() {
         setFilter('');
         setShowSelector(false);
@@ -175,7 +134,7 @@ export function DslSelector (props: Props) {
         }
     }
 
-    function filterElements(elements: DslMetaModel[], type: 'eip' | 
'components' | 'kamelets'):DslMetaModel[] {
+    function filterElements(elements: DslMetaModel[], type: 'eip' | 
'components' | 'kamelets'): DslMetaModel[] {
         return elements.filter((dsl: DslMetaModel) => CamelUi.checkFilter(dsl, 
filter))
             .filter((dsl: DslMetaModel) => {
                 if (type !== 'eip' || selectedLabels.length === 0) {
@@ -187,6 +146,7 @@ export function DslSelector (props: Props) {
     }
 
     const isEip = selectorTabIndex === 'eip';
+    const type = isEip ? 'eip' : (selectorTabIndex === 'components' ? 
'components' : 'kamelets');
     const isRouteConfig = parentDsl === 'RouteConfigurationDefinition';
     const title = parentDsl === undefined ? "Select source" : "Select step";
     const navigation: string = selectorTabIndex ? selectorTabIndex.toString() 
: '';
@@ -219,6 +179,17 @@ export function DslSelector (props: Props) {
         ? filteredComponentElements
         : (navigation === 'kamelets' ? filteredKameletElements : 
filteredEipElements);
 
+    const fastElements = elements.filter((d: DslMetaModel) => {
+        if (isEip) {
+            return preferredElements.includes(d.dsl);
+        } else if (navigation === 'components') {
+            return d.uri && preferredElements.includes(d.uri)
+        } else {
+            return preferredElements.includes(d.name)
+        }
+    }).filter((_, i) => i < 7)
+
+
     return (
         <Modal
             aria-label={title}
@@ -237,7 +208,8 @@ export function DslSelector (props: Props) {
                               onSelect={selectTab}>
                             {parentDsl !== undefined &&
                                 <Tab eventKey={"eip"} key={"tab-eip"}
-                                     title={<TabTitleText>{`Integration 
Patterns (${filteredEipElements?.length})`}</TabTitleText>}>
+                                     title={
+                                         <TabTitleText>{`Integration Patterns 
(${filteredEipElements?.length})`}</TabTitleText>}>
                                 </Tab>
                             }
                             {!isRouteConfig &&
@@ -259,30 +231,26 @@ export function DslSelector (props: Props) {
             actions={{}}>
             <PageSection padding={{default: "noPadding"}} variant={dark ? 
"darker" : "light"}>
                 {isEip && <ToggleGroup aria-label="Labels" isCompact>
-                    {eipLabels.map(eipLabel => <ToggleGroupItem
-                        key={eipLabel}
-                        text={eipLabel}
-                        buttonId={eipLabel}
-                        isSelected={selectedLabels.includes(eipLabel)}
-                        onChange={selected => selectLabel(eipLabel)}
-                    />)}
-                    <ToggleGroupItem key='clean' buttonId='clean' 
isSelected={false} onChange={clearSelectedLabels} icon={<TimesIcon/>}/>
+                    {eipLabels.map(eipLabel =>
+                        <ToggleGroupItem key={eipLabel}
+                                         text={eipLabel}
+                                         buttonId={eipLabel}
+                                         
isSelected={selectedLabels.includes(eipLabel)}
+                                         onChange={selected => 
selectLabel(eipLabel)}
+                        />)}
+                    <ToggleGroupItem key='clean' buttonId='clean' 
isSelected={false} onChange={clearSelectedLabels}
+                                     icon={<TimesIcon/>}/>
                 </ToggleGroup>}
-                <Gallery key={"fast-gallery-" + navigation} hasGutter 
className="dsl-gallery" minWidths={{default: '150px'}}>
-                    {showSelector && elements
-                        .filter((d: DslMetaModel) => {
-                            if (isEip) {
-                                return preferredElements.includes(d.dsl);
-                            } else if (navigation === 'components') {
-                                return d.uri && 
preferredElements.includes(d.uri)
-                            } else {
-                                return preferredElements.includes(d.name)
-                            }
-                        })
-                        .filter((_, i) => i < 7).map((dsl: DslMetaModel, 
index: number) => getFastCard(dsl, index))}
+                <Gallery key={"fast-gallery-" + navigation} hasGutter 
className="dsl-gallery"
+                         minWidths={{default: '150px'}}>
+                    {showSelector && fastElements.map((dsl: DslMetaModel, 
index: number) =>
+                        <DslFastCard dsl={dsl} index={index} 
onDslSelect={selectDsl} onDeleteFast={deleteFast}/>
+                    )}
                 </Gallery>
                 <Gallery key={"gallery-" + navigation} hasGutter 
className="dsl-gallery" minWidths={{default: '200px'}}>
-                    {showSelector && filteredElements.map((dsl: DslMetaModel, 
index: number) => getCard(dsl, index))}
+                    {showSelector && filteredElements.map((dsl: DslMetaModel, 
index: number) =>
+                        <DslCard dsl={dsl} index={index} 
onDslSelect={selectDsl}/>
+                    )}
                 </Gallery>
             </PageSection>
         </Modal>
diff --git a/karavan-space/src/designer/selector/DslCard.tsx 
b/karavan-space/src/designer/selector/DslCard.tsx
new file mode 100644
index 00000000..1e0b9a3f
--- /dev/null
+++ b/karavan-space/src/designer/selector/DslCard.tsx
@@ -0,0 +1,71 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import React from 'react';
+import {
+    Badge,
+    Card, CardBody, CardFooter,
+    CardHeader,
+    Text,
+} from '@patternfly/react-core';
+import './DslSelector.css';
+import {CamelUi} from "../utils/CamelUi";
+import {DslMetaModel} from "../utils/DslMetaModel";
+import {KameletApi} from "karavan-core/lib/api/KameletApi";
+
+interface Props {
+    dsl: DslMetaModel,
+    index: number
+    onDslSelect: (evt: React.MouseEvent, dsl: DslMetaModel) => void
+}
+
+export function DslCard (props: Props) {
+
+    function selectDsl(evt: React.MouseEvent, dsl: DslMetaModel) {
+        props.onDslSelect(evt, dsl);
+    }
+
+    const {dsl, index} = props;
+    const labels = dsl.labels !== undefined ? 
dsl.labels.split(",").filter(label => label !== 'eip') : [];
+    const isCustom = KameletApi.getCustomKameletNames().includes(dsl.name);
+    return (
+        <Card key={dsl.dsl + index} isCompact isPlain isFlat isRounded 
className="dsl-card"
+              onClick={event => selectDsl(event, dsl)}>
+            <CardHeader className="header-labels">
+                <Badge isRead className="support-level 
labels">{dsl.supportLevel}</Badge>
+                {['kamelet', 
'component'].includes(dsl.navigation.toLowerCase()) &&
+                    <Badge isRead className="version 
labels">{dsl.version}</Badge>
+                }
+                {isCustom && <Badge className="custom">custom</Badge>}
+            </CardHeader>
+            <CardHeader>
+                {CamelUi.getIconForDsl(dsl)}
+                <Text className='dsl-card-title'>{dsl.title}</Text>
+            </CardHeader>
+            <CardBody>
+                {/*<Text>{dsl.description}</Text>*/}
+                <Text className="pf-v5-u-color-200">{dsl.description}</Text>
+            </CardBody>
+            <CardFooter className="footer-labels">
+                <div style={{display: "flex", flexDirection: "row", 
justifyContent: "start"}}>
+                    {labels.map((label, index) => <Badge key={label + "-" + 
index} isRead
+                                                         
className="labels">{label}</Badge>)}
+                </div>
+
+            </CardFooter>
+        </Card>
+    )
+}
\ No newline at end of file
diff --git a/karavan-space/src/designer/selector/DslFastCard.tsx 
b/karavan-space/src/designer/selector/DslFastCard.tsx
new file mode 100644
index 00000000..26bb8dfb
--- /dev/null
+++ b/karavan-space/src/designer/selector/DslFastCard.tsx
@@ -0,0 +1,58 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import React from 'react';
+import {
+    Button,
+    Card,
+    CardHeader,
+    Text,
+} from '@patternfly/react-core';
+import './DslSelector.css';
+import {CamelUi} from "../utils/CamelUi";
+import {DslMetaModel} from "../utils/DslMetaModel";
+import TimesIcon from 
"@patternfly/react-icons/dist/esm/icons/outlined-times-circle-icon";
+
+interface Props {
+    dsl: DslMetaModel,
+    index: number
+    onDslSelect: (evt: React.MouseEvent, dsl: DslMetaModel) => void
+    onDeleteFast: (evt: React.MouseEvent, dsl: DslMetaModel) => void
+}
+
+export function DslFastCard (props: Props) {
+
+    function selectDsl(evt: React.MouseEvent, dsl: DslMetaModel) {
+        props.onDslSelect(evt, dsl);
+    }
+
+    function deleteFast(evt: React.MouseEvent) {
+        props.onDeleteFast(evt, dsl);
+    }
+
+    const {dsl, index} = props;
+
+    return (
+        <Card key={dsl.dsl + index} isCompact isPlain isFlat isRounded 
className="dsl-card dsl-fast-card"
+              onClick={event => selectDsl(event, dsl)}>
+            <Button className='fast-delete' variant='link' icon={<TimesIcon/>} 
onClick={deleteFast}/>
+            <CardHeader className='header'>
+                {CamelUi.getIconForDsl(dsl)}
+                <Text className='dsl-fast-card-title'>{dsl.title}</Text>
+            </CardHeader>
+        </Card>
+    )
+}
\ No newline at end of file
diff --git a/karavan-space/src/designer/selector/DslPreferences.tsx 
b/karavan-space/src/designer/selector/DslPreferences.tsx
index 87844b5a..9464b813 100644
--- a/karavan-space/src/designer/selector/DslPreferences.tsx
+++ b/karavan-space/src/designer/selector/DslPreferences.tsx
@@ -74,3 +74,16 @@ export function addPreferredElement(type: 'eip' | 
'components' | 'kamelets', dsl
         console.log(e);
     }
 }
+
+export function deletePreferredElement(type: 'eip' | 'components' | 
'kamelets', dsl: DslMetaModel) {
+    try {
+        const dslKey = type === 'eip' ? dsl.dsl : (type === 'components' ? 
dsl.uri : dsl.name);
+        const local = localStorage.getItem(PREFERRED_ELEMENTS_STORAGE_NAME);
+        const pes = local !== null ? new PreferredElements(JSON.parse(local)) 
: new PreferredElements();
+        let list: PreferredElement[] = (pes as any)[type];
+        (pes as any)[type] = [...list.filter(l => l.dslKey !== dslKey)];
+        localStorage.setItem(PREFERRED_ELEMENTS_STORAGE_NAME, 
JSON.stringify(pes));
+    } catch (e) {
+        console.log(e);
+    }
+}
diff --git a/karavan-space/src/designer/selector/DslSelector.css 
b/karavan-space/src/designer/selector/DslSelector.css
index 279c1f41..4c54ffa3 100644
--- a/karavan-space/src/designer/selector/DslSelector.css
+++ b/karavan-space/src/designer/selector/DslSelector.css
@@ -27,6 +27,8 @@
 .dsl-modal .dsl-card .pf-v5-c-card__header-main {
     display: flex;
     flex-direction: row;
+    justify-content: start;
+    overflow-x: hidden;
 }
 
 .dsl-modal .dsl-card .header-labels .pf-v5-c-card__header-main {
@@ -120,15 +122,23 @@
 .dsl-modal .labels {
     opacity: 0.5;
     font-weight: 200;
-    text-wrap: nowrap;
+    text-wrap: none;
 }
 
-.dsl-modal .dsl-card:hover .labels {
-    opacity: 1;
+.dsl-modal .dsl-card:hover .pf-v5-c-badge {
+    margin-right: 1px;
+}
+
+.dsl-modal .dsl-card:hover {
+    background-color: var(--pf-v5-global--palette--black-200);
+}
+
+.dsl-modal .dsl-card:hover .pf-v5-c-badge.pf-m-read {
+    --pf-v5-c-badge--BackgroundColor: var(--pf-v5-global--palette--black-150);
 }
 
 .dsl-modal .dsl-card:hover p {
-    color: var(--pf-v5-global--Color--100);
+    /*color: var(--pf-v5-global--Color--200);*/
 }
 
 .dsl-modal .dsl-fast-card .header p {
@@ -140,3 +150,25 @@
 .dsl-modal .dsl-fast-card .header {
     padding: 10px;
 }
+
+.dsl-modal .dsl-fast-card .fast-delete {
+    padding: 0;
+    margin: 0;
+    width: 16px;
+    height: 16px;
+    vertical-align: text-bottom;
+    position: absolute;
+    right: 0;
+    top: -1px;
+    visibility: hidden;
+}
+
+.dsl-modal .dsl-fast-card .fast-delete .pf-v5-svg {
+    fill: var(--pf-v5-global--danger-color--100);
+    width: 16px;
+    height: 16px;
+}
+
+.dsl-modal .dsl-fast-card:hover .fast-delete {
+    visibility: visible;
+}
\ No newline at end of file
diff --git a/karavan-space/src/designer/selector/DslSelector.tsx 
b/karavan-space/src/designer/selector/DslSelector.tsx
index 9e366515..50424e8c 100644
--- a/karavan-space/src/designer/selector/DslSelector.tsx
+++ b/karavan-space/src/designer/selector/DslSelector.tsx
@@ -16,12 +16,7 @@
  */
 import React, {useEffect, useState} from 'react';
 import {
-    Badge,
     Button,
-    Card,
-    CardBody,
-    CardFooter,
-    CardHeader,
     Flex,
     FlexItem,
     Gallery,
@@ -31,7 +26,6 @@ import {
     Tab,
     Tabs,
     TabTitleText,
-    Text,
     TextInputGroup,
     TextInputGroupMain,
     TextInputGroupUtilities,
@@ -47,13 +41,15 @@ import {useRouteDesignerHook} from 
"../route/useRouteDesignerHook";
 import {ComponentApi} from 'karavan-core/lib/api/ComponentApi';
 import {KameletApi} from 'karavan-core/lib/api/KameletApi';
 import TimesIcon from "@patternfly/react-icons/dist/esm/icons/times-icon";
-import {addPreferredElement, getPreferredElements} from "./DslPreferences";
+import {addPreferredElement, deletePreferredElement, getPreferredElements} 
from "./DslPreferences";
+import {DslFastCard} from "./DslFastCard";
+import {DslCard} from "./DslCard";
 
 interface Props {
     tabIndex?: string | number
 }
 
-export function DslSelector (props: Props) {
+export function DslSelector(props: Props) {
 
     const [showSelector, showSteps, parentId, parentDsl, selectorTabIndex, 
setShowSelector, setSelectorTabIndex,
         selectedPosition, selectedLabels, addSelectedLabel, 
deleteSelectedLabel, clearSelectedLabels] =
@@ -72,11 +68,14 @@ export function DslSelector (props: Props) {
     const [preferredKamelets, setPreferredKamelets] = useState<string[]>([]);
 
     useEffect(() => {
+        setPreferences();
+    }, [selectedLabels]);
+
+    function setPreferences() {
         setPreferredEip(getPreferredElements('eip'));
         setPreferredComponents(getPreferredElements('components'));
         setPreferredKamelets(getPreferredElements('kamelets'));
-    }, [selectedLabels]);
-
+    }
 
     function selectTab(evt: React.MouseEvent<HTMLElement, MouseEvent>, 
eventKey: string | number) {
         setSelectorTabIndex(eventKey);
@@ -87,9 +86,13 @@ export function DslSelector (props: Props) {
         setFilter('');
         setShowSelector(false);
         onDslSelect(dsl, parentId, selectedPosition);
-        const type = isEip ? 'eip' : (selectorTabIndex === 'components' ? 
'components' : 'kamelets');
         addPreferredElement(type, dsl)
     }
+    function deleteFast(evt: React.MouseEvent, dsl: DslMetaModel) {
+        evt.stopPropagation();
+        deletePreferredElement(type, dsl);
+        setPreferences();
+    }
 
     function searchInput() {
         return (
@@ -105,11 +108,11 @@ export function DslSelector (props: Props) {
                 <FlexItem>
                     <TextInputGroup>
                         <TextInputGroupMain className="text-field" type="text" 
autoComplete={"off"}
-                               value={filter}
-                               onChange={(_, value) => setFilter(value)}/>
+                                            value={filter}
+                                            onChange={(_, value) => 
setFilter(value)}/>
                         <TextInputGroupUtilities>
                             <Button variant="plain" onClick={_ => 
setFilter('')}>
-                                <TimesIcon />
+                                <TimesIcon/>
                             </Button>
                         </TextInputGroupUtilities>
                     </TextInputGroup>
@@ -118,50 +121,6 @@ export function DslSelector (props: Props) {
         )
     }
 
-    function getCard(dsl: DslMetaModel, index: number) {
-        const labels = dsl.labels !== undefined ? 
dsl.labels.split(",").filter(label => label !== 'eip') : [];
-        const isCustom = KameletApi.getCustomKameletNames().includes(dsl.name);
-        return (
-            <Card key={dsl.dsl + index} isCompact className="dsl-card"
-                  onClick={event => selectDsl(event, dsl)}>
-                <CardHeader className="header-labels">
-                    <Badge isRead className="support-level 
labels">{dsl.supportLevel}</Badge>
-                    {['kamelet', 
'component'].includes(dsl.navigation.toLowerCase()) &&
-                        <Badge isRead className="version 
labels">{dsl.version}</Badge>
-                    }
-                    {isCustom && <Badge className="custom">custom</Badge>}
-                </CardHeader>
-                <CardHeader>
-                    {CamelUi.getIconForDsl(dsl)}
-                    <Text className='dsl-card-title'>{dsl.title}</Text>
-                </CardHeader>
-                <CardBody>
-                    {/*<Text>{dsl.description}</Text>*/}
-                    <Text 
className="pf-v5-u-color-200">{dsl.description}</Text>
-                </CardBody>
-                <CardFooter className="footer-labels">
-                    <div style={{display: "flex", flexDirection: "row", 
justifyContent: "start"}}>
-                        {labels.map((label, index) => <Badge key={label + "-" 
+ index} isRead
-                                                             
className="labels">{label}</Badge>)}
-                    </div>
-
-                </CardFooter>
-            </Card>
-        )
-    }
-
-    function getFastCard(dsl: DslMetaModel, index: number) {
-        return (
-            <Card key={dsl.dsl + index} isCompact className="dsl-card 
dsl-fast-card"
-                  onClick={event => selectDsl(event, dsl)}>
-                <CardHeader className='header'>
-                    {CamelUi.getIconForDsl(dsl)}
-                    <Text className='dsl-fast-card-title'>{dsl.title}</Text>
-                </CardHeader>
-            </Card>
-        )
-    }
-
     function close() {
         setFilter('');
         setShowSelector(false);
@@ -175,7 +134,7 @@ export function DslSelector (props: Props) {
         }
     }
 
-    function filterElements(elements: DslMetaModel[], type: 'eip' | 
'components' | 'kamelets'):DslMetaModel[] {
+    function filterElements(elements: DslMetaModel[], type: 'eip' | 
'components' | 'kamelets'): DslMetaModel[] {
         return elements.filter((dsl: DslMetaModel) => CamelUi.checkFilter(dsl, 
filter))
             .filter((dsl: DslMetaModel) => {
                 if (type !== 'eip' || selectedLabels.length === 0) {
@@ -187,6 +146,7 @@ export function DslSelector (props: Props) {
     }
 
     const isEip = selectorTabIndex === 'eip';
+    const type = isEip ? 'eip' : (selectorTabIndex === 'components' ? 
'components' : 'kamelets');
     const isRouteConfig = parentDsl === 'RouteConfigurationDefinition';
     const title = parentDsl === undefined ? "Select source" : "Select step";
     const navigation: string = selectorTabIndex ? selectorTabIndex.toString() 
: '';
@@ -219,6 +179,17 @@ export function DslSelector (props: Props) {
         ? filteredComponentElements
         : (navigation === 'kamelets' ? filteredKameletElements : 
filteredEipElements);
 
+    const fastElements = elements.filter((d: DslMetaModel) => {
+        if (isEip) {
+            return preferredElements.includes(d.dsl);
+        } else if (navigation === 'components') {
+            return d.uri && preferredElements.includes(d.uri)
+        } else {
+            return preferredElements.includes(d.name)
+        }
+    }).filter((_, i) => i < 7)
+
+
     return (
         <Modal
             aria-label={title}
@@ -237,7 +208,8 @@ export function DslSelector (props: Props) {
                               onSelect={selectTab}>
                             {parentDsl !== undefined &&
                                 <Tab eventKey={"eip"} key={"tab-eip"}
-                                     title={<TabTitleText>{`Integration 
Patterns (${filteredEipElements?.length})`}</TabTitleText>}>
+                                     title={
+                                         <TabTitleText>{`Integration Patterns 
(${filteredEipElements?.length})`}</TabTitleText>}>
                                 </Tab>
                             }
                             {!isRouteConfig &&
@@ -259,30 +231,26 @@ export function DslSelector (props: Props) {
             actions={{}}>
             <PageSection padding={{default: "noPadding"}} variant={dark ? 
"darker" : "light"}>
                 {isEip && <ToggleGroup aria-label="Labels" isCompact>
-                    {eipLabels.map(eipLabel => <ToggleGroupItem
-                        key={eipLabel}
-                        text={eipLabel}
-                        buttonId={eipLabel}
-                        isSelected={selectedLabels.includes(eipLabel)}
-                        onChange={selected => selectLabel(eipLabel)}
-                    />)}
-                    <ToggleGroupItem key='clean' buttonId='clean' 
isSelected={false} onChange={clearSelectedLabels} icon={<TimesIcon/>}/>
+                    {eipLabels.map(eipLabel =>
+                        <ToggleGroupItem key={eipLabel}
+                                         text={eipLabel}
+                                         buttonId={eipLabel}
+                                         
isSelected={selectedLabels.includes(eipLabel)}
+                                         onChange={selected => 
selectLabel(eipLabel)}
+                        />)}
+                    <ToggleGroupItem key='clean' buttonId='clean' 
isSelected={false} onChange={clearSelectedLabels}
+                                     icon={<TimesIcon/>}/>
                 </ToggleGroup>}
-                <Gallery key={"fast-gallery-" + navigation} hasGutter 
className="dsl-gallery" minWidths={{default: '150px'}}>
-                    {showSelector && elements
-                        .filter((d: DslMetaModel) => {
-                            if (isEip) {
-                                return preferredElements.includes(d.dsl);
-                            } else if (navigation === 'components') {
-                                return d.uri && 
preferredElements.includes(d.uri)
-                            } else {
-                                return preferredElements.includes(d.name)
-                            }
-                        })
-                        .filter((_, i) => i < 7).map((dsl: DslMetaModel, 
index: number) => getFastCard(dsl, index))}
+                <Gallery key={"fast-gallery-" + navigation} hasGutter 
className="dsl-gallery"
+                         minWidths={{default: '150px'}}>
+                    {showSelector && fastElements.map((dsl: DslMetaModel, 
index: number) =>
+                        <DslFastCard dsl={dsl} index={index} 
onDslSelect={selectDsl} onDeleteFast={deleteFast}/>
+                    )}
                 </Gallery>
                 <Gallery key={"gallery-" + navigation} hasGutter 
className="dsl-gallery" minWidths={{default: '200px'}}>
-                    {showSelector && filteredElements.map((dsl: DslMetaModel, 
index: number) => getCard(dsl, index))}
+                    {showSelector && filteredElements.map((dsl: DslMetaModel, 
index: number) =>
+                        <DslCard dsl={dsl} index={index} 
onDslSelect={selectDsl}/>
+                    )}
                 </Gallery>
             </PageSection>
         </Modal>

Reply via email to