Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 | 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x | <template>
<div :class="cardClasses">
<div
v-if="$slots.header"
class="px-4 py-3 border-b border-secondary-200"
>
<slot name="header" />
</div>
<div :class="bodyClasses">
<slot />
</div>
<div
v-if="$slots.footer"
class="px-4 py-3 border-t border-secondary-200 bg-secondary-50"
>
<slot name="footer" />
</div>
</div>
</template>
<script setup lang="ts">
/**
* BaseCard
* @description Reusable card container with header, body, and footer slots
*/
interface Props {
/** Card padding size */
padding?: 'none' | 'sm' | 'md' | 'lg'
/** Hover effect */
hoverable?: boolean
/** Clickable card styling */
clickable?: boolean
}
const props = withDefaults(defineProps<Props>(), {
padding: 'md',
hoverable: false,
clickable: false
})
const cardClasses = computed(() => {
const base = [
'bg-white rounded-lg shadow-sm border border-secondary-200 overflow-hidden'
]
const interactive = []
if (props.hoverable) {
interactive.push('hover:shadow-md transition-shadow duration-200')
}
if (props.clickable) {
interactive.push('cursor-pointer')
}
return [...base, ...interactive]
})
const bodyClasses = computed(() => {
const paddings = {
none: '',
sm: 'px-3 py-2',
md: 'px-4 py-3',
lg: 'px-6 py-4'
}
return paddings[props.padding]
})
</script>
|