diff --git a/bookwyrm/static/css/bookwyrm.css b/bookwyrm/static/css/bookwyrm.css
index 43a59a62..3c8386dc 100644
--- a/bookwyrm/static/css/bookwyrm.css
+++ b/bookwyrm/static/css/bookwyrm.css
@@ -93,6 +93,9 @@ body {
display: inline !important;
}
+/** File input styles
+ ******************************************************************************/
+
input[type=file]::file-selector-button {
-moz-appearance: none;
-webkit-appearance: none;
@@ -119,17 +122,11 @@ input[type=file]::file-selector-button:hover {
color: #363636;
}
-details .dropdown-menu {
- display: block !important;
-}
+/** General `details` element styles
+ ******************************************************************************/
-details.dropdown[open] summary.dropdown-trigger::before {
- content: "";
- position: fixed;
- top: 0;
- bottom: 0;
- left: 0;
- right: 0;
+summary::-webkit-details-marker {
+ display: none;
}
summary::marker {
@@ -151,6 +148,57 @@ summary {
margin-top: 1em;
}
+/** Details dropdown
+ ******************************************************************************/
+
+details.dropdown[open] summary.dropdown-trigger::before {
+ content: "";
+ position: fixed;
+ top: 0;
+ bottom: 0;
+ left: 0;
+ right: 0;
+}
+
+details .dropdown-menu {
+ display: block !important;
+}
+
+details .dropdown-menu button {
+ /* Fix weird Safari defaults */
+ box-sizing: border-box;
+}
+
+details.dropdown .dropdown-menu button:focus-visible,
+details.dropdown .dropdown-menu a:focus-visible {
+ outline-style: auto;
+ outline-offset: -2px;
+}
+
+@media only screen and (max-width: 768px) {
+ details.dropdown[open] summary.dropdown-trigger::before {
+ background-color: rgba(0, 0, 0, 0.5);
+ z-index: 30;
+ }
+
+ details .dropdown-menu {
+ position: fixed;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 100%;
+ display: flex !important;
+ align-items: center;
+ justify-content: center;
+ pointer-events: none;
+ z-index: 100;
+ }
+
+ details .dropdown-menu > * {
+ pointer-events: all;
+ }
+}
+
/** Shelving
******************************************************************************/
diff --git a/bookwyrm/static/js/bookwyrm.js b/bookwyrm/static/js/bookwyrm.js
index 2e446a85..c79471fe 100644
--- a/bookwyrm/static/js/bookwyrm.js
+++ b/bookwyrm/static/js/bookwyrm.js
@@ -51,6 +51,12 @@ let BookWyrm = new class {
'click',
this.duplicateInput.bind(this)
+ ))
+ document.querySelectorAll('details.dropdown')
+ .forEach(node => node.addEventListener(
+ 'toggle',
+ this.handleDetailsDropdown.bind(this)
+
))
}
@@ -480,4 +486,101 @@ let BookWyrm = new class {
textareaEl.parentNode.appendChild(copyButtonEl)
}
+
+ /**
+ * Handle the details dropdown component.
+ *
+ * @param {Event} event - Event fired by a `details` element
+ * with the `dropdown` class name, on toggle.
+ * @return {undefined}
+ */
+ handleDetailsDropdown(event) {
+ const detailsElement = event.target;
+ const summaryElement = detailsElement.querySelector('summary');
+ const menuElement = detailsElement.querySelector('.dropdown-menu');
+ const htmlElement = document.querySelector('html');
+
+ if (detailsElement.open) {
+ // Focus first menu element
+ menuElement.querySelectorAll(
+ 'a[href]:not([disabled]), button:not([disabled])'
+ )[0].focus();
+
+ // Enable focus trap
+ menuElement.addEventListener('keydown', this.handleFocusTrap);
+
+ // Close on Esc
+ detailsElement.addEventListener('keydown', handleEscKey);
+
+ // Clip page if Mobile
+ if (this.isMobile()) {
+ htmlElement.classList.add('is-clipped');
+ }
+ } else {
+ summaryElement.focus();
+
+ // Disable focus trap
+ menuElement.removeEventListener('keydown', this.handleFocusTrap);
+
+ // Unclip page
+ if (this.isMobile()) {
+ htmlElement.classList.remove('is-clipped');
+ }
+ }
+
+ function handleEscKey(event) {
+ if (event.key !== 'Escape') {
+ return;
+ }
+
+ summaryElement.click();
+ }
+ }
+
+ /**
+ * Check if windows matches mobile media query.
+ *
+ * @return {Boolean}
+ */
+ isMobile() {
+ return window.matchMedia("(max-width: 768px)").matches;
+ }
+
+ /**
+ * Focus trap handler
+ *
+ * @param {Event} event - Keydown event.
+ * @return {undefined}
+ */
+ handleFocusTrap(event) {
+ if (event.key !== 'Tab') {
+ return;
+ }
+
+ const focusableEls = event.currentTarget.querySelectorAll(
+ [
+ 'a[href]:not([disabled])',
+ 'button:not([disabled])',
+ 'textarea:not([disabled])',
+ 'input:not([type="hidden"]):not([disabled])',
+ 'select:not([disabled])',
+ 'details:not([disabled])',
+ '[tabindex]:not([tabindex="-1"]):not([disabled])'
+ ].join(',')
+ );
+ const firstFocusableEl = focusableEls[0];
+ const lastFocusableEl = focusableEls[focusableEls.length - 1];
+
+ if (event.shiftKey ) /* Shift + tab */ {
+ if (document.activeElement === firstFocusableEl) {
+ lastFocusableEl.focus();
+ event.preventDefault();
+ }
+ } else /* Tab */ {
+ if (document.activeElement === lastFocusableEl) {
+ firstFocusableEl.focus();
+ event.preventDefault();
+ }
+ }
+ }
}();
diff --git a/bookwyrm/static/js/check_all.js b/bookwyrm/static/js/check_all.js
deleted file mode 100644
index fd29f2cd..00000000
--- a/bookwyrm/static/js/check_all.js
+++ /dev/null
@@ -1,34 +0,0 @@
-
-(function() {
- 'use strict';
-
- /**
- * Toggle all descendant checkboxes of a target.
- *
- * Use `data-target="ID_OF_TARGET"` on the node on which the event is listened
- * to (checkbox, button, link…), where_ID_OF_TARGET_ should be the ID of an
- * ancestor for the checkboxes.
- *
- * @example
- *
- * @param {Event} event
- * @return {undefined}
- */
- function toggleAllCheckboxes(event) {
- const mainCheckbox = event.target;
-
- document
- .querySelectorAll(`#${mainCheckbox.dataset.target} [type="checkbox"]`)
- .forEach(checkbox => checkbox.checked = mainCheckbox.checked);
- }
-
- document
- .querySelectorAll('[data-action="toggle-all"]')
- .forEach(input => {
- input.addEventListener('change', toggleAllCheckboxes);
- });
-})();
diff --git a/bookwyrm/templates/components/dropdown.html b/bookwyrm/templates/components/dropdown.html
index b3710271..afa6d050 100644
--- a/bookwyrm/templates/components/dropdown.html
+++ b/bookwyrm/templates/components/dropdown.html
@@ -7,6 +7,7 @@
class="
dropdown control
{% if right %}is-right{% endif %}
+ has-text-left
"
>
-