77 */
88
99import React , { useState , useEffect } from 'react' ;
10- import { useDataScope , useSchemaContext } from '@object-ui/react' ;
10+ import { useDataScope , useSchemaContext , useNavigationOverlay } from '@object-ui/react' ;
1111import { ComponentRegistry } from '@object-ui/core' ;
12- import { cn , Card , CardContent } from '@object-ui/components' ;
13- import type { GalleryConfig } from '@object-ui/types' ;
12+ import { cn , Card , CardContent , NavigationOverlay } from '@object-ui/components' ;
13+ import type { GalleryConfig , ViewNavigationConfig } from '@object-ui/types' ;
1414
1515export interface ObjectGalleryProps {
1616 schema : {
@@ -20,6 +20,8 @@ export interface ObjectGalleryProps {
2020 data ?: Record < string , unknown > [ ] ;
2121 className ?: string ;
2222 gallery ?: GalleryConfig ;
23+ /** Navigation config for item click behavior */
24+ navigation ?: ViewNavigationConfig ;
2325 /** @deprecated Use gallery.coverField instead */
2426 imageField ?: string ;
2527 /** @deprecated Use gallery.titleField instead */
@@ -29,6 +31,8 @@ export interface ObjectGalleryProps {
2931 data ?: Record < string , unknown > [ ] ;
3032 dataSource ?: { find : ( name : string , query : unknown ) => Promise < unknown > } ;
3133 onCardClick ?: ( record : Record < string , unknown > ) => void ;
34+ /** Callback when a row/item is clicked (overrides NavigationConfig) */
35+ onRowClick ?: ( record : Record < string , unknown > ) => void ;
3236}
3337
3438const GRID_CLASSES : Record < NonNullable < GalleryConfig [ 'cardSize' ] > , string > = {
@@ -52,6 +56,13 @@ export const ObjectGallery: React.FC<ObjectGalleryProps> = (props) => {
5256 const [ fetchedData , setFetchedData ] = useState < Record < string , unknown > [ ] > ( [ ] ) ;
5357 const [ loading , setLoading ] = useState ( false ) ;
5458
59+ // --- NavigationConfig support ---
60+ const navigation = useNavigationOverlay ( {
61+ navigation : schema . navigation ,
62+ objectName : schema . objectName ,
63+ onRowClick : props . onRowClick ?? props . onCardClick ,
64+ } ) ;
65+
5566 // Resolve GalleryConfig with backwards-compatible fallbacks
5667 const gallery = schema . gallery ;
5768 const coverField = gallery ?. coverField ?? schema . imageField ?? 'image' ;
@@ -110,66 +121,84 @@ export const ObjectGallery: React.FC<ObjectGalleryProps> = (props) => {
110121 if ( ! items . length ) return < div className = "p-4 text-sm text-muted-foreground" > No items to display</ div > ;
111122
112123 return (
113- < div
114- className = { cn ( 'grid gap-4 p-4' , GRID_CLASSES [ cardSize ] , schema . className ) }
115- role = "list"
116- >
117- { items . map ( ( item , i ) => {
118- const id = ( item . _id ?? item . id ?? i ) as string | number ;
119- const title = String ( item [ titleField ] ?? 'Untitled' ) ;
120- const imageUrl = item [ coverField ] as string | undefined ;
121-
122- return (
123- < Card
124- key = { id }
125- role = "listitem"
126- className = { cn (
127- 'group overflow-hidden transition-all hover:shadow-md' ,
128- props . onCardClick && 'cursor-pointer' ,
129- ) }
130- onClick = { props . onCardClick ? ( ) => props . onCardClick ! ( item ) : undefined }
131- >
132- < div className = { cn ( 'w-full overflow-hidden bg-muted relative' , ASPECT_CLASSES [ cardSize ] ) } >
133- { imageUrl ? (
134- < img
135- src = { imageUrl }
136- alt = { title }
137- className = { cn (
138- 'h-full w-full transition-transform group-hover:scale-105' ,
139- coverFit === 'cover' && 'object-cover' ,
140- coverFit === 'contain' && 'object-contain' ,
141- ) }
142- />
143- ) : (
144- < div className = "flex h-full w-full items-center justify-center bg-secondary/50 text-muted-foreground" >
145- < span className = "text-4xl font-light opacity-20" >
146- { title [ 0 ] ?. toUpperCase ( ) }
124+ < >
125+ < div
126+ className = { cn ( 'grid gap-4 p-4' , GRID_CLASSES [ cardSize ] , schema . className ) }
127+ role = "list"
128+ >
129+ { items . map ( ( item , i ) => {
130+ const id = ( item . _id ?? item . id ?? i ) as string | number ;
131+ const title = String ( item [ titleField ] ?? 'Untitled' ) ;
132+ const imageUrl = item [ coverField ] as string | undefined ;
133+
134+ return (
135+ < Card
136+ key = { id }
137+ role = "listitem"
138+ className = { cn (
139+ 'group overflow-hidden transition-all hover:shadow-md' ,
140+ ( props . onCardClick || props . onRowClick || schema . navigation ) && 'cursor-pointer' ,
141+ ) }
142+ onClick = { ( ) => navigation . handleClick ( item ) }
143+ >
144+ < div className = { cn ( 'w-full overflow-hidden bg-muted relative' , ASPECT_CLASSES [ cardSize ] ) } >
145+ { imageUrl ? (
146+ < img
147+ src = { imageUrl }
148+ alt = { title }
149+ className = { cn (
150+ 'h-full w-full transition-transform group-hover:scale-105' ,
151+ coverFit === 'cover' && 'object-cover' ,
152+ coverFit === 'contain' && 'object-contain' ,
153+ ) }
154+ />
155+ ) : (
156+ < div className = "flex h-full w-full items-center justify-center bg-secondary/50 text-muted-foreground" >
157+ < span className = "text-4xl font-light opacity-20" >
158+ { title [ 0 ] ?. toUpperCase ( ) }
159+ </ span >
160+ </ div >
161+ ) }
162+ </ div >
163+ < CardContent className = "p-3 border-t" >
164+ < h3 className = "font-medium truncate text-sm" title = { title } >
165+ { title }
166+ </ h3 >
167+ { visibleFields && visibleFields . length > 0 && (
168+ < div className = "mt-1 space-y-0.5" >
169+ { visibleFields . map ( ( field ) => {
170+ const value = item [ field ] ;
171+ if ( value == null ) return null ;
172+ return (
173+ < p key = { field } className = "text-xs text-muted-foreground truncate" >
174+ { String ( value ) }
175+ </ p >
176+ ) ;
177+ } ) }
178+ </ div >
179+ ) }
180+ </ CardContent >
181+ </ Card >
182+ ) ;
183+ } ) }
184+ </ div >
185+ { navigation . isOverlay && (
186+ < NavigationOverlay { ...navigation } title = "Gallery Item" >
187+ { ( record ) => (
188+ < div className = "space-y-3" >
189+ { Object . entries ( record ) . map ( ( [ key , value ] ) => (
190+ < div key = { key } className = "flex flex-col" >
191+ < span className = "text-xs font-medium text-muted-foreground uppercase tracking-wide" >
192+ { key . replace ( / _ / g, ' ' ) }
147193 </ span >
194+ < span className = "text-sm" > { String ( value ?? '—' ) } </ span >
148195 </ div >
149- ) }
196+ ) ) }
150197 </ div >
151- < CardContent className = "p-3 border-t" >
152- < h3 className = "font-medium truncate text-sm" title = { title } >
153- { title }
154- </ h3 >
155- { visibleFields && visibleFields . length > 0 && (
156- < div className = "mt-1 space-y-0.5" >
157- { visibleFields . map ( ( field ) => {
158- const value = item [ field ] ;
159- if ( value == null ) return null ;
160- return (
161- < p key = { field } className = "text-xs text-muted-foreground truncate" >
162- { String ( value ) }
163- </ p >
164- ) ;
165- } ) }
166- </ div >
167- ) }
168- </ CardContent >
169- </ Card >
170- ) ;
171- } ) }
172- </ div >
198+ ) }
199+ </ NavigationOverlay >
200+ ) }
201+ </ >
173202 ) ;
174203} ;
175204
0 commit comments