-
-
Notifications
You must be signed in to change notification settings - Fork 133
/
modal.js
147 lines (114 loc) · 4.86 KB
/
modal.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
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
// Visit The Stimulus Handbook for more details
// https://stimulusjs.org/handbook/introduction
//
// This example controller works with specially annotated HTML like:
//
//<div data-controller="modal" data-modal-allow-background-close="false">
//<a href="#" data-action="click->modal#open" class="bg-blue-500 hover:bg-blue-700 text-white font-bold py-3 px-6 rounded">
//<span>Open Modal</span>
//</a>
//<!-- Modal Container -->
//<div data-target="modal.container" data-action="click->modal#closeBackground keyup@window->modal#closeWithKeyboard" class="hidden animated fadeIn fixed inset-0 overflow-y-auto flex items-center justify-center" style="z-index: 9999;">
//<!-- Modal Inner Container -->
//<div class="max-h-screen w-full max-w-lg relative">
//<!-- Modal Card -->
//<div class="m-1 bg-white rounded shadow">
//<div class="p-8">
//<h2 class="text-xl mb-4">Large Modal Content</h2>
//<p class="mb-4">This is an example modal dialog box.</p>
//<div class="flex justify-end items-center flex-wrap mt-6">
//<button class="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded" data-action="click->modal#close">Close</button>
//</div>
//</div>
//</div>
//</div>
//</div>
//</div>
import { Controller } from 'stimulus';
export default class extends Controller {
static targets = ['container'];
connect() {
// The class we should toggle on the container
this.toggleClass = this.data.get('class') || 'hidden';
// The HTML for the background element
this.backgroundHtml = this.data.get('backgroundHtml') || this._backgroundHTML();
// The ID of the background to hide/remove
this.backgroundId = this.data.get('backgroundId') || 'modal-background';
// Let the user close the modal by clicking on the background
this.allowBackgroundClose = (this.data.get('allowBackgroundClose') || 'true') === 'true';
// Prevent the default action of the clicked element (following a link for example) when opening the modal
this.preventDefaultActionOpening = (this.data.get('preventDefaultActionOpening') || 'true') === 'true';
// Prevent the default action of the clicked element (following a link for example) when closing the modal
this.preventDefaultActionClosing = (this.data.get('preventDefaultActionClosing') || 'true') === 'true';
}
disconnect() {
this.close();
}
open(e) {
if (this.preventDefaultActionOpening) {
e.preventDefault();
}
e.target.blur();
// Lock the scroll and save current scroll position
this.lockScroll();
// Unhide the modal
this.containerTarget.classList.remove(this.toggleClass);
// Insert the background
if (!this.data.get("disable-backdrop")) {
document.body.insertAdjacentHTML('beforeend', this.backgroundHtml);
this.background = document.querySelector(`#${this.backgroundId}`);
}
}
close(e) {
if (e && this.preventDefaultActionClosing) {
e.preventDefault();
}
// Unlock the scroll and restore previous scroll position
this.unlockScroll();
// Hide the modal
this.containerTarget.classList.add(this.toggleClass);
// Remove the background
if (this.background) { this.background.remove() }
}
closeBackground(e) {
if (this.allowBackgroundClose && e.target === this.containerTarget) {
this.close(e);
}
}
closeWithKeyboard(e) {
if (e.keyCode === 27 && !this.containerTarget.classList.contains(this.toggleClass)) {
this.close(e);
}
}
_backgroundHTML() {
return '<div id="modal-background" class="fixed top-0 left-0 w-full h-full" style="background-color: rgba(0, 0, 0, 0.8); z-index: 9998;"></div>';
}
lockScroll() {
// Add right padding to the body so the page doesn't shift
// when we disable scrolling
const scrollbarWidth = window.innerWidth - document.documentElement.clientWidth;
document.body.style.paddingRight = `${scrollbarWidth}px`;
// Save the scroll position
this.saveScrollPosition();
// Add classes to body to fix its position
document.body.classList.add('fixed', 'inset-x-0', 'overflow-hidden');
// Add negative top position in order for body to stay in place
document.body.style.top = `-${this.scrollPosition}px`;
}
unlockScroll() {
// Remove tweaks for scrollbar
document.body.style.paddingRight = null;
// Remove classes from body to unfix position
document.body.classList.remove('fixed', 'inset-x-0', 'overflow-hidden');
// Restore the scroll position of the body before it got locked
this.restoreScrollPosition();
// Remove the negative top inline style from body
document.body.style.top = null;
}
saveScrollPosition() {
this.scrollPosition = window.pageYOffset || document.body.scrollTop;
}
restoreScrollPosition() {
document.documentElement.scrollTop = this.scrollPosition;
}
}