mirror of
https://github.com/storytold/artcraft.git
synced 2026-11-04 17:38:10 +00:00
Merge pull request #642 from storytold/further-modal-improvements
added modal click outside to close, consolidated modal context
This commit is contained in:
12 files changed
+156
-162
No files matched your search
+98
-44
@@ -6,80 +6,134 @@ import useLogin from "./useLogin";
|
||||
import useSignup from "./useSignup";
|
||||
import LoginView from "./LoginView";
|
||||
import SignupView from "./SignupView";
|
||||
import { ModalView } from "context/AccountModalContext";
|
||||
import { Analytics } from "common/Analytics";
|
||||
import "./AccountModal.scss";
|
||||
|
||||
interface AniProps {
|
||||
animating: boolean,
|
||||
isLeaving: boolean,
|
||||
render: any,
|
||||
style: any
|
||||
animating: boolean;
|
||||
isLeaving: boolean;
|
||||
render: any;
|
||||
style: any;
|
||||
}
|
||||
|
||||
const AniMod = ({ animating, isLeaving, render: Render, style, ...rest }: AniProps) => <a.div {...{ style: {
|
||||
...style,
|
||||
position: isLeaving && animating ? "absolute" : "relative"
|
||||
} }}>
|
||||
export enum AccountModalView {
|
||||
Closed,
|
||||
Signup,
|
||||
Login,
|
||||
}
|
||||
|
||||
const AniMod = ({
|
||||
animating,
|
||||
isLeaving,
|
||||
render: Render,
|
||||
style,
|
||||
...rest
|
||||
}: AniProps) => (
|
||||
<a.div
|
||||
{...{
|
||||
style: {
|
||||
...style,
|
||||
position: isLeaving && animating ? "absolute" : "relative",
|
||||
},
|
||||
}}
|
||||
>
|
||||
<Render {...{ ...rest, animating }} />
|
||||
</a.div>;
|
||||
</a.div>
|
||||
);
|
||||
|
||||
const Loader = ({ viewLogin }: { viewLogin: boolean }) => <div {...{ className: `fy-modal-page modal-spinner` }}>
|
||||
<h3> {
|
||||
viewLogin ? "Logging you in" : "Creating your account"
|
||||
} </h3>
|
||||
<Spinner />
|
||||
</div>;
|
||||
const Loader = ({ viewLogin }: { viewLogin: boolean }) => (
|
||||
<div {...{ className: `fy-modal-page modal-spinner` }}>
|
||||
<h3> {viewLogin ? "Logging you in" : "Creating your account"} </h3>
|
||||
<Spinner />
|
||||
</div>
|
||||
);
|
||||
|
||||
export default function LoginModal({ handleClose, view, viewSwitch }: { handleClose: any, view: ModalView, viewSwitch: () => void }) {
|
||||
const viewLogin = view === ModalView.Login;
|
||||
const [status,statusSet] = useState(FetchStatus.paused);
|
||||
export default function LoginModal({
|
||||
handleClose,
|
||||
view,
|
||||
viewSwitch,
|
||||
}: {
|
||||
handleClose: any;
|
||||
view: AccountModalView;
|
||||
viewSwitch: () => void;
|
||||
}) {
|
||||
const viewLogin = view === AccountModalView.Login;
|
||||
const [status, statusSet] = useState(FetchStatus.paused);
|
||||
// const [viewLogin,viewLoginSet] = useState(false);
|
||||
const [animating,animatingSet] = useState(false);
|
||||
const { errorType, setProps: loginProps, login } = useLogin({
|
||||
const [animating, animatingSet] = useState(false);
|
||||
const {
|
||||
errorType,
|
||||
setProps: loginProps,
|
||||
login,
|
||||
} = useLogin({
|
||||
onSuccess: () => {
|
||||
Analytics.accountLoginSuccess();
|
||||
handleClose();
|
||||
handleClose();
|
||||
},
|
||||
status,
|
||||
statusSet
|
||||
statusSet,
|
||||
});
|
||||
const { setProps: signupProps, signup } = useSignup({
|
||||
onSuccess: () => {
|
||||
Analytics.accountSignupAttempt();
|
||||
handleClose();
|
||||
handleClose();
|
||||
},
|
||||
status,
|
||||
statusSet
|
||||
statusSet,
|
||||
});
|
||||
|
||||
const index = status === FetchStatus.in_progress ? 2 : viewLogin ? 1: 0;
|
||||
const index = status === FetchStatus.in_progress ? 2 : viewLogin ? 1 : 0;
|
||||
|
||||
const amt = 5;
|
||||
|
||||
const transitions = useTransition(index, {
|
||||
config: { mass: 1, tension: 80, friction: 10 },
|
||||
from: { opacity: 0, transform: `translateX(${ viewLogin ? amt : -amt }rem)` },
|
||||
from: { opacity: 0, transform: `translateX(${viewLogin ? amt : -amt}rem)` },
|
||||
enter: { opacity: 1, transform: `translateX(0)` },
|
||||
leave: { opacity: 0, transform: `translateX(${ viewLogin ? -amt : amt }rem)` },
|
||||
leave: {
|
||||
opacity: 0,
|
||||
transform: `translateX(${viewLogin ? -amt : amt}rem)`,
|
||||
},
|
||||
onRest: () => animatingSet(false),
|
||||
onStart: () => animatingSet(true)
|
||||
});
|
||||
onStart: () => animatingSet(true),
|
||||
});
|
||||
|
||||
return <div {...{ className: "fy-login-modal" }}>
|
||||
{
|
||||
transitions((style: any, i: number, state: any) => {
|
||||
return (
|
||||
<div {...{ className: "fy-login-modal" }}>
|
||||
{transitions((style: any, i: number, state: any) => {
|
||||
let isLeaving = state.phase === "leave";
|
||||
let sharedProps = { animating, handleClose, isLeaving, style, viewLogin, viewSwitch };
|
||||
let sharedProps = {
|
||||
animating,
|
||||
handleClose,
|
||||
isLeaving,
|
||||
style,
|
||||
viewLogin,
|
||||
viewSwitch,
|
||||
};
|
||||
|
||||
switch(i) {
|
||||
case 0: return <AniMod {...{ render: SignupView, signupProps, signup, ...sharedProps }}/>;
|
||||
case 1: return <AniMod {...{ errorType, login, loginProps, render: LoginView, ...sharedProps }}/>;
|
||||
case 2: return <AniMod {...{ render: Loader, ...sharedProps }}/>;
|
||||
switch (i) {
|
||||
case 0:
|
||||
return (
|
||||
<AniMod
|
||||
{...{ render: SignupView, signupProps, signup, ...sharedProps }}
|
||||
/>
|
||||
);
|
||||
case 1:
|
||||
return (
|
||||
<AniMod
|
||||
{...{
|
||||
errorType,
|
||||
login,
|
||||
loginProps,
|
||||
render: LoginView,
|
||||
...sharedProps,
|
||||
}}
|
||||
/>
|
||||
);
|
||||
case 2:
|
||||
return <AniMod {...{ render: Loader, ...sharedProps }} />;
|
||||
}
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
</div>;
|
||||
};
|
||||
})}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
-41
@@ -1,41 +0,0 @@
|
||||
import React, { useState } from 'react';
|
||||
import { AccountModalContext } from 'context';
|
||||
import { useSession } from "hooks";
|
||||
import { Modal } from "components/common";
|
||||
import AccountModal from "components/layout/AccountModal";
|
||||
import { ModalView } from "context/AccountModalContext";
|
||||
|
||||
interface Props {
|
||||
children?: any;
|
||||
}
|
||||
|
||||
export default function AccountModalProvider({ children }: Props) {
|
||||
const { user } = useSession();
|
||||
const [view,viewSet] = useState(ModalView.Closed);
|
||||
const open = () => viewSet(ModalView.Signup);
|
||||
const close = () => { viewSet(ModalView.Closed); console.log("🍏",);};
|
||||
const viewSwitch = () => view === ModalView.Signup ? viewSet(ModalView.Login) : viewSet(ModalView.Signup);
|
||||
const sessionCheck = () => {
|
||||
if (user) {
|
||||
return true;
|
||||
} else {
|
||||
open();
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
return <AccountModalContext.Provider {...{ value: { view, close, open, sessionCheck } }}>
|
||||
{children}
|
||||
{
|
||||
<Modal {...{
|
||||
content: AccountModal,
|
||||
contentProps: { view, viewSwitch },
|
||||
handleClose: close,
|
||||
noHeader: true,
|
||||
show: view > 0,
|
||||
showButtons: false,
|
||||
// title: "You need to login",
|
||||
}}/>
|
||||
}
|
||||
</AccountModalContext.Provider>;
|
||||
};
|
||||
-1
@@ -1 +0,0 @@
|
||||
export { default } from "./AccountModalProvider";
|
||||
+1
@@ -19,6 +19,7 @@
|
||||
.fy-modal-body {
|
||||
width: 100%;
|
||||
height: fit-content;
|
||||
overflow: hidden;
|
||||
background-color: $panel;
|
||||
border-color: lighten($panel, 8%) !important;
|
||||
border: 1px solid;
|
||||
|
||||
+25
-6
@@ -5,8 +5,9 @@ import "./ModalLayer.scss";
|
||||
interface Props {
|
||||
content?: React.ElementType | null;
|
||||
contentProps?: any;
|
||||
handleClose: () => void;
|
||||
close: () => void;
|
||||
killModal: boolean;
|
||||
lockTint?: boolean;
|
||||
modalOpen: boolean;
|
||||
onModalCloseEnd: (x: any) => void;
|
||||
}
|
||||
@@ -14,10 +15,12 @@ interface Props {
|
||||
export default function ModalLayer({
|
||||
content: Content,
|
||||
contentProps,
|
||||
handleClose,
|
||||
close,
|
||||
lockTint,
|
||||
modalOpen,
|
||||
onModalCloseEnd,
|
||||
}: Props) {
|
||||
const mainClassName = "fy-modal-layer";
|
||||
const tintTransition = useTransition(modalOpen, {
|
||||
config: {
|
||||
easing: modalOpen ? easings.easeOutQuad : easings.easeInQuad,
|
||||
@@ -30,11 +33,27 @@ export default function ModalLayer({
|
||||
});
|
||||
|
||||
return tintTransition(
|
||||
(tinter, shade) =>
|
||||
shade && (
|
||||
<a.div {...{ className: "fy-modal-layer", style: { ...tinter } }}>
|
||||
(tintStyle, modalIsOpen) =>
|
||||
modalIsOpen && (
|
||||
<a.div
|
||||
{...{
|
||||
className: mainClassName,
|
||||
style: tintStyle,
|
||||
onClick: ({ target }) => {
|
||||
if (
|
||||
!lockTint &&
|
||||
target instanceof HTMLElement &&
|
||||
target.className === mainClassName
|
||||
) {
|
||||
close();
|
||||
}
|
||||
},
|
||||
}}
|
||||
>
|
||||
<div {...{ className: "fy-modal-body" }}>
|
||||
{Content && <Content {...{ ...contentProps, handleClose }} />}
|
||||
{Content && (
|
||||
<Content {...{ ...contentProps, handleClose: close }} />
|
||||
)}
|
||||
</div>
|
||||
</a.div>
|
||||
)
|
||||
|
||||
+25
-12
@@ -1,5 +1,4 @@
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { ModalContext } from "context";
|
||||
import React, { createContext, useEffect, useState } from "react";
|
||||
|
||||
import ModalLayer from "./ModalLayer";
|
||||
import { isMobile } from "react-device-detect";
|
||||
@@ -10,9 +9,24 @@ interface ModalProviderProps {
|
||||
|
||||
export interface ModalConfig {
|
||||
component: React.ElementType;
|
||||
lockTint?: boolean;
|
||||
props?: any;
|
||||
}
|
||||
|
||||
export interface ModalContextShared {
|
||||
close: () => void;
|
||||
modalOpen: boolean;
|
||||
modalState: ModalConfig | null;
|
||||
open: (cfg: ModalConfig) => void;
|
||||
}
|
||||
|
||||
export const ModalContext = createContext<ModalContextShared>({
|
||||
close: () => {},
|
||||
open: () => {},
|
||||
modalOpen: false,
|
||||
modalState: null,
|
||||
});
|
||||
|
||||
// how this works
|
||||
//
|
||||
// When modalState is set via open(), modalOpen is set to true to via useEffect
|
||||
@@ -27,6 +41,7 @@ export default function ModalProvider({ children }: ModalProviderProps) {
|
||||
const [modalState, modalStateSet] = useState<ModalConfig | null>(null);
|
||||
const [modalOpen, modalOpenSet] = useState(false);
|
||||
const [killModal, killModalSet] = useState(false);
|
||||
|
||||
const open = (cfg: ModalConfig) => modalStateSet(cfg);
|
||||
const close = () => {
|
||||
modalOpenSet(false);
|
||||
@@ -40,19 +55,17 @@ export default function ModalProvider({ children }: ModalProviderProps) {
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (!killModal && modalState && !modalOpen) {
|
||||
modalOpenSet(true);
|
||||
}
|
||||
}, [killModal, modalOpen, modalState]);
|
||||
|
||||
// Prevent body scrolling when modal is open on mobile
|
||||
useEffect(() => {
|
||||
// Prevent body scrolling when modal is open on mobile
|
||||
if (modalOpen && isMobile) {
|
||||
document.body.classList.add("overflow-hidden");
|
||||
} else {
|
||||
document.body.classList.remove("overflow-hidden");
|
||||
}
|
||||
}, [modalOpen]);
|
||||
|
||||
if (!killModal && modalState && !modalOpen) {
|
||||
modalOpenSet(true);
|
||||
}
|
||||
}, [killModal, modalOpen, modalState]);
|
||||
|
||||
return (
|
||||
<ModalContext.Provider
|
||||
@@ -63,10 +76,10 @@ export default function ModalProvider({ children }: ModalProviderProps) {
|
||||
{...{
|
||||
content: modalState?.component,
|
||||
contentProps: modalState?.props,
|
||||
handleClose: close,
|
||||
close,
|
||||
killModal,
|
||||
lockTint: modalState?.lockTint,
|
||||
modalOpen,
|
||||
modalState,
|
||||
onModalCloseEnd,
|
||||
}}
|
||||
/>
|
||||
|
||||
@@ -1 +1 @@
|
||||
export { default } from "./ModalProvider";
|
||||
export { default, ModalContext } from "./ModalProvider";
|
||||
@@ -1,18 +1,17 @@
|
||||
import AccountModalProvider from "./AccountModalProvider";
|
||||
import CoreServicesProvider from "./CoreServicesProvider";
|
||||
import InferenceJobsProvider, {
|
||||
InferenceJobsContext,
|
||||
} from "./InferenceJobsProvider";
|
||||
import ModalProvider from "./ModalProvider/ModalProvider";
|
||||
import ModalProvider, { ModalContext } from "./ModalProvider/ModalProvider";
|
||||
import NotificationProvider from "./NotificationProvider";
|
||||
import SessionProvider from "./SessionProvider";
|
||||
|
||||
export {
|
||||
AccountModalProvider,
|
||||
CoreServicesProvider,
|
||||
InferenceJobsContext,
|
||||
InferenceJobsProvider,
|
||||
ModalProvider,
|
||||
ModalContext,
|
||||
NotificationProvider,
|
||||
SessionProvider,
|
||||
};
|
||||
@@ -1,16 +0,0 @@
|
||||
import { createContext } from 'react';
|
||||
|
||||
export enum ModalView {
|
||||
Closed,
|
||||
Signup,
|
||||
Login
|
||||
}
|
||||
|
||||
interface AccountModalContext { close: () => void, open: () => void, sessionCheck: () => boolean, view: ModalView, }
|
||||
|
||||
export default createContext<AccountModalContext>({
|
||||
close: () => {},
|
||||
open: () => {},
|
||||
sessionCheck: () => false,
|
||||
view: ModalView.Closed,
|
||||
});
|
||||
@@ -1,26 +0,0 @@
|
||||
import { createContext } from "react";
|
||||
|
||||
export enum ModalView {
|
||||
Closed,
|
||||
Signup,
|
||||
Login,
|
||||
}
|
||||
|
||||
interface ModalConfig {
|
||||
component: React.ElementType;
|
||||
props?: any;
|
||||
}
|
||||
|
||||
interface AccountModalContext {
|
||||
close: () => void;
|
||||
modalOpen: boolean;
|
||||
modalState: ModalConfig | null;
|
||||
open: (comp: any) => void;
|
||||
}
|
||||
|
||||
export default createContext<AccountModalContext>({
|
||||
close: () => {},
|
||||
open: () => {},
|
||||
modalOpen: false,
|
||||
modalState: null,
|
||||
});
|
||||
@@ -1,13 +1,5 @@
|
||||
import AccountModalContext from "./AccountModalContext";
|
||||
import ModalContext from "./ModalContext";
|
||||
import NotificationContext from "./NotificationContext";
|
||||
import SessionContext from "./SessionContext";
|
||||
import DomainConfigContext from "./DomainConfigContext";
|
||||
|
||||
export {
|
||||
AccountModalContext,
|
||||
ModalContext,
|
||||
NotificationContext,
|
||||
SessionContext,
|
||||
DomainConfigContext,
|
||||
};
|
||||
export { NotificationContext, SessionContext, DomainConfigContext };
|
||||
@@ -1,6 +1,6 @@
|
||||
import { useContext } from 'react';
|
||||
import { ModalContext } from 'context';
|
||||
import { useContext } from "react";
|
||||
import { ModalContext } from "components/providers";
|
||||
|
||||
export default function useInferenceJobs() {
|
||||
return useContext(ModalContext);
|
||||
};
|
||||
}
|
||||
Reference in new issue
Block a user