{ "$schema": "https://ui.shadcn.com/schema/registry-item.json", "name": "image-list", "type": "registry:ui", "title": "Image List", "description": "Image grid with per-item span and title bars.", "registryDependencies": [ "https://raw.githubusercontent.com/truongnat/rnui/main/registry/dist/nativewind/utils.json", "https://raw.githubusercontent.com/truongnat/rnui/main/registry/dist/nativewind/image.json", "https://raw.githubusercontent.com/truongnat/rnui/main/registry/dist/nativewind/text.json" ], "files": [ { "path": "registry/shared/ui/image-list.tsx", "type": "registry:ui", "target": "components/ui/image-list.tsx", "content": "import {\n createContext,\n useContext,\n useMemo,\n useState,\n type ComponentProps,\n type ReactNode,\n} from 'react';\nimport {\n useWindowDimensions,\n View,\n type ImageSourcePropType,\n type ViewProps,\n} from 'react-native';\nimport { Image } from '@/components/ui/image';\nimport { Text } from '@/components/ui/text';\nimport { cn } from '@/lib/utils';\n\ninterface ImageListContextValue {\n cols: number;\n gap: number;\n width: number;\n}\n\nconst ImageListContext = createContext({\n cols: 2,\n gap: 8,\n width: 0,\n});\n\nexport interface ImageListProps extends ViewProps {\n /** Number of grid columns. */\n cols?: number;\n /** Gap between items in px. */\n gap?: number;\n className?: string;\n children?: ReactNode;\n}\n\n/**\n * Simple flex-wrap image grid. Pair with `ImageListItem`, which\n * measures this container's width to size each tile.\n */\nexport function ImageList({\n cols = 2,\n gap = 8,\n className,\n style,\n onLayout,\n children,\n ...props\n}: ImageListProps) {\n const { width: windowWidth } = useWindowDimensions();\n const [width, setWidth] = useState(windowWidth);\n\n const ctx = useMemo(() => ({ cols, gap, width }), [cols, gap, width]);\n\n return (\n \n {\n const next = e.nativeEvent.layout.width;\n if (next !== width) setWidth(next);\n onLayout?.(e);\n }}\n {...props}\n >\n {children}\n \n \n );\n}\n\nexport interface ImageListItemProps extends ViewProps {\n /** How many columns this item spans. */\n span?: number;\n /** Width/height ratio of the tile. */\n aspectRatio?: number;\n /** When set, renders an filling the tile. */\n source?: ImageSourcePropType;\n /** Forwarded to the inner when `source` is set. */\n imageProps?: Omit<\n ComponentProps,\n 'source' | 'style' | 'className'\n >;\n className?: string;\n children?: ReactNode;\n}\n\nexport function ImageListItem({\n span = 1,\n aspectRatio = 1,\n source,\n imageProps,\n className,\n style,\n children,\n ...props\n}: ImageListItemProps) {\n const { cols, gap, width } = useContext(ImageListContext);\n\n const colWidth =\n cols > 0 && width > 0 ? (width - gap * (cols - 1)) / cols : 0;\n const itemWidth = span * colWidth + gap * (span - 1);\n\n return (\n 0 && { width: itemWidth }, { aspectRatio }, style]}\n {...props}\n >\n {source ? (\n \n ) : (\n children\n )}\n \n );\n}\n\nexport interface ImageListItemBarProps extends ViewProps {\n title?: ReactNode;\n subtitle?: ReactNode;\n /** Trailing element (e.g. an IconButton). */\n actionIcon?: ReactNode;\n position?: 'top' | 'bottom';\n className?: string;\n}\n\n/** Overlay caption bar for `ImageListItem`. */\nexport function ImageListItemBar({\n title,\n subtitle,\n actionIcon,\n position = 'bottom',\n className,\n ...props\n}: ImageListItemBarProps) {\n return (\n \n \n {title ? (\n \n {title}\n \n ) : null}\n {subtitle ? (\n \n {subtitle}\n \n ) : null}\n \n {actionIcon}\n \n );\n}\n" } ] }