@@ -14,9 +14,9 @@ import {
14
14
collapseContentClassName ,
15
15
} from './base' ;
16
16
import { Scrollable } from '../scrollable' ;
17
+ import { select } from 'mo/common/dom' ;
17
18
18
19
type RenderFunctionProps = ( data : DataBaseProps ) => React . ReactNode ;
19
-
20
20
interface DataBaseProps {
21
21
id : React . Key ;
22
22
name : string ;
@@ -25,6 +25,17 @@ interface DataBaseProps {
25
25
toolbar ?: IActionBarItemProps [ ] ;
26
26
renderPanel ?: RenderFunctionProps ;
27
27
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
+
28
39
[ key : string ] : any ;
29
40
}
30
41
@@ -40,6 +51,10 @@ export interface ICollapseProps {
40
51
41
52
// default collapse height, only contains header
42
53
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 ;
43
58
44
59
export function Collapse ( props : ICollapseProps ) {
45
60
const [ activePanelKeys , setActivePanelKeys ] = useState < React . Key [ ] > ( [ ] ) ;
@@ -53,9 +68,13 @@ export function Collapse(props: ICollapseProps) {
53
68
...restProps
54
69
} = props ;
55
70
71
+ const visibleData = data . filter ( ( d ) => ! d . hidden ) ;
72
+
56
73
// 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 ) {
59
78
Logger . warn ( new SyntaxError ( 'collapse data must have id' ) ) ;
60
79
}
61
80
@@ -78,7 +97,7 @@ export function Collapse(props: ICollapseProps) {
78
97
const isActive = activePanelKeys . includes ( panel . id ) ;
79
98
let isEmpty = true ;
80
99
if ( isActive ) {
81
- const contentDom = document . querySelector (
100
+ const contentDom = select (
82
101
`.${ collapseContentClassName } [data-content='${ panel . id } ']`
83
102
) ;
84
103
isEmpty = ! contentDom ?. hasChildNodes ( ) ;
@@ -90,15 +109,15 @@ export function Collapse(props: ICollapseProps) {
90
109
filterData
91
110
) ;
92
111
_cachePosition . push ( [ height , top ] ) ;
93
- const dom = document . querySelector < HTMLElement > (
112
+ const dom = select < HTMLElement > (
94
113
`.${ collapseItemClassName } [data-content='${ panel . id } ']`
95
114
) ;
96
115
if ( dom ) {
97
116
dom . style . height = `${ height } px` ;
98
117
dom . style . top = `${ top } px` ;
99
118
}
100
119
} ) ;
101
- } , [ activePanelKeys ] ) ;
120
+ } , [ filterData ] ) ;
102
121
103
122
const handleChangeCallback = ( key : React . Key ) => {
104
123
const currentKeys = activePanelKeys . concat ( ) ;
@@ -130,6 +149,53 @@ export function Collapse(props: ICollapseProps) {
130
149
return null ;
131
150
} ;
132
151
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
+
133
199
/**
134
200
* Calculate the position of the panel in view
135
201
* @param keys Current active keys
@@ -150,35 +216,79 @@ export function Collapse(props: ICollapseProps) {
150
216
// the height of inactive panel or empty panel is a fixed value
151
217
res [ 0 ] = HEADER_HEIGTH ;
152
218
} 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 ;
173
230
}
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
+ ) ;
175
248
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 =
179
281
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
+ }
182
292
}
183
293
184
294
// calculate top for current panel
0 commit comments