Skip to content

Commit

Permalink
feat(RSS-ECOMM-3_03): implement filtering product list (#227)
Browse files Browse the repository at this point in the history
* feat: expand loder options

* fix: styles product card

* feat: implement saving Set into LS Co-authored-by: Meg G. <[email protected]>

* feat: get and draw product items

* feat: create ProductFilters component

* feat: implement price filter

* feat: implement size filter

* feat: filters reset button

* fix: burger styles

* fix: lost title
  • Loading branch information
Kleostro authored May 12, 2024
1 parent cb5fe96 commit 81af873
Show file tree
Hide file tree
Showing 38 changed files with 1,247 additions and 94 deletions.
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@
"js-cookie": "^3.0.5",
"materialize-css": "^1.0.0-rc.2",
"modern-normalize": "^2.0.0",
"nouislider": "^15.7.1",
"postcode-validator": "^3.8.20",
"vite-plugin-checker": "^0.6.4",
"vite-plugin-image-optimizer": "^1.1.7",
Expand Down
12 changes: 6 additions & 6 deletions src/app/App/model/AppModel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,9 @@ class AppModel {
private router = new RouterModel();

constructor() {
this.initialize()
.then()
.catch(() => {
throw new Error('AppModel initialization error');
});
this.initialize().catch(() => {
throw new Error('AppModel initialization error');
});
}

private createRoutes(): Promise<Map<string, () => Promise<Page>>> {
Expand Down Expand Up @@ -79,7 +77,9 @@ class AppModel {

private async initialize(): Promise<void> {
document.body.append(this.appView.getHTML());
this.appView.getHTML().insertAdjacentElement('beforebegin', new HeaderModel(this.router).getHTML());
this.appView
.getHTML()
.insertAdjacentElement('beforebegin', new HeaderModel(this.router, this.appView.getHTML()).getHTML());
this.appView.getHTML().insertAdjacentElement('afterend', new FooterModel(this.router).getHTML());

const routes = await this.createRoutes();
Expand Down
100 changes: 100 additions & 0 deletions src/app/styles/noUiSlider.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
/* stylelint-disable selector-class-pattern */
.noUi-touch-area {
width: 100%;
height: 100%;
}

.noUi-handle.noUi-handle-lower,
.noUi-handle.noUi-handle-upper {
outline: 0;
}

.noUi-horizontal .noUi-handle {
right: -12px;
top: -5px;
width: var(--extra-small-offset);
height: var(--extra-small-offset);
}

.noUi-handle {
position: absolute;
border: 2px solid var(--noble-white-100);
border-radius: 50%;
box-shadow: none;
background-color: var(--steam-green-800);
-webkit-backface-visibility: hidden;
backface-visibility: hidden;
transition: background-color 0.3;
cursor: pointer;
}

.noUi-target,
.noUi-target * {
-moz-box-sizing: border-box;
box-sizing: border-box;
-webkit-tap-highlight-color: transparent;
-ms-touch-action: none;
touch-action: none;
-webkit-user-select: none;
-ms-user-select: none;
-moz-user-select: none;
user-select: none;
-webkit-touch-callout: none;
}

.noUi-horizontal .noUi-origin {
height: 0;
}

.noUi-connect,
.noUi-origin {
position: absolute;
right: 0;
top: 0;
z-index: 1;
width: 100%;
height: 100%;
-ms-transform-origin: 0 0;
-webkit-transform-origin: 0 0;
transform-origin: 0 0;
-webkit-transform-style: preserve-3d;
transform-style: flat;
will-change: transform;
}

.noUi-connect {
height: 8px;
background: var(--steam-green-800);
cursor: pointer;
}

.noUi-base,
.noUi-connects {
position: relative;
z-index: 1;
width: 100%;
height: 100%;
}

.noUi-connects {
z-index: 0;
overflow: hidden;
border-radius: 3px;
}

.noUi-target {
position: relative;
border-radius: 4px;
background: var(--steam-green-400);
cursor: pointer;
}

.noUi-horizontal {
height: 8px;
}

.noUi-state-tap .noUi-connect,
.noUi-state-tap .noUi-origin {
-webkit-transition: transform 0.3s;
transition: transform 0.3s;
}
2 changes: 2 additions & 0 deletions src/app/styles/variables.scss
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
--noble-gray-600: #a5a5a5;
--noble-gray-700: #727272;
--noble-gray-800: #3d3d3d;
--noble-gray-1000: #1a1a1ab5;
--red-power-600: #d0302f;
--steam-green-300: #46a35880;
--steam-green-400: #c8f4b4;
Expand All @@ -56,6 +57,7 @@
--noble-gray-600: #cdcdcd;
--noble-gray-700: #b0b0b0;
--noble-gray-800: #c4c4c4;
--noble-gray-1000: #1a1a1ab5;
--red-power-600: #d0302f;
--steam-green-300: #46a35880;
--steam-green-400: #c8f4b4;
Expand Down
3 changes: 1 addition & 2 deletions src/entities/ProductCard/model/ProductCardModel.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
import type { SizeType } from '@/shared/types/product.ts';
import type ProductCardParams from '@/shared/types/productCard';

import ProductCardView from '../view/ProductCardView.ts';

class ProductCardModel {
private view: ProductCardView;

constructor(params: ProductCardParams, size: SizeType) {
constructor(params: ProductCardParams, size: null | string) {
this.view = new ProductCardView(params, size);
}

Expand Down
52 changes: 39 additions & 13 deletions src/entities/ProductCard/view/ProductCardView.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import type { SizeType } from '@/shared/types/product';
import type ProductCardParams from '@/shared/types/productCard.ts';

import ButtonModel from '@/shared/Button/model/ButtonModel.ts';
import LinkModel from '@/shared/Link/model/LinkModel.ts';
import LoaderModel from '@/shared/Loader/model/LoaderModel.ts';
import getStore from '@/shared/Store/Store.ts';
import observeStore, { selectCurrentLanguage } from '@/shared/Store/observer.ts';
import { MORE_TEXT } from '@/shared/constants/buttons.ts';
import { SIZES } from '@/shared/constants/sizes.ts';
import { LANGUAGE_CHOICE, MORE_TEXT } from '@/shared/constants/buttons.ts';
import { PAGE_ID } from '@/shared/constants/pages.ts';
import { LOADER_SIZE } from '@/shared/constants/sizes.ts';
import createBaseElement from '@/shared/utils/createBaseElement.ts';

import styles from './productCardView.module.scss';
Expand Down Expand Up @@ -35,9 +35,9 @@ class ProductCardView {

private productShortDescription: HTMLParagraphElement;

private size: SizeType;
private size: null | string;

constructor(params: ProductCardParams, size: SizeType) {
constructor(params: ProductCardParams, size: null | string) {
this.size = size;
this.params = params;
this.productImage = this.createProductImage();
Expand All @@ -62,10 +62,13 @@ class ProductCardView {
}

private createBasicPrice(): HTMLSpanElement {
const { discount, price } = this.params.variant.find(({ size }) => size === this.size) ?? {};
const { discount, price } = this.size
? this.params.variant.find(({ size }) => size === this.size) ?? {}
: this.params.variant[0];
const innerContent = discount ? `$${discount.toFixed(2)}` : `$${price?.toFixed(2)}`;
this.basicPrice = createBaseElement({
cssClasses: [styles.basicPrice],
innerContent: discount ? `$${discount.toFixed(2)}` : `$${price?.toFixed(2)}`,
innerContent,
tag: 'span',
});

Expand Down Expand Up @@ -114,10 +117,13 @@ class ProductCardView {
}

private createOldPrice(): HTMLSpanElement {
const { discount, price } = this.params.variant.find(({ size }) => size === this.size) ?? {};
const { discount, price } = this.size
? this.params.variant.find(({ size }) => size === this.size) ?? {}
: this.params.variant[0];
const innerContent = discount ? `$${price?.toFixed(2)}` : '';
this.oldPrice = createBaseElement({
cssClasses: [styles.oldPrice],
innerContent: discount ? `$${price?.toFixed(2)}` : '',
innerContent,
tag: 'span',
});

Expand All @@ -137,6 +143,7 @@ class ProductCardView {
private createProductImage(): HTMLImageElement {
const productImage = createBaseElement({
attributes: {
alt: this.params.name[0].value,
src: this.params.images[0],
},
cssClasses: [styles.productImage],
Expand All @@ -151,7 +158,7 @@ class ProductCardView {
tag: 'div',
});

const loader = new LoaderModel(SIZES.MEDIUM).getHTML();
const loader = new LoaderModel(LOADER_SIZE.MEDIUM).getHTML();
this.productImageWrapper.append(this.productImage, loader);
this.productImage.classList.add(styles.hidden);

Expand All @@ -172,28 +179,47 @@ class ProductCardView {

this.productLink.getHTML().addEventListener('click', (event) => {
event.preventDefault();
window.location.href = this.params.key;
// TBD: fix href on product page
window.location.href = `${PAGE_ID.CATALOG_PAGE}/${this.params.key}`;
});

return this.productLink;
}

private createProductName(): HTMLHeadingElement {
// TBD: replace on locale
const innerContent = this.params.name[getStore().getState().currentLanguage === LANGUAGE_CHOICE.EN ? 0 : 1].value;
const productName = createBaseElement({
cssClasses: [styles.productName],
innerContent: this.params.name[0].value,
innerContent,
tag: 'h3',
});

observeStore(selectCurrentLanguage, () => {
// TBD: replace on locale
const textContent = this.params.name[getStore().getState().currentLanguage === LANGUAGE_CHOICE.EN ? 0 : 1].value;
productName.textContent = textContent;
});
return productName;
}

private createProductShortDescription(): HTMLParagraphElement {
// TBD: replace on locale
const innerContent =
this.params.description[getStore().getState().currentLanguage === LANGUAGE_CHOICE.EN ? 0 : 1].value;
this.productShortDescription = createBaseElement({
cssClasses: [styles.productShortDescription],
innerContent: this.params.description[0].value,
innerContent,
tag: 'p',
});

observeStore(selectCurrentLanguage, () => {
// TBD: replace on locale
const textContent =
this.params.description[getStore().getState().currentLanguage === LANGUAGE_CHOICE.EN ? 0 : 1].value;
this.productShortDescription.textContent = textContent;
});

return this.productShortDescription;
}

Expand Down
12 changes: 8 additions & 4 deletions src/entities/ProductCard/view/productCardView.module.scss
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,12 @@
display: flex;
flex-direction: column;
align-items: center;
justify-self: center;
outline: 2px solid var(--noble-gray-300);
border-radius: var(--medium-br);
min-height: 350px;
max-width: 270px;
background-color: var(--noble-white-200);
transition:
outline 0.2s,
transform 0.2s;
Expand Down Expand Up @@ -35,13 +38,13 @@

.bottomWrapper {
display: flex;
flex-grow: 1;
flex-direction: column;
align-items: center;
justify-content: space-evenly;
padding: calc(var(--extra-small-offset) / 2) calc(var(--extra-small-offset) / 4);
width: 100%;
height: 70%;
background-color: var(--noble-white-200);
gap: calc(var(--extra-small-offset) / 4);
}

.productName {
Expand All @@ -58,7 +61,7 @@
max-width: 90%;
font: var(--regular-font);
letter-spacing: 1px;
text-align: center;
text-align: left;
text-overflow: ellipsis;
color: var(--noble-gray-400);
-webkit-box-orient: vertical;
Expand All @@ -79,7 +82,7 @@
position: relative;
z-index: 1;
align-self: flex-end;
margin-top: calc(var(--extra-small-offset) * -1);
margin-top: calc(var(--extra-small-offset) * (-0.2));
margin-right: 5px;
font: var(--regular-font);
letter-spacing: 1px;
Expand All @@ -101,6 +104,7 @@
display: flex;
align-items: center;
justify-content: center;
margin-top: auto;
width: 100%;
text-align: center;
gap: var(--extra-small-offset);
Expand Down
Loading

0 comments on commit 81af873

Please sign in to comment.