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: `
+
+
+
+
+
+
+
+
+ `,
+ 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 @@
-
+
{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!
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+