diff --git a/package-lock.json b/package-lock.json index 18f9956..f0bf095 100644 --- a/package-lock.json +++ b/package-lock.json @@ -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", diff --git a/package.json b/package.json index 9b9c711..cf9094f 100644 --- a/package.json +++ b/package.json @@ -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", diff --git a/src/Components/Modals/Modal/Modal.tsx b/src/Components/Modals/Modal/Modal.tsx index 37c3fa5..bb8b994 100644 --- a/src/Components/Modals/Modal/Modal.tsx +++ b/src/Components/Modals/Modal/Modal.tsx @@ -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 ( - -
-
- {children} -
-
- ) + const dispatch = useAppDispatch() + + return dispatch(removeClosedModal(props.id))} + contentElement={(_props, children) =>
+
+ {children} +
} + > + {children} +
} + diff --git a/src/Components/Modals/ModalsContainer/ModalsContainer.tsx b/src/Components/Modals/ModalsContainer/ModalsContainer.tsx index 7784554..0186017 100644 --- a/src/Components/Modals/ModalsContainer/ModalsContainer.tsx +++ b/src/Components/Modals/ModalsContainer/ModalsContainer.tsx @@ -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 ( - +
+ {openModals.map((modal, idx) => { + const Child = ALL_MODALS[modal.Modal]; - - {isOpen && - - - {openModals.map((modal, idx) => { - const Child = ALL_MODALS[modal.Modal]; - - return ( - - - ) - })} - - } - - + + ) + })} +
) } diff --git a/src/features/Projects/pages/ProjectPage/ProjectDetailsCard/ProjectDetailsCard.stories.tsx b/src/features/Projects/pages/ProjectPage/ProjectDetailsCard/ProjectDetailsCard.stories.tsx index ea989f3..5b9d118 100644 --- a/src/features/Projects/pages/ProjectPage/ProjectDetailsCard/ProjectDetailsCard.stories.tsx +++ b/src/features/Projects/pages/ProjectPage/ProjectDetailsCard/ProjectDetailsCard.stories.tsx @@ -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; const Template: ComponentStory = (args) => ; export const Default = Template.bind({}); Default.args = { - + projectId: 1, } diff --git a/src/features/Projects/pages/ProjectPage/ProjectDetailsCard/ProjectDetailsCard.tsx b/src/features/Projects/pages/ProjectPage/ProjectDetailsCard/ProjectDetailsCard.tsx index 92ecda4..1040098 100644 --- a/src/features/Projects/pages/ProjectPage/ProjectDetailsCard/ProjectDetailsCard.tsx +++ b/src/features/Projects/pages/ProjectPage/ProjectDetailsCard/ProjectDetailsCard.tsx @@ -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 } -

{ }, ...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(); diff --git a/src/redux/features/modals.slice.ts b/src/redux/features/modals.slice.ts index a2f56b0..a4657b7 100644 --- a/src/redux/features/modals.slice.ts +++ b/src/redux/features/modals.slice.ts @@ -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 = -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) { 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 ) { 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) { + state.openModals = state.openModals.filter(m => m.id !== action.payload) + + } }, }); @@ -156,6 +170,7 @@ export const { scheduleModal, openSceduledModal, removeScheduledModal, + removeClosedModal } = modalSlice.actions; diff --git a/src/styles/index.scss b/src/styles/index.scss index e9040a5..c6c8d33 100644 --- a/src/styles/index.scss +++ b/src/styles/index.scss @@ -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; +} \ No newline at end of file diff --git a/src/utils/helperFunctions.tsx b/src/utils/helperFunctions.tsx index c81ac69..7af4a09 100644 --- a/src/utils/helperFunctions.tsx +++ b/src/utils/helperFunctions.tsx @@ -57,4 +57,9 @@ export function lazyModal> 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(); } \ No newline at end of file diff --git a/src/utils/hooks/usePressHolder.ts b/src/utils/hooks/usePressHolder.ts index cd4da2e..74fbc43 100644 --- a/src/utils/hooks/usePressHolder.ts +++ b/src/utils/hooks/usePressHolder.ts @@ -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; diff --git a/src/utils/storybook/decorators.tsx b/src/utils/storybook/decorators.tsx index 46ab9fc..879cdab 100644 --- a/src/utils/storybook/decorators.tsx +++ b/src/utils/storybook/decorators.tsx @@ -74,7 +74,7 @@ export const AppDecorator: DecoratorFn = (Component) => { return } -export const wrapModal: DecoratorFn = (Component) => { }}> +export const wrapModal: DecoratorFn = (Component) => { }}> export const wrapPage: DecoratorFn = (Component) =>

@@ -83,22 +83,27 @@ export const wrapPage: DecoratorFn = (Component) =>
+ + + ) + // return ( + // + // + // + // + // + // + // + // ); } export const centerDecorator: DecoratorFn = (Story) => {