feat: hero effect with dynamic navbar height and inline title
All checks were successful
Build Docker Image / build (push) Successful in 3m41s
All checks were successful
Build Docker Image / build (push) Successful in 3m41s
This commit is contained in:
@@ -1,7 +1,8 @@
|
|||||||
<template>
|
<template>
|
||||||
<header
|
<header
|
||||||
class="h-[100px] shadow-lg"
|
class="shadow-lg"
|
||||||
:class="glassStyle ? 'bg-black/30 backdrop-blur-md border-b border-white/10' : 'bg-base-100 border-b border-base-300'"
|
:class="glassStyle ? 'bg-black/30 backdrop-blur-md border-b border-white/10' : 'bg-base-100 border-b border-base-300'"
|
||||||
|
:style="{ height: `${height}px` }"
|
||||||
>
|
>
|
||||||
<!-- Single row: Logo + Search + Icons -->
|
<!-- Single row: Logo + Search + Icons -->
|
||||||
<div class="flex items-stretch h-full px-4 lg:px-6 gap-4">
|
<div class="flex items-stretch h-full px-4 lg:px-6 gap-4">
|
||||||
@@ -36,6 +37,9 @@
|
|||||||
|
|
||||||
<!-- Center: Search input (vertically centered) -->
|
<!-- Center: Search input (vertically centered) -->
|
||||||
<div class="flex-1 flex flex-col items-center max-w-2xl mx-auto gap-2 justify-center">
|
<div class="flex-1 flex flex-col items-center max-w-2xl mx-auto gap-2 justify-center">
|
||||||
|
<!-- Hero slot for home page title -->
|
||||||
|
<slot name="hero" />
|
||||||
|
|
||||||
<!-- Quote mode: Simple segmented input with search inside (white glass) -->
|
<!-- Quote mode: Simple segmented input with search inside (white glass) -->
|
||||||
<template v-if="catalogMode === 'quote'">
|
<template v-if="catalogMode === 'quote'">
|
||||||
<div class="flex items-center w-full rounded-full border border-white/40 bg-white/80 backdrop-blur-md shadow-lg divide-x divide-base-300/30">
|
<div class="flex items-center w-full rounded-full border border-white/40 bg-white/80 backdrop-blur-md shadow-lg divide-x divide-base-300/30">
|
||||||
@@ -274,7 +278,7 @@ import { entityColors } from '~/composables/useCatalogSearch'
|
|||||||
|
|
||||||
import type { CatalogMode } from '~/composables/useCatalogSearch'
|
import type { CatalogMode } from '~/composables/useCatalogSearch'
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = withDefaults(defineProps<{
|
||||||
sessionChecked?: boolean
|
sessionChecked?: boolean
|
||||||
loggedIn?: boolean
|
loggedIn?: boolean
|
||||||
userAvatarSvg?: string
|
userAvatarSvg?: string
|
||||||
@@ -304,7 +308,11 @@ const props = defineProps<{
|
|||||||
showActiveMode?: boolean // Whether to show active state on mode toggle
|
showActiveMode?: boolean // Whether to show active state on mode toggle
|
||||||
// Glass style (transparent) for map pages
|
// Glass style (transparent) for map pages
|
||||||
glassStyle?: boolean
|
glassStyle?: boolean
|
||||||
}>()
|
// Dynamic height for hero effect
|
||||||
|
height?: number
|
||||||
|
}>(), {
|
||||||
|
height: 100
|
||||||
|
})
|
||||||
|
|
||||||
defineEmits([
|
defineEmits([
|
||||||
'toggle-theme',
|
'toggle-theme',
|
||||||
|
|||||||
@@ -8,24 +8,10 @@
|
|||||||
<div class="absolute inset-0 bg-[radial-gradient(ellipse_at_center,_var(--tw-gradient-stops))] from-primary/20 via-transparent to-transparent" />
|
<div class="absolute inset-0 bg-[radial-gradient(ellipse_at_center,_var(--tw-gradient-stops))] from-primary/20 via-transparent to-transparent" />
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<!-- Hero title (home page only, fades on scroll) -->
|
<!-- MainNavigation - dynamic height on home page -->
|
||||||
<div
|
|
||||||
v-if="isHomePage"
|
|
||||||
class="absolute left-0 right-0 text-center pointer-events-none z-20"
|
|
||||||
:style="heroTitleStyle"
|
|
||||||
>
|
|
||||||
<h1 class="text-4xl lg:text-5xl font-bold text-white mb-2">
|
|
||||||
{{ $t('hero.title', 'Оптовая торговля') }}
|
|
||||||
</h1>
|
|
||||||
<p class="text-lg text-white/70">
|
|
||||||
{{ $t('hero.subtitle', 'Найдите лучших поставщиков и товары') }}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- MainNavigation - same everywhere, centered vertically on home page -->
|
|
||||||
<MainNavigation
|
<MainNavigation
|
||||||
class="relative z-10"
|
class="relative z-10"
|
||||||
:style="mainNavStyle"
|
:height="isHomePage ? heroHeight : 100"
|
||||||
:session-checked="sessionChecked"
|
:session-checked="sessionChecked"
|
||||||
:logged-in="isLoggedIn"
|
:logged-in="isLoggedIn"
|
||||||
:user-avatar-svg="userAvatarSvg"
|
:user-avatar-svg="userAvatarSvg"
|
||||||
@@ -58,7 +44,19 @@
|
|||||||
@update:search-query="searchQuery = $event"
|
@update:search-query="searchQuery = $event"
|
||||||
@update-quantity="setQuantity"
|
@update-quantity="setQuantity"
|
||||||
@search="onSearch"
|
@search="onSearch"
|
||||||
/>
|
>
|
||||||
|
<!-- Hero content for home page -->
|
||||||
|
<template v-if="isHomePage && collapseProgress < 1" #hero>
|
||||||
|
<div class="text-center mb-4" :style="{ opacity: 1 - collapseProgress }">
|
||||||
|
<h1 class="text-4xl lg:text-5xl font-bold text-white mb-2">
|
||||||
|
{{ $t('hero.title', 'Оптовая торговля') }}
|
||||||
|
</h1>
|
||||||
|
<p class="text-lg text-white/70">
|
||||||
|
{{ $t('hero.subtitle', 'Найдите лучших поставщиков и товары') }}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</MainNavigation>
|
||||||
|
|
||||||
<!-- Sub Navigation (section-specific tabs) - only for non-catalog/non-home sections -->
|
<!-- Sub Navigation (section-specific tabs) - only for non-catalog/non-home sections -->
|
||||||
<SubNavigation
|
<SubNavigation
|
||||||
@@ -177,12 +175,8 @@ const hasSubNav = computed(() => !isHomePage.value && !isCatalogSection.value)
|
|||||||
const canCollapse = computed(() => hasSubNav.value)
|
const canCollapse = computed(() => hasSubNav.value)
|
||||||
const isHeaderCollapsed = computed(() => canCollapse.value && isCollapsed.value)
|
const isHeaderCollapsed = computed(() => canCollapse.value && isCollapsed.value)
|
||||||
|
|
||||||
// Header container style - dynamic height for home page, transform for SubNav pages
|
// Header container style - transform for SubNav pages
|
||||||
const headerContainerStyle = computed(() => {
|
const headerContainerStyle = computed(() => {
|
||||||
if (isHomePage.value) {
|
|
||||||
// Home page: height changes from heroHeight to 100px on scroll
|
|
||||||
return { height: `${heroHeight.value}px` }
|
|
||||||
}
|
|
||||||
if (hasSubNav.value) {
|
if (hasSubNav.value) {
|
||||||
// SubNav pages: slide up on scroll
|
// SubNav pages: slide up on scroll
|
||||||
return { transform: `translateY(${headerOffset.value}px)` }
|
return { transform: `translateY(${headerOffset.value}px)` }
|
||||||
@@ -190,25 +184,6 @@ const headerContainerStyle = computed(() => {
|
|||||||
return {}
|
return {}
|
||||||
})
|
})
|
||||||
|
|
||||||
// MainNavigation style - centered vertically on home page, normal on other pages
|
|
||||||
const mainNavStyle = computed(() => {
|
|
||||||
if (!isHomePage.value) return {}
|
|
||||||
// Center MainNav (100px) in container: margin-top = (height - 100) / 2
|
|
||||||
const marginTop = Math.max(0, (heroHeight.value - 100) / 2)
|
|
||||||
return { marginTop: `${marginTop}px` }
|
|
||||||
})
|
|
||||||
|
|
||||||
// Hero title style - positioned above MainNav, fades on scroll
|
|
||||||
const heroTitleStyle = computed(() => {
|
|
||||||
// Position title above the centered MainNav
|
|
||||||
const navMarginTop = Math.max(0, (heroHeight.value - 100) / 2)
|
|
||||||
const titleBottom = heroHeight.value - navMarginTop + 20 // 20px gap above nav
|
|
||||||
return {
|
|
||||||
bottom: `${titleBottom}px`,
|
|
||||||
opacity: 1 - collapseProgress.value,
|
|
||||||
transform: `translateY(${collapseProgress.value * -20}px)`
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
// Main content padding-top to compensate for fixed header
|
// Main content padding-top to compensate for fixed header
|
||||||
const mainStyle = computed(() => {
|
const mainStyle = computed(() => {
|
||||||
|
|||||||
Reference in New Issue
Block a user