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 + + + )} + + + + + + + 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 - - - )} - - - - - - - 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' && ( + + - - ); -} +}; 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} /> +