ImageGallery
Thumbnail grid with a fullscreen lightbox preview, clamped start index, and bounded previous/next navigation.
Usage
Loading demo...
Clicking a thumbnail opens the current image in a fullscreen lightbox: the image is contained in the viewport with the title, page count, previous/next and close controls on top of it.
Track Preview Opens
<script setup lang="ts">
function onOpen({ index, item }: { index: number, item: { id: string } }) {
analytics.track('gallery_open', { index, imageId: item.id })
}
</script>
<template>
<TxImageGallery :items="images" @open="onOpen" @close="onClose" />
</template>
Controlled Starting Image
<template>
<TxImageGallery :items="screenshots" :start-index="selectedIndex" />
</template>
startIndex is clamped whenever it changes. It chooses the initial/current preview index but does not open the lightbox by itself.
Best Practices
- Use stable
idvalues; do not use array indexes for long-lived gallery data. - Provide
namefor informative images so button labels, alt text, and preview titles are meaningful. - Keep the list size modest. This component renders all thumbnails; it is not virtualized.
- Do not pass untrusted remote image URLs without normalizing or proxying them at your application boundary.
- Use
startIndexfor selected preview context, not as a visibility control. - Use
@openfor analytics or detail-panel coordination; do not mutateitemssynchronously in a way that invalidates the opened image.
API Reference
Props
| Prop | Type | Default | Description |
|---|---|---|---|
items | ImageGalleryItem[] | required | Images rendered as thumbnails and openable in the fullscreen preview. |
startIndex | number | 0 | Initial/current preview index, clamped to the available item range. |
ImageGalleryItem
| Field | Type | Description |
|---|---|---|
id | string | Stable key for thumbnail rendering. |
url | string | Image URL used by thumbnails and the preview image. |
name | string | Optional display name used for labels, alt text, and the preview title. |
Events
| Event | Payload | Description |
|---|---|---|
open | { index: number, item: ImageGalleryItem } | Emitted after a thumbnail opens the fullscreen preview. |
close | void | Emitted when the preview closes. |
Slots
No public slots are exposed.
Exposed Methods
No public instance methods are exposed.
Overview
- The thumbnail grid renders one native
button type="button"for each item. - Thumbnail buttons use
aria-label="Open {label} preview", wherelabelisitem.nameor a generatedImage Nfallback. - Thumbnail and preview image
alttext useitem.name; unnamed images intentionally use empty alt text. - Empty
itemsrender no thumbnail buttons and cannot emitopen. - Clicking a thumbnail clamps the clicked index, opens the fullscreen preview, and emits
openwith{ index, item }. startIndexchanges are clamped to0..items.length - 1.- When
itemsbecomes empty, the preview closes and the index resets to0. - Preview navigation is bounded: previous is disabled at the first image, next is disabled at the last image.
- When a navigation button becomes disabled at a boundary, focus moves to the other enabled navigation button so Escape and Tab remain inside the preview.
- The preview title is the current image name, falling back to
Preview. - The preview fills the viewport: the image is contained (never cropped) inside the space left by the header and footer bars, so both axes stay visible on any viewport shape.
- The preview reuses
TxModalinfullscreenmode, so it teleports tobody, traps Tab focus, closes on Escape or the close button, and restores focus to the thumbnail that opened it. - Closing emits
close.
Technologies
- Reviewed against
packages/tuffex/packages/components/src/image-gallery/src/types.ts,TxImageGallery.vue, andimage-gallery.test.ts. startIndexis clamped and updates the current preview index, but it never opens the fullscreen preview by itself.- Unnamed images intentionally use empty image alt text while thumbnail buttons still receive generated open labels.
- The lightbox is
TxModalwith itsfullscreenprop rather than a second custom overlay, so the shared focus trap, Escape handling, z-index allocation and focus restore stay in one place. max-height: 70vhon the preview image was rejected: it letterboxes a portrait image into the middle band of a tall viewport instead of using the space the header and footer leave.- Component source:
packages/tuffex/packages/components/src/image-gallery/src/TxImageGallery.vue. - Types:
packages/tuffex/packages/components/src/image-gallery/src/types.ts. - Verified coverage: Coverage:
packages/tuffex/packages/components/src/image-gallery/__tests__/image-gallery.test.tsverifies thumbnail labels and alt text, open payloads, bounded previous / next navigation, empty-list no-op behavior, start-index clamping, and close-on-empty updates.
查看源码
packages/tuffex/packages/components/src/image-gallery/index.ts