All files / components/base BaseCard.vue

100% Statements 38/38
100% Branches 0/0
100% Functions 0/0
100% Lines 38/38

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>