react-pure-modal 2.3.4 → 3.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +94 -78
- package/dist/index.js +1 -0
- package/dist/rslib.config.d.ts +2 -0
- package/dist/src/compounds/Backdrop.d.ts +3 -0
- package/dist/src/compounds/Close.d.ts +5 -0
- package/dist/src/compounds/Content.d.ts +5 -0
- package/dist/src/compounds/Footer.d.ts +5 -0
- package/dist/src/compounds/Header.d.ts +5 -0
- package/dist/src/compounds/Modal.d.ts +15 -0
- package/dist/src/compounds/Modal.types.d.ts +15 -0
- package/dist/src/compounds/ModalContext.d.ts +4 -0
- package/dist/src/index.d.ts +1 -0
- package/package.json +40 -49
- package/.babelrc.js +0 -12
- package/.eslintrc +0 -22
- package/.github/release-drafter-config.yml +0 -20
- package/.github/workflows/build.yml +0 -24
- package/.github/workflows/npm-publish.yml +0 -35
- package/.github/workflows/release-drafter.yml +0 -16
- package/.prettierrc +0 -19
- package/.travis.yml +0 -16
- package/@types/scheduler/tracing.d.ts +0 -22
- package/__tests__/__snapshots__/react-pure-modal-test.js.snap +0 -92
- package/__tests__/react-pure-modal-test.js +0 -44
- package/dist/pure-modal-content.d.ts +0 -25
- package/dist/react-pure-modal.d.ts +0 -20
- package/dist/react-pure-modal.min.css +0 -1
- package/dist/react-pure-modal.min.js +0 -1
- package/dist/types.d.ts +0 -1
- package/example/content.ts +0 -50
- package/example/example.min.js +0 -2
- package/example/example.min.js.LICENSE.txt +0 -109
- package/example/example.tsx +0 -109
- package/index.html +0 -22
- package/screencast/scrollable.gif +0 -0
- package/screencast/simple.gif +0 -0
- package/src/pure-modal-content.tsx +0 -74
- package/src/react-pure-modal.css +0 -123
- package/src/react-pure-modal.tsx +0 -256
- package/tsconfig.json +0 -25
- package/webpack.config.dev.js +0 -65
- package/webpack.config.js +0 -62
- /package/{src/types.ts → dist/@types/types.d.ts} +0 -0
|
@@ -1,109 +0,0 @@
|
|
|
1
|
-
/*! !!../node_modules/css-loader/dist/cjs.js!./react-pure-modal.min.css */
|
|
2
|
-
|
|
3
|
-
/*! !../node_modules/style-loader/dist/runtime/injectStylesIntoStyleTag.js */
|
|
4
|
-
|
|
5
|
-
/*! ../dist/react-pure-modal.min.css */
|
|
6
|
-
|
|
7
|
-
/*! ../dist/react-pure-modal.min.js */
|
|
8
|
-
|
|
9
|
-
/*! ../node_modules/css-loader/dist/runtime/api.js */
|
|
10
|
-
|
|
11
|
-
/*! ./cjs/react-dom.development.js */
|
|
12
|
-
|
|
13
|
-
/*! ./cjs/react.development.js */
|
|
14
|
-
|
|
15
|
-
/*! ./cjs/scheduler.development.js */
|
|
16
|
-
|
|
17
|
-
/*! ./content */
|
|
18
|
-
|
|
19
|
-
/*! react */
|
|
20
|
-
|
|
21
|
-
/*! react-dom */
|
|
22
|
-
|
|
23
|
-
/*! scheduler */
|
|
24
|
-
|
|
25
|
-
/*!****************************!*\
|
|
26
|
-
!*** ./example/content.ts ***!
|
|
27
|
-
\****************************/
|
|
28
|
-
|
|
29
|
-
/*!*****************************!*\
|
|
30
|
-
!*** ./example/example.tsx ***!
|
|
31
|
-
\*****************************/
|
|
32
|
-
|
|
33
|
-
/*!*************************************!*\
|
|
34
|
-
!*** ./node_modules/react/index.js ***!
|
|
35
|
-
\*************************************/
|
|
36
|
-
|
|
37
|
-
/*!**************************************!*\
|
|
38
|
-
!*** ./dist/react-pure-modal.min.js ***!
|
|
39
|
-
\**************************************/
|
|
40
|
-
|
|
41
|
-
/*!***************************************!*\
|
|
42
|
-
!*** ./dist/react-pure-modal.min.css ***!
|
|
43
|
-
\***************************************/
|
|
44
|
-
|
|
45
|
-
/*!*****************************************!*\
|
|
46
|
-
!*** ./node_modules/react-dom/index.js ***!
|
|
47
|
-
\*****************************************/
|
|
48
|
-
|
|
49
|
-
/*!*****************************************!*\
|
|
50
|
-
!*** ./node_modules/scheduler/index.js ***!
|
|
51
|
-
\*****************************************/
|
|
52
|
-
|
|
53
|
-
/*!*****************************************************!*\
|
|
54
|
-
!*** ./node_modules/css-loader/dist/runtime/api.js ***!
|
|
55
|
-
\*****************************************************/
|
|
56
|
-
|
|
57
|
-
/*!*****************************************************!*\
|
|
58
|
-
!*** ./node_modules/react/cjs/react.development.js ***!
|
|
59
|
-
\*****************************************************/
|
|
60
|
-
|
|
61
|
-
/*!*************************************************************!*\
|
|
62
|
-
!*** ./node_modules/react-dom/cjs/react-dom.development.js ***!
|
|
63
|
-
\*************************************************************/
|
|
64
|
-
|
|
65
|
-
/*!*************************************************************!*\
|
|
66
|
-
!*** ./node_modules/scheduler/cjs/scheduler.development.js ***!
|
|
67
|
-
\*************************************************************/
|
|
68
|
-
|
|
69
|
-
/*!****************************************************************************!*\
|
|
70
|
-
!*** ./node_modules/style-loader/dist/runtime/injectStylesIntoStyleTag.js ***!
|
|
71
|
-
\****************************************************************************/
|
|
72
|
-
|
|
73
|
-
/*!*****************************************************************************!*\
|
|
74
|
-
!*** ./node_modules/css-loader/dist/cjs.js!./dist/react-pure-modal.min.css ***!
|
|
75
|
-
\*****************************************************************************/
|
|
76
|
-
|
|
77
|
-
/**
|
|
78
|
-
* @license React
|
|
79
|
-
* react-dom.development.js
|
|
80
|
-
*
|
|
81
|
-
* Copyright (c) Facebook, Inc. and its affiliates.
|
|
82
|
-
*
|
|
83
|
-
* This source code is licensed under the MIT license found in the
|
|
84
|
-
* LICENSE file in the root directory of this source tree.
|
|
85
|
-
*/
|
|
86
|
-
|
|
87
|
-
/**
|
|
88
|
-
* @license React
|
|
89
|
-
* scheduler.development.js
|
|
90
|
-
*
|
|
91
|
-
* Copyright (c) Facebook, Inc. and its affiliates.
|
|
92
|
-
*
|
|
93
|
-
* This source code is licensed under the MIT license found in the
|
|
94
|
-
* LICENSE file in the root directory of this source tree.
|
|
95
|
-
*/
|
|
96
|
-
|
|
97
|
-
/**
|
|
98
|
-
* Checks if an event is supported in the current execution environment.
|
|
99
|
-
*
|
|
100
|
-
* NOTE: This will not work correctly for non-generic events such as `change`,
|
|
101
|
-
* `reset`, `load`, `error`, and `select`.
|
|
102
|
-
*
|
|
103
|
-
* Borrows from Modernizr.
|
|
104
|
-
*
|
|
105
|
-
* @param {string} eventNameSuffix Event name, e.g. "click".
|
|
106
|
-
* @return {boolean} True if the event is supported.
|
|
107
|
-
* @internal
|
|
108
|
-
* @license Modernizr 3.0.0pre (Custom Build) | MIT
|
|
109
|
-
*/
|
package/example/example.tsx
DELETED
|
@@ -1,109 +0,0 @@
|
|
|
1
|
-
import React, { useState } from 'react';
|
|
2
|
-
import { render } from 'react-dom';
|
|
3
|
-
import PureModal from '../dist/react-pure-modal.min.js';
|
|
4
|
-
import { smallContent, largeContent } from './content';
|
|
5
|
-
import '../dist/react-pure-modal.min.css';
|
|
6
|
-
|
|
7
|
-
function ModalContainer() {
|
|
8
|
-
const [modal, setModal] = useState(false);
|
|
9
|
-
const [modalInnerScroll, setModalInnerScroll] = useState(false);
|
|
10
|
-
const [modalCenter, setModalCenter] = useState(false);
|
|
11
|
-
const [modalDrag, setModalDrag] = useState(false);
|
|
12
|
-
const [modalNoScrollable, setModalNoScrollable] = useState(false);
|
|
13
|
-
|
|
14
|
-
return (
|
|
15
|
-
<div>
|
|
16
|
-
<button type="button" className="button" onClick={() => setModal(true)}>
|
|
17
|
-
Open simple modal
|
|
18
|
-
</button>
|
|
19
|
-
<button type="button" className="button" onClick={() => setModalInnerScroll(true)}>
|
|
20
|
-
Open modal with inner scroll
|
|
21
|
-
</button>
|
|
22
|
-
<button type="button" className="button" onClick={() => setModalCenter(true)}>
|
|
23
|
-
Open small modal on center scrollable or not
|
|
24
|
-
</button>
|
|
25
|
-
<button type="button" className="button" onClick={() => setModalDrag(true)}>
|
|
26
|
-
Draggable
|
|
27
|
-
</button>
|
|
28
|
-
<button type="button" className="button" onClick={() => setModalNoScrollable(true)}>
|
|
29
|
-
Open large modal on center without scroll
|
|
30
|
-
</button>
|
|
31
|
-
<PureModal
|
|
32
|
-
header="Custom header with a lot of symbols. It's very important to have a dynamic header height and this modal supports it"
|
|
33
|
-
footer="Buttons?"
|
|
34
|
-
isOpen={modalInnerScroll}
|
|
35
|
-
closeButtonPosition="bottom"
|
|
36
|
-
onClose={() => {
|
|
37
|
-
setModalInnerScroll(false);
|
|
38
|
-
return true;
|
|
39
|
-
}}
|
|
40
|
-
>
|
|
41
|
-
<input type="text" placeholder="with input" value="" />
|
|
42
|
-
{largeContent}
|
|
43
|
-
</PureModal>
|
|
44
|
-
<PureModal
|
|
45
|
-
header="Custom heading"
|
|
46
|
-
footer="Buttons?"
|
|
47
|
-
width="800px"
|
|
48
|
-
scrollable={false}
|
|
49
|
-
isOpen={modal}
|
|
50
|
-
onClose={() => {
|
|
51
|
-
setModal(false);
|
|
52
|
-
return true;
|
|
53
|
-
}}
|
|
54
|
-
>
|
|
55
|
-
{largeContent}
|
|
56
|
-
</PureModal>
|
|
57
|
-
<PureModal
|
|
58
|
-
header="Custom heading"
|
|
59
|
-
footer="Buttons?"
|
|
60
|
-
scrollable={false}
|
|
61
|
-
isOpen={modalCenter}
|
|
62
|
-
closeButtonPosition="bottom"
|
|
63
|
-
portal
|
|
64
|
-
closeButton={<div>✗</div>}
|
|
65
|
-
onClose={() => {
|
|
66
|
-
setModalCenter(false);
|
|
67
|
-
return true;
|
|
68
|
-
}}
|
|
69
|
-
>
|
|
70
|
-
<p>Center</p>
|
|
71
|
-
</PureModal>
|
|
72
|
-
|
|
73
|
-
<PureModal
|
|
74
|
-
header="Custom heading"
|
|
75
|
-
draggable
|
|
76
|
-
footer="Buttons?"
|
|
77
|
-
scrollable={false}
|
|
78
|
-
isOpen={modalDrag}
|
|
79
|
-
closeButtonPosition="bottom"
|
|
80
|
-
portal
|
|
81
|
-
closeButton={<div>✗</div>}
|
|
82
|
-
onClose={() => {
|
|
83
|
-
setModalDrag(false);
|
|
84
|
-
return true;
|
|
85
|
-
}}
|
|
86
|
-
>
|
|
87
|
-
<p>Center</p>
|
|
88
|
-
</PureModal>
|
|
89
|
-
|
|
90
|
-
<PureModal
|
|
91
|
-
header="Custom heading"
|
|
92
|
-
footer="Buttons?"
|
|
93
|
-
isOpen={modalNoScrollable}
|
|
94
|
-
closeButtonPosition="bottom"
|
|
95
|
-
scrollable={false}
|
|
96
|
-
portal
|
|
97
|
-
closeButton={<div>✗</div>}
|
|
98
|
-
onClose={() => {
|
|
99
|
-
setModalNoScrollable(false);
|
|
100
|
-
return true;
|
|
101
|
-
}}
|
|
102
|
-
>
|
|
103
|
-
{largeContent}
|
|
104
|
-
</PureModal>
|
|
105
|
-
</div>
|
|
106
|
-
);
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
render(<ModalContainer />, document.getElementById('js--modals'));
|
package/index.html
DELETED
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
<!DOCTYPE html>
|
|
2
|
-
<html lang="en">
|
|
3
|
-
<head>
|
|
4
|
-
<meta charset="utf-8" />
|
|
5
|
-
<meta http-equiv="x-ua-compatible" content="ie=edge" />
|
|
6
|
-
<title></title>
|
|
7
|
-
<title>Example 1</title>
|
|
8
|
-
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=0">
|
|
9
|
-
<style>
|
|
10
|
-
.button {
|
|
11
|
-
padding: 10px 15px;
|
|
12
|
-
background-color: #ffd34f;
|
|
13
|
-
border: none;
|
|
14
|
-
margin: 20px;
|
|
15
|
-
}
|
|
16
|
-
</style>
|
|
17
|
-
</head>
|
|
18
|
-
<body style="height: 150vh">
|
|
19
|
-
<div id="js--modals"></div>
|
|
20
|
-
<script src="./example/example.min.js"></script>
|
|
21
|
-
</body>
|
|
22
|
-
</html>
|
|
Binary file
|
package/screencast/simple.gif
DELETED
|
Binary file
|
|
@@ -1,74 +0,0 @@
|
|
|
1
|
-
import React from 'react';
|
|
2
|
-
|
|
3
|
-
import type { MouseOrTouch } from './types';
|
|
4
|
-
|
|
5
|
-
type Props = {
|
|
6
|
-
replace: boolean;
|
|
7
|
-
children: JSX.Element;
|
|
8
|
-
onDragStart?: (event: MouseOrTouch) => unknown;
|
|
9
|
-
onDragEnd?: (event: MouseOrTouch) => unknown;
|
|
10
|
-
onClose?: (event: React.MouseEvent<HTMLDivElement>) => void;
|
|
11
|
-
bodyClass?: string;
|
|
12
|
-
header?: JSX.Element | string;
|
|
13
|
-
footer?: JSX.Element | string;
|
|
14
|
-
closeButton: JSX.Element | string;
|
|
15
|
-
closeButtonPosition: string;
|
|
16
|
-
draggable: boolean;
|
|
17
|
-
};
|
|
18
|
-
|
|
19
|
-
const defaultProps = {
|
|
20
|
-
closeButton: '×',
|
|
21
|
-
closeButtonPosition: 'header',
|
|
22
|
-
replace: false,
|
|
23
|
-
draggable: false,
|
|
24
|
-
};
|
|
25
|
-
|
|
26
|
-
function PureModalContent(props: Props): JSX.Element {
|
|
27
|
-
const {
|
|
28
|
-
children,
|
|
29
|
-
replace,
|
|
30
|
-
bodyClass,
|
|
31
|
-
header,
|
|
32
|
-
footer,
|
|
33
|
-
onDragStart,
|
|
34
|
-
onDragEnd,
|
|
35
|
-
onClose,
|
|
36
|
-
closeButton,
|
|
37
|
-
closeButtonPosition,
|
|
38
|
-
} = props;
|
|
39
|
-
|
|
40
|
-
return replace ? (
|
|
41
|
-
children
|
|
42
|
-
) : (
|
|
43
|
-
<div
|
|
44
|
-
className={`panel panel-default ${closeButtonPosition === 'bottom' ? 'additional-row' : ''}`}
|
|
45
|
-
>
|
|
46
|
-
<div
|
|
47
|
-
className="panel-heading"
|
|
48
|
-
onTouchStart={onDragStart}
|
|
49
|
-
onMouseDown={onDragStart}
|
|
50
|
-
onTouchEnd={onDragEnd}
|
|
51
|
-
onMouseUp={onDragEnd}
|
|
52
|
-
>
|
|
53
|
-
{header && <h3 className="panel-title">{header}</h3>}
|
|
54
|
-
</div>
|
|
55
|
-
|
|
56
|
-
<div className={bodyClass}>{children}</div>
|
|
57
|
-
{footer && <div className="panel-footer">{footer}</div>}
|
|
58
|
-
<div
|
|
59
|
-
className="close"
|
|
60
|
-
onClick={onClose}
|
|
61
|
-
style={{
|
|
62
|
-
position: closeButtonPosition === 'header' ? 'absolute' : 'static',
|
|
63
|
-
margin: closeButtonPosition === 'bottom' ? '10px auto' : '',
|
|
64
|
-
}}
|
|
65
|
-
>
|
|
66
|
-
{closeButton}
|
|
67
|
-
</div>
|
|
68
|
-
</div>
|
|
69
|
-
);
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
PureModalContent.defaultProps = defaultProps;
|
|
73
|
-
|
|
74
|
-
export default PureModalContent;
|
package/src/react-pure-modal.css
DELETED
|
@@ -1,123 +0,0 @@
|
|
|
1
|
-
.body-modal-fix {
|
|
2
|
-
height: 100%;
|
|
3
|
-
width: 100%;
|
|
4
|
-
overflow: hidden;
|
|
5
|
-
}
|
|
6
|
-
|
|
7
|
-
.pure-modal-backdrop {
|
|
8
|
-
position: fixed;
|
|
9
|
-
top: 0;
|
|
10
|
-
right: 0;
|
|
11
|
-
bottom: 0;
|
|
12
|
-
left: 0;
|
|
13
|
-
z-index: 1040;
|
|
14
|
-
background-color: rgba(0, 0, 0, 0.4);
|
|
15
|
-
display: flex;
|
|
16
|
-
flex-direction: column;
|
|
17
|
-
justify-content: center;
|
|
18
|
-
align-items: center;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
.backdrop-overflow-hidden {
|
|
22
|
-
overflow: hidden !important;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
.pure-modal-backdrop .pure-modal {
|
|
26
|
-
width: 300px;
|
|
27
|
-
max-width: 100%;
|
|
28
|
-
box-sizing: border-box;
|
|
29
|
-
transition: all 0.2s ease-in-out;
|
|
30
|
-
max-height: 100%;
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
.pure-modal.auto-height {
|
|
34
|
-
position: static;
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
.pure-modal-backdrop.scrollable {
|
|
38
|
-
overflow-y: auto;
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
.pure-modal-backdrop .panel {
|
|
42
|
-
display: grid;
|
|
43
|
-
grid-template-rows: repeat(3, min-content);
|
|
44
|
-
}
|
|
45
|
-
.pure-modal-backdrop:not(.scrollable) .panel {
|
|
46
|
-
grid-template-rows: min-content minmax(0, 1fr) min-content;
|
|
47
|
-
max-height: -moz-available;
|
|
48
|
-
max-height: -webkit-fill-available;
|
|
49
|
-
max-height: fill-available;
|
|
50
|
-
height: 100%;
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
.pure-modal > * > * {
|
|
54
|
-
flex: 0 0 auto;
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
.pure-modal > * > .scrollable {
|
|
58
|
-
overflow-x: hidden;
|
|
59
|
-
overflow-scrolling: touch;
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
@media (max-width: 480px) {
|
|
63
|
-
.pure-modal-backdrop .pure-modal {
|
|
64
|
-
width: 100%;
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
.pure-modal .panel-body {
|
|
69
|
-
background-color: #fff;
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
.pure-modal .panel-heading {
|
|
73
|
-
background: rgb(240, 240, 240);
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
.pure-modal .panel-title {
|
|
77
|
-
padding: 12px 45px 12px 15px;
|
|
78
|
-
margin: 0;
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
.pure-modal .close {
|
|
82
|
-
right: 10px;
|
|
83
|
-
top: 10px;
|
|
84
|
-
z-index: 1;
|
|
85
|
-
background: rgba(240, 240, 240, 0.8);
|
|
86
|
-
width: 30px;
|
|
87
|
-
color: rgb(140, 140, 140);
|
|
88
|
-
transition: color ease-in-out 0.1s;
|
|
89
|
-
height: 30px;
|
|
90
|
-
border-radius: 15px;
|
|
91
|
-
text-align: center;
|
|
92
|
-
line-height: 30px;
|
|
93
|
-
cursor: pointer;
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
.pure-modal .panel-heading .close:hover {
|
|
97
|
-
color: #000;
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
.pure-modal .panel-body {
|
|
101
|
-
padding: 15px;
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
.pure-modal .panel-footer {
|
|
105
|
-
padding: 12px 45px 12px 15px;
|
|
106
|
-
background: rgb(240, 240, 240);
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
.pure-modal .panel-body,
|
|
110
|
-
.pure-modal .panel-footer,
|
|
111
|
-
.pure-modal .panel-title {
|
|
112
|
-
word-break: break-all;
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
.pure-modal-backdrop .additional-row {
|
|
116
|
-
display: grid;
|
|
117
|
-
grid-template-rows: min-content minmax(0, 1fr) min-content min-content;
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
.pure-modal-backdrop:not(.scrollable) .additional-row {
|
|
121
|
-
display: grid;
|
|
122
|
-
grid-template-rows: min-content minmax(0, 1fr) min-content min-content;
|
|
123
|
-
}
|
package/src/react-pure-modal.tsx
DELETED
|
@@ -1,256 +0,0 @@
|
|
|
1
|
-
import React, { useState, useEffect, useCallback, useId } from 'react';
|
|
2
|
-
import { createPortal } from 'react-dom';
|
|
3
|
-
|
|
4
|
-
// components
|
|
5
|
-
import PureModalContent from './pure-modal-content';
|
|
6
|
-
|
|
7
|
-
// types
|
|
8
|
-
import type { MouseOrTouch } from './types';
|
|
9
|
-
|
|
10
|
-
// styles
|
|
11
|
-
import './react-pure-modal.css';
|
|
12
|
-
|
|
13
|
-
type Props = {
|
|
14
|
-
children: JSX.Element;
|
|
15
|
-
replace?: boolean;
|
|
16
|
-
className?: string;
|
|
17
|
-
header?: JSX.Element | string;
|
|
18
|
-
footer?: JSX.Element | string;
|
|
19
|
-
scrollable?: boolean;
|
|
20
|
-
draggable?: boolean;
|
|
21
|
-
width?: string;
|
|
22
|
-
isOpen?: boolean;
|
|
23
|
-
onClose?: Function;
|
|
24
|
-
closeButton?: JSX.Element | string;
|
|
25
|
-
closeButtonPosition?: string;
|
|
26
|
-
portal?: boolean;
|
|
27
|
-
};
|
|
28
|
-
|
|
29
|
-
type CloseInteractionEvent = MouseOrTouch | KeyboardEvent;
|
|
30
|
-
|
|
31
|
-
function PureModal(props: Props) {
|
|
32
|
-
const hash = useId();
|
|
33
|
-
const [isDragged, setIsDragged] = useState(false);
|
|
34
|
-
const [x, setX] = useState<number | null>(null);
|
|
35
|
-
const [y, setY] = useState<number | null>(null);
|
|
36
|
-
const [deltaX, setDeltaX] = useState(0);
|
|
37
|
-
const [deltaY, setDeltaY] = useState(0);
|
|
38
|
-
const [mouseOffsetX, setMouseOffsetX] = useState(0);
|
|
39
|
-
const [mouseOffsetY, setMouseOffsetY] = useState(0);
|
|
40
|
-
|
|
41
|
-
const { isOpen, onClose } = props;
|
|
42
|
-
|
|
43
|
-
const removeClassBody = useCallback(() => {
|
|
44
|
-
document.body.classList.remove('body-modal-fix');
|
|
45
|
-
}, []);
|
|
46
|
-
|
|
47
|
-
/**
|
|
48
|
-
* useEffect to setup popup OR perform a cleanup after popup was closed
|
|
49
|
-
*/
|
|
50
|
-
useEffect(() => {
|
|
51
|
-
/**
|
|
52
|
-
* if popup was opened:
|
|
53
|
-
* - add esc event listener
|
|
54
|
-
* - add overflow fix class to body
|
|
55
|
-
* - remove focus from focused element (e.g. remove focus from btn after click, cuz popup was opened)
|
|
56
|
-
*/
|
|
57
|
-
if (isOpen) {
|
|
58
|
-
document.addEventListener('keydown', handleEsc);
|
|
59
|
-
if (document.activeElement instanceof HTMLElement) document.activeElement.blur();
|
|
60
|
-
document.body.classList.add('body-modal-fix');
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
return () => {
|
|
64
|
-
/**
|
|
65
|
-
* if popup was closed:
|
|
66
|
-
* - remove esc event listener
|
|
67
|
-
* - remove overflow fix class from body
|
|
68
|
-
* - reset popup states
|
|
69
|
-
*/
|
|
70
|
-
if (!document.querySelector('.pure-modal')) {
|
|
71
|
-
document.removeEventListener('keydown', handleEsc);
|
|
72
|
-
removeClassBody();
|
|
73
|
-
setX(null);
|
|
74
|
-
setY(null);
|
|
75
|
-
setDeltaX(0);
|
|
76
|
-
setDeltaY(0);
|
|
77
|
-
setMouseOffsetX(0);
|
|
78
|
-
setMouseOffsetY(0);
|
|
79
|
-
}
|
|
80
|
-
};
|
|
81
|
-
}, [isOpen]);
|
|
82
|
-
|
|
83
|
-
const handleEsc = useCallback(
|
|
84
|
-
(event: KeyboardEvent) => {
|
|
85
|
-
const allModals = document.querySelectorAll('.pure-modal');
|
|
86
|
-
const isManyModalsOpen = allModals.length > 1; // open modal in modal
|
|
87
|
-
const firstModalData = allModals[allModals.length - 1];
|
|
88
|
-
|
|
89
|
-
if (isManyModalsOpen && !firstModalData.className.includes(hash)) {
|
|
90
|
-
return false; // closing only current modal
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
if (event.key === 'Escape' && document.activeElement) {
|
|
94
|
-
close(event);
|
|
95
|
-
return true;
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
return false;
|
|
99
|
-
},
|
|
100
|
-
[close, hash],
|
|
101
|
-
);
|
|
102
|
-
|
|
103
|
-
if (!isOpen) {
|
|
104
|
-
return null;
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
/**
|
|
108
|
-
* method that will be called when some of the closing elements are beeing interacted with
|
|
109
|
-
*
|
|
110
|
-
* click on close btn, click on backdrop, press on esc
|
|
111
|
-
*/
|
|
112
|
-
function close(event?: CloseInteractionEvent) {
|
|
113
|
-
if (event) {
|
|
114
|
-
event.stopPropagation();
|
|
115
|
-
event.preventDefault();
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
onClose?.({ isPassive: Boolean(event) });
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
function getCoords(e: MouseOrTouch) {
|
|
122
|
-
if (e instanceof TouchEvent && e.changedTouches.length > 0) {
|
|
123
|
-
return {
|
|
124
|
-
pageX: e.changedTouches[0].pageX,
|
|
125
|
-
pageY: e.changedTouches[0].pageY,
|
|
126
|
-
};
|
|
127
|
-
}
|
|
128
|
-
if (e instanceof MouseEvent) {
|
|
129
|
-
return {
|
|
130
|
-
pageX: e.pageX,
|
|
131
|
-
pageY: e.pageY,
|
|
132
|
-
};
|
|
133
|
-
}
|
|
134
|
-
return {
|
|
135
|
-
pageX: 0,
|
|
136
|
-
pageY: 0,
|
|
137
|
-
};
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
function handleStartDrag(e: MouseOrTouch) {
|
|
141
|
-
if (e instanceof TouchEvent && e.changedTouches && e.changedTouches.length > 1) return;
|
|
142
|
-
|
|
143
|
-
e.preventDefault();
|
|
144
|
-
|
|
145
|
-
const { pageX, pageY } = getCoords(e);
|
|
146
|
-
const { top, left } = e.currentTarget.getBoundingClientRect();
|
|
147
|
-
|
|
148
|
-
setIsDragged(true);
|
|
149
|
-
setX(typeof x === 'number' ? x : left);
|
|
150
|
-
setY(typeof y === 'number' ? y : top);
|
|
151
|
-
setMouseOffsetX(pageX - left);
|
|
152
|
-
setMouseOffsetY(pageY - top);
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
function handleDrag(e: MouseOrTouch) {
|
|
156
|
-
if (e instanceof TouchEvent && e.changedTouches && e.changedTouches.length > 1) {
|
|
157
|
-
return handleEndDrag();
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
e.preventDefault();
|
|
161
|
-
|
|
162
|
-
const { pageX, pageY } = getCoords(e);
|
|
163
|
-
|
|
164
|
-
if (typeof x === 'number' && typeof y === 'number') {
|
|
165
|
-
setDeltaX(pageX - x - mouseOffsetX);
|
|
166
|
-
setDeltaY(pageY - y - mouseOffsetY);
|
|
167
|
-
}
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
function handleEndDrag() {
|
|
171
|
-
return setIsDragged(false);
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
function handleBackdropClick(event: MouseOrTouch) {
|
|
175
|
-
if (event) {
|
|
176
|
-
if (!(event.target as Element).classList.contains('pure-modal-backdrop')) {
|
|
177
|
-
return;
|
|
178
|
-
}
|
|
179
|
-
event.stopPropagation();
|
|
180
|
-
event.preventDefault();
|
|
181
|
-
}
|
|
182
|
-
close(event);
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
const {
|
|
186
|
-
children,
|
|
187
|
-
replace = false,
|
|
188
|
-
className,
|
|
189
|
-
header,
|
|
190
|
-
footer,
|
|
191
|
-
scrollable = true,
|
|
192
|
-
draggable = false,
|
|
193
|
-
width,
|
|
194
|
-
closeButton,
|
|
195
|
-
closeButtonPosition,
|
|
196
|
-
portal = false,
|
|
197
|
-
} = props;
|
|
198
|
-
|
|
199
|
-
let backdropclasses = ['pure-modal-backdrop'];
|
|
200
|
-
let modalclasses = ['pure-modal', hash];
|
|
201
|
-
let bodyClasses = ['panel-body'];
|
|
202
|
-
|
|
203
|
-
if (className) {
|
|
204
|
-
modalclasses = modalclasses.concat(className);
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
if (scrollable) {
|
|
208
|
-
bodyClasses = bodyClasses.concat('scrollable');
|
|
209
|
-
} else {
|
|
210
|
-
backdropclasses = backdropclasses.concat('scrollable');
|
|
211
|
-
modalclasses = modalclasses.concat('auto-height');
|
|
212
|
-
}
|
|
213
|
-
|
|
214
|
-
if (draggable) {
|
|
215
|
-
backdropclasses = backdropclasses.concat('backdrop-overflow-hidden');
|
|
216
|
-
}
|
|
217
|
-
|
|
218
|
-
const modalContent = (
|
|
219
|
-
<div
|
|
220
|
-
className={backdropclasses.join(' ')}
|
|
221
|
-
onMouseDown={handleBackdropClick}
|
|
222
|
-
onTouchMove={isDragged ? handleDrag : undefined}
|
|
223
|
-
onMouseMove={isDragged ? handleDrag : undefined}
|
|
224
|
-
>
|
|
225
|
-
<div
|
|
226
|
-
className={modalclasses.join(' ')}
|
|
227
|
-
style={{
|
|
228
|
-
transform: `translate(${deltaX}px, ${deltaY}px)`,
|
|
229
|
-
transition: 'none',
|
|
230
|
-
width,
|
|
231
|
-
}}
|
|
232
|
-
>
|
|
233
|
-
<PureModalContent
|
|
234
|
-
replace={replace}
|
|
235
|
-
header={header}
|
|
236
|
-
footer={footer}
|
|
237
|
-
onDragStart={draggable ? handleStartDrag : undefined}
|
|
238
|
-
onDragEnd={draggable ? handleEndDrag : undefined}
|
|
239
|
-
onClose={close}
|
|
240
|
-
bodyClass={bodyClasses.join(' ')}
|
|
241
|
-
closeButton={closeButton}
|
|
242
|
-
closeButtonPosition={closeButtonPosition}
|
|
243
|
-
>
|
|
244
|
-
{children}
|
|
245
|
-
</PureModalContent>
|
|
246
|
-
</div>
|
|
247
|
-
</div>
|
|
248
|
-
);
|
|
249
|
-
|
|
250
|
-
if (portal) {
|
|
251
|
-
return createPortal(modalContent, document.body);
|
|
252
|
-
}
|
|
253
|
-
return modalContent;
|
|
254
|
-
}
|
|
255
|
-
|
|
256
|
-
export default React.memo(PureModal);
|