chore: change Modal Component to a new accessible one, fix usePressHolder bug, fix ProjectDetailsCard body breaklines

This commit is contained in:
MTG2000
2022-05-05 10:57:36 +03:00
parent 0188825b5b
commit fff2dda9a4
12 changed files with 201 additions and 92 deletions

69
package-lock.json generated
View File

@@ -52,6 +52,7 @@
"react-icons": "^4.3.1",
"react-loader-spinner": "^6.0.0-0",
"react-loading-skeleton": "^3.1.0",
"react-modal": "^3.15.1",
"react-multi-carousel": "^2.8.0",
"react-query": "^3.35.0",
"react-redux": "^8.0.0",
@@ -90,6 +91,7 @@
"@types/marked": "^4.0.3",
"@types/react-copy-to-clipboard": "^5.0.2",
"@types/react-datepicker": "^4.4.0",
"@types/react-modal": "^3.13.1",
"autoprefixer": "^10.4.4",
"gh-pages": "^3.2.3",
"msw": "^0.39.2",
@@ -15562,6 +15564,15 @@
"@types/react": "*"
}
},
"node_modules/@types/react-modal": {
"version": "3.13.1",
"resolved": "https://registry.npmjs.org/@types/react-modal/-/react-modal-3.13.1.tgz",
"integrity": "sha512-iY/gPvTDIy6Z+37l+ibmrY+GTV4KQTHcCyR5FIytm182RQS69G5ps4PH2FxtC7bAQ2QRHXMevsBgck7IQruHNg==",
"dev": true,
"dependencies": {
"@types/react": "*"
}
},
"node_modules/@types/react-syntax-highlighter": {
"version": "11.0.5",
"resolved": "https://registry.npmjs.org/@types/react-syntax-highlighter/-/react-syntax-highlighter-11.0.5.tgz",
@@ -22597,6 +22608,11 @@
"url": "https://github.com/sindresorhus/execa?sponsor=1"
}
},
"node_modules/exenv": {
"version": "1.2.2",
"resolved": "https://registry.npmjs.org/exenv/-/exenv-1.2.2.tgz",
"integrity": "sha1-KueOhdmJQVhnCwPUe+wfA72Ru50="
},
"node_modules/exit": {
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz",
@@ -58942,6 +58958,11 @@
"resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz",
"integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w=="
},
"node_modules/react-lifecycles-compat": {
"version": "3.0.4",
"resolved": "https://registry.npmjs.org/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz",
"integrity": "sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA=="
},
"node_modules/react-loader-spinner": {
"version": "6.0.0-0",
"resolved": "https://registry.npmjs.org/react-loader-spinner/-/react-loader-spinner-6.0.0-0.tgz",
@@ -58970,6 +58991,24 @@
"react": ">=16.8.0"
}
},
"node_modules/react-modal": {
"version": "3.15.1",
"resolved": "https://registry.npmjs.org/react-modal/-/react-modal-3.15.1.tgz",
"integrity": "sha512-duB9bxOaYg7Zt6TMFldIFxQRtSP+Dg3F1ZX3FXxSUn+3tZZ/9JCgeAQKDg7rhZSAqopq8TFRw3yIbnx77gyFTw==",
"dependencies": {
"exenv": "^1.2.0",
"prop-types": "^15.7.2",
"react-lifecycles-compat": "^3.0.0",
"warning": "^4.0.3"
},
"engines": {
"node": ">=8"
},
"peerDependencies": {
"react": "^0.14.0 || ^15.0.0 || ^16 || ^17 || ^18",
"react-dom": "^0.14.0 || ^15.0.0 || ^16 || ^17 || ^18"
}
},
"node_modules/react-multi-carousel": {
"version": "2.8.0",
"resolved": "https://registry.npmjs.org/react-multi-carousel/-/react-multi-carousel-2.8.0.tgz",
@@ -78131,6 +78170,15 @@
"@types/react": "*"
}
},
"@types/react-modal": {
"version": "3.13.1",
"resolved": "https://registry.npmjs.org/@types/react-modal/-/react-modal-3.13.1.tgz",
"integrity": "sha512-iY/gPvTDIy6Z+37l+ibmrY+GTV4KQTHcCyR5FIytm182RQS69G5ps4PH2FxtC7bAQ2QRHXMevsBgck7IQruHNg==",
"dev": true,
"requires": {
"@types/react": "*"
}
},
"@types/react-syntax-highlighter": {
"version": "11.0.5",
"resolved": "https://registry.npmjs.org/@types/react-syntax-highlighter/-/react-syntax-highlighter-11.0.5.tgz",
@@ -83640,6 +83688,11 @@
"strip-final-newline": "^2.0.0"
}
},
"exenv": {
"version": "1.2.2",
"resolved": "https://registry.npmjs.org/exenv/-/exenv-1.2.2.tgz",
"integrity": "sha1-KueOhdmJQVhnCwPUe+wfA72Ru50="
},
"exit": {
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz",
@@ -111132,6 +111185,11 @@
"resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz",
"integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w=="
},
"react-lifecycles-compat": {
"version": "3.0.4",
"resolved": "https://registry.npmjs.org/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz",
"integrity": "sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA=="
},
"react-loader-spinner": {
"version": "6.0.0-0",
"resolved": "https://registry.npmjs.org/react-loader-spinner/-/react-loader-spinner-6.0.0-0.tgz",
@@ -111150,6 +111208,17 @@
"integrity": "sha512-j1U1CWWs68nBPOg7tkQqnlFcAMFF6oEK6MgqAo15f8A5p7mjH6xyKn2gHbkcimpwfO0VQXqxAswnSYVr8lWzjw==",
"requires": {}
},
"react-modal": {
"version": "3.15.1",
"resolved": "https://registry.npmjs.org/react-modal/-/react-modal-3.15.1.tgz",
"integrity": "sha512-duB9bxOaYg7Zt6TMFldIFxQRtSP+Dg3F1ZX3FXxSUn+3tZZ/9JCgeAQKDg7rhZSAqopq8TFRw3yIbnx77gyFTw==",
"requires": {
"exenv": "^1.2.0",
"prop-types": "^15.7.2",
"react-lifecycles-compat": "^3.0.0",
"warning": "^4.0.3"
}
},
"react-multi-carousel": {
"version": "2.8.0",
"resolved": "https://registry.npmjs.org/react-multi-carousel/-/react-multi-carousel-2.8.0.tgz",

View File

@@ -47,6 +47,7 @@
"react-icons": "^4.3.1",
"react-loader-spinner": "^6.0.0-0",
"react-loading-skeleton": "^3.1.0",
"react-modal": "^3.15.1",
"react-multi-carousel": "^2.8.0",
"react-query": "^3.35.0",
"react-redux": "^8.0.0",
@@ -141,6 +142,7 @@
"@types/marked": "^4.0.3",
"@types/react-copy-to-clipboard": "^5.0.2",
"@types/react-datepicker": "^4.4.0",
"@types/react-modal": "^3.13.1",
"autoprefixer": "^10.4.4",
"gh-pages": "^3.2.3",
"msw": "^0.39.2",

View File

@@ -1,34 +1,40 @@
import { motion } from "framer-motion";
import { ReactElement } from "react";
import ReactModal from 'react-modal';
import { removeClosedModal } from "src/redux/features/modals.slice";
import { useAppDispatch } from 'src/utils/hooks'
interface Props {
onClose: () => void;
id: string,
isOpen: boolean;
isPageModal?: boolean;
children: ReactElement
onClose: () => void;
[key: string]: any;
}
ReactModal.setAppElement('#root');
export default function Modal({ onClose, children, ...props }: Props) {
return (
<motion.div
initial={false}
animate={{ opacity: 1 }}
exit={{ opacity: 0 }}
className='fixed w-full h-full items-center overflow-x-hidden no-scrollbar'
{...props}
>
<div
className='w-screen min-h-screen relative flex flex-col justify-center items-center md:py-64 md:px-16 overflow-x-hidden no-scrollbar'
>
<div
className={`absolute w-full h-full top-0 left-0 bg-gray-300 bg-opacity-50 ${props.isPageModal && "hidden md:block"}`}
onClick={onClose}
></div>
{children}
</div>
</motion.div>
)
const dispatch = useAppDispatch()
return <ReactModal
isOpen={props.isOpen}
onRequestClose={onClose}
overlayClassName='fixed w-full inset-0 overflow-x-hidden z-[2020]'
className=' '
closeTimeoutMS={1000}
onAfterClose={() => dispatch(removeClosedModal(props.id))}
contentElement={(_props, children) => <div {..._props} className={`${_props.className} w-screen min-h-screen relative flex flex-col justify-center items-center md:py-64 md:px-16 inset-0`}>
<div
onClick={onClose}
className={`absolute w-full h-full top-0 left-0 bg-gray-300 bg-opacity-50 ${props.isPageModal && "hidden md:block"}`}
></div>
{children}
</div>}
>
{children}
</ReactModal>
}

View File

@@ -1,9 +1,7 @@
import { AnimatePresence, motion } from "framer-motion";
import { useEffect } from "react";
import { ALL_MODALS, closeModal, Direction, removeScheduledModal } from "src/redux/features/modals.slice";
import { useAppDispatch, useAppSelector } from "src/utils/hooks";
import Modal from "../Modal/Modal";
import { Portal } from "../../Portal/Portal";
export interface ModalCard {
onClose?: () => void;
@@ -52,36 +50,29 @@ export default function ModalsContainer() {
}
useEffect(() => {
if (isOpen) document.body.style.overflowY = "hidden";
else document.body.style.overflowY = "initial";
if (isOpen)
document.body.style.overflowY = "hidden";
else
document.body.style.overflowY = "initial";
}, [isOpen]);
return (
<Portal className="modals">
<div className="z-[2020]">
{openModals.map((modal, idx) => {
const Child = ALL_MODALS[modal.Modal];
<AnimatePresence exitBeforeEnter>
{isOpen &&
<motion.div
className="w-screen fixed inset-0 overflow-x-hidden z-[2020]"
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
exit={{
opacity: 0,
transition: { ease: "easeInOut" },
}}
return (
<Modal
key={idx}
isOpen={modal.isOpen}
onClose={onClose}
direction={direction}
id={modal.id}
isPageModal={modal.props?.isPageModal}
>
<AnimatePresence>
{openModals.map((modal, idx) => {
const Child = ALL_MODALS[modal.Modal];
return (
<Modal key={idx} onClose={onClose} direction={direction} isPageModal={modal.props?.isPageModal}>
<Child onClose={onClose} direction={direction} isPageModal={modal.props?.isPageModal} {...modal.props} />
</Modal>)
})}
</AnimatePresence>
</motion.div>}
</AnimatePresence>
</Portal>
<Child onClose={onClose} direction={direction} isPageModal={modal.props?.isPageModal} {...modal.props} />
</Modal>)
})}
</div>
)
}

View File

@@ -1,5 +1,4 @@
import { ComponentStory, ComponentMeta } from '@storybook/react';
import { ModifyArgs } from 'src/utils/storybook/utils'
import ProjectDetailsCard from './ProjectDetailsCard';
import ProjectDetailsCardSkeleton from './ProjectDetailsCard.Skeleton';
import { ModalsDecorator } from 'src/utils/storybook/decorators';
@@ -8,22 +7,13 @@ export default {
title: 'Projects/Project Page/Project Details Modal',
component: ProjectDetailsCard,
decorators: [ModalsDecorator],
parameters: {
modifyArgs: {
store: {
project: {
openId: 1
}
}
} as ModifyArgs
}
} as ComponentMeta<typeof ProjectDetailsCard>;
const Template: ComponentStory<typeof ProjectDetailsCard> = (args) => <ProjectDetailsCard {...args} />;
export const Default = Template.bind({});
Default.args = {
projectId: 1,
}

View File

@@ -36,9 +36,11 @@ export default function ProjectDetailsCard({ direction, projectId, ...props }: P
const { loading, error } = useProjectDetailsQuery({
variables: { projectId: projectId! },
onCompleted: data => {
dispatch(setProject(data.getProject))
},
onError: () => {
dispatch(setProject(null));
},
skip: !Boolean(projectId)
});
@@ -48,7 +50,6 @@ export default function ProjectDetailsCard({ direction, projectId, ...props }: P
}, [dispatch]);
const closeModal = () => {
dispatch(setProject(null));
props.onClose?.();
}
@@ -126,7 +127,7 @@ export default function ProjectDetailsCard({ direction, projectId, ...props }: P
}
</div>
</div>
<p className="mt-40 text-body4 leading-normal" dangerouslySetInnerHTML={{
<p className="mt-40 text-body4 leading-normal whitespace-pre-line" dangerouslySetInnerHTML={{
__html: linkifyHtml(project?.description, {
className: ' text-blue-500 underline',
defaultProtocol: 'https',

View File

@@ -60,6 +60,7 @@ export default function VoteButton({ onVote = () => { }, ...props }: Props) {
}, 100), 100);
const handlePressDown = () => {
console.log('HANDLE PRESS DOWN');
setWasActive(true);
onPressDown();
}
@@ -67,6 +68,8 @@ export default function VoteButton({ onVote = () => { }, ...props }: Props) {
const handlePressUp = (event?: any) => {
if (!wasActive) return;
console.log('HANDLE PRESS UP');
setWasActive(false);
if (event?.preventDefault) event.preventDefault();
onPressUp();

View File

@@ -6,6 +6,7 @@ import InsertImageModal from 'src/Components/Inputs/TextEditor/InsertImageModal/
import { Claim_FundWithdrawCard, Claim_CopySignatureCard, Claim_GenerateSignatureCard, Claim_SubmittedCard } from "src/features/Projects/pages/ProjectPage/ClaimProject";
import { ModalCard } from "src/Components/Modals/ModalsContainer/ModalsContainer";
import { ComponentProps } from "react";
import { generateId } from "src/utils/helperFunctions";
export enum Direction {
START,
@@ -48,9 +49,11 @@ type ModalAction<U extends keyof typeof ALL_MODALS = keyof typeof ALL_MODALS> =
interface OpenModal {
interface ModalObject {
id: string
Modal: ModalAction['Modal'],
props?: any;
isOpen: boolean
}
interface StoreState {
@@ -58,8 +61,8 @@ interface StoreState {
isLoading: boolean;
direction: Direction;
flows: keyof typeof ALL_MODALS[];
toOpenLater: OpenModal | null;
openModals: OpenModal[];
toOpenLater: ModalObject | null;
openModals: ModalObject[];
isMobileScreen?: boolean;
}
@@ -69,7 +72,7 @@ const initialState = {
direction: Direction.START,
flows: [] as any,
toOpenLater: null,
openModals: [] as OpenModal[],
openModals: [] as ModalObject[],
} as StoreState;
export const modalSlice = createSlice({
@@ -82,7 +85,9 @@ export const modalSlice = createSlice({
scheduleModal(state, action: PayloadAction<ModalAction>) {
state.toOpenLater = {
id: generateId(),
Modal: action.payload.Modal,
isOpen: false,
};
},
@@ -90,7 +95,7 @@ export const modalSlice = createSlice({
if (state.toOpenLater) {
state.direction = Direction.START;
state.isOpen = true;
state.openModals.push(state.toOpenLater);
state.openModals.push({ ...state.toOpenLater, isOpen: true });
state.toOpenLater = null;
}
},
@@ -114,8 +119,10 @@ export const modalSlice = createSlice({
state.openModals.push({
id: generateId(),
Modal: action.payload.Modal,
props
props,
isOpen: true
});
},
@@ -124,7 +131,7 @@ export const modalSlice = createSlice({
action: PayloadAction<ModalAction & { direction: Direction }>
) {
state.direction = action.payload.direction;
state.openModals.pop();
state.openModals[state.openModals.length - 1].isOpen = false;
let props: any = {};
@@ -133,16 +140,23 @@ export const modalSlice = createSlice({
props = { ...props, ...action.payload.props }
state.openModals.push({
id: generateId(),
Modal: action.payload.Modal,
props,
isOpen: true,
});
},
closeModal(state) {
state.direction = Direction.EXIT;
state.openModals.pop();
state.isOpen = Boolean(state.openModals.length);
state.openModals[state.openModals.length - 1].isOpen = false;
state.isOpen = Boolean(state.openModals.filter(modal => modal.isOpen).length);
},
removeClosedModal(state, action: PayloadAction<string>) {
state.openModals = state.openModals.filter(m => m.id !== action.payload)
}
},
});
@@ -156,6 +170,7 @@ export const {
scheduleModal,
openSceduledModal,
removeScheduledModal,
removeClosedModal
} = modalSlice.actions;

View File

@@ -2,6 +2,11 @@
@import './tw.scss',"./shared.scss",'./vendors.scss';
html{
width: 100vw;
}
body {
overflow-x: hidden;
/* background-color: #F8FAFC; */
@@ -62,13 +67,29 @@ svg {
/* Firefox */
}
.no-scrollbar ::-webkit-scrollbar {
.no-scrollbar::-webkit-scrollbar {
display: none;
/* Safari and Chrome */
}
@media (pointer: coarse) {
.touch-device\:hidden {
display: none;
}
}
.ReactModal__Overlay {
opacity: 0;
transition: opacity 900ms ease-in-out;
}
.ReactModal__Overlay--after-open {
opacity: 1;
}
.ReactModal__Overlay--before-close {
opacity: 0;
transition-timing-function: ease-in;
transition-duration: 400ms;
}

View File

@@ -57,4 +57,9 @@ export function lazyModal<T extends ComponentType<any>>
export function trimText(text: string, length: number) {
return text.slice(0, length) + (text.length > length ? "..." : "")
}
export function generateId() {
// TODO: Change to proper generator
return Math.random().toString();
}

View File

@@ -9,11 +9,12 @@ export const usePressHolder = (onHold: () => any, holdThreshold: number = 400) =
});
const onPressDown = () => {
requestAnimationFrame(timer)
ref.current.timerID = requestAnimationFrame(timer)
}
const onPressUp = () => {
console.log('ON PRESS UP');
cancelAnimationFrame(ref.current.timerID);
ref.current.cntr = 0;

View File

@@ -74,7 +74,7 @@ export const AppDecorator: DecoratorFn = (Component) => {
return <Component />
}
export const wrapModal: DecoratorFn = (Component) => <Modal isOpen onClose={() => { }}><Component /></Modal>
export const wrapModal: DecoratorFn = (Component) => <Modal isOpen id='some-id' onClose={() => { }}><Component /></Modal>
export const wrapPage: DecoratorFn = (Component) => <div className='page-container'><Component /></div>
@@ -83,22 +83,27 @@ export const wrapPage: DecoratorFn = (Component) => <div className='page-contain
export const ModalsDecorator: DecoratorFn = (Story) => {
const onClose = () => { };
return (
<motion.div
className="w-screen fixed inset-0 overflow-x-hidden z-[2020]"
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
exit={{
opacity: 0,
transition: { ease: "easeInOut" },
}}
>
<AnimatePresence>
<Modal onClose={onClose} >
<Story onClose={onClose} />
</Modal>
</AnimatePresence>
</motion.div>
);
<Modal isOpen id={'some-id'} onClose={onClose} >
<Story onClose={onClose} />
</Modal>
)
// return (
// <motion.div
// className="w-screen fixed inset-0 overflow-x-hidden z-[2020]"
// initial={{ opacity: 0 }}
// animate={{ opacity: 1 }}
// exit={{
// opacity: 0,
// transition: { ease: "easeInOut" },
// }}
// >
// <AnimatePresence>
// <Modal onClose={onClose} >
// <Story onClose={onClose} />
// </Modal>
// </AnimatePresence>
// </motion.div>
// );
}
export const centerDecorator: DecoratorFn = (Story) => {