1 | /* |
||
2 | * Modal |
||
3 | * |
||
4 | * Pico.css - https://picocss.com |
||
5 | * Copyright 2019-2022 - Licensed under MIT |
||
6 | */ |
||
7 | |||
8 | /** global: screen */ |
||
9 | |||
10 | // Config |
||
11 | const isOpenClass = 'modal-is-open'; |
||
12 | const openingClass = 'modal-is-opening'; |
||
13 | const closingClass = 'modal-is-closing'; |
||
14 | const animationDuration = 400; // ms |
||
15 | let visibleModal = null; |
||
16 | |||
17 | |||
18 | // Toggle modal |
||
19 | const toggleModal = event => { |
||
0 ignored issues
–
show
Unused Code
introduced
by
![]() |
|||
20 | event.preventDefault(); |
||
21 | const modal = document.getElementById(event.currentTarget.getAttribute('data-target')); |
||
22 | (typeof(modal) !== 'undefined' && modal !== null) |
||
23 | && isModalOpen(modal) ? closeModal(modal) : openModal(modal) |
||
24 | } |
||
25 | |||
26 | // Is modal open |
||
27 | const isModalOpen = modal => { |
||
0 ignored issues
–
show
|
|||
28 | return modal.hasAttribute('open') && modal.getAttribute('open') != 'false' ? true : false; |
||
29 | } |
||
30 | |||
31 | // Open modal |
||
32 | const openModal = modal => { |
||
0 ignored issues
–
show
|
|||
33 | if (isScrollbarVisible()) { |
||
34 | document.documentElement.style.setProperty('--scrollbar-width', `${getScrollbarWidth()}px`); |
||
35 | } |
||
36 | document.documentElement.classList.add(isOpenClass, openingClass); |
||
37 | setTimeout(() => { |
||
38 | visibleModal = modal; |
||
39 | document.documentElement.classList.remove(openingClass); |
||
40 | }, animationDuration); |
||
41 | modal.setAttribute('open', true); |
||
42 | } |
||
43 | |||
44 | // Close modal |
||
45 | const closeModal = modal => { |
||
46 | visibleModal = null; |
||
47 | document.documentElement.classList.add(closingClass); |
||
48 | setTimeout(() => { |
||
49 | document.documentElement.classList.remove(closingClass, isOpenClass); |
||
50 | document.documentElement.style.removeProperty('--scrollbar-width'); |
||
51 | modal.removeAttribute('open'); |
||
52 | }, animationDuration); |
||
53 | } |
||
54 | |||
55 | // Close with a click outside |
||
56 | document.addEventListener('click', event => { |
||
57 | if (visibleModal !== null) { |
||
58 | const modalContent = visibleModal.querySelector('article'); |
||
59 | const isClickInside = modalContent.contains(event.target); |
||
60 | !isClickInside && closeModal(visibleModal); |
||
61 | } |
||
62 | }); |
||
63 | |||
64 | // Close with Esc key |
||
65 | document.addEventListener('keydown', event => { |
||
66 | if (event.key === 'Escape' && visibleModal !== null) { |
||
67 | closeModal(visibleModal); |
||
68 | } |
||
69 | }); |
||
70 | |||
71 | // Get scrollbar width |
||
72 | const getScrollbarWidth = () => { |
||
0 ignored issues
–
show
|
|||
73 | |||
74 | // Creating invisible container |
||
75 | const outer = document.createElement('div'); |
||
76 | outer.style.visibility = 'hidden'; |
||
77 | outer.style.overflow = 'scroll'; // forcing scrollbar to appear |
||
78 | outer.style.msOverflowStyle = 'scrollbar'; // needed for WinJS apps |
||
79 | document.body.appendChild(outer); |
||
80 | |||
81 | // Creating inner element and placing it in the container |
||
82 | const inner = document.createElement('div'); |
||
83 | outer.appendChild(inner); |
||
84 | |||
85 | // Calculating difference between container's full width and the child width |
||
86 | const scrollbarWidth = (outer.offsetWidth - inner.offsetWidth); |
||
87 | |||
88 | // Removing temporary elements from the DOM |
||
89 | outer.parentNode.removeChild(outer); |
||
90 | |||
91 | return scrollbarWidth; |
||
92 | } |
||
93 | |||
94 | // Is scrollbar visible |
||
95 | const isScrollbarVisible = () => { |
||
0 ignored issues
–
show
|
|||
96 | return document.body.scrollHeight > screen.height; |
||
97 | } |
||
98 |