Skip to content

Commit d5dca2e

Browse files
authored
feat(collapse): collaspe support to specify grow attr (#188)
* feat(collapse): collaspe support to specify grow attr * fix: improve querySelect & attribute name of collapse * fix: explorer not rerender along with editor after selecting file
1 parent c49e534 commit d5dca2e

File tree

4 files changed

+152
-32
lines changed

4 files changed

+152
-32
lines changed

src/components/collapse/index.tsx

+142-32
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,9 @@ import {
1414
collapseContentClassName,
1515
} from './base';
1616
import { Scrollable } from '../scrollable';
17+
import { select } from 'mo/common/dom';
1718

1819
type RenderFunctionProps = (data: DataBaseProps) => React.ReactNode;
19-
2020
interface DataBaseProps {
2121
id: React.Key;
2222
name: string;
@@ -25,6 +25,17 @@ interface DataBaseProps {
2525
toolbar?: IActionBarItemProps[];
2626
renderPanel?: RenderFunctionProps;
2727

28+
// detect the collapse panel whether empty
29+
_isEmpty?: boolean;
30+
config?: {
31+
/**
32+
* Specify how much of the remaining space should be assigned to the item, default is 1
33+
*
34+
* It unfolds in its own content height or the `MAX_GROW_HEIGHT` rather than in calculated height
35+
*/
36+
grow?: number;
37+
};
38+
2839
[key: string]: any;
2940
}
3041

@@ -40,6 +51,10 @@ export interface ICollapseProps {
4051

4152
// default collapse height, only contains header
4253
const HEADER_HEIGTH = 26;
54+
/**
55+
* It's the max height for the item which set the grow to 0
56+
*/
57+
const MAX_GROW_HEIGHT = 220;
4358

4459
export function Collapse(props: ICollapseProps) {
4560
const [activePanelKeys, setActivePanelKeys] = useState<React.Key[]>([]);
@@ -53,9 +68,13 @@ export function Collapse(props: ICollapseProps) {
5368
...restProps
5469
} = props;
5570

71+
const visibleData = data.filter((d) => !d.hidden);
72+
5673
// assets data must have id
57-
const filterData = data.filter((panel) => panel.id) as DataBaseProps[];
58-
if (filterData.length < data.length) {
74+
const filterData = visibleData.filter(
75+
(panel) => panel.id
76+
) as DataBaseProps[];
77+
if (filterData.length < visibleData.length) {
5978
Logger.warn(new SyntaxError('collapse data must have id'));
6079
}
6180

@@ -78,7 +97,7 @@ export function Collapse(props: ICollapseProps) {
7897
const isActive = activePanelKeys.includes(panel.id);
7998
let isEmpty = true;
8099
if (isActive) {
81-
const contentDom = document.querySelector(
100+
const contentDom = select(
82101
`.${collapseContentClassName}[data-content='${panel.id}']`
83102
);
84103
isEmpty = !contentDom?.hasChildNodes();
@@ -90,15 +109,15 @@ export function Collapse(props: ICollapseProps) {
90109
filterData
91110
);
92111
_cachePosition.push([height, top]);
93-
const dom = document.querySelector<HTMLElement>(
112+
const dom = select<HTMLElement>(
94113
`.${collapseItemClassName}[data-content='${panel.id}']`
95114
);
96115
if (dom) {
97116
dom.style.height = `${height}px`;
98117
dom.style.top = `${top}px`;
99118
}
100119
});
101-
}, [activePanelKeys]);
120+
}, [filterData]);
102121

103122
const handleChangeCallback = (key: React.Key) => {
104123
const currentKeys = activePanelKeys.concat();
@@ -130,6 +149,53 @@ export function Collapse(props: ICollapseProps) {
130149
return null;
131150
};
132151

152+
/**
153+
* Returns the grow of data, or 1
154+
*/
155+
const getGrow = (data: DataBaseProps) => {
156+
if (typeof data.config?.grow === 'number') {
157+
return data.config.grow;
158+
} else {
159+
return 1;
160+
}
161+
};
162+
163+
/**
164+
* Returns the key whose panel is active and whose grow is 0
165+
*/
166+
const getZeroPanelsByKeys = (
167+
keys: React.Key[],
168+
panels: DataBaseProps[]
169+
) => {
170+
return keys.filter((key) => {
171+
const targetPanel = panels.find((panel) => panel.id === key);
172+
if (targetPanel) {
173+
return targetPanel.config?.grow === 0;
174+
}
175+
return false;
176+
});
177+
};
178+
179+
/**
180+
* Returns the collections of height
181+
*/
182+
const getContentHeightsByKeys = (data: React.Key[]) => {
183+
return data.map((key) => {
184+
const contentDom = select(
185+
`.${collapseContentClassName}[data-content='${key}']`
186+
);
187+
if (contentDom) {
188+
// border-top-width + border-bottom-width = 2
189+
const basisHeight =
190+
contentDom.getBoundingClientRect().height -
191+
2 +
192+
HEADER_HEIGTH;
193+
return basisHeight > 220 ? 220 : basisHeight;
194+
}
195+
return 0;
196+
});
197+
};
198+
133199
/**
134200
* Calculate the position of the panel in view
135201
* @param keys Current active keys
@@ -150,35 +216,79 @@ export function Collapse(props: ICollapseProps) {
150216
// the height of inactive panel or empty panel is a fixed value
151217
res[0] = HEADER_HEIGTH;
152218
} else {
153-
// total height
154-
const wrapperHeight =
155-
wrapper.current?.getBoundingClientRect().height ||
156-
_cacheWrapperHeight.current;
157-
_cacheWrapperHeight.current = wrapperHeight;
158-
// count active panels
159-
const activeCount = keys.length;
160-
// count the height for active panels
161-
const activePanelHeight =
162-
wrapperHeight - HEADER_HEIGTH * (panels.length - activeCount);
163-
// count the non-empty & active panels in active panels
164-
const nonEmptyAndActivePanels = keys.filter((key) => {
165-
const targetPanel = panels.find((panel) => panel.id === key);
166-
if (!targetPanel) {
167-
return false;
168-
} else if (typeof targetPanel._isEmpty === 'boolean') {
169-
return !targetPanel._isEmpty;
170-
} else {
171-
const content = renderPanels(panel, panel.renderPanel);
172-
return !!content;
219+
if (panel.config?.grow === 0) {
220+
// to get current panel content
221+
const contentDom = select(
222+
`.${collapseContentClassName}[data-content='${panel.id}']`
223+
);
224+
if (contentDom) {
225+
const height =
226+
contentDom.getBoundingClientRect().height +
227+
HEADER_HEIGTH;
228+
res[0] =
229+
height > MAX_GROW_HEIGHT ? MAX_GROW_HEIGHT : height;
173230
}
174-
});
231+
} else {
232+
// get the height of the wrapper
233+
let wrapperHeight =
234+
wrapper.current?.getBoundingClientRect().height ||
235+
_cacheWrapperHeight.current;
236+
_cacheWrapperHeight.current = wrapperHeight;
237+
// count active panels
238+
const activeCount = keys.length;
239+
const inactiveCount = panels.length - activeCount;
240+
// the height active panels can occupied
241+
wrapperHeight = wrapperHeight - HEADER_HEIGTH * inactiveCount;
242+
243+
// get grow-zero panels' heights
244+
const growZeroPanelsKeys = getZeroPanelsByKeys(keys, panels);
245+
const growZeroPanelsHeights = getContentHeightsByKeys(
246+
growZeroPanelsKeys
247+
);
175248

176-
// the height for active panels is divided equally by non-empty & active panels
177-
res[0] =
178-
(activePanelHeight -
249+
// the height grow-normal panels can occupied =
250+
// the height active panels can occupied -
251+
// each grow-zero panels' heights
252+
growZeroPanelsHeights.forEach((height) => {
253+
wrapperHeight -= height;
254+
});
255+
256+
// count the non-empty & active & non-grow-zero panels in active panels
257+
const nonEmptyAndActivePanels: DataBaseProps[] = [];
258+
const nonEmptyAndActivePanelKeys = keys.filter((key) => {
259+
const target = panels.find((p) => p.id === key);
260+
if (target) {
261+
if (getGrow(target) === 0) return false;
262+
if (typeof target._isEmpty === 'boolean') {
263+
!target._isEmpty &&
264+
nonEmptyAndActivePanels.push(target);
265+
return !target._isEmpty;
266+
}
267+
// In general, the following code will not be excuted
268+
const contentDom = select(
269+
`.${collapseContentClassName}[data-content='${panel.id}']`
270+
);
271+
return contentDom?.hasChildNodes();
272+
}
273+
return false;
274+
});
275+
276+
const growSum = nonEmptyAndActivePanels.reduce((pre, cur) => {
277+
return pre + getGrow(cur);
278+
}, 0);
279+
280+
const emptyAndActivePanelsHeights =
179281
HEADER_HEIGTH *
180-
(keys.length - nonEmptyAndActivePanels.length)) /
181-
nonEmptyAndActivePanels.length;
282+
(keys.length -
283+
growZeroPanelsKeys.length -
284+
nonEmptyAndActivePanelKeys.length);
285+
286+
// the height for grow-normal panels is divided by non-empty & active & grow-normal panels depends on grow number
287+
res[0] =
288+
((wrapperHeight - emptyAndActivePanelsHeights) *
289+
getGrow(panel)) /
290+
growSum;
291+
}
182292
}
183293

184294
// calculate top for current panel

src/extensions/editorTree/index.ts

+3
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,12 @@ export const ExtendsEditorTree: IExtension = {
99

1010
molecule.editorTree.onClose((tabId, groupId) => {
1111
molecule.editor.closeTab(tabId, groupId);
12+
molecule.explorer.forceUpdate();
1213
});
1314

1415
molecule.editorTree.onCloseOthers((tabItem, groupId) => {
1516
molecule.editor.closeOthers(tabItem, groupId);
17+
molecule.explorer.forceUpdate();
1618
});
1719

1820
molecule.editorTree.onCloseSaved((groupId) => {
@@ -28,6 +30,7 @@ export const ExtendsEditorTree: IExtension = {
2830
molecule.editor.closeAll(group.id!);
2931
});
3032
}
33+
molecule.explorer.forceUpdate();
3134
});
3235

3336
molecule.editorTree.onSaveAll((groupId) => {

src/extensions/folderTree/index.tsx

+1
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,7 @@ export const ExtendsFolderTree: IExtension = {
102102
} else {
103103
molecule.editor.open(tabData);
104104
}
105+
molecule.explorer.updateRender();
105106
}
106107
);
107108

src/model/workbench/explorer/explorer.tsx

+6
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,9 @@ export function builtInExplorerEditorPanel() {
111111
iconName: 'codicon-close-all',
112112
},
113113
],
114+
config: {
115+
grow: 0,
116+
},
114117
};
115118
}
116119

@@ -164,6 +167,9 @@ export function builtInExplorerFolderPanel() {
164167
iconName: 'codicon-collapse-all',
165168
},
166169
],
170+
config: {
171+
grow: 2,
172+
},
167173
};
168174
}
169175

0 commit comments

Comments
 (0)