mirror of
https://github.com/moodle/moodle.git
synced 2025-08-04 16:36:37 +02:00
MDL-66828 core: Add toast-style notifications
Part of MDL-66074
This commit is contained in:
parent
b253a4f21d
commit
224ea04e68
15 changed files with 135 additions and 1 deletions
|
@ -512,6 +512,7 @@ $string['digitalminor_desc'] = 'Please ask your parent/guardian to contact:';
|
||||||
$string['directory'] = 'Directory';
|
$string['directory'] = 'Directory';
|
||||||
$string['disable'] = 'Disable';
|
$string['disable'] = 'Disable';
|
||||||
$string['disabledcomments'] = 'Comments are disabled';
|
$string['disabledcomments'] = 'Comments are disabled';
|
||||||
|
$string['dismissnotification'] = 'Dismiss this notification';
|
||||||
$string['displayingfirst'] = 'Only the first {$a->count} {$a->things} are displayed';
|
$string['displayingfirst'] = 'Only the first {$a->count} {$a->things} are displayed';
|
||||||
$string['displayingrecords'] = 'Displaying {$a} records';
|
$string['displayingrecords'] = 'Displaying {$a} records';
|
||||||
$string['displayingusers'] = 'Displaying users {$a->start} to {$a->end}';
|
$string['displayingusers'] = 'Displaying users {$a->start} to {$a->end}';
|
||||||
|
|
2
lib/amd/build/toast.min.js
vendored
Normal file
2
lib/amd/build/toast.min.js
vendored
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
define ("core/toast",["exports","core/templates","core/notification","core/pending"],function(a,b,c,d){"use strict";Object.defineProperty(a,"__esModule",{value:!0});a.add=a.addToastRegion=void 0;b=e(b);c=e(c);d=e(d);function e(a){return a&&a.__esModule?a:{default:a}}function f(a){for(var b=1;b<arguments.length;b++){var c=null!=arguments[b]?arguments[b]:{},d=Object.keys(c);if("function"==typeof Object.getOwnPropertySymbols){d=d.concat(Object.getOwnPropertySymbols(c).filter(function(a){return Object.getOwnPropertyDescriptor(c,a).enumerable}))}d.forEach(function(b){g(a,b,c[b])})}return a}function g(a,b,c){if(b in a){Object.defineProperty(a,b,{value:c,enumerable:!0,configurable:!0,writable:!0})}else{a[b]=c}return a}function h(a,b,c,d,e,f,g){try{var h=a[f](g),i=h.value}catch(a){c(a);return}if(h.done){b(i)}else{Promise.resolve(i).then(d,e)}}function i(a){return function(){var b=this,c=arguments;return new Promise(function(d,e){var i=a.apply(b,c);function f(a){h(i,d,e,f,g,"next",a)}function g(a){h(i,d,e,f,g,"throw",a)}f(void 0)})}}var j=function(){var a=i(regeneratorRuntime.mark(function a(e){var f,g,h,i;return regeneratorRuntime.wrap(function(a){while(1){switch(a.prev=a.next){case 0:f=new d.default("addToastRegion");a.prev=1;a.next=4;return b.default.renderForPromise("core/local/toast/wrapper",{});case 4:g=a.sent;h=g.html;i=g.js;b.default.prependNodeContents(e,h,i);a.next=13;break;case 10:a.prev=10;a.t0=a["catch"](1);c.default.exception(a.t0);case 13:f.resolve();case 14:case"end":return a.stop();}}},a,null,[[1,10]])}));return function(){return a.apply(this,arguments)}}();a.addToastRegion=j;var k=function(){var a=i(regeneratorRuntime.mark(function a(e,g){var h,i,j,k,m,n;return regeneratorRuntime.wrap(function(a){while(1){switch(a.prev=a.next){case 0:h=new d.default("addToastRegion");g=f({closeButton:!1,autohide:!0,delay:4e3},g);i="core/local/toast/message";a.prev=3;a.next=6;return l();case 6:j=a.sent;a.next=9;return b.default.renderForPromise(i,f({message:e},g));case 9:k=a.sent;m=k.html;n=k.js;b.default.prependNodeContents(j,m,n);a.next=18;break;case 15:a.prev=15;a.t0=a["catch"](3);c.default.exception(a.t0);case 18:h.resolve();case 19:case"end":return a.stop();}}},a,null,[[3,15]])}));return function(){return a.apply(this,arguments)}}();a.add=k;var l=function(){var a=i(regeneratorRuntime.mark(function a(){var b;return regeneratorRuntime.wrap(function(a){while(1){switch(a.prev=a.next){case 0:b=document.querySelector(".toast-wrapper");if(!b){a.next=3;break}return a.abrupt("return",b);case 3:a.next=5;return j(document.body,"fixed-bottom");case 5:return a.abrupt("return",l());case 6:case"end":return a.stop();}}},a)}));return function(){return a.apply(this,arguments)}}()});
|
||||||
|
//# sourceMappingURL=toast.min.js.map
|
1
lib/amd/build/toast.min.js.map
Normal file
1
lib/amd/build/toast.min.js.map
Normal file
|
@ -0,0 +1 @@
|
||||||
|
{"version":3,"sources":["../src/toast.js"],"names":["addToastRegion","parent","pendingPromise","Pending","Templates","renderForPromise","html","js","prependNodeContents","Notification","exception","resolve","add","message","configuration","closeButton","autohide","delay","templateName","getTargetNode","targetNode","region","document","querySelector","body"],"mappings":"mMAuBA,OACA,OACA,O,wzBAOO,GAAMA,CAAAA,CAAc,4CAAG,WAAMC,CAAN,+FACpBC,CADoB,CACH,GAAIC,UAAJ,CAAY,gBAAZ,CADG,yBAIGC,WAAUC,gBAAV,CAA2B,0BAA3B,CAAuD,EAAvD,CAJH,iBAIfC,CAJe,GAIfA,IAJe,CAITC,CAJS,GAITA,EAJS,CAKtBH,UAAUI,mBAAV,CAA8BP,CAA9B,CAAsCK,CAAtC,CAA4CC,CAA5C,EALsB,qDAOtBE,UAAaC,SAAb,OAPsB,QAU1BR,CAAc,CAACS,OAAf,GAV0B,uDAAH,uDAApB,C,mBAuBA,GAAMC,CAAAA,CAAG,4CAAG,WAAMC,CAAN,CAAeC,CAAf,mGACTZ,CADS,CACQ,GAAIC,UAAJ,CAAY,gBAAZ,CADR,CAEfW,CAAa,IACTC,WAAW,GADF,CAETC,QAAQ,GAFC,CAGTC,KAAK,CAAE,GAHE,EAINH,CAJM,CAAb,CAOMI,CATS,oDAWcC,CAAAA,CAAa,EAX3B,QAWLC,CAXK,uBAYchB,WAAUC,gBAAV,CAA2Ba,CAA3B,IACrBL,OAAO,CAAPA,CADqB,EAElBC,CAFkB,EAZd,iBAYJR,CAZI,GAYJA,IAZI,CAYEC,CAZF,GAYEA,EAZF,CAgBXH,UAAUI,mBAAV,CAA8BY,CAA9B,CAA0Cd,CAA1C,CAAgDC,CAAhD,EAhBW,qDAkBXE,UAAaC,SAAb,OAlBW,QAqBfR,CAAc,CAACS,OAAf,GArBe,uDAAH,uDAAT,C,QAwBP,GAAMQ,CAAAA,CAAa,4CAAG,oGACZE,CADY,CACHC,QAAQ,CAACC,aAAT,CAAuB,gBAAvB,CADG,KAGdF,CAHc,0CAIPA,CAJO,wBAOZrB,CAAAA,CAAc,CAACsB,QAAQ,CAACE,IAAV,CAAgB,cAAhB,CAPF,iCAQXL,CAAa,EARF,0CAAH,uD","sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see <http://www.gnu.org/licenses/>.\n\n/**\n * A system for displaying small snackbar notifications to users which disappear shortly after they are shown.\n *\n * @module core/toast\n * @package core\n * @copyright 2019 Andrew Nicols <andrew@nicols.co.uk>\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\nimport Templates from 'core/templates';\nimport Notification from 'core/notification';\nimport Pending from 'core/pending';\n\n/**\n * Add a new region to place toasts in, taking in a parent element.\n *\n * @param {Element} parent\n */\nexport const addToastRegion = async(parent) => {\n const pendingPromise = new Pending('addToastRegion');\n\n try {\n const {html, js} = await Templates.renderForPromise('core/local/toast/wrapper', {});\n Templates.prependNodeContents(parent, html, js);\n } catch (e) {\n Notification.exception(e);\n }\n\n pendingPromise.resolve();\n};\n\n/**\n * Add a new toast or snackbar notification to the page.\n *\n * @param {String} message\n * @param {Object} configuration\n * @param {String} [configuration.title]\n * @param {String} [configuration.subtitle]\n * @param {Bool} [configuration.autohide=true]\n * @param {Number} [configuration.delay=4000]\n */\nexport const add = async(message, configuration) => {\n const pendingPromise = new Pending('addToastRegion');\n configuration = {\n closeButton: false,\n autohide: true,\n delay: 4000,\n ...configuration,\n };\n\n const templateName = `core/local/toast/message`;\n try {\n const targetNode = await getTargetNode();\n const {html, js} = await Templates.renderForPromise(templateName, {\n message,\n ...configuration\n });\n Templates.prependNodeContents(targetNode, html, js);\n } catch (e) {\n Notification.exception(e);\n }\n\n pendingPromise.resolve();\n};\n\nconst getTargetNode = async() => {\n const region = document.querySelector('.toast-wrapper');\n\n if (region) {\n return region;\n }\n\n await addToastRegion(document.body, 'fixed-bottom');\n return getTargetNode();\n};\n"],"file":"toast.min.js"}
|
89
lib/amd/src/toast.js
Normal file
89
lib/amd/src/toast.js
Normal file
|
@ -0,0 +1,89 @@
|
||||||
|
// This file is part of Moodle - http://moodle.org/
|
||||||
|
//
|
||||||
|
// Moodle is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// Moodle is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A system for displaying small snackbar notifications to users which disappear shortly after they are shown.
|
||||||
|
*
|
||||||
|
* @module core/toast
|
||||||
|
* @package core
|
||||||
|
* @copyright 2019 Andrew Nicols <andrew@nicols.co.uk>
|
||||||
|
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||||
|
*/
|
||||||
|
import Templates from 'core/templates';
|
||||||
|
import Notification from 'core/notification';
|
||||||
|
import Pending from 'core/pending';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a new region to place toasts in, taking in a parent element.
|
||||||
|
*
|
||||||
|
* @param {Element} parent
|
||||||
|
*/
|
||||||
|
export const addToastRegion = async(parent) => {
|
||||||
|
const pendingPromise = new Pending('addToastRegion');
|
||||||
|
|
||||||
|
try {
|
||||||
|
const {html, js} = await Templates.renderForPromise('core/local/toast/wrapper', {});
|
||||||
|
Templates.prependNodeContents(parent, html, js);
|
||||||
|
} catch (e) {
|
||||||
|
Notification.exception(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
pendingPromise.resolve();
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a new toast or snackbar notification to the page.
|
||||||
|
*
|
||||||
|
* @param {String} message
|
||||||
|
* @param {Object} configuration
|
||||||
|
* @param {String} [configuration.title]
|
||||||
|
* @param {String} [configuration.subtitle]
|
||||||
|
* @param {Bool} [configuration.autohide=true]
|
||||||
|
* @param {Number} [configuration.delay=4000]
|
||||||
|
*/
|
||||||
|
export const add = async(message, configuration) => {
|
||||||
|
const pendingPromise = new Pending('addToastRegion');
|
||||||
|
configuration = {
|
||||||
|
closeButton: false,
|
||||||
|
autohide: true,
|
||||||
|
delay: 4000,
|
||||||
|
...configuration,
|
||||||
|
};
|
||||||
|
|
||||||
|
const templateName = `core/local/toast/message`;
|
||||||
|
try {
|
||||||
|
const targetNode = await getTargetNode();
|
||||||
|
const {html, js} = await Templates.renderForPromise(templateName, {
|
||||||
|
message,
|
||||||
|
...configuration
|
||||||
|
});
|
||||||
|
Templates.prependNodeContents(targetNode, html, js);
|
||||||
|
} catch (e) {
|
||||||
|
Notification.exception(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
pendingPromise.resolve();
|
||||||
|
};
|
||||||
|
|
||||||
|
const getTargetNode = async() => {
|
||||||
|
const region = document.querySelector('.toast-wrapper');
|
||||||
|
|
||||||
|
if (region) {
|
||||||
|
return region;
|
||||||
|
}
|
||||||
|
|
||||||
|
await addToastRegion(document.body, 'fixed-bottom');
|
||||||
|
return getTargetNode();
|
||||||
|
};
|
21
lib/templates/local/toast/message.mustache
Normal file
21
lib/templates/local/toast/message.mustache
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
<div id="toast-{{uniqid}}" class="toast bg-dark text-white rounded mx-auto" data-delay="{{delay}}" {{^autohide}}data-autohide="false"{{/autohide}}>
|
||||||
|
<div class="toast-body">
|
||||||
|
<span>{{{message}}}</span>
|
||||||
|
{{#closeButton}}
|
||||||
|
<button type="button" class="ml-2 mb-1 close text-white" data-dismiss="toast" aria-label="{{#str}}dismissnotification, core{{/str}}">
|
||||||
|
<span aria-hidden="true">×</span>
|
||||||
|
</button>
|
||||||
|
{{/closeButton}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{{#js}}
|
||||||
|
require(['jquery', 'theme_boost/toast'], function(jQuery) {
|
||||||
|
// Show the toast.
|
||||||
|
// Bootstrap toast components are not shown automatically.
|
||||||
|
jQuery('#toast-{{uniqid}}').toast('show');
|
||||||
|
|
||||||
|
jQuery('#toast-{{uniqid}}').on('hidden.bs.toast', function(e) {
|
||||||
|
e.target.remove();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
{{/js}}
|
1
lib/templates/local/toast/wrapper.mustache
Normal file
1
lib/templates/local/toast/wrapper.mustache
Normal file
|
@ -0,0 +1 @@
|
||||||
|
<div class="toast-wrapper mx-auto py-3 fixed-top" role="status" aria-live="polite"></div>
|
|
@ -2286,7 +2286,6 @@ $switch-transition: .2s all !default;
|
||||||
float: right !important; /* stylelint-disable-line declaration-no-important */
|
float: right !important; /* stylelint-disable-line declaration-no-important */
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Emoji picker.
|
// Emoji picker.
|
||||||
$picker-width: 350px !default;
|
$picker-width: 350px !default;
|
||||||
$picker-width-xs: 320px !default;
|
$picker-width-xs: 320px !default;
|
||||||
|
@ -2370,3 +2369,9 @@ $picker-emojis-per-row: 7 !default;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.toast-wrapper {
|
||||||
|
max-width: $toast-max-width;
|
||||||
|
// Place these above any modals and other elements.
|
||||||
|
z-index: 1051;
|
||||||
|
}
|
||||||
|
|
|
@ -11545,6 +11545,10 @@ div.editor_atto_toolbar button .icon {
|
||||||
.emoji-auto-complete .btn.btn-link.btn-icon.emoji-button.active, .emoji-auto-complete .btn.btn-icon.emoji-button.active, .emoji-auto-complete #page-grade-grading-manage .actions .btn-icon.emoji-button.active.action, #page-grade-grading-manage .actions .emoji-auto-complete .btn-icon.emoji-button.active.action, .emoji-auto-complete #rubric-rubric.gradingform_rubric #rubric-criteria .criterion .addlevel input.btn-icon.emoji-button.active, #rubric-rubric.gradingform_rubric #rubric-criteria .criterion .addlevel .emoji-auto-complete input.btn-icon.emoji-button.active, .emoji-auto-complete #rubric-rubric.gradingform_rubric .btn-icon.emoji-button.active.addcriterion, #rubric-rubric.gradingform_rubric .emoji-auto-complete .btn-icon.emoji-button.active.addcriterion {
|
.emoji-auto-complete .btn.btn-link.btn-icon.emoji-button.active, .emoji-auto-complete .btn.btn-icon.emoji-button.active, .emoji-auto-complete #page-grade-grading-manage .actions .btn-icon.emoji-button.active.action, #page-grade-grading-manage .actions .emoji-auto-complete .btn-icon.emoji-button.active.action, .emoji-auto-complete #rubric-rubric.gradingform_rubric #rubric-criteria .criterion .addlevel input.btn-icon.emoji-button.active, #rubric-rubric.gradingform_rubric #rubric-criteria .criterion .addlevel .emoji-auto-complete input.btn-icon.emoji-button.active, .emoji-auto-complete #rubric-rubric.gradingform_rubric .btn-icon.emoji-button.active.addcriterion, #rubric-rubric.gradingform_rubric .emoji-auto-complete .btn-icon.emoji-button.active.addcriterion {
|
||||||
background-color: #e9ecef; }
|
background-color: #e9ecef; }
|
||||||
|
|
||||||
|
.toast-wrapper {
|
||||||
|
max-width: 350px;
|
||||||
|
z-index: 1051; }
|
||||||
|
|
||||||
.icon {
|
.icon {
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
width: 16px;
|
width: 16px;
|
||||||
|
|
|
@ -39,6 +39,7 @@
|
||||||
{{> theme_boost/head }}
|
{{> theme_boost/head }}
|
||||||
|
|
||||||
<body {{{ bodyattributes }}}>
|
<body {{{ bodyattributes }}}>
|
||||||
|
{{> core/local/toast/wrapper}}
|
||||||
|
|
||||||
<div id="page-wrapper">
|
<div id="page-wrapper">
|
||||||
|
|
||||||
|
|
|
@ -51,6 +51,7 @@
|
||||||
{{> theme_boost/head }}
|
{{> theme_boost/head }}
|
||||||
|
|
||||||
<body {{{ bodyattributes }}}>
|
<body {{{ bodyattributes }}}>
|
||||||
|
{{> core/local/toast/wrapper}}
|
||||||
|
|
||||||
<div id="page-wrapper">
|
<div id="page-wrapper">
|
||||||
|
|
||||||
|
|
|
@ -42,6 +42,7 @@
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body {{{ output.body_attributes }}}>
|
<body {{{ output.body_attributes }}}>
|
||||||
|
{{> core/local/toast/wrapper}}
|
||||||
|
|
||||||
{{{ output.standard_top_of_body_html }}}
|
{{{ output.standard_top_of_body_html }}}
|
||||||
<div id="page">
|
<div id="page">
|
||||||
|
|
|
@ -32,6 +32,7 @@
|
||||||
{{> theme_boost/head }}
|
{{> theme_boost/head }}
|
||||||
|
|
||||||
<body {{{ bodyattributes }}}>
|
<body {{{ bodyattributes }}}>
|
||||||
|
{{> core/local/toast/wrapper}}
|
||||||
|
|
||||||
<div id="page-wrapper">
|
<div id="page-wrapper">
|
||||||
|
|
||||||
|
|
|
@ -42,6 +42,7 @@
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body {{{ output.body_attributes }}}>
|
<body {{{ output.body_attributes }}}>
|
||||||
|
{{> core/local/toast/wrapper}}
|
||||||
|
|
||||||
<div id="page-wrapper">
|
<div id="page-wrapper">
|
||||||
|
|
||||||
|
|
|
@ -37,6 +37,7 @@
|
||||||
{{> theme_boost/head }}
|
{{> theme_boost/head }}
|
||||||
|
|
||||||
<body {{{ bodyattributes }}}>
|
<body {{{ bodyattributes }}}>
|
||||||
|
{{> core/local/toast/wrapper}}
|
||||||
|
|
||||||
<div id="page-wrapper">
|
<div id="page-wrapper">
|
||||||
|
|
||||||
|
|
|
@ -11800,6 +11800,10 @@ div.editor_atto_toolbar button .icon {
|
||||||
.emoji-auto-complete .btn.btn-link.btn-icon.emoji-button.active, .emoji-auto-complete .btn.btn-icon.emoji-button.active, .emoji-auto-complete #page-grade-grading-manage .actions .btn-icon.emoji-button.active.action, #page-grade-grading-manage .actions .emoji-auto-complete .btn-icon.emoji-button.active.action, .emoji-auto-complete #rubric-rubric.gradingform_rubric #rubric-criteria .criterion .addlevel input.btn-icon.emoji-button.active, #rubric-rubric.gradingform_rubric #rubric-criteria .criterion .addlevel .emoji-auto-complete input.btn-icon.emoji-button.active, .emoji-auto-complete #rubric-rubric.gradingform_rubric .btn-icon.emoji-button.active.addcriterion, #rubric-rubric.gradingform_rubric .emoji-auto-complete .btn-icon.emoji-button.active.addcriterion {
|
.emoji-auto-complete .btn.btn-link.btn-icon.emoji-button.active, .emoji-auto-complete .btn.btn-icon.emoji-button.active, .emoji-auto-complete #page-grade-grading-manage .actions .btn-icon.emoji-button.active.action, #page-grade-grading-manage .actions .emoji-auto-complete .btn-icon.emoji-button.active.action, .emoji-auto-complete #rubric-rubric.gradingform_rubric #rubric-criteria .criterion .addlevel input.btn-icon.emoji-button.active, #rubric-rubric.gradingform_rubric #rubric-criteria .criterion .addlevel .emoji-auto-complete input.btn-icon.emoji-button.active, .emoji-auto-complete #rubric-rubric.gradingform_rubric .btn-icon.emoji-button.active.addcriterion, #rubric-rubric.gradingform_rubric .emoji-auto-complete .btn-icon.emoji-button.active.addcriterion {
|
||||||
background-color: #e9ecef; }
|
background-color: #e9ecef; }
|
||||||
|
|
||||||
|
.toast-wrapper {
|
||||||
|
max-width: 350px;
|
||||||
|
z-index: 1051; }
|
||||||
|
|
||||||
.icon {
|
.icon {
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
width: 16px;
|
width: 16px;
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue