@@ -15,7 +15,7 @@ import { NameGenerator } from "comps/utils";
15
15
import { ScrollBar , Section , sectionNames } from "lowcoder-design" ;
16
16
import { HintPlaceHolder } from "lowcoder-design" ;
17
17
import _ from "lodash" ;
18
- import React , { useContext , useEffect , useState } from "react" ;
18
+ import React , { useContext , useMemo } from "react" ;
19
19
import styled , { css } from "styled-components" ;
20
20
import { IContainer } from "../containerBase/iContainer" ;
21
21
import { SimpleContainerComp } from "../containerBase/simpleContainerComp" ;
@@ -47,10 +47,9 @@ const EVENT_OPTIONS = [
47
47
] as const ;
48
48
49
49
const TAB_BEHAVIOR_OPTIONS = [
50
- { label : "Lazy Loading" , value : "lazy" } ,
51
- { label : "Remember State" , value : "remember" } ,
52
- { label : "Destroy Inactive" , value : "destroy" } ,
53
- { label : "Keep Alive (render all)" , value : "keep-alive" } ,
50
+ { label : trans ( "tabbedContainer.tabBehaviorLazy" ) , value : "lazy" } ,
51
+ { label : trans ( "tabbedContainer.tabBehaviorKeepAlive" ) , value : "keep-alive" } ,
52
+ { label : trans ( "tabbedContainer.tabBehaviorDestroy" ) , value : "destroy" } ,
54
53
] as const ;
55
54
56
55
const TabBehaviorControl = dropdownControl ( TAB_BEHAVIOR_OPTIONS , "lazy" ) ;
@@ -153,7 +152,8 @@ const StyledTabs = styled(Tabs)<{
153
152
$bodyStyle : TabBodyStyleType ;
154
153
$isMobile ?: boolean ;
155
154
$showHeader ?: boolean ;
156
- $animationStyle :AnimationStyleType
155
+ $animationStyle :AnimationStyleType ;
156
+ $isDestroyPane ?: boolean ;
157
157
} > `
158
158
&.ant-tabs {
159
159
height: 100%;
@@ -166,7 +166,6 @@ const StyledTabs = styled(Tabs)<{
166
166
167
167
.ant-tabs-content {
168
168
height: 100%;
169
-
170
169
}
171
170
172
171
.ant-tabs-nav {
@@ -183,16 +182,71 @@ const StyledTabs = styled(Tabs)<{
183
182
margin-right: -24px;
184
183
}
185
184
186
- ${ ( props ) => props . $style && getStyle (
187
- props . $style ,
188
- props . $headerStyle ,
189
- props . $bodyStyle ,
190
- ) }
185
+ ${ ( props ) =>
186
+ props . $style && getStyle ( props . $style , props . $headerStyle , props . $bodyStyle ) }
187
+
188
+ /* Conditional styling for all modes except Destroy Inactive Pane */
189
+ ${ ( props ) => ! props . $isDestroyPane && `
190
+ .ant-tabs-content-holder { position: relative; }
191
+
192
+ .ant-tabs-tabpane[aria-hidden="true"],
193
+ .ant-tabs-tabpane-hidden {
194
+ display: block !important;
195
+ visibility: hidden !important;
196
+ position: absolute !important;
197
+ inset: 0;
198
+ pointer-events: none;
199
+ }
200
+ ` }
191
201
` ;
192
202
193
203
const ContainerInTab = ( props : ContainerBaseProps ) => {
204
+ return < InnerGrid { ...props } emptyRows = { 15 } hintPlaceholder = { HintPlaceHolder } /> ;
205
+ } ;
206
+
207
+ type TabPaneContentProps = {
208
+ autoHeight : boolean ;
209
+ showVerticalScrollbar : boolean ;
210
+ paddingWidth : number ;
211
+ horizontalGridCells : number ;
212
+ bodyBackground : string ;
213
+ layoutView : any ;
214
+ itemsView : any ;
215
+ positionParamsView : any ;
216
+ dispatch : DispatchType ;
217
+ } ;
218
+
219
+ const TabPaneContent : React . FC < TabPaneContentProps > = ( {
220
+ autoHeight,
221
+ showVerticalScrollbar,
222
+ paddingWidth,
223
+ horizontalGridCells,
224
+ bodyBackground,
225
+ layoutView,
226
+ itemsView,
227
+ positionParamsView,
228
+ dispatch,
229
+ } ) => {
230
+ const gridItems = useMemo ( ( ) => gridItemCompToGridItems ( itemsView ) , [ itemsView ] ) ;
231
+
194
232
return (
195
- < InnerGrid { ...props } emptyRows = { 15 } hintPlaceholder = { HintPlaceHolder } />
233
+ < BackgroundColorContext . Provider value = { bodyBackground } >
234
+ < ScrollBar
235
+ style = { { height : autoHeight ? "auto" : "100%" , margin : "0px" , padding : "0px" } }
236
+ hideScrollbar = { ! showVerticalScrollbar }
237
+ overflow = { autoHeight ? "hidden" : "scroll" }
238
+ >
239
+ < ContainerInTab
240
+ layout = { layoutView }
241
+ items = { gridItems }
242
+ horizontalGridCells = { horizontalGridCells }
243
+ positionParams = { positionParamsView }
244
+ dispatch = { dispatch }
245
+ autoHeight = { autoHeight }
246
+ containerPadding = { [ paddingWidth , 20 ] }
247
+ />
248
+ </ ScrollBar >
249
+ </ BackgroundColorContext . Provider >
196
250
) ;
197
251
} ;
198
252
@@ -212,13 +266,6 @@ const TabbedContainer = (props: TabbedContainerProps) => {
212
266
const selectedTab = visibleTabs . find ( ( tab ) => tab . key === props . selectedTabKey . value ) ;
213
267
const activeKey = selectedTab ? selectedTab . key : visibleTabs . length > 0 ? visibleTabs [ 0 ] . key : undefined ;
214
268
215
- // Placeholder-based lazy loading — only for "lazy" mode
216
- const [ loadedTabs , setLoadedTabs ] = useState < Set < string > > ( new Set ( ) ) ;
217
- useEffect ( ( ) => {
218
- if ( tabBehavior === "lazy" && activeKey ) {
219
- setLoadedTabs ( ( prev : Set < string > ) => new Set ( [ ...prev , activeKey ] ) ) ;
220
- }
221
- } , [ tabBehavior , activeKey ] ) ;
222
269
223
270
const editorState = useContext ( EditorContext ) ;
224
271
const maxWidth = editorState . getAppSettings ( ) . maxWidth ;
@@ -229,7 +276,7 @@ const TabbedContainer = (props: TabbedContainerProps) => {
229
276
const tabItems = visibleTabs . map ( ( tab ) => {
230
277
const id = String ( tab . id ) ;
231
278
const childDispatch = wrapDispatch ( wrapDispatch ( dispatch , "containers" ) , id ) ;
232
- const containerProps = containers [ id ] . children ;
279
+ const containerChildren = containers [ id ] . children ;
233
280
const hasIcon = tab . icon . props . value ;
234
281
235
282
const label = (
@@ -240,50 +287,25 @@ const TabbedContainer = (props: TabbedContainerProps) => {
240
287
</ >
241
288
) ;
242
289
243
- // Item-level forceRender mapping
244
- const forceRender : boolean = tabBehavior === "keep-alive" ;
245
-
246
- // Render content (placeholder only for "lazy" & not yet opened)
247
- const renderTabContent = ( ) => {
248
- if ( tabBehavior === "lazy" && ! loadedTabs . has ( tab . key ) ) {
249
- return (
250
- < div
251
- style = { {
252
- display : "flex" ,
253
- justifyContent : "center" ,
254
- alignItems : "center" ,
255
- height : "200px" ,
256
- color : "#999" ,
257
- fontSize : "14px" ,
258
- } }
259
- >
260
- Click to load tab content
261
- </ div >
262
- ) ;
263
- }
264
-
265
- return (
266
- < BackgroundColorContext . Provider value = { bodyStyle . background } >
267
- < ScrollBar style = { { height : props . autoHeight ? "auto" : "100%" , margin : "0px" , padding : "0px" } } hideScrollbar = { ! props . showVerticalScrollbar } overflow = { props . autoHeight ? 'hidden' :'scroll' } >
268
- < ContainerInTab
269
- layout = { containerProps . layout . getView ( ) }
270
- items = { gridItemCompToGridItems ( containerProps . items . getView ( ) ) }
271
- horizontalGridCells = { horizontalGridCells }
272
- positionParams = { containerProps . positionParams . getView ( ) }
273
- dispatch = { childDispatch }
274
- autoHeight = { props . autoHeight }
275
- containerPadding = { [ paddingWidth , 20 ] }
276
- />
277
- </ ScrollBar >
278
- </ BackgroundColorContext . Provider >
279
- ) ;
280
- } ;
290
+ const forceRender = tabBehavior === "keep-alive" ;
281
291
282
292
return {
283
293
label,
284
294
key : tab . key ,
285
- forceRender, // true only for keep-alive
286
- children : renderTabContent ( ) ,
295
+ forceRender,
296
+ children : (
297
+ < TabPaneContent
298
+ autoHeight = { props . autoHeight }
299
+ showVerticalScrollbar = { props . showVerticalScrollbar }
300
+ paddingWidth = { paddingWidth }
301
+ horizontalGridCells = { horizontalGridCells }
302
+ bodyBackground = { bodyStyle . background }
303
+ layoutView = { containerChildren . layout . getView ( ) }
304
+ itemsView = { containerChildren . items . getView ( ) }
305
+ positionParamsView = { containerChildren . positionParams . getView ( ) }
306
+ dispatch = { childDispatch }
307
+ />
308
+ ) ,
287
309
} ;
288
310
} ) ;
289
311
@@ -299,13 +321,11 @@ const TabbedContainer = (props: TabbedContainerProps) => {
299
321
$headerStyle = { headerStyle }
300
322
$bodyStyle = { bodyStyle }
301
323
$showHeader = { showHeader }
324
+ $isDestroyPane = { tabBehavior === "destroy" }
302
325
onChange = { ( key ) => {
303
326
if ( key !== props . selectedTabKey . value ) {
304
327
props . selectedTabKey . onChange ( key ) ;
305
328
props . onEvent ( "change" ) ;
306
- if ( tabBehavior === "lazy" ) {
307
- setLoadedTabs ( ( prev : Set < string > ) => new Set ( [ ...prev , key ] ) ) ;
308
- }
309
329
}
310
330
} }
311
331
animated
@@ -344,7 +364,25 @@ export const TabbedContainerBaseComp = (function () {
344
364
{ disabledPropertyView ( children ) }
345
365
{ hiddenPropertyView ( children ) }
346
366
{ children . showHeader . propertyView ( { label : trans ( "tabbedContainer.showTabs" ) } ) }
347
- { children . tabBehavior . propertyView ( { label : "Tab Behavior" } ) }
367
+ { children . tabBehavior . propertyView ( {
368
+ label : trans ( "tabbedContainer.tabBehavior" ) ,
369
+ tooltip : (
370
+ < div style = { { display : "flex" , flexDirection : "column" , gap : 6 } } >
371
+ < div >
372
+ < b > { trans ( "tabbedContainer.tabBehaviorLazy" ) } :</ b >
373
+ { trans ( "tabbedContainer.tabBehaviorLazyTooltip" ) }
374
+ </ div >
375
+ < div >
376
+ < b > { trans ( "tabbedContainer.tabBehaviorKeepAlive" ) } :</ b >
377
+ { trans ( "tabbedContainer.tabBehaviorKeepAliveTooltip" ) }
378
+ </ div >
379
+ < div >
380
+ < b > { trans ( "tabbedContainer.tabBehaviorDestroy" ) } :</ b >
381
+ { trans ( "tabbedContainer.tabBehaviorDestroyTooltip" ) }
382
+ </ div >
383
+ </ div >
384
+ ) ,
385
+ } ) }
348
386
</ Section >
349
387
) }
350
388
@@ -435,6 +473,7 @@ class TabbedContainerImplComp extends TabbedContainerBaseComp implements IContai
435
473
return this ;
436
474
}
437
475
}
476
+
438
477
let newInstance = super . reduce ( action ) ;
439
478
if ( action . type === CompActionTypes . UPDATE_NODES_V2 ) {
440
479
// Need eval to get the value in StringControl
@@ -489,4 +528,3 @@ export const TabbedContainerComp = withExposingConfigs(TabbedContainerImplComp,
489
528
new NameConfig ( "selectedTabKey" , trans ( "tabbedContainer.selectedTabKeyDesc" ) ) ,
490
529
NameConfigHidden ,
491
530
] ) ;
492
-
0 commit comments