Skip to content

Commit

Permalink
Mini Cart (#12)
Browse files Browse the repository at this point in the history
* upgrade cart

* fixed cart id set

* pdp update, styling

* header styling

* update

* css fix

* fixed versions

* fix Safari styling

* mini cart counter

* fixed empty count

* style fixes
  • Loading branch information
fnhipster authored Feb 21, 2024
1 parent 560822b commit bdd36a6
Show file tree
Hide file tree
Showing 33 changed files with 232 additions and 116 deletions.
102 changes: 76 additions & 26 deletions blocks/header/header.css
Original file line number Diff line number Diff line change
Expand Up @@ -53,10 +53,6 @@ header nav p {
line-height: 1;
}

header nav a:any-link {
color: currentcolor;
}

/* hamburger */
header nav .nav-hamburger {
grid-area: hamburger;
Expand Down Expand Up @@ -273,23 +269,61 @@ header nav .nav-sections ul > li > ul > li {
}

/* tools */
header nav .nav-panel {
z-index: 100;
position: absolute;
box-shadow: var(--shape-shadow-2);
background: var(--background-color);
box-sizing: content-box;
left: 0;
width: 100%;
display: none;
box-sizing: border-box;
}

header nav .nav-panel--show {
display: block;
}

header nav .nav-tools {
grid-area: tools;
display: flex;
gap: 10px;
height: 100%;
gap: var(--spacing-small);
}

header nav .nav-tools > button, header nav .nav-tools .minicart-wrapper > button {
header nav .nav-tools .search-wrapper > button,
header nav .nav-tools .minicart-wrapper > button {
color: var(--color-brand-700);
background: transparent;
padding: 5px 10px;
height: 100%;
border: unset;
cursor: pointer;
}

header nav .nav-tools button.nav-cart-button {
background-image: url('../../icons/cart.svg');
background-repeat: no-repeat;
background-position: left center;
padding-left: 27px;
background-position: center;
padding: 5px 15px;
position: relative;
}

header nav .nav-tools button.nav-cart-button[data-count]::after {
content: attr(data-count);
position: absolute;
top: 1.5em;
right: -0.75em;
background-color: var(--color-brand-700);
color: var(--color-neutral-100);
border-radius: 50%;
width: 1.75em;
height: 1.75em;
display: flex;
align-items: center;
font-size: 0.8em;
justify-content: center;
}

header nav .nav-tools button.nav-search-button {
Expand All @@ -300,33 +334,49 @@ header nav .nav-tools button.nav-search-button {
padding: 5px 15px;
}

header .nav-search-input {
position: absolute;
z-index: 99;
top: var(--nav-height);
box-shadow: var(--shape-shadow-2);
background: var(--color-neutral-50);
left: 0;
right: 0;
padding: 20px;
header .nav-search-panel {
padding: var(--spacing-small);
}

header .nav-search-input.hidden {
display: none;
header .nav-search-panel input {
width: 100%;
padding: 10px;
box-sizing: border-box;
font: var(--type-headline-2-default-font);
letter-spacing: var(--type-headline-2-default-letter-spacing);
border: 1px solid currentcolor;
appearance: none;
}

header .nav-search-input input {
width: 100%;
padding: 5px;
/* Mini Cart */

.cart-mini-cart:not(:has(.cart-empty-cart)) {
max-height: 760px;
min-height: 360px;
}

/* If viewport height is below max, set max height to 90% of viewport */
@media (height < 820px) {
.cart-mini-cart:not(:has(.cart-empty-cart)) {
max-height: calc(100vh - var(--nav-height));
}
}

@media (width >= 1024px) {
header .nav-search-input {
header nav .minicart-wrapper,
header nav .search-wrapper {
position: relative;
}

header nav .minicart-panel {
width: 398px;
left: unset;
right: 20px;
right: 0;
}

header .nav-search-input input {
min-width: 400px;
header nav .nav-search-panel {
width: 398px;
left: unset;
right: 0;
}
}
107 changes: 82 additions & 25 deletions blocks/header/header.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,16 @@
/* eslint-disable import/no-unresolved */

// Drop-in Providers
import { render as cartProvider } from '@dropins/storefront-cart/render.js';

// Drop-in Containers
import MiniCart from '@dropins/storefront-cart/containers/MiniCart.js';

// Drop-in Tools
import { events } from '@dropins/elsie/event-bus.js';
import { getMetadata } from '../../scripts/aem.js';

import { loadFragment } from '../fragment/fragment.js';
import { getMetadata } from '../../scripts/aem.js';

// media query match that indicates mobile/tablet width
const isDesktop = window.matchMedia('(min-width: 900px)');
Expand Down Expand Up @@ -133,36 +141,85 @@ export default async function decorate(block) {

const navTools = nav.querySelector('.nav-tools');

// Minicart
const minicartButton = document.createRange().createContextualFragment(`<div class="minicart-wrapper">
<button type="button" class="button nav-cart-button">&nbsp;&nbsp;</button>
<div class="minicart-panel"></div>
</div>`);

navTools.append(minicartButton);

// TODO: Toggle Mini Cart; Mini Cart Drop-in is not yet available, go to Cart page instead.
// const minicartPanel = navTools.querySelector('.minicart-panel');
// let cartVisible = false;
navTools.querySelector('.nav-cart-button').addEventListener('click', async () => {
// cartVisible = !cartVisible;
// minicartPanel.classList.toggle('minicart-panel-visible', cartVisible);
window.location.href = '/cart';
});
/** Mini Cart */
const minicart = document.createRange().createContextualFragment(`
<div class="minicart-wrapper">
<button type="button" class="button nav-cart-button"></button>
<div class="minicart-panel nav-panel"></div>
</div>
`);

navTools.append(minicart);

const minicartPanel = navTools.querySelector('.minicart-panel');

const cartButton = navTools.querySelector('.nav-cart-button');
cartButton.setAttribute('aria-label', 'Cart');

async function toggleMiniCart(state) {
const show = state ?? !minicartPanel.classList.contains('nav-panel--show');

if (show) {
await cartProvider.render(MiniCart, {
routeEmptyCartCTA: () => '/',
routeProduct: (product) => `/products/${product.url.urlKey}/${product.sku}`,
routeCart: () => '/cart',
routeCheckout: () => '/checkout',
})(minicartPanel);
} else {
minicartPanel.innerHTML = '';
}

minicartPanel.classList.toggle('nav-panel--show', show);
}

cartButton.addEventListener('click', () => toggleMiniCart());

// Cart Item Counter
events.on('cart/data', (data) => {
navTools.querySelector('.nav-cart-button').textContent = data?.totalQuantity || '0';
if (data?.totalQuantity) {
cartButton.setAttribute('data-count', data.totalQuantity);
} else {
cartButton.removeAttribute('data-count');
}
}, { eager: true });

// Search
const searchInput = document.createRange().createContextualFragment('<div class="nav-search-input hidden"><form action="/search" method="GET"><input type="search" name="q" placeholder="Search" /></form></div>');
document.body.querySelector('header').append(searchInput);
/** Search */

const search = document.createRange().createContextualFragment(`
<div class="search-wrapper">
<button type="button" class="button nav-search-button">Search</button>
<div class="nav-search-input nav-search-panel nav-panel"><form action="/search" method="GET"><input type="search" name="q" placeholder="Search" /></form></div>
</div>
`);

const searchButton = document.createRange().createContextualFragment('<button type="button" class="button nav-search-button">Search</button>');
navTools.append(searchButton);
navTools.querySelector('.nav-search-button').addEventListener('click', () => {
document.querySelector('header .nav-search-input').classList.toggle('hidden');
navTools.append(search);

const searchPanel = navTools.querySelector('.nav-search-panel');

const searchButton = navTools.querySelector('.nav-search-button');

const searchInput = searchPanel.querySelector('input');

function toggleSearch(state) {
const show = state ?? !searchPanel.classList.contains('nav-panel--show');

searchPanel.classList.toggle('nav-panel--show', show);

if (show) searchInput.focus();
}

navTools.querySelector('.nav-search-button').addEventListener('click', () => toggleSearch());

// Close panels when clicking outside
document.addEventListener('click', (e) => {
if (!minicartPanel.contains(e.target) && !cartButton.contains(e.target)) {
toggleMiniCart(false);
}

if (!searchPanel.contains(e.target) && !searchButton.contains(e.target)) {
toggleSearch(false);
}
});

// hamburger for mobile
Expand Down
22 changes: 10 additions & 12 deletions blocks/product-details/product-details.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ export default async function decorate(block) {
});

// Render Containers

return productRenderer.render(ProductDetails, {
sku: getSkuFromUrl(),
carousel: {
Expand All @@ -46,36 +47,33 @@ export default async function decorate(block) {
slots: {
Actions: (ctx) => {
// Add to Cart Button
ctx.appendButton({
ctx.appendButton((next) => ({
text: 'Add to Cart',
icon: 'Cart',
variant: 'primary',
onClick: async () => {
try {
if (!ctx.valid) {
if (!next.valid) {
// eslint-disable-next-line no-console
console.warn('Invalid product', ctx.values);
console.warn('Invalid product', next.values);
return;
}

await addProductsToCart([{
...ctx.values,
}]);

window.location.href = '/cart';
await addProductsToCart([{ ...next.values }]);
} catch (error) {
// eslint-disable-next-line no-console
console.warn('Error adding product to cart', error);
}
},
});
}));

// Add to Wishlist Button
// ctx.appendButton({
// ctx.appendButton(() => ({
// icon: 'Heart',
// variant: 'primary',
// variant: 'secondary',
// text: 'Add to Wishlist',
// onClick: () => console.debug('Add to Wishlist', ctx.data),
// });
// }));
},
},
})(block);
Expand Down
32 changes: 16 additions & 16 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading

0 comments on commit bdd36a6

Please sign in to comment.