diff --git a/packages/boilerplate/api-client/src/api/getCategory/index.ts b/packages/boilerplate/api-client/src/api/getCategory/index.ts
index 4f42f4ad660..4e5d960ace2 100644
--- a/packages/boilerplate/api-client/src/api/getCategory/index.ts
+++ b/packages/boilerplate/api-client/src/api/getCategory/index.ts
@@ -5,25 +5,81 @@ export default async function getCategory(context, params, customQuery?: CustomQ
return Promise.resolve([
{
id: 1,
+ name: 'New',
+ slug: 'new',
+ childCount: 2,
+ children: [
+ {
+ id: 15,
+ name: 'Women',
+ slug: 'new-women',
+ childCount: 2,
+ children: [
+ {
+ id: 16,
+ name: 'Clothing',
+ slug: 'new-women-clothing',
+ childCount: 0,
+ children: []
+ },
+ {
+ id: 17,
+ name: 'Shoes',
+ slug: 'new-women-shoes',
+ childCount: 0,
+ children: []
+ }
+ ]
+ },
+ {
+ id: 11,
+ name: 'Men',
+ slug: 'new-men',
+ childCount: 1,
+ children: [
+ {
+ id: 18,
+ name: 'Clothing',
+ slug: 'new-men-clothing',
+ childCount: 0,
+ children: []
+ },
+ {
+ id: 19,
+ name: 'Shoes',
+ slug: 'new-men-shoes',
+ childCount: 0,
+ children: []
+ }
+ ]
+ }
+ ]
+ },
+ {
+ id: 2,
name: 'Women',
slug: 'women',
- items: [
+ childCount: 2,
+ children: [
{
id: 4,
name: 'Women jackets',
slug: 'women-jackets',
- items: [
+ childCount: 2,
+ children: [
{
id: 9,
name: 'Winter jackets',
slug: 'winter-jackets',
- items: []
+ childCount: 0,
+ children: []
},
{
id: 10,
name: 'Autumn jackets',
slug: 'autmun-jackets',
- items: []
+ childCount: 0,
+ children: []
}
]
},
@@ -31,44 +87,51 @@ export default async function getCategory(context, params, customQuery?: CustomQ
id: 5,
name: 'Skirts',
slug: 'skirts',
- items: []
+ childCount: 0,
+ children: []
}
]
},
{
- id: 2,
+ id: 3,
name: 'Men',
slug: 'men',
- items: [
+ childCount: 1,
+ children: [
{
id: 6,
name: 'Men T-shirts',
slug: 'men-tshirts',
- items: []
+ childCount: 0,
+ children: []
}
]
},
{
- id: 3,
+ id: 4,
name: 'Kids',
slug: 'kids',
- items: [
+ childCount: 1,
+ children: [
{
id: 7,
name: 'Toys',
slug: 'toys',
- items: [
+ childCount: 2,
+ children: [
{
id: 8,
name: 'Toy Cars',
slug: 'toy-cars',
- items: []
+ childCount: 0,
+ children: []
},
{
id: 8,
name: 'Dolls',
slug: 'dolls',
- items: []
+ childCount: 0,
+ children: []
}
]
}
diff --git a/packages/boilerplate/theme/static/megamenu/bannerA.webp b/packages/boilerplate/theme/static/megamenu/bannerA.webp
new file mode 100644
index 00000000000..a5ab92d2662
Binary files /dev/null and b/packages/boilerplate/theme/static/megamenu/bannerA.webp differ
diff --git a/packages/boilerplate/theme/static/megamenu/bannerB.webp b/packages/boilerplate/theme/static/megamenu/bannerB.webp
new file mode 100644
index 00000000000..32290c8e352
Binary files /dev/null and b/packages/boilerplate/theme/static/megamenu/bannerB.webp differ
diff --git a/packages/commercetools/theme/components/Header/HeaderNav.vue b/packages/commercetools/theme/components/Header/HeaderNav.vue
new file mode 100644
index 00000000000..da071b95e29
--- /dev/null
+++ b/packages/commercetools/theme/components/Header/HeaderNav.vue
@@ -0,0 +1,111 @@
+
+
+ handleMouseEnter(category.slug)"
+ @mouseleave="() => handleMouseLeave()"
+ @click="handleMouseLeave()"
+ :link="localePath(`/c/${category.slug}`)"
+ >
+
+
+
+
+
+
+
+ {{ subCategoryChild.name }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/packages/commercetools/theme/components/Header/MobileMenu.vue b/packages/commercetools/theme/components/Header/MobileMenu.vue
new file mode 100644
index 00000000000..cab0fb08a41
--- /dev/null
+++ b/packages/commercetools/theme/components/Header/MobileMenu.vue
@@ -0,0 +1,109 @@
+
+
+
+
+
+
+
+
+
+
+ {{ subCategory.name }}
+
+
+
+
+
+
+
+
+
+
diff --git a/packages/commercetools/theme/middleware.config.js b/packages/commercetools/theme/middleware.config.js
index 834ffc13788..03fb2174ff8 100644
--- a/packages/commercetools/theme/middleware.config.js
+++ b/packages/commercetools/theme/middleware.config.js
@@ -24,6 +24,36 @@ module.exports = {
},
currency: 'USD',
country: 'US'
+ },
+ customQueries: {
+ 'root-categories-query': ({ variables }) => {
+
+ const rootCategoriesQuery = `
+ fragment DefaultRootCategory on Category {
+ id
+ slug(acceptLanguage: $acceptLanguage)
+ name(acceptLanguage: $acceptLanguage)
+ childCount
+ }
+ query categories($where: String, $sort: [String!], $limit: Int, $offset: Int, $acceptLanguage: [Locale!]) {
+ categories(where: $where, sort: $sort, limit: $limit, offset: $offset) {
+ offset
+ count
+ total
+ results {
+ ...DefaultRootCategory
+ }
+ }
+ }
+ `;
+
+ variables.where = 'parent is not defined';
+
+ return {
+ query: rootCategoriesQuery,
+ variables
+ };
+ }
}
}
}
diff --git a/packages/commercetools/theme/static/megamenu/bannerA.webp b/packages/commercetools/theme/static/megamenu/bannerA.webp
new file mode 100644
index 00000000000..a5ab92d2662
Binary files /dev/null and b/packages/commercetools/theme/static/megamenu/bannerA.webp differ
diff --git a/packages/commercetools/theme/static/megamenu/bannerB.webp b/packages/commercetools/theme/static/megamenu/bannerB.webp
new file mode 100644
index 00000000000..32290c8e352
Binary files /dev/null and b/packages/commercetools/theme/static/megamenu/bannerB.webp differ
diff --git a/packages/core/core/__tests__/composables/useUiState.spec.ts b/packages/core/core/__tests__/composables/useUiState.spec.ts
index 4ec89bdd5c7..d775a4ca4b0 100644
--- a/packages/core/core/__tests__/composables/useUiState.spec.ts
+++ b/packages/core/core/__tests__/composables/useUiState.spec.ts
@@ -6,11 +6,13 @@ const {
isLoginModalOpen,
isCategoryGridView,
isFilterSidebarOpen,
+ isMobileMenuOpen,
toggleCartSidebar,
toggleWishlistSidebar,
toggleLoginModal,
toggleCategoryGridView,
- toggleFilterSidebar
+ toggleFilterSidebar,
+ toggleMobileMenu
} = useUiState();
describe('useUiState', () => {
@@ -53,4 +55,12 @@ describe('useUiState', () => {
expect(expectedIsFilterSidebarOpen).toBe(isFilterSidebarOpen.value);
});
+
+ it('Mobile Menu', () => {
+ const expectedIsMobileMenuOpen = !isMobileMenuOpen.value;
+
+ toggleMobileMenu();
+
+ expect(expectedIsMobileMenuOpen).toBe(isMobileMenuOpen.value);
+ });
});
diff --git a/packages/core/docs/changelog/5267.js b/packages/core/docs/changelog/5267.js
new file mode 100644
index 00000000000..f349c125f6f
--- /dev/null
+++ b/packages/core/docs/changelog/5267.js
@@ -0,0 +1,8 @@
+module.exports = {
+ description: 'added MegaMenu to theme',
+ link: 'https://github.com/vuestorefront/vue-storefront/issues/5267',
+ isBreaking: false,
+ breakingChanges: [],
+ author: 'Łukasz Jędrasik',
+ linkToGitHubAccount: 'https://github.com/lukaszjedrasik'
+};
diff --git a/packages/core/nuxt-theme-module/theme/components/BottomNavigation.vue b/packages/core/nuxt-theme-module/theme/components/BottomNavigation.vue
index 39792b33c73..696dcdaac55 100644
--- a/packages/core/nuxt-theme-module/theme/components/BottomNavigation.vue
+++ b/packages/core/nuxt-theme-module/theme/components/BottomNavigation.vue
@@ -2,9 +2,9 @@
-
+
-
+
@@ -31,6 +31,7 @@
import { SfBottomNavigation, SfIcon, SfCircleIcon } from '@storefront-ui/vue';
import { useUiState } from '~/composables';
import { useUser } from '<%= options.generate.replace.composables %>';
+import { watch } from '@vue/composition-api';
export default {
components: {
@@ -39,20 +40,32 @@ export default {
SfCircleIcon
},
setup(props, { root }) {
- const { toggleCartSidebar, toggleWishlistSidebar, toggleLoginModal } = useUiState();
+ const { toggleCartSidebar, toggleWishlistSidebar, toggleLoginModal, toggleMobileMenu, isMobileMenuOpen } = useUiState();
const { isAuthenticated } = useUser();
const handleAccountClick = async () => {
+ if (isMobileMenuOpen.value) toggleMobileMenu();
if (isAuthenticated.value) {
return root.$router.push('/my-account');
}
toggleLoginModal();
};
+ const handleHomeClick = () => {
+ if (isMobileMenuOpen.value) toggleMobileMenu();
+ };
+
+ watch(isMobileMenuOpen, () => {
+ if (isMobileMenuOpen.value) document.body.classList.add('no-scroll');
+ else document.body.classList.remove('no-scroll');
+ });
+
return {
toggleWishlistSidebar,
toggleCartSidebar,
- handleAccountClick
+ toggleMobileMenu,
+ handleAccountClick,
+ handleHomeClick
};
}
};
diff --git a/packages/core/nuxt-theme-module/theme/components/AppHeader.vue b/packages/core/nuxt-theme-module/theme/components/Header/AppHeader.vue
similarity index 88%
rename from packages/core/nuxt-theme-module/theme/components/AppHeader.vue
rename to packages/core/nuxt-theme-module/theme/components/Header/AppHeader.vue
index e2218e899ec..734bc434d9b 100644
--- a/packages/core/nuxt-theme-module/theme/components/AppHeader.vue
+++ b/packages/core/nuxt-theme-module/theme/components/Header/AppHeader.vue
@@ -1,5 +1,6 @@
+
+
+
+
diff --git a/packages/core/nuxt-theme-module/theme/components/LocaleSelector.vue b/packages/core/nuxt-theme-module/theme/components/Header/LocaleSelector.vue
similarity index 100%
rename from packages/core/nuxt-theme-module/theme/components/LocaleSelector.vue
rename to packages/core/nuxt-theme-module/theme/components/Header/LocaleSelector.vue
diff --git a/packages/core/nuxt-theme-module/theme/components/Header/MobileMenu.vue b/packages/core/nuxt-theme-module/theme/components/Header/MobileMenu.vue
new file mode 100644
index 00000000000..62277080f7b
--- /dev/null
+++ b/packages/core/nuxt-theme-module/theme/components/Header/MobileMenu.vue
@@ -0,0 +1,75 @@
+
+
+
+
+
+
+
+
+
+
+ {{ subCategoryChild.name }}
+
+
+
+
+
+
+
+
+
+
diff --git a/packages/core/nuxt-theme-module/theme/components/Header/NewCatBanners.vue b/packages/core/nuxt-theme-module/theme/components/Header/NewCatBanners.vue
new file mode 100644
index 00000000000..53f2314cd0e
--- /dev/null
+++ b/packages/core/nuxt-theme-module/theme/components/Header/NewCatBanners.vue
@@ -0,0 +1,70 @@
+
+
+
+
+
+
+
diff --git a/packages/core/nuxt-theme-module/theme/components/TopBar.vue b/packages/core/nuxt-theme-module/theme/components/Header/TopBar.vue
similarity index 100%
rename from packages/core/nuxt-theme-module/theme/components/TopBar.vue
rename to packages/core/nuxt-theme-module/theme/components/Header/TopBar.vue
diff --git a/packages/core/nuxt-theme-module/theme/composables/useUiState.ts b/packages/core/nuxt-theme-module/theme/composables/useUiState.ts
index e526b286937..95b3828dcf4 100644
--- a/packages/core/nuxt-theme-module/theme/composables/useUiState.ts
+++ b/packages/core/nuxt-theme-module/theme/composables/useUiState.ts
@@ -9,22 +9,31 @@ const state = reactive({
isWishlistSidebarOpen: false,
isLoginModalOpen: false,
isCategoryGridView: true,
- isFilterSidebarOpen: false
+ isFilterSidebarOpen: false,
+ isMobileMenuOpen: false
});
const useUiState = () => {
+ const isMobileMenuOpen = computed(() => state.isMobileMenuOpen);
+ const toggleMobileMenu = () => {
+ state.isMobileMenuOpen = !state.isMobileMenuOpen;
+ };
+
const isCartSidebarOpen = computed(() => state.isCartSidebarOpen);
const toggleCartSidebar = () => {
+ if (state.isMobileMenuOpen) toggleMobileMenu();
state.isCartSidebarOpen = !state.isCartSidebarOpen;
};
const isWishlistSidebarOpen = computed(() => state.isWishlistSidebarOpen);
const toggleWishlistSidebar = () => {
+ if (state.isMobileMenuOpen) toggleMobileMenu();
state.isWishlistSidebarOpen = !state.isWishlistSidebarOpen;
};
const isLoginModalOpen = computed(() => state.isLoginModalOpen);
const toggleLoginModal = () => {
+ if (state.isMobileMenuOpen) toggleMobileMenu();
state.isLoginModalOpen = !state.isLoginModalOpen;
};
@@ -44,11 +53,13 @@ const useUiState = () => {
isLoginModalOpen,
isCategoryGridView,
isFilterSidebarOpen,
+ isMobileMenuOpen,
toggleCartSidebar,
toggleWishlistSidebar,
toggleLoginModal,
toggleCategoryGridView,
- toggleFilterSidebar
+ toggleFilterSidebar,
+ toggleMobileMenu
};
};
diff --git a/packages/core/nuxt-theme-module/theme/layouts/account.vue b/packages/core/nuxt-theme-module/theme/layouts/account.vue
index 8e2755e7ad1..970d0a4acd9 100644
--- a/packages/core/nuxt-theme-module/theme/layouts/account.vue
+++ b/packages/core/nuxt-theme-module/theme/layouts/account.vue
@@ -12,9 +12,9 @@