diff --git a/src/components/collapse/index.tsx b/src/components/collapse/index.tsx index 8d7871cd0..044b837bc 100644 --- a/src/components/collapse/index.tsx +++ b/src/components/collapse/index.tsx @@ -14,9 +14,9 @@ import { collapseContentClassName, } from './base'; import { Scrollable } from '../scrollable'; +import { select } from 'mo/common/dom'; type RenderFunctionProps = (data: DataBaseProps) => React.ReactNode; - interface DataBaseProps { id: React.Key; name: string; @@ -25,6 +25,17 @@ interface DataBaseProps { toolbar?: IActionBarItemProps[]; renderPanel?: RenderFunctionProps; + // detect the collapse panel whether empty + _isEmpty?: boolean; + config?: { + /** + * Specify how much of the remaining space should be assigned to the item, default is 1 + * + * It unfolds in its own content height or the `MAX_GROW_HEIGHT` rather than in calculated height + */ + grow?: number; + }; + [key: string]: any; } @@ -40,6 +51,10 @@ export interface ICollapseProps { // default collapse height, only contains header const HEADER_HEIGTH = 26; +/** + * It's the max height for the item which set the grow to 0 + */ +const MAX_GROW_HEIGHT = 220; export function Collapse(props: ICollapseProps) { const [activePanelKeys, setActivePanelKeys] = useState([]); @@ -53,9 +68,13 @@ export function Collapse(props: ICollapseProps) { ...restProps } = props; + const visibleData = data.filter((d) => !d.hidden); + // assets data must have id - const filterData = data.filter((panel) => panel.id) as DataBaseProps[]; - if (filterData.length < data.length) { + const filterData = visibleData.filter( + (panel) => panel.id + ) as DataBaseProps[]; + if (filterData.length < visibleData.length) { Logger.warn(new SyntaxError('collapse data must have id')); } @@ -78,7 +97,7 @@ export function Collapse(props: ICollapseProps) { const isActive = activePanelKeys.includes(panel.id); let isEmpty = true; if (isActive) { - const contentDom = document.querySelector( + const contentDom = select( `.${collapseContentClassName}[data-content='${panel.id}']` ); isEmpty = !contentDom?.hasChildNodes(); @@ -90,7 +109,7 @@ export function Collapse(props: ICollapseProps) { filterData ); _cachePosition.push([height, top]); - const dom = document.querySelector( + const dom = select( `.${collapseItemClassName}[data-content='${panel.id}']` ); if (dom) { @@ -98,7 +117,7 @@ export function Collapse(props: ICollapseProps) { dom.style.top = `${top}px`; } }); - }, [activePanelKeys]); + }, [filterData]); const handleChangeCallback = (key: React.Key) => { const currentKeys = activePanelKeys.concat(); @@ -130,6 +149,53 @@ export function Collapse(props: ICollapseProps) { return null; }; + /** + * Returns the grow of data, or 1 + */ + const getGrow = (data: DataBaseProps) => { + if (typeof data.config?.grow === 'number') { + return data.config.grow; + } else { + return 1; + } + }; + + /** + * Returns the key whose panel is active and whose grow is 0 + */ + const getZeroPanelsByKeys = ( + keys: React.Key[], + panels: DataBaseProps[] + ) => { + return keys.filter((key) => { + const targetPanel = panels.find((panel) => panel.id === key); + if (targetPanel) { + return targetPanel.config?.grow === 0; + } + return false; + }); + }; + + /** + * Returns the collections of height + */ + const getContentHeightsByKeys = (data: React.Key[]) => { + return data.map((key) => { + const contentDom = select( + `.${collapseContentClassName}[data-content='${key}']` + ); + if (contentDom) { + // border-top-width + border-bottom-width = 2 + const basisHeight = + contentDom.getBoundingClientRect().height - + 2 + + HEADER_HEIGTH; + return basisHeight > 220 ? 220 : basisHeight; + } + return 0; + }); + }; + /** * Calculate the position of the panel in view * @param keys Current active keys @@ -150,35 +216,79 @@ export function Collapse(props: ICollapseProps) { // the height of inactive panel or empty panel is a fixed value res[0] = HEADER_HEIGTH; } else { - // total height - const wrapperHeight = - wrapper.current?.getBoundingClientRect().height || - _cacheWrapperHeight.current; - _cacheWrapperHeight.current = wrapperHeight; - // count active panels - const activeCount = keys.length; - // count the height for active panels - const activePanelHeight = - wrapperHeight - HEADER_HEIGTH * (panels.length - activeCount); - // count the non-empty & active panels in active panels - const nonEmptyAndActivePanels = keys.filter((key) => { - const targetPanel = panels.find((panel) => panel.id === key); - if (!targetPanel) { - return false; - } else if (typeof targetPanel._isEmpty === 'boolean') { - return !targetPanel._isEmpty; - } else { - const content = renderPanels(panel, panel.renderPanel); - return !!content; + if (panel.config?.grow === 0) { + // to get current panel content + const contentDom = select( + `.${collapseContentClassName}[data-content='${panel.id}']` + ); + if (contentDom) { + const height = + contentDom.getBoundingClientRect().height + + HEADER_HEIGTH; + res[0] = + height > MAX_GROW_HEIGHT ? MAX_GROW_HEIGHT : height; } - }); + } else { + // get the height of the wrapper + let wrapperHeight = + wrapper.current?.getBoundingClientRect().height || + _cacheWrapperHeight.current; + _cacheWrapperHeight.current = wrapperHeight; + // count active panels + const activeCount = keys.length; + const inactiveCount = panels.length - activeCount; + // the height active panels can occupied + wrapperHeight = wrapperHeight - HEADER_HEIGTH * inactiveCount; + + // get grow-zero panels' heights + const growZeroPanelsKeys = getZeroPanelsByKeys(keys, panels); + const growZeroPanelsHeights = getContentHeightsByKeys( + growZeroPanelsKeys + ); - // the height for active panels is divided equally by non-empty & active panels - res[0] = - (activePanelHeight - + // the height grow-normal panels can occupied = + // the height active panels can occupied - + // each grow-zero panels' heights + growZeroPanelsHeights.forEach((height) => { + wrapperHeight -= height; + }); + + // count the non-empty & active & non-grow-zero panels in active panels + const nonEmptyAndActivePanels: DataBaseProps[] = []; + const nonEmptyAndActivePanelKeys = keys.filter((key) => { + const target = panels.find((p) => p.id === key); + if (target) { + if (getGrow(target) === 0) return false; + if (typeof target._isEmpty === 'boolean') { + !target._isEmpty && + nonEmptyAndActivePanels.push(target); + return !target._isEmpty; + } + // In general, the following code will not be excuted + const contentDom = select( + `.${collapseContentClassName}[data-content='${panel.id}']` + ); + return contentDom?.hasChildNodes(); + } + return false; + }); + + const growSum = nonEmptyAndActivePanels.reduce((pre, cur) => { + return pre + getGrow(cur); + }, 0); + + const emptyAndActivePanelsHeights = HEADER_HEIGTH * - (keys.length - nonEmptyAndActivePanels.length)) / - nonEmptyAndActivePanels.length; + (keys.length - + growZeroPanelsKeys.length - + nonEmptyAndActivePanelKeys.length); + + // the height for grow-normal panels is divided by non-empty & active & grow-normal panels depends on grow number + res[0] = + ((wrapperHeight - emptyAndActivePanelsHeights) * + getGrow(panel)) / + growSum; + } } // calculate top for current panel diff --git a/src/extensions/editorTree/index.ts b/src/extensions/editorTree/index.ts index 8e2e4d4c7..0d82c8e57 100644 --- a/src/extensions/editorTree/index.ts +++ b/src/extensions/editorTree/index.ts @@ -9,10 +9,12 @@ export const ExtendsEditorTree: IExtension = { molecule.editorTree.onClose((tabId, groupId) => { molecule.editor.closeTab(tabId, groupId); + molecule.explorer.forceUpdate(); }); molecule.editorTree.onCloseOthers((tabItem, groupId) => { molecule.editor.closeOthers(tabItem, groupId); + molecule.explorer.forceUpdate(); }); molecule.editorTree.onCloseSaved((groupId) => { @@ -28,6 +30,7 @@ export const ExtendsEditorTree: IExtension = { molecule.editor.closeAll(group.id!); }); } + molecule.explorer.forceUpdate(); }); molecule.editorTree.onSaveAll((groupId) => { diff --git a/src/extensions/folderTree/index.tsx b/src/extensions/folderTree/index.tsx index 85d6defde..d54bf4e0b 100644 --- a/src/extensions/folderTree/index.tsx +++ b/src/extensions/folderTree/index.tsx @@ -83,6 +83,7 @@ export const ExtendsFolderTree: IExtension = { } else { molecule.editor.open(tabData); } + molecule.explorer.updateRender(); } ); diff --git a/src/model/workbench/explorer/explorer.tsx b/src/model/workbench/explorer/explorer.tsx index 3ef5db5ee..d5a2fb048 100644 --- a/src/model/workbench/explorer/explorer.tsx +++ b/src/model/workbench/explorer/explorer.tsx @@ -111,6 +111,9 @@ export function builtInExplorerEditorPanel() { iconName: 'codicon-close-all', }, ], + config: { + grow: 0, + }, }; } @@ -164,6 +167,9 @@ export function builtInExplorerFolderPanel() { iconName: 'codicon-collapse-all', }, ], + config: { + grow: 2, + }, }; }