Card View
A block for visualizing data in a grid of cards.
Usage
<template>
<MeCardView
v-model:card-selection="cardSelection"
v-model:card-favorite="cardFavorite"
:data="data"
selectable
favorite
/>
</template>
<script setup>
const cardSelection = ref({ card0: true, card2: true })
const cardFavorite = ref({ card1: true, card3: true })
const data = Array.from({ length: 5 }, (_, i) => ({
id: `card${i}`,
title: `Card ${i}`,
description: `Description ${i}`,
image: { src: 'https://picsum.photos/252/172', alt: 'Image' },
badges: [
{
icon: 'i-lucide-shopping-basket',
color: 'success',
description: 'Shopping basket'
},
{
icon: 'i-lucide-file-check',
color: 'primary'
}
],
cart: {
modelValue: i % 2 === 0 ? i : undefined,
onClick: (cardId, value) => console.log(cardId, value),
}
}))
</script>
The cards data objects are defined as follows:
type InputCartProps = {
disabled?: boolean
incrementDisabled?: boolean
decrementDisabled?: boolean
cartDisabled?: boolean
min?: number
max?: number
modelValue?: number
}
type CardViewItemData = {
id: string
selectionLabel?: string
image?: { src: string, alt?: string }
badges?: {
icon: string
color?: "error" | "primary" | "secondary" | "success" | "info" | "warning" | "neutral"
description?: string
}[]
title?: string
description?: string
cart?: InputCartProps & { onClick: (cardId: string, value: number | undefined) => unknown }
cardHeight?: string
}
Selection and favorite
Use the selectable and favorite props to enable card selection and the favorite button. Bound the cardSelection and cardFavorite props to v-model to control both behaviors.
<template>
<MeCardView
v-model:card-selection="cardSelection"
v-model:card-favorite="cardFavorite"
:data="data"
selectable
favorite
/>
</template>
<script setup>
const cardSelection = ref({ card0: true })
const cardFavorite = ref({ card1: true })
const data = [
{
id: 'card0',
title: 'Card 0',
description: 'Card 0 description',
image: { src: 'https://picsum.photos/252/172', alt: 'Image' },
cart: { onClick: (_cardId, _value) => null },
badges: [{ icon: 'i-lucide-file-check' }]
},
{
id: 'card1',
title: 'Card 1',
description: 'Card 1 description',
image: { src: 'https://picsum.photos/252/172', alt: 'Image' },
cart: { onClick: (_cardId, _value) => null },
badges: [{ icon: 'i-lucide-file-check' }]
}
]
</script>
Header
Use the header slot to add custom content.
Header slot content
<template>
<MeCardView
:data="data"
selectable
favorite
>
<template #header>
Header slot content
</template>
<MeCardView />
</template>
<script setup>
const data = [{
id: 'card',
title: 'Card title',
description: 'Card description',
image: { src: 'https://picsum.photos/252/172' },
cart: { modelValue: 1 }
}]
</script>
Cart
Defining the cart field in the card data object will render a InputCart component. The behavior of the cart button is defined by the onClick function.
<template>
<MeCardView
:data="data"
favorite
selectable
/>
</template>
<script setup>
const data = [{
id: 'card',
title: 'Card title',
description: 'Card description',
image: { src: 'https://picsum.photos/252/172' },
cart: { modelValue: 3, onClick: (cardId, value) => console.log(cardId, value) }
}]
</script>
Custom cards
Use the card-header, card-image, card-default, card-extra-content and card-footer slots to customize your card. All slots are scoped with the card data object.
<template>
<MeCardView
:data="data"
selectable
favorite
>
<template #card-header="{ card }">
Card {{ card.id }} header
</template>
<template #card-image="{ card }">
<div class="h-43 bg-accented flex justify-center items-start">
Card {{ card.id }} image
</div>
</template>
<template #card-default="{ card }">
<div class="flex flex-col gap-y-2 mt-4 px-2">
<span class="text-default font-bold text-base truncate">
Card {{ card.id }} title
</span>
<span class="text-muted font-normal text-sm line-clamp-2">
Card {{ card.id }} description
</span>
</div>
</template>
<template #card-extra-content="{ card }">
Card {{ card.id }} extra content
</template>
<template #card-footer="{ card }">
Card {{ card.id }} footer
</template>
</MeCardView>
</template>
<script setup>
const data = [{
id: 'custom',
title: 'Custom',
description: 'Custom',
badges: [
{
icon: 'i-lucide-shopping-basket',
color: 'success',
description: 'Shopping basket'
},
{
icon: 'i-lucide-file-check',
color: 'primary'
}
]
}]
</script>
Empty view
Use the empty prop to customize the view when no data is available. It accepts all props from Empty except for variant, plus a src prop to customize the image. Alternatively, use the empty slot to create your empty view from scratch.
<template>
<MeCardView
:data="[]"
:empty="{
title: 'No data',
description: 'No data available',
actions: [{ label: 'Action A' }, { label: 'Action B' }]
}"
selectable
/>
</template>
API
Props
| Prop | Default | Type |
|---|---|---|
data | CardViewItemData | (() => Promise<CardViewItemData[]>) Data to display | |
selectable | boolean Enables card selection | |
cardSelection | {} | Record<string, boolean> Current selected cards, bound with v-model |
favorite | boolean Enables card favorite | |
cardFavorite | {} | Record<string, boolean> Current favorite cards, bound with v-model |
loading | boolean Displays loading state | |
empty | Omit<EmptyProps, 'variant'> & { src?: string } Empty view configuration. See Empty | |
virtualize | Omit<ScrollAreaVirtualizeOptions, 'gap' | 'lanes' | 'paddingStart' | 'paddingEnd'>Virtualization configuration. Use this option appropriately to improve performance with large datasets. See ScrollArea for more info | |
cardHeight | 448px | String Predefined card height |
Slots
| Slot | Type |
|---|---|
header | {} |
empty | {} |
card-header | CardViewItemData |
card-image | CardViewItemData |
card-default | CardViewItemData |
card-extra-content | CardViewItemData |
card-footer | CardViewItemData |