mirror of
https://github.com/muerwre/vault-frontend.git
synced 2025-04-25 12:56:41 +07:00
#58 made dialogs as routes
This commit is contained in:
parent
d9af895558
commit
4dc8bea040
21 changed files with 230 additions and 172 deletions
38
src/containers/dialogs/EditorCreateDialog/index.tsx
Normal file
38
src/containers/dialogs/EditorCreateDialog/index.tsx
Normal file
|
@ -0,0 +1,38 @@
|
|||
import React, { FC, useCallback, useMemo, useRef } from 'react';
|
||||
import { EMPTY_NODE, NODE_TYPES } from '~/redux/node/constants';
|
||||
import { EditorDialog } from '~/containers/dialogs/EditorDialog';
|
||||
import { useHistory, useRouteMatch } from 'react-router';
|
||||
import { values } from 'ramda';
|
||||
import { ModalWrapper } from '~/components/dialogs/ModalWrapper';
|
||||
|
||||
const EditorCreateDialog: FC = () => {
|
||||
const history = useHistory();
|
||||
const {
|
||||
params: { type },
|
||||
url,
|
||||
} = useRouteMatch<{ type: string }>();
|
||||
|
||||
const backUrl = useMemo(() => {
|
||||
return url.replace(/\/create\/(.*)$/, '');
|
||||
}, [url]);
|
||||
|
||||
const goBack = useCallback(() => {
|
||||
history.replace(backUrl);
|
||||
}, [backUrl, history]);
|
||||
|
||||
const isExist = useMemo(() => values(NODE_TYPES).some(el => el === type), [type]);
|
||||
|
||||
const data = useRef({ ...EMPTY_NODE, type });
|
||||
|
||||
if (!type || !isExist) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<ModalWrapper onOverlayClick={goBack}>
|
||||
<EditorDialog node={data.current} onRequestClose={goBack} />
|
||||
</ModalWrapper>
|
||||
);
|
||||
};
|
||||
|
||||
export { EditorCreateDialog };
|
|
@ -11,22 +11,20 @@ import { EditorButtons } from '~/components/editors/EditorButtons';
|
|||
import { FileUploaderProvider, useFileUploader } from '~/utils/hooks/fileUploader';
|
||||
import { UPLOAD_SUBJECTS, UPLOAD_TARGETS } from '~/redux/uploads/constants';
|
||||
import { FormikProvider } from 'formik';
|
||||
import { useShallowSelect } from '~/utils/hooks/useShallowSelect';
|
||||
import { selectNodeEditor } from '~/redux/node/selectors';
|
||||
import { INode } from '~/redux/types';
|
||||
|
||||
interface Props extends IDialogProps {
|
||||
type: string;
|
||||
node: INode;
|
||||
}
|
||||
|
||||
const EditorDialog: FC<Props> = ({ type, onRequestClose }) => {
|
||||
const editor = useShallowSelect(selectNodeEditor);
|
||||
const EditorDialog: FC<Props> = ({ node, onRequestClose }) => {
|
||||
const uploader = useFileUploader(UPLOAD_SUBJECTS.EDITOR, UPLOAD_TARGETS.NODES, []);
|
||||
const formik = useNodeFormFormik({ ...editor, type }, uploader, onRequestClose);
|
||||
const formik = useNodeFormFormik(node, uploader, onRequestClose);
|
||||
const { values, handleSubmit } = formik;
|
||||
|
||||
useCloseOnEscape(onRequestClose);
|
||||
|
||||
const component = useMemo(() => prop(type, NODE_EDITORS), [type]);
|
||||
const component = useMemo(() => node.type && prop(node.type, NODE_EDITORS), [node.type]);
|
||||
|
||||
if (!component) {
|
||||
return null;
|
||||
|
|
57
src/containers/dialogs/EditorEditDialog/index.tsx
Normal file
57
src/containers/dialogs/EditorEditDialog/index.tsx
Normal file
|
@ -0,0 +1,57 @@
|
|||
import React, { FC, useCallback, useEffect, useMemo, useState } from 'react';
|
||||
import { EMPTY_NODE } from '~/redux/node/constants';
|
||||
import { EditorDialog } from '~/containers/dialogs/EditorDialog';
|
||||
import { useHistory, useRouteMatch } from 'react-router';
|
||||
import { ModalWrapper } from '~/components/dialogs/ModalWrapper';
|
||||
import { apiGetNodeWithCancel } from '~/redux/node/api';
|
||||
import { LoaderCircle } from '~/components/input/LoaderCircle';
|
||||
import styles from './styles.module.scss';
|
||||
|
||||
const EditorEditDialog: FC = () => {
|
||||
const [data, setData] = useState(EMPTY_NODE);
|
||||
const [isLoading, setLoading] = useState(false);
|
||||
const history = useHistory();
|
||||
|
||||
const {
|
||||
params: { id },
|
||||
url,
|
||||
} = useRouteMatch<{ id: string }>();
|
||||
|
||||
const backUrl = useMemo(() => {
|
||||
return url.replace(/\/edit$/, '');
|
||||
}, [url]);
|
||||
|
||||
const goBack = useCallback(() => {
|
||||
history.replace(backUrl);
|
||||
}, [backUrl, history]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!id) {
|
||||
return;
|
||||
}
|
||||
|
||||
const { request, cancel } = apiGetNodeWithCancel({ id });
|
||||
|
||||
setLoading(true);
|
||||
request
|
||||
.then(data => setData(data.node))
|
||||
.then(() => setLoading(false))
|
||||
.catch(console.log);
|
||||
|
||||
return () => cancel();
|
||||
}, [id]);
|
||||
|
||||
return (
|
||||
<ModalWrapper onOverlayClick={console.log}>
|
||||
{isLoading ? (
|
||||
<div className={styles.loader}>
|
||||
<LoaderCircle size={64} />
|
||||
</div>
|
||||
) : (
|
||||
<EditorDialog node={data} onRequestClose={goBack} />
|
||||
)}
|
||||
</ModalWrapper>
|
||||
);
|
||||
};
|
||||
|
||||
export { EditorEditDialog };
|
|
@ -0,0 +1,4 @@
|
|||
.loader {
|
||||
fill: white;
|
||||
color: white;
|
||||
}
|
|
@ -1,57 +1,34 @@
|
|||
import React, { Attributes, FC, useCallback } from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import ReactDOM from 'react-dom';
|
||||
import styles from './styles.module.scss';
|
||||
import { IState } from '~/redux/store';
|
||||
import * as ACTIONS from '~/redux/modal/actions';
|
||||
import React, { FC, useCallback } from 'react';
|
||||
import { useDispatch } from 'react-redux';
|
||||
import { DIALOG_CONTENT } from '~/constants/dialogs';
|
||||
import { useShallowSelect } from '~/utils/hooks/useShallowSelect';
|
||||
import { selectModal } from '~/redux/modal/selectors';
|
||||
import { modalSetDialog, modalSetShown, modalShowDialog } from '~/redux/modal/actions';
|
||||
import { ModalWrapper } from '~/components/dialogs/ModalWrapper';
|
||||
|
||||
const mapStateToProps = ({ modal }: IState) => ({ ...modal });
|
||||
const mapDispatchToProps = {
|
||||
modalSetShown: ACTIONS.modalSetShown,
|
||||
modalSetDialog: ACTIONS.modalSetDialog,
|
||||
modalShowDialog: ACTIONS.modalShowDialog,
|
||||
};
|
||||
type IProps = {};
|
||||
|
||||
type IProps = typeof mapDispatchToProps & ReturnType<typeof mapStateToProps> & {};
|
||||
const Modal: FC<IProps> = ({}) => {
|
||||
const { is_shown, dialog } = useShallowSelect(selectModal);
|
||||
const dispatch = useDispatch();
|
||||
|
||||
const ModalUnconnected: FC<IProps> = ({
|
||||
modalSetShown,
|
||||
modalSetDialog,
|
||||
modalShowDialog,
|
||||
is_shown,
|
||||
dialog,
|
||||
}) => {
|
||||
const onRequestClose = useCallback(() => {
|
||||
modalSetShown(false);
|
||||
modalSetDialog('');
|
||||
}, [modalSetShown, modalSetDialog]);
|
||||
dispatch(modalSetShown(false));
|
||||
dispatch(modalSetDialog(''));
|
||||
}, [dispatch]);
|
||||
|
||||
const onDialogChange = useCallback((val: string) => dispatch(modalShowDialog(val)), [dispatch]);
|
||||
|
||||
if (!dialog || !DIALOG_CONTENT[dialog] || !is_shown) return null;
|
||||
|
||||
return ReactDOM.createPortal(
|
||||
<div className={styles.fixed}>
|
||||
<div className={styles.overlay} onClick={onRequestClose} />
|
||||
<div className={styles.content}>
|
||||
{React.createElement(DIALOG_CONTENT[dialog], {
|
||||
onRequestClose,
|
||||
onDialogChange: modalShowDialog,
|
||||
})}
|
||||
</div>
|
||||
</div>,
|
||||
document.body
|
||||
return (
|
||||
<ModalWrapper onOverlayClick={onRequestClose}>
|
||||
{React.createElement(DIALOG_CONTENT[dialog], {
|
||||
onRequestClose,
|
||||
onDialogChange,
|
||||
})}
|
||||
</ModalWrapper>
|
||||
);
|
||||
};
|
||||
|
||||
const Modal = connect(mapStateToProps, mapDispatchToProps)(ModalUnconnected);
|
||||
|
||||
export { ModalUnconnected, Modal };
|
||||
|
||||
/*
|
||||
|
||||
<div className={styles.content_scroller}>
|
||||
<div className={styles.content_padder}>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
*/
|
||||
export { Modal };
|
||||
|
|
|
@ -1,61 +0,0 @@
|
|||
@import "src/styles/variables";
|
||||
|
||||
.fixed {
|
||||
position: fixed;
|
||||
z-index: 30;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
@keyframes appear {
|
||||
0% {
|
||||
opacity: 0;
|
||||
}
|
||||
100% {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.content {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
opacity: 0;
|
||||
animation: appear 0.25s forwards;
|
||||
}
|
||||
|
||||
.content_scroller {
|
||||
width: 100%;
|
||||
max-width: 100vw;
|
||||
max-height: 100vh;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.content_padder {
|
||||
box-sizing: border-box;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.overlay {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: transparentize($color: #000000, $amount: 0.9);
|
||||
cursor: pointer;
|
||||
animation: appear 0.25s forwards;
|
||||
|
||||
@include can_backdrop {
|
||||
backdrop-filter: blur(15px);
|
||||
}
|
||||
}
|
|
@ -1,10 +0,0 @@
|
|||
import React, { FC } from 'react';
|
||||
import { EditorDialog } from '~/containers/dialogs/EditorDialog';
|
||||
import { IDialogProps } from '~/redux/types';
|
||||
import { NODE_TYPES } from '~/redux/node/constants';
|
||||
|
||||
type IProps = IDialogProps & {};
|
||||
|
||||
const EditorDialogAudio: FC<IProps> = props => <EditorDialog type={NODE_TYPES.AUDIO} {...props} />;
|
||||
|
||||
export { EditorDialogAudio };
|
|
@ -1,10 +0,0 @@
|
|||
import React, { FC } from 'react';
|
||||
import { EditorDialog } from '~/containers/dialogs/EditorDialog';
|
||||
import { IDialogProps } from '~/redux/types';
|
||||
import { NODE_TYPES } from '~/redux/node/constants';
|
||||
|
||||
type IProps = IDialogProps & {};
|
||||
|
||||
const EditorDialogImage: FC<IProps> = props => <EditorDialog type={NODE_TYPES.IMAGE} {...props} />;
|
||||
|
||||
export { EditorDialogImage };
|
|
@ -1,10 +0,0 @@
|
|||
import React, { FC } from 'react';
|
||||
import { EditorDialog } from '~/containers/dialogs/EditorDialog';
|
||||
import { IDialogProps } from '~/redux/types';
|
||||
import { NODE_TYPES } from '~/redux/node/constants';
|
||||
|
||||
type IProps = IDialogProps & {};
|
||||
|
||||
const EditorDialogText: FC<IProps> = props => <EditorDialog type={NODE_TYPES.TEXT} {...props} />;
|
||||
|
||||
export { EditorDialogText };
|
|
@ -1,10 +0,0 @@
|
|||
import React, { FC } from 'react';
|
||||
import { EditorDialog } from '~/containers/dialogs/EditorDialog';
|
||||
import { IDialogProps } from '~/redux/types';
|
||||
import { NODE_TYPES } from '~/redux/node/constants';
|
||||
|
||||
type IProps = IDialogProps & {};
|
||||
|
||||
const EditorDialogVideo: FC<IProps> = props => <EditorDialog type={NODE_TYPES.VIDEO} {...props} />;
|
||||
|
||||
export { EditorDialogVideo };
|
|
@ -5,6 +5,7 @@ import { TagSidebar } from '~/containers/sidebars/TagSidebar';
|
|||
import { ProfileSidebar } from '~/containers/sidebars/ProfileSidebar';
|
||||
import { Authorized } from '~/components/containers/Authorized';
|
||||
import { SubmitBar } from '~/components/bars/SubmitBar';
|
||||
import { EditorCreateDialog } from '~/containers/dialogs/EditorCreateDialog';
|
||||
|
||||
interface IProps {
|
||||
prefix?: string;
|
||||
|
@ -15,6 +16,7 @@ const SidebarRouter: FC<IProps> = ({ prefix = '', isLab }) => {
|
|||
return createPortal(
|
||||
<>
|
||||
<Switch>
|
||||
<Route path={`${prefix}/create/:type`} component={EditorCreateDialog} />
|
||||
<Route path={`${prefix}/tag/:tag`} component={TagSidebar} />
|
||||
<Route path={`${prefix}/~:username`} component={ProfileSidebar} />
|
||||
</Switch>
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue