feat(mall): pin home category menu beside hero carousel
Home hero becomes a 450px flex row with a pinned 240x450 category rail (ShellCategoryMenu `pinned` mode) left of the 960px center-cropped carousel; hovering a top-level category expands the mega-menu over the carousel. The header no longer collapses on scroll, and its category dropdown is now hover-only on non-home pages. OpenSpec change: openspec/changes/pin-home-category-menu BREAKING CHANGE: the header logo/search/cart bar no longer auto-hides past 200px of scroll, and the home page no longer shows the header category dropdown (it is pinned in the hero row instead).
This commit is contained in:
@@ -2,6 +2,8 @@
|
||||
import { topCategories, childCategories } from "~/mock/data";
|
||||
import { t as pick } from "@vmall/shared";
|
||||
|
||||
const props = withDefaults(defineProps<{ pinned?: boolean }>(), { pinned: false });
|
||||
|
||||
const { locale } = useI18n();
|
||||
const open = ref(false);
|
||||
const hoverId = ref<string | null>(null);
|
||||
@@ -19,7 +21,7 @@ defineExpose({ open });
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="catmenu" @mouseleave="hoverId = null">
|
||||
<div class="catmenu" :class="{ pinned: props.pinned }" @mouseleave="hoverId = null">
|
||||
<ul class="lvl1">
|
||||
<li
|
||||
v-for="g in cats"
|
||||
@@ -62,6 +64,15 @@ defineExpose({ open });
|
||||
display: flex;
|
||||
width: 960px;
|
||||
}
|
||||
/* pinned: in-flow left rail of the home hero row, flyout overlays the carousel */
|
||||
.catmenu.pinned {
|
||||
position: relative;
|
||||
top: auto;
|
||||
left: auto;
|
||||
flex: none;
|
||||
width: 240px;
|
||||
height: 450px;
|
||||
}
|
||||
.lvl1 {
|
||||
width: 240px;
|
||||
margin: 0;
|
||||
@@ -131,4 +142,20 @@ defineExpose({ open });
|
||||
.sub-items a:hover {
|
||||
color: var(--mall-red);
|
||||
}
|
||||
/* pinned overrides: 240x450 rail, flyout opens to the right over the carousel */
|
||||
.catmenu.pinned .lvl1 {
|
||||
box-sizing: border-box;
|
||||
height: 450px;
|
||||
min-height: 450px;
|
||||
}
|
||||
.catmenu.pinned .sub {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 240px;
|
||||
box-sizing: border-box;
|
||||
width: 700px;
|
||||
height: 450px;
|
||||
min-height: 450px;
|
||||
overflow: auto;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -32,19 +32,14 @@ onMounted(async () => {
|
||||
|
||||
const menuOpen = ref(false);
|
||||
const isHome = computed(() => route.path === "/");
|
||||
const collapsed = ref(false);
|
||||
onMounted(() => {
|
||||
const onScroll = (): void => {
|
||||
collapsed.value = window.scrollY > 200;
|
||||
};
|
||||
window.addEventListener("scroll", onScroll, { passive: true });
|
||||
});
|
||||
const showMenu = computed(() => menuOpen.value || (isHome.value && !collapsed.value));
|
||||
// The home page pins the category menu in the hero row instead, so the header
|
||||
// dropdown is hover-only and only on non-home pages.
|
||||
const showMenu = computed(() => menuOpen.value && !isHome.value);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<header class="hdr">
|
||||
<div v-show="!collapsed" class="w1200 hdr-top">
|
||||
<div class="w1200 hdr-top">
|
||||
<NuxtLink to="/" class="logo">VMall</NuxtLink>
|
||||
<div class="search-box">
|
||||
<input
|
||||
|
||||
@@ -9,7 +9,10 @@ const floors = computed(() => homeFloors().filter((f) => f.products.length > 0))
|
||||
<template>
|
||||
<div>
|
||||
<div class="w1200 hero">
|
||||
<UiCarousel :images="MOCK_BANNERS" :height="450" />
|
||||
<ShellCategoryMenu pinned />
|
||||
<div class="hero-slider">
|
||||
<UiCarousel :images="MOCK_BANNERS" :height="450" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="w1200 strip">
|
||||
@@ -49,7 +52,14 @@ const floors = computed(() => homeFloors().filter((f) => f.products.length > 0))
|
||||
|
||||
<style scoped>
|
||||
.hero {
|
||||
display: flex;
|
||||
height: 450px;
|
||||
margin-top: 0;
|
||||
background: #fff;
|
||||
}
|
||||
.hero-slider {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
.strip {
|
||||
display: flex;
|
||||
|
||||
Reference in New Issue
Block a user