UNPKG

4.63 kBJavaScriptView Raw
1
2import { onClickOutside, removeClickOutside, addEvent, removeEvent } from './utils/DOM';
3
4export const ModalConfig = {
5
6 tagName: 'modal',
7 tagNameBackdrop: 'fade',
8 tagNameCloser: 'button',
9 classNameShow: 'show',
10 classNameCloser: 'close',
11 classNameSlideOut: 'slide-top',
12 classNameSlideIn: 'slide-in-bottom',
13 classNameFadeOut: 'fade-out',
14 classNameFadeIn: 'fade-in',
15 classNameBodyNoScroll: 'no-scroll',
16 attrDataTarget: 'data-target',
17
18};
19
20export const ModalUI = {
21
22 Config: ModalConfig,
23
24 getAll(container = document) {
25 return container.getElementsByTagName(this.Config.tagName) || false;
26 },
27
28 getCloser(modal) {
29 return modal.getElementsByClassName(this.Config.classNameCloser)[0] || false;
30 },
31
32 createBackdrop() {
33 return document.createElement(this.Config.tagNameBackdrop);
34 },
35
36 wrapBackdropAroundModal(modal) {
37 const backdrop = this.createBackdrop();
38 backdrop.classList.add(this.Config.classNameFadeIn);
39 const parent = modal.parentNode;
40 parent.insertBefore(backdrop, modal);
41 backdrop.appendChild(modal);
42 },
43
44 unwrapBackdropFromModal(modal) {
45 const backdrop = modal.parentNode;
46 backdrop.classList.add(this.Config.classNameFadeOut);
47 backdrop.addEventListener('animationend', () => {
48 const parent = backdrop.parentNode;
49 parent.insertBefore(modal, backdrop);
50 parent.removeChild(backdrop);
51 });
52 },
53
54 getToggleBtn(modal) {
55 const id = modal.id;
56 if (id) {
57 return document.querySelector('[data-target="' + id + '"]') || false;
58 }
59 return false;
60 },
61
62 show(modal, prevFocusedElement) {
63 modal.setAttribute('tabindex', 0);
64 modal.__prev_focused = prevFocusedElement;
65 document.body.classList.add(this.Config.classNameBodyNoScroll);
66 modal.classList.add(this.Config.classNameShow);
67 modal.classList.add(this.Config.classNameSlideIn);
68 this.wrapBackdropAroundModal(modal);
69 setTimeout(() => {
70 modal.focus();
71 }, 0);
72 },
73
74 hide(modal) {
75 modal.removeAttribute('tabindex');
76 modal.__prev_focused.focus();
77 delete modal.__prev_focused;
78 modal.classList.add(this.Config.classNameSlideOut);
79 this.unwrapBackdropFromModal(modal);
80 const handler = () => {
81 modal.classList.remove(this.Config.classNameShow);
82 modal.classList.remove(this.Config.classNameSlideIn);
83 modal.classList.remove(this.Config.classNameSlideOut);
84 document.body.classList.remove(this.Config.classNameBodyNoScroll);
85 modal.removeEventListener('animationend', handler);
86 };
87 modal.addEventListener('animationend', handler);
88 }
89
90};
91
92export const Modal = {
93
94 Config: ModalConfig,
95 UI: ModalUI,
96
97 init(modal) {
98 this.addEvents(modal);
99 },
100
101 initAll(container = document) {
102 [].forEach.call(this.UI.getAll(container), modal => {
103 this.init(modal);
104 });
105 },
106
107 addEvents(modal) {
108 const btn = this.UI.getToggleBtn(modal);
109 if (btn) {
110 modal.__click_toggler = addEvent(btn, 'click', this.handlerShow.bind(this, modal));
111 }
112 },
113
114 handlerShow(modal, e) {
115 const btn = this.UI.getToggleBtn(modal);
116 if (btn) {
117 removeEvent(btn, 'click', modal.__click_toggler);
118 delete modal.__click_toggler;
119 }
120
121 const closer = this.UI.getCloser(modal);
122 if (closer) {
123 modal.__click_closer = addEvent(closer, 'click', this.handlerHide.bind(this, modal));
124 }
125
126 this.UI.show(modal, e.target);
127
128 setTimeout(() => {
129 modal.__click_outside = onClickOutside(modal, this.handlerHide.bind(this, modal));
130 }, 100);
131
132 if (modal.__on_show !== undefined) modal.__on_show.forEach(cb => cb());
133 },
134
135 handlerHide(modal) {
136 const btn = this.UI.getToggleBtn(modal);
137 if (btn) {
138 modal.__click_toggler = addEvent(btn, 'click', this.handlerShow.bind(this, modal));
139 }
140
141 const closer = this.UI.getCloser(modal);
142 if (closer) {
143 removeEvent(closer, 'click', modal.__click_closer);
144 delete modal.__click_closer;
145 }
146
147 this.UI.hide(modal);
148
149 removeClickOutside(modal, modal.__click_outside);
150 delete modal.__click_outside;
151
152 if (modal.__on_hide !== undefined) modal.__on_hide.forEach(cb => cb());
153 },
154
155 show(modal) {
156 this.handlerShow(modal, {target: document.activeElement});
157 },
158
159 hide(modal) {
160 this.handlerHide(modal);
161 },
162
163 onShow(modal, callback) {
164 if (modal.__on_show === undefined) modal.__on_show = [];
165 modal.__on_show.push(callback);
166 },
167
168 onHide(modal, callback) {
169 if (modal.__on_hide === undefined) modal.__on_hide = [];
170 modal.__on_hide.push(callback);
171 },
172
173};
174
175document.addEventListener('DOMContentLoaded', () => {
176 Modal.initAll();
177});