feat: navbar onboarding, add blur name input from done button on compatibility page, fix cross button on compatibility page, add menu button to home page, replace download button moon page, fix percents

This commit is contained in:
gofnnp 2023-10-06 00:34:38 +04:00
parent 2fc26aceb0
commit 844d5d1295
16 changed files with 183 additions and 47 deletions

17
public/finger.svg Normal file

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 10 KiB

View File

@ -225,7 +225,9 @@ function Layout({ setIsSpecialOfferOpen }: LayoutProps): JSX.Element {
{showNavbar ? ( {showNavbar ? (
<Navbar isOpen={isMenuOpen} closeMenu={() => setIsMenuOpen(false)} /> <Navbar isOpen={isMenuOpen} closeMenu={() => setIsMenuOpen(false)} />
) : null} ) : null}
{showNavbarFooter && hasNavbarFooter(location.pathname) ? ( {
// showNavbarFooter &&
hasNavbarFooter(location.pathname) ? (
<NavbarFooter items={navbarItems} /> <NavbarFooter items={navbarItems} />
) : null} ) : null}
</div> </div>

View File

@ -40,9 +40,10 @@ function CompatResultPage(): JSX.Element {
if (homeConfig.pathFromHome === EPathsFromHome.compatibility) { if (homeConfig.pathFromHome === EPathsFromHome.compatibility) {
return navigate(routes.client.breath()); return navigate(routes.client.breath());
} }
return navigate(routes.client.compatibility());
}; };
const handleCompatibility = () => navigate(routes.client.compatibility()); // const handleCompatibility = () => navigate(routes.client.compatibility());
const loadData = useCallback(async () => { const loadData = useCallback(async () => {
const right_bday = const right_bday =
@ -119,7 +120,7 @@ function CompatResultPage(): JSX.Element {
}} }}
/> />
</FullScreenModal> </FullScreenModal>
<div className={styles.cross} onClick={handleCompatibility}></div> <div className={styles.cross} onClick={handleNext}></div>
<div className={styles["title-container"]}> <div className={styles["title-container"]}>
<Title variant="h2">{t("you_and", { user: rightUser.name })}</Title> <Title variant="h2">{t("you_and", { user: rightUser.name })}</Title>
</div> </div>

View File

@ -169,7 +169,7 @@ function CompatibilityPage(): JSX.Element {
{!onboardingCompatibility.isShown && <> {!onboardingCompatibility.isShown && <>
{currentOnboarding === 0 && ( {currentOnboarding === 0 && (
<Onboarding <Onboarding
targetRef={inputRef} targetRef={inputRef.current as HTMLDivElement}
isShow={currentOnboarding === 0} isShow={currentOnboarding === 0}
direction={EDirectionOnboarding.BOTTOM} direction={EDirectionOnboarding.BOTTOM}
showBackground={true} showBackground={true}
@ -183,7 +183,7 @@ function CompatibilityPage(): JSX.Element {
)} )}
{currentOnboarding === 1 && ( {currentOnboarding === 1 && (
<Onboarding <Onboarding
targetRef={dateRef} targetRef={dateRef.current as HTMLDivElement}
isShow={currentOnboarding === 1} isShow={currentOnboarding === 1}
direction={EDirectionOnboarding.BOTTOM} direction={EDirectionOnboarding.BOTTOM}
showBackground={true} showBackground={true}
@ -206,7 +206,7 @@ function CompatibilityPage(): JSX.Element {
)} )}
{currentOnboarding === 3 && ( {currentOnboarding === 3 && (
<Onboarding <Onboarding
targetRef={mainButtonRef} targetRef={mainButtonRef.current as HTMLDivElement}
isShow={currentOnboarding === 3} isShow={currentOnboarding === 3}
direction={EDirectionOnboarding.TOP} direction={EDirectionOnboarding.TOP}
showBackground={true} showBackground={true}
@ -231,13 +231,16 @@ function CompatibilityPage(): JSX.Element {
onKeyDown={(e) => { onKeyDown={(e) => {
if (!name.length) return; if (!name.length) return;
if (e.key === "Enter") { if (e.key === "Enter") {
setCurrentOnboarding(1);
(e.target as HTMLInputElement).blur(); (e.target as HTMLInputElement).blur();
dateRef.current?.scrollIntoView({ behavior: "smooth" });
} }
}} }}
onValid={handleValidName} onValid={handleValidName}
onInvalid={() => setIsDisabledName(true)} onInvalid={() => setIsDisabledName(true)}
onBlur={() => {
if (!name.length) return;
setCurrentOnboarding(1);
dateRef.current?.scrollIntoView({ behavior: "smooth" });
}}
/> />
</div> </div>
<div <div
@ -250,7 +253,7 @@ function CompatibilityPage(): JSX.Element {
</div> </div>
{currentOnboarding === 2 && !onboardingCompatibility.isShown && ( {currentOnboarding === 2 && !onboardingCompatibility.isShown && (
<Onboarding <Onboarding
targetRef={categoriesRef} targetRef={categoriesRef.current as HTMLDivElement}
isShow={currentOnboarding === 2} isShow={currentOnboarding === 2}
direction={EDirectionOnboarding.TOP} direction={EDirectionOnboarding.TOP}
showBackground={true} showBackground={true}

View File

@ -10,6 +10,7 @@ interface INameInput<T> {
onKeyDown: (event: React.KeyboardEvent<HTMLInputElement>) => void; onKeyDown: (event: React.KeyboardEvent<HTMLInputElement>) => void;
onValid: (value: string) => void; onValid: (value: string) => void;
onInvalid: () => void; onInvalid: () => void;
onBlur: () => void;
} }
const isValidName = (name: string) => { const isValidName = (name: string) => {
@ -17,7 +18,8 @@ const isValidName = (name: string) => {
}; };
function NameInput(props: INameInput<string>): JSX.Element { function NameInput(props: INameInput<string>): JSX.Element {
const { name, value, placeholder, onValid, onInvalid, onKeyDown } = props; const { name, value, placeholder, onValid, onInvalid, onKeyDown, onBlur } =
props;
const [userName, setUserName] = useState(value); const [userName, setUserName] = useState(value);
const handleChange = (event: React.ChangeEvent<HTMLInputElement>) => { const handleChange = (event: React.ChangeEvent<HTMLInputElement>) => {
setUserName(event.target.value); setUserName(event.target.value);
@ -40,6 +42,7 @@ function NameInput(props: INameInput<string>): JSX.Element {
onChange={handleChange} onChange={handleChange}
onKeyDown={onKeyDown} onKeyDown={onKeyDown}
placeholder={placeholder ?? " "} placeholder={placeholder ?? " "}
onBlur={onBlur}
/> />
</div> </div>
); );

View File

@ -14,7 +14,7 @@ const colors: Record<string, string> = {
} }
const valueFormatter = (value: number) => { const valueFormatter = (value: number) => {
return `${(value * 10).toFixed(1)}%` return `${(value * 100).toFixed(1)}%`
} }
function EnergyValues({ className, values }: IEnergyValues): JSX.Element { function EnergyValues({ className, values }: IEnergyValues): JSX.Element {

View File

@ -166,7 +166,7 @@ function HomePage(): JSX.Element {
isShowNavbar ? styles["content__buttons--hidden"] : "" isShowNavbar ? styles["content__buttons--hidden"] : ""
}`} }`}
> >
<Onboarding targetRef={buttonsRef} isShow={isShowOnboardingHome}> <Onboarding targetRef={buttonsRef.current as HTMLDivElement} isShow={isShowOnboardingHome}>
<TextWithFinger <TextWithFinger
text={t("au.web_onbording.start")} text={t("au.web_onbording.start")}
crossClickHandler={() => setIsShowOnboardingHome(false)} crossClickHandler={() => setIsShowOnboardingHome(false)}

View File

@ -6,7 +6,7 @@
} }
.navbar.navbar--open { .navbar.navbar--open {
z-index: 5; z-index: 99;
} }
.navbar.navbar--open .navbar__panel { .navbar.navbar--open .navbar__panel {

View File

@ -1,5 +1,11 @@
import { Link } from "react-router-dom"; import { Link } from "react-router-dom";
import styles from "./styles.module.css"; import styles from "./styles.module.css";
import Onboarding from "../Onboarding";
import { useRef, useState } from "react";
import TextWithFinger from "../TextWithFinger";
import { useDispatch, useSelector } from "react-redux";
import { actions, selectors } from "@/store";
import { useTranslation } from "react-i18next";
export interface INavbarHomeItems { export interface INavbarHomeItems {
title: string; title: string;
@ -15,6 +21,58 @@ interface INavbarHomeProps {
} }
function NavbarFooter({ items }: INavbarHomeProps): JSX.Element { function NavbarFooter({ items }: INavbarHomeProps): JSX.Element {
const dispatch = useDispatch();
const { t } = useTranslation();
const onboardingConfigNavbarFooter = useSelector(
selectors.selectOnboardingNavbarFooter
);
const [isShowOnboardingNavbarFooter, setIsShowOnboardingNavbarFooter] =
useState(!onboardingConfigNavbarFooter.isShown);
const [selectedOnboarding, setSelectedOnboarding] = useState(3);
const buttonsRef = useRef([] as HTMLDivElement[]);
const setShownOnboarding = () => {
setIsShowOnboardingNavbarFooter(false);
dispatch(
actions.onboardingConfig.update({
navbarFooter: {
isShown: true,
},
})
);
};
const onboardingsSettings = [
{
text: t("au.web_onbording.breathing"),
onClick: () => setSelectedOnboarding(2),
classNameText: `${styles["breathing-onboarding__text"]} ${styles.onboarding}`,
},
{
text: t("au.web_onbording.onboarding_2"),
onClick: () => setSelectedOnboarding(2),
},
{
text: t("au.web_onbording.compatibility"),
onClick: () => setShownOnboarding(),
classNameText: `${styles["compatibility-onboarding__text"]} ${styles.onboarding}`,
},
{
text: t("au.web_onbording.moon"),
onClick: () => setSelectedOnboarding(0),
classNameText: `${styles["moon-onboarding__text"]} ${styles.onboarding}`,
},
];
const handleClick = (item: INavbarHomeItems) => {
onboardingsSettings[selectedOnboarding].onClick();
if (item.onClick) {
item.onClick();
}
};
return ( return (
<div className={`${styles["container"]}`}> <div className={`${styles["container"]}`}>
{items.map((item, index) => ( {items.map((item, index) => (
@ -23,17 +81,33 @@ function NavbarFooter({ items }: INavbarHomeProps): JSX.Element {
item.active ? styles["navbar-item--active"] : "" item.active ? styles["navbar-item--active"] : ""
}`} }`}
key={index} key={index}
ref={(el) => (buttonsRef.current[index] = el as HTMLDivElement)}
> >
{isShowOnboardingNavbarFooter && (
<Onboarding
targetRef={buttonsRef.current[index]}
isShow={index === selectedOnboarding}
>
<TextWithFinger
text={onboardingsSettings[index].text}
crossClickHandler={() => onboardingsSettings[index].onClick()}
classNameText={onboardingsSettings[index].classNameText}
/>
</Onboarding>
)}
<Link <Link
className={styles["navbar-item__link"]} className={styles["navbar-item__link"]}
to={item.path} to={item.path}
onClick={item.onClick} onClick={() => {
handleClick(item);
}}
> >
<div <div
className={styles["navbar-item__image-container"]} className={styles["navbar-item__image-container"]}
style={{ style={{
backgroundColor: backgroundColor: item.paths.includes(location.pathname)
item.paths.includes(location.pathname) ? "#ff2c57" : "#fff", ? "#ff2c57"
: "#fff",
mask: `url('/navbar-icons/${item.image}')`, mask: `url('/navbar-icons/${item.image}')`,
WebkitMask: `url('/navbar-icons/${item.image}')`, WebkitMask: `url('/navbar-icons/${item.image}')`,
WebkitMaskSize: "contain", WebkitMaskSize: "contain",
@ -50,7 +124,9 @@ function NavbarFooter({ items }: INavbarHomeProps): JSX.Element {
</div> </div>
<p <p
style={{ style={{
color: item.paths.includes(location.pathname) ? "#ff2c57" : "#fff", color: item.paths.includes(location.pathname)
? "#ff2c57"
: "#fff",
}} }}
> >
{item.title} {item.title}

View File

@ -44,3 +44,16 @@
.navbar-item__image { .navbar-item__image {
width: 30px; width: 30px;
} }
.onboarding {
width: 188px;
color: #000;
}
.moon-onboarding__text {
margin-right: 108px;
}
.breathing-onboarding__text {
margin-left: 108px;
}

View File

@ -9,11 +9,12 @@ export enum EDirectionOnboarding {
} }
interface OnboardingProps { interface OnboardingProps {
targetRef: React.RefObject<HTMLElement>; targetRef: HTMLElement;
isShow: boolean; isShow: boolean;
direction?: EDirectionOnboarding; direction?: EDirectionOnboarding;
showBackground?: boolean; showBackground?: boolean;
children: JSX.Element; children: JSX.Element;
classNameContainer?: string;
} }
interface IBoardingCoordinates { interface IBoardingCoordinates {
top: number; top: number;
@ -21,43 +22,43 @@ interface IBoardingCoordinates {
} }
const getCoordinates = ( const getCoordinates = (
targetRef: React.RefObject<HTMLElement>, targetRef: HTMLElement,
direction: EDirectionOnboarding, direction: EDirectionOnboarding,
onboardingRef: React.RefObject<HTMLDivElement> onboardingRef: React.RefObject<HTMLDivElement>
): IBoardingCoordinates => { ): IBoardingCoordinates => {
if (targetRef.current && onboardingRef.current) { if (targetRef && onboardingRef.current) {
switch (direction) { switch (direction) {
case EDirectionOnboarding.LEFT: case EDirectionOnboarding.LEFT:
return { return {
top: top:
targetRef.current.offsetTop + targetRef.offsetTop +
targetRef.current.offsetHeight / 2 - targetRef.offsetHeight / 2 -
onboardingRef.current.offsetHeight / 2, onboardingRef.current.offsetHeight / 2,
left: left:
targetRef.current.offsetLeft - onboardingRef.current.offsetWidth, targetRef.offsetLeft - onboardingRef.current.offsetWidth,
}; };
case EDirectionOnboarding.RIGHT: case EDirectionOnboarding.RIGHT:
return { return {
top: top:
targetRef.current.offsetTop + targetRef.offsetTop +
targetRef.current.offsetHeight / 2 - targetRef.offsetHeight / 2 -
onboardingRef.current.offsetHeight / 2, onboardingRef.current.offsetHeight / 2,
left: targetRef.current.offsetLeft + targetRef.current.offsetWidth, left: targetRef.offsetLeft + targetRef.offsetWidth,
}; };
case EDirectionOnboarding.TOP: case EDirectionOnboarding.TOP:
return { return {
top: targetRef.current.offsetTop - onboardingRef.current.offsetHeight, top: targetRef.offsetTop - onboardingRef.current.offsetHeight,
left: left:
targetRef.current.offsetLeft + targetRef.offsetLeft +
targetRef.current.offsetWidth / 2 - targetRef.offsetWidth / 2 -
onboardingRef.current.offsetWidth / 2, onboardingRef.current.offsetWidth / 2,
}; };
case EDirectionOnboarding.BOTTOM: case EDirectionOnboarding.BOTTOM:
return { return {
top: targetRef.current.offsetTop + targetRef.current.offsetHeight, top: targetRef.offsetTop + targetRef.offsetHeight,
left: left:
targetRef.current.offsetLeft + targetRef.offsetLeft +
targetRef.current.offsetWidth / 2 - targetRef.offsetWidth / 2 -
onboardingRef.current.offsetWidth / 2, onboardingRef.current.offsetWidth / 2,
}; };
} }
@ -70,11 +71,12 @@ const getCoordinates = (
const getClassNameContainer = ( const getClassNameContainer = (
direction: EDirectionOnboarding, direction: EDirectionOnboarding,
showBackground: boolean showBackground: boolean,
classNameContainer?: string
) => { ) => {
return `${styles["onboarding-container"]} ${ return `${styles["onboarding-container"]} ${
styles[`direction-${direction}`] styles[`direction-${direction}`]
} ${showBackground ? styles["background"] : ""}`; } ${showBackground ? styles["background"] : ""} ${classNameContainer}`;
}; };
function Onboarding({ function Onboarding({
@ -82,6 +84,7 @@ function Onboarding({
isShow, isShow,
direction = EDirectionOnboarding.TOP, direction = EDirectionOnboarding.TOP,
showBackground = false, showBackground = false,
classNameContainer = '',
children, children,
}: OnboardingProps): JSX.Element { }: OnboardingProps): JSX.Element {
const onboardingRef = useRef<HTMLDivElement>(null); const onboardingRef = useRef<HTMLDivElement>(null);
@ -99,10 +102,10 @@ function Onboarding({
} }
return ( return (
<div className={getClassNameContainer(direction, showBackground)}> <div className={getClassNameContainer(direction, showBackground, classNameContainer)}>
<div <div
className={`${styles["onboarding"]} ${ className={`${styles["onboarding"]} ${
!targetRef.current || !onboardingRef.current ? styles["hide"] : "" !targetRef || !onboardingRef.current ? styles["hide"] : ""
}`} }`}
style={{ top: `${top}px`, left: `${left}px` }} style={{ top: `${top}px`, left: `${left}px` }}
ref={onboardingRef} ref={onboardingRef}

View File

@ -1,23 +1,25 @@
import { EDirectionOnboarding } from "../Onboarding"; import { EDirectionOnboarding } from "../Onboarding";
import styles from "./styles.module.css"; import styles from "./styles.module.css";
interface OnboardingProps { interface TextWithFingerProps {
text: string; text: string;
showCross?: boolean; showCross?: boolean;
direction?: EDirectionOnboarding; direction?: EDirectionOnboarding;
classNameText?: string;
crossClickHandler?: () => void; crossClickHandler?: () => void;
} }
function Onboarding({ function TextWithFinger({
text, text,
showCross = true, showCross = true,
direction = EDirectionOnboarding.TOP, direction = EDirectionOnboarding.TOP,
classNameText = "",
crossClickHandler, crossClickHandler,
}: OnboardingProps): JSX.Element { }: TextWithFingerProps): JSX.Element {
return ( return (
<> <>
{text.length && ( {text.length && (
<div className={styles["onboarding-text"]}> <div className={`${styles["onboarding-text"]} ${classNameText}`}>
{showCross && ( {showCross && (
<div className={styles["cross"]} onClick={crossClickHandler}></div> <div className={styles["cross"]} onClick={crossClickHandler}></div>
)} )}
@ -26,11 +28,11 @@ function Onboarding({
)} )}
<img <img
className={`${styles["finger"]} ${styles[`direction-${direction}`]}`} className={`${styles["finger"]} ${styles[`direction-${direction}`]}`}
src="/finger.png" src="/finger.svg"
alt="finger" alt="finger"
/> />
</> </>
); );
} }
export default Onboarding; export default TextWithFinger;

View File

@ -60,15 +60,19 @@
.btn-download { .btn-download {
position: absolute; position: absolute;
cursor: pointer;
top: 28px; top: 28px;
right: 32px; right: 32px;
width: 50px; width: 50px;
height: 50px; height: 50px;
background-image: url(./Dowload.png); background-size: 70%;
background-position: center; background-image: url("/Save-icon.png");
background-size: cover;
background-repeat: no-repeat; background-repeat: no-repeat;
background-position: center center;
-webkit-backdrop-filter: blur(14px);
background-color: #696969;
backdrop-filter: blur(14px);
border-radius: 100%;
cursor: pointer;
} }
.zodiac-metas { .zodiac-metas {

View File

@ -89,7 +89,7 @@ export const isEntrypoint = (path: string) => entrypoints.includes(path);
export const isNotEntrypoint = (path: string) => !isEntrypoint(path); export const isNotEntrypoint = (path: string) => !isEntrypoint(path);
export const withNavigationRoutes = [ export const withNavigationRoutes = [
routes.client.wallpaper(), routes.client.wallpaper(),
// routes.client.home(), routes.client.home(),
]; ];
export const hasNavigation = (path: string) => export const hasNavigation = (path: string) =>
withNavigationRoutes.includes(path); withNavigationRoutes.includes(path);

View File

@ -19,6 +19,7 @@ import onboardingConfig, {
selectOnboardingBreath, selectOnboardingBreath,
selectOnboardingCompatibility, selectOnboardingCompatibility,
selectOnboardingHome, selectOnboardingHome,
selectOnboardingNavbarFooter,
actions as onboardingConfigActions, actions as onboardingConfigActions,
} from "./onboarding"; } from "./onboarding";
import payment, { import payment, {
@ -90,6 +91,7 @@ export const selectors = {
selectOnboardingHome, selectOnboardingHome,
selectOnboardingCompatibility, selectOnboardingCompatibility,
selectOnboardingBreath, selectOnboardingBreath,
selectOnboardingNavbarFooter,
...formSelectors, ...formSelectors,
}; };
export type RootState = ReturnType<typeof reducer>; export type RootState = ReturnType<typeof reducer>;

View File

@ -11,6 +11,9 @@ interface IOnboardingConfig {
breath: { breath: {
isShown: boolean; isShown: boolean;
}; };
navbarFooter: {
isShown: boolean;
}
} }
const initialState: IOnboardingConfig = { const initialState: IOnboardingConfig = {
@ -23,6 +26,9 @@ const initialState: IOnboardingConfig = {
breath: { breath: {
isShown: false, isShown: false,
}, },
navbarFooter: {
isShown: false
}
}; };
const onboardingConfigSlice = createSlice({ const onboardingConfigSlice = createSlice({
@ -45,6 +51,10 @@ export const selectOnboardingHome = createSelector(
(state: { onboardingConfig: IOnboardingConfig }) => state.onboardingConfig.home, (state: { onboardingConfig: IOnboardingConfig }) => state.onboardingConfig.home,
(onboardingConfig) => onboardingConfig (onboardingConfig) => onboardingConfig
); );
export const selectOnboardingNavbarFooter = createSelector(
(state: { onboardingConfig: IOnboardingConfig }) => state.onboardingConfig.navbarFooter,
(onboardingConfig) => onboardingConfig
);
export const selectOnboardingCompatibility = createSelector( export const selectOnboardingCompatibility = createSelector(
(state: { onboardingConfig: IOnboardingConfig }) => (state: { onboardingConfig: IOnboardingConfig }) =>
state.onboardingConfig.compatibility, state.onboardingConfig.compatibility,