Skip to content

Commit

Permalink
Make a <Modal> controllable via touch events
Browse files Browse the repository at this point in the history
  • Loading branch information
sashachabin committed Aug 26, 2024
1 parent 2ef80cc commit 25bad25
Show file tree
Hide file tree
Showing 10 changed files with 145 additions and 81 deletions.
2 changes: 1 addition & 1 deletion client/components/Map/MainContainer/MapMainContainer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import { MapZoomControl } from 'components/Map/ZoomControl/MapZoomControl';
import { MapWelcomeMessage } from 'components/Map/WelcomeMessage/MapWelcomeMessage';
import { MapSearchBar } from 'components/Map/SearchBar/SearchBar';
import { Info } from 'components/Map/Info/Info';
import { Feedback } from 'components/Common/Feedback/Feedback';
import { Feedback } from 'components/UI/Feedback/Feedback';

import styles from './MapMainContainer.module.css';
import 'leaflet/dist/leaflet.css';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
background: #f9d548;
font-weight: 400;
font-style: normal;
font-size: 20px;
font-size: 16px;
line-height: 1.05;
text-decoration: none;
}
Expand All @@ -18,13 +18,25 @@

.Feedback_Size-m,
.Feedback_Size-l {
height: 48px;
padding: 8px 24px;
padding: 12px 16px;
}

