1515 */
1616import type { ReactNode } from 'react' ;
1717
18- import { useState , useEffect , Fragment } from 'react' ;
18+ import { useState , useEffect , Fragment , useRef , useCallback } from 'react' ;
1919
2020import {
2121 CodeSnippet ,
@@ -34,7 +34,6 @@ import CloseIcon from '@mui/icons-material/Close';
3434import CircularProgress from '@mui/material/CircularProgress' ;
3535import CardContent from '@mui/material/CardContent' ;
3636import { useTheme , styled } from '@mui/material/styles' ;
37- import useMediaQuery from '@mui/material/useMediaQuery' ;
3837
3938import EntityCard from './EntityCard' ;
4039import { ViewMoreLink } from './ViewMoreLink' ;
@@ -46,6 +45,11 @@ import {
4645} from '../../utils/utils' ;
4746import { useTranslation } from '../../hooks/useTranslation' ;
4847import { Trans } from '../Trans' ;
48+ import { containerGridItemSx } from '../../utils/GridItem' ;
49+ import {
50+ useContainerQuery ,
51+ type ContainerSize ,
52+ } from '../../hooks/useContainerQuery' ;
4953
5054const StyledLink = styled ( BackstageLink ) ( ( { theme } ) => ( {
5155 textDecoration : 'none' ,
@@ -63,31 +67,44 @@ export const EntitySection = () => {
6367 const [ isRemoveFirstCard , setIsRemoveFirstCard ] = useState ( false ) ;
6468 const [ showDiscoveryCard , setShowDiscoveryCard ] = useState ( true ) ;
6569 const [ imgLoaded , setImgLoaded ] = useState ( false ) ;
66- const [ isMediumBreakpoint , setIsMediumBreakpoint ] = useState ( false ) ;
6770
68- const isMd = useMediaQuery ( theme . breakpoints . only ( 'md' ) ) ;
71+ const containerRef = useRef < HTMLDivElement > ( null ) ;
72+ const containerSize = useContainerQuery ( containerRef ) ;
6973
70- useEffect ( ( ) => {
71- if ( isMd ) {
72- setIsMediumBreakpoint ( true ) ;
73- } else {
74- setIsMediumBreakpoint ( false ) ;
75- }
76- } , [ isMd ] ) ;
74+ const entityCardCount =
75+ containerSize === 'xs' || containerSize === 'sm' ? 2 : 4 ;
76+
77+ const illustrationWidthMap : Partial < Record < ContainerSize , number > > = {
78+ md : 180 ,
79+ lg : 220 ,
80+ xl : 266 ,
81+ } ;
82+ const illustrationWidth = illustrationWidthMap [ containerSize ] ?? 266 ;
83+
84+ const visibleEntitiesCount = ( ( ) => {
85+ const isWide =
86+ containerSize === 'xl' ||
87+ containerSize === 'lg' ||
88+ containerSize === 'md' ;
89+
90+ if ( ! isWide ) return entityCardCount ;
91+
92+ return isRemoveFirstCard ? entityCardCount : entityCardCount - 2 ;
93+ } ) ( ) ;
7794
7895 useEffect ( ( ) => {
7996 const isUserDismissedEntityIllustration =
8097 hasEntityIllustrationUserDismissed ( displayName ) ;
8198 setIsRemoveFirstCard ( isUserDismissedEntityIllustration ) ;
8299 } , [ displayName ] ) ;
83100
84- const handleClose = ( ) => {
101+ const handleClose = useCallback ( ( ) => {
85102 setShowDiscoveryCard ( false ) ;
86103 setTimeout ( ( ) => {
87104 addDismissedEntityIllustrationUsers ( displayName ) ;
88105 setIsRemoveFirstCard ( true ) ;
89106 } , 500 ) ;
90- } ;
107+ } , [ displayName ] ) ;
91108
92109 const { data, error, isLoading } = useEntities ( {
93110 kind : [ 'Component' , 'API' , 'Resource' , 'System' ] ,
@@ -120,102 +137,121 @@ export const EntitySection = () => {
120137 </ WarningPanel >
121138 ) ;
122139 } else {
123- let entityCardCount = 2 ;
124- if ( isMediumBreakpoint ) entityCardCount = 3 ;
125-
126140 content = (
127141 < Box sx = { { padding : '8px 8px 8px 0' } } >
128142 < Fragment >
129143 < Grid container spacing = { 1 } alignItems = "stretch" >
130- { ! isRemoveFirstCard && ! profileLoading && (
131- < Grid item xs = { 12 } md = { 6 } lg = { 5 } key = "entities illustration" >
132- < Card
133- elevation = { 0 }
134- sx = { {
135- border : `1px solid ${ theme . palette . grey [ 400 ] } ` ,
136- display : 'flex' ,
137- flexDirection : 'row' ,
138- alignItems : 'center' ,
139- position : 'relative' ,
140- transition :
141- 'opacity 0.5s ease-out, transform 0.5s ease-in-out' ,
142- opacity : showDiscoveryCard ? 1 : 0 ,
143- transform : showDiscoveryCard
144- ? 'translateX(0)'
145- : 'translateX(-50px)' ,
146- } }
147- >
148- { ! imgLoaded && (
149- < Skeleton
150- variant = "rectangular"
151- height = { 300 }
152- sx = { {
153- borderRadius : 3 ,
154- width : 'clamp(140px, 14vw, 266px)' ,
155- } }
156- />
157- ) }
158- < Box
159- component = "img"
160- src = { HomePageEntityIllustration }
161- onLoad = { ( ) => setImgLoaded ( true ) }
162- alt = ""
163- height = { 300 }
144+ { /* hiding discovery card on small containers */ }
145+ { ! isRemoveFirstCard &&
146+ ! profileLoading &&
147+ containerSize !== 'xs' &&
148+ containerSize !== 'sm' && (
149+ < Grid item sx = { containerGridItemSx ( { md : 4 } ) } >
150+ < Card
151+ elevation = { 0 }
164152 sx = { {
165- width : 'clamp(140px, 14vw, 266px)' ,
153+ height : '100%' ,
154+ border : `1px solid ${ theme . palette . grey [ 400 ] } ` ,
155+ display : 'flex' ,
156+ flexDirection : 'row' ,
157+ alignItems : 'center' ,
158+ position : 'relative' ,
159+ transition :
160+ 'opacity 0.5s ease-out, transform 0.5s ease-in-out' ,
161+ opacity : showDiscoveryCard ? 1 : 0 ,
162+ transform : showDiscoveryCard
163+ ? 'translateX(0)'
164+ : 'translateX(-50px)' ,
166165 } }
167- />
168- < Box sx = { { p : 2 } } >
169- < Box >
170- < Typography variant = "body2" paragraph >
171- { t ( 'entities.description' ) }
172- </ Typography >
173- </ Box >
174- { entities ?. length > 0 && (
175- < IconButton
176- onClick = { handleClose }
177- aria-label = { t ( 'entities.close' ) }
178- style = { {
179- position : 'absolute' ,
180- top : '8px' ,
181- right : '8px' ,
166+ >
167+ < Box
168+ sx = { {
169+ display : 'flex' ,
170+ flexDirection : 'row' ,
171+ alignItems : 'center' ,
172+ } }
173+ >
174+ { ! imgLoaded && (
175+ < Skeleton
176+ variant = "rectangular"
177+ height = { 300 }
178+ sx = { {
179+ borderRadius : 3 ,
180+ width : illustrationWidth ,
181+ } }
182+ />
183+ ) }
184+ < Box
185+ component = "img"
186+ src = { HomePageEntityIllustration }
187+ onLoad = { ( ) => setImgLoaded ( true ) }
188+ alt = ""
189+ height = { 300 }
190+ sx = { {
191+ width : illustrationWidth ,
182192 } }
183- >
184- < CloseIcon style = { { width : '16px' , height : '16px' } } />
185- </ IconButton >
186- ) }
187- </ Box >
188- </ Card >
189- </ Grid >
190- ) }
191- { entities
192- ?. slice ( 0 , isRemoveFirstCard ? 4 : entityCardCount )
193- . map ( ( item : any ) => (
194- < Grid
195- item
196- xs = { 12 }
197- md = { 6 }
198- lg = { isRemoveFirstCard ? 3 : 3.5 }
199- key = { item . metadata . name }
200- >
201- < EntityCard
202- entity = { item }
203- title = { item . metadata . title ?? item . metadata . name }
204- version = "latest"
205- description = { item . metadata . description ?? '' }
206- tags = { item . metadata ?. tags ?? [ ] }
207- kind = { item . kind }
208- />
193+ />
194+ < Box sx = { { p : 2 } } >
195+ < Box sx = { { p : 2 } } >
196+ < Typography variant = "body2" paragraph >
197+ { t ( 'entities.description' ) }
198+ </ Typography >
199+ </ Box >
200+ { entities ?. length > 0 && (
201+ < IconButton
202+ onClick = { handleClose }
203+ aria-label = { t ( 'entities.close' ) }
204+ style = { {
205+ position : 'absolute' ,
206+ top : '8px' ,
207+ right : '8px' ,
208+ } }
209+ >
210+ < CloseIcon
211+ style = { { width : '16px' , height : '16px' } }
212+ />
213+ </ IconButton >
214+ ) }
215+ </ Box >
216+ </ Box >
217+ </ Card >
209218 </ Grid >
210- ) ) }
219+ ) }
220+ { entities ?. slice ( 0 , visibleEntitiesCount ) . map ( ( item : any ) => (
221+ < Grid
222+ item
223+ sx = { containerGridItemSx ( {
224+ xs : 12 ,
225+ sm : 6 ,
226+ md : isRemoveFirstCard ? 3 : 4 ,
227+ } ) }
228+ key = { item . metadata . name }
229+ >
230+ < EntityCard
231+ entity = { item }
232+ title = { item . metadata . title ?? item . metadata . name }
233+ version = "latest"
234+ description = { item . metadata . description ?? '' }
235+ tags = { item . metadata ?. tags ?? [ ] }
236+ kind = { item . kind }
237+ />
238+ </ Grid >
239+ ) ) }
211240 { entities ?. length === 0 && (
212- < Grid item md = { isRemoveFirstCard ? 12 : 7 } >
241+ < Grid
242+ item
243+ sx = { containerGridItemSx ( {
244+ sm : 12 ,
245+ md : isRemoveFirstCard ? 12 : 8 ,
246+ } ) }
247+ >
213248 < Box
214249 sx = { {
250+ height : '100%' ,
251+ minHeight : 300 ,
215252 display : 'flex' ,
216253 alignItems : 'center' ,
217254 justifyContent : 'center' ,
218- minHeight : 300 ,
219255 border : muiTheme =>
220256 `1px solid ${ muiTheme . palette . grey [ 400 ] } ` ,
221257 borderRadius : 3 ,
@@ -255,7 +291,9 @@ export const EntitySection = () => {
255291 sx = { {
256292 padding : '24px' ,
257293 border : muitheme => `1px solid ${ muitheme . palette . grey [ 300 ] } ` ,
258- overflow : 'auto' ,
294+ containerType : 'inline-size' ,
295+ display : 'flex' ,
296+ flexDirection : 'column' ,
259297 } }
260298 >
261299 < Typography
@@ -265,21 +303,32 @@ export const EntitySection = () => {
265303 alignItems : 'center' ,
266304 fontWeight : '500' ,
267305 fontSize : '1.5rem' ,
306+ flexShrink : 0 ,
268307 } }
269308 >
270309 { t ( 'entities.title' ) }
271310 </ Typography >
272- { content }
273- { entities ?. length > 0 && (
274- < Box sx = { { pt : 2 } } >
275- < ViewMoreLink to = "/catalog" >
276- < Trans
277- message = "entities.viewAll"
278- params = { { count : data ?. totalItems ?. toString ( ) || '' } }
279- />
280- </ ViewMoreLink >
281- </ Box >
282- ) }
311+ < Box
312+ ref = { containerRef }
313+ sx = { {
314+ flex : 1 ,
315+ minHeight : 0 ,
316+ overflowY : 'auto' ,
317+ mt : 1 ,
318+ } }
319+ >
320+ { content }
321+ { entities ?. length > 0 && (
322+ < Box sx = { { pt : 2 } } >
323+ < ViewMoreLink to = "/catalog" >
324+ < Trans
325+ message = "entities.viewAll"
326+ params = { { count : data ?. totalItems ?. toString ( ) || '' } }
327+ />
328+ </ ViewMoreLink >
329+ </ Box >
330+ ) }
331+ </ Box >
283332 </ Card >
284333 ) ;
285334} ;
0 commit comments