-
-
Notifications
You must be signed in to change notification settings - Fork 32.5k
/
Copy pathmodalManager.js
131 lines (104 loc) · 3.77 KB
/
modalManager.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
// Taken from https://github.com/react-bootstrap/react-overlays/blob/master/src/ModalManager.js
import warning from 'warning';
import isWindow from 'dom-helpers/query/isWindow';
import ownerDocument from 'dom-helpers/ownerDocument';
import canUseDom from 'dom-helpers/util/inDOM';
import getScrollbarSize from 'dom-helpers/util/scrollbarSize';
import { hideSiblings, showSiblings, ariaHidden } from '../utils/manageAriaHidden';
function getPaddingRight(node) {
return parseInt(node.style.paddingRight || 0, 10);
}
// Do we have a scroll bar?
function bodyIsOverflowing(node) {
const doc = ownerDocument(node);
const win = isWindow(doc);
// Takes in account potential non zero margin on the body.
const style = window.getComputedStyle(doc.body);
const marginLeft = parseInt(style.getPropertyValue('margin-left'), 10);
const marginRight = parseInt(style.getPropertyValue('margin-right'), 10);
return marginLeft + doc.body.clientWidth + marginRight < win.innerWidth;
}
function getContainer() {
const container = canUseDom ? window.document.body : {};
warning(
container !== null,
`
Material-UI: you are most likely evaluating the code before the
browser has a chance to reach the <body>.
Please move the import at the end of the <body>.
`,
);
return container;
}
/**
* State management helper for modals/layers.
* Simplified, but inspired by react-overlay's ModalManager class
*
* @internal Used by the Modal to ensure proper focus management.
*/
function createModalManager({ hideSiblingNodes = true }: Object = {}) {
const modals = [];
let prevOverflow;
let prevPaddings = [];
function add(modal: Object) {
const container = getContainer();
let modalIdx = modals.indexOf(modal);
if (modalIdx !== -1) {
return modalIdx;
}
modalIdx = modals.length;
modals.push(modal);
if (hideSiblingNodes) {
hideSiblings(container, modal.mountNode);
}
if (modals.length === 1) {
// Save our current overflow so we can revert
// back to it when all modals are closed!
prevOverflow = container.style.overflow;
if (bodyIsOverflowing(container)) {
prevPaddings = [getPaddingRight(container)];
const scrollbarSize = getScrollbarSize();
container.style.paddingRight = `${prevPaddings[0] + scrollbarSize}px`;
const fixedNodes = document.querySelectorAll('.mui-fixed');
for (let i = 0; i < fixedNodes.length; i += 1) {
const paddingRight = getPaddingRight(fixedNodes[i]);
prevPaddings.push(paddingRight);
fixedNodes[i].style.paddingRight = `${paddingRight + scrollbarSize}px`;
}
}
container.style.overflow = 'hidden';
}
return modalIdx;
}
function remove(modal: Object) {
const container = getContainer();
const modalIdx = modals.indexOf(modal);
if (modalIdx === -1) {
return modalIdx;
}
modals.splice(modalIdx, 1);
if (modals.length === 0) {
container.style.overflow = prevOverflow;
container.style.paddingRight = prevPaddings[0];
const fixedNodes = document.querySelectorAll('.mui-fixed');
for (let i = 0; i < fixedNodes.length; i += 1) {
fixedNodes[i].style.paddingRight = `${prevPaddings[i + 1]}px`;
}
prevOverflow = undefined;
prevPaddings = [];
if (hideSiblingNodes) {
showSiblings(container, modal.mountNode);
}
} else if (hideSiblingNodes) {
// otherwise make sure the next top modal is visible to a SR
ariaHidden(false, modals[modals.length - 1].mountNode);
}
return modalIdx;
}
function isTopModal(modal: Object) {
return !!modals.length && modals[modals.length - 1] === modal;
}
const modalManager = { add, remove, isTopModal };
return modalManager;
}
export default createModalManager;