@@ -12,6 +12,7 @@ import {
1212 CardHeader ,
1313 CardTitle ,
1414 CardContent ,
15+ Badge ,
1516 Button ,
1617 Input ,
1718} from '@object-ui/components' ;
@@ -24,6 +25,7 @@ import {
2425 ChevronLeft ,
2526 ChevronRight ,
2627 ArrowUpDown ,
28+ ChevronDown ,
2729} from 'lucide-react' ;
2830import type { DataSource , FieldMetadata } from '@object-ui/types' ;
2931import { getCellRenderer } from '@object-ui/fields' ;
@@ -55,6 +57,10 @@ export interface RelatedListProps {
5557 sortable ?: boolean ;
5658 /** Enable text filtering */
5759 filterable ?: boolean ;
60+ /** Whether the card is collapsible */
61+ collapsible ?: boolean ;
62+ /** Whether the card starts collapsed (requires collapsible=true) */
63+ defaultCollapsed ?: boolean ;
5864}
5965
6066export const RelatedList : React . FC < RelatedListProps > = ( {
@@ -74,6 +80,8 @@ export const RelatedList: React.FC<RelatedListProps> = ({
7480 pageSize,
7581 sortable = false ,
7682 filterable = false ,
83+ collapsible = false ,
84+ defaultCollapsed = false ,
7785} ) => {
7886 const [ relatedData , setRelatedData ] = React . useState ( data ) ;
7987 const [ loading , setLoading ] = React . useState ( false ) ;
@@ -82,6 +90,7 @@ export const RelatedList: React.FC<RelatedListProps> = ({
8290 const [ sortDirection , setSortDirection ] = React . useState < 'asc' | 'desc' > ( 'asc' ) ;
8391 const [ filterText , setFilterText ] = React . useState ( '' ) ;
8492 const [ objectSchema , setObjectSchema ] = React . useState < any > ( null ) ;
93+ const [ collapsed , setCollapsed ] = React . useState ( defaultCollapsed ) ;
8594 const { t } = useDetailTranslation ( ) ;
8695 const { fieldLabel : resolveFieldLabel } = useSafeFieldLabel ( ) ;
8796
@@ -245,39 +254,43 @@ export const RelatedList: React.FC<RelatedListProps> = ({
245254 }
246255 } , [ type , paginatedData , effectiveColumns , schema , effectivePageSize ] ) ;
247256
248- const recordCountText = relatedData . length === 1
249- ? t ( 'detail.relatedRecordOne' , { count : relatedData . length } )
250- : t ( 'detail.relatedRecords' , { count : relatedData . length } ) ;
251-
252257 const hasRowActions = ! ! onRowEdit || ! ! onRowDelete ;
253258
259+ const headerClassName = collapsible ? 'cursor-pointer select-none' : undefined ;
260+ const handleHeaderClick = collapsible ? ( ) => setCollapsed ( ( c ) => ! c ) : undefined ;
261+
254262 return (
255263 < Card className = { className } >
256- < CardHeader >
264+ < CardHeader className = { headerClassName } onClick = { handleHeaderClick } >
257265 < CardTitle className = "flex items-center justify-between" >
258266 < div className = "flex items-center gap-2" >
267+ { collapsible && (
268+ collapsed
269+ ? ( < ChevronRight className = "h-4 w-4 text-muted-foreground" /> )
270+ : ( < ChevronDown className = "h-4 w-4 text-muted-foreground" /> )
271+ ) }
259272 < span > { title } </ span >
260- < span className = "text-sm font-normal text-muted-foreground" >
261- { recordCountText }
262- </ span >
273+ < Badge variant = "secondary" className = "text-xs font-normal" aria-label = { ` ${ relatedData . length } records` } >
274+ { relatedData . length }
275+ </ Badge >
263276 </ div >
264277 < div className = "flex items-center gap-1" >
265278 { onNew && (
266- < Button variant = "ghost" size = "sm" onClick = { onNew } className = "gap-1 h-7 text-xs" >
279+ < Button variant = "ghost" size = "sm" onClick = { ( e ) => { e . stopPropagation ( ) ; onNew ( ) ; } } className = "gap-1 h-7 text-xs" >
267280 < Plus className = "h-3.5 w-3.5" />
268281 { t ( 'detail.new' ) }
269282 </ Button >
270283 ) }
271284 { onViewAll && (
272- < Button variant = "ghost" size = "sm" onClick = { onViewAll } className = "gap-1 h-7 text-xs" >
285+ < Button variant = "ghost" size = "sm" onClick = { ( e ) => { e . stopPropagation ( ) ; onViewAll ( ) ; } } className = "gap-1 h-7 text-xs" >
273286 { t ( 'detail.viewAll' ) }
274287 < ExternalLink className = "h-3 w-3" />
275288 </ Button >
276289 ) }
277290 </ div >
278291 </ CardTitle >
279292 </ CardHeader >
280- < CardContent >
293+ { ! collapsed && < CardContent >
281294 { /* Filter bar */ }
282295 { filterable && relatedData . length > 0 && (
283296 < div className = "mb-3" >
@@ -394,7 +407,7 @@ export const RelatedList: React.FC<RelatedListProps> = ({
394407 </ Button >
395408 </ div >
396409 ) }
397- </ CardContent >
410+ </ CardContent > }
398411 </ Card >
399412 ) ;
400413} ;
0 commit comments