You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@camel.apache.org by ma...@apache.org on 2024/03/02 00:07:07 UTC
(camel-karavan) branch main updated: Navigation improvements
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 ad3a375e Navigation improvements
ad3a375e is described below
commit ad3a375e4109969eef864845beb612330ae8b340
Author: Marat Gubaidullin <ma...@talismancloud.io>
AuthorDate: Fri Mar 1 19:07:00 2024 -0500
Navigation improvements
---
karavan-app/src/main/webui/src/api/ProjectStore.ts | 8 +-
.../src/main/webui/src/editor/CodeEditor.tsx | 68 ++++++++++++
.../FileEditor.tsx => editor/DesignerEditor.tsx} | 52 ++-------
.../EditorToolbar.tsx} | 56 +++-------
.../src/main/webui/src/editor/FileEditor.tsx | 50 +++++++++
.../src/main/webui/src/project/ProjectPage.tsx | 24 +++--
.../src/main/webui/src/project/ProjectPanel.tsx | 2 +-
.../src/main/webui/src/project/ProjectTitle.tsx | 117 +++++++++++++--------
.../src/main/webui/src/project/ProjectToolbar.tsx | 20 +---
9 files changed, 237 insertions(+), 160 deletions(-)
diff --git a/karavan-app/src/main/webui/src/api/ProjectStore.ts b/karavan-app/src/main/webui/src/api/ProjectStore.ts
index ccbb0399..861a0c06 100644
--- a/karavan-app/src/main/webui/src/api/ProjectStore.ts
+++ b/karavan-app/src/main/webui/src/api/ProjectStore.ts
@@ -130,7 +130,7 @@ export const useProjectStore = createWithEqualityFn<ProjectState>((set) => ({
project: new Project(),
images: [],
operation: 'none',
- tabIndex: 'files',
+ tabIndex: 'topology',
isPushing: false,
isPulling: false,
isRunning: false,
@@ -152,9 +152,9 @@ export const useProjectStore = createWithEqualityFn<ProjectState>((set) => ({
}));
},
setTabIndex: (tabIndex: string | number) => {
- set((state: ProjectState) => ({
- tabIndex: tabIndex
- }));
+ set((state: ProjectState) => {
+ return {tabIndex: tabIndex};
+ });
},
setImages: (images: string[]) => {
set((state: ProjectState) => {
diff --git a/karavan-app/src/main/webui/src/editor/CodeEditor.tsx b/karavan-app/src/main/webui/src/editor/CodeEditor.tsx
new file mode 100644
index 00000000..3658889b
--- /dev/null
+++ b/karavan-app/src/main/webui/src/editor/CodeEditor.tsx
@@ -0,0 +1,68 @@
+/*
+ * 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, {useEffect, useState} from 'react';
+import '../designer/karavan.css';
+import Editor from "@monaco-editor/react";
+import {useFileStore} from "../api/ProjectStore";
+import {ProjectService} from "../api/ProjectService";
+import {shallow} from "zustand/shallow";
+
+interface Props {
+ projectId: string
+}
+
+const languages = new Map<string, string>([
+ ['sh', 'shell'],
+ ['md', 'markdown'],
+ ['properties', 'ini']
+])
+
+export function CodeEditor(props: Props) {
+
+ const [file, designerTab, setFile] = useFileStore((s) => [s.file, s.designerTab, s.setFile], shallow)
+ const [code, setCode] = useState<string>();
+
+ useEffect(() => {
+ setCode(file?.code);
+ }, []);
+
+ function save(name: string, code: string) {
+ if (file) {
+ file.code = code;
+ ProjectService.updateFile(file, true);
+ }
+ }
+
+ const extension = file?.name.split('.').pop();
+ const language = extension && languages.has(extension) ? languages.get(extension) : extension;
+ return (
+ file !== undefined ?
+ <Editor
+ height="100vh"
+ defaultLanguage={language}
+ theme={'light'}
+ value={code}
+ className={'code-editor'}
+ onChange={(value, ev) => {
+ if (value) {
+ save(file?.name, value)
+ }
+ }}
+ />
+ : <></>
+ )
+}
diff --git a/karavan-app/src/main/webui/src/project/FileEditor.tsx b/karavan-app/src/main/webui/src/editor/DesignerEditor.tsx
similarity index 79%
rename from karavan-app/src/main/webui/src/project/FileEditor.tsx
rename to karavan-app/src/main/webui/src/editor/DesignerEditor.tsx
index 95a1a195..a4391f72 100644
--- a/karavan-app/src/main/webui/src/project/FileEditor.tsx
+++ b/karavan-app/src/main/webui/src/editor/DesignerEditor.tsx
@@ -17,7 +17,6 @@
import React, {useEffect, useState} from 'react';
import '../designer/karavan.css';
import Editor from "@monaco-editor/react";
-import {CamelDefinitionYaml} from "karavan-core/lib/api/CamelDefinitionYaml";
import {ProjectFile} from "../api/ProjectModels";
import {useFilesStore, useFileStore} from "../api/ProjectStore";
import {KaravanDesigner} from "../designer/KaravanDesigner";
@@ -25,21 +24,16 @@ import {ProjectService} from "../api/ProjectService";
import {shallow} from "zustand/shallow";
import {CodeUtils} from "../util/CodeUtils";
import {RegistryBeanDefinition} from "karavan-core/lib/model/CamelDefinition";
+import {KameletApi} from "karavan-core/lib/api/KameletApi";
+import {CamelDefinitionYaml} from "karavan-core/lib/api/CamelDefinitionYaml";
import {TopologyUtils} from "karavan-core/lib/api/TopologyUtils";
import {IntegrationFile} from "karavan-core/lib/model/IntegrationDefinition";
-import { KameletApi } from 'karavan-core/lib/api/KameletApi';
interface Props {
projectId: string
}
-const languages = new Map<string, string>([
- ['sh', 'shell'],
- ['md', 'markdown'],
- ['properties', 'ini']
-])
-
-export function FileEditor(props: Props) {
+export function DesignerEditor(props: Props) {
const [file, designerTab, setFile] = useFileStore((s) => [s.file, s.designerTab, s.setFile], shallow)
const [files] = useFilesStore((s) => [s.files], shallow);
@@ -67,7 +61,7 @@ export function FileEditor(props: Props) {
if (props.projectId.includes('kamelets') && file) {
KameletApi.saveKamelet(file?.code);
}
- };
+ };
}, []);
function save(name: string, code: string) {
@@ -117,9 +111,7 @@ export function FileEditor(props: Props) {
}
}
- function getDesigner() {
- return (
- file !== undefined &&
+ return (file !== undefined ?
<KaravanDesigner key={key}
showCodeTab={true}
dark={false}
@@ -136,38 +128,6 @@ export function FileEditor(props: Props) {
onInternalConsumerClick={internalConsumerClick}
files={files.map(f => new IntegrationFile(f.name, f.code))}
/>
- )
- }
-
- function getEditor() {
- const extension = file?.name.split('.').pop();
- const language = extension && languages.has(extension) ? languages.get(extension) : extension;
- return (
- file !== undefined &&
- <Editor
- height="100vh"
- defaultLanguage={language}
- theme={'light'}
- value={code}
- className={'code-editor'}
- onChange={(value, ev) => {
- if (value) {
- save(file?.name, value)
- }
- }}
- />
- )
- }
-
- const isCamelYaml = file !== undefined && file.name.endsWith(".camel.yaml");
- const isKameletYaml = file !== undefined && file.name.endsWith(".kamelet.yaml");
- const isIntegration = isCamelYaml && file?.code && CamelDefinitionYaml.yamlIsIntegration(file.code);
- const showDesigner = (isCamelYaml && isIntegration) || isKameletYaml;
- const showEditor = !showDesigner;
- return (
- <>
- {showDesigner && getDesigner()}
- {showEditor && getEditor()}
- </>
+ : <></>
)
}
diff --git a/karavan-app/src/main/webui/src/project/ProjectToolbar.tsx b/karavan-app/src/main/webui/src/editor/EditorToolbar.tsx
similarity index 57%
copy from karavan-app/src/main/webui/src/project/ProjectToolbar.tsx
copy to karavan-app/src/main/webui/src/editor/EditorToolbar.tsx
index a3c9471a..f583fa00 100644
--- a/karavan-app/src/main/webui/src/project/ProjectToolbar.tsx
+++ b/karavan-app/src/main/webui/src/editor/EditorToolbar.tsx
@@ -23,12 +23,11 @@ import {
ToolbarContent,
} from '@patternfly/react-core';
import '../designer/karavan.css';
-import {DevModeToolbar} from "./DevModeToolbar";
import {useFileStore, useProjectStore} from "../api/ProjectStore";
import {shallow} from "zustand/shallow";
-import {BuildToolbar} from "./BuildToolbar";
+import {DevModeToolbar} from "../project/DevModeToolbar";
-export function ProjectToolbar() {
+export function EditorToolbar() {
const [project, tabIndex] = useProjectStore((s) => [s.project, s.tabIndex], shallow)
const [file] = useFileStore((state) => [state.file], shallow)
@@ -36,37 +35,6 @@ export function ProjectToolbar() {
useEffect(() => {
}, [project, file]);
- function isFile(): boolean {
- return file !== undefined;
- }
-
- function getFileToolbar() {
- return (
- <Toolbar id="toolbar-group-types">
- <ToolbarContent>
- <Flex className="" direction={{default: "row"}}
- justifyContent={{default: 'justifyContentSpaceBetween'}}
- alignItems={{default: "alignItemsCenter"}}>
- {isRunnable() &&
- <FlexItem align={{default: 'alignRight'}}>
- <DevModeToolbar reloadOnly={true}/>
- </FlexItem>
- }
- </Flex>
- </ToolbarContent>
- </Toolbar>
- )
- }
-
- function getProjectToolbar() {
- return (<Toolbar id="toolbar-group-types">
- <ToolbarContent>
- {isRunnable() && <DevModeToolbar/>}
- {isBuildContainer() && <BuildToolbar/>}
- </ToolbarContent>
- </Toolbar>)
- }
-
function isKameletsProject(): boolean {
return project.projectId === 'kamelets';
}
@@ -83,9 +51,19 @@ export function ProjectToolbar() {
return !isKameletsProject() && !isTemplatesProject() && !isServicesProject() && !['build', 'container'].includes(tabIndex.toString());
}
- function isBuildContainer(): boolean {
- return !isKameletsProject() && !isTemplatesProject() && !isServicesProject() && ['build', 'container'].includes(tabIndex.toString());
- }
-
- return isFile() ? getFileToolbar() : getProjectToolbar()
+ return (
+ <Toolbar id="toolbar-group-types">
+ <ToolbarContent>
+ <Flex className="" direction={{default: "row"}}
+ justifyContent={{default: 'justifyContentSpaceBetween'}}
+ alignItems={{default: "alignItemsCenter"}}>
+ {isRunnable() &&
+ <FlexItem align={{default: 'alignRight'}}>
+ <DevModeToolbar reloadOnly={true}/>
+ </FlexItem>
+ }
+ </Flex>
+ </ToolbarContent>
+ </Toolbar>
+ )
}
diff --git a/karavan-app/src/main/webui/src/editor/FileEditor.tsx b/karavan-app/src/main/webui/src/editor/FileEditor.tsx
new file mode 100644
index 00000000..2812d005
--- /dev/null
+++ b/karavan-app/src/main/webui/src/editor/FileEditor.tsx
@@ -0,0 +1,50 @@
+/*
+ * 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 '../designer/karavan.css';
+import {useFileStore} from "../api/ProjectStore";
+import {shallow} from "zustand/shallow";
+import {CamelDefinitionYaml} from "karavan-core/lib/api/CamelDefinitionYaml";
+import {DesignerEditor} from "./DesignerEditor";
+import {CodeEditor} from "./CodeEditor";
+
+interface Props {
+ projectId: string
+}
+
+const languages = new Map<string, string>([
+ ['sh', 'shell'],
+ ['md', 'markdown'],
+ ['properties', 'ini']
+])
+
+export function FileEditor(props: Props) {
+
+ const [file] = useFileStore((s) => [s.file], shallow)
+
+ const isCamelYaml = file !== undefined && file.name.endsWith(".camel.yaml");
+ const isKameletYaml = file !== undefined && file.name.endsWith(".kamelet.yaml");
+ const isIntegration = isCamelYaml && file?.code && CamelDefinitionYaml.yamlIsIntegration(file.code);
+ const showDesigner = (isCamelYaml && isIntegration) || isKameletYaml;
+ const showEditor = !showDesigner;
+ return (
+ <>
+ {showDesigner && <DesignerEditor projectId={props.projectId}/>}
+ {showEditor && <CodeEditor projectId={props.projectId}/>}
+ </>
+ )
+}
diff --git a/karavan-app/src/main/webui/src/project/ProjectPage.tsx b/karavan-app/src/main/webui/src/project/ProjectPage.tsx
index 6ed5ad4e..0773203d 100644
--- a/karavan-app/src/main/webui/src/project/ProjectPage.tsx
+++ b/karavan-app/src/main/webui/src/project/ProjectPage.tsx
@@ -29,7 +29,7 @@ import {useAppConfigStore, useFilesStore, useFileStore, useProjectsStore, usePro
import {MainToolbar} from "../designer/MainToolbar";
import {ProjectTitle} from "./ProjectTitle";
import {ProjectPanel} from "./ProjectPanel";
-import {FileEditor} from "./FileEditor";
+import {FileEditor} from "../editor/FileEditor";
import {shallow} from "zustand/shallow";
import {useParams} from "react-router-dom";
import {KaravanApi} from "../api/KaravanApi";
@@ -91,15 +91,19 @@ export function ProjectPage() {
<PageSection className="tools-section" padding={{default: 'noPadding'}}>
<Flex direction={{default: "column"}} spaceItems={{default: "spaceItemsNone"}}>
<FlexItem className="project-tabs">
- {showTabs() && <Tabs activeKey={tabIndex} onSelect={(event, tabIndex) => setTabIndex(tabIndex)}>
- {!ephemeral && <Tab eventKey="topology" title="Topology"/>}
- <Tab eventKey="files" title="Files"/>
- {!ephemeral && <Tab eventKey="dashboard" title="Dashboard"/>}
- {!ephemeral && <Tab eventKey="trace" title="Trace"/>}
- {!ephemeral && <Tab eventKey="build" title="Build"/>}
- <Tab eventKey="container" title="Container"/>
- {hasReadme() && <Tab eventKey="readme" title="Readme"/>}
- </Tabs>}
+ {showTabs() &&
+ <Tabs activeKey={tabIndex} onSelect={(event, tabIndex) => {
+ setTabIndex(tabIndex);
+ }}>
+ {!ephemeral && <Tab eventKey="topology" title="Topology"/>}
+ <Tab eventKey="files" title="Files"/>
+ {!ephemeral && <Tab eventKey="dashboard" title="Dashboard"/>}
+ {!ephemeral && <Tab eventKey="trace" title="Trace"/>}
+ {!ephemeral && <Tab eventKey="build" title="Build"/>}
+ <Tab eventKey="container" title="Container"/>
+ {hasReadme() && <Tab eventKey="readme" title="Readme"/>}
+ </Tabs>
+ }
</FlexItem>
</Flex>
</PageSection>
diff --git a/karavan-app/src/main/webui/src/project/ProjectPanel.tsx b/karavan-app/src/main/webui/src/project/ProjectPanel.tsx
index 238d6b47..c98e9d02 100644
--- a/karavan-app/src/main/webui/src/project/ProjectPanel.tsx
+++ b/karavan-app/src/main/webui/src/project/ProjectPanel.tsx
@@ -53,7 +53,7 @@ export function ProjectPanel() {
function onRefresh() {
if (project.projectId) {
ProjectService.refreshProjectData(project.projectId);
- setTab(project.type === ProjectType.normal ? 'topology' : 'files');
+ setTab(project.type !== ProjectType.normal ? 'files' : tab);
}
}
diff --git a/karavan-app/src/main/webui/src/project/ProjectTitle.tsx b/karavan-app/src/main/webui/src/project/ProjectTitle.tsx
index da03f0fe..1c43c281 100644
--- a/karavan-app/src/main/webui/src/project/ProjectTitle.tsx
+++ b/karavan-app/src/main/webui/src/project/ProjectTitle.tsx
@@ -23,56 +23,89 @@ import {
Text,
TextContent,
Flex,
- FlexItem,
+ FlexItem, Button
} from '@patternfly/react-core';
import '../designer/karavan.css';
import {getProjectFileType} from "../api/ProjectModels";
-import {useAppConfigStore, useFileStore, useProjectStore} from "../api/ProjectStore";
+import {useFileStore, useProjectStore} from "../api/ProjectStore";
+import TopologyIcon from "@patternfly/react-icons/dist/js/icons/topology-icon";
+import FilesIcon from "@patternfly/react-icons/dist/js/icons/folder-open-icon";
+import {shallow} from "zustand/shallow";
-export function ProjectTitle () {
+export function ProjectTitle() {
- const {project} = useProjectStore();
- const {file, operation, setFile} = useFileStore();
- const {config} = useAppConfigStore();
+ const [project, tabIndex, setTabIndex] =
+ useProjectStore((s) => [s.project, s.tabIndex, s.setTabIndex], shallow);
+ const [file,setFile, operation] = useFileStore((s) => [s.file, s.setFile, s.operation], shallow);
const isFile = file !== undefined;
const isLog = file !== undefined && file.name.endsWith("log");
const filename = file ? file.name.substring(0, file.name.lastIndexOf('.')) : "";
- return (<div className="dsl-title project-title">
- {isFile && <Flex direction={{default: "column"}} >
- <FlexItem>
- <Breadcrumb>
- <BreadcrumbItem to="#" onClick={event => {
- useFileStore.setState({file: undefined, operation: 'none'});
- }}>
- <div className={"project-breadcrumb"}>{project?.name + " (" + project?.projectId + ")"}</div>
- </BreadcrumbItem>
- </Breadcrumb>
- </FlexItem>
- <FlexItem>
- <Flex direction={{default: "row"}}>
- <FlexItem>
- <Badge>{getProjectFileType(file)}</Badge>
- </FlexItem>
- <FlexItem>
- <TextContent className="description">
- <Text>{isLog ? filename : file.name}</Text>
- </TextContent>
- </FlexItem>
+
+ function getProjectTitle() {
+ return (
+ <Flex direction={{default: "column"}}>
+ <FlexItem>
+ <TextContent className="title">
+ <Text component="h2">{project?.name + " (" + project?.projectId + ")"}</Text>
+ </TextContent>
+ </FlexItem>
+ <FlexItem>
+ <TextContent className="description">
+ <Text>{project?.description}</Text>
+ </TextContent>
+ </FlexItem>
+ </Flex>
+ )
+ }
+
+ function getFileTitle() {
+ return (isFile ?
+ <Flex alignItems={{default: "alignItemsCenter"}}>
+ <Flex direction={{default: "column"}}>
+ <FlexItem>
+ <Breadcrumb>
+ <BreadcrumbItem to="#" onClick={event => {
+ setFile('none', undefined);
+ }}>
+ <div className={"project-breadcrumb"}>{'Back to ' +project?.name + " project"}</div>
+ </BreadcrumbItem>
+ <BreadcrumbItem to="#" onClick={_ => {
+ setTabIndex('topology');
+ setFile('none', undefined);
+ }}>
+ <TopologyIcon/>
+ </BreadcrumbItem>
+ <BreadcrumbItem to="#files" onClick={_ => {
+ setTabIndex('files');
+ setFile('none', undefined);
+ }}>
+ <FilesIcon/>
+ </BreadcrumbItem>
+ </Breadcrumb>
+ </FlexItem>
+ <FlexItem>
+ <Flex direction={{default: "row"}}>
+ <FlexItem>
+ <Badge>{getProjectFileType(file)}</Badge>
+ </FlexItem>
+ <FlexItem>
+ <TextContent className="description">
+ <Text>{isLog ? filename : file.name}</Text>
+ </TextContent>
+ </FlexItem>
+ </Flex>
+ </FlexItem>
+ </Flex>
</Flex>
- </FlexItem>
- </Flex>}
- {!isFile && <Flex direction={{default: "column"}} >
- <FlexItem>
- <TextContent className="title">
- <Text component="h2">{project?.name + " (" + project?.projectId + ")"}</Text>
- </TextContent>
- </FlexItem>
- <FlexItem>
- <TextContent className="description">
- <Text>{project?.description}</Text>
- </TextContent>
- </FlexItem>
- </Flex>}
- </div>)
+ : <></>
+ )
+ }
+
+ return (
+ <div className="dsl-title project-title">
+ {isFile && getFileTitle()}
+ {!isFile && getProjectTitle()}
+ </div>
+ )
}
diff --git a/karavan-app/src/main/webui/src/project/ProjectToolbar.tsx b/karavan-app/src/main/webui/src/project/ProjectToolbar.tsx
index a3c9471a..ef5f9ba5 100644
--- a/karavan-app/src/main/webui/src/project/ProjectToolbar.tsx
+++ b/karavan-app/src/main/webui/src/project/ProjectToolbar.tsx
@@ -27,6 +27,7 @@ import {DevModeToolbar} from "./DevModeToolbar";
import {useFileStore, useProjectStore} from "../api/ProjectStore";
import {shallow} from "zustand/shallow";
import {BuildToolbar} from "./BuildToolbar";
+import {EditorToolbar} from "../editor/EditorToolbar";
export function ProjectToolbar() {
@@ -40,23 +41,6 @@ export function ProjectToolbar() {
return file !== undefined;
}
- function getFileToolbar() {
- return (
- <Toolbar id="toolbar-group-types">
- <ToolbarContent>
- <Flex className="" direction={{default: "row"}}
- justifyContent={{default: 'justifyContentSpaceBetween'}}
- alignItems={{default: "alignItemsCenter"}}>
- {isRunnable() &&
- <FlexItem align={{default: 'alignRight'}}>
- <DevModeToolbar reloadOnly={true}/>
- </FlexItem>
- }
- </Flex>
- </ToolbarContent>
- </Toolbar>
- )
- }
function getProjectToolbar() {
return (<Toolbar id="toolbar-group-types">
@@ -87,5 +71,5 @@ export function ProjectToolbar() {
return !isKameletsProject() && !isTemplatesProject() && !isServicesProject() && ['build', 'container'].includes(tabIndex.toString());
}
- return isFile() ? getFileToolbar() : getProjectToolbar()
+ return isFile() ? <EditorToolbar/> : getProjectToolbar()
}