Redesign header: single row with unified search block
All checks were successful
Build Docker Image / build (push) Successful in 3m6s
All checks were successful
Build Docker Image / build (push) Successful in 3m6s
- Merge two rows into one: logo + search block + icons - Search block now contains input and chips together - Input is bigger with larger tokens - Remove hero section from landing page - Update padding to match new header height
This commit is contained in:
@@ -1,7 +1,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<header class="bg-base-100 shadow-md">
|
<header class="bg-base-100 shadow-md">
|
||||||
<!-- Top row: Logo + Icons -->
|
<!-- Single row: Logo + Search Block + Icons -->
|
||||||
<div class="flex items-center h-14 px-4 lg:px-6">
|
<div class="flex items-center px-4 lg:px-6 py-3 gap-4">
|
||||||
<!-- Left: Logo -->
|
<!-- Left: Logo -->
|
||||||
<div class="flex items-center flex-shrink-0">
|
<div class="flex items-center flex-shrink-0">
|
||||||
<NuxtLink :to="localePath('/')" class="flex items-center gap-2">
|
<NuxtLink :to="localePath('/')" class="flex items-center gap-2">
|
||||||
@@ -9,13 +9,17 @@
|
|||||||
</NuxtLink>
|
</NuxtLink>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Center: Search input with tokens -->
|
<!-- Center: Search block (input + chips together) -->
|
||||||
<div class="flex-1 flex justify-center px-4 max-w-2xl mx-auto">
|
<div class="flex-1 flex justify-center px-4 max-w-3xl mx-auto">
|
||||||
<div
|
<div
|
||||||
class="flex items-center gap-3 w-full px-5 py-2.5 border border-base-300 rounded-full bg-base-100 shadow-sm hover:shadow-md focus-within:border-primary focus-within:ring-2 focus-within:ring-primary/20 transition-all cursor-text"
|
class="w-full bg-base-200/50 rounded-2xl p-4 border border-base-300 shadow-sm hover:shadow-md transition-all"
|
||||||
|
>
|
||||||
|
<!-- Input row with tokens -->
|
||||||
|
<div
|
||||||
|
class="flex items-center gap-3 px-4 py-3 bg-base-100 rounded-xl border border-base-300 cursor-text"
|
||||||
@click="focusInput"
|
@click="focusInput"
|
||||||
>
|
>
|
||||||
<Icon name="lucide:search" size="20" class="text-base-content/50 flex-shrink-0" />
|
<Icon name="lucide:search" size="22" class="text-primary flex-shrink-0" />
|
||||||
|
|
||||||
<!-- Tokens + input inline -->
|
<!-- Tokens + input inline -->
|
||||||
<div class="flex items-center gap-2 flex-wrap flex-1 min-w-0">
|
<div class="flex items-center gap-2 flex-wrap flex-1 min-w-0">
|
||||||
@@ -23,23 +27,23 @@
|
|||||||
<div
|
<div
|
||||||
v-for="token in activeTokens"
|
v-for="token in activeTokens"
|
||||||
:key="token.type"
|
:key="token.type"
|
||||||
class="badge badge-md gap-1.5 cursor-pointer hover:badge-primary transition-colors flex-shrink-0"
|
class="badge badge-lg badge-primary gap-1.5 cursor-pointer hover:badge-secondary transition-colors flex-shrink-0"
|
||||||
@click.stop="$emit('edit-token', token.type)"
|
@click.stop="$emit('edit-token', token.type)"
|
||||||
>
|
>
|
||||||
<Icon :name="token.icon" size="14" />
|
<Icon :name="token.icon" size="14" />
|
||||||
<span class="max-w-24 truncate">{{ token.label }}</span>
|
<span class="max-w-28 truncate">{{ token.label }}</span>
|
||||||
<button
|
<button
|
||||||
class="hover:text-error"
|
class="hover:text-error"
|
||||||
@click.stop="$emit('remove-token', token.type)"
|
@click.stop="$emit('remove-token', token.type)"
|
||||||
>
|
>
|
||||||
<Icon name="lucide:x" size="12" />
|
<Icon name="lucide:x" size="14" />
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Active selection mode indicator -->
|
<!-- Active selection mode indicator -->
|
||||||
<div
|
<div
|
||||||
v-if="selectMode"
|
v-if="selectMode"
|
||||||
class="badge badge-md badge-outline badge-primary gap-1.5 flex-shrink-0"
|
class="badge badge-lg badge-outline badge-primary gap-1.5 flex-shrink-0"
|
||||||
>
|
>
|
||||||
<Icon :name="selectModeIcon" size="14" />
|
<Icon :name="selectModeIcon" size="14" />
|
||||||
{{ selectModeLabel }}:
|
{{ selectModeLabel }}:
|
||||||
@@ -47,7 +51,7 @@
|
|||||||
class="hover:text-error"
|
class="hover:text-error"
|
||||||
@click.stop="$emit('cancel-select')"
|
@click.stop="$emit('cancel-select')"
|
||||||
>
|
>
|
||||||
<Icon name="lucide:x" size="12" />
|
<Icon name="lucide:x" size="14" />
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -57,11 +61,28 @@
|
|||||||
v-model="localSearchQuery"
|
v-model="localSearchQuery"
|
||||||
type="text"
|
type="text"
|
||||||
:placeholder="placeholder"
|
:placeholder="placeholder"
|
||||||
class="flex-1 min-w-32 bg-transparent outline-none"
|
class="flex-1 min-w-32 bg-transparent outline-none text-lg"
|
||||||
@input="$emit('update:search-query', localSearchQuery)"
|
@input="$emit('update:search-query', localSearchQuery)"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Chips row (inside same block) -->
|
||||||
|
<div
|
||||||
|
v-if="availableChips.length > 0"
|
||||||
|
class="flex items-center justify-center gap-2 mt-3"
|
||||||
|
>
|
||||||
|
<button
|
||||||
|
v-for="chip in availableChips"
|
||||||
|
:key="chip.type"
|
||||||
|
class="btn btn-sm btn-ghost gap-1.5"
|
||||||
|
@click="$emit('start-select', chip.type)"
|
||||||
|
>
|
||||||
|
<Icon name="lucide:plus" size="14" />
|
||||||
|
{{ chip.label }}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Right: AI + Globe + Team + User -->
|
<!-- Right: AI + Globe + Team + User -->
|
||||||
@@ -178,21 +199,6 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Bottom row: Quick filter chips -->
|
|
||||||
<div
|
|
||||||
v-if="availableChips.length > 0"
|
|
||||||
class="flex items-center justify-center gap-3 px-4 py-2"
|
|
||||||
>
|
|
||||||
<button
|
|
||||||
v-for="chip in availableChips"
|
|
||||||
:key="chip.type"
|
|
||||||
class="btn btn-sm btn-ghost gap-1.5"
|
|
||||||
@click="$emit('start-select', chip.type)"
|
|
||||||
>
|
|
||||||
<Icon name="lucide:plus" size="14" />
|
|
||||||
{{ chip.label }}
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</header>
|
</header>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|||||||
@@ -60,7 +60,7 @@ const {
|
|||||||
} = useCatalogSearch()
|
} = useCatalogSearch()
|
||||||
|
|
||||||
// Collapsible header for catalog pages
|
// Collapsible header for catalog pages
|
||||||
const { headerOffset, isCollapsed } = useScrollCollapse(118)
|
const { headerOffset, isCollapsed } = useScrollCollapse(130)
|
||||||
|
|
||||||
// Theme state
|
// Theme state
|
||||||
const theme = useState<'cupcake' | 'night'>('theme', () => 'cupcake')
|
const theme = useState<'cupcake' | 'night'>('theme', () => 'cupcake')
|
||||||
@@ -131,10 +131,10 @@ const headerStyle = computed(() => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
// Main content padding-top to compensate for fixed header
|
// Main content padding-top to compensate for fixed header
|
||||||
// 90px = MainNav with search (2 rows: top 56px + chips 34px)
|
// 130px = MainNav with search block (input + chips in one block)
|
||||||
// 144px = MainNav + SubNav (orders, seller, settings)
|
// 184px = MainNav + SubNav (orders, seller, settings)
|
||||||
const mainStyle = computed(() => ({
|
const mainStyle = computed(() => ({
|
||||||
paddingTop: (isHomePage.value || isCatalogSection.value) ? '90px' : '144px'
|
paddingTop: (isHomePage.value || isCatalogSection.value) ? '130px' : '184px'
|
||||||
}))
|
}))
|
||||||
|
|
||||||
// Provide collapsed state to child components (CatalogPage needs it for map positioning)
|
// Provide collapsed state to child components (CatalogPage needs it for map positioning)
|
||||||
|
|||||||
@@ -1,38 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
<Stack gap="12">
|
<Stack gap="12">
|
||||||
<!-- Hero with search -->
|
|
||||||
<Section variant="plain">
|
|
||||||
<Stack gap="6" align="center">
|
|
||||||
<Heading :level="1">{{ $t('searchPage.hero.title') }}</Heading>
|
|
||||||
<Text tone="muted" align="center" class="max-w-lg">
|
|
||||||
{{ $t('searchPage.hero.subtitle') }}
|
|
||||||
</Text>
|
|
||||||
<div class="flex flex-wrap justify-center gap-4 mt-4">
|
|
||||||
<button
|
|
||||||
class="btn btn-lg btn-primary gap-2"
|
|
||||||
@click="startSelect('product')"
|
|
||||||
>
|
|
||||||
<Icon name="lucide:package" size="24" />
|
|
||||||
{{ $t('catalog.filters.product') }}
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
class="btn btn-lg btn-outline gap-2"
|
|
||||||
@click="startSelect('supplier')"
|
|
||||||
>
|
|
||||||
<Icon name="lucide:factory" size="24" />
|
|
||||||
{{ $t('catalog.filters.supplier') }}
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
class="btn btn-lg btn-outline gap-2"
|
|
||||||
@click="startSelect('hub')"
|
|
||||||
>
|
|
||||||
<Icon name="lucide:map-pin" size="24" />
|
|
||||||
{{ $t('catalog.filters.hub') }}
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</Stack>
|
|
||||||
</Section>
|
|
||||||
|
|
||||||
<!-- How it works -->
|
<!-- How it works -->
|
||||||
<Section variant="plain">
|
<Section variant="plain">
|
||||||
<Stack gap="6" align="center">
|
<Stack gap="6" align="center">
|
||||||
@@ -123,6 +90,4 @@
|
|||||||
definePageMeta({
|
definePageMeta({
|
||||||
layout: 'topnav'
|
layout: 'topnav'
|
||||||
})
|
})
|
||||||
|
|
||||||
const { startSelect } = useCatalogSearch()
|
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
Reference in New Issue
Block a user