215 lines
4.4 KiB
Vue
215 lines
4.4 KiB
Vue
<template>
|
|
<div
|
|
:class="[
|
|
'neumorphic-card',
|
|
sizeClasses,
|
|
elevationClasses,
|
|
{
|
|
'cursor-pointer': clickable,
|
|
'transition-all duration-300': animated
|
|
}
|
|
]"
|
|
@click="handleClick"
|
|
>
|
|
<div v-if="$slots.header || title" class="neumorphic-card__header">
|
|
<slot name="header">
|
|
<h3 v-if="title" class="neumorphic-card__title">{{ title }}</h3>
|
|
<p v-if="subtitle" class="neumorphic-card__subtitle">{{ subtitle }}</p>
|
|
</slot>
|
|
</div>
|
|
|
|
<div class="neumorphic-card__content">
|
|
<slot></slot>
|
|
</div>
|
|
|
|
<div v-if="$slots.footer" class="neumorphic-card__footer">
|
|
<slot name="footer"></slot>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<script setup lang="ts">
|
|
import { computed } from 'vue';
|
|
|
|
interface Props {
|
|
title?: string;
|
|
subtitle?: string;
|
|
size?: 'sm' | 'md' | 'lg' | 'xl';
|
|
elevation?: 'none' | 'sm' | 'md' | 'lg' | 'xl';
|
|
clickable?: boolean;
|
|
animated?: boolean;
|
|
pressed?: boolean;
|
|
}
|
|
|
|
const props = withDefaults(defineProps<Props>(), {
|
|
size: 'md',
|
|
elevation: 'md',
|
|
clickable: false,
|
|
animated: true,
|
|
pressed: false
|
|
});
|
|
|
|
const emit = defineEmits<{
|
|
click: [event: MouseEvent];
|
|
}>();
|
|
|
|
const sizeClasses = computed(() => {
|
|
const sizes = {
|
|
sm: 'p-4',
|
|
md: 'p-6',
|
|
lg: 'p-8',
|
|
xl: 'p-10'
|
|
};
|
|
return sizes[props.size];
|
|
});
|
|
|
|
const elevationClasses = computed(() => {
|
|
if (props.pressed) return 'neumorphic-pressed';
|
|
|
|
const elevations = {
|
|
none: '',
|
|
sm: 'neumorphic-elevation-sm',
|
|
md: 'neumorphic-elevation-md',
|
|
lg: 'neumorphic-elevation-lg',
|
|
xl: 'neumorphic-elevation-xl'
|
|
};
|
|
return elevations[props.elevation];
|
|
});
|
|
|
|
const handleClick = (event: MouseEvent) => {
|
|
if (props.clickable) {
|
|
emit('click', event);
|
|
}
|
|
};
|
|
</script>
|
|
|
|
<style lang="scss" scoped>
|
|
@import '../../styles/neumorphic-system.scss';
|
|
|
|
.neumorphic-card {
|
|
background: linear-gradient(145deg, #ffffff, #f0f0f0);
|
|
border-radius: $radius-xl;
|
|
position: relative;
|
|
overflow: hidden;
|
|
|
|
// Base elevation styles
|
|
&.neumorphic-elevation-sm {
|
|
box-shadow: $shadow-soft-sm;
|
|
}
|
|
|
|
&.neumorphic-elevation-md {
|
|
box-shadow: $shadow-soft-md;
|
|
}
|
|
|
|
&.neumorphic-elevation-lg {
|
|
box-shadow: $shadow-soft-lg;
|
|
}
|
|
|
|
&.neumorphic-elevation-xl {
|
|
box-shadow: $shadow-soft-xl;
|
|
}
|
|
|
|
&.neumorphic-pressed {
|
|
box-shadow: $shadow-inset-md;
|
|
background: linear-gradient(145deg, #e6e6e6, #ffffff);
|
|
}
|
|
|
|
// Hover effects for clickable cards
|
|
&.cursor-pointer {
|
|
&:hover {
|
|
transform: translateY(-2px);
|
|
box-shadow: $shadow-soft-lg;
|
|
}
|
|
|
|
&:active {
|
|
transform: translateY(0);
|
|
box-shadow: $shadow-inset-sm;
|
|
}
|
|
}
|
|
|
|
// Header section
|
|
&__header {
|
|
padding-bottom: $space-4;
|
|
border-bottom: 1px solid rgba($neutral-300, 0.5);
|
|
margin-bottom: $space-4;
|
|
}
|
|
|
|
&__title {
|
|
font-family: $font-heading;
|
|
font-size: $text-xl;
|
|
font-weight: $font-semibold;
|
|
color: $neutral-800;
|
|
margin: 0;
|
|
line-height: $leading-tight;
|
|
}
|
|
|
|
&__subtitle {
|
|
font-family: $font-body;
|
|
font-size: $text-sm;
|
|
color: $neutral-600;
|
|
margin-top: $space-2;
|
|
line-height: $leading-normal;
|
|
}
|
|
|
|
// Content section
|
|
&__content {
|
|
font-family: $font-body;
|
|
color: $neutral-700;
|
|
line-height: $leading-relaxed;
|
|
}
|
|
|
|
// Footer section
|
|
&__footer {
|
|
padding-top: $space-4;
|
|
border-top: 1px solid rgba($neutral-300, 0.5);
|
|
margin-top: $space-4;
|
|
}
|
|
|
|
// Dark mode support
|
|
@include dark-mode {
|
|
background: linear-gradient(145deg, $neutral-800, $neutral-700);
|
|
|
|
&.neumorphic-elevation-sm {
|
|
box-shadow: $shadow-dark-soft-sm;
|
|
}
|
|
|
|
&.neumorphic-elevation-md {
|
|
box-shadow: $shadow-dark-soft-md;
|
|
}
|
|
|
|
&.neumorphic-elevation-lg {
|
|
box-shadow: $shadow-dark-soft-lg;
|
|
}
|
|
|
|
&.neumorphic-pressed {
|
|
box-shadow: $shadow-dark-inset-md;
|
|
background: linear-gradient(145deg, $neutral-900, $neutral-800);
|
|
}
|
|
|
|
.neumorphic-card__title {
|
|
color: $neutral-100;
|
|
}
|
|
|
|
.neumorphic-card__subtitle {
|
|
color: $neutral-400;
|
|
}
|
|
|
|
.neumorphic-card__content {
|
|
color: $neutral-300;
|
|
}
|
|
|
|
.neumorphic-card__header,
|
|
.neumorphic-card__footer {
|
|
border-color: rgba($neutral-600, 0.5);
|
|
}
|
|
}
|
|
|
|
// Responsive adjustments
|
|
@include responsive('sm') {
|
|
&.p-4 { padding: $space-5; }
|
|
&.p-6 { padding: $space-8; }
|
|
&.p-8 { padding: $space-10; }
|
|
&.p-10 { padding: $space-12; }
|
|
}
|
|
}
|
|
</style> |