Skip to content

Commit

Permalink
feat: implement switching locale
Browse files Browse the repository at this point in the history
  • Loading branch information
stardustmeg committed May 13, 2024
1 parent 3d86cfb commit 9be3d0f
Show file tree
Hide file tree
Showing 4 changed files with 174 additions and 51 deletions.
12 changes: 12 additions & 0 deletions src/shared/constants/messages.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,18 @@ export const SERVER_MESSAGE = {
},
} as const;

export const SERVER_MESSAGE_KEYS = {
BAD_REQUEST: 'BAD_REQUEST',
INCORRECT_PASSWORD: 'INCORRECT_PASSWORD',
INVALID_EMAIL: 'INVALID_EMAIL',
LANGUAGE_CHANGED: 'LANGUAGE_CHANGED',
SUCCESSFUL_LOGIN: 'SUCCESSFUL_LOGIN',
SUCCESSFUL_REGISTRATION: 'SUCCESSFUL_REGISTRATION',
USER_EXISTS: 'USER_EXISTS',
} as const;

export type ServerMessageKey = (typeof SERVER_MESSAGE_KEYS)[keyof typeof SERVER_MESSAGE_KEYS];

export const ERROR_MESSAGE = {
en: {
INVALID_COUNTRY: 'Invalid country',
Expand Down
35 changes: 25 additions & 10 deletions src/widgets/Header/model/HeaderModel.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import type RouterModel from '@/app/Router/model/RouterModel.ts';

import NavigationModel from '@/entities/Navigation/model/NavigationModel.ts';
import getCustomerModel from '@/shared/API/customer/model/CustomerModel.ts';
import getCustomerModel, { CustomerModel } from '@/shared/API/customer/model/CustomerModel.ts';
import serverMessageModel from '@/shared/ServerMessage/model/ServerMessageModel.ts';
import getStore from '@/shared/Store/Store.ts';
import { setCurrentLanguage, setCurrentUser } from '@/shared/Store/actions.ts';
import observeStore, { selectCurrentUser } from '@/shared/Store/observer.ts';
import { LANGUAGE_CHOICE } from '@/shared/constants/buttons.ts';
import { MESSAGE_STATUS, SERVER_MESSAGE } from '@/shared/constants/messages.ts';
import { MESSAGE_STATUS, SERVER_MESSAGE, SERVER_MESSAGE_KEYS } from '@/shared/constants/messages.ts';
import { PAGE_ID } from '@/shared/constants/pages.ts';

import HeaderView from '../view/HeaderView.ts';
Expand Down Expand Up @@ -66,7 +66,7 @@ class HeaderModel {
this.setLogoutButtonHandler();
this.setCartLinkHandler();
this.setProfileLinkHandler();
this.setChangeLanguageButtonHandler();
this.setChangeLanguageCheckboxHandler();
return true;
}

Expand Down Expand Up @@ -108,14 +108,29 @@ class HeaderModel {
return true;
}

private setChangeLanguageButtonHandler(): boolean {
const switchLanguageButton = this.view.getSwitchLanguageButton().getHTML();
switchLanguageButton.addEventListener('click', () => {
const newLanguage =
getStore().getState().currentLanguage === LANGUAGE_CHOICE.EN ? LANGUAGE_CHOICE.RU : LANGUAGE_CHOICE.EN;
private setChangeLanguageCheckboxHandler(): boolean {
const switchLanguageCheckbox = this.view.getSwitchLanguageCheckbox().getHTML();
switchLanguageCheckbox.addEventListener('click', async () => {
const { currentUser } = getStore().getState();

serverMessageModel.showServerMessage(SERVER_MESSAGE[newLanguage].LANGUAGE_CHANGED, MESSAGE_STATUS.SUCCESS);
getStore().dispatch(setCurrentLanguage(newLanguage));
try {
if (currentUser) {
const newLanguage = currentUser.locale === LANGUAGE_CHOICE.EN ? LANGUAGE_CHOICE.RU : LANGUAGE_CHOICE.EN;
const newUser = await getCustomerModel().editCustomer(
[CustomerModel.actionSetLocale(newLanguage)],
currentUser,
);
getStore().dispatch(setCurrentLanguage(newLanguage));
serverMessageModel.showServerMessage(SERVER_MESSAGE_KEYS.LANGUAGE_CHANGED, MESSAGE_STATUS.SUCCESS);
getStore().dispatch(setCurrentUser(newUser));
}
} catch {
// TBD Change to showError
serverMessageModel.showServerMessage(
SERVER_MESSAGE[getStore().getState().currentLanguage].BAD_REQUEST,
MESSAGE_STATUS.ERROR,
);
}
});

return true;
Expand Down
55 changes: 37 additions & 18 deletions src/widgets/Header/view/HeaderView.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,18 @@
import type { LanguageChoiceType } from '@/shared/constants/buttons.ts';

import ButtonModel from '@/shared/Button/model/ButtonModel.ts';
import InputModel from '@/shared/Input/model/InputModel.ts';
import LinkModel from '@/shared/Link/model/LinkModel.ts';
import getStore from '@/shared/Store/Store.ts';
import { switchAppTheme } from '@/shared/Store/actions.ts';
import observeStore, { selectCurrentLanguage, selectCurrentPage, selectCurrentUser } from '@/shared/Store/observer.ts';
import { BUTTON_TEXT, BUTTON_TEXT_KEYS } from '@/shared/constants/buttons.ts';
import observeStore, { selectCurrentPage, selectCurrentUser } from '@/shared/Store/observer.ts';
import { BUTTON_TEXT, BUTTON_TEXT_KEYS, LANGUAGE_CHOICE } from '@/shared/constants/buttons.ts';
import { AUTOCOMPLETE_OPTION } from '@/shared/constants/common.ts';
import { INPUT_TYPE } from '@/shared/constants/forms.ts';
import { EMAIL_FIELD } from '@/shared/constants/forms/login/fieldParams.ts';
import { PAGE_ID } from '@/shared/constants/pages.ts';
import APP_THEME from '@/shared/constants/styles.ts';
import SVG_DETAILS from '@/shared/constants/svg.ts';
import clearOutElement from '@/shared/utils/clearOutElement.ts';
import createBaseElement from '@/shared/utils/createBaseElement.ts';
import createSVGUse from '@/shared/utils/createSVGUse.ts';
import observeCurrentLanguage from '@/shared/utils/observeCurrentLanguage.ts';
Expand All @@ -28,7 +30,7 @@ class HeaderView {

private navigationWrapper: HTMLDivElement;

private switchLanguageButton: ButtonModel;
private switchLanguageCheckbox: InputModel;

private switchThemeCheckbox: InputModel;

Expand All @@ -44,7 +46,7 @@ class HeaderView {
this.toCartLink = this.createToCartLink();
this.toProfileLink = this.createToProfileLink();
this.switchThemeCheckbox = this.createSwitchThemeCheckbox();
this.switchLanguageButton = this.createSwitchLanguageButton();
this.switchLanguageCheckbox = this.createSwitchLanguageCheckbox();
this.navigationWrapper = this.createNavigationWrapper();
this.burgerButton = this.createBurgerButton();
this.wrapper = this.createWrapper();
Expand Down Expand Up @@ -104,9 +106,9 @@ class HeaderView {
return this.header;
}

private createLanguageButtonSVG(): SVGSVGElement {
private createLanguageSVG(lang: LanguageChoiceType): SVGSVGElement {
const svg = document.createElementNS(SVG_DETAILS.SVG_URL, 'svg');
svg.append(createSVGUse(SVG_DETAILS.SWITCH_LANGUAGE[getStore().getState().currentLanguage]));
svg.append(createSVGUse(lang));
return svg;
}

Expand Down Expand Up @@ -141,7 +143,7 @@ class HeaderView {
tag: 'div',
});
this.navigationWrapper.append(
this.switchLanguageButton.getHTML(),
this.createSwitchLanguageLabel(),
this.logoutButton.getHTML(),
this.toCartLink.getHTML(),
this.toProfileLink.getHTML(),
Expand All @@ -151,19 +153,36 @@ class HeaderView {
return this.navigationWrapper;
}

private createSwitchLanguageButton(): ButtonModel {
this.switchLanguageButton = new ButtonModel({
classes: [styles.switchLanguageButton],
private createSwitchLanguageCheckbox(): InputModel {
this.switchLanguageCheckbox = new InputModel({
autocomplete: EMAIL_FIELD.inputParams.autocomplete,
id: styles.switchLanguageLabel,
placeholder: '',
type: INPUT_TYPE.CHECK_BOX,
});
this.switchLanguageCheckbox.getHTML().classList.add(styles.switchLanguageCheckbox);
this.switchLanguageCheckbox.getHTML().checked = getStore().getState().currentUser?.locale === LANGUAGE_CHOICE.EN;

this.switchLanguageButton.getHTML().append(this.createLanguageButtonSVG());
return this.switchLanguageCheckbox;
}

observeStore(selectCurrentLanguage, () => {
clearOutElement(this.switchLanguageButton.getHTML());
this.switchLanguageButton.getHTML().append(this.createLanguageButtonSVG());
private createSwitchLanguageLabel(): HTMLLabelElement {
const label = createBaseElement({
attributes: { for: styles.switchLanguageLabel },
cssClasses: [styles.switchLanguageLabel, styles.switchLanguageLabelAbs],
tag: 'label',
});
const span = createBaseElement({
cssClasses: [styles.switchLanguageLabelSpan],
tag: 'span',
});
const enSVG = this.createLanguageSVG(LANGUAGE_CHOICE.EN);
enSVG.classList.add(styles.enSVG);
const ruSVG = this.createLanguageSVG(LANGUAGE_CHOICE.RU);
ruSVG.classList.add(styles.ruSVG);

return this.switchLanguageButton;
label.append(enSVG, ruSVG, this.switchLanguageCheckbox.getHTML(), span);
return label;
}

private createSwitchThemeCheckbox(): InputModel {
Expand Down Expand Up @@ -297,8 +316,8 @@ class HeaderView {
return this.navigationWrapper;
}

public getSwitchLanguageButton(): ButtonModel {
return this.switchLanguageButton;
public getSwitchLanguageCheckbox(): InputModel {
return this.switchLanguageCheckbox;
}

public getToCartLink(): LinkModel {
Expand Down
123 changes: 100 additions & 23 deletions src/widgets/Header/view/headerView.module.scss
Original file line number Diff line number Diff line change
Expand Up @@ -45,29 +45,6 @@
}
}

.switchLanguageButton {
display: flex;
align-items: center;
order: 3;
overflow: hidden;
border: 2px solid var(--noble-gray-300);
border-radius: 50%;
width: var(--small-offset);
height: var(--small-offset);
transition: border-color 0.2s;

svg {
width: var(--small-offset);
height: var(--small-offset);
}

@media (hover: hover) {
&:hover {
border-color: var(--steam-green-800);
}
}
}

.logo {
order: 1;
width: 40px;
Expand Down Expand Up @@ -377,3 +354,103 @@
right: 5px;
fill: var(--white);
}

.switchLanguageLabel {
position: relative;
display: inline-block;
order: 3;
width: var(--large-offset);
height: calc(calc(var(--small-offset) / 1.5) + var(--extra-small-offset));
cursor: pointer;

@media (max-width: 768px) {
width: var(--large-offset);
}

&:disabled {
background-color: var(--noble-gray-300);
pointer-events: none;
}
}

.switchLanguageCheckbox {
width: 0;
height: 0;
opacity: 1;

&:checked {
+ .switchLanguageLabelSpan {
background-color: #c4c4c4a8;
}

+ .switchLanguageLabelSpan::before {
background-color: #c4c4c4a8;
transform: translate(calc(var(--small-offset) + 3.5px), -50%);
}

@media (max-width: 768px) {
+ .switchLanguageLabelSpan::before {
transform: translate(calc(var(--small-offset) - 3.5px), -50%);
}
}
}
}

.switchLanguageLabelSpan {
position: absolute;
border-radius: calc(var(--large-br) * 2);
background-color: #d8d8d85c;
transition: 0.3s cubic-bezier(0.8, 0.5, 0.2, 1.4);
cursor: pointer;
pointer-events: none;
inset: 0;

&::before {
content: '';
position: absolute;
left: 5px;
top: 50%;
bottom: 0;
z-index: 2;
border-radius: var(--large-br);
width: calc(var(--small-offset) / 1.5);
height: calc(var(--small-offset) / 1.5);
box-shadow: 0 1px 5px #353535;
background-color: #c4c4c4a8;
transform: translateY(-50%);
transition: 0.3s cubic-bezier(0.8, 0.5, 0.2, 1.4);
}
}

.enSVG,
.ruSVG {
position: absolute;
top: 50%;
z-index: 1;
border-radius: 50%;
width: calc(var(--small-offset) / 1.5);
height: calc(var(--small-offset) / 1.5);
transform: translateY(-50%);
pointer-events: none;
}

.enSVG {
left: 5px;
fill: var(--noble-gray-800);
}

.ruSVG {
right: 5px;
fill: var(--white);
}

@keyframes show {
0% {
opacity: 0;
}

100% {
display: block;
opacity: 1;
}
}

0 comments on commit 9be3d0f

Please sign in to comment.