Thumbnail Group
The <ThumbnailGroup>
component allows you to display thumbnails with images in conjunction with the <Image>
or <Carousel>
components or to be used with <FileUpload>
to preview the uploaded files. It provides a simple way to manage the thumbnails and their order. It also allows you to remove the thumbnails.
Basic Usage
A basic example of the <ThumbnailGroup>
component with a few images.
<script setup lang="ts">
import { ref } from 'vue';
import Thumbnail from '../../../src/components/Thumbnail/Thumbnail.vue';
import ThumbnailGroup from '../../../src/components/ThumbnailGroup/ThumbnailGroup.vue';
const thumbnails = ref([
{
id: 1,
imageUrl: 'https://picsum.photos/100.webp?random=1',
},
{
id: 2,
imageUrl: 'https://picsum.photos/100.webp?random=2',
},
{
id: 3,
imageUrl: 'https://picsum.photos/100.webp?random=3',
},
{
id: 4,
imageUrl: 'https://picsum.photos/100.webp?random=4',
},
]);
const activeThumbnail = ref(0);
</script>
<template>
<ThumbnailGroup v-model:active-thumbnail="activeThumbnail" :thumbnails>
<Thumbnail v-for="thumb in thumbnails" :key="thumb.id" :thumbnail="thumb" />
</ThumbnailGroup>
</template>
Border radius variants
The <ThumbnailGroup>
component supports the radius
prop to set the border radius of the thumbnails. It comes with two flavors: rounded
and circle
.
Rounded
Sets the border radius to 4px
on all sides.
<script setup lang="ts">
import { ref } from 'vue';
import Thumbnail from '../../../src/components/Thumbnail/Thumbnail.vue';
import ThumbnailGroup from '../../../src/components/ThumbnailGroup/ThumbnailGroup.vue';
const thumbnails = ref([
{
id: 1,
imageUrl: 'https://picsum.photos/100.webp?random=1',
},
{
id: 2,
imageUrl: 'https://picsum.photos/100.webp?random=2',
},
{
id: 3,
imageUrl: 'https://picsum.photos/100.webp?random=3',
},
{
id: 4,
imageUrl: 'https://picsum.photos/100.webp?random=4',
},
]);
</script>
<template>
<ThumbnailGroup :thumbnails radius="rounded">
<Thumbnail v-for="thumb in thumbnails" :key="thumb.id" :thumbnail="thumb" />
</ThumbnailGroup>
</template>
Circle
Sets the border radius to 50%
on all sides.
<script setup lang="ts">
import { ref } from 'vue';
import Thumbnail from '../../../src/components/Thumbnail/Thumbnail.vue';
import ThumbnailGroup from '../../../src/components/ThumbnailGroup/ThumbnailGroup.vue';
const thumbnails = ref([
{
id: 1,
imageUrl: 'https://picsum.photos/100.webp?random=1',
},
{
id: 2,
imageUrl: 'https://picsum.photos/100.webp?random=2',
},
{
id: 3,
imageUrl: 'https://picsum.photos/100.webp?random=3',
},
{
id: 4,
imageUrl: 'https://picsum.photos/100.webp?random=4',
},
]);
</script>
<template>
<ThumbnailGroup :thumbnails radius="circle">
<Thumbnail v-for="thumb in thumbnails" :key="thumb.id" :thumbnail="thumb" />
</ThumbnailGroup>
</template>
Active thumbnail
The <ThumbnailGroup>
component can receive an activeThumbnail
model ref to indicate the index of the active thumbnail.
NOTE
The activeThumbnail
prop changes when the user clicks on a thumbnail.
Active Thumbnail: 0
<script setup lang="ts">
import { ref } from 'vue';
import Thumbnail from '../../../src/components/Thumbnail/Thumbnail.vue';
import ThumbnailGroup from '../../../src/components/ThumbnailGroup/ThumbnailGroup.vue';
const thumbnails = ref([
{
id: 1,
imageUrl: 'https://picsum.photos/100.webp?random=1',
},
{
id: 2,
imageUrl: 'https://picsum.photos/100.webp?random=2',
},
{
id: 3,
imageUrl: 'https://picsum.photos/100.webp?random=3',
},
{
id: 4,
imageUrl: 'https://picsum.photos/100.webp?random=4',
},
]);
const activeThumbnail = ref(0);
</script>
<template>
<div class="tw-space-y-4">
<ThumbnailGroup v-model:active-thumbnail="activeThumbnail" :thumbnails>
<Thumbnail v-for="thumb in thumbnails" :key="thumb.id" :thumbnail="thumb" />
</ThumbnailGroup>
<p>Active Thumbnail: {{ activeThumbnail }}</p>
</div>
</template>
Empty thumbnails
The <ThumbnailGroup>
component can receive the showEmpty
prop to display some placeholder images that indicate how many images can be added to it.
NOTE
The empty thumbnail does not emit any events, so you need to add images from a different source like the FileUpload component.
<script setup lang="ts">
import { ref } from 'vue';
import ThumbnailGroup from '../../../src/components/ThumbnailGroup/ThumbnailGroup.vue';
const thumbnails = ref([]);
</script>
<template>
<ThumbnailGroup :thumbnails show-empty />
</template>
You can provide an empty-options
object to the component to set some custom options like { max: 6 }
which sets the max amount of empty thumbnails to be shown to 6.
NOTE
The empty-options
object automatically sets max: 6
when showEmpty
is set to true
.
<script setup lang="ts">
import { ref } from 'vue';
import ThumbnailGroup from '../../../src/components/ThumbnailGroup/ThumbnailGroup.vue';
const thumbnails = ref([]);
</script>
<template>
<ThumbnailGroup :thumbnails show-empty :empty-options="{ max: 2 }" />
</template>
Removable
The removable
prop can be passed to the <ThumbnailGroup>
component to allow the user to remove images.
NOTE
The <Thumbnail>
child component emits a remove
event with the index of the item to be removed.
<script setup lang="ts">
import { ref } from 'vue';
import Thumbnail from '../../../src/components/Thumbnail/Thumbnail.vue';
import ThumbnailGroup from '../../../src/components/ThumbnailGroup/ThumbnailGroup.vue';
const thumbnails = ref([
{
id: 1,
imageUrl: 'https://picsum.photos/100.webp?random=1',
},
{
id: 2,
imageUrl: 'https://picsum.photos/100.webp?random=2',
},
{
id: 3,
imageUrl: 'https://picsum.photos/100.webp?random=3',
},
{
id: 4,
imageUrl: 'https://picsum.photos/100.webp?random=4',
},
]);
</script>
<template>
<ThumbnailGroup v-model:thumbnails="thumbnails" show-empty removable>
<Thumbnail v-for="thumb in thumbnails" :key="thumb.id" :thumbnail="thumb" @remove="thumbnails.splice($event, 1)" />
</ThumbnailGroup>
</template>
Draggable
The draggable
prop can be passed to the <ThumbnailGroup>
component to allow the component to reorder the thumbnails.
NOTE
Altough the order of the thumbnails is updated, no thumbnail internal property is changed. If you have a position property, for example, that should be updated when the element is dropped by listening to the dragEnd
event.
<script setup lang="ts">
import { ref } from 'vue';
import Thumbnail from '../../../src/components/Thumbnail/Thumbnail.vue';
import ThumbnailGroup from '../../../src/components/ThumbnailGroup/ThumbnailGroup.vue';
const thumbnails = ref([
{
id: 1,
imageUrl: 'https://picsum.photos/100.webp?random=1',
},
{
id: 2,
imageUrl: 'https://picsum.photos/100.webp?random=2',
},
{
id: 3,
imageUrl: 'https://picsum.photos/100.webp?random=3',
},
{
id: 4,
imageUrl: 'https://picsum.photos/100.webp?random=4',
},
]);
</script>
<template>
<ThumbnailGroup v-model:thumbnails="thumbnails" draggable>
<Thumbnail v-for="thumb in thumbnails" :key="thumb.id" :thumbnail="thumb" />
</ThumbnailGroup>
</template>
Variant
The <Thumbnail>
component can receive a variant
prop to change the variant of the thumbnail between opaque
or translucent
.
INFO
opaque
is the default variant.
<script setup lang="ts">
import { ref } from 'vue';
import Thumbnail from '../../../src/components/Thumbnail/Thumbnail.vue';
import ThumbnailGroup from '../../../src/components/ThumbnailGroup/ThumbnailGroup.vue';
const thumbnails = ref([
{
id: 1,
imageUrl: 'https://picsum.photos/100.webp?random=1',
},
{
id: 2,
imageUrl: 'https://picsum.photos/100.webp?random=2',
},
{
id: 3,
imageUrl: 'https://picsum.photos/100.webp?random=3',
},
{
id: 4,
imageUrl: 'https://picsum.photos/100.webp?random=4',
},
]);
const activeThumbnail = ref(0);
</script>
<template>
<ThumbnailGroup v-model:active-thumbnail="activeThumbnail" :thumbnails>
<Thumbnail v-for="thumb in thumbnails" :key="thumb.id" :thumbnail="thumb" variant="translucent" />
</ThumbnailGroup>
</template>
Advanced Usage
The <ThumbnailGroup>
can be leveraged to complete all sorts of composability like the Carousel component which uses the thumbnails as a navigation system.
Another case would be a file upload component that uses the <ThumbnailGroup>
to preview the uploaded files.
<script setup lang="ts">
import { ref } from 'vue';
import FileUpload from '../../../src/components/FileUpload/FileUpload.vue';
import Thumbnail from '../../../src/components/Thumbnail/Thumbnail.vue';
import ThumbnailGroup from '../../../src/components/ThumbnailGroup/ThumbnailGroup.vue';
interface AdvancedThumbnail {
id: number;
imageUrl: string;
position: number;
}
const activeThumbnail = ref(0);
const thumbnails = ref<AdvancedThumbnail[]>([]);
let fileId = 0;
function selectFile({ files }: { files?: File[] }) {
if (!files) return;
for (const file of files) {
const reader = new FileReader();
reader.onload = (e) => {
thumbnails.value.push({
id: fileId++,
imageUrl: e.target?.result as string,
position: thumbnails.value.length,
});
};
reader.readAsDataURL(file);
}
}
function handleRemoveThumbnail(index: number) {
thumbnails.value.splice(index, 1);
}
</script>
<template>
<div class="tw-space-y-4">
<ThumbnailGroup
v-model:active-thumbnail="activeThumbnail"
v-model:thumbnails="thumbnails"
show-empty
draggable
removable
>
<Thumbnail v-for="thumb in thumbnails" :key="thumb.id" :thumbnail="thumb" @remove="handleRemoveThumbnail" />
</ThumbnailGroup>
<FileUpload :file-types="['JPEG', 'PNG']" button-only multiple @file-select="selectFile" />
</div>
</template>
API
See the documentation below for a complete reference to all the props and classes available.