Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
68 changes: 40 additions & 28 deletions examples/astro-instagram-demo/src/react-pages/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,15 +36,19 @@ export default function IndexPage(props: {
<InstagramFeed.InstagramMedias>
<div className="grid grid-cols-2 md:grid-cols-3 lg:grid-cols-4 gap-4">
<InstagramFeed.InstagramMediaRepeater>
<MediaGallery.Viewport asChild>
{({ src, alt }) => (
<img
src={src}
alt={alt || ''}
className="w-full h-full object-cover cursor-pointer"
/>
)}
</MediaGallery.Viewport>
<InstagramMedia.MediaGalleries>
<InstagramMedia.MediaGalleryRepeater>
<MediaGallery.Viewport asChild>
{({ src, alt }) => (
<img
src={src}
alt={alt || ''}
className="w-full h-full object-cover cursor-pointer"
/>
)}
</MediaGallery.Viewport>
</InstagramMedia.MediaGalleryRepeater>
</InstagramMedia.MediaGalleries>
</InstagramFeed.InstagramMediaRepeater>
</div>
</InstagramFeed.InstagramMedias>
Expand All @@ -61,17 +65,21 @@ export default function IndexPage(props: {
<div className="flex min-h-[500px]">
{/* Left Side - Image */}
<div className="flex-1 bg-gray-900 flex items-center justify-center">
<div className="relative w-full h-full flex items-center justify-center">
<MediaGallery.Viewport asChild>
{({ src, alt }) => (
<img
src={src}
alt={alt || ''}
className="max-w-full max-h-full object-contain"
/>
)}
</MediaGallery.Viewport>
</div>
<InstagramMedia.MediaGalleries>
<InstagramMedia.MediaGalleryRepeater>
<div className="relative w-full h-full flex items-center justify-center">
<MediaGallery.Viewport asChild>
{({ src, alt }) => (
<img
src={src}
alt={alt || ''}
className="max-w-full max-h-full object-contain"
/>
)}
</MediaGallery.Viewport>
</div>
</InstagramMedia.MediaGalleryRepeater>
</InstagramMedia.MediaGalleries>
</div>

{/* Right Side - Content */}
Expand All @@ -90,14 +98,18 @@ export default function IndexPage(props: {
</div>
<div className="p-6 border-b border-gray-200 flex-1">
<div className="flex items-center justify-between mb-6">
<div className="flex items-center space-x-6">
<MediaGallery.Previous className="text-gray-600 hover:text-gray-800 text-2xl cursor-pointer">
</MediaGallery.Previous>
<MediaGallery.Next className="text-gray-600 hover:text-gray-800 text-2xl cursor-pointer">
</MediaGallery.Next>
</div>
<InstagramMedia.MediaGalleries>
<InstagramMedia.MediaGalleryRepeater>
<div className="flex items-center space-x-6">
<MediaGallery.Previous className="text-gray-600 hover:text-gray-800 text-2xl cursor-pointer">
</MediaGallery.Previous>
<MediaGallery.Next className="text-gray-600 hover:text-gray-800 text-2xl cursor-pointer">
</MediaGallery.Next>
</div>
</InstagramMedia.MediaGalleryRepeater>
</InstagramMedia.MediaGalleries>
</div>

<div className="text-xl font-medium text-gray-900 mb-8">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,40 @@ export const Timestamp = React.forwardRef<HTMLElement, TimestampProps>(
);


export interface MediaGalleryRepeaterProps {
children: React.ReactNode;
}

/**
* Repeater component that renders children for each media gallery item.
* This follows the Repeater Level pattern and works within an existing MediaGallery.Root context.
* Just passes through children - does not create MediaGallery.Root components.
*
* @component
*/
export const MediaGalleryRepeater = React.forwardRef<HTMLElement, MediaGalleryRepeaterProps>(
(props, _ref) => {
const { children } = props;
// Simply pass through children - MediaGallery.Root is created at a higher level
return <>{children}</>;
},
);

export interface MediaGalleryItemsProps {
children: React.ReactNode;
className?: string;
}

export const MediaGalleryItems = React.forwardRef<
HTMLDivElement,
MediaGalleryItemsProps
>(({ children, className, ...otherProps }, ref) => {
return (
<div ref={ref} className={className} {...otherProps}>
{children}
</div>
);
});

export const InstagramMedia = {
Root: ({ children }: { children: React.ReactNode }) => <>{children}</>,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,16 +35,7 @@ export const InstagramMediaRepeater: React.FC<InstagramMediaRepeaterProps> = ({
)}
>
<MediaGallery.Root
mediaGalleryServiceConfig={{
media: [
{
image: (mediaItem.type === 'video'
? (mediaItem.thumbnailUrl || mediaItem.mediaUrl)
: mediaItem.mediaUrl) || '',
altText: mediaItem.altText || '',
}
].filter(item => item.image) // Only include items with valid image URLs
}}
mediaGalleryServiceConfig={{ media: mediaItem.mediaGalleryItems }}
>
{children}
</MediaGallery.Root>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -132,30 +132,4 @@ export interface MediaGalleryRepeaterRenderProps {
hasMedia: boolean;
}

/**
* Headless component for Instagram media gallery repeater
*/
export function MediaGalleryRepeater(props: MediaGalleryRepeaterProps) {
const mediaItemService = useService(InstagramMediaItemServiceDefinition);
const mediaItem = mediaItemService.mediaItem.get();

const media: MediaItem[] = (
mediaItem?.type === 'video' ? mediaItem.thumbnailUrl : mediaItem?.mediaUrl
)
? [
{
image: (mediaItem.type === 'video'
? mediaItem.thumbnailUrl!
: mediaItem.mediaUrl) as string,
altText: mediaItem.altText,
},
]
: [];

const hasMedia = media.length > 0;

return props.children({
media,
hasMedia,
});
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,15 +20,50 @@ export interface InstagramMediasRenderProps {
/**
* Headless component for Instagram medias
* Handles service logic and provides render props with media items data
* Converts Instagram media items to MediaGallery format
*/
export function InstagramMedias(props: InstagramMediasProps) {
const instagramFeedService = useService(InstagramFeedServiceDefinition);
const feedData = instagramFeedService.feedData.get();

const hasItems = feedData.mediaItems.length > 0;

// Convert Instagram media items to MediaGallery format
const convertedMediaItems = feedData.mediaItems.map((mediaItem: any) => {
let mediaGalleryItems: any[] = [];

if (mediaItem.type === 'carousel' && mediaItem.children && mediaItem.children.length > 0) {
// Convert all carousel children to MediaGallery format
mediaGalleryItems = mediaItem.children.map((carouselItem: any, carouselIndex: number) => {
const imageUrl = carouselItem.type === 'video'
? carouselItem.thumbnailUrl || carouselItem.mediaUrl
: carouselItem.mediaUrl;

return {
image: imageUrl,
altText: carouselItem.altText || mediaItem.caption || `Instagram carousel item ${carouselIndex + 1}`,
};
});
} else {
// Convert single Instagram media item to MediaGallery format
const imageUrl = mediaItem.type === 'video'
? mediaItem.thumbnailUrl || mediaItem.mediaUrl
: mediaItem.mediaUrl;

mediaGalleryItems = [{
image: imageUrl,
altText: mediaItem.altText || mediaItem.caption || `Instagram ${mediaItem.type}`,
}];
}

return {// Keep original Instagram data
...mediaItem,
mediaGalleryItems, // Add converted MediaGallery data
};
});

return props.children({
hasItems,
mediaItems: feedData.mediaItems,
mediaItems: convertedMediaItems,
});
}
Loading