From 3f88e4435a726a55be11b7feab7458aa83c57c7c Mon Sep 17 00:00:00 2001 From: Ben <36240171+benkyd@users.noreply.github.com> Date: Thu, 31 Mar 2022 18:22:52 +0100 Subject: [PATCH] search box and also search bar and also basket Former-commit-id: d63b7aa5fe3ff0ac16d8f8eab2bb67724cf2ff56 --- client/public/components/basket.mjs | 195 ++++++++++++++++++ client/public/components/compact-listing.mjs | 10 +- client/public/components/components.mjs | 2 +- client/public/components/css/navbar.css | 81 +------- .../public/components/css/product-listing.css | 30 ++- client/public/components/navbar.mjs | 4 +- client/public/components/notificationbar.mjs | 4 +- client/public/components/product-listing.mjs | 17 +- client/public/components/search.mjs | 64 ++++++ .../public/components/templates/navbar.html | 23 +-- .../components/templates/product-listing.html | 6 +- client/public/helpers.mjs | 8 +- client/public/index.html | 18 +- client/public/index.mjs | 4 +- client/public/product/index.html | 44 ++++ 15 files changed, 375 insertions(+), 135 deletions(-) create mode 100644 client/public/components/basket.mjs create mode 100644 client/public/components/search.mjs create mode 100644 client/public/product/index.html diff --git a/client/public/components/basket.mjs b/client/public/components/basket.mjs new file mode 100644 index 0000000..e51842a --- /dev/null +++ b/client/public/components/basket.mjs @@ -0,0 +1,195 @@ +import { RegisterComponent, Component } from './components.mjs'; + +// Basket is stored locally only and is not persisted to the server. +// It is used to store the current basket and is used to calculate the total price of the basket. +// It is also used to store the current user's basket. +// The structure of the basket is in local storage and is as follows: +// { +// "basket": { +// "items": { +// "item1": amount, +// "item2": amount, +// ... +// }, +// "total": total +// }, +// } + +let basketCallback = null; + +// TODO: Does the localstorage have a problem with mutual exclusion? +// TODO: Should the basket be persisted to the server? +export function AddProductToBasket(product, amount) { + if (localStorage.getItem('basket') === null || !localStorage.getItem('basket')) { + localStorage.setItem('basket', JSON.stringify({ + items: {}, + total: 0, + })); + } + + const basket = JSON.parse(localStorage.getItem('basket')); + console.log(basket); + + if (basket.items.product) { + basket.items.product += amount; + } else { + basket.items.product = amount; + } + console.log(basket); + + basket.total += amount; + console.log(basket); + + console.log(JSON.stringify(basket, null, 4)); + localStorage.setItem('basket', JSON.stringify(basket)); + + if (basketCallback) { + basketCallback(); + } +} + +export function RemoveProductFromBasket(item, amount) { + if (localStorage.getItem('basket') === null || !localStorage.getItem('basket')) { + return; + } + const basket = JSON.parse(localStorage.getItem('basket')); + + if (basket.items.item > amount) { + basket.items.item -= amount; + } else { + delete basket.items.item; + } + + basket.total -= amount; + + localStorage.setItem('basket', JSON.stringify(basket)); + + if (basketCallback) { + basketCallback(); + } +} + + +class Basket extends Component { + static __IDENTIFY() { return 'basket'; } + + constructor() { + super(Basket); + } + + OnLocalBasketUpdate() { + const basket = localStorage.getItem('basket'); + + if (basket) { + try { + const basketJSON = JSON.parse(basket); + this.setState({ + items: basketJSON.items, + total: basketJSON.total, + }); + } catch (e) { + console.log(e); + } + } else { + this.setState({ + items: {}, + total: 0, + }); + } + } + + OnMount() { + this.OnLocalBasketUpdate(Object.bind(this)); + + basketCallback = this.OnLocalBasketUpdate.bind(this); + } + + Render() { + return { + template: ` + +
+ + {this.state.total} +
+ + +
+ `, + style: ` + #basket-wrapper { + flex-basis: 4%; + } + + .basket { + display: flex; + justify-content: space-between; + padding-bottom: 2px; + } + + .basket:hover { + opacity: 0.5; + } + + #basket-icon { + padding-top: 2px; + cursor: pointer; + } + + #basket-count { + padding-top: 9px; + font-size: 1em; + font-weight: 100; + cursor: pointer; + } + + .popup { + display: none; + } + + .show { + display: flex; + } + + #basket-popup { + position: absolute; + background-color: #EC914B; + width: 200px; + height: 200px; + flex-direction: column; + justify-content: center; + align-items: center; + z-index: 100; + } + + `, + }; + } + + OnRender() { + // set up basket + const basketToggler = this.root.querySelector('.basket'); + + basketToggler.addEventListener('click', () => { + const popup = this.root.querySelector('.popup'); + popup.classList.toggle('show'); + }); + } +} + +RegisterComponent(Basket); diff --git a/client/public/components/compact-listing.mjs b/client/public/components/compact-listing.mjs index c51e344..da5047c 100644 --- a/client/public/components/compact-listing.mjs +++ b/client/public/components/compact-listing.mjs @@ -97,15 +97,7 @@ class CompactProductListing extends Component { } OpenProductListing() { - const location = document.querySelector('#current-open-listing'); - - // Open the product listing - const productListing = document.createElement('product-listing-component'); - productListing.setAttribute('id', this.state.id); - productListing.setAttribute('type', this.state.type); - location.appendChild(productListing); - - Helpers.SwapActivePage('store', 'current-open-listing'); + window.location.href = `/product/?type=${this.state.type}&id=${this.state.id}&name=${encodeURIComponent(this.state.name)}`; } OnRender() { diff --git a/client/public/components/components.mjs b/client/public/components/components.mjs index 2ffb180..396aa32 100644 --- a/client/public/components/components.mjs +++ b/client/public/components/components.mjs @@ -31,7 +31,7 @@ export class Component extends HTMLElement { OnMount() { } Update() { } Render() { Component.__WARN('Render'); } - OnRender() { Component.__WARN('Render'); } + OnRender() { } static __IDENTIFY() { Component.__WARN('identify'); } async connectedCallback() { diff --git a/client/public/components/css/navbar.css b/client/public/components/css/navbar.css index 07ec50b..ef1dbe9 100644 --- a/client/public/components/css/navbar.css +++ b/client/public/components/css/navbar.css @@ -7,7 +7,7 @@ margin-top:-10px!important; margin-bottom:0px!important; width: 100%; - background-color: #D7C2FF; + background-color: #EFAE4F; box-shadow: #222 0px 0px 5px; background-size: 100% 100%; z-index: 1; @@ -85,10 +85,6 @@ width: 100%; } -.hamburger:hover .hamburger-line { - background: #555; -} - .hamburger-line-top { top: 3px; } @@ -150,13 +146,13 @@ } .sub-nav { - border: 1px solid #ccc; display: none; position: absolute; - background-color: #E4D6FF; + background-color: #EC914B; padding: 5px 5px; list-style: none; - width: 230px; + left: parent; + max-width: 300px; z-index: 5; } @@ -172,7 +168,7 @@ .nav-menu { position: fixed; - background: #e4d6ffde; + background: #efaf4fbb; flex-direction: column; justify-content: center; opacity: 50; @@ -198,6 +194,7 @@ .sub-nav { position: relative; width: 100%; + max-width: 100%; display: none; background-color: rgba(0, 0, 0, 0.20); box-sizing: border-box; @@ -236,76 +233,10 @@ z-index: 3; } -/* Modified version of https://codepen.io/mihaeltomic/pen/vmwMdm */ -#search-bar { - width: 100%; - padding: 12px 14px; - background-color: transparent; - transition: transform 250ms ease-in-out; - font-family: 'Josefin Sans', sans-serif; - font-size: 0.5em; - color: #222; - background-color: transparent; - background-image: url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24'%3E%3Cpath d='M15.5 14h-.79l-.28-.27C15.41 12.59 16 11.11 16 9.5 16 5.91 13.09 3 9.5 3S3 5.91 3 9.5 5.91 16 9.5 16c1.61 0 3.09-.59 4.23-1.57l.27.28v.79l5 4.99L20.49 19l-4.99-5zm-6 0C7.01 14 5 11.99 5 9.5S7.01 5 9.5 5 14 7.01 14 9.5 11.99 14 9.5 14z'/%3E%3Cpath d='M0 0h24v24H0z' fill='none'/%3E%3C/svg%3E"); - background-repeat: no-repeat; - background-size: 27px 27px; - background-position: 97.5% center; - border-radius: 10px; - border: 4px solid #222; - transition: all 250ms ease-in-out; - backface-visibility: hidden; - transform-style: preserve-3d; -} - -.search__input::placeholder { - color: rgba(87, 87, 86, 0.8); - text-transform: uppercase; - letter-spacing: 1.5px; -} - -#search-bar:hover, #search-bar:focus { - padding: 12px 0; - outline: 0; - border: 2px solid transparent; - border-bottom: 4px solid #222; - border-radius: 0; - background-position: 100% center; -} - - -/* #search-bar:hover, #search-bar:focus { - border: 1.5px solid #009688; - background-color: white; -} */ - - -#cart-wrapper { - display: flex; - flex-basis: 4%; - justify-content: space-between; - padding-bottom: 2px; -} - -#cart-icon { - padding-top: 2px; -} - -#cart-number { - padding-top: 9px; - font-size: 1em; - font-weight: 100; -} - @media (pointer:none), (pointer:coarse), screen and (max-width: 900px) { .search-wrapper { flex-basis: 80%; } - - #search-bar { - width: 100%; - margin: 0; - height: 2em; - } } /*switch, from https://www.w3schools.com/howto/howto_css_switch.asp*/ diff --git a/client/public/components/css/product-listing.css b/client/public/components/css/product-listing.css index 91ad940..c9e9c33 100644 --- a/client/public/components/css/product-listing.css +++ b/client/public/components/css/product-listing.css @@ -19,24 +19,38 @@ display: flex; flex-direction: row; justify-content: center; - align-items: center; + align-items: flex-start; margin-left: auto; margin-right: auto; } +.product-image-container { + aspect-ratio: 1; + flex-grow: 1; +} + +.active-image { + display: block; + width: 100%; + height: 100%; +} + @media (pointer:none), (pointer:coarse), screen and (max-width: 900px) { .product-page, .product-display { flex-direction: column; + height: 80% } -} -.product-image-container { - flex-grow: 2; -} + .product-display { + align-items: center; + justify-content: flex-start; + } -.active-image { - width: 100%; - height: 100%; + .product-image-container { + aspect-ratio: 1; + flex-grow: 2; + width: 90%; + } } .product-info { diff --git a/client/public/components/navbar.mjs b/client/public/components/navbar.mjs index 02174fa..f339b96 100644 --- a/client/public/components/navbar.mjs +++ b/client/public/components/navbar.mjs @@ -9,8 +9,8 @@ class NavBar extends Component { Render() { return { - template: SideLoad('./components/templates/navbar.html'), - style: SideLoad('./components/css/navbar.css'), + template: SideLoad('/components/templates/navbar.html'), + style: SideLoad('/components/css/navbar.css'), }; } diff --git a/client/public/components/notificationbar.mjs b/client/public/components/notificationbar.mjs index 1a23ab3..ed62517 100644 --- a/client/public/components/notificationbar.mjs +++ b/client/public/components/notificationbar.mjs @@ -47,8 +47,8 @@ class NotificationBar extends Component { Render() { return { - template: SideLoad('./components/templates/notificationbar.html'), - style: SideLoad('./components/css/notificationbar.css'), + template: SideLoad('/components/templates/notificationbar.html'), + style: SideLoad('/components/css/notificationbar.css'), }; } diff --git a/client/public/components/product-listing.mjs b/client/public/components/product-listing.mjs index c62a5ce..8272e56 100644 --- a/client/public/components/product-listing.mjs +++ b/client/public/components/product-listing.mjs @@ -9,7 +9,11 @@ class ProductListing extends Component { } async OnMount() { - const getURL = new URL(`/api/${this.state.type}/${this.state.id}`, document.baseURI); + const urlParams = new URLSearchParams(window.location.search); + const type = urlParams.get('type'); + const id = urlParams.get('id'); + + const getURL = new URL(`/api/${type}/${id}`, document.baseURI); const data = await fetch(getURL).then(response => response.json()); this.setState({ ...this.getState, @@ -20,19 +24,16 @@ class ProductListing extends Component { Render() { return { - template: SideLoad('./components/templates/product-listing.html'), - style: SideLoad('./components/css/product-listing.css'), + template: SideLoad('/components/templates/product-listing.html'), + style: SideLoad('/components/css/product-listing.css'), }; } OnRender() { const backButton = this.root.querySelector('.back-button-svg'); - backButton.addEventListener('click', async () => { - await Helpers.SwapActivePage('current-open-listing', 'store'); - // clean up - const location = document.querySelector('#current-open-listing'); - location.removeChild(location.lastChild); + backButton.addEventListener('click', () => { + window.history.back(); }); } } diff --git a/client/public/components/search.mjs b/client/public/components/search.mjs new file mode 100644 index 0000000..8cdf84c --- /dev/null +++ b/client/public/components/search.mjs @@ -0,0 +1,64 @@ +import { RegisterComponent, Component } from './components.mjs'; + +class Search extends Component { + static __IDENTIFY() { return 'search'; } + + constructor() { + super(Search); + } + + Render() { + return { + template: ` + + `, + style: ` + /* Modified version of https://codepen.io/mihaeltomic/pen/vmwMdm */ + #search-bar { + width: 100%; + height: 100%; + padding: 12px 14px; + background-color: transparent; + transition: transform 250ms ease-in-out; + font-family: 'Josefin Sans', sans-serif; + font-size: 0.5em; + color: #222; + background-color: transparent; + background-image: url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24'%3E%3Cpath d='M15.5 14h-.79l-.28-.27C15.41 12.59 16 11.11 16 9.5 16 5.91 13.09 3 9.5 3S3 5.91 3 9.5 5.91 16 9.5 16c1.61 0 3.09-.59 4.23-1.57l.27.28v.79l5 4.99L20.49 19l-4.99-5zm-6 0C7.01 14 5 11.99 5 9.5S7.01 5 9.5 5 14 7.01 14 9.5 11.99 14 9.5 14z'/%3E%3Cpath d='M0 0h24v24H0z' fill='none'/%3E%3C/svg%3E"); + background-repeat: no-repeat; + background-size: 27px 27px; + background-position: 97.5% center; + border-radius: 10px; + border: 4px solid #222; + transition: all 250ms ease-in-out; + backface-visibility: hidden; + transform-style: preserve-3d; + } + + .search__input::placeholder { + color: rgba(87, 87, 86, 0.8); + text-transform: uppercase; + letter-spacing: 1.5px; + } + + #search-bar:hover, #search-bar:focus { + padding: 12px 0; + outline: 0; + border: 2px solid transparent; + border-bottom: 4px solid #222; + border-radius: 0; + background-position: 100% center; + } + + @media (pointer:none), (pointer:coarse), screen and (max-width: 900px) { + #search-bar { + width: 100%; + margin: 0; + } + } + `, + }; + } +} + +RegisterComponent(Search); diff --git a/client/public/components/templates/navbar.html b/client/public/components/templates/navbar.html index 3480ed5..3a38803 100644 --- a/client/public/components/templates/navbar.html +++ b/client/public/components/templates/navbar.html @@ -1,6 +1,6 @@ \ No newline at end of file diff --git a/client/public/components/templates/product-listing.html b/client/public/components/templates/product-listing.html index 5fa0457..3dcd944 100644 --- a/client/public/components/templates/product-listing.html +++ b/client/public/components/templates/product-listing.html @@ -1,17 +1,17 @@
- back-arrow + back-arrow
- product-image + Image of ${this.state.name}
{this.state.name}
-
${this.state.price}
+
£{this.state.price}
{this.state.description}
Quantity: diff --git a/client/public/helpers.mjs b/client/public/helpers.mjs index bd2b13c..00a6b2d 100644 --- a/client/public/helpers.mjs +++ b/client/public/helpers.mjs @@ -10,13 +10,13 @@ export function SwapActivePage(current, target) { currentPage.classList.add('slide-out-left'); targetPage.classList.add('slide-in-left'); + currentPage.classList.remove('slide-out-left'); + targetPage.classList.remove('slide-in-left'); + currentPage.style.display = 'none'; + targetPage.style.display = 'block'; // wait for transition to finish return new Promise((resolve) => { setTimeout(() => { - currentPage.classList.remove('slide-out-left'); - targetPage.classList.remove('slide-in-left'); - currentPage.style.display = 'none'; - targetPage.style.display = 'block'; resolve(); }, 500); }); diff --git a/client/public/index.html b/client/public/index.html index c264ec3..d0bf0ef 100644 --- a/client/public/index.html +++ b/client/public/index.html @@ -6,17 +6,18 @@ - - - - - - - - + + + + + + + + + @@ -27,7 +28,6 @@ - diff --git a/client/public/index.mjs b/client/public/index.mjs index d97e715..f6c178d 100644 --- a/client/public/index.mjs +++ b/client/public/index.mjs @@ -1,13 +1,13 @@ // import { RendererPreInit, BrickRenderer } from './brick-renderer/index.mjs'; +import { AddProductToBasket, RemoveProductFromBasket } from '/components/basket.mjs'; -async function main() { +function main() { // await RendererPreInit(); // const canvas = document.querySelectorAll('#webglviewer'); // for (let i = 0; i < canvas.length; i++) { // const Renderer = new BrickRenderer(canvas[i]); // } - } window.onload = main; diff --git a/client/public/product/index.html b/client/public/product/index.html new file mode 100644 index 0000000..cb126ea --- /dev/null +++ b/client/public/product/index.html @@ -0,0 +1,44 @@ + + + LegoLog Home! + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +