diff --git a/public/index.html b/public/index.html
index 549a1fb387..f5fc5f3efe 100644
--- a/public/index.html
+++ b/public/index.html
@@ -1,14 +1,14 @@
-
-
-
-
-
-
-
- Monokle
-
-
-
-
+
+
+
+
+
+
+
+ Monokle
+
+
+
+
diff --git a/src/App.tsx b/src/App.tsx
index 59693f312d..9316885ab6 100644
--- a/src/App.tsx
+++ b/src/App.tsx
@@ -54,15 +54,18 @@ const SettingsManager = React.lazy(() => import('@organisms/SettingsManager'));
const StartupModal = React.lazy(() => import('@organisms/StartupModal'));
const UpdateModal = React.lazy(() => import('@organisms/UpdateModal'));
-const AppContainer = styled.div<{$height: number; $width: number}>`
- ${props => (props.$height ? `height: ${props.$height}px;` : `height: 100%;`)}
- ${props => (props.$width ? `width: ${props.$width}px;` : `width: 100%;`)}
+const AppContainer = styled.div`
+ height: 100%;
+ width: 100%;
overflow: hidden;
`;
const MainContainer = styled.div`
height: 100%;
width: 100%;
+
+ display: grid;
+ grid-template-rows: max-content 1fr max-content;
`;
const App = () => {
@@ -238,33 +241,33 @@ const App = () => {
return (
-
+
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
{isChangeFiltersConfirmModalVisible && }
{isClusterDiffModalVisible && }
diff --git a/src/components/atoms/FileExplorer/FileExplorer.tsx b/src/components/atoms/FileExplorer/FileExplorer.tsx
index 51f25697c1..fe344415fd 100644
--- a/src/components/atoms/FileExplorer/FileExplorer.tsx
+++ b/src/components/atoms/FileExplorer/FileExplorer.tsx
@@ -5,6 +5,7 @@ import {useEffect} from 'react';
import log from 'loglevel';
import {getChannelName} from '@utils/ipc';
+
import {FileExplorerOptions} from './FileExplorerOptions';
export type FileExplorerProps = {
diff --git a/src/components/atoms/Footer/Footer.tsx b/src/components/atoms/Footer/Footer.tsx
deleted file mode 100644
index 11ed6d2f1c..0000000000
--- a/src/components/atoms/Footer/Footer.tsx
+++ /dev/null
@@ -1,22 +0,0 @@
-import React from 'react';
-
-import Layout, {LayoutProps} from 'antd/lib/layout/index';
-
-import styled from 'styled-components';
-
-const AntFooter = Layout.Footer;
-
-export type FooterProps = LayoutProps & {
- noborder?: React.ReactNode;
-};
-
-const Footer = styled((props: FooterProps) => )`
- ${props =>
- props.noborder &&
- `
- padding: 0px;
- margin: 0px;
- `};
-`;
-
-export default Footer;
diff --git a/src/components/atoms/Footer/index.ts b/src/components/atoms/Footer/index.ts
deleted file mode 100644
index 17beaf8fa8..0000000000
--- a/src/components/atoms/Footer/index.ts
+++ /dev/null
@@ -1,2 +0,0 @@
-export type {FooterProps} from './Footer';
-export {default} from './Footer';
diff --git a/src/components/atoms/index.ts b/src/components/atoms/index.ts
index 4b24a5050b..a342a545d7 100644
--- a/src/components/atoms/index.ts
+++ b/src/components/atoms/index.ts
@@ -14,3 +14,4 @@ export {default as KeyValueInput} from './KeyValueInput';
export {default as Dots} from './Dots';
export {default as Spinner} from './Spinner';
export {default as Icon} from './Icon';
+export {default as TabHeader} from './TabHeader';
diff --git a/src/components/molecules/FormEditor/FormEditor.tsx b/src/components/molecules/FormEditor/FormEditor.tsx
index a93d3455c6..da9a6c8fa7 100644
--- a/src/components/molecules/FormEditor/FormEditor.tsx
+++ b/src/components/molecules/FormEditor/FormEditor.tsx
@@ -35,7 +35,7 @@ const FormContainer = styled.div`
width: 100%;
padding: 20px 15px 0px 15px;
margin: 0px;
- overflow-y: scroll;
+ overflow-y: auto;
overflow-x: hidden;
${GlobalScrollbarStyle}
diff --git a/src/components/molecules/GraphView/GraphView.tsx b/src/components/molecules/GraphView/GraphView.tsx
index 899686457d..b8d935cc70 100644
--- a/src/components/molecules/GraphView/GraphView.tsx
+++ b/src/components/molecules/GraphView/GraphView.tsx
@@ -79,9 +79,13 @@ const getLayoutedElements = (elements: any[]): any => {
});
};
-const GraphView = (props: {editorHeight: string}) => {
+interface IProps {
+ editorHeight: number;
+}
+
+const GraphView: React.FC = props => {
const {editorHeight} = props;
- const graphAreaHeight = parseInt(editorHeight, 10) - 150;
+ const graphAreaHeight = editorHeight - 150;
const fileMap = useAppSelector(state => state.main.fileMap);
const resourceMap = useAppSelector(state => state.main.resourceMap);
const activeResources = useSelector(activeResourcesSelector);
diff --git a/src/components/molecules/TitleBar/TitleBar.tsx b/src/components/molecules/TitleBar/TitleBar.tsx
index e6d63928ca..f63371425c 100644
--- a/src/components/molecules/TitleBar/TitleBar.tsx
+++ b/src/components/molecules/TitleBar/TitleBar.tsx
@@ -4,14 +4,23 @@ import {MonoPaneTitle} from '@components/atoms';
import * as S from './styled';
-function TitleBar(props: {title: string; children?: React.ReactNode}) {
+interface IProps {
+ title: string;
+}
+
+const TitleBar: React.FC = props => {
const {title, children} = props;
+
return (
-
- {title}
- {children && {children}}
-
+
+
+
+ {title}
+ {children && {children}}
+
+
+
);
-}
+};
export default TitleBar;
diff --git a/src/components/molecules/TitleBar/styled.tsx b/src/components/molecules/TitleBar/styled.tsx
index 916ef2eccf..7013bda08f 100644
--- a/src/components/molecules/TitleBar/styled.tsx
+++ b/src/components/molecules/TitleBar/styled.tsx
@@ -5,18 +5,17 @@ import {BackgroundColors} from '@styles/Colors';
export const Container = styled.div`
display: flex;
- height: 24px;
justify-content: space-between;
- border-bottom: ${AppBorders.sectionDivider};
- width: 100%;
- height: 40px;
- margin: 0;
- padding: 0;
- background: ${BackgroundColors.darkThemeBackground};
+ align-items: center;
`;
export const RightButtons = styled.div`
- float: right;
display: flex;
align-items: center;
`;
+
+export const TitleBarContainer = styled.div`
+ width: 100%;
+ border-bottom: ${AppBorders.sectionDivider};
+ background: ${BackgroundColors.darkThemeBackground};
+`;
diff --git a/src/components/molecules/index.ts b/src/components/molecules/index.ts
index b7d85615ca..65679043a3 100644
--- a/src/components/molecules/index.ts
+++ b/src/components/molecules/index.ts
@@ -3,7 +3,6 @@ export {default as FormEditor} from './FormEditor';
export {default as GraphView} from './GraphView';
export {default as Monaco} from './Monaco';
export {default as ScrollIntoView} from './ScrollIntoView';
-export {default as TitleBar} from './TitleBar';
export {default as SectionRenderer} from './SectionRenderer';
export {default as ClusterDiff} from './ClusterDiff';
export {default as ResourceFilter} from './ResourceFilter';
@@ -11,4 +10,7 @@ export {default as ResourceFilterIconWithPopover} from './ResourceFilterIconWith
export {default as ResourceDiff} from './ResourceDiff';
export {default as PreviewDropdown} from './PreviewDropdown';
export {default as TemplateFormRenderer} from './TemplateFormRenderer';
+export {default as TitleBar} from './TitleBar';
export {default as ValidationErrorsPopover} from './ValidationErrorsPopover';
+export {default as HelmChartModalConfirmWithNamespaceSelect} from './HelmChartModalConfirmWithNamespaceSelect';
+export {default as ModalConfirmWithNamespaceSelect} from './ModalConfirmWithNamespaceSelect';
diff --git a/src/components/organisms/ActionsPane/ActionsPane.styled.tsx b/src/components/organisms/ActionsPane/ActionsPane.styled.tsx
index 821f11a538..7af69e9fea 100644
--- a/src/components/organisms/ActionsPane/ActionsPane.styled.tsx
+++ b/src/components/organisms/ActionsPane/ActionsPane.styled.tsx
@@ -4,9 +4,11 @@ import styled from 'styled-components';
import {GlobalScrollbarStyle} from '@utils/scrollbar';
-import {BackgroundColors} from '@styles/Colors';
-
-export const Tabs = styled(RawTabs)`
+export const Tabs = styled(RawTabs)<{$height: number; $width: number}>`
+ ${({$height, $width}) => `
+ height: ${$height}px;
+ width: ${$width}px;
+ `};
overflow: visible;
& .ant-tabs-nav {
@@ -17,23 +19,24 @@ export const Tabs = styled(RawTabs)`
& .ant-tabs-nav::before {
border-bottom: 1px solid #363636;
}
+
+ & .ant-tabs-content {
+ height: 100%;
+ }
`;
-export const ActionsPaneContainer = styled.div<{$height: number}>`
- ${props => props.$height && `height: ${props.$height}px;`}
+export const ActionsPaneContainer = styled.div`
+ height: 100%;
width: 100%;
- background-color: ${BackgroundColors.darkThemeBackground};
- margin: 0;
- padding: 0;
- display: flex;
- flex-direction: column;
- align-content: start;
- align-items: start;
+
+ display: grid;
+ grid-template-rows: 1fr max-content;
`;
export const ActionsPaneFooterContainer = styled.div`
position: relative;
width: 100%;
+ height: 100%;
& .react-resizable {
overflow-y: auto;
@@ -51,21 +54,11 @@ export const ActionsPaneFooterContainer = styled.div`
}
`;
-export const TabsContainer = styled.div`
- flex-grow: 1;
- flex-basis: 0;
+export const ActionsPaneMainContainer = styled.div`
+ height: 100%;
width: 100%;
-`;
-
-export const TitleBarContainer = styled.div`
- display: flex;
- height: 24px;
- justify-content: space-between;
-`;
-
-export const RightButtons = styled.div`
- float: right;
- display: flex;
+ display: grid;
+ grid-template-rows: max-content 1fr;
`;
export const DiffButton = styled(Button)`
diff --git a/src/components/organisms/ActionsPane/ActionsPane.tsx b/src/components/organisms/ActionsPane/ActionsPane.tsx
index f7dc7468b0..6a1447e80d 100644
--- a/src/components/organisms/ActionsPane/ActionsPane.tsx
+++ b/src/components/organisms/ActionsPane/ActionsPane.tsx
@@ -1,19 +1,17 @@
import {ipcRenderer} from 'electron';
-import {LegacyRef, useCallback, useContext, useEffect, useMemo, useRef, useState} from 'react';
+import {LegacyRef, useCallback, useEffect, useMemo, useRef, useState} from 'react';
import {ResizableBox} from 'react-resizable';
import {useMeasure} from 'react-use';
-import {Button, Row, Tabs, Tooltip} from 'antd';
+import {Button, Tabs, Tooltip} from 'antd';
import {ArrowLeftOutlined, ArrowRightOutlined, BookOutlined, CodeOutlined, ContainerOutlined} from '@ant-design/icons';
import {
ACTIONS_PANE_FOOTER_DEFAULT_HEIGHT,
ACTIONS_PANE_FOOTER_EXPANDED_DEFAULT_HEIGHT,
- ACTIONS_PANE_TAB_PANE_OFFSET,
KUSTOMIZE_HELP_URL,
- NAVIGATOR_HEIGHT_OFFSET,
TOOLTIP_DELAY,
} from '@constants/constants';
import {makeApplyKustomizationText, makeApplyResourceText} from '@constants/makeApplyText';
@@ -34,7 +32,6 @@ import {setAlert} from '@redux/reducers/alert';
import {openResourceDiffModal} from '@redux/reducers/main';
import {openSaveResourcesToFileFolderModal, setMonacoEditor, setPaneConfiguration} from '@redux/reducers/ui';
import {
- isInPreviewModeSelector,
knownResourceKindsSelector,
kubeConfigContextSelector,
kubeConfigPathSelector,
@@ -48,19 +45,18 @@ import {applyHelmChart} from '@redux/thunks/applyHelmChart';
import {applyResource} from '@redux/thunks/applyResource';
import {selectFromHistory} from '@redux/thunks/selectionHistory';
-import FormEditor from '@molecules/FormEditor';
-import Monaco from '@molecules/Monaco';
-
-import {MonoPaneTitle, MonoPaneTitleCol} from '@atoms';
-import TabHeader from '@atoms/TabHeader';
+import {
+ FormEditor,
+ HelmChartModalConfirmWithNamespaceSelect,
+ ModalConfirmWithNamespaceSelect,
+ Monaco,
+ TitleBar,
+} from '@molecules';
-import Icon from '@components/atoms/Icon';
-import HelmChartModalConfirmWithNamespaceSelect from '@components/molecules/HelmChartModalConfirmWithNamespaceSelect';
-import ModalConfirmWithNamespaceSelect from '@components/molecules/ModalConfirmWithNamespaceSelect';
+import {Icon, TabHeader} from '@atoms';
import {openExternalResourceKindDocumentation} from '@utils/shell';
-import AppContext from '@src/AppContext';
import featureFlags from '@src/feature-flags.json';
import {getResourceKindHandler} from '@src/kindhandlers';
import {extractFormSchema} from '@src/kindhandlers/common/customObjectKindHandler';
@@ -71,12 +67,14 @@ import ActionsPaneFooter from './ActionsPaneFooter';
const {TabPane} = Tabs;
-const ActionsPane = (props: {contentHeight: string}) => {
- const {contentHeight} = props;
+interface IProps {
+ contentHeight: number;
+}
- const {windowSize} = useContext(AppContext);
- const windowHeight = windowSize.height;
+const ActionsPane: React.FC = props => {
+ const {contentHeight} = props;
+ const dispatch = useAppDispatch();
const applyingResource = useAppSelector(state => state.main.isApplyingResource);
const currentSelectionHistoryIndex = useAppSelector(state => state.main.currentSelectionHistoryIndex);
const fileMap = useAppSelector(state => state.main.fileMap);
@@ -84,7 +82,7 @@ const ActionsPane = (props: {contentHeight: string}) => {
const helmValuesMap = useAppSelector(state => state.main.helmValuesMap);
const isActionsPaneFooterExpanded = useAppSelector(state => state.ui.isActionsPaneFooterExpanded);
const isClusterDiffVisible = useAppSelector(state => state.ui.isClusterDiffVisible);
- const isInPreviewMode = useAppSelector(isInPreviewModeSelector);
+ const isFolderLoading = useAppSelector(state => state.ui.isFolderLoading);
const kubeConfigContext = useAppSelector(kubeConfigContextSelector);
const kubeConfigPath = useAppSelector(kubeConfigPathSelector);
const {kustomizeCommand} = useAppSelector(settingsSelector);
@@ -98,12 +96,6 @@ const ActionsPane = (props: {contentHeight: string}) => {
const selectedResourceId = useAppSelector(state => state.main.selectedResourceId);
const selectedValuesFileId = useAppSelector(state => state.main.selectedValuesFileId);
const selectionHistory = useAppSelector(state => state.main.selectionHistory);
- const uiState = useAppSelector(state => state.ui);
-
- const navigatorHeight = useMemo(
- () => windowHeight - NAVIGATOR_HEIGHT_OFFSET - (isInPreviewMode ? 25 : 0),
- [windowHeight, isInPreviewMode]
- );
const [activeTabKey, setActiveTabKey] = useState('source');
const [isApplyModalVisible, setIsApplyModalVisible] = useState(false);
@@ -112,14 +104,14 @@ const ActionsPane = (props: {contentHeight: string}) => {
const [selectedResource, setSelectedResource] = useState();
const [schemaForSelectedPath, setSchemaForSelectedPath] = useState();
- const dispatch = useAppDispatch();
-
// Could not get the ref of Tabs Component
const tabsList = document.getElementsByClassName('ant-tabs-nav-list');
const extraButton = useRef();
const [actionsPaneFooterRef, {height: actionsPaneFooterHeight, width: actionsPaneFooterWidth}] =
useMeasure();
+ const [actionsPaneRef, {width: actionsPaneWidth}] = useMeasure();
+ const [titleBarRef, {height: titleBarHeight}] = useMeasure();
const getDistanceBetweenTwoComponents = useCallback(() => {
const tabsListEl = tabsList[0].getBoundingClientRect();
@@ -127,11 +119,8 @@ const ActionsPane = (props: {contentHeight: string}) => {
const distance = extraButtonEl.left - tabsListEl.right;
- if (isButtonShrinked) {
- // 230px = approx width of not collapsed button
- if (distance > 350) {
- setButtonShrinkedState(false);
- }
+ if (isButtonShrinked && distance > 280) {
+ setButtonShrinkedState(false);
}
// The button has 10px margin-left
@@ -153,14 +142,13 @@ const ActionsPane = (props: {contentHeight: string}) => {
return ACTIONS_PANE_FOOTER_DEFAULT_HEIGHT;
}
- return -5;
+ return 0;
}, [actionsPaneFooterHeight, isActionsPaneFooterExpanded, paneConfiguration.actionsPaneFooterExpandedHeight]);
- const editorTabPaneHeight = useMemo(() => {
- let defaultHeight = parseInt(contentHeight, 10) - ACTIONS_PANE_TAB_PANE_OFFSET - resizableBoxHeight;
-
- return defaultHeight;
- }, [contentHeight, resizableBoxHeight]);
+ const tabsHeight = useMemo(
+ () => contentHeight - resizableBoxHeight - titleBarHeight,
+ [contentHeight, resizableBoxHeight, titleBarHeight]
+ );
const onSaveHandler = () => {
if (selectedResource) {
@@ -245,7 +233,7 @@ const ActionsPane = (props: {contentHeight: string}) => {
[dispatch]
);
- const onMouseUp = useCallback(() => {
+ const resizeActionsPaneFooter = useCallback(() => {
if (isActionsPaneFooterExpanded && actionsPaneFooterHeight !== paneConfiguration.actionsPaneFooterExpandedHeight) {
dispatch(setPaneConfiguration({...paneConfiguration, actionsPaneFooterExpandedHeight: actionsPaneFooterHeight}));
}
@@ -367,179 +355,156 @@ const ActionsPane = (props: {contentHeight: string}) => {
if (tabsList && tabsList.length && extraButton.current) {
getDistanceBetweenTwoComponents();
}
- }, [tabsList, uiState.paneConfiguration, windowSize, selectedResource, getDistanceBetweenTwoComponents]);
-
- useEffect(() => {
- document.addEventListener('mouseup', onMouseUp);
-
- return () => {
- document.removeEventListener('mouseup', onMouseUp);
- };
- // eslint-disable-next-line react-hooks/exhaustive-deps
- }, [actionsPaneFooterHeight, paneConfiguration.actionsPaneFooterExpandedHeight]);
+ }, [actionsPaneWidth, tabsList, paneConfiguration, selectedResource, getDistanceBetweenTwoComponents]);
useEffect(() => {
setSchemaForSelectedPath(selectedPath ? getSchemaForPath(selectedPath, fileMap) : undefined);
}, [selectedPath, fileMap]);
return (
- <>
-
-
-
-
- Editor
-
-
+
+
+ <>
+ }
+ />
+ }
+ />
+
+ {isSelectedResourceUnsaved() && (
+
+
+ Save
+
+
+ )}
+
+
+ }
+ >
+ Deploy
+
+
+
+
+ Diff
+
+
+ >
+
+
+
+
+ setActiveTabKey(k)}
+ tabBarExtraContent={
+ selectedResource && resourceKindHandler?.helpLink ? (
+
+ openExternalResourceKindDocumentation(resourceKindHandler?.helpLink)}
type="link"
- size="small"
- icon={}
- />
-
+ {isButtonShrinked ? '' : `See ${selectedResource?.kind} documentation`}
+
+
+ ) : isKustomization ? (
+
+ openExternalResourceKindDocumentation(KUSTOMIZE_HELP_URL)}
type="link"
- size="small"
- icon={}
- />
-
- {isSelectedResourceUnsaved() && (
-
-
- Save
-
-
- )}
-
-
- }
- >
- Deploy
-
-
-
-
- Diff
-
-
-
-
-
-
-
-
-
-
- setActiveTabKey(k)}
- tabBarExtraContent={
- selectedResource && resourceKindHandler?.helpLink ? (
-
- openExternalResourceKindDocumentation(resourceKindHandler?.helpLink)}
- type="link"
- ref={extraButton}
- >
- {isButtonShrinked ? '' : `See ${selectedResource?.kind} documentation`}
-
-
- ) : isKustomization ? (
-
- openExternalResourceKindDocumentation(KUSTOMIZE_HELP_URL)}
- type="link"
- ref={extraButton}
- >
- {isButtonShrinked ? '' : `See Kustomization documentation`}
-
-
- ) : null
- }
- >
+ {isButtonShrinked ? '' : `See Kustomization documentation`}
+
+
+ ) : null
+ }
+ >
+ }>Source}>
+ {isFolderLoading || previewLoader.isLoading ? (
+
+ ) : activeTabKey === 'source' ? (
+ !isClusterDiffVisible &&
+ (selectedResourceId || selectedPath || selectedValuesFileId) && (
+
+ )
+ ) : null}
+
+
+ {schemaForSelectedPath ||
+ (selectedResource && (isKustomization || resourceKindHandler?.formEditorOptions?.editorSchema)) ? (
}>Source}
+ key="form"
+ tab={
+ }>{selectedResource ? selectedResource.kind : 'Form'}
+ }
>
- {uiState.isFolderLoading || previewLoader.isLoading ? (
+ {isFolderLoading || previewLoader.isLoading ? (
- ) : activeTabKey === 'source' ? (
- !isClusterDiffVisible &&
- (selectedResourceId || selectedPath || selectedValuesFileId) && (
-
- )
+ ) : activeTabKey === 'form' ? (
+ selectedPath && schemaForSelectedPath ? (
+
+ ) : isKustomization && selectedResource ? (
+
+ ) : resourceKindHandler?.formEditorOptions ? (
+
+ ) : null
) : null}
- {schemaForSelectedPath ||
- (selectedResource && (isKustomization || resourceKindHandler?.formEditorOptions?.editorSchema)) ? (
- }>
- {selectedResource ? selectedResource.kind : 'Form'}
-
- }
- >
- {uiState.isFolderLoading || previewLoader.isLoading ? (
-
- ) : activeTabKey === 'form' ? (
- selectedPath && schemaForSelectedPath ? (
-
- ) : isKustomization && selectedResource ? (
-
- ) : resourceKindHandler?.formEditorOptions ? (
-
- ) : null
- ) : null}
-
- ) : null}
- {selectedResource && resourceKindHandler && !isKustomization && (
- }>Metadata}
- >
- {uiState.isFolderLoading || previewLoader.isLoading ? (
-
- ) : activeTabKey === 'metadataForm' ? (
-
- ) : null}
-
- )}
-
-
+ ) : null}
+
+ {selectedResource && resourceKindHandler && !isKustomization && (
+ }>Metadata}>
+ {isFolderLoading || previewLoader.isLoading ? (
+
+ ) : activeTabKey === 'metadataForm' ? (
+
+ ) : null}
+
+ )}
+
{featureFlags.ActionsPaneFooter && (
@@ -554,10 +519,11 @@ const ActionsPane = (props: {contentHeight: string}) => {
? ACTIONS_PANE_FOOTER_EXPANDED_DEFAULT_HEIGHT
: ACTIONS_PANE_FOOTER_DEFAULT_HEIGHT,
]}
- maxConstraints={[actionsPaneFooterWidth, navigatorHeight - 200]}
+ maxConstraints={[actionsPaneFooterWidth, contentHeight - 300]}
handle={(h: number, ref: LegacyRef) => (
)}
+ onResizeStop={resizeActionsPaneFooter}
>
{
)}
-
{isApplyModalVisible && (
{
onCancel={() => setIsApplyModalVisible(false)}
/>
)}
-
{isHelmChartApplyModalVisible && (
{
}
/>
)}
- >
+
);
};
diff --git a/src/components/organisms/ActionsPane/ActionsPaneFooter.styled.tsx b/src/components/organisms/ActionsPane/ActionsPaneFooter.styled.tsx
index c20a8b12a8..17fe0f176d 100644
--- a/src/components/organisms/ActionsPane/ActionsPaneFooter.styled.tsx
+++ b/src/components/organisms/ActionsPane/ActionsPaneFooter.styled.tsx
@@ -6,7 +6,6 @@ export const Container = styled.div`
width: 100%;
border-top: 1px solid ${Colors.grey3};
padding: 10px;
- display
`;
export const Pane = styled.div`
diff --git a/src/components/organisms/ActionsPane/ActionsPaneFooter.tsx b/src/components/organisms/ActionsPane/ActionsPaneFooter.tsx
index 404a8cc3f2..ede6516c00 100644
--- a/src/components/organisms/ActionsPane/ActionsPaneFooter.tsx
+++ b/src/components/organisms/ActionsPane/ActionsPaneFooter.tsx
@@ -35,6 +35,7 @@ const ActionsPaneFooter: React.FC = props => {
{Object.entries(tabs).map(([key, value]) => (
onClickTabLabel(key)}
>
diff --git a/src/components/organisms/FileTreePane/FileTreePane.tsx b/src/components/organisms/FileTreePane/FileTreePane.tsx
index 73631603f5..6612b4a44f 100644
--- a/src/components/organisms/FileTreePane/FileTreePane.tsx
+++ b/src/components/organisms/FileTreePane/FileTreePane.tsx
@@ -3,7 +3,7 @@ import {ipcRenderer} from 'electron';
import React, {useCallback, useContext, useEffect, useMemo, useRef, useState} from 'react';
import {useSelector} from 'react-redux';
-import {Button, Modal, Row, Tooltip} from 'antd';
+import {Button, Modal, Tooltip} from 'antd';
import {ExclamationCircleOutlined, ReloadOutlined} from '@ant-design/icons';
@@ -34,7 +34,7 @@ import {isKustomizationResource} from '@redux/services/kustomize';
import {startPreview, stopPreview} from '@redux/services/preview';
import {setRootFolder} from '@redux/thunks/setRootFolder';
-import {MonoPaneTitle, MonoPaneTitleCol} from '@atoms';
+import {MonoPaneTitle} from '@atoms';
import Icon from '@components/atoms/Icon';
@@ -43,7 +43,7 @@ import {uniqueArr} from '@utils/index';
import AppContext from '@src/AppContext';
import * as S from './Styled';
-import {TreeItem} from './TreeItem';
+import TreeItem from './TreeItem';
import {ProcessingEntity, TreeNode} from './types';
const createNode = (
@@ -202,7 +202,7 @@ const FileTreePane = () => {
let node: TreeNode | undefined = tree || undefined;
for (let c = 0; c < keys.length && node; c += 1) {
- node = node.children.find(i => i.key === keys[c]);
+ node = node.children.find((i: any) => i.key === keys[c]);
}
if (node) {
@@ -446,44 +446,43 @@ const FileTreePane = () => {
return (
-
-
-
-
-
- File Explorer{' '}
- {isScanExcludesUpdated === 'outdated' ? (
-
-
-
- ) : (
- ''
- )}
-
-
-
- }
- type="link"
- disabled={isButtonDisabled}
- />
+
+
+
+
+ File Explorer{' '}
+ {isScanExcludesUpdated === 'outdated' && (
+
+
-
- }
- onClick={onToggleTree}
- type="link"
- size="small"
- disabled={isButtonDisabled}
- />
-
-
-
-
-
-
+ )}
+
+
+
+
+ }
+ type="link"
+ disabled={isButtonDisabled}
+ />
+
+
+
+ }
+ onClick={onToggleTree}
+ type="link"
+ size="small"
+ disabled={isButtonDisabled}
+ />
+
+
+
+
+
+
{uiState.isFolderLoading ? (
) : tree ? (
diff --git a/src/components/organisms/FileTreePane/Styled.tsx b/src/components/organisms/FileTreePane/Styled.tsx
index c90b3b20e8..d9bce370f3 100644
--- a/src/components/organisms/FileTreePane/Styled.tsx
+++ b/src/components/organisms/FileTreePane/Styled.tsx
@@ -2,6 +2,7 @@ import {Button, Skeleton as RawSkeleton, Tree} from 'antd';
import styled from 'styled-components';
+import {AppBorders} from '@styles/Borders';
import Colors, {BackgroundColors, FontColors} from '@styles/Colors';
export const FileTreeContainer = styled.div`
@@ -107,46 +108,29 @@ export const FileTreeContainer = styled.div`
}
`;
+export const ContextMenuDivider = styled.div`
+ border-bottom: 1px solid rgba(255, 255, 255, 0.25);
+`;
+
export const NoFilesContainer = styled.div`
margin-left: 16px;
margin-top: 10px;
`;
-export const TreeContainer = styled.div`
- margin-left: 2px;
- margin-top: 10px;
+export const NodeContainer = styled.div`
+ position: relative;
`;
-export const RootFolderText = styled.div`
- font-size: 12px;
- line-height: 22px;
- color: ${Colors.grey7};
- margin-left: 14px;
+export const NodeTitleContainer = styled.div`
+ padding-right: 14px;
+ white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
- white-space: nowrap;
-`;
-
-export const TreeDirectoryTree = styled(Tree.DirectoryTree)`
- margin-top: 10px;
- .ant-tree-switcher svg {
- color: ${props => (props.disabled ? `${Colors.grey800}` : 'inherit')} !important;
- }
-
- opacity: ${props => (props.disabled ? '70%' : '100%')};
`;
-export const TitleBarContainer = styled.div`
- display: flex;
- height: 24px;
- justify-content: space-between;
-`;
-
-export const Title = styled.span`
- text-overflow: ellipsis;
- white-space: nowrap;
- overflow: hidden;
- padding-right: 10px;
+export const NumberOfResources = styled.span`
+ margin-left: 12px;
+ color: ${Colors.grey7};
`;
export const RightButtons = styled.div`
@@ -163,23 +147,14 @@ export const RightButtons = styled.div`
}
`;
-export const TreeTitleWrapper = styled.div`
- display: flex;
- justify-content: space-between;
- align-items: center;
-
- height: 100%;
-
- & .ant-dropdown-trigger {
- height: inherit;
- margin-right: 10px;
- }
-`;
-
-export const TreeTitleText = styled.span`
- flex: 1;
+export const RootFolderText = styled.div`
+ font-size: 12px;
+ line-height: 22px;
+ color: ${Colors.grey7};
+ margin-left: 14px;
overflow: hidden;
- position: relative;
+ text-overflow: ellipsis;
+ white-space: nowrap;
`;
export const Skeleton = styled(RawSkeleton)`
@@ -187,8 +162,6 @@ export const Skeleton = styled(RawSkeleton)`
width: 90%;
`;
-export const ReloadButton = styled(Button)``;
-
export const SpinnerWrapper = styled.div`
position: absolute;
left: 0;
@@ -208,24 +181,53 @@ export const SpinnerWrapper = styled.div`
}
`;
-export const ContextMenuDivider = styled.div`
- border-bottom: 1px solid rgba(255, 255, 255, 0.25);
+export const Title = styled.span`
+ text-overflow: ellipsis;
+ white-space: nowrap;
+ overflow: hidden;
+ padding-right: 10px;
`;
-export const NumberOfResources = styled.span`
- margin-left: 12px;
- color: ${Colors.grey7};
+export const TitleBarContainer = styled.div`
+ border-bottom: ${AppBorders.sectionDivider};
`;
-export const NodeContainer = styled.div`
- position: relative;
+export const TitleContainer = styled.div`
+ display: flex;
+ justify-content: space-between;
`;
-export const NodeTitleContainer = styled.div`
- padding-right: 14px;
- white-space: nowrap;
+export const TreeContainer = styled.div`
+ margin-left: 2px;
+ margin-top: 10px;
+`;
+
+export const TreeDirectoryTree = styled(Tree.DirectoryTree)`
+ margin-top: 10px;
+ .ant-tree-switcher svg {
+ color: ${props => (props.disabled ? `${Colors.grey800}` : 'inherit')} !important;
+ }
+
+ opacity: ${props => (props.disabled ? '70%' : '100%')};
+`;
+
+export const TreeTitleText = styled.span`
+ flex: 1;
overflow: hidden;
- text-overflow: ellipsis;
+ position: relative;
+`;
+
+export const TreeTitleWrapper = styled.div`
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+
+ height: 100%;
+
+ & .ant-dropdown-trigger {
+ height: inherit;
+ margin-right: 10px;
+ }
`;
export const PreviewButton = styled(Button)<{$isItemSelected: boolean}>`
diff --git a/src/components/organisms/FileTreePane/TreeItem.tsx b/src/components/organisms/FileTreePane/TreeItem.tsx
index 7018083801..5ea14c5ee4 100644
--- a/src/components/organisms/FileTreePane/TreeItem.tsx
+++ b/src/components/organisms/FileTreePane/TreeItem.tsx
@@ -41,7 +41,7 @@ function deleteEntityWizard(entityInfo: {entityAbsolutePath: string}, onOk: () =
});
}
-export const TreeItem: React.FC = props => {
+const TreeItem: React.FC = props => {
const {isExcluded, isFolder, isSupported, processingEntity, title, treeKey} = props;
const {
setProcessingEntity,
@@ -282,3 +282,5 @@ export const TreeItem: React.FC = props => {
);
};
+
+export default TreeItem;
diff --git a/src/components/organisms/HelmPane/HelmPane.tsx b/src/components/organisms/HelmPane/HelmPane.tsx
index 5c0d19242b..17abc9f587 100644
--- a/src/components/organisms/HelmPane/HelmPane.tsx
+++ b/src/components/organisms/HelmPane/HelmPane.tsx
@@ -1,29 +1,20 @@
-import React, {useContext} from 'react';
+import React from 'react';
-import {NAVIGATOR_HEIGHT_OFFSET} from '@constants/constants';
+import {SectionRenderer, TitleBar} from '@molecules';
-import {MonoPaneTitle} from '@components/atoms';
-import {SectionRenderer} from '@components/molecules';
-
-import AppContext from '@src/AppContext';
import RootHelmChartsSectionBlueprint from '@src/navsections/HelmChartSectionBlueprint';
import * as S from './styled';
const HelmPane: React.FC = () => {
- const {windowSize} = useContext(AppContext);
- const windowHeight = windowSize.height;
- const navigatorHeight = windowHeight - NAVIGATOR_HEIGHT_OFFSET;
-
return (
-
-
- Helm
-
-
+
+
+
+
-
+
);
};
diff --git a/src/components/organisms/HelmPane/styled.tsx b/src/components/organisms/HelmPane/styled.tsx
index 84e6101e7e..a1c9992b52 100644
--- a/src/components/organisms/HelmPane/styled.tsx
+++ b/src/components/organisms/HelmPane/styled.tsx
@@ -2,34 +2,18 @@ import styled from 'styled-components';
import {GlobalScrollbarStyle} from '@utils/scrollbar';
-import {AppBorders} from '@styles/Borders';
-import {BackgroundColors} from '@styles/Colors';
-
-export const TitleBar = styled.div`
- display: flex;
- height: 24px;
- justify-content: space-between;
- border-bottom: ${AppBorders.sectionDivider};
- width: 100%;
- height: 40px;
- margin: 0;
- padding: 0;
- background: ${BackgroundColors.darkThemeBackground};
- overflow: hidden;
-`;
-
-export const TitleBarRightButtons = styled.div`
- float: right;
- display: flex;
- align-items: center;
- padding-right: 16px;
+export const HelmPaneContainer = styled.div`
+ height: 100%;
+ display: grid;
+ grid-template-rows: max-content 1fr;
`;
-export const List = styled.ol<{height: number}>`
+export const List = styled.ol`
+ height: 100%;
list-style-type: none;
padding: 0;
- overflow-y: auto;
- ${GlobalScrollbarStyle}
- ${props => `height: ${props.height}px;`}
padding-bottom: 20px;
+ overflow-y: auto;
+ margin: 0;
+ ${GlobalScrollbarStyle};
`;
diff --git a/src/components/organisms/KustomizePane/KustomizePane.tsx b/src/components/organisms/KustomizePane/KustomizePane.tsx
index 92b4f715f5..fcd6a1b43b 100644
--- a/src/components/organisms/KustomizePane/KustomizePane.tsx
+++ b/src/components/organisms/KustomizePane/KustomizePane.tsx
@@ -1,31 +1,22 @@
-import React, {useContext} from 'react';
+import React from 'react';
-import {NAVIGATOR_HEIGHT_OFFSET} from '@constants/constants';
+import {SectionRenderer, TitleBar} from '@molecules';
-import {MonoPaneTitle} from '@components/atoms';
-import {SectionRenderer} from '@components/molecules';
-
-import AppContext from '@src/AppContext';
import KustomizationSectionBlueprint from '@src/navsections/KustomizationSectionBlueprint';
import KustomizePatchSectionBlueprint from '@src/navsections/KustomizePatchSectionBlueprint';
import * as S from './styled';
const KustomizePane: React.FC = () => {
- const {windowSize} = useContext(AppContext);
- const windowHeight = windowSize.height;
- const navigatorHeight = windowHeight - NAVIGATOR_HEIGHT_OFFSET;
-
return (
-
-
- Kustomize
-
-
+
+
+
+
-
+
);
};
diff --git a/src/components/organisms/KustomizePane/styled.tsx b/src/components/organisms/KustomizePane/styled.tsx
index 84e6101e7e..e5c599ec70 100644
--- a/src/components/organisms/KustomizePane/styled.tsx
+++ b/src/components/organisms/KustomizePane/styled.tsx
@@ -2,34 +2,19 @@ import styled from 'styled-components';
import {GlobalScrollbarStyle} from '@utils/scrollbar';
-import {AppBorders} from '@styles/Borders';
-import {BackgroundColors} from '@styles/Colors';
-
-export const TitleBar = styled.div`
- display: flex;
- height: 24px;
- justify-content: space-between;
- border-bottom: ${AppBorders.sectionDivider};
- width: 100%;
- height: 40px;
- margin: 0;
- padding: 0;
- background: ${BackgroundColors.darkThemeBackground};
- overflow: hidden;
-`;
-
-export const TitleBarRightButtons = styled.div`
- float: right;
- display: flex;
- align-items: center;
- padding-right: 16px;
+export const KustomizePaneContainer = styled.div`
+ height: 100%;
+ display: grid;
+ grid-template-rows: max-content 1fr;
+ grid-row-gap: 3px;
+ padding-bottom: 3px;
`;
-export const List = styled.ol<{height: number}>`
+export const List = styled.ol`
+ height: 100%;
list-style-type: none;
- padding: 0;
+ padding: 0px;
overflow-y: auto;
+
${GlobalScrollbarStyle}
- ${props => `height: ${props.height}px;`}
- padding-bottom: 20px;
`;
diff --git a/src/components/organisms/NavigatorPane/ClusterCompareButton.tsx b/src/components/organisms/NavigatorPane/ClusterCompareButton.tsx
index 0ab531f30d..4cbe65e231 100644
--- a/src/components/organisms/NavigatorPane/ClusterCompareButton.tsx
+++ b/src/components/organisms/NavigatorPane/ClusterCompareButton.tsx
@@ -15,27 +15,23 @@ import {useAppDispatch, useAppSelector} from '@redux/hooks';
import {openClusterDiff} from '@redux/reducers/ui';
import {isInClusterModeSelector} from '@redux/selectors';
-import {useWindowSize} from '@utils/hooks';
+interface IProps {
+ navigatorPaneWidth: number;
+}
+
+const ClusterCompareButton: React.FC = props => {
+ const {navigatorPaneWidth} = props;
-function ClusterCompareButton() {
const dispatch = useAppDispatch();
const fileMap = useAppSelector(state => state.main.fileMap);
- const navWidth = useAppSelector(state => state.ui.paneConfiguration.navWidth);
const isInClusterMode = useAppSelector(isInClusterModeSelector);
- const windowSize = useWindowSize();
- const isFolderOpen = useMemo(() => {
- return Boolean(fileMap[ROOT_FILE_ENTRY]);
- }, [fileMap]);
+ const isFolderOpen = useMemo(() => Boolean(fileMap[ROOT_FILE_ENTRY]), [fileMap]);
const onClickClusterComparison = () => {
dispatch(openClusterDiff());
};
- const shouldHideClusterCompareText = useMemo(() => {
- return windowSize.width * Number(navWidth.toFixed(2)) < 350;
- }, [navWidth, windowSize.width]);
-
return (
- {shouldHideClusterCompareText ? '' : 'Cluster Compare'}
+ {navigatorPaneWidth < 400 ? '' : 'Cluster Compare'}
);
-}
+};
export default ClusterCompareButton;
diff --git a/src/components/organisms/NavigatorPane/NavigatorPane.styled.tsx b/src/components/organisms/NavigatorPane/NavigatorPane.styled.tsx
index 912a597544..72c53203ae 100644
--- a/src/components/organisms/NavigatorPane/NavigatorPane.styled.tsx
+++ b/src/components/organisms/NavigatorPane/NavigatorPane.styled.tsx
@@ -7,37 +7,51 @@ import {GlobalScrollbarStyle} from '@utils/scrollbar';
import {AppBorders} from '@styles/Borders';
import Colors, {BackgroundColors} from '@styles/Colors';
-export const List = styled.ol<{height: number}>`
- ${props => `height: ${props.height}px;`}
+export const FiltersContainer = styled.div`
+ position: relative;
+ padding: 6px 0 3px 0;
+ margin-bottom: 5px;
+
+ & .react-resizable {
+ padding: 8px 16px;
+ overflow-y: auto;
+
+ ${GlobalScrollbarStyle}
+ }
+
+ & .custom-handle {
+ position: absolute;
+ left: 0;
+ right: 0;
+ bottom: -4px;
+ height: 3px;
+ border-bottom: 1px solid rgba(255, 255, 255, 0.12);
+ cursor: row-resize;
+ }
+`;
+
+export const FiltersNumber = styled.div`
+ margin-left: 5px;
+`;
+
+export const List = styled.ol`
+ height: 100%;
list-style-type: none;
padding: 0;
+ margin: 0;
overflow-y: auto;
- padding-bottom: 20px;
- ${GlobalScrollbarStyle}
-`;
-export const TitleBar = styled.div`
- display: flex;
- justify-content: space-between;
- height: 24px;
- border-bottom: ${AppBorders.sectionDivider};
- width: 100%;
- height: 40px;
- margin: 0;
- padding: 0;
- background: ${BackgroundColors.darkThemeBackground};
- overflow: hidden;
+ ${GlobalScrollbarStyle}
`;
-export const TitleBarRightButtons = styled.div`
- float: right;
- display: flex;
- align-items: center;
- padding-right: 16px;
-`;
+export const NavigatorPaneContainer = styled.div<{$gridTemplateRows: string}>`
+ height: 100%;
+ display: grid;
-export const FiltersNumber = styled.div`
- margin-left: 5px;
+ ${({$gridTemplateRows}) => `
+ grid-template-rows: ${$gridTemplateRows};
+ `};
+ grid-row-gap: 5px;
`;
export const PlusButton = styled(Button)<{$highlighted: boolean; $disabled: boolean}>`
@@ -54,3 +68,21 @@ export const PlusButton = styled(Button)<{$highlighted: boolean; $disabled: bool
`};
}
`;
+
+export const TitleBar = styled.div`
+ display: flex;
+ justify-content: space-between;
+ border-bottom: ${AppBorders.sectionDivider};
+ width: 100%;
+ margin: 0;
+ padding: 0;
+ background: ${BackgroundColors.darkThemeBackground};
+ overflow: hidden;
+`;
+
+export const TitleBarRightButtons = styled.div`
+ float: right;
+ display: flex;
+ align-items: center;
+ padding-right: 16px;
+`;
diff --git a/src/components/organisms/NavigatorPane/NavigatorPane.tsx b/src/components/organisms/NavigatorPane/NavigatorPane.tsx
index 39337d53f6..6cc049400e 100644
--- a/src/components/organisms/NavigatorPane/NavigatorPane.tsx
+++ b/src/components/organisms/NavigatorPane/NavigatorPane.tsx
@@ -1,5 +1,4 @@
-import {LegacyRef, useContext, useMemo} from 'react';
-import {useSelector} from 'react-redux';
+import {LegacyRef, useMemo} from 'react';
import {ResizableBox} from 'react-resizable';
import {useMeasure} from 'react-use';
@@ -7,9 +6,7 @@ import {Badge, Button, Tooltip} from 'antd';
import {FilterOutlined, PlusOutlined} from '@ant-design/icons';
-import styled from 'styled-components';
-
-import {NAVIGATOR_HEIGHT_OFFSET, ROOT_FILE_ENTRY} from '@constants/constants';
+import {ROOT_FILE_ENTRY} from '@constants/constants';
import {NewResourceTooltip, QuickFilterTooltip} from '@constants/tooltips';
import {ResourceFilterType} from '@models/appstate';
@@ -22,11 +19,10 @@ import {MonoPaneTitle} from '@components/atoms';
import {ResourceFilter, SectionRenderer} from '@components/molecules';
import CheckedResourcesActionsMenu from '@components/molecules/CheckedResourcesActionsMenu';
-import {GlobalScrollbarStyle} from '@utils/scrollbar';
+import {useMainPaneHeight} from '@utils/hooks';
import Colors from '@styles/Colors';
-import AppContext from '@src/AppContext';
import K8sResourceSectionBlueprint from '@src/navsections/K8sResourceSectionBlueprint';
import UnknownResourceSectionBlueprint from '@src/navsections/UnknownResourceSectionBlueprint';
@@ -34,71 +30,44 @@ import ClusterCompareButton from './ClusterCompareButton';
import * as S from './NavigatorPane.styled';
import WarningsAndErrorsDisplay from './WarningsAndErrorsDisplay';
-const FiltersContainer = styled.div`
- position: relative;
- padding: 6px 0 3px 0;
- margin-bottom: 10px;
-
- & .react-resizable {
- padding: 8px 16px;
- overflow-y: auto;
-
- ${GlobalScrollbarStyle}
- }
-
- & .custom-handle {
- position: absolute;
- left: 0;
- right: 0;
- bottom: -4px;
- height: 3px;
- border-bottom: 1px solid rgba(255, 255, 255, 0.12);
- cursor: row-resize;
- }
-`;
-
const NavPane: React.FC = () => {
const dispatch = useAppDispatch();
- const {windowSize} = useContext(AppContext);
-
- const [filtersContainerRef, {height, width}] = useMeasure();
-
- const resourceFilters: ResourceFilterType = useAppSelector(state => state.main.resourceFilter);
-
const activeResources = useAppSelector(activeResourcesSelector);
const checkedResourceIds = useAppSelector(state => state.main.checkedResourceIds);
const fileMap = useAppSelector(state => state.main.fileMap);
+ const highlightedItems = useAppSelector(state => state.ui.highlightedItems);
+ const isInClusterMode = useAppSelector(isInClusterModeSelector);
+ const isInPreviewMode = useAppSelector(isInPreviewModeSelector);
const isPreviewLoading = useAppSelector(state => state.main.previewLoader.isLoading);
const isResourceFiltersOpen = useAppSelector(state => state.ui.isResourceFiltersOpen);
+ const resourceFilters: ResourceFilterType = useAppSelector(state => state.main.resourceFilter);
- const isInClusterMode = useSelector(isInClusterModeSelector);
- const isInPreviewMode = useSelector(isInPreviewModeSelector);
- const highlightedItems = useAppSelector(state => state.ui.highlightedItems);
+ const paneHeight = useMainPaneHeight();
- const navigatorHeight = useMemo(
- () => windowSize.height - NAVIGATOR_HEIGHT_OFFSET - (isInPreviewMode ? 25 : 0),
- [windowSize.height, isInPreviewMode]
+ const [filtersContainerRef, {height, width}] = useMeasure();
+ const [navigatorPaneRef, {width: navigatorPaneWidth}] = useMeasure();
+
+ const appliedFilters = useMemo(
+ () =>
+ Object.entries(resourceFilters)
+ .map(([key, value]) => {
+ return {filterName: key, filterValue: value};
+ })
+ .filter(filter => filter.filterValue && Object.values(filter.filterValue).length),
+ [resourceFilters]
);
- const appliedFilters = useMemo(() => {
- return Object.entries(resourceFilters)
- .map(([key, value]) => {
- return {filterName: key, filterValue: value};
- })
- .filter(filter => filter.filterValue && Object.values(filter.filterValue).length);
- }, [resourceFilters]);
-
- const isFolderOpen = useMemo(() => {
- return Boolean(fileMap[ROOT_FILE_ENTRY]);
- }, [fileMap]);
-
- const sectionListHeight = useMemo(() => {
- if (isResourceFiltersOpen && height) {
- return navigatorHeight - (height + 24);
+ const containerGridTemplateRows = useMemo(() => {
+ let gridTemplateRows = 'max-content 1fr';
+
+ if (isResourceFiltersOpen) {
+ gridTemplateRows = 'repeat(2, max-content) 1fr';
}
- return navigatorHeight;
- }, [height, isResourceFiltersOpen, navigatorHeight]);
+ return gridTemplateRows;
+ }, [isResourceFiltersOpen]);
+
+ const isFolderOpen = useMemo(() => Boolean(fileMap[ROOT_FILE_ENTRY]), [fileMap]);
const onClickNewResource = () => {
dispatch(openNewResourceWizard());
@@ -109,7 +78,7 @@ const NavPane: React.FC = () => {
};
return (
- <>
+
{checkedResourceIds.length && !isPreviewLoading ? (
) : (
@@ -133,6 +102,7 @@ const NavPane: React.FC = () => {
onClick={onClickNewResource}
/>
+
-
+
+
)}
{isResourceFiltersOpen && (
-
+
) => }
>
-
+
)}
-
+
- >
+
);
};
diff --git a/src/components/organisms/PageFooter/PageFooter.tsx b/src/components/organisms/PageFooter/PageFooter.tsx
index c95793cd3c..499cf5ce36 100644
--- a/src/components/organisms/PageFooter/PageFooter.tsx
+++ b/src/components/organisms/PageFooter/PageFooter.tsx
@@ -1,22 +1,23 @@
import {ipcRenderer} from 'electron';
-import React, {useEffect, useState} from 'react';
+import {useEffect, useState} from 'react';
+import {useMeasure} from 'react-use';
import styled from 'styled-components';
import {ROOT_FILE_ENTRY} from '@constants/constants';
-import {useAppSelector} from '@redux/hooks';
-
-import Footer from '@components/atoms/Footer';
+import {useAppDispatch, useAppSelector} from '@redux/hooks';
+import {setLayoutSize} from '@redux/reducers/ui';
import {AppBorders} from '@styles/Borders';
import Colors, {BackgroundColors} from '@styles/Colors';
-const StyledFooter = styled(Footer)`
+const StyledFooter = styled.footer`
width: 100%;
- height: 1.5em;
+ padding: 0px;
padding-left: 10px;
+ margin: 0px;
background: ${BackgroundColors.darkThemeBackground};
border-top: ${AppBorders.pageDivider};
color: ${Colors.grey7};
@@ -26,9 +27,22 @@ const StyledFooter = styled(Footer)`
const PageFooter = () => {
const [appVersion, setAppVersion] = useState('');
const [footerText, setFooterText] = useState('');
+ const dispatch = useAppDispatch();
const fileMap = useAppSelector(state => state.main.fileMap);
const rootEntry = fileMap[ROOT_FILE_ENTRY];
+ const layoutSize = useAppSelector(state => state.ui.layoutSize);
+
+ const [footerRef, {height: footerHeight}] = useMeasure();
+
+ useEffect(() => {
+ if (!layoutSize.footer && footerHeight) {
+ dispatch(setLayoutSize({...layoutSize, footer: footerHeight}));
+ }
+
+ // eslint-disable-next-line react-hooks/exhaustive-deps
+ }, [footerHeight]);
+
// not counting the root
const nrOfFiles = Object.values(fileMap).filter(f => !f.children).length;
@@ -45,7 +59,7 @@ const PageFooter = () => {
);
}, [appVersion, nrOfFiles, rootEntry]);
- return {footerText};
+ return {footerText};
};
export default PageFooter;
diff --git a/src/components/organisms/PageHeader/PageHeader.tsx b/src/components/organisms/PageHeader/PageHeader.tsx
index 671c63a593..b02ed21d13 100644
--- a/src/components/organisms/PageHeader/PageHeader.tsx
+++ b/src/components/organisms/PageHeader/PageHeader.tsx
@@ -1,4 +1,5 @@
import {useEffect, useState} from 'react';
+import {useMeasure} from 'react-use';
import {Badge, Tooltip} from 'antd';
@@ -10,7 +11,7 @@ import {K8sResource} from '@models/k8sresource';
import {useAppDispatch, useAppSelector} from '@redux/hooks';
import {openPluginsDrawer} from '@redux/reducers/extension';
-import {toggleNotifications, toggleSettings, toggleStartProjectPane} from '@redux/reducers/ui';
+import {setLayoutSize, toggleNotifications, toggleSettings, toggleStartProjectPane} from '@redux/reducers/ui';
import {activeResourcesSelector, isInPreviewModeSelector, kubeConfigContextSelector} from '@redux/selectors';
import {stopPreview} from '@redux/services/preview';
@@ -40,6 +41,7 @@ const PageHeader = () => {
const helmValuesMap = useAppSelector(state => state.main.helmValuesMap);
const isInPreviewMode = useAppSelector(isInPreviewModeSelector);
const isStartProjectPaneVisible = useAppSelector(state => state.ui.isStartProjectPaneVisible);
+ const layoutSize = useAppSelector(state => state.ui.layoutSize);
const previewResourceId = useAppSelector(state => state.main.previewResourceId);
const previewType = useAppSelector(state => state.main.previewType);
const previewValuesFileId = useAppSelector(state => state.main.previewValuesFileId);
@@ -50,22 +52,7 @@ const PageHeader = () => {
const [previewResource, setPreviewResource] = useState();
const [previewValuesFile, setPreviewValuesFile] = useState();
- useEffect(() => {
- if (previewResourceId) {
- setPreviewResource(resourceMap[previewResourceId]);
- } else {
- setPreviewResource(undefined);
- }
-
- if (previewValuesFileId && helmValuesMap[previewValuesFileId]) {
- const valuesFile = helmValuesMap[previewValuesFileId];
- setPreviewValuesFile(valuesFile);
- setHelmChart(helmChartMap[valuesFile.helmChartId]);
- } else {
- setPreviewValuesFile(undefined);
- setHelmChart(undefined);
- }
- }, [previewResourceId, previewValuesFileId, helmValuesMap, resourceMap, helmChartMap]);
+ const [pageHeaderRef, {height: pageHeaderHeight}] = useMeasure();
const toggleSettingsDrawer = () => {
dispatch(toggleSettings());
@@ -89,8 +76,33 @@ const PageHeader = () => {
stopPreview(dispatch);
};
+ useEffect(() => {
+ if (previewResourceId) {
+ setPreviewResource(resourceMap[previewResourceId]);
+ } else {
+ setPreviewResource(undefined);
+ }
+
+ if (previewValuesFileId && helmValuesMap[previewValuesFileId]) {
+ const valuesFile = helmValuesMap[previewValuesFileId];
+ setPreviewValuesFile(valuesFile);
+ setHelmChart(helmChartMap[valuesFile.helmChartId]);
+ } else {
+ setPreviewValuesFile(undefined);
+ setHelmChart(undefined);
+ }
+ }, [previewResourceId, previewValuesFileId, helmValuesMap, resourceMap, helmChartMap]);
+
+ useEffect(() => {
+ if (pageHeaderHeight) {
+ dispatch(setLayoutSize({...layoutSize, header: pageHeaderHeight}));
+ }
+
+ // eslint-disable-next-line react-hooks/exhaustive-deps
+ }, [pageHeaderHeight]);
+
return (
- <>
+
{isInPreviewMode && previewType === 'kustomization' && (
PREVIEW MODE
@@ -155,7 +167,7 @@ const PageHeader = () => {
- >
+
);
};
diff --git a/src/components/organisms/PageHeader/ProjectSelection.tsx b/src/components/organisms/PageHeader/ProjectSelection.tsx
index c5a67f5a64..26e46683f2 100644
--- a/src/components/organisms/PageHeader/ProjectSelection.tsx
+++ b/src/components/organisms/PageHeader/ProjectSelection.tsx
@@ -132,7 +132,7 @@ const ProjectSelection = () => {
-
+
diff --git a/src/components/organisms/PageHeader/styled.tsx b/src/components/organisms/PageHeader/styled.tsx
index 7435fd9f11..25be7693cd 100644
--- a/src/components/organisms/PageHeader/styled.tsx
+++ b/src/components/organisms/PageHeader/styled.tsx
@@ -94,6 +94,8 @@ export const PreviewRow = styled(Row)`
justify-content: space-between;
`;
+export const PageHeaderContainer = styled.div``;
+
export const ProjectClusterSelectionContainer = styled.div`
display: flex;
justify-content: center;
diff --git a/src/components/organisms/PaneManager/MenuButton.tsx b/src/components/organisms/PaneManager/MenuButton.tsx
index 2b35c01dfe..43a6af0e38 100644
--- a/src/components/organisms/PaneManager/MenuButton.tsx
+++ b/src/components/organisms/PaneManager/MenuButton.tsx
@@ -55,6 +55,10 @@ const MenuButton: React.FC = props => {
shallowEqual
);
+ const isAnyHelmValuesFileSelected = useMemo(() => {
+ return Object.values(helmValuesMap).some(v => v.isSelected);
+ }, [helmValuesMap]);
+
const isAnySectionSelected = useMemo(() => {
if (!sectionInstanceByName) {
return false;
@@ -65,8 +69,11 @@ const MenuButton: React.FC = props => {
const style: React.CSSProperties = {};
const hasGradientBackground = useMemo(() => {
- return Boolean((isAnySectionSelected || (shouldWatchSelectedPath && selectedPath)) && (!isSelected || !isActive));
- }, [isAnySectionSelected, shouldWatchSelectedPath, selectedPath, isSelected, isActive]);
+ return Boolean(
+ (isAnySectionSelected || (shouldWatchSelectedPath && selectedPath && !isAnyHelmValuesFileSelected)) &&
+ (!isSelected || !isActive)
+ );
+ }, [isAnySectionSelected, shouldWatchSelectedPath, selectedPath, isAnyHelmValuesFileSelected, isSelected, isActive]);
if (hasGradientBackground) {
if (isHovered) {
diff --git a/src/components/organisms/PaneManager/PaneManager.tsx b/src/components/organisms/PaneManager/PaneManager.tsx
index 00235a40f1..72c892e44a 100644
--- a/src/components/organisms/PaneManager/PaneManager.tsx
+++ b/src/components/organisms/PaneManager/PaneManager.tsx
@@ -1,319 +1,52 @@
-import {useContext, useEffect, useMemo, useState} from 'react';
+import {useMemo} from 'react';
-import {Badge, Button, Tooltip} from 'antd';
-import 'antd/dist/antd.less';
+import {useAppSelector} from '@redux/hooks';
+import {activeProjectSelector} from '@redux/selectors';
-import {
- ApartmentOutlined,
- CodeOutlined,
- FolderOpenOutlined,
- FolderOutlined,
- FormatPainterOutlined,
-} from '@ant-design/icons';
-
-import {ROOT_FILE_ENTRY, TOOLTIP_DELAY} from '@constants/constants';
-
-import {Project} from '@models/appconfig';
-import {LeftMenuSelectionType} from '@models/ui';
-
-import {useAppDispatch, useAppSelector} from '@redux/hooks';
-import {
- setLeftMenuSelection,
- setRightMenuSelection,
- toggleLeftMenu,
- toggleRightMenu,
- toggleStartProjectPane,
-} from '@redux/reducers/ui';
-import {activeProjectSelector, isInPreviewModeSelector, kustomizationsSelector} from '@redux/selectors';
-
-import {ActionsPane, FileTreePane, HelmPane, KustomizePane, NavigatorPane, TemplateManagerPane} from '@organisms';
-
-import {GraphView} from '@molecules';
-
-import {SplitView} from '@atoms';
-
-import Colors from '@styles/Colors';
-
-import AppContext from '@src/AppContext';
import featureJson from '@src/feature-flags.json';
-import {HELM_CHART_SECTION_NAME} from '@src/navsections/HelmChartSectionBlueprint';
-import {KUSTOMIZATION_SECTION_NAME} from '@src/navsections/KustomizationSectionBlueprint';
-import {KUSTOMIZE_PATCH_SECTION_NAME} from '@src/navsections/KustomizePatchSectionBlueprint';
import RecentProjectsPane from '../RecentProjectsPane';
import StartProjectPane from '../StartProjectPane';
-import MenuButton from './MenuButton';
-import MenuIcon from './MenuIcon';
+import PaneManagerLeftMenu from './PaneManagerLeftMenu';
+import PaneManagerRightMenu from './PaneManagerRightMenu';
+import PaneManagerSplitView from './PaneMangerSplitView';
import * as S from './styled';
-const iconMenuWidth = 45;
-
-const PaneManager = () => {
- const dispatch = useAppDispatch();
+const PaneManager: React.FC = () => {
const activeProject = useAppSelector(activeProjectSelector);
- const projects: Project[] = useAppSelector(state => state.config.projects);
- const fileMap = useAppSelector(state => state.main.fileMap);
- const highlightedItems = useAppSelector(state => state.ui.highlightedItems);
- const isInPreviewMode = useAppSelector(isInPreviewModeSelector);
const isProjectLoading = useAppSelector(state => state.config.isProjectLoading);
const isStartProjectPaneVisible = useAppSelector(state => state.ui.isStartProjectPaneVisible);
- const leftActive = useAppSelector(state => state.ui.leftMenu.isActive);
- const leftMenuSelection = useAppSelector(state => state.ui.leftMenu.selection);
- const rightActive = useAppSelector(state => state.ui.rightMenu.isActive);
- const rightMenuSelection = useAppSelector(state => state.ui.rightMenu.selection);
-
- const {windowSize} = useContext(AppContext);
+ const projects = useAppSelector(state => state.config.projects);
- const contentWidth = windowSize.width - (featureJson.ShowRightMenu ? 2 : 1) * iconMenuWidth;
- const kustomizations = useAppSelector(kustomizationsSelector);
- const helmCharts = useAppSelector(state => Object.values(state.main.helmChartMap));
+ const gridColumns = useMemo(() => {
+ let gridTemplateColumns = 'max-content 1fr';
- const rootFileEntry = useMemo(() => fileMap[ROOT_FILE_ENTRY], [fileMap]);
-
- const [hasSeenKustomizations, setHasSeenKustomizations] = useState(false);
- const [hasSeenHelmCharts, setHasSeenHelmCharts] = useState(false);
-
- useEffect(() => {
- if (leftActive && leftMenuSelection === 'kustomize-pane') {
- setHasSeenKustomizations(true);
- }
- if (leftActive && leftMenuSelection === 'helm-pane') {
- setHasSeenHelmCharts(true);
- }
- }, [leftActive, leftMenuSelection]);
-
- useEffect(() => {
- setHasSeenKustomizations(false);
- setHasSeenHelmCharts(false);
- }, [rootFileEntry]);
-
- // TODO: refactor this to get the size of the page header dynamically
- const contentHeight = useMemo(() => {
- return isInPreviewMode ? `${windowSize.height - 100}px` : `${windowSize.height - 75}px`;
- }, [isInPreviewMode, windowSize.height]);
-
- const isFolderOpen = useMemo(() => {
- return Boolean(fileMap[ROOT_FILE_ENTRY]);
- }, [fileMap]);
-
- const setLeftActiveMenu = (selectedMenu: LeftMenuSelectionType) => {
- if (leftMenuSelection === selectedMenu) {
- if (isStartProjectPaneVisible) {
- dispatch(toggleStartProjectPane());
- } else {
- dispatch(toggleLeftMenu());
- }
- } else {
- if (isStartProjectPaneVisible) {
- dispatch(toggleStartProjectPane());
- }
- dispatch(setLeftMenuSelection(selectedMenu));
-
- if (!leftActive) {
- dispatch(toggleLeftMenu());
- }
- }
- };
-
- const setRightActiveMenu = (selectedMenu: string) => {
if (featureJson.ShowRightMenu) {
- if (rightMenuSelection === selectedMenu) {
- dispatch(toggleRightMenu());
- } else {
- dispatch(setRightMenuSelection(selectedMenu));
- if (!rightActive) {
- dispatch(toggleRightMenu());
- }
- }
+ gridTemplateColumns += ' max-content';
}
- };
- let content;
- if (isProjectLoading) {
- content = ;
- } else if (activeProject && !isStartProjectPaneVisible) {
- content = (
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- >
- }
- hideLeft={!leftActive}
- nav={}
- editor={}
- right={
- <>
- {featureJson.ShowGraphView && rightMenuSelection === 'graph' ? (
-
- ) : undefined}
- >
- }
- hideRight={!rightActive}
- />
-
- );
- } else {
- content = (
-
-
-
-
-
- {Boolean(projects.length) && (
-
-
-
- )}
-
-
- );
- }
+ return gridTemplateColumns;
+ }, []);
return (
-
-
-
-
- setLeftActiveMenu('file-explorer')}
- disabled={!activeProject}
- >
-
-
-
-
- setLeftActiveMenu('kustomize-pane')}
- sectionNames={[KUSTOMIZATION_SECTION_NAME, KUSTOMIZE_PATCH_SECTION_NAME]}
- disabled={!activeProject}
- >
-
-
-
-
-
-
- setLeftActiveMenu('helm-pane')}
- sectionNames={[HELM_CHART_SECTION_NAME]}
- disabled={!activeProject}
- >
-
-
-
-
-
-
-
- setLeftActiveMenu('templates-pane')}
- disabled={!activeProject}
- >
-
-
-
-
-
-
- {content}
-
-
-
-
-
-
+
+
+
+ {isProjectLoading ? (
+
+ ) : activeProject && !isStartProjectPaneVisible ? (
+
+ ) : (
+
+
+
+ {Boolean(projects.length) && }
+
+ )}
+
+ {featureJson.ShowRightMenu && }
+
);
};
diff --git a/src/components/organisms/PaneManager/PaneManagerLeftMenu.styled.tsx b/src/components/organisms/PaneManager/PaneManagerLeftMenu.styled.tsx
new file mode 100644
index 0000000000..510cdf4879
--- /dev/null
+++ b/src/components/organisms/PaneManager/PaneManagerLeftMenu.styled.tsx
@@ -0,0 +1,22 @@
+import {Badge as RawBadge} from 'antd';
+
+import styled from 'styled-components';
+
+import {AppBorders} from '@styles/Borders';
+
+export const Badge = styled(RawBadge)`
+ & .ant-badge-dot {
+ top: 3px;
+ right: 3px;
+ }
+`;
+
+export const Container = styled.div`
+ height: 100%;
+ width: 100%;
+ display: flex;
+ padding-right: 4px;
+ flex-direction: column;
+ gap: 7px;
+ border-right: ${AppBorders.pageDivider};
+`;
diff --git a/src/components/organisms/PaneManager/PaneManagerLeftMenu.tsx b/src/components/organisms/PaneManager/PaneManagerLeftMenu.tsx
new file mode 100644
index 0000000000..1224a937b9
--- /dev/null
+++ b/src/components/organisms/PaneManager/PaneManagerLeftMenu.tsx
@@ -0,0 +1,176 @@
+import {useEffect, useMemo, useState} from 'react';
+
+import {Tooltip} from 'antd';
+
+import {FolderOpenOutlined, FolderOutlined, FormatPainterOutlined} from '@ant-design/icons';
+
+import {ROOT_FILE_ENTRY, TOOLTIP_DELAY} from '@constants/constants';
+
+import {LeftMenuSelectionType} from '@models/ui';
+
+import {useAppDispatch, useAppSelector} from '@redux/hooks';
+import {setLeftMenuSelection, toggleLeftMenu, toggleStartProjectPane} from '@redux/reducers/ui';
+import {activeProjectSelector, kustomizationsSelector} from '@redux/selectors';
+
+import Colors from '@styles/Colors';
+
+import {HELM_CHART_SECTION_NAME} from '@src/navsections/HelmChartSectionBlueprint';
+import {KUSTOMIZATION_SECTION_NAME} from '@src/navsections/KustomizationSectionBlueprint';
+import {KUSTOMIZE_PATCH_SECTION_NAME} from '@src/navsections/KustomizePatchSectionBlueprint';
+
+import MenuButton from './MenuButton';
+import MenuIcon from './MenuIcon';
+import * as S from './PaneManagerLeftMenu.styled';
+
+const PaneManagerLeftMenu: React.FC = () => {
+ const dispatch = useAppDispatch();
+ const activeProject = useAppSelector(activeProjectSelector);
+ const fileMap = useAppSelector(state => state.main.fileMap);
+ const leftActive = useAppSelector(state => state.ui.leftMenu.isActive);
+ const leftMenuSelection = useAppSelector(state => state.ui.leftMenu.selection);
+ const helmCharts = useAppSelector(state => Object.values(state.main.helmChartMap));
+ const highlightedItems = useAppSelector(state => state.ui.highlightedItems);
+ const isStartProjectPaneVisible = useAppSelector(state => state.ui.isStartProjectPaneVisible);
+ const kustomizations = useAppSelector(kustomizationsSelector);
+
+ const [hasSeenKustomizations, setHasSeenKustomizations] = useState(false);
+ const [hasSeenHelmCharts, setHasSeenHelmCharts] = useState(false);
+
+ const isFolderOpen = useMemo(() => Boolean(fileMap[ROOT_FILE_ENTRY]), [fileMap]);
+ const rootFileEntry = useMemo(() => fileMap[ROOT_FILE_ENTRY], [fileMap]);
+
+ const setLeftActiveMenu = (selectedMenu: LeftMenuSelectionType) => {
+ if (leftMenuSelection === selectedMenu) {
+ if (isStartProjectPaneVisible) {
+ dispatch(toggleStartProjectPane());
+ } else {
+ dispatch(toggleLeftMenu());
+ }
+ } else {
+ if (isStartProjectPaneVisible) {
+ dispatch(toggleStartProjectPane());
+ }
+ dispatch(setLeftMenuSelection(selectedMenu));
+
+ if (!leftActive) {
+ dispatch(toggleLeftMenu());
+ }
+ }
+ };
+
+ useEffect(() => {
+ if (leftActive && leftMenuSelection === 'kustomize-pane') {
+ setHasSeenKustomizations(true);
+ }
+ if (leftActive && leftMenuSelection === 'helm-pane') {
+ setHasSeenHelmCharts(true);
+ }
+ }, [leftActive, leftMenuSelection]);
+
+ useEffect(() => {
+ setHasSeenKustomizations(false);
+ setHasSeenHelmCharts(false);
+ }, [rootFileEntry]);
+
+ return (
+
+
+ setLeftActiveMenu('file-explorer')}
+ disabled={!activeProject}
+ >
+
+
+
+
+
+ setLeftActiveMenu('kustomize-pane')}
+ sectionNames={[KUSTOMIZATION_SECTION_NAME, KUSTOMIZE_PATCH_SECTION_NAME]}
+ disabled={!activeProject}
+ >
+
+
+
+
+
+
+
+ setLeftActiveMenu('helm-pane')}
+ sectionNames={[HELM_CHART_SECTION_NAME]}
+ disabled={!activeProject}
+ >
+
+
+
+
+
+
+
+ setLeftActiveMenu('templates-pane')}
+ disabled={!activeProject}
+ >
+
+
+
+
+ );
+};
+
+export default PaneManagerLeftMenu;
diff --git a/src/components/organisms/PaneManager/PaneManagerRightMenu.styled.tsx b/src/components/organisms/PaneManager/PaneManagerRightMenu.styled.tsx
new file mode 100644
index 0000000000..710be3dda6
--- /dev/null
+++ b/src/components/organisms/PaneManager/PaneManagerRightMenu.styled.tsx
@@ -0,0 +1,13 @@
+import styled from 'styled-components';
+
+import {AppBorders} from '@styles/Borders';
+
+export const Container = styled.div`
+ height: 100%;
+ width: 100%;
+ padding-left: 4px;
+ display: flex;
+ flex-direction: column;
+ gap: 7px;
+ border-left: ${AppBorders.pageDivider};
+`;
diff --git a/src/components/organisms/PaneManager/PaneManagerRightMenu.tsx b/src/components/organisms/PaneManager/PaneManagerRightMenu.tsx
new file mode 100644
index 0000000000..9f4bff853a
--- /dev/null
+++ b/src/components/organisms/PaneManager/PaneManagerRightMenu.tsx
@@ -0,0 +1,53 @@
+import {Button} from 'antd';
+
+import {ApartmentOutlined, CodeOutlined} from '@ant-design/icons';
+
+import {RightMenuSelectionType} from '@models/ui';
+
+import {useAppDispatch, useAppSelector} from '@redux/hooks';
+import {setRightMenuSelection, toggleRightMenu} from '@redux/reducers/ui';
+
+import featureJson from '@src/feature-flags.json';
+
+import MenuIcon from './MenuIcon';
+import * as S from './PaneManagerRightMenu.styled';
+
+const PaneManagerRightMenu: React.FC = () => {
+ const dispatch = useAppDispatch();
+ const rightActive = useAppSelector(state => state.ui.rightMenu.isActive);
+ const rightMenuSelection = useAppSelector(state => state.ui.rightMenu.selection);
+
+ const setRightActiveMenu = (selectedMenu: RightMenuSelectionType) => {
+ if (featureJson.ShowRightMenu) {
+ if (rightMenuSelection === selectedMenu) {
+ dispatch(toggleRightMenu());
+ } else {
+ dispatch(setRightMenuSelection(selectedMenu));
+ if (!rightActive) {
+ dispatch(toggleRightMenu());
+ }
+ }
+ }
+ };
+
+ return (
+
+ setRightActiveMenu('graph')}
+ icon={}
+ style={{display: featureJson.ShowGraphView ? 'inline' : 'none'}}
+ />
+
+ setRightActiveMenu('logs')}
+ icon={}
+ />
+
+ );
+};
+
+export default PaneManagerRightMenu;
diff --git a/src/components/organisms/PaneManager/PaneManagerSplitView.styled.tsx b/src/components/organisms/PaneManager/PaneManagerSplitView.styled.tsx
new file mode 100644
index 0000000000..d498c4c20c
--- /dev/null
+++ b/src/components/organisms/PaneManager/PaneManagerSplitView.styled.tsx
@@ -0,0 +1,49 @@
+import styled from 'styled-components';
+
+import {AppBorders} from '@styles/Borders';
+
+export const EditorPaneContainer = styled.div`
+ height: 100%;
+ border-left: ${AppBorders.sectionDivider};
+ position: relative;
+ margin-left: 1px;
+
+ & .custom-modal-handle {
+ left: -3px;
+ }
+`;
+
+export const LeftPaneContainer = styled.div`
+ height: 100%;
+ border-right: ${AppBorders.sectionDivider};
+ position: relative;
+
+ & .custom-modal-handle {
+ right: -3px;
+ }
+`;
+
+export const Pane = styled.div<{$height?: number}>`
+ ${({$height}) => `
+ height: ${$height ? `${$height}px` : '100%'};
+ `};
+ width: 100%;
+`;
+
+export const SplitViewContainer = styled.div<{$gridTemplateColumns: string}>`
+ width: 100%;
+ height: 100%;
+ display: grid;
+
+ ${({$gridTemplateColumns}) => `
+ grid-template-columns: ${$gridTemplateColumns};
+ `};
+
+ & .custom-modal-handle {
+ position: absolute;
+ top: 0;
+ bottom: 0;
+ width: 5px;
+ cursor: col-resize;
+ }
+`;
diff --git a/src/components/organisms/PaneManager/PaneMangerSplitView.tsx b/src/components/organisms/PaneManager/PaneMangerSplitView.tsx
new file mode 100644
index 0000000000..57948e2b0a
--- /dev/null
+++ b/src/components/organisms/PaneManager/PaneMangerSplitView.tsx
@@ -0,0 +1,156 @@
+import React, {LegacyRef, Suspense, useCallback, useEffect, useMemo} from 'react';
+import {ResizableBox} from 'react-resizable';
+import {useMeasure} from 'react-use';
+
+import {MIN_SPLIT_VIEW_PANE_WIDTH} from '@constants/constants';
+
+import {useAppDispatch, useAppSelector} from '@redux/hooks';
+import {setPaneConfiguration} from '@redux/reducers/ui';
+
+import {ActionsPane, NavigatorPane} from '@organisms';
+
+import {GraphView} from '@components/molecules';
+
+import {useMainPaneHeight} from '@utils/hooks';
+
+import featureJson from '@src/feature-flags.json';
+
+import * as S from './PaneManagerSplitView.styled';
+
+const FileTreePane = React.lazy(() => import('@organisms/FileTreePane'));
+const HelmPane = React.lazy(() => import('@organisms/HelmPane'));
+const KustomizePane = React.lazy(() => import('@organisms/KustomizePane'));
+const TemplateManagerPane = React.lazy(() => import('@organisms/TemplateManagerPane'));
+
+const PaneManagerSplitView: React.FC = () => {
+ const dispatch = useAppDispatch();
+ const editWidth = useAppSelector(state => state.ui.paneConfiguration.editWidth);
+ const leftActive = useAppSelector(state => state.ui.leftMenu.isActive);
+ const leftMenuSelection = useAppSelector(state => state.ui.leftMenu.selection);
+ const leftWidth = useAppSelector(state => state.ui.paneConfiguration.leftWidth);
+ const navWidth = useAppSelector(state => state.ui.paneConfiguration.navWidth);
+ const paneConfiguration = useAppSelector(state => state.ui.paneConfiguration);
+ const rightActive = useAppSelector(state => state.ui.rightMenu.isActive);
+ const rightMenuSelection = useAppSelector(state => state.ui.rightMenu.selection);
+
+ const paneHeight = useMainPaneHeight();
+
+ const [editPaneRef, {width: editPaneWidth}] = useMeasure();
+ const [leftPaneRef, {width: leftPaneWidth}] = useMeasure();
+ const [navPaneRef, {width: navPaneWidth}] = useMeasure();
+ const [splitViewContainerRef, {width: splitViewContainerWidth}] = useMeasure();
+
+ const leftPaneMaxWidth = useMemo(() => {
+ return splitViewContainerWidth - MIN_SPLIT_VIEW_PANE_WIDTH - editPaneWidth;
+ }, [editPaneWidth, splitViewContainerWidth]);
+
+ const editPaneMaxWidth = useMemo(() => {
+ let maxWidth = splitViewContainerWidth - MIN_SPLIT_VIEW_PANE_WIDTH;
+
+ if (leftActive) {
+ maxWidth -= leftPaneWidth;
+ }
+
+ return maxWidth;
+ }, [leftActive, leftPaneWidth, splitViewContainerWidth]);
+
+ const resizeLeftPane = useCallback(() => {
+ const newLeftWidthPercentage = leftPaneWidth / splitViewContainerWidth;
+
+ if (newLeftWidthPercentage !== paneConfiguration.leftWidth) {
+ dispatch(setPaneConfiguration({...paneConfiguration, leftWidth: newLeftWidthPercentage}));
+ }
+ }, [dispatch, leftPaneWidth, paneConfiguration, splitViewContainerWidth]);
+
+ const resizeEditPane = useCallback(() => {
+ const newEditPaneWidthPercentage = editPaneWidth / splitViewContainerWidth;
+
+ if (newEditPaneWidthPercentage !== paneConfiguration.editWidth) {
+ dispatch(setPaneConfiguration({...paneConfiguration, editWidth: newEditPaneWidthPercentage}));
+ }
+ }, [dispatch, editPaneWidth, paneConfiguration, splitViewContainerWidth]);
+
+ const splitViewGridTemplateColumns = useMemo(() => {
+ let gridTemplateColumns = 'max-content 1fr max-content';
+
+ if (!leftActive) {
+ gridTemplateColumns = '1fr max-content';
+ }
+
+ return gridTemplateColumns;
+ }, [leftActive]);
+
+ useEffect(() => {
+ if (
+ leftActive &&
+ navPaneWidth + splitViewContainerWidth * leftWidth + splitViewContainerWidth * editWidth > splitViewContainerWidth
+ ) {
+ const newEditPaneWidthPercentage = 1 - leftWidth - navWidth;
+
+ dispatch(setPaneConfiguration({...paneConfiguration, editWidth: newEditPaneWidthPercentage}));
+ }
+
+ if (!leftActive) {
+ dispatch(setPaneConfiguration({...paneConfiguration, navWidth: navPaneWidth / splitViewContainerWidth}));
+ }
+
+ // eslint-disable-next-line react-hooks/exhaustive-deps
+ }, [leftActive]);
+
+ return (
+
+ {leftActive && leftMenuSelection && (
+
+ ) => }
+ onResizeStop={resizeLeftPane}
+ >
+
+
+ {leftMenuSelection === 'file-explorer' && }
+ {leftMenuSelection === 'helm-pane' && }
+ {leftMenuSelection === 'kustomize-pane' && }
+ {leftMenuSelection === 'templates-pane' && }
+
+
+
+
+ )}
+
+
+
+
+
+
+ ) => }
+ onResizeStop={resizeEditPane}
+ >
+
+
+
+
+
+
+ {featureJson.ShowGraphView && rightMenuSelection === 'graph' && rightActive && (
+
+
+
+ )}
+
+ );
+};
+
+export default PaneManagerSplitView;
diff --git a/src/components/organisms/PaneManager/index.js b/src/components/organisms/PaneManager/index.ts
similarity index 100%
rename from src/components/organisms/PaneManager/index.js
rename to src/components/organisms/PaneManager/index.ts
diff --git a/src/components/organisms/PaneManager/styled.tsx b/src/components/organisms/PaneManager/styled.tsx
index 7319d0bc86..1e94be4f79 100644
--- a/src/components/organisms/PaneManager/styled.tsx
+++ b/src/components/organisms/PaneManager/styled.tsx
@@ -1,67 +1,20 @@
-import {Skeleton as RawSkeleton, Space as RawSpace} from 'antd';
+import {Skeleton as RawSkeleton} from 'antd';
import styled from 'styled-components';
-import {Col} from '@atoms';
-
-import {AppBorders} from '@styles/Borders';
-import Colors, {BackgroundColors} from '@styles/Colors';
-
-export const ColumnLeftMenu = styled(Col)`
- background-color: ${BackgroundColors.darkThemeBackground};
- height: 100%;
- padding: 5px 0px 0px 0px;
- margin: 0px;
- border-right: ${AppBorders.pageDivider};
-`;
-
-export const ColumnPanes = styled(Col)`
- background-color: ${BackgroundColors.darkThemeBackground};
- height: 100%;
- padding: 0px;
- margin: 0px;
- overflow-x: visible !important;
- overflow-y: visible !important;
-`;
-
-export const ColumnRightMenu = styled(Col)`
- background-color: ${BackgroundColors.darkThemeBackground};
- height: 100%;
- padding: 0px;
- margin: 0px;
- border-left: ${AppBorders.pageDivider};
-`;
-
-export const GettingStartedPaneContainer = styled.div`
- display: flex;
- flex-direction: row;
+export const GettingStartedContainer = styled.div`
height: 100%;
+ width: 100%;
+ display: grid;
+ grid-template-columns: 1fr max-content;
`;
-export const RecentProjectsPaneContainer = styled.div`
- flex: 1;
- border-left: 1px solid ${Colors.grey3};
- max-width: 28vw;
-`;
+export const PaneManagerContainer = styled.div<{$gridTemplateColumns: string}>`
+ display: grid;
-export const Row = styled.div`
- background-color: ${BackgroundColors.darkThemeBackground};
- width: 100%;
- padding: 0px;
- margin: 0px;
- display: flex;
- flex-direction: row;
- align-items: flex-start;
+ ${({$gridTemplateColumns}) => `grid-template-columns: ${$gridTemplateColumns};`};
`;
export const Skeleton = styled(RawSkeleton)`
padding: 8px 16px;
`;
-
-export const Space = styled(RawSpace)`
- width: 43px;
-`;
-
-export const StartProjectPaneContainer = styled.div`
- flex: 3;
-`;
diff --git a/src/components/organisms/RecentProjectsPane/RecentProjectsPane.tsx b/src/components/organisms/RecentProjectsPane/RecentProjectsPane.tsx
index 5737f2ef08..7a072f5c65 100644
--- a/src/components/organisms/RecentProjectsPane/RecentProjectsPane.tsx
+++ b/src/components/organisms/RecentProjectsPane/RecentProjectsPane.tsx
@@ -1,6 +1,8 @@
-import React from 'react';
+import {LegacyRef, useCallback, useMemo} from 'react';
+import {ResizableBox} from 'react-resizable';
+import {useMeasure, useWindowSize} from 'react-use';
-import {Row, Tooltip} from 'antd';
+import {Tooltip} from 'antd';
import {DateTime} from 'luxon';
@@ -8,18 +10,27 @@ import {Project} from '@models/appconfig';
import {useAppDispatch, useAppSelector} from '@redux/hooks';
import {setOpenProject} from '@redux/reducers/appConfig';
-import {toggleStartProjectPane} from '@redux/reducers/ui';
+import {setPaneConfiguration, toggleStartProjectPane} from '@redux/reducers/ui';
import {activeProjectSelector} from '@redux/selectors';
-import {MonoPaneTitle, MonoPaneTitleCol} from '@atoms';
+import {TitleBar} from '@molecules';
-import * as S from './Styled';
+import * as S from './styled';
const RecentProjectsPane = () => {
const dispatch = useAppDispatch();
-
- const projects: Project[] = useAppSelector(state => state.config.projects);
const activeProject = useAppSelector(activeProjectSelector);
+ const paneConfiguration = useAppSelector(state => state.ui.paneConfiguration);
+ const projects = useAppSelector(state => state.config.projects);
+
+ const size = useWindowSize();
+
+ const recentProjectsPaneMaxWidth = useMemo(() => {
+ return 0.5 * size.width;
+ }, [size.width]);
+
+ const [recentProjectsPaneRef, {height: recentProjectsPaneHeight, width: recentProjectsPaneWidth}] =
+ useMeasure();
const openProject = (project: Project) => {
dispatch(setOpenProject(project.rootFolder));
@@ -39,42 +50,53 @@ const RecentProjectsPane = () => {
}
return '';
};
+
+ const resizePane = useCallback(() => {
+ if (recentProjectsPaneWidth !== paneConfiguration.recentProjectsPaneWidth) {
+ dispatch(setPaneConfiguration({...paneConfiguration, recentProjectsPaneWidth}));
+ }
+ }, [dispatch, paneConfiguration, recentProjectsPaneWidth]);
+
return (
- <>
-
-
-
-
- Recent Projects
-
-
-
-
-
-
- {projects.map((project: Project) => {
- const isActivePropject = project.rootFolder === activeProject?.rootFolder;
- return (
- onProjectItemClick(isActivePropject, project)}
- >
- {project.name}
-
- {project.rootFolder}
-
-
- {getRelativeDate(project.lastOpened)
- ? `last opened ${getRelativeDate(project.lastOpened)}`
- : 'Not opened yet'}
-
-
- );
- })}
-
-
- >
+
+ ) => }
+ onResizeStop={resizePane}
+ >
+
+
+
+
+ {projects.map((project: Project) => {
+ const isActivePropject = project.rootFolder === activeProject?.rootFolder;
+ return (
+ onProjectItemClick(isActivePropject, project)}
+ >
+ {project.name}
+
+ {project.rootFolder}
+
+
+ {getRelativeDate(project.lastOpened)
+ ? `last opened ${getRelativeDate(project.lastOpened)}`
+ : 'Not opened yet'}
+
+
+ );
+ })}
+
+
+
+
);
};
diff --git a/src/components/organisms/RecentProjectsPane/Styled.tsx b/src/components/organisms/RecentProjectsPane/styled.tsx
similarity index 75%
rename from src/components/organisms/RecentProjectsPane/Styled.tsx
rename to src/components/organisms/RecentProjectsPane/styled.tsx
index b1b747a2db..4a7fb13446 100644
--- a/src/components/organisms/RecentProjectsPane/Styled.tsx
+++ b/src/components/organisms/RecentProjectsPane/styled.tsx
@@ -6,27 +6,35 @@ import {GlobalScrollbarStyle} from '@utils/scrollbar';
import Colors from '@styles/Colors';
-export const TitleBarContainer = styled.div`
- display: flex;
- height: 24px;
- justify-content: space-between;
+export const BackToProjectButton = styled(Button)`
+ font-size: 12px;
+ color: ${Colors.lightSeaGreen};
`;
-export const Title = styled.span`
- text-overflow: ellipsis;
- white-space: nowrap;
- overflow: hidden;
- padding-right: 10px;
+export const Container = styled.div`
+ height: 100%;
+ display: grid;
+ grid-template-rows: max-content 1fr;
`;
export const ProjectsContainer = styled.div`
padding: 8px 12px;
- height: calc(100vh - 112px);
- overflow-y: scroll;
+ height: 100%;
width: 100%;
+ overflow-y: auto;
+
${GlobalScrollbarStyle}
`;
+export const ProjectLastOpened = styled.div`
+ color: ${Colors.grey5};
+ font-size: 12px;
+ white-space: nowrap;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ width: 100%;
+`;
+
export const ProjectItem = styled.div<{activeproject: boolean}>`
padding: 4px 8px 4px 8px;
margin-left: ${props => (props.activeproject ? '-12px' : 'unset')};
@@ -61,16 +69,19 @@ export const ProjectPath = styled.div`
width: 100%;
`;
-export const ProjectLastOpened = styled.div`
- color: ${Colors.grey5};
- font-size: 12px;
- white-space: nowrap;
- overflow: hidden;
- text-overflow: ellipsis;
- width: 100%;
-`;
+export const RecentProjectsPaneContainer = styled.div`
+ height: 100%;
+ border-left: 1px solid ${Colors.grey3};
+ position: relative;
-export const BackToProjectButton = styled(Button)`
- font-size: 12px;
- color: ${Colors.lightSeaGreen};
+ & .custom-modal-handle {
+ position: absolute;
+ top: 50%;
+ left: -5px;
+ height: 100%;
+ width: 10px;
+ background-color: transparent;
+ cursor: col-resize;
+ transform: translateY(-50%);
+ }
`;
diff --git a/src/components/organisms/StartProjectPane/StartProjectPane.tsx b/src/components/organisms/StartProjectPane/StartProjectPane.tsx
index 6b072e731d..230d92ea50 100644
--- a/src/components/organisms/StartProjectPane/StartProjectPane.tsx
+++ b/src/components/organisms/StartProjectPane/StartProjectPane.tsx
@@ -1,17 +1,13 @@
-import React from 'react';
-
-import {Row} from 'antd';
-
import {useAppDispatch} from '@redux/hooks';
import {openCreateProjectModal, openFolderExplorer} from '@redux/reducers/ui';
-import {MonoPaneTitle, MonoPaneTitleCol} from '@atoms';
+import {TitleBar} from '@molecules';
import SelectAnEmptyProject from '@assets/SelectAnEmptyProject.svg';
import SelectAnExistingFolder from '@assets/SelectAnExistingFolder.svg';
import StartFromTemplate from '@assets/StartFromTemplate.svg';
-import * as S from './Styled';
+import * as S from './styled';
const StartProjectPane = () => {
const dispatch = useAppDispatch();
@@ -25,44 +21,34 @@ const StartProjectPane = () => {
};
return (
- <>
-
-
-
-
- Start a Project
-
-
-
-
-
-
-
-
How would you like to begin?
-
-
-
-
-
- Select an existing folder
-
- handleCreateProject(false)}>
-
-
-
- Create an empty project
-
- handleCreateProject(true)}>
-
-
-
- Start from a template
-
-
-
-
-
- >
+
+
+
+
+ How would you like to begin?
+
+
+
+
+
+
+ Select an existing folder
+
+ handleCreateProject(false)}>
+
+
+
+ Create an empty project
+
+ handleCreateProject(true)}>
+
+
+
+ Start from a template
+
+
+
+
);
};
diff --git a/src/components/organisms/StartProjectPane/Styled.tsx b/src/components/organisms/StartProjectPane/styled.tsx
similarity index 75%
rename from src/components/organisms/StartProjectPane/Styled.tsx
rename to src/components/organisms/StartProjectPane/styled.tsx
index 5a8cc002dd..a0a282fae0 100644
--- a/src/components/organisms/StartProjectPane/Styled.tsx
+++ b/src/components/organisms/StartProjectPane/styled.tsx
@@ -4,23 +4,28 @@ import StartBackgrojnd from '@assets/StartBackground.svg';
import Colors from '@styles/Colors';
-export const TitleBarContainer = styled.div`
+export const ActionContainer = styled.div`
display: flex;
- height: 24px;
- justify-content: space-between;
+ flex-direction: column;
+ justify-content: center;
+ cursor: pointer;
`;
-export const Title = styled.span`
- text-overflow: ellipsis;
- white-space: nowrap;
- overflow: hidden;
- padding-right: 10px;
+export const ActionText = styled.div`
+ color: ${Colors.blue7};
+ font-size: 0.875em;
+ text-align: center;
`;
-export const FolderOpenOutlined = styled.img`
- width: 64px;
- color: ${Colors.blue10};
- margin-bottom: 24px;
+export const ActionTitle = styled.div`
+ font-size: 22px;
+ text-align: center;
+ margin-bottom: 150px;
+`;
+
+export const ActionsContainer = styled.div`
+ display: flex;
+ gap: 80px;
`;
export const FolderAddOutlined = styled.img`
@@ -28,18 +33,16 @@ export const FolderAddOutlined = styled.img`
margin-bottom: 24px;
`;
-export const FormatPainterOutlined = styled.img`
+export const FolderOpenOutlined = styled.img`
width: 64px;
color: ${Colors.blue10};
margin-bottom: 24px;
`;
-export const ActionContainer = styled.div`
- display: flex;
- flex-direction: column;
- justify-content: center;
- margin: 0 40px;
- cursor: pointer;
+export const FormatPainterOutlined = styled.img`
+ width: 64px;
+ color: ${Colors.blue10};
+ margin-bottom: 24px;
`;
export const IconWrapper = styled.div`
@@ -48,28 +51,22 @@ export const IconWrapper = styled.div`
flex: 1;
`;
-export const ActionText = styled.div`
- color: ${Colors.blue7};
- font-size: 0.875em;
- text-align: center;
-`;
-
-export const ActionTitle = styled.div`
- font-size: 22px;
- text-align: center;
- margin-bottom: 150px;
-`;
+export const StartBackground = styled.div`
+ background: url('${StartBackgrojnd}') no-repeat;
+ background-position: 55% 35%;
-export const Container = styled.div`
width: 100%;
- height: calc(100vh - 112px);
+ height: 100%;
display: flex;
+ flex-direction: column;
justify-content: center;
align-items: center;
padding-bottom: 150px;
`;
-export const StartBackground = styled(Container)`
- background: url('${StartBackgrojnd}') no-repeat;
- background-position: 55% 35%;
+export const StartProjectPaneContainer = styled.div`
+ height: 100%;
+ border-left: 1px solid ${Colors.grey3};
+ display: grid;
+ grid-template-rows: max-content 1fr;
`;
diff --git a/src/components/organisms/TemplateManagerPane/TemplateManagerPane.styled.tsx b/src/components/organisms/TemplateManagerPane/TemplateManagerPane.styled.tsx
index befc19c824..25fc719292 100644
--- a/src/components/organisms/TemplateManagerPane/TemplateManagerPane.styled.tsx
+++ b/src/components/organisms/TemplateManagerPane/TemplateManagerPane.styled.tsx
@@ -6,8 +6,10 @@ import {GlobalScrollbarStyle} from '@utils/scrollbar';
import Colors from '@styles/Colors';
-export const Container = styled.div`
- padding: 16px 0px;
+export const Container = styled.div<{$height: number}>`
+ ${({$height}) => `height: ${$height ? `${$height}px` : '100%'};`};
+ display: grid;
+ grid-template-rows: max-content 1fr;
`;
export const NotFoundLabel = styled.span`
@@ -22,12 +24,18 @@ export const SearchInput = styled(Input.Search)`
`;
export const SearchInputContainer = styled.div`
- margin-bottom: 25px;
+ margin: 16px 0px 25px 0px;
padding: 0px 16px;
`;
-export const TemplatesContainer = styled.div<{$height: number}>`
- ${props => `height: ${props.$height}px;`}
+export const TemplateManagerPaneContainer = styled.div`
+ height: 100%;
+ display: grid;
+ grid-template-rows: max-content 1fr;
+`;
+
+export const TemplatesContainer = styled.div<{$height?: number}>`
+ ${props => `height: ${props.$height ? `${props.$height}px` : '100%'};`}
display: grid;
grid-auto-rows: max-content;
grid-row-gap: 25px;
diff --git a/src/components/organisms/TemplateManagerPane/TemplateManagerPane.tsx b/src/components/organisms/TemplateManagerPane/TemplateManagerPane.tsx
index 5a7256e5ea..406fe56cbb 100644
--- a/src/components/organisms/TemplateManagerPane/TemplateManagerPane.tsx
+++ b/src/components/organisms/TemplateManagerPane/TemplateManagerPane.tsx
@@ -1,10 +1,10 @@
import React, {useCallback, useEffect, useMemo, useState} from 'react';
+import {useMeasure} from 'react-use';
import {Button, Tooltip} from 'antd';
import {ReloadOutlined} from '@ant-design/icons';
-import {TEMPLATES_HEIGHT_OFFSET} from '@constants/constants';
import {TemplateManagerPaneReloadTooltip} from '@constants/tooltips';
import {AnyTemplate} from '@models/template';
@@ -13,9 +13,7 @@ import {useAppDispatch, useAppSelector} from '@redux/hooks';
import {isInPreviewModeSelector} from '@redux/selectors';
import {checkForExtensionsUpdates} from '@redux/services/extension';
-import {TitleBar} from '@components/molecules';
-
-import {useWindowSize} from '@utils/hooks';
+import {TitleBar} from '@molecules';
import TemplateModal from '../TemplateModal';
import TemplateInformation from './TemplateInformation';
@@ -35,7 +33,13 @@ const filterTemplateBySearchedValue = (searchedValue: string, name: string) => {
return shouldBeFiltered;
};
-const TemplatesPane: React.FC = () => {
+interface IProps {
+ contentHeight?: number;
+}
+
+const TemplatesManagerPane: React.FC = props => {
+ const {contentHeight} = props;
+
const dispatch = useAppDispatch();
const [selectedTemplate, setSelectedTemplate] = useState(undefined);
@@ -49,7 +53,7 @@ const TemplatesPane: React.FC = () => {
const [searchedValue, setSearchedValue] = useState();
const [visibleTemplateEntries, setVisibleTemplateEntries] = useState<[string, AnyTemplate][]>();
- const windowSize = useWindowSize();
+ const [titleBarRef, {height: titleBarHeight}] = useMeasure();
const isLoading = useMemo(() => {
return isLoadingExistingTemplates || isLoadingExistingTemplatePacks;
@@ -59,11 +63,6 @@ const TemplatesPane: React.FC = () => {
return Object.values(templateMap);
}, [templateMap]);
- const templatesHeight = useMemo(
- () => windowSize.height - TEMPLATES_HEIGHT_OFFSET - (isInPreviewMode ? 25 : 0),
- [windowSize.height, isInPreviewMode]
- );
-
const onTemplateModalClose = useCallback(() => {
setSelectedTemplate(undefined);
}, []);
@@ -93,21 +92,22 @@ const TemplatesPane: React.FC = () => {
}, [searchedValue, templateMap]);
return (
- <>
- {selectedTemplate && }
-
-
- }
- />
-
-
-
-
+
+
+
+
+ }
+ />
+
+
+
+
+
{isLoading ? (
) : !visibleTemplateEntries ? (
@@ -125,7 +125,7 @@ const TemplatesPane: React.FC = () => {
{!visibleTemplateEntries.length ? (
No templates found.
) : (
-
+
{visibleTemplateEntries.map(([path, template]) => (
{
>
)}
- >
+
+ {selectedTemplate && }
+
);
};
-export default TemplatesPane;
+export default TemplatesManagerPane;
diff --git a/src/constants/constants.ts b/src/constants/constants.ts
index a1d132ef5d..48b78b77e0 100644
--- a/src/constants/constants.ts
+++ b/src/constants/constants.ts
@@ -7,7 +7,6 @@ export const ROOT_FILE_ENTRY = '';
export const APP_MIN_WIDTH = 800;
export const APP_MIN_HEIGHT = 600;
export const TOOLTIP_DELAY = 1.0;
-export const NAVIGATOR_HEIGHT_OFFSET = 116;
export const FILE_TREE_HEIGHT_OFFSET = 185;
export const REF_PATH_SEPARATOR = '#';
export const KUSTOMIZATION_FILE_NAME = 'kustomization.yaml';
@@ -18,10 +17,9 @@ export const KUSTOMIZE_HELP_URL = 'https://kubectl.docs.kubernetes.io/references
export const DEPENDENCIES_HELP_URL = 'https://kubeshop.github.io/monokle/getting-started/#install-dependencies';
export const DEFAULT_EDITOR_DEBOUNCE = 500;
export const DEFAULT_KUBECONFIG_DEBOUNCE = 1000;
-export const ACTIONS_PANE_TAB_PANE_OFFSET = 95;
export const ACTIONS_PANE_FOOTER_DEFAULT_HEIGHT = 43;
export const ACTIONS_PANE_FOOTER_EXPANDED_DEFAULT_HEIGHT = 150;
-export const TEMPLATES_HEIGHT_OFFSET = 190;
+export const MIN_SPLIT_VIEW_PANE_WIDTH = 350;
export const DEFAULT_PLUGINS = [
{
owner: 'kubeshop',
diff --git a/src/index.css b/src/index.css
index 3b1bdb2c3f..95e33514a6 100644
--- a/src/index.css
+++ b/src/index.css
@@ -6,10 +6,20 @@ body {
-moz-osx-font-smoothing: grayscale;
}
+#root {
+ height: 100%;
+ width: 100%;
+}
+
code {
font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', monospace;
}
+.custom-modal-handle:active {
+ border-right: 2px solid rgba(23, 125, 220, 0.5);
+ border-left: 2px solid rgba(23, 125, 220, 0.5);
+}
+
.monokleEditorUnsatisfiedRefGlyphClass {
background-image: url('./assets/MonokleWarning.svg');
background-repeat: no-repeat;
diff --git a/src/models/ui.ts b/src/models/ui.ts
index f7f7e4efb2..10a77df9de 100644
--- a/src/models/ui.ts
+++ b/src/models/ui.ts
@@ -47,12 +47,13 @@ export type MonacoUiState = {
selection?: MonacoUiSelection;
};
-export type LeftMenuSelectionType =
- | 'file-explorer'
- | 'helm-pane'
- | 'kustomize-pane'
- | 'cluster-explorer'
- | 'templates-pane';
+export type LeftMenuSelectionType = 'file-explorer' | 'helm-pane' | 'kustomize-pane' | 'templates-pane';
+export type RightMenuSelectionType = 'logs' | 'graph';
+
+export type LayoutSizeType = {
+ footer: number;
+ header: number;
+};
export type UiState = {
isResourceFiltersOpen: boolean;
@@ -85,6 +86,7 @@ export type UiState = {
entityName: string;
absolutePathToEntity: string;
};
+ layoutSize: LayoutSizeType;
isFolderLoading: boolean;
leftMenu: {
selection: LeftMenuSelectionType;
@@ -94,7 +96,7 @@ export type UiState = {
isOpen: boolean;
};
rightMenu: {
- selection?: string;
+ selection?: RightMenuSelectionType;
isActive: boolean;
};
navPane: {
@@ -122,4 +124,5 @@ export type PaneConfiguration = {
editWidth: number;
rightWidth: number;
actionsPaneFooterExpandedHeight: number;
+ recentProjectsPaneWidth: number;
};
diff --git a/src/navsections/K8sResourceSectionBlueprint/K8sResourceSectionEmptyDisplay.tsx b/src/navsections/K8sResourceSectionBlueprint/K8sResourceSectionEmptyDisplay.tsx
index 01b15c3cb4..87e916438a 100644
--- a/src/navsections/K8sResourceSectionBlueprint/K8sResourceSectionEmptyDisplay.tsx
+++ b/src/navsections/K8sResourceSectionBlueprint/K8sResourceSectionEmptyDisplay.tsx
@@ -59,12 +59,20 @@ function K8sResourceSectionEmptyDisplay() {
{activeResources.length === 0 ? (
Get started:
- handleClick(HighlightItems.CREATE_RESOURCE)}>Create a resource
- handleClick(HighlightItems.BROWSE_TEMPLATES)}>Browse templates
+ handleClick(HighlightItems.CREATE_RESOURCE)}>
+ Create a resource
+
+ handleClick(HighlightItems.BROWSE_TEMPLATES)}>
+ Browse templates
+
{isKubeConfigPathValid ? (
- handleClick(HighlightItems.CONNECT_TO_CLUSTER)}>Connect to a cluster
+ handleClick(HighlightItems.CONNECT_TO_CLUSTER)}>
+ Connect to a cluster
+
) : (
- Configure a cluster
+
+ Configure a cluster
+
)}
) : (
diff --git a/src/redux/initialState.ts b/src/redux/initialState.ts
index 11e62737a6..0835a0a8cf 100644
--- a/src/redux/initialState.ts
+++ b/src/redux/initialState.ts
@@ -153,6 +153,10 @@ const initialUiState: UiState = {
collapsedNavSectionNames: [],
},
paneConfiguration: electronStore.get('ui.paneConfiguration'),
+ layoutSize: {
+ footer: 0,
+ header: 0,
+ },
shouldExpandAllNodes: false,
resetLayout: false,
isActionsPaneFooterExpanded: false,
diff --git a/src/redux/reducers/ui.ts b/src/redux/reducers/ui.ts
index 6f91e22a82..c43d94bbb6 100644
--- a/src/redux/reducers/ui.ts
+++ b/src/redux/reducers/ui.ts
@@ -8,10 +8,12 @@ import {ACTIONS_PANE_FOOTER_EXPANDED_DEFAULT_HEIGHT} from '@constants/constants'
import {
HighlightItems,
+ LayoutSizeType,
LeftMenuSelectionType,
MonacoUiState,
NewResourceWizardInput,
PaneConfiguration,
+ RightMenuSelectionType,
UiState,
} from '@models/ui';
@@ -59,6 +61,9 @@ export const uiSlice = createSlice({
state.rightMenu.isActive = !state.rightMenu.isActive;
electronStore.set('ui.rightMenu.isActive', state.rightMenu.isActive);
},
+ setLayoutSize: (state: Draft, action: PayloadAction) => {
+ state.layoutSize = action.payload;
+ },
toggleNotifications: (state: Draft) => {
state.isNotificationsOpen = !state.isNotificationsOpen;
},
@@ -66,7 +71,7 @@ export const uiSlice = createSlice({
state.rightMenu.isActive = action.payload;
electronStore.set('ui.rightMenu.isActive', state.rightMenu.isActive);
},
- setRightMenuSelection: (state: Draft, action: PayloadAction) => {
+ setRightMenuSelection: (state: Draft, action: PayloadAction) => {
state.rightMenu.selection = action.payload;
electronStore.set('ui.rightMenu.selection', state.rightMenu.selection);
},
@@ -207,6 +212,7 @@ export const uiSlice = createSlice({
editWidth: 0.3333,
rightWidth: 0,
actionsPaneFooterExpandedHeight: ACTIONS_PANE_FOOTER_EXPANDED_DEFAULT_HEIGHT,
+ recentProjectsPaneWidth: 450,
};
state.paneConfiguration = defaultPaneConfiguration;
electronStore.set('ui.paneConfiguration', defaultPaneConfiguration);
@@ -274,6 +280,7 @@ export const {
closeCreateProjectModal,
toggleExpandActionsPaneFooter,
resetLayout,
+ setLayoutSize,
highlightItem,
openQuickSearchActionsPopup,
closeQuickSearchActionsPopup,
diff --git a/src/utils/hooks.ts b/src/utils/hooks.ts
index f5bb1acb6b..965a4ea072 100644
--- a/src/utils/hooks.ts
+++ b/src/utils/hooks.ts
@@ -2,6 +2,8 @@ import React, {useEffect, useRef, useState} from 'react';
import {Size} from '@models/window';
+import {useAppSelector} from '@redux/hooks';
+
export function useFocus(): [React.RefObject, () => void] {
const htmlElRef = useRef(null);
const focus = () => {
@@ -13,6 +15,24 @@ export function useFocus(): [React.RefObject, () => void] {
return [htmlElRef, focus];
}
+export function useMainPaneHeight(): number {
+ const layoutSize = useAppSelector(state => state.ui.layoutSize);
+ const [mainPaneHeight, setMainPaneHeight] = useState(0);
+
+ useEffect(() => {
+ const handleResize = () => {
+ setMainPaneHeight(window.innerHeight - layoutSize.footer - layoutSize.header);
+ };
+
+ window.addEventListener('resize', handleResize);
+ handleResize();
+ // Remove event listener on cleanup
+ return () => window.removeEventListener('resize', handleResize);
+ }, [layoutSize]);
+
+ return mainPaneHeight;
+}
+
export function useWindowSize(): Size {
const [windowSize, setWindowSize] = useState({
width: window.innerWidth,
diff --git a/src/utils/ipc.ts b/src/utils/ipc.ts
index ea5f6bf79a..1f2a70ec66 100644
--- a/src/utils/ipc.ts
+++ b/src/utils/ipc.ts
@@ -1,9 +1,6 @@
import {StartupFlag, StartupFlags} from './startupFlag';
-export function getChannelName(
- name: string,
- hasAutomationFlag = StartupFlag.getInstance().hasAutomationFlag,
-) {
+export function getChannelName(name: string, hasAutomationFlag = StartupFlag.getInstance().hasAutomationFlag) {
if (hasAutomationFlag) {
return `${name}-${StartupFlags.AUTOMATION}`;
}
diff --git a/src/utils/startupFlag.ts b/src/utils/startupFlag.ts
index f1a47fd25b..91f186d849 100644
--- a/src/utils/startupFlag.ts
+++ b/src/utils/startupFlag.ts
@@ -3,7 +3,6 @@ export enum StartupFlags {
}
export class StartupFlag {
-
private static _instance: StartupFlag;
private _automationFlag = false;