Grid to Player
A profile page or a search result shows posts as a grid of thumbnails. Tapping one should open it full screen, let the viewer swipe on through the rest, and, when they close the player, leave them at the post they stopped at rather than the one they started from.
Try it
Every version imports ./api, which stands in for your backend: posts is the list a profile or a search would load, in the shape of the player's ContentItem, and thumbnailOf picks the picture a grid cell shows. Replace both with your own data.
import { cdnUrl } from '@reelkit/example-data';
// Stands in for your backend: the posts a profile or a search result would
// load. Each one has the shape of `ContentItem` from the reel player of your
// framework, so the list goes to the player as it is.
export interface Post {
id: string;
media: {
id: string;
type: 'image' | 'video';
src: string;
poster?: string;
aspectRatio: number;
}[];
author: { name: string; avatar: string };
likes: number;
description: string;
}
const kAuthors = ['Alex Johnson', 'Sarah Miller', 'Mike Chen', 'Emma Davis'];
const pad = (n: number) => String(n).padStart(2, '0');
export const posts: Post[] = Array.from({ length: 12 }, (_, i) => {
const isVideo = i % 4 === 0;
const n = (i % (isVideo ? 3 : 9)) + 1;
return {
id: `post-${i + 1}`,
media: [
isVideo
? {
id: `video-${i + 1}`,
type: 'video' as const,
src: cdnUrl(`samples/videos/video-${pad(n)}.mp4`),
poster: cdnUrl(`samples/videos/video-poster-${pad(n)}.jpg`),
aspectRatio: 16 / 9,
}
: {
id: `image-${i + 1}`,
type: 'image' as const,
src: cdnUrl(`samples/images/image-${pad(n)}.jpg`),
aspectRatio: 2 / 3,
},
],
author: {
name: kAuthors[i % kAuthors.length],
avatar: cdnUrl(`samples/avatars/avatar-${pad((i % 8) + 1)}.jpg`),
},
likes: 120 + i * 37,
description: `Post ${i + 1}`,
};
});
/** The picture a grid cell shows: a video's poster, or the image itself. */
export const thumbnailOf = (post: Post) =>
post.media[0].poster ?? post.media[0].src;The sample media works only on reelkit.dev
cdnUrl builds links to cdn.reelkit.dev, which serves its files only to the ReelKit sites. In your app those images and videos fail to load, so point media and avatar at your own files before you copy this file.
Open a post, swipe or press ↓ a few times, then close with Escape: the grid scrolls to the post you stopped at and its thumbnail has focus.
Why it works
- The player opens where the viewer tapped.
initialIndextakes the index of the thumbnail, and the whole list goes tocontent, so the viewer can keep swiping past the post they chose. Both are props of the player in React, Vue, and Angular. - The grid knows where the viewer stopped. The slide change callback reports every post the viewer reaches, and the recipe keeps the last one. It is listed with the player's callbacks, events, and outputs.
- Focus comes back to the right thumbnail. As it closes, the player returns focus to the element that opened it, which is its accessibility contract. That return happens after the close handler has run, so focus set inside the handler would be undone. The recipe waits one task, then scrolls the last post's thumbnail into view and focuses it.
- Thumbnails are buttons. Each cell is a
<button>with a label, so the grid works with Tab and Enter, a screen reader names the post it opens, and the player has a focusable element to hand focus back to, as its accessibility section describes. - A grid cell needs a picture, not a video.
thumbnailOfuses a video'sposter, a field of MediaItem, and an image as it is, so the grid never loads a video it does not play. - Want the open post in the address bar? The URL state player puts the visible post in the URL, so it can be shared and closed with the back button.
Next Steps
- Gallery Slideshow - the same grid opening a lightbox that plays as a slideshow
- Reel Player - every prop, slot, and part of the playerReel Player - every prop, slot, and part of the playerReel Player - every prop, slot, and part of the player