first commit
This commit is contained in:
86
modules/contribute/contribute.module.scss
Normal file
86
modules/contribute/contribute.module.scss
Normal file
@@ -0,0 +1,86 @@
|
||||
.reviewCard {
|
||||
margin-top: 48px;
|
||||
border-bottom-left-radius: 40px !important;
|
||||
border-bottom-right-radius: 40px !important;
|
||||
overflow: unset !important;
|
||||
--on-background: #303309;
|
||||
--on-background-secondary: #616825;
|
||||
--background: #e6f45d;
|
||||
}
|
||||
|
||||
.reviewCardContent {
|
||||
margin: -36px;
|
||||
margin-top: 36px !important;
|
||||
}
|
||||
|
||||
.googlePlayButton {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.main {
|
||||
padding: 48px;
|
||||
padding-top: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 48px;
|
||||
max-width: 1500px;
|
||||
width: 100%;
|
||||
align-self: center;
|
||||
}
|
||||
|
||||
.cardsRow {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
gap: 48px;
|
||||
}
|
||||
|
||||
.stores {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
.additionalStores {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
|
||||
&>*:first-child {
|
||||
flex-grow: 1;
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-width: 980px) {
|
||||
.cardsRow {
|
||||
flex-direction: column;
|
||||
}
|
||||
.reviewCard {
|
||||
margin-top: 0;
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-width: 730px) {
|
||||
.main {
|
||||
padding: 24px;
|
||||
gap: 24px;
|
||||
}
|
||||
.cardsRow {
|
||||
gap: 24px;
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-width: 500px) {
|
||||
.main {
|
||||
padding: 16px;
|
||||
gap: 16px;
|
||||
}
|
||||
.cardsRow {
|
||||
gap: 16px;
|
||||
}
|
||||
.homeLink {
|
||||
margin: 0 auto;
|
||||
margin-top: 44px;
|
||||
}
|
||||
.description {
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
216
modules/contribute/header/header.module.scss
Normal file
216
modules/contribute/header/header.module.scss
Normal file
@@ -0,0 +1,216 @@
|
||||
.header {
|
||||
min-height: 30vh;
|
||||
padding: 220px 78px;
|
||||
padding-top: 330px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
justify-content: center;
|
||||
position: relative;
|
||||
width: 100%;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.content {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
max-width: 1300px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.description {
|
||||
font-weight: 600;
|
||||
font-size: 2rem;
|
||||
max-width: 780px;
|
||||
color: #4F3527;
|
||||
line-height: 150%;
|
||||
|
||||
.appName {
|
||||
font-weight: 700;
|
||||
color: #361400;
|
||||
}
|
||||
|
||||
.hobbyProject {
|
||||
}
|
||||
|
||||
.notPlanEarnMoney {
|
||||
margin: 0px 20px;
|
||||
|
||||
span {
|
||||
box-shadow: -16px 0px 0px 2px #FF005C, 16px 0px 0px 2px #FF005C;
|
||||
background-color: #FF005C;
|
||||
color: #fff;
|
||||
box-decoration-break: clone;
|
||||
border-radius: 9px;
|
||||
}
|
||||
}
|
||||
|
||||
.contributeArrowWrapper {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.contributeArrow {
|
||||
position: relative;
|
||||
display: inline;
|
||||
|
||||
div {
|
||||
position: absolute;
|
||||
top: 13px;
|
||||
left: 26px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.chipsContainer {
|
||||
flex-grow: 1;
|
||||
align-items: center;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
transform: rotate(-25deg);
|
||||
width: max-content;
|
||||
}
|
||||
|
||||
.chip {
|
||||
padding: 12px 32px;
|
||||
padding-right: 22px;
|
||||
border-radius: 40px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
flex-direction: row;
|
||||
gap: 9px;
|
||||
font-size: 1.62rem;
|
||||
font-weight: 600;
|
||||
width: fit-content;
|
||||
|
||||
.icon {
|
||||
display: flex;
|
||||
}
|
||||
}
|
||||
|
||||
.openSourceChip {
|
||||
background-color: #DDF9D0;
|
||||
color: #339704;
|
||||
transform: translateX(-50px);
|
||||
|
||||
svg {
|
||||
color: #339704;
|
||||
width: 35px;
|
||||
height: 35px;
|
||||
}
|
||||
}
|
||||
|
||||
.madeWithLoveChip {
|
||||
background-color: #FFEEF2;
|
||||
color: #861118;
|
||||
transform: translateX(50px);
|
||||
|
||||
svg {
|
||||
color: #FF003D;
|
||||
width: 35px;
|
||||
height: 35px;
|
||||
}
|
||||
}
|
||||
|
||||
.gradientContainer {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: calc(100% + 300px);
|
||||
z-index: -100;
|
||||
}
|
||||
|
||||
.transitionToBackgound {
|
||||
content: '';
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 300px;
|
||||
background: linear-gradient(180deg, #ffffff00 0%, #ffffff 300px);
|
||||
}
|
||||
|
||||
|
||||
@media (max-width: 1250px) {
|
||||
.chipsContainer {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
left: -60px;
|
||||
margin: auto;
|
||||
top: 230px;
|
||||
flex-direction: row;
|
||||
transform: rotate(356deg);
|
||||
z-index: -1;
|
||||
}
|
||||
.openSourceChip {
|
||||
transform: translate(90px, -20px) rotate(-15deg);
|
||||
}
|
||||
.madeWithLoveChip {
|
||||
transform: translateX(0px);
|
||||
}
|
||||
.header {
|
||||
padding-top: 390px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 940px) {
|
||||
.header {
|
||||
padding: 200px 48px;
|
||||
padding-top: 360px;
|
||||
}
|
||||
|
||||
.description {
|
||||
text-align: justify;
|
||||
max-width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 800px) {
|
||||
.header {
|
||||
padding: 130px 48px;
|
||||
padding-top: 360px;
|
||||
}
|
||||
|
||||
.description {
|
||||
font-size: 1.5rem;
|
||||
line-height: 170%;
|
||||
|
||||
.contributeArrow {
|
||||
div {
|
||||
top: 8px;
|
||||
}
|
||||
svg {
|
||||
width: auto;
|
||||
height: 150px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 640px) {
|
||||
.header {
|
||||
padding: 110px 36px;
|
||||
padding-top: 360px;
|
||||
}
|
||||
|
||||
.chipsContainer {
|
||||
top: 200px;
|
||||
scale: 0.7;
|
||||
}
|
||||
|
||||
.description {
|
||||
.contributeArrow {
|
||||
div {
|
||||
top: 10px;
|
||||
}
|
||||
svg {
|
||||
width: auto;
|
||||
height: 130px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
127
modules/contribute/header/header.tsx
Normal file
127
modules/contribute/header/header.tsx
Normal file
@@ -0,0 +1,127 @@
|
||||
import styles from './header.module.scss';
|
||||
import { TopBar } from '@/components/topBar';
|
||||
import useLocale from '@/utils/useLocale';
|
||||
import { LocalesMap } from '@/utils/useLocale';
|
||||
import { clsx } from 'clsx';
|
||||
import LoveIcon from '@/assets/icons/love.svg';
|
||||
import OpenSourceIcon from '@/assets/icons/open-source.svg';
|
||||
import { Gradients } from '@/components/gradients';
|
||||
import ContributeArrow from '@/assets/images/contribute-arrow.svg';
|
||||
import ContributeArrowInvert from '@/assets/images/contribute-arrow-invert.svg';
|
||||
import { useEffect, useRef, useState } from 'react';
|
||||
import useMediaQuery from '@/utils/useMediaQuery';
|
||||
|
||||
export const locales: LocalesMap = {
|
||||
ru: {
|
||||
appName: 'RideBus',
|
||||
description: (
|
||||
<>
|
||||
<span className={styles.appName}>RideBus</span> — это <span className={styles.hobbyProject}>хобби-проект</span>{' '}
|
||||
и я <span className={styles.notPlanEarnMoney}><span>не планирую на нём зарабатывать</span></span>,{' '}
|
||||
если вы хотите помочь, вот способ, как это <span className={styles.contributeArrowWrapper}>сделать<Arrow /></span>
|
||||
</>
|
||||
),
|
||||
openSource: 'Open source',
|
||||
madeWithLove: 'Сделано с',
|
||||
},
|
||||
en: {
|
||||
appName: 'RideBus',
|
||||
description: (
|
||||
<>
|
||||
<span className={styles.appName}>RideBus</span> is <span className={styles.hobbyProject}>a hobby project</span>{' '}
|
||||
and I <span className={styles.notPlanEarnMoney}><span>do not plan to earn money on it</span></span>,{' '}
|
||||
if you want to help, here is a way how to do <span className={styles.contributeArrowWrapper}>it <Arrow /></span>
|
||||
</>
|
||||
),
|
||||
openSource: 'Open source',
|
||||
madeWithLove: 'Made with',
|
||||
},
|
||||
};
|
||||
|
||||
function Arrow() {
|
||||
const rootRef = useRef<HTMLDivElement>(null);
|
||||
const [isInvertArrow, setIsInvertArrow] = useState(false);
|
||||
const breakpoint640 = useMediaQuery('(max-width: 640px)');
|
||||
const breakpoint800 = useMediaQuery('(max-width: 800px)');
|
||||
|
||||
useEffect(() => {
|
||||
const arrowWidth = breakpoint640 ? 260 : breakpoint800 ? 300 : 460;
|
||||
|
||||
const handleResize = () => {
|
||||
if (rootRef.current) {
|
||||
const { left } = rootRef.current.getBoundingClientRect();
|
||||
|
||||
if (window.innerWidth < left + arrowWidth) {
|
||||
setIsInvertArrow(true);
|
||||
} else {
|
||||
setIsInvertArrow(false);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
window.addEventListener('resize', handleResize);
|
||||
|
||||
return () => {
|
||||
window.removeEventListener('resize', handleResize);
|
||||
};
|
||||
}, [breakpoint640, breakpoint800]);
|
||||
|
||||
let left = isInvertArrow ? '-130px' : '26px';
|
||||
|
||||
if (breakpoint640) {
|
||||
left = isInvertArrow ? '-95px' : '16px';
|
||||
}
|
||||
|
||||
if (breakpoint800) {
|
||||
left = isInvertArrow ? '-95px' : '16px';
|
||||
}
|
||||
|
||||
return (
|
||||
<span ref={rootRef} className={styles.contributeArrow}>
|
||||
<span style={{ width: '140px', display: 'inline-block' }} />
|
||||
<div style={{ left }}>
|
||||
{isInvertArrow ? <ContributeArrowInvert /> : <ContributeArrow />}
|
||||
</div>
|
||||
</span>
|
||||
)
|
||||
}
|
||||
|
||||
type ChipProps = {
|
||||
className?: string;
|
||||
label: string;
|
||||
icon: any;
|
||||
};
|
||||
|
||||
function Chip(props: ChipProps) {
|
||||
const { className, label, icon } = props;
|
||||
|
||||
return (
|
||||
<div className={clsx(styles.chip, className)}>
|
||||
<span>{label}</span>
|
||||
<span className={styles.icon}>{icon}</span>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
export function Header() {
|
||||
const t = useLocale(locales);
|
||||
return (
|
||||
<header className={styles.header}>
|
||||
<TopBar notHide />
|
||||
<div className={styles.content}>
|
||||
<p className={styles.description}>{t('description')}</p>
|
||||
<div className={styles.chipsContainer}>
|
||||
<Chip className={styles.openSourceChip} label={t('openSource')} icon={(<OpenSourceIcon />)} />
|
||||
<Chip className={styles.madeWithLoveChip} label={t('madeWithLove')} icon={(<LoveIcon />)} />
|
||||
</div>
|
||||
</div>
|
||||
<div className={styles.gradientContainer}>
|
||||
<div
|
||||
className={styles.transitionToBackgound}
|
||||
/>
|
||||
<Gradients />
|
||||
</div>
|
||||
</header>
|
||||
);
|
||||
}
|
||||
1
modules/contribute/header/index.tsx
Normal file
1
modules/contribute/header/index.tsx
Normal file
@@ -0,0 +1 @@
|
||||
export * from './header';
|
||||
21
modules/contribute/locales.tsx
Normal file
21
modules/contribute/locales.tsx
Normal file
@@ -0,0 +1,21 @@
|
||||
import { LocalesMap } from '@/utils/useLocale';
|
||||
|
||||
export const locales: LocalesMap = {
|
||||
ru: {
|
||||
pageTitle: 'RideBus - Вклад',
|
||||
pageDescription:
|
||||
'RideBus — Приложение, которое поможет вам удобно смотреть расписание общественного транспорта',
|
||||
appName: 'RideBus',
|
||||
reviewTitle: 'Оставьте отзыв',
|
||||
reviewDescription:
|
||||
'Буду благодарен, если вы оставите свой отзыв о приложении. Отзывы очень важны и помогают улучшать приложение.',
|
||||
},
|
||||
en: {
|
||||
pageTitle: 'RideBus - Contribute',
|
||||
pageDescription: 'RideBus — An app that helps you conveniently view public transportation schedules',
|
||||
appName: 'RideBus',
|
||||
reviewTitle: 'Leave a review',
|
||||
reviewDescription:
|
||||
'I would be grateful if you could leave your feedback on the application. Reviews are very important and help us improve application.',
|
||||
},
|
||||
};
|
||||
1
modules/contribute/shareCard/index.tsx
Normal file
1
modules/contribute/shareCard/index.tsx
Normal file
@@ -0,0 +1 @@
|
||||
export * from './shareCard';
|
||||
49
modules/contribute/shareCard/shareCard.module.scss
Normal file
49
modules/contribute/shareCard/shareCard.module.scss
Normal file
@@ -0,0 +1,49 @@
|
||||
.card {
|
||||
--on-background: #59321c;
|
||||
--on-background-secondary: #866655;
|
||||
--background: #ffefe9;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.description {
|
||||
padding: 8px;
|
||||
margin: -8px;
|
||||
margin-top: 0;
|
||||
padding-top: 6px;
|
||||
}
|
||||
|
||||
.heartContiner {
|
||||
position: absolute;
|
||||
bottom: -18%;
|
||||
right: -17%;
|
||||
z-index: 0;
|
||||
}
|
||||
|
||||
.heartImage {
|
||||
width: 420px;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 980px) {
|
||||
.card {
|
||||
height: min-content;
|
||||
}
|
||||
.heartContiner {
|
||||
bottom: -32%;
|
||||
right: -4%;
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-width: 820px) {
|
||||
.heartContiner {
|
||||
bottom: -42%;
|
||||
right: -4%;
|
||||
}
|
||||
|
||||
.heartImage {
|
||||
width: 300px;
|
||||
}
|
||||
.content {
|
||||
margin-top: 0;
|
||||
}
|
||||
}
|
||||
60
modules/contribute/shareCard/shareCard.tsx
Normal file
60
modules/contribute/shareCard/shareCard.tsx
Normal file
@@ -0,0 +1,60 @@
|
||||
import styles from './shareCard.module.scss';
|
||||
import { Card } from '@/components/card';
|
||||
import clsx from 'clsx';
|
||||
import useLocale, { LocalesMap } from '@/utils/useLocale';
|
||||
import { CursorEffect } from '@/components/cursor';
|
||||
|
||||
const locales: LocalesMap = {
|
||||
ru: {
|
||||
appName: 'RideBus',
|
||||
slogan: 'Приложение, которое поможет вам удобно смотреть расписание общественного транспорта',
|
||||
title: 'Расскажите друзьям',
|
||||
description:
|
||||
'Мне очень важно, чтобы мои проекты приносили пользу. Если вы расскажете своим друзьям о своем опыте использования этого приложения и как оно помогло вам, я буду вам очень признателен.',
|
||||
},
|
||||
en: {
|
||||
appName: 'RideBus',
|
||||
slogan: 'An app that helps you conveniently view public transportation schedules',
|
||||
title: 'Tell your friends',
|
||||
description:
|
||||
'It is very important to me that my projects bring benefits. If you tell your friends about your experience using this application and how it helped you, I will be very grateful to you.',
|
||||
},
|
||||
};
|
||||
|
||||
export function ShareCard() {
|
||||
const t = useLocale(locales);
|
||||
|
||||
const ShareCard = (
|
||||
<Card
|
||||
className={clsx(styles.card)}
|
||||
title={t('title')}
|
||||
subtitle={t('description')}
|
||||
backdropOnText
|
||||
classes={{ subtitle: styles.description, content: styles.content }}
|
||||
onClick={() => {
|
||||
if (navigator.share) {
|
||||
navigator
|
||||
.share({
|
||||
title: t('appName'),
|
||||
text: t('slogan'),
|
||||
url: 'https://ridebus.by/',
|
||||
})
|
||||
.then(() => {
|
||||
console.log('Thanks for sharing!');
|
||||
})
|
||||
.catch(console.error);
|
||||
}
|
||||
}}
|
||||
>
|
||||
</Card>
|
||||
);
|
||||
if (typeof window !== 'undefined' && (navigator.share as any)) {
|
||||
return (
|
||||
<CursorEffect cursorPadding={8} cursorBorderRadius={36}>
|
||||
{ShareCard}
|
||||
</CursorEffect>
|
||||
);
|
||||
} else {
|
||||
return ShareCard;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user