@media screen and (min-width: 768px) {

@media screen and (min-width: 420px) {
.Feedback {
font-size: 20px;
}

.Feedback_Size-m,
.Feedback_Size-l {
height: 48px;
padding: 8px 24px;
}
}

@media screen and (min-width: 1440px) {
.Feedback_Size-l {
height: 65px;
height: 60px;
padding: 24px 32px;
font-size: 24px;
}
Expand Down
File renamed without changes.
112 changes: 78 additions & 34 deletions client/components/UI/Modal/Modal.module.css
Original file line number Diff line number Diff line change
Expand Up @@ -6,39 +6,49 @@ html:has(.Modal) {
display: flex;
flex-direction: column;
overflow: visible;
width: calc(100% - 32px);
width: calc(100% - 16px);
max-width: none;
margin: auto;
max-height: none;
margin-top: auto;
margin-bottom: 0;
padding: 0;
border: 0;
background: transparent;
transition: .15s ease-out;
animation: .2s ease-in-out fadeIn;
}

.Modal_Align-top {
margin-top: 16px;
}

.Modal_Align-center {
margin-top: auto;
margin-bottom: auto;
.Modal:not([open]) {
display: none;
}

.Modal::backdrop {
background: rgba(12, 27, 39, .3);
}

.Modal_Position-fullOpen {
height: calc(100% - 90px);
}

.Modal_Position-halfOpen {
height: 50%;
}

.Modal_Position-hidden {
overflow: hidden;
height: 15%;
}

.ModalTitle {
margin: 0;
padding: 24px 48px 0 24px;
padding: 32px 56px 0 24px;
font-size: 24px;
}

.ModalInner {
flex-grow: 1;
overflow: auto;
height: 100%;
border-radius: 8px;
border-radius: 16px 16px 0 0;
background: white;
scrollbar-color: transparent transparent;
}
Expand All @@ -48,31 +58,75 @@ html:has(.Modal) {
}

.ModalContent {
padding: 0px 24px 8px;
padding: 0px 24px;
}

.ModalDragArea {
position: sticky;
top: 0;
left: 0;
z-index: 1;
height: 76px;
margin-bottom: -76px;
touch-action: none;
}

.ModalDragArea::before {
content: '';
position: absolute;
top: 7px;
left: 50%;
display: block;
width: 50px;
height: 5px;
border-radius: 10px;
background: #9baac3;
opacity: 0.5;
transform: translate(-50%, 0);
}

.ModalClose {
position: absolute;
top: 0;
right: 0;
padding: 16px;
z-index: 1;
display: flex;
padding: 20px;
border: 0;
border-radius: 50%;
background: none;
color: rgba(0, 0, 0, 0.5);
}

.ModalClose svg {
width: 18px;
height: 18px;
width: 16px;
height: 16px;
}

@media screen and (min-width: 768px) {
.Modal {
width: calc(100% - 140px);
width: calc(100% - 120px);
height: fit-content;
max-height: calc(100% - 160px);
background: none;
}

.Modal_Align-top {
margin-top: 56px;
margin-top: 80px;
margin-bottom: 0;
}

.Modal_Align-center {
margin-top: auto;
margin-bottom: auto;
}

.ModalDragArea {
display: none;
}

.ModalInner {
border-radius: 24px;
}

.ModalClose {
Expand Down Expand Up @@ -117,18 +171,10 @@ html:has(.Modal) {

@media screen and (min-width: 991px) {
.Modal {
width: calc(100% - 154px);
width: calc(100% - 150px);
max-width: 1360px;
}

.Modal_Align-top {
margin-top: 64px;
}

.ModalInner {
border-radius: 24px;
}

.ModalTitle {
padding: 60px 64px 0;
font-size: 32px;
Expand All @@ -138,9 +184,13 @@ html:has(.Modal) {
padding: 24px 32px;
}

.ModalInner {
border-radius: 28px;
}

.ModalClose {
right: -8px;
padding: 16px;
right: -12px;
}

.ModalClose svg {
Expand All @@ -149,12 +199,6 @@ html:has(.Modal) {
}
}

@media screen and (min-width: 991px) and (min-height: 800px) {
.Modal {
max-height: calc(100vh - 128px);
}
}

@keyframes fadeIn {
from {
opacity: 0;
Expand Down
46 changes: 27 additions & 19 deletions client/components/UI/Modal/Modal.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
import React, { useEffect, useRef } from 'react';
import classNames from 'classnames/bind';

import { CardPosition, useSwipeableCard } from 'hooks/useSwipeableCard';
import t from 'utils/typograph';

import { ModalProps } from './Modal.types';

import Close from 'public/icons/close.svg';
Expand All @@ -21,43 +20,52 @@ export function Modal({
const ref = useRef<HTMLDialogElement>(null);
const refInner = useRef<HTMLDivElement>(null);

const mobilePosition = align === 'center'
? CardPosition.HalfOpen
: CardPosition.FullOpen;

const [currentPosition, onDragEnd, onDrag] = useSwipeableCard(mobilePosition);

const close = () => {
ref.current.close();
};

useEffect(() => {
ref.current.showModal();
refInner.current.scrollTo(0, 0);
// Remove focus after open
(document.activeElement as HTMLElement).blur();

const handleClickOutside = (e) => {
if (ref.current === e.target) {
close();
}
const handleClickOutside = (e) => {
if (e.target === ref.current) {
close();
}
};

document.addEventListener('click', handleClickOutside)

return () => {
document.removeEventListener('click', handleClickOutside)
useEffect(() => {
if (refInner.current) {
refInner.current.scrollTo(0, 0);
}
ref.current.showModal();
(document.activeElement as HTMLElement).blur();
}, []);


return (
<dialog
className={cn(styles.Modal, { [`${styles[`Modal_Align-${align}`]}`]: align })}
className={cn(styles.Modal, {
[`${styles[`Modal_Align-${align}`]}`]: align,
[`${styles[`Modal_Position-${currentPosition}`]}`]: currentPosition,
})}
style={{ maxWidth }}
onClick={handleClickOutside}
onClose={onClose}
ref={ref}
>
<div className={cn(styles.ModalInner)} ref={refInner}>
<div
className={cn(styles.ModalDragArea)}
onTouchMoveCapture={onDrag}
onTouchEndCapture={onDragEnd}
/>
{title && <h1 className={cn(styles.ModalTitle)}>{t(title)}</h1>}
<div className={cn(styles.ModalContent)}>{children}</div>
</div>

<button className={cn(styles.ModalClose)} onClick={close} aria-label="Закрыть">
<button className={cn(styles.ModalClose)} onClick={() => close()} aria-label="Закрыть">
<Close />
</button>
</dialog>
Expand Down
8 changes: 4 additions & 4 deletions client/components/UI/Sidepage/Sidepage.module.css
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@
float: right;
margin-bottom: -100%;
top: 0;
padding: 12px;
padding: 0;
border: 0;
background: none;
cursor: pointer;
Expand All @@ -61,14 +61,14 @@
background-color: rgba(255, 255, 255, 0.6);
backdrop-filter: blur(8px);
color: rgba(0, 0, 0, 0.5);
padding: 10px;
padding: 20px;
border-radius: 50%;
transition: .15s ease;
}

.SidepageCloseButtonWrapper svg {
width: 18px;
height: 18px;
width: 16px;
height: 16px;
}

@media screen and (min-width: 768px) {
Expand Down
4 changes: 2 additions & 2 deletions client/components/UI/Sidepage/Sidepage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import React, { useRef } from 'react';
import classNames from 'classnames/bind';

import { sidebarService } from 'services/sidebar/sidebar';
import { SidepagePosition, useSidepage } from 'hooks/useSidepage/useSidepage';
import { CardPosition, useSwipeableCard } from 'hooks/useSwipeableCard';
import { useDisablePropagation } from 'hooks/useDisablePropagation';

import Close from 'public/icons/close.svg';
Expand All @@ -16,7 +16,7 @@ export function Sidepage({ children }: React.PropsWithChildren) {

useDisablePropagation(ref);

const [currentPosition, onDragEnd, onDrag] = useSidepage(SidepagePosition.HalfOpen);
const [currentPosition, onDragEnd, onDrag] = useSwipeableCard(CardPosition.HalfOpen);

return (
<div
Expand Down
Loading

0 comments on commit 25bad25

Please sign in to comment.