MDL-72456 theme_boost: add tooltips to drawers

This commit is contained in:
Ferran Recio
2021-11-25 11:28:06 +01:00
parent 73cfbea3ee
commit 844681f54f
5 changed files with 157 additions and 23 deletions
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+135 -11
View File
@@ -25,11 +25,23 @@ import Templates from 'core/templates';
import * as Aria from 'core/aria';
import {dispatchEvent} from 'core/event_dispatcher';
import {debounce} from 'core/utils';
import Pending from 'core/pending';
// The jQuery module is only used for interacting with Boostrap 4. It can we removed when MDL-71979 is integrated.
import jQuery from 'jquery';
let backdropPromise = null;
const drawerMap = new Map();
const SELECTORS = {
BUTTONS: '[data-toggler="drawers"]',
CLOSEBTN: '[data-toggler="drawers"][data-action="closedrawer"]',
OPENBTN: '[data-toggler="drawers"][data-action="opendrawer"]',
TOGGLEBTN: '[data-toggler="drawers"][data-action="toggle"]',
DRAWERS: '[data-region="fixed-drawer"]',
CONTAINER: '#page.drawers',
};
/**
* Maximum sizes for breakpoints. This needs to correspond with Bootstrap
* Breakpoints
@@ -96,6 +108,71 @@ const getBackdrop = () => {
return backdropPromise;
};
/**
* Get the button element to open a specific drawer.
*
* @param {String} drawerId the drawer element Id
* @return {HTMLElement|undefined} the open button element
* @private
*/
const getDrawerOpenButton = (drawerId) => {
let openButton = document.querySelector(`${SELECTORS.OPENBTN}[data-target="${drawerId}"]`);
if (!openButton) {
openButton = document.querySelector(`${SELECTORS.TOGGLEBTN}[data-target="${drawerId}"]`);
}
return openButton;
};
/**
* Disable drawer tooltips.
*
* @param {HTMLElement} drawerNode the drawer main node
* @private
*/
const disableDrawerTooltips = (drawerNode) => {
const buttons = [
drawerNode.querySelector(SELECTORS.CLOSEBTN),
getDrawerOpenButton(drawerNode.id),
];
buttons.forEach(button => {
if (!button) {
return;
}
if (button.hasAttribute('data-original-title')) {
// The jQuery is still used in Boostrap 4. It can we removed when MDL-71979 is integrated.
jQuery(button).tooltip('disable');
} else {
button.dataset.disabledToggle = button.dataset.toggle;
button.removeAttribute('data-toggle');
}
});
};
/**
* Enable drawer tooltips.
*
* @param {HTMLElement} drawerNode the drawer main node
* @private
*/
const enableDrawerTooltips = (drawerNode) => {
const buttons = [
drawerNode.querySelector(SELECTORS.CLOSEBTN),
getDrawerOpenButton(drawerNode.id),
];
buttons.forEach(button => {
if (!button) {
return;
}
// The jQuery is still used in Boostrap 4. It can we removed when MDL-71979 is integrated.
if (button.hasAttribute('data-original-title')) {
jQuery(button).tooltip('enable');
} else if (button.dataset.disabledToggle) {
button.dataset.toggle = button.dataset.disabledToggle;
jQuery(button).tooltip();
}
});
};
/**
* The Drawers class is used to control on-screen drawer elements.
*
@@ -164,6 +241,11 @@ export default class Drawers {
Aria.hide(this.drawerNode);
}
// Disable tooltips in small screens.
if (isSmall()) {
disableDrawerTooltips(this.drawerNode);
}
drawerMap.set(drawerNode, this);
}
@@ -278,11 +360,23 @@ export default class Drawers {
* @param {boolean} [args.focusOnCloseButton=true] Whether to alter page focus when opening the drawer
*/
openDrawer({focusOnCloseButton = true} = {}) {
const pendingPromise = new Pending('theme_boost/drawers:open');
const showEvent = this.dispatchEvent(Drawers.eventTypes.drawerShow, true);
if (showEvent.defaultPrevented) {
return;
}
// Hide close button while the drawer is showing to prevent glitchy effects.
this.drawerNode.querySelector(SELECTORS.CLOSEBTN)?.classList.toggle('hidden', true);
// Remove open tooltip if still visible.
let openButton = getDrawerOpenButton(this.drawerNode.id);
if (openButton && openButton.hasAttribute('data-original-title')) {
// The jQuery is still used in Boostrap 4. It can we removed when MDL-71979 is integrated.
jQuery(openButton)?.tooltip('hide');
}
Aria.unhide(this.drawerNode);
this.drawerNode.classList.add('show');
@@ -308,10 +402,15 @@ export default class Drawers {
.catch();
}
if (focusOnCloseButton) {
const closeButton = this.drawerNode.querySelector('[data-toggle="drawers"][data-action="closedrawer"]');
closeButton.focus();
}
// Show close button once the drawer is fully opened.
setTimeout(() => {
const closeButton = this.drawerNode.querySelector(SELECTORS.CLOSEBTN);
closeButton.classList.toggle('hidden', false);
if (focusOnCloseButton) {
closeButton.focus();
}
pendingPromise.resolve();
}, 300);
this.dispatchEvent(Drawers.eventTypes.drawerShown);
}
@@ -320,11 +419,23 @@ export default class Drawers {
* Close the drawer.
*/
closeDrawer() {
const pendingPromise = new Pending('theme_boost/drawers:close');
const hideEvent = this.dispatchEvent(Drawers.eventTypes.drawerHide, true);
if (hideEvent.defaultPrevented) {
return;
}
// Hide close button while the drawer is hiding to prevent glitchy effects.
const closeButton = this.drawerNode.querySelector(SELECTORS.CLOSEBTN);
closeButton?.classList.toggle('hidden', true);
// Remove the close button tooltip if visible.
if (closeButton.hasAttribute('data-original-title')) {
// The jQuery is still used in Boostrap 4. It can we removed when MDL-71979 is integrated.
jQuery(closeButton)?.tooltip('hide');
}
const preference = this.drawerNode.dataset.preference;
if (preference) {
M.util.set_user_preference(preference, false);
@@ -350,6 +461,15 @@ export default class Drawers {
})
.catch();
// Move focus to the open drawer (or toggler) button once the drawer is hidden.
setTimeout(() => {
let openButton = getDrawerOpenButton(this.drawerNode.id);
if (openButton) {
openButton.focus();
}
pendingPromise.resolve();
}, 300);
this.dispatchEvent(Drawers.eventTypes.drawerHidden);
}
@@ -396,7 +516,7 @@ export default class Drawers {
*/
const scroller = () => {
const body = document.querySelector('body');
const drawerLayout = document.querySelector('#page.drawers');
const drawerLayout = document.querySelector(SELECTORS.CONTAINER);
if (drawerLayout) {
drawerLayout.addEventListener("scroll", () => {
if (drawerLayout.scrollTop >= window.innerHeight) {
@@ -415,7 +535,7 @@ const scroller = () => {
*/
const setLastUsedToggle = (toggleButton) => {
if (toggleButton.dataset.target) {
document.querySelectorAll('[data-toggle="drawers"][data-target="' + toggleButton.dataset.target + '"]')
document.querySelectorAll(`${SELECTORS.BUTTONS}[data-target="${toggleButton.dataset.target}"]`)
.forEach(btn => {
btn.dataset.lastused = false;
});
@@ -428,7 +548,7 @@ const setLastUsedToggle = (toggleButton) => {
* @param {string} target The drawer target.
*/
const focusLastUsedToggle = (target) => {
const lastUsedButton = document.querySelector('[data-toggle="drawers"][data-target="' + target + '"][data-lastused="true"');
const lastUsedButton = document.querySelector(`${SELECTORS.BUTTONS}[data-target="${target}"][data-lastused="true"`);
if (lastUsedButton) {
lastUsedButton.focus();
}
@@ -442,7 +562,7 @@ const focusLastUsedToggle = (target) => {
const registerListeners = () => {
// Listen for show/hide events.
document.addEventListener('click', e => {
const toggleButton = e.target.closest('[data-toggle="drawers"][data-action="toggle"]');
const toggleButton = e.target.closest(SELECTORS.TOGGLEBTN);
if (toggleButton && toggleButton.dataset.target) {
e.preventDefault();
const targetDrawer = document.getElementById(toggleButton.dataset.target);
@@ -452,7 +572,7 @@ const registerListeners = () => {
drawerInstance.toggleVisibility();
}
const openDrawerButton = e.target.closest('[data-toggle="drawers"][data-action="opendrawer"]');
const openDrawerButton = e.target.closest(SELECTORS.OPENBTN);
if (openDrawerButton && openDrawerButton.dataset.target) {
e.preventDefault();
const targetDrawer = document.getElementById(openDrawerButton.dataset.target);
@@ -462,7 +582,7 @@ const registerListeners = () => {
drawerInstance.openDrawer();
}
const closeDrawerButton = e.target.closest('[data-toggle="drawers"][data-action="closedrawer"]');
const closeDrawerButton = e.target.closest(SELECTORS.CLOSEBTN);
if (closeDrawerButton && closeDrawerButton.dataset.target) {
e.preventDefault();
const targetDrawer = document.getElementById(closeDrawerButton.dataset.target);
@@ -485,6 +605,7 @@ const registerListeners = () => {
if (isSmall()) {
let anyOpen = false;
drawerMap.forEach(drawerInstance => {
disableDrawerTooltips(drawerInstance.drawerNode);
if (drawerInstance.isOpen) {
if (drawerInstance.closeOnResize) {
drawerInstance.closeDrawer();
@@ -498,6 +619,9 @@ const registerListeners = () => {
getBackdrop().then(backdrop => backdrop.show()).catch();
}
} else {
drawerMap.forEach(drawerInstance => {
enableDrawerTooltips(drawerInstance.drawerNode);
});
getBackdrop().then(backdrop => backdrop.hide()).catch();
}
};
@@ -508,5 +632,5 @@ const registerListeners = () => {
scroller();
registerListeners();
const drawers = document.querySelectorAll('[data-region="fixed-drawer"]');
const drawers = document.querySelectorAll(SELECTORS.DRAWERS);
drawers.forEach(drawerNode => Drawers.getDrawerInstanceForNode(drawerNode));
+10 -2
View File
@@ -21,7 +21,7 @@
{
"drawerclasses": "drawer drawer-right",
"drawertrigger": "toggleblocks",
"drawerheading": "Blocks",
"tooltipplacement": "right",
"drawerconent": "Content for the blocks region"
}
}}
@@ -35,7 +35,15 @@
}} data-close-on-resize="{{$drawercloseonresize}}0{{/drawercloseonresize}}"{{!
}}>
<div class="drawerheader">
<button class="btn drawertoggle icon-no-margin" data-toggle="drawers" data-action="closedrawer" data-target="{{$id}}{{/id}}">
<button
class="btn drawertoggle icon-no-margin hidden"
data-toggler="drawers"
data-action="closedrawer"
data-target="{{$id}}{{/id}}"
data-toggle="tooltip"
data-placement="{{$tooltipplacement}}right{{/tooltipplacement}}"
title="{{#str}}closedrawer, core{{/str}}"
>
{{#pix}}e/cancel, core, {{#str}}closedrawer, core{{/str}}{{/pix}}
</button>
</div>
+10 -8
View File
@@ -62,23 +62,18 @@
{{< theme_boost/drawer }}
{{$id}}theme_boost-drawers-courseindex{{/id}}
{{$drawerclasses}}drawer drawer-left {{#courseindexopen}}show{{/courseindexopen}}{{/drawerclasses}}
{{$drawerheading}}
{{#str}}courseindex, core_courseformat{{/str}}
{{/drawerheading}}
{{$drawercontent}}
{{{courseindex}}}
{{/drawercontent}}
{{$drawerpreferencename}}drawer-open-index{{/drawerpreferencename}}
{{$drawerstate}}show-drawer-left{{/drawerstate}}
{{$tooltipplacement}}right{{/tooltipplacement}}
{{/ theme_boost/drawer}}
{{/courseindex}}
{{#hasblocks}}
{{< theme_boost/drawer }}
{{$id}}theme_boost-drawers-blocks{{/id}}
{{$drawerclasses}}drawer drawer-right{{#blockdraweropen}} show{{/blockdraweropen}}{{/drawerclasses}}
{{$drawerheading}}
{{#str}} blocks {{/str}}
{{/drawerheading}}
{{$drawercontent}}
<section class="d-print-none" aria-label="{{#str}}blocks{{/str}}">
{{{ sidepreblocks }}}
@@ -87,6 +82,7 @@
{{$drawerpreferencename}}drawer-open-block{{/drawerpreferencename}}
{{$forceopen}}{{#forceblockdraweropen}}1{{/forceblockdraweropen}}{{/forceopen}}
{{$drawerstate}}show-drawer-right{{/drawerstate}}
{{$tooltipplacement}}left{{/tooltipplacement}}
{{$drawercloseonresize}}1{{/drawercloseonresize}}
{{/ theme_boost/drawer}}
{{/hasblocks}}
@@ -97,9 +93,12 @@
<div class="drawer-toggler drawer-left-toggle open-nav">
<button
class="shadow-sm btn icon-no-margin"
data-toggle="drawers"
data-toggler="drawers"
data-action="toggle"
data-target="theme_boost-drawers-courseindex"
data-toggle="tooltip"
data-placement="right"
title="{{#str}}opendrawerindex, core{{/str}}"
>
<span class="sr-only">{{#str}}opendrawerindex, core{{/str}}</span>
{{#pix}} t/index_drawer, moodle {{/pix}}
@@ -110,9 +109,12 @@
<div class="drawer-toggler drawer-right-toggle ml-auto">
<button
class="shadow-sm btn icon-no-margin"
data-toggle="drawers"
data-toggler="drawers"
data-action="toggle"
data-target="theme_boost-drawers-blocks"
data-toggle="tooltip"
data-placement="right"
title="{{#str}}opendrawerblocks, core{{/str}}"
>
<span class="sr-only">{{#str}}opendrawerblocks, core{{/str}}</span>
{{#pix}}t/blocks_drawer, core, {{#str}}opendrawerblocks, core{{/str}}{{/pix}}