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 @@ + + + + + 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 @@ + + + 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 @@ + + + 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 @@