mirror of
https://github.com/muerwre/vault-frontend.git
synced 2025-04-25 04:46:40 +07:00
better scroll dialog
This commit is contained in:
parent
782ef41eda
commit
0c2229d9af
13 changed files with 221 additions and 27 deletions
5
package-lock.json
generated
5
package-lock.json
generated
|
@ -3028,6 +3028,11 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"body-scroll-lock": {
|
||||||
|
"version": "2.6.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/body-scroll-lock/-/body-scroll-lock-2.6.4.tgz",
|
||||||
|
"integrity": "sha512-NP08WsovlmxEoZP9pdlqrE+AhNaivlTrz9a0FF37BQsnOrpN48eNqivKkE7SYpM9N+YIPjsdVzfLAUQDBm6OQw=="
|
||||||
|
},
|
||||||
"bonjour": {
|
"bonjour": {
|
||||||
"version": "3.5.0",
|
"version": "3.5.0",
|
||||||
"resolved": "https://registry.npmjs.org/bonjour/-/bonjour-3.5.0.tgz",
|
"resolved": "https://registry.npmjs.org/bonjour/-/bonjour-3.5.0.tgz",
|
||||||
|
|
|
@ -57,6 +57,7 @@
|
||||||
"@typescript-eslint/parser": "^1.13.0",
|
"@typescript-eslint/parser": "^1.13.0",
|
||||||
"axios": "^0.18.0",
|
"axios": "^0.18.0",
|
||||||
"babel-runtime": "^6.26.0",
|
"babel-runtime": "^6.26.0",
|
||||||
|
"body-scroll-lock": "^2.6.4",
|
||||||
"classnames": "^2.2.6",
|
"classnames": "^2.2.6",
|
||||||
"clean-webpack-plugin": "^0.1.9",
|
"clean-webpack-plugin": "^0.1.9",
|
||||||
"connected-react-router": "^6.3.2",
|
"connected-react-router": "^6.3.2",
|
||||||
|
|
64
src/containers/dialogs/BetterScrollDialog/index.tsx
Normal file
64
src/containers/dialogs/BetterScrollDialog/index.tsx
Normal file
|
@ -0,0 +1,64 @@
|
||||||
|
import React, {
|
||||||
|
FC,
|
||||||
|
MouseEventHandler,
|
||||||
|
ReactChild,
|
||||||
|
useCallback,
|
||||||
|
useEffect,
|
||||||
|
useState,
|
||||||
|
useRef,
|
||||||
|
} from 'react';
|
||||||
|
// import { DialogPanel } from '~/components/panels/DialogPanel';
|
||||||
|
import classNames from 'classnames';
|
||||||
|
import { Scroll } from '~/components/containers/Scroll';
|
||||||
|
import * as styles from './styles.scss';
|
||||||
|
import { enableBodyScroll, disableBodyScroll } from 'body-scroll-lock';
|
||||||
|
import { Icon } from '~/components/input/Icon';
|
||||||
|
|
||||||
|
interface IProps {
|
||||||
|
children: React.ReactChild;
|
||||||
|
header?: JSX.Element;
|
||||||
|
footer?: JSX.Element;
|
||||||
|
size?: 'medium' | 'big';
|
||||||
|
width?: number;
|
||||||
|
error?: string;
|
||||||
|
|
||||||
|
onOverlayClick?: MouseEventHandler<HTMLDivElement>;
|
||||||
|
onRefCapture?: (ref: any) => void;
|
||||||
|
onClose?: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
const BetterScrollDialog: FC<IProps> = ({
|
||||||
|
children,
|
||||||
|
header,
|
||||||
|
footer,
|
||||||
|
width = 600,
|
||||||
|
error,
|
||||||
|
onOverlayClick,
|
||||||
|
onRefCapture,
|
||||||
|
onClose,
|
||||||
|
}) => {
|
||||||
|
const ref = useRef(null);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
disableBodyScroll(ref.current);
|
||||||
|
|
||||||
|
return () => enableBodyScroll(ref.current);
|
||||||
|
}, [ref]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={styles.wrap} ref={ref}>
|
||||||
|
<div className={styles.container} style={{ maxWidth: width }}>
|
||||||
|
{onClose && (
|
||||||
|
<div className={styles.close} onClick={onClose}>
|
||||||
|
<Icon icon="close" />
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{header && <div className={styles.header}>{header}</div>}
|
||||||
|
<div className={styles.body}>{children}</div>
|
||||||
|
{footer && <div className={styles.header}>{footer}</div>}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export { BetterScrollDialog };
|
84
src/containers/dialogs/BetterScrollDialog/styles.scss
Normal file
84
src/containers/dialogs/BetterScrollDialog/styles.scss
Normal file
|
@ -0,0 +1,84 @@
|
||||||
|
.wrap {
|
||||||
|
width: 100vw;
|
||||||
|
height: 100vh;
|
||||||
|
background: transparentize(darken($content_bg, 4%), 0.5);
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
padding: 40px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
.container {
|
||||||
|
display: flex;
|
||||||
|
align-items: stretch;
|
||||||
|
justify-content: center;
|
||||||
|
flex-direction: column;
|
||||||
|
min-width: $cell;
|
||||||
|
max-width: 400px;
|
||||||
|
max-height: 100%;
|
||||||
|
width: 100%;
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
& > div:nth-child(2) {
|
||||||
|
border-top-left-radius: $dialog_radius;
|
||||||
|
border-top-right-radius: $dialog_radius;
|
||||||
|
}
|
||||||
|
|
||||||
|
& > div:last-child {
|
||||||
|
border-bottom-left-radius: $dialog_radius;
|
||||||
|
border-bottom-right-radius: $dialog_radius;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.header,
|
||||||
|
.footer {
|
||||||
|
@include outer_shadow();
|
||||||
|
|
||||||
|
padding: 10px;
|
||||||
|
background: darken($content_bg, 2%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.body {
|
||||||
|
@include outer_shadow();
|
||||||
|
|
||||||
|
overflow: auto;
|
||||||
|
flex: 1;
|
||||||
|
background: $content_bg;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes appear {
|
||||||
|
0% {
|
||||||
|
top: -48px;
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
top: -58px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.close {
|
||||||
|
background: darken($content_bg, 2%);
|
||||||
|
width: 48px;
|
||||||
|
height: 48px;
|
||||||
|
position: absolute;
|
||||||
|
top: -58px;
|
||||||
|
right: 50%;
|
||||||
|
transform: translate(50%, 0);
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
border-radius: 100%;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: transform 0.25s, background-color 0.25s;
|
||||||
|
animation: appear 0.5s forwards;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background-color: $red;
|
||||||
|
transform: translate(50%, -5px);
|
||||||
|
}
|
||||||
|
|
||||||
|
svg {
|
||||||
|
width: 24px;
|
||||||
|
height: 24px;
|
||||||
|
}
|
||||||
|
}
|
|
@ -14,6 +14,7 @@ import * as NODE_ACTIONS from '~/redux/node/actions';
|
||||||
import { selectUploads } from '~/redux/uploads/selectors';
|
import { selectUploads } from '~/redux/uploads/selectors';
|
||||||
import { ERROR_LITERAL } from '~/constants/errors';
|
import { ERROR_LITERAL } from '~/constants/errors';
|
||||||
import { NODE_EDITORS, EMPTY_NODE } from '~/redux/node/constants';
|
import { NODE_EDITORS, EMPTY_NODE } from '~/redux/node/constants';
|
||||||
|
import { BetterScrollDialog } from '../BetterScrollDialog';
|
||||||
|
|
||||||
const mapStateToProps = state => {
|
const mapStateToProps = state => {
|
||||||
const { editor, errors } = selectNode(state);
|
const { editor, errors } = selectNode(state);
|
||||||
|
@ -90,8 +91,8 @@ const EditorDialogUnconnected: FC<IProps> = ({
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<form onSubmit={onSubmit} className={styles.form}>
|
<form onSubmit={onSubmit} className={styles.form}>
|
||||||
<ScrollDialog
|
<BetterScrollDialog
|
||||||
buttons={buttons}
|
footer={buttons}
|
||||||
width={860}
|
width={860}
|
||||||
error={error && ERROR_LITERAL[error]}
|
error={error && ERROR_LITERAL[error]}
|
||||||
onClose={onRequestClose}
|
onClose={onRequestClose}
|
||||||
|
@ -104,7 +105,7 @@ const EditorDialogUnconnected: FC<IProps> = ({
|
||||||
setTemp,
|
setTemp,
|
||||||
})}
|
})}
|
||||||
</div>
|
</div>
|
||||||
</ScrollDialog>
|
</BetterScrollDialog>
|
||||||
</form>
|
</form>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -11,6 +11,7 @@ import * as styles from './styles.scss';
|
||||||
import { selectAuthLogin } from '~/redux/auth/selectors';
|
import { selectAuthLogin } from '~/redux/auth/selectors';
|
||||||
import * as ACTIONS from '~/redux/auth/actions';
|
import * as ACTIONS from '~/redux/auth/actions';
|
||||||
import { API } from '~/constants/api';
|
import { API } from '~/constants/api';
|
||||||
|
import { BetterScrollDialog } from '../BetterScrollDialog';
|
||||||
|
|
||||||
const mapStateToProps = selectAuthLogin;
|
const mapStateToProps = selectAuthLogin;
|
||||||
|
|
||||||
|
@ -47,20 +48,18 @@ const LoginDialogUnconnected: FC<IProps> = ({
|
||||||
}, [username, password]);
|
}, [username, password]);
|
||||||
|
|
||||||
const buttons = (
|
const buttons = (
|
||||||
<Padder>
|
<Group horizontal className={styles.footer}>
|
||||||
<Group horizontal>
|
<Button stretchy>
|
||||||
<Button iconLeft="key" stretchy>
|
|
||||||
<span>Войти</span>
|
<span>Войти</span>
|
||||||
</Button>
|
</Button>
|
||||||
</Group>
|
</Group>
|
||||||
</Padder>
|
|
||||||
);
|
);
|
||||||
|
|
||||||
useCloseOnEscape(onRequestClose);
|
useCloseOnEscape(onRequestClose);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<form onSubmit={onSubmit}>
|
<form onSubmit={onSubmit}>
|
||||||
<ScrollDialog width={260} error={error} onClose={onRequestClose} buttons={buttons}>
|
<BetterScrollDialog width={260} error={error} onClose={onRequestClose} footer={buttons}>
|
||||||
<Padder>
|
<Padder>
|
||||||
<div className={styles.wrap}>
|
<div className={styles.wrap}>
|
||||||
<Group>
|
<Group>
|
||||||
|
@ -77,7 +76,7 @@ const LoginDialogUnconnected: FC<IProps> = ({
|
||||||
</Group>
|
</Group>
|
||||||
</div>
|
</div>
|
||||||
</Padder>
|
</Padder>
|
||||||
</ScrollDialog>
|
</BetterScrollDialog>
|
||||||
</form>
|
</form>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,14 +1,17 @@
|
||||||
$vk_color: darken(desaturate($blue, 70%), 20%);
|
$vk_color: darken(desaturate($blue, 100%), 30%);
|
||||||
|
|
||||||
.wrap {
|
.wrap {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: flex-start;
|
align-items: flex-start;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
padding: 20px;
|
padding: $gap;
|
||||||
margin: auto;
|
|
||||||
|
button {
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
|
||||||
h2 {
|
h2 {
|
||||||
margin-bottom: $gap * 3 !important;
|
margin: $gap 0 $gap * 4 !important;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -20,8 +23,8 @@ $vk_color: darken(desaturate($blue, 70%), 20%);
|
||||||
svg {
|
svg {
|
||||||
fill: $vk_color;
|
fill: $vk_color;
|
||||||
margin-right: $gap;
|
margin-right: $gap;
|
||||||
width: 24px;
|
// width: 24px;
|
||||||
height: 24px;
|
// height: 24px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -30,3 +33,11 @@ $vk_color: darken(desaturate($blue, 70%), 20%);
|
||||||
padding: $gap * 2 0 0 0;
|
padding: $gap * 2 0 0 0;
|
||||||
border-top: 1px solid black;
|
border-top: 1px solid black;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.footer {
|
||||||
|
padding: $gap;
|
||||||
|
|
||||||
|
button {
|
||||||
|
// text-align: left;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -33,15 +33,11 @@ const ModalUnconnected: FC<IProps> = ({
|
||||||
<div className={styles.fixed}>
|
<div className={styles.fixed}>
|
||||||
<div className={styles.overlay} onClick={onRequestClose} />
|
<div className={styles.overlay} onClick={onRequestClose} />
|
||||||
<div className={styles.content}>
|
<div className={styles.content}>
|
||||||
<div className={styles.content_scroller}>
|
|
||||||
<div className={styles.content_padder}>
|
|
||||||
{React.createElement(DIALOG_CONTENT[dialog], {
|
{React.createElement(DIALOG_CONTENT[dialog], {
|
||||||
onRequestClose,
|
onRequestClose,
|
||||||
onDialogChange: modalShowDialog,
|
onDialogChange: modalShowDialog,
|
||||||
})}
|
})}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>,
|
</div>,
|
||||||
document.body
|
document.body
|
||||||
);
|
);
|
||||||
|
@ -53,3 +49,12 @@ const Modal = connect(
|
||||||
)(ModalUnconnected);
|
)(ModalUnconnected);
|
||||||
|
|
||||||
export { ModalUnconnected, Modal };
|
export { ModalUnconnected, Modal };
|
||||||
|
|
||||||
|
/*
|
||||||
|
|
||||||
|
<div className={styles.content_scroller}>
|
||||||
|
<div className={styles.content_padder}>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
16
src/containers/dialogs/TestDialog/index.tsx
Normal file
16
src/containers/dialogs/TestDialog/index.tsx
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
import React, { FC } from 'react';
|
||||||
|
import { BetterScrollDialog } from '../BetterScrollDialog';
|
||||||
|
import * as styles from './styles.scss';
|
||||||
|
|
||||||
|
interface IProps {}
|
||||||
|
|
||||||
|
const TestDialog: FC<IProps> = ({}) => (
|
||||||
|
<BetterScrollDialog
|
||||||
|
header={<div className={styles.head}>HEAD</div>}
|
||||||
|
footer={<div className={styles.footer}>FOOTER</div>}
|
||||||
|
>
|
||||||
|
<div className={styles.example} />
|
||||||
|
</BetterScrollDialog>
|
||||||
|
);
|
||||||
|
|
||||||
|
export { TestDialog };
|
4
src/containers/dialogs/TestDialog/styles.scss
Normal file
4
src/containers/dialogs/TestDialog/styles.scss
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
.example {
|
||||||
|
height: 1200px;
|
||||||
|
background: yellow;
|
||||||
|
}
|
|
@ -6,6 +6,7 @@ import { EditorDialogText } from '~/containers/editors/EditorDialogText';
|
||||||
import { EditorDialogVideo } from '~/containers/editors/EditorDialogVideo';
|
import { EditorDialogVideo } from '~/containers/editors/EditorDialogVideo';
|
||||||
import { EditorDialogAudio } from '~/containers/editors/EditorDialogAudio';
|
import { EditorDialogAudio } from '~/containers/editors/EditorDialogAudio';
|
||||||
import { NODE_TYPES } from '../node/constants';
|
import { NODE_TYPES } from '../node/constants';
|
||||||
|
import { TestDialog } from '~/containers/dialogs/TestDialog';
|
||||||
|
|
||||||
export const MODAL_ACTIONS = {
|
export const MODAL_ACTIONS = {
|
||||||
SET_SHOWN: 'MODAL.SET_SHOWN',
|
SET_SHOWN: 'MODAL.SET_SHOWN',
|
||||||
|
@ -20,6 +21,7 @@ export const DIALOGS = {
|
||||||
EDITOR_AUDIO: 'EDITOR_AUDIO',
|
EDITOR_AUDIO: 'EDITOR_AUDIO',
|
||||||
LOGIN: 'LOGIN',
|
LOGIN: 'LOGIN',
|
||||||
LOADING: 'LOADING',
|
LOADING: 'LOADING',
|
||||||
|
TEST: 'TEST',
|
||||||
};
|
};
|
||||||
|
|
||||||
export const DIALOG_CONTENT = {
|
export const DIALOG_CONTENT = {
|
||||||
|
@ -29,6 +31,7 @@ export const DIALOG_CONTENT = {
|
||||||
[DIALOGS.EDITOR_AUDIO]: EditorDialogAudio,
|
[DIALOGS.EDITOR_AUDIO]: EditorDialogAudio,
|
||||||
[DIALOGS.LOGIN]: LoginDialog,
|
[DIALOGS.LOGIN]: LoginDialog,
|
||||||
[DIALOGS.LOADING]: LoadingDialog,
|
[DIALOGS.LOADING]: LoadingDialog,
|
||||||
|
[DIALOGS.TEST]: TestDialog,
|
||||||
};
|
};
|
||||||
|
|
||||||
export const NODE_EDITOR_DIALOGS = {
|
export const NODE_EDITOR_DIALOGS = {
|
||||||
|
|
|
@ -10,7 +10,7 @@ export interface IModalState {
|
||||||
|
|
||||||
const INITIAL_STATE: IModalState = {
|
const INITIAL_STATE: IModalState = {
|
||||||
is_shown: false,
|
is_shown: false,
|
||||||
dialog: DIALOGS.EDITOR_TEXT,
|
dialog: DIALOGS.LOGIN,
|
||||||
};
|
};
|
||||||
|
|
||||||
export default createReducer(INITIAL_STATE, MODAL_HANDLERS);
|
export default createReducer(INITIAL_STATE, MODAL_HANDLERS);
|
||||||
|
|
|
@ -12,6 +12,7 @@ $radius: 4px;
|
||||||
$cell_radius: $radius;
|
$cell_radius: $radius;
|
||||||
$panel_radius: $radius;
|
$panel_radius: $radius;
|
||||||
$input_radius: $radius;
|
$input_radius: $radius;
|
||||||
|
$dialog_radius: $radius * 2;
|
||||||
|
|
||||||
$input_height: 36px;
|
$input_height: 36px;
|
||||||
$info_height: 24px;
|
$info_height: 24px;
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue