mirror of
https://github.com/picocss/pico.git
synced 2025-04-23 18:06:14 -04:00
Modal: Delete blur background and handle scrollbar
This commit is contained in:
parent
3d2bc1f911
commit
d9a6ac1a65
24 changed files with 221 additions and 390 deletions
|
@ -404,6 +404,9 @@ body > nav {
|
|||
background-color: var(--nav-background-color);
|
||||
box-shadow: 0px 1px 0 var(--nav-border-color);
|
||||
}
|
||||
body > nav.container-fluid {
|
||||
padding-right: calc(var(--spacing) + var(--scrollbar-width, 0px));
|
||||
}
|
||||
body > nav a {
|
||||
border-radius: 0;
|
||||
}
|
||||
|
@ -437,7 +440,7 @@ body > nav ul:first-of-type li:nth-of-type(2) {
|
|||
*/
|
||||
.switcher {
|
||||
position: fixed;
|
||||
right: calc(var(--spacing) / 2);
|
||||
right: calc(var(--spacing) / 2 + var(--scrollbar-width, 0px));
|
||||
bottom: var(--spacing);
|
||||
width: auto;
|
||||
margin-bottom: 0;
|
||||
|
@ -484,7 +487,7 @@ body > nav ul:first-of-type li:nth-of-type(2) {
|
|||
}
|
||||
@media (min-width: 576px) {
|
||||
.switcher {
|
||||
right: var(--spacing);
|
||||
right: calc(var(--spacing) + var(--scrollbar-width, 0px));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
File diff suppressed because one or more lines are too long
2
docs/css/pico.docs.min.css
vendored
2
docs/css/pico.docs.min.css
vendored
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -9,8 +9,10 @@
|
|||
const isOpenClass = 'modal-is-open';
|
||||
const openingClass = 'modal-is-opening';
|
||||
const closingClass = 'modal-is-closing';
|
||||
const animationDuration = 400; // ms
|
||||
let visibleModal = null;
|
||||
|
||||
|
||||
// Toggle modal
|
||||
const toggleModal = event => {
|
||||
event.preventDefault();
|
||||
|
@ -26,11 +28,14 @@ const isModalOpen = modal => {
|
|||
|
||||
// Open modal
|
||||
const openModal = modal => {
|
||||
if (isScrollbarVisible()) {
|
||||
document.documentElement.style.setProperty('--scrollbar-width', `${getScrollbarWidth()}px`);
|
||||
}
|
||||
document.documentElement.classList.add(isOpenClass, openingClass);
|
||||
setTimeout(() => {
|
||||
visibleModal = modal;
|
||||
document.documentElement.classList.remove(openingClass);
|
||||
}, 200);
|
||||
}, animationDuration);
|
||||
modal.setAttribute('open', true);
|
||||
}
|
||||
|
||||
|
@ -40,8 +45,9 @@ const closeModal = modal => {
|
|||
document.documentElement.classList.add(closingClass);
|
||||
setTimeout(() => {
|
||||
document.documentElement.classList.remove(closingClass, isOpenClass);
|
||||
document.documentElement.style.removeProperty('--scrollbar-width');
|
||||
modal.removeAttribute('open');
|
||||
}, 200);
|
||||
}, animationDuration);
|
||||
}
|
||||
|
||||
// Close with a click outside
|
||||
|
@ -58,4 +64,32 @@ document.addEventListener('keydown', event => {
|
|||
if (event.key === 'Escape' && visibleModal != null) {
|
||||
closeModal(visibleModal);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// Get scrollbar width
|
||||
const getScrollbarWidth = () => {
|
||||
|
||||
// Creating invisible container
|
||||
const outer = document.createElement('div');
|
||||
outer.style.visibility = 'hidden';
|
||||
outer.style.overflow = 'scroll'; // forcing scrollbar to appear
|
||||
outer.style.msOverflowStyle = 'scrollbar'; // needed for WinJS apps
|
||||
document.body.appendChild(outer);
|
||||
|
||||
// Creating inner element and placing it in the container
|
||||
const inner = document.createElement('div');
|
||||
outer.appendChild(inner);
|
||||
|
||||
// Calculating difference between container's full width and the child width
|
||||
const scrollbarWidth = (outer.offsetWidth - inner.offsetWidth);
|
||||
|
||||
// Removing temporary elements from the DOM
|
||||
outer.parentNode.removeChild(outer);
|
||||
|
||||
return scrollbarWidth;
|
||||
}
|
||||
|
||||
// Is scrollbar visible
|
||||
const isScrollbarVisible = () => {
|
||||
return document.body.scrollHeight > screen.height;
|
||||
}
|
2
docs/js/modal.min.js
vendored
2
docs/js/modal.min.js
vendored
|
@ -1 +1 @@
|
|||
"use strict";var isOpenClass="modal-is-open",openingClass="modal-is-opening",closingClass="modal-is-closing",visibleModal=null,toggleModal=function(e){e.preventDefault();e=document.getElementById(e.target.getAttribute("data-target"));(void 0!==e&&null!=e&&isModalOpen(e)?closeModal:openModal)(e)},isModalOpen=function(e){return!(!e.hasAttribute("open")||"false"==e.getAttribute("open"))},openModal=function(e){document.documentElement.classList.add(isOpenClass,openingClass),setTimeout(function(){visibleModal=e,document.documentElement.classList.remove(openingClass)},200),e.setAttribute("open",!0)},closeModal=function(e){visibleModal=null,document.documentElement.classList.add(closingClass),setTimeout(function(){document.documentElement.classList.remove(closingClass,isOpenClass),e.removeAttribute("open")},200)};document.addEventListener("click",function(e){null!=visibleModal&&(visibleModal.querySelector("article").contains(e.target)||closeModal(visibleModal))}),document.addEventListener("keydown",function(e){"Escape"===e.key&&null!=visibleModal&&closeModal(visibleModal)});
|
||||
"use strict";var isOpenClass="modal-is-open",openingClass="modal-is-opening",closingClass="modal-is-closing",animationDuration=400,visibleModal=null,toggleModal=function(e){e.preventDefault();e=document.getElementById(e.target.getAttribute("data-target"));(void 0!==e&&null!=e&&isModalOpen(e)?closeModal:openModal)(e)},isModalOpen=function(e){return!(!e.hasAttribute("open")||"false"==e.getAttribute("open"))},openModal=function(e){isScrollbarVisible()&&document.documentElement.style.setProperty("--scrollbar-width","".concat(getScrollbarWidth(),"px")),document.documentElement.classList.add(isOpenClass,openingClass),setTimeout(function(){visibleModal=e,document.documentElement.classList.remove(openingClass)},animationDuration),e.setAttribute("open",!0)},closeModal=function(e){visibleModal=null,document.documentElement.classList.add(closingClass),setTimeout(function(){document.documentElement.classList.remove(closingClass,isOpenClass),document.documentElement.style.removeProperty("--scrollbar-width"),e.removeAttribute("open")},animationDuration)};document.addEventListener("click",function(e){null!=visibleModal&&(visibleModal.querySelector("article").contains(e.target)||closeModal(visibleModal))}),document.addEventListener("keydown",function(e){"Escape"===e.key&&null!=visibleModal&&closeModal(visibleModal)});var getScrollbarWidth=function(){var e=document.createElement("div");e.style.visibility="hidden",e.style.overflow="scroll",e.style.msOverflowStyle="scrollbar",document.body.appendChild(e);var t=document.createElement("div");e.appendChild(t);t=e.offsetWidth-t.offsetWidth;return e.parentNode.removeChild(e),t},isScrollbarVisible=function(){return document.body.scrollHeight>screen.height};
|
87
docs/modal copy.html
Normal file
87
docs/modal copy.html
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -13,6 +13,10 @@ body > nav {
|
|||
background-color: var(--nav-background-color);
|
||||
box-shadow: 0px 1px 0 var(--nav-border-color);
|
||||
|
||||
&.container-fluid {
|
||||
padding-right: calc(var(--spacing) + var(--scrollbar-width, 0px));
|
||||
}
|
||||
|
||||
a {
|
||||
border-radius: 0;
|
||||
}
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
|
||||
.switcher {
|
||||
position: fixed;
|
||||
right: calc(var(--spacing) / 2);
|
||||
right: calc(var(--spacing) / 2 + var(--scrollbar-width, 0px));
|
||||
bottom: var(--spacing);
|
||||
width: auto;
|
||||
margin-bottom: 0;
|
||||
|
@ -66,6 +66,6 @@
|
|||
}
|
||||
|
||||
@media (min-width: map-get($breakpoints, "sm")) {
|
||||
right: var(--spacing);
|
||||
right: calc(var(--spacing) + var(--scrollbar-width, 0px));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,170 +20,11 @@
|
|||
<h1>Modal</h1>
|
||||
<h2>The classic modal element with graceful spacings across devices and viewports.</h2>
|
||||
</hgroup>
|
||||
|
||||
<p>Modals are built with <code><<b>dialog</b>></code> as a wrapper and <code><<b>article</b>></code> as a content container.</p>
|
||||
<p>Inside <code><<b>header</b>></code>, <code><<b>a</b> <i>class</i>=<u>"close"</u>></code> is defined to <code><i>float</i>: <u>right</u>;</code> allowing a close icon to be top aligned with a title.</p>
|
||||
|
||||
<dialog class="example" open>
|
||||
<article>
|
||||
<header>
|
||||
<a href="#close" aria-label="Close" class="close" onclick="event.preventDefault()"></a>
|
||||
<p>Modal title</p>
|
||||
</header>
|
||||
<p>
|
||||
Nunc nec ligula a tortor sollicitudin dictum in vel enim.
|
||||
Quisque facilisis turpis vel eros dictum aliquam et nec turpis.
|
||||
Sed eleifend a dui nec ullamcorper.
|
||||
Praesent vehicula lacus ac justo accumsan ullamcorper.
|
||||
</p>
|
||||
</article>
|
||||
</dialog>
|
||||
|
||||
<pre><code><<b>dialog</b> <i>open</i>>
|
||||
<<b>article</b>>
|
||||
<<b>header</b>>
|
||||
<<b>a</b> <i>href</i>=<u>"#close"</u> <i>aria-label</i>=<u>"Close"</u> <i>class</i>=<u>"close"</u>></<b>a</b>>
|
||||
Modal title
|
||||
</<b>header</b>>
|
||||
<<b>p</b>>
|
||||
Nunc nec ligula a tortor sollicitudin dictum in vel enim.
|
||||
Quisque facilisis turpis vel eros dictum aliquam et nec turpis.
|
||||
Sed eleifend a dui nec ullamcorper.
|
||||
Praesent vehicula lacus ac justo accumsan ullamcorper.
|
||||
</<b>p</b>>
|
||||
</<b>article</b>>
|
||||
</<b>dialog</b>></code></pre>
|
||||
|
||||
<p>Inside <code><<b>footer</b>></code>, the content is right aligned by default.</p>
|
||||
|
||||
<dialog class="example" open>
|
||||
<article>
|
||||
<h3>Confirm your action!</h3>
|
||||
<p>
|
||||
Mauris non nibh vel nisi sollicitudin malesuada.
|
||||
Donec ut sagittis erat. Praesent eu eros felis.
|
||||
Ut consectetur placerat pulvinar.
|
||||
</p>
|
||||
<footer>
|
||||
<a href="#cancel" role="button" class="secondary" onclick="event.preventDefault()">Cancel</a><a href="#confirm" role="button" onclick="event.preventDefault()">Confirm</a>
|
||||
</footer>
|
||||
</article>
|
||||
</dialog>
|
||||
|
||||
|
||||
<pre><code><<b>dialog</b> <i>open</i>>
|
||||
<<b>article</b>>
|
||||
<<b>h3</b>>Confirm your action!</<b>h3</b>>
|
||||
<<b>p</b>>
|
||||
Mauris non nibh vel nisi sollicitudin malesuada.
|
||||
Donec ut sagittis erat. Praesent eu eros felis.
|
||||
Ut consectetur placerat pulvinar.
|
||||
</<b>p</b>>
|
||||
<<b>footer</b>>
|
||||
<<b>a</b> <i>href</i>=<u>"#cancel"</u> <i>role</i>=<u>"button"</u> <i>class</i>=<u>"secondary"</u>>Cancel</<b>a</b>>
|
||||
<<b>a</b> <i>href</i>=<u>"#confirm"</u> <i>role</i>=<u>"button"</u>>Confirm</<b>a</b>>
|
||||
</<b>footer</b>>
|
||||
</<b>article</b>>
|
||||
</<b>dialog</b>></code></pre>
|
||||
|
||||
<hgroup>
|
||||
<h2>Live demo</h2>
|
||||
<h3>Toggle a modal by clicking the button below.</h3>
|
||||
</hgroup>
|
||||
|
||||
|
||||
<article>
|
||||
<button class="contrast" data-target="modal-example" onclick="toggleModal(event)">Launch demo modal</button>
|
||||
<footer class="code">
|
||||
|
||||
<pre><code><em><!-- Button to trigger the modal --></em>
|
||||
<<b>button</b> <i>class</i>=<u>"contrast"</u>
|
||||
<i>data-target</i>=<u>"modal-example"</u>
|
||||
<i>onClick</i>=<u>"toggleModal()"</u>>
|
||||
Launch demo modal
|
||||
</<b>button</b>>
|
||||
|
||||
<em><!-- Modal --></em>
|
||||
<<b>dialog</b> <i>id</i>=<u>"modal-example"</u>>
|
||||
<<b>article</b>>
|
||||
<<b>a</b> <i>href</i>=<u>"#close"</u>
|
||||
<i>aria-label</i>=<u>"Close"</u>>
|
||||
<i>class</i>=<u>"close"</u>
|
||||
<i>data-target</i>=<u>"modal-example"</u>
|
||||
<i>onClick</i>=<u>"toggleModal()"</u>>
|
||||
</<b>a</b>>
|
||||
<<b>h3</b>>Confirm your action!</<b>h3</b>>
|
||||
<<b>p</b>>
|
||||
Cras sit amet maximus risus.
|
||||
Pellentesque sodales odio sit amet augue finibus pellentesque.
|
||||
Nullam finibus risus non semper euismod.
|
||||
</<b>p</b>>
|
||||
<<b>footer</b>>
|
||||
<<b>a</b> <i>href</i>=<u>"#cancel"</u>
|
||||
<i>role</i>=<u>"button"</u>>
|
||||
<i>class</i>=<u>"secondary"</u>
|
||||
<i>data-target</i>=<u>"modal-example"</u>
|
||||
<i>onClick</i>=<u>"toggleModal()"</u>>
|
||||
Cancel
|
||||
</<b>a</b>>
|
||||
<<b>a</b> <i>href</i>=<u>"#confirm"</u>
|
||||
<i>role</i>=<u>"button"</u>
|
||||
<i>data-target</i>=<u>"modal-example"</u>
|
||||
<i>onClick</i>=<u>"toggleModal()"</u>>
|
||||
Confirm
|
||||
</<b>a</b>>
|
||||
</<b>footer</b>>
|
||||
</<b>article</b>>
|
||||
</<b>dialog</b>></code></pre>
|
||||
|
||||
</footer>
|
||||
</article>
|
||||
|
||||
<p>Pico does not include JavaScript code. You will need to implement your JS to interact with modals.</p>
|
||||
|
||||
<p>As a starting point, you can look at the JavaScript used in this documentation: <a href="https://github.com/picocss/pico/blob/dev/docs/js/modal.js">js/modal.js</a>.</p>
|
||||
|
||||
<p>To make a modal appear, add the <code><i>open</i></code> attribute to the <code><<b>dialog</b></u>></code> container.</p>
|
||||
|
||||
<pre><code><em><!-- Open modal--></em>
|
||||
<<b>dialog</b> <i>open</i>>
|
||||
<<b>article</b>>
|
||||
<em>…</em>
|
||||
</<b>article</b>>
|
||||
</<b>dialog</b>>
|
||||
|
||||
<em><!-- Modal closed --></em>
|
||||
<<b>dialog</b>>
|
||||
<<b>article</b>>
|
||||
<em>…</em>
|
||||
</<b>article</b>>
|
||||
</<b>dialog</b>>
|
||||
</code></pre>
|
||||
|
||||
<h2>Utilities</h2>
|
||||
|
||||
<p>Modals come with 3 utility classes.</p>
|
||||
|
||||
<p><code>.modal-is-open</code> blurs the background and blocks any scrolling and interactions below the modal.</p>
|
||||
|
||||
<pre><code><em><!doctype html></em>
|
||||
<<b>html</b> <i>class</i>=<u>"modal-is-open"</u>>
|
||||
<em>…</em>
|
||||
</<b>html</b>></code></pre>
|
||||
|
||||
<p><code>.modal-is-opening</code> brings an opening animation.</p>
|
||||
|
||||
<pre><code><em><!doctype html></em>
|
||||
<<b>html</b> <i>class</i>=<u>"modal-is-open modal-is-opening"</u>>
|
||||
<em>…</em>
|
||||
</<b>html</b>></code></pre>
|
||||
|
||||
<p><code>.modal-is-closing</code> brings a closing animation.</p>
|
||||
|
||||
<pre><code><em><!doctype html></em>
|
||||
<<b>html</b> <i>class</i>=<u>"modal-is-open modal-is-closing"</u>>
|
||||
<em>…</em>
|
||||
</<b>html</b>></code></pre>
|
||||
|
||||
</section>
|
||||
|
||||
${require('./_footer.html')}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue