schon/storefront/components/base/header/catalog.vue
Alexandr SaVBaD Waltz 761fecf67f Features: 1) Add useWishlistOverwrite composable for wishlist mutations, including adding, removing, and bulk actions; 2) Introduce new localized UI texts for cart and wishlist operations; 3) Enhance filtering logic with parseAttributesString and route query synchronization;
Fixes: 1) Replace `ElNotification` calls with `useNotification` utility across all authentication and user-related composables; 2) Add missing semicolons in multiple index exports and styled components; 3) Resolve issues with reactivity in `useStore` composable by renaming and restructuring product variables;

Extra: 1) Refactor localized strings and translations for better readability and maintenance; 2) Tweak styles including scoped styles, z-index adjustments, and SCSS mixins; 3) Remove unused components and imports to streamline storefront layout.
2025-07-06 19:49:26 +03:00

271 lines
No EOL
5.6 KiB
Vue

<template>
<div class="catalog" ref="blockRef">
<button
@click="setBlock(!isBlockOpen)"
class="catalog__button"
:class="[{ active: isBlockOpen }]"
>
{{ t('header.catalog.title') }}
<span></span>
</button>
<div class="container">
<div class="categories" :class="[{active: isBlockOpen}]">
<div class="categories__block" v-if="categories.length > 0">
<div class="categories__left">
<p
v-for="category in categories"
:key="category.node.uuid"
:class="[{ active: category.node.uuid === activeCategory.uuid }]"
@click="setActiveCategory( category.node)"
>
{{ category.node.name }}
</p>
</div>
<div class="categories__main">
<div
class="categories__main-block"
v-for="mainChildren in activeCategory.children"
:key="mainChildren.uuid"
>
<nuxt-link-locale
:to="`/catalog/${mainChildren.slug}`"
class="categories__main-link"
@click="setBlock(false)"
>
{{ mainChildren.name }}
</nuxt-link-locale>
<div class="categories__main-list">
<nuxt-link-locale
v-for="children in mainChildren.children"
:key="children.uuid"
:to="`/catalog/${children.slug}`"
@click="setBlock(false)"
>
{{ children.name }}
</nuxt-link-locale >
</div>
</div>
</div>
</div>
<div class="categories__empty" v-else><p>{{ t('header.catalog.empty') }}</p></div>
</div>
</div>
</div>
</template>
<script setup lang="ts">
import {ref} from "vue";
import {onClickOutside} from "@vueuse/core";
import type {ICategory} from "~/types";
import {useCategoryStore} from "~/stores/category";
const { t } = useI18n()
const categoryStore = useCategoryStore();
const categories = computed(() => categoryStore.categories)
const isBlockOpen = ref<boolean>(false)
const setBlock = (state: boolean) => {
isBlockOpen.value = state
}
// TODO: add loading state
const blockRef = ref(null)
onClickOutside(blockRef, () => setBlock(false))
const activeCategory = ref<ICategory>(categories.value[0]?.node)
const setActiveCategory = (category: ICategory) => {
activeCategory.value = category
}
</script>
<style lang="scss" scoped>
.catalog {
&__button {
cursor: pointer;
border-radius: $default_border_radius;
background-color: rgba($accent, 0.2);
border: 1px solid $accent;
padding: 5px 20px;
display: flex;
align-items: center;
gap: 10px;
transition: 0.2s;
color: $accent;
font-size: 16px;
font-weight: 600;
@include hover {
background-color: $accent;
color: $white;
}
& span {
transition: 0.2s;
font-size: 26px;
}
&.active {
background-color: $accent;
color: $white;
& span {
transform: rotate(-180deg);
}
}
}
}
.container {
position: absolute;
left: 50%;
top: 110%;
transform: translateX(-50%);
width: 100%;
}
.categories {
border-radius: $default_border_radius;
width: 100%;
background-color: $white;
box-shadow: 0 0 15px 1px $accentLight;
display: grid;
grid-template-rows: 0fr;
transition: grid-template-rows 0.2s ease;
overflow: hidden;
&.active {
grid-template-rows: 1fr;
}
& > * {
min-height: 0;
}
&__block {
display: grid;
grid-template-columns: 20% 80%;
max-height: 50vh;
}
&__columns {
& div {
padding: 20px 50px;
display: grid;
grid-template-columns: repeat(4, 1fr);
grid-column-gap: 30px;
grid-row-gap: 5px;
& p {
cursor: pointer;
padding: 5px 20px;
transition: 0.2s;
font-size: 16px;
font-weight: 600;
@include hover {
color: $accent;
}
}
}
}
&__left {
flex-shrink: 0;
padding-block: 10px;
overflow: auto;
& p {
cursor: pointer;
transition: 0.2s;
flex-shrink: 0;
padding: 10px;
border-left: 3px solid $white;
font-weight: 700;
@include hover {
color: $accent;
}
&.active {
border-color: $accent;
color: $accent;
}
}
}
&__main {
padding: 20px;
border-left: 2px solid $accentDark;
overflow: auto;
&-block {
padding-bottom: 15px;
border-bottom: 1px solid #eeeeee;
margin-bottom: 15px;
&:last-child {
border-bottom: none;
margin-bottom: 0;
}
}
&-link {
position: relative;
width: fit-content;
cursor: pointer;
font-weight: 600;
font-size: 16px;
&::after {
content: "";
position: absolute;
bottom: -2px;
left: 0;
height: 2px;
width: 0;
transition: all .3s ease;
background-color: $accent;
}
@include hover {
&::after {
width: 100%;
}
}
}
&-list {
margin-top: 10px;
display: grid;
grid-template-columns: repeat(3,1fr);
grid-column-gap: 30px;
grid-row-gap: 5px;
& a {
cursor: pointer;
transition: 0.1s;
font-size: 14px;
@include hover {
color: $accent;
}
}
}
}
&__empty {
& p {
padding: 20px;
text-align: center;
font-size: 16px;
font-weight: 600;
}
}
}
</style>