Skip to content

Commit

Permalink
feat(RSS-ECOMM-4_38): scroll to top button (#330)
Browse files Browse the repository at this point in the history
* feat: implement scrollToTop button

* fix: binding context

* refactor: update confirm component

* refactor: update address edit buttons

* fix: table heading color
  • Loading branch information
stardustmeg authored Jun 3, 2024
1 parent c1e9264 commit 1b45d94
Show file tree
Hide file tree
Showing 14 changed files with 148 additions and 19 deletions.
4 changes: 3 additions & 1 deletion src/app/App/model/AppModel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import type { Page, PageParams, PagesType } from '@/shared/types/page.ts';

import RouterModel from '@/app/Router/model/RouterModel.ts';
import modal from '@/shared/Modal/model/ModalModel.ts';
import ScrollToTopModel from '@/shared/ScrollToTop/model/ScrollToTopModel.ts';
import { PAGE_ID } from '@/shared/constants/pages.ts';
import FooterModel from '@/widgets/Footer/model/FooterModel.ts';
import HeaderModel from '@/widgets/Header/model/HeaderModel.ts';
Expand Down Expand Up @@ -77,7 +78,8 @@ class AppModel {
private async initialize(): Promise<RouterModel> {
const routes = await this.createRoutes();
const router = new RouterModel(routes);
document.body.append(this.appView.getHTML());
const scrollToTop = new ScrollToTopModel();
document.body.append(this.appView.getHTML(), scrollToTop.getHTML().getHTML());
this.appView.getHTML().insertAdjacentElement('beforebegin', new HeaderModel(this.appView.getHTML()).getHTML());
this.appView.getHTML().insertAdjacentElement('afterend', new FooterModel().getHTML());
this.appView.getHTML().insertAdjacentElement('afterend', modal.getHTML());
Expand Down
5 changes: 3 additions & 2 deletions src/app/styles/mixins.scss
Original file line number Diff line number Diff line change
Expand Up @@ -65,13 +65,14 @@ $filter-brightness: 1.3;
justify-self: center;
border-radius: 50%;
padding: $btn-padding;
width: max-content;
width: fit-content;
background-color: $btn-bg;
transition:
filter $transition-duration,
color $transition-duration,
background-color $transition-duration,
transform $transition-duration;
transform $transition-duration,
opacity $transition-duration;

@media (hover: hover) {
&:hover {
Expand Down
6 changes: 5 additions & 1 deletion src/entities/UserAddress/model/UserAddressModel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import modal from '@/shared/Modal/model/ModalModel.ts';
import serverMessageModel from '@/shared/ServerMessage/model/ServerMessageModel.ts';
import getStore from '@/shared/Store/Store.ts';
import { setBillingCountry } from '@/shared/Store/actions.ts';
import { USER_MESSAGE } from '@/shared/constants/confirmUserMessage.ts';
import MEDIATOR_EVENT from '@/shared/constants/events.ts';
import { ADDRESS_TYPE, type AddressTypeType } from '@/shared/constants/forms.ts';
import { MESSAGE_STATUS, SERVER_MESSAGE_KEYS } from '@/shared/constants/messages.ts';
Expand Down Expand Up @@ -109,7 +110,10 @@ class UserAddressModel {
.getDeleteButton()
.getHTML()
.addEventListener('click', () => {
const confirmModel = new ConfirmModel(() => this.deleteAddress(address));
const confirmModel = new ConfirmModel(
() => this.deleteAddress(address),
USER_MESSAGE[getStore().getState().currentLanguage].DELETE_ADDRESS,
);
modal.setContent(confirmModel.getHTML());
modal.show();
});
Expand Down
18 changes: 10 additions & 8 deletions src/entities/UserAddress/view/userAddressView.module.scss
Original file line number Diff line number Diff line change
Expand Up @@ -67,25 +67,27 @@
scale 0.2s;
}

.editLogo,
.deleteLogo {
@include svg-logo;

width: calc(var(--extra-small-offset) * 1.5);
height: calc(var(--extra-small-offset) * 1.5);
}

.editButton,
.deleteButton {
@include green-btn;

padding: 0;
width: calc(var(--extra-small-offset) * 1.5);
height: calc(var(--extra-small-offset) * 1.5);
background-color: transparent;

svg {
width: calc(var(--extra-small-offset) * 1.5);
height: calc(var(--extra-small-offset) * 1.5);

&:hover {
fill: var(--steam-green-800);
stroke: var(--steam-green-800);
}
}

&:hover {
filter: brightness(1);
}
}

Expand Down
2 changes: 1 addition & 1 deletion src/pages/CartPage/view/cartPageView.module.scss
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@
.th {
padding: var(--tiny-offset) 0;
font: var(--bold-font);
color: var(--noble-gray-400);
color: var(--noble-gray-800);

@media (max-width: 768px) {
display: none;
Expand Down
6 changes: 2 additions & 4 deletions src/shared/Confirm/model/ConfirmModel.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
import LoaderModel from '@/shared/Loader/model/LoaderModel.ts';
import modal from '@/shared/Modal/model/ModalModel.ts';
import getStore from '@/shared/Store/Store.ts';
import { USER_MESSAGE } from '@/shared/constants/confirmUserMessage.ts';
import { LOADER_SIZE } from '@/shared/constants/sizes.ts';
import showErrorMessage from '@/shared/utils/userMessage.ts';

Expand All @@ -12,9 +10,9 @@ class ConfirmModel {

private view: ConfirmView;

constructor(callback: () => Promise<void> | void) {
constructor(callback: () => Promise<void> | void, message: string) {
this.callback = callback;
this.view = new ConfirmView(USER_MESSAGE[getStore().getState().currentLanguage].DELETE_ADDRESS);
this.view = new ConfirmView(message);
this.setCancelButtonHandler();
this.setConfirmButtonHandler();
}
Expand Down
2 changes: 1 addition & 1 deletion src/shared/Modal/view/modalView.module.scss
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
position: fixed;
left: 0;
top: 0;
z-index: 1;
z-index: 2;
width: 100%;
height: 100%;
opacity: 1;
Expand Down
27 changes: 27 additions & 0 deletions src/shared/ScrollToTop/model/ScrollToTopModel.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import ScrollToTopView from '../view/ScrollToTopView.ts';

class ScrollToTopModel {
private view: ScrollToTopView;

constructor() {
this.view = new ScrollToTopView();
this.init();
}

private init(): void {
const buttonElement = this.view.getHTML();
buttonElement.addEventListener('click', this.scrollToTopHandler.bind(this));
window.addEventListener('scroll', () => this.view.toggleVisibility());
this.view.toggleVisibility();
}

private scrollToTopHandler(): void {
window.scrollTo(0, 0);
}

public getHTML(): ScrollToTopView {
return this.view;
}
}

export default ScrollToTopModel;
44 changes: 44 additions & 0 deletions src/shared/ScrollToTop/view/ScrollToTopView.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import ButtonModel from '@/shared/Button/model/ButtonModel.ts';
import getStore from '@/shared/Store/Store.ts';
import observeStore, { selectCurrentLanguage } from '@/shared/Store/observer.ts';
import { SCROLL_TO_TOP_THRESHOLD } from '@/shared/constants/common.ts';
import SVG_DETAILS from '@/shared/constants/svg.ts';
import TOOLTIP_TEXT from '@/shared/constants/tooltip.ts';
import createSVGUse from '@/shared/utils/createSVGUse.ts';

import styles from './scrollToTopView.module.scss';

class ScrollToTopView {
private button: ButtonModel;

constructor() {
this.button = this.createButton();
}

private createButton(): ButtonModel {
this.button = new ButtonModel({
classes: [styles.scrollToTopButton],
title: TOOLTIP_TEXT[getStore().getState().currentLanguage].SCROLL_TO_TOP,
});

const svg = document.createElementNS(SVG_DETAILS.SVG_URL, 'svg');
svg.append(createSVGUse(SVG_DETAILS.ARROW_UP));
this.button.getHTML().append(svg);

observeStore(selectCurrentLanguage, () => {
this.button.getHTML().title = TOOLTIP_TEXT[getStore().getState().currentLanguage].SCROLL_TO_TOP;
});

return this.button;
}

public getHTML(): HTMLButtonElement {
return this.button.getHTML();
}

public toggleVisibility(): void {
this.button.getHTML().classList.toggle(styles.hidden, window.scrollY <= SCROLL_TO_TOP_THRESHOLD);
}
}

export default ScrollToTopView;
45 changes: 45 additions & 0 deletions src/shared/ScrollToTop/view/scrollToTopView.module.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
/* stylelint-disable scss/no-global-function-names */
@import 'src/app/styles/mixins';

$btn-vars: (
padding: var(--tiny-offset),
bg: var(--steam-green-900),
hover-bg: var(--steam-green-1000),
fill: var(--steam-green-800),
stroke: var(--steam-green-800),
offset: var(--small-offset),
);

.scrollToTopButton {
@include round-btn(
map-get($btn-vars, padding),
map-get($btn-vars, bg),
map-get($btn-vars, hover-bg),
map-get($btn-vars, fill),
map-get($btn-vars, stroke)
);

position: fixed;
right: map-get($btn-vars, offset);
bottom: map-get($btn-vars, offset);
z-index: 1;
width: calc(map-get($btn-vars, offset) * 1.5);
height: calc(map-get($btn-vars, offset) * 1.5);
fill: map-get($btn-vars, fill);
stroke: map-get($btn-vars, stroke);
transition:
transform 0.2s,
opacity 0.2s,
visibility 0.2s;

svg {
width: map-get($btn-vars, offset);
height: map-get($btn-vars, offset);
}
}

.hidden {
opacity: 0;
visibility: hidden;
transform: scale(0);
}
2 changes: 2 additions & 0 deletions src/shared/constants/common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,6 @@ export const DATA_KEYS = {

export const TABLET_WIDTH = 768;

export const SCROLL_TO_TOP_THRESHOLD = 200;

export type LanguageChoiceType = (typeof LANGUAGE_CHOICE)[keyof typeof LANGUAGE_CHOICE];
1 change: 1 addition & 0 deletions src/shared/constants/svg.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
const SVG_DETAILS = {
ARROW_UP: 'arrowUp',
BILL: 'bill',
BIN: 'bin',
CART: 'cart',
Expand Down
3 changes: 3 additions & 0 deletions src/shared/constants/tooltip.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ const TOOLTIP_TEXT: Record<string, Record<string, string>> = {
EDIT_ADDRESS: 'Edit address',
EDIT_PASSWORD: 'Edit password',
EDIT_SHIPPING_ADDRESS: 'Switch shipping address status',
SCROLL_TO_TOP: 'Scroll to top',
SWITCH_ADDRESS_STATUS: 'Change address status',
SWITCH_BILLING_ADDRESS: 'Switch billing address status',
SWITCH_DEFAULT_BILLING_ADDRESS: 'Switch default billing address status',
Expand All @@ -18,6 +19,7 @@ const TOOLTIP_TEXT: Record<string, Record<string, string>> = {
DELETE_ADDRESS: 'Удалить адрес полностью',
EDIT_ADDRESS: 'Изменить адрес',
EDIT_PASSWORD: 'Изменить пароль',
SCROLL_TO_TOP: 'Наверх',
SWITCH_ADDRESS_STATUS: 'Изменить статус адреса',
SWITCH_BILLING_ADDRESS: 'Изменить статус адреса выставления счетов',
SWITCH_DEFAULT_BILLING_ADDRESS: 'Изменить статус адреса выставления счетов по умолчанию',
Expand All @@ -32,6 +34,7 @@ export const TOOLTIP_TEXT_KEYS = {
DELETE_ADDRESS: 'DELETE_ADDRESS',
EDIT_ADDRESS: 'EDIT_ADDRESS',
EDIT_PASSWORD: 'EDIT_PASSWORD',
SCROLL_TO_TOP: 'SCROLL_TO_TOP',
SWITCH_ADDRESS_STATUS: 'SWITCH_ADDRESS_STATUS',
SWITCH_BILLING_ADDRESS: 'SWITCH_BILLING_ADDRESS',
SWITCH_DEFAULT_BILLING_ADDRESS: 'SWITCH_DEFAULT_BILLING_ADDRESS',
Expand Down
2 changes: 1 addition & 1 deletion src/shared/img/svg/arrowUp.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

0 comments on commit 1b45d94

Please sign in to comment.