ghostguild-org/app/components/PageHeader.vue

172 lines
No EOL
4.6 KiB
Vue

<template>
<header
class="py-16 md:py-24"
:class="[
backgroundClass,
textColorClass
]"
>
<UContainer>
<div class="text-center max-w-4xl mx-auto">
<h1
class="font-bold mb-6 md:mb-8"
:class="[
titleSizeClass,
titleColorClass
]"
>
{{ title }}
</h1>
<p
v-if="subtitle"
class="text-lg md:text-xl leading-relaxed mb-8"
:class="subtitleColorClass"
>
{{ subtitle }}
</p>
<!-- Interactive Content Area (for hero sections with carousels, etc.) -->
<div v-if="showInteractiveArea" class="bg-white dark:bg-gray-800 rounded-2xl p-6 md:p-8 shadow-xl border border-blue-200 dark:border-blue-800 mb-12">
<div class="flex items-center justify-between">
<button
class="p-3 rounded-full bg-blue-500 text-white hover:bg-blue-600 transition-colors"
@click="$emit('prev')"
>
<svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 19l-7-7 7-7"/>
</svg>
</button>
<div class="text-center flex-1">
<slot name="interactive-content">
<p class="text-lg text-gray-600 dark:text-gray-300">
{{ interactiveContent || 'Lorem ipsum dolor sit amet, consectetur adipiscing elit.' }}
</p>
</slot>
</div>
<button
class="p-3 rounded-full bg-blue-500 text-white hover:bg-blue-600 transition-colors"
@click="$emit('next')"
>
<svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7"/>
</svg>
</button>
</div>
</div>
<!-- Call to Action Button -->
<div v-if="showCta" class="flex justify-center">
<UButton
:to="ctaLink"
:size="ctaSize"
:color="ctaColor"
class="font-semibold"
>
{{ ctaText }}
</UButton>
</div>
<!-- Custom Content Slot -->
<div v-if="$slots.default">
<slot />
</div>
</div>
</UContainer>
</header>
</template>
<script setup>
import { computed } from 'vue'
const props = defineProps({
title: {
type: String,
required: true
},
subtitle: {
type: String,
default: ''
},
theme: {
type: String,
default: 'blue',
validator: (value) => ['blue', 'purple', 'emerald', 'gray'].includes(value)
},
size: {
type: String,
default: 'large',
validator: (value) => ['small', 'medium', 'large', 'hero'].includes(value)
},
showInteractiveArea: {
type: Boolean,
default: false
},
interactiveContent: {
type: String,
default: ''
},
showCta: {
type: Boolean,
default: false
},
ctaText: {
type: String,
default: 'Get Started'
},
ctaLink: {
type: String,
default: '/join'
},
ctaSize: {
type: String,
default: 'lg'
},
ctaColor: {
type: String,
default: 'primary'
}
})
defineEmits(['prev', 'next'])
const backgroundClass = computed(() => {
const themes = {
blue: 'bg-gradient-to-br from-blue-50 to-indigo-100 dark:from-gray-900 dark:to-gray-800',
purple: 'bg-gradient-to-br from-purple-50 to-violet-100 dark:from-gray-900 dark:to-purple-900/20',
emerald: 'bg-gradient-to-br from-emerald-50 to-teal-100 dark:from-gray-900 dark:to-emerald-900/20',
gray: 'bg-gray-50 dark:bg-gray-900'
}
return themes[props.theme] || themes.blue
})
const titleSizeClass = computed(() => {
const sizes = {
small: 'text-2xl md:text-3xl',
medium: 'text-3xl md:text-4xl',
large: 'text-4xl md:text-5xl',
hero: 'text-5xl md:text-6xl'
}
return sizes[props.size] || sizes.large
})
const titleColorClass = computed(() => {
const themes = {
blue: 'text-blue-600 dark:text-blue-400',
purple: 'text-purple-600 dark:text-purple-400',
emerald: 'text-emerald-600 dark:text-emerald-400',
gray: 'text-gray-900 dark:text-white'
}
return themes[props.theme] || themes.blue
})
const subtitleColorClass = computed(() => {
return 'text-gray-600 dark:text-gray-300'
})
const textColorClass = computed(() => {
return 'text-gray-900 dark:text-white'
})
</script>