Spaces:
Sleeping
Sleeping
| <template> | |
| <component | |
| :is="tag" | |
| :class="cardClasses" | |
| v-bind="$attrs" | |
| > | |
| <!-- Header --> | |
| <header v-if="title || subtitle || $slots.header" class="saap-card__header"> | |
| <slot name="header"> | |
| <div v-if="title || subtitle" class="saap-card__title-group"> | |
| <h3 v-if="title" class="saap-card__title">{{ title }}</h3> | |
| <p v-if="subtitle" class="saap-card__subtitle">{{ subtitle }}</p> | |
| </div> | |
| </slot> | |
| <div v-if="$slots.actions" class="saap-card__actions"> | |
| <slot name="actions" /> | |
| </div> | |
| </header> | |
| <!-- Content --> | |
| <div :class="contentClasses"> | |
| <slot /> | |
| </div> | |
| <!-- Footer --> | |
| <footer v-if="$slots.footer" class="saap-card__footer"> | |
| <slot name="footer" /> | |
| </footer> | |
| </component> | |
| </template> | |
| <script setup lang="ts"> | |
| import { computed, defineProps, withDefaults } from 'vue' | |
| import type { CardProps } from '@/types' | |
| interface Props extends CardProps { | |
| tag?: 'div' | 'article' | 'section' | |
| } | |
| const props = withDefaults(defineProps<Props>(), { | |
| tag: 'div', | |
| hoverable: false, | |
| padding: 'md', | |
| shadow: 'sm', | |
| border: true | |
| }) | |
| // Computed | |
| const cardClasses = computed(() => [ | |
| 'saap-card', | |
| `saap-card--padding-${props.padding}`, | |
| `saap-card--shadow-${props.shadow}`, | |
| { | |
| 'saap-card--hoverable': props.hoverable, | |
| 'saap-card--bordered': props.border | |
| } | |
| ]) | |
| const contentClasses = computed(() => [ | |
| 'saap-card__content', | |
| { | |
| 'saap-card__content--no-padding': props.padding === 'none' | |
| } | |
| ]) | |
| </script> | |
| <style lang="scss" scoped> | |
| @import '@/styles/mixins.scss'; | |
| .saap-card { | |
| @include card-base; | |
| // Padding variants | |
| &--padding-none { | |
| padding: 0; | |
| } | |
| &--padding-sm { | |
| padding: var(--saap-space-4); | |
| } | |
| &--padding-md { | |
| padding: var(--saap-space-6); | |
| } | |
| &--padding-lg { | |
| padding: var(--saap-space-8); | |
| } | |
| // Shadow variants | |
| &--shadow-none { | |
| box-shadow: none; | |
| } | |
| &--shadow-sm { | |
| box-shadow: var(--saap-shadow-sm); | |
| } | |
| &--shadow-md { | |
| box-shadow: var(--saap-shadow-md); | |
| } | |
| &--shadow-lg { | |
| box-shadow: var(--saap-shadow-lg); | |
| } | |
| // Border | |
| &--bordered { | |
| border: 1px solid var(--saap-neutral-200); | |
| } | |
| // Hoverable | |
| &--hoverable { | |
| @include card-hover; | |
| } | |
| } | |
| .saap-card__header { | |
| display: flex; | |
| align-items: flex-start; | |
| justify-content: space-between; | |
| gap: var(--saap-space-4); | |
| margin-bottom: var(--saap-space-4); | |
| &:last-child { | |
| margin-bottom: 0; | |
| } | |
| } | |
| .saap-card__title-group { | |
| flex: 1; | |
| min-width: 0; | |
| } | |
| .saap-card__title { | |
| font-size: var(--saap-text-lg); | |
| font-weight: var(--saap-font-semibold); | |
| color: var(--saap-neutral-900); | |
| margin: 0; | |
| line-height: var(--saap-leading-snug); | |
| } | |
| .saap-card__subtitle { | |
| font-size: var(--saap-text-sm); | |
| color: var(--saap-neutral-600); | |
| margin: var(--saap-space-1) 0 0 0; | |
| line-height: var(--saap-leading-normal); | |
| } | |
| .saap-card__actions { | |
| flex-shrink: 0; | |
| display: flex; | |
| align-items: center; | |
| gap: var(--saap-space-2); | |
| } | |
| .saap-card__content { | |
| flex: 1; | |
| &--no-padding { | |
| margin: calc(-1 * var(--saap-space-6)); | |
| margin-top: 0; | |
| .saap-card--padding-sm & { | |
| margin: calc(-1 * var(--saap-space-4)); | |
| margin-top: 0; | |
| } | |
| .saap-card--padding-lg & { | |
| margin: calc(-1 * var(--saap-space-8)); | |
| margin-top: 0; | |
| } | |
| } | |
| } | |
| .saap-card__footer { | |
| margin-top: var(--saap-space-4); | |
| padding-top: var(--saap-space-4); | |
| border-top: 1px solid var(--saap-neutral-200); | |
| &:first-child { | |
| margin-top: 0; | |
| padding-top: 0; | |
| border-top: none; | |
| } | |
| } | |
| // Responsive adjustments | |
| @include respond-to('sm') { | |
| .saap-card__header { | |
| align-items: center; | |
| } | |
| } | |
| </style> | |