Refactor catalog layout: replace Teleport with provide/inject
All checks were successful
Build Docker Image / build (push) Successful in 4m1s
All checks were successful
Build Docker Image / build (push) Successful in 4m1s
- Create useCatalogLayout composable for data transfer from pages to layout - Layout now owns SearchBar and CatalogMap components directly - Pages provide data via provideCatalogLayout() - Fixes navigation glitches (multiple SearchBars) when switching tabs - Support custom subNavItems for clientarea pages - Unify 6 pages to use catalog layout: - catalog/offers, suppliers, hubs - clientarea/orders, addresses, offers
This commit is contained in:
@@ -37,7 +37,7 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Layer 3: SearchBar (fixed, slides to top:0) - teleport target -->
|
||||
<!-- Layer 3: SearchBar (fixed, slides to top:0) -->
|
||||
<div
|
||||
class="fixed left-0 right-0 z-40 h-14 bg-base-100 border-b border-base-300 flex items-center"
|
||||
:style="{ top: `${searchBarTop}px` }"
|
||||
@@ -49,16 +49,51 @@
|
||||
>
|
||||
<Icon :name="isCollapsed ? 'lucide:chevron-down' : 'lucide:chevron-up'" size="16" />
|
||||
</button>
|
||||
<!-- SearchBar content teleported here (flex-1 for full width) -->
|
||||
<div id="catalog-searchbar" class="flex-1 min-w-0" />
|
||||
<!-- SearchBar from page via inject -->
|
||||
<div class="flex-1 min-w-0 px-4 lg:px-6 py-2">
|
||||
<CatalogSearchBar
|
||||
v-if="catalogData"
|
||||
v-model:search-query="catalogData.searchQuery.value"
|
||||
:active-filters="toValue(catalogData.activeFilters)"
|
||||
:displayed-count="toValue(catalogData.displayedCount)"
|
||||
:total-count="toValue(catalogData.totalCount)"
|
||||
@remove-filter="catalogData.onRemoveFilter"
|
||||
@search="catalogData.onSearch"
|
||||
>
|
||||
<template v-if="catalogData.filterComponent" #filters>
|
||||
<component :is="catalogData.filterComponent" />
|
||||
</template>
|
||||
</CatalogSearchBar>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Layer 4: Map (fixed, right side, to bottom) - teleport target -->
|
||||
<!-- Layer 4: Map (fixed, right side, to bottom) -->
|
||||
<div
|
||||
id="catalog-map"
|
||||
class="fixed right-0 bottom-0 w-3/5 z-20 hidden lg:block"
|
||||
:style="{ top: `${mapTop}px` }"
|
||||
/>
|
||||
>
|
||||
<div v-if="catalogData" class="h-full w-full relative">
|
||||
<!-- Search with map checkbox -->
|
||||
<label class="absolute top-4 left-4 z-10 bg-white/90 backdrop-blur px-3 py-2 rounded-lg shadow flex items-center gap-2 cursor-pointer">
|
||||
<input type="checkbox" v-model="catalogData.searchWithMap.value" class="checkbox checkbox-sm" />
|
||||
<span class="text-sm">{{ t('catalogMap.searchWithMap') }}</span>
|
||||
</label>
|
||||
<ClientOnly>
|
||||
<CatalogMap
|
||||
ref="mapRef"
|
||||
:map-id="catalogData.mapId"
|
||||
:items="catalogData.useServerClustering ? [] : toValue(catalogData.mapItems)"
|
||||
:clustered-points="catalogData.useServerClustering ? toValue(catalogData.clusteredNodes || []) : []"
|
||||
:use-server-clustering="catalogData.useServerClustering || false"
|
||||
:point-color="catalogData.pointColor"
|
||||
:hovered-item-id="catalogData.hoveredItemId.value"
|
||||
:hovered-item="toValue(catalogData.hoveredItem)"
|
||||
@select-item="catalogData.onMapSelect"
|
||||
@bounds-change="catalogData.onBoundsChange"
|
||||
/>
|
||||
</ClientOnly>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Content area (left side, with dynamic padding for fixed header) -->
|
||||
<div
|
||||
@@ -90,17 +125,40 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Mobile map view - teleport target -->
|
||||
<!-- Mobile map view -->
|
||||
<div
|
||||
v-if="mobileView === 'map'"
|
||||
id="catalog-map-mobile"
|
||||
v-if="mobileView === 'map' && catalogData"
|
||||
class="lg:hidden fixed inset-0 z-20"
|
||||
:style="{ top: `${mapTop}px` }"
|
||||
/>
|
||||
>
|
||||
<div class="h-full w-full relative">
|
||||
<!-- Search with map checkbox -->
|
||||
<label class="absolute top-4 left-4 z-10 bg-white/90 backdrop-blur px-3 py-2 rounded-lg shadow flex items-center gap-2 cursor-pointer">
|
||||
<input type="checkbox" v-model="catalogData.searchWithMap.value" class="checkbox checkbox-sm" />
|
||||
<span class="text-sm">{{ t('catalogMap.searchWithMap') }}</span>
|
||||
</label>
|
||||
<ClientOnly>
|
||||
<CatalogMap
|
||||
:map-id="`${catalogData.mapId}-mobile`"
|
||||
:items="catalogData.useServerClustering ? [] : toValue(catalogData.mapItems)"
|
||||
:clustered-points="catalogData.useServerClustering ? toValue(catalogData.clusteredNodes || []) : []"
|
||||
:use-server-clustering="catalogData.useServerClustering || false"
|
||||
:point-color="catalogData.pointColor"
|
||||
:hovered-item-id="catalogData.hoveredItemId.value"
|
||||
:hovered-item="toValue(catalogData.hoveredItem)"
|
||||
@select-item="catalogData.onMapSelect"
|
||||
@bounds-change="catalogData.onBoundsChange"
|
||||
/>
|
||||
</ClientOnly>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { toValue } from 'vue'
|
||||
import { useCatalogLayoutData } from '~/composables/useCatalogLayout'
|
||||
|
||||
const runtimeConfig = useRuntimeConfig()
|
||||
const siteUrl = runtimeConfig.public.siteUrl || 'https://optovia.ru/'
|
||||
const { signIn, signOut, loggedIn, fetch: fetchSession } = useAuth()
|
||||
@@ -108,6 +166,12 @@ const route = useRoute()
|
||||
const localePath = useLocalePath()
|
||||
const { t } = useI18n()
|
||||
|
||||
// Inject catalog data from page
|
||||
const catalogData = useCatalogLayoutData()
|
||||
|
||||
// Map ref for external flyTo access
|
||||
const mapRef = ref<{ flyTo: (lat: number, lng: number, zoom?: number) => void } | null>(null)
|
||||
|
||||
// Smooth collapsible header
|
||||
// MainNav: 64px (h-16)
|
||||
// SubNav: 40px (py-2 + links)
|
||||
@@ -160,12 +224,16 @@ const userInitials = computed(() => {
|
||||
return '?'
|
||||
})
|
||||
|
||||
// SubNavigation items for catalog section
|
||||
const subNavItems = computed(() => [
|
||||
// SubNavigation items - use page-provided items or default to catalog section
|
||||
const defaultSubNavItems = [
|
||||
{ label: t('cabinetNav.offers'), path: '/catalog/offers' },
|
||||
{ label: t('cabinetNav.suppliers'), path: '/catalog/suppliers' },
|
||||
{ label: t('cabinetNav.hubs'), path: '/catalog/hubs' },
|
||||
])
|
||||
]
|
||||
|
||||
const subNavItems = computed(() => {
|
||||
return catalogData?.subNavItems || defaultSubNavItems
|
||||
})
|
||||
|
||||
const isSubNavActive = (path: string) => {
|
||||
return route.path === localePath(path) || route.path.startsWith(localePath(path) + '/')
|
||||
|
||||
Reference in New Issue
Block a user