mirror of
https://github.com/moodle/moodle.git
synced 2025-04-20 07:56:06 +02:00
MDL-76246 core_grades: Group search widget to combobox
This commit is contained in:
parent
343e676c4b
commit
d2881df06b
4
grade/amd/build/searchwidget/group.min.js
vendored
4
grade/amd/build/searchwidget/group.min.js
vendored
@ -1,10 +1,10 @@
|
||||
define("core_grades/searchwidget/group",["exports","core/pending","core/templates","core_grades/searchwidget/repository","core_grades/searchwidget/basewidget","jquery","core_grades/searchwidget/selectors"],(function(_exports,_pending,Templates,Repository,WidgetBase,_jquery,Selectors){function _getRequireWildcardCache(nodeInterop){if("function"!=typeof WeakMap)return null;var cacheBabelInterop=new WeakMap,cacheNodeInterop=new WeakMap;return(_getRequireWildcardCache=function(nodeInterop){return nodeInterop?cacheNodeInterop:cacheBabelInterop})(nodeInterop)}function _interopRequireWildcard(obj,nodeInterop){if(!nodeInterop&&obj&&obj.__esModule)return obj;if(null===obj||"object"!=typeof obj&&"function"!=typeof obj)return{default:obj};var cache=_getRequireWildcardCache(nodeInterop);if(cache&&cache.has(obj))return cache.get(obj);var newObj={},hasPropertyDescriptor=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var key in obj)if("default"!==key&&Object.prototype.hasOwnProperty.call(obj,key)){var desc=hasPropertyDescriptor?Object.getOwnPropertyDescriptor(obj,key):null;desc&&(desc.get||desc.set)?Object.defineProperty(newObj,key,desc):newObj[key]=obj[key]}return newObj.default=obj,cache&&cache.set(obj,newObj),newObj}function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}}
|
||||
define("core_grades/searchwidget/group",["exports","core/local/aria/focuslock","core/pending","core/templates","core_grades/searchwidget/repository","core_grades/searchwidget/basewidget","jquery","core_grades/searchwidget/selectors"],(function(_exports,FocusLockManager,_pending,Templates,Repository,WidgetBase,_jquery,Selectors){function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}}function _getRequireWildcardCache(nodeInterop){if("function"!=typeof WeakMap)return null;var cacheBabelInterop=new WeakMap,cacheNodeInterop=new WeakMap;return(_getRequireWildcardCache=function(nodeInterop){return nodeInterop?cacheNodeInterop:cacheBabelInterop})(nodeInterop)}function _interopRequireWildcard(obj,nodeInterop){if(!nodeInterop&&obj&&obj.__esModule)return obj;if(null===obj||"object"!=typeof obj&&"function"!=typeof obj)return{default:obj};var cache=_getRequireWildcardCache(nodeInterop);if(cache&&cache.has(obj))return cache.get(obj);var newObj={},hasPropertyDescriptor=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var key in obj)if("default"!==key&&Object.prototype.hasOwnProperty.call(obj,key)){var desc=hasPropertyDescriptor?Object.getOwnPropertyDescriptor(obj,key):null;desc&&(desc.get||desc.set)?Object.defineProperty(newObj,key,desc):newObj[key]=obj[key]}return newObj.default=obj,cache&&cache.set(obj,newObj),newObj}
|
||||
/**
|
||||
* A widget to search groups within the gradebook.
|
||||
*
|
||||
* @module core_grades/searchwidget/group
|
||||
* @copyright 2022 Mathew May <mathew.solutions>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=void 0,_pending=_interopRequireDefault(_pending),Templates=_interopRequireWildcard(Templates),Repository=_interopRequireWildcard(Repository),WidgetBase=_interopRequireWildcard(WidgetBase),_jquery=_interopRequireDefault(_jquery),Selectors=_interopRequireWildcard(Selectors);let registered=!1;_exports.init=()=>{if(registered)return;const pendingPromise=new _pending.default;registerListenerEvents(),pendingPromise.resolve(),registered=!0};const registerListenerEvents=()=>{let{bodyPromiseResolver:bodyPromiseResolver,bodyPromise:bodyPromise}=WidgetBase.promisesAndResolvers();const dropdownMenuContainer=document.querySelector(Selectors.elements.getSearchWidgetDropdownSelector("group"));(0,_jquery.default)(Selectors.elements.getSearchWidgetSelector("group")).on("show.bs.dropdown",(async e=>{const courseID=e.relatedTarget.dataset.courseid,actionBaseUrl=e.relatedTarget.dataset.actionBaseUrl;await WidgetBase.showLoader(dropdownMenuContainer);const data=await Repository.groupFetch(courseID,actionBaseUrl).catch((async e=>{const errorTemplateData={errormessage:e.message};bodyPromiseResolver(await Templates.render("core_grades/searchwidget/error",errorTemplateData))}));data!==[]&&await WidgetBase.init(dropdownMenuContainer,bodyPromise,data.groups,searchGroups())})),bodyPromiseResolver(Templates.render("core_grades/searchwidget/group/groupsearch_body",[])),(0,_jquery.default)(Selectors.elements.getSearchWidgetSelector("group")).on("hide.bs.dropdown",(()=>{dropdownMenuContainer.innerHTML=""}))},searchGroups=()=>()=>(groups,searchTerm)=>{if(""===searchTerm)return groups;searchTerm=searchTerm.toLowerCase();const searchResults=[];return groups.forEach((group=>{group.name.toLowerCase().includes(searchTerm)&&searchResults.push(group)})),searchResults}}));
|
||||
*/Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=void 0,FocusLockManager=_interopRequireWildcard(FocusLockManager),_pending=_interopRequireDefault(_pending),Templates=_interopRequireWildcard(Templates),Repository=_interopRequireWildcard(Repository),WidgetBase=_interopRequireWildcard(WidgetBase),_jquery=_interopRequireDefault(_jquery),Selectors=_interopRequireWildcard(Selectors);let initialised=!1;_exports.init=()=>{if(!initialised&&document.querySelector(Selectors.elements.getSearchWidgetSelector("group"))){const pendingPromise=new _pending.default;registerListenerEvents(),pendingPromise.resolve()}initialised=!0};const registerListenerEvents=()=>{let{bodyPromiseResolver:bodyPromiseResolver,bodyPromise:bodyPromise}=WidgetBase.promisesAndResolvers();const dropdownMenuContainer=document.querySelector(Selectors.elements.getSearchWidgetDropdownSelector("group")),menuContainer=document.querySelector(Selectors.elements.getSearchWidgetSelector("group")),inputElement=menuContainer.querySelector('input[name="group"]');(0,_jquery.default)(menuContainer).on("show.bs.dropdown",(async e=>{const courseID=e.relatedTarget.dataset.courseid,actionBaseUrl=e.relatedTarget.dataset.actionBaseUrl;await WidgetBase.showLoader(dropdownMenuContainer);const data=await Repository.groupFetch(courseID,actionBaseUrl).catch((async e=>{const errorTemplateData={errormessage:e.message};bodyPromiseResolver(await Templates.render("core_grades/searchwidget/error",errorTemplateData))}));data!==[]&&(await WidgetBase.init(dropdownMenuContainer,bodyPromise,data.groups,searchGroups(),null,afterSelect),FocusLockManager.trapFocus(dropdownMenuContainer))})),bodyPromiseResolver(Templates.render("core_grades/searchwidget/group/groupsearch_body",[])),(0,_jquery.default)(menuContainer).on("hide.bs.dropdown",(()=>{FocusLockManager.untrapFocus()})),inputElement.addEventListener("change",(e=>{const toggle=menuContainer.querySelector(".dropdown-toggle"),courseId=toggle.dataset.courseid,actionUrl=toggle.dataset.actionBaseUrl?new URL(toggle.dataset.actionBaseUrl.replace(/&/g,"&")):new URL(location.href);actionUrl.searchParams.set("id",courseId),actionUrl.searchParams.set("group",e.target.value),location.href=actionUrl.href,e.stopPropagation()}))},searchGroups=()=>()=>(groups,searchTerm)=>{if(""===searchTerm)return groups;searchTerm=searchTerm.toLowerCase();const searchResults=[];return groups.forEach((group=>{group.name.toLowerCase().includes(searchTerm)&&searchResults.push(group)})),searchResults},afterSelect=selected=>{const menuContainer=document.querySelector(Selectors.elements.getSearchWidgetSelector("group")),inputElement=menuContainer.querySelector('input[name="group"]');(0,_jquery.default)(menuContainer).dropdown("hide"),inputElement.value!=selected&&(inputElement.value=selected,inputElement.dispatchEvent(new Event("change",{bubbles:!0})))}}));
|
||||
|
||||
//# sourceMappingURL=group.min.js.map
|
File diff suppressed because one or more lines are too long
@ -21,6 +21,7 @@
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
import * as FocusLockManager from 'core/local/aria/focuslock';
|
||||
import Pending from 'core/pending';
|
||||
import * as Templates from 'core/templates';
|
||||
import * as Repository from 'core_grades/searchwidget/repository';
|
||||
@ -29,11 +30,11 @@ import $ from 'jquery';
|
||||
import * as Selectors from 'core_grades/searchwidget/selectors';
|
||||
|
||||
/**
|
||||
* Whether the event listener has already been registered for this module.
|
||||
* Whether this module is already initialised.
|
||||
*
|
||||
* @type {boolean}
|
||||
*/
|
||||
let registered = false;
|
||||
let initialised = false;
|
||||
|
||||
/**
|
||||
* Our entry point into starting to build the group search widget.
|
||||
@ -43,13 +44,12 @@ let registered = false;
|
||||
* @method init
|
||||
*/
|
||||
export const init = () => {
|
||||
if (registered) {
|
||||
return;
|
||||
if (!initialised && document.querySelector(Selectors.elements.getSearchWidgetSelector('group'))) {
|
||||
const pendingPromise = new Pending();
|
||||
registerListenerEvents();
|
||||
pendingPromise.resolve();
|
||||
}
|
||||
const pendingPromise = new Pending();
|
||||
registerListenerEvents();
|
||||
pendingPromise.resolve();
|
||||
registered = true;
|
||||
initialised = true;
|
||||
};
|
||||
|
||||
/**
|
||||
@ -60,9 +60,11 @@ export const init = () => {
|
||||
const registerListenerEvents = () => {
|
||||
let {bodyPromiseResolver, bodyPromise} = WidgetBase.promisesAndResolvers();
|
||||
const dropdownMenuContainer = document.querySelector(Selectors.elements.getSearchWidgetDropdownSelector('group'));
|
||||
const menuContainer = document.querySelector(Selectors.elements.getSearchWidgetSelector('group'));
|
||||
const inputElement = menuContainer.querySelector('input[name="group"]');
|
||||
|
||||
// Handle the 'shown.bs.dropdown' event (Fired when the dropdown menu is fully displayed).
|
||||
$(Selectors.elements.getSearchWidgetSelector('group')).on('show.bs.dropdown', async(e) => {
|
||||
$(menuContainer).on('show.bs.dropdown', async(e) => {
|
||||
const courseID = e.relatedTarget.dataset.courseid;
|
||||
const actionBaseUrl = e.relatedTarget.dataset.actionBaseUrl;
|
||||
// Display a loading icon in the dropdown menu container until the body promise is resolved.
|
||||
@ -86,7 +88,12 @@ const registerListenerEvents = () => {
|
||||
bodyPromise,
|
||||
data.groups,
|
||||
searchGroups(),
|
||||
null,
|
||||
afterSelect
|
||||
);
|
||||
|
||||
// Lock tab control. It has to be locked because the dropdown's role is dialog.
|
||||
FocusLockManager.trapFocus(dropdownMenuContainer);
|
||||
});
|
||||
|
||||
// Resolvers for passed functions in the dropdown creation.
|
||||
@ -96,16 +103,29 @@ const registerListenerEvents = () => {
|
||||
));
|
||||
|
||||
// Handle the 'hide.bs.dropdown' event (Fired when the dropdown menu is being closed).
|
||||
$(Selectors.elements.getSearchWidgetSelector('group')).on('hide.bs.dropdown', () => {
|
||||
// Reset the state once the groups menu dropdown is closed.
|
||||
dropdownMenuContainer.innerHTML = '';
|
||||
$(menuContainer).on('hide.bs.dropdown', () => {
|
||||
FocusLockManager.untrapFocus();
|
||||
});
|
||||
|
||||
inputElement.addEventListener('change', e => {
|
||||
const toggle = menuContainer.querySelector('.dropdown-toggle');
|
||||
const courseId = toggle.dataset.courseid;
|
||||
const actionUrl = toggle.dataset.actionBaseUrl ?
|
||||
new URL(toggle.dataset.actionBaseUrl.replace(/&/g, "&")) :
|
||||
new URL(location.href);
|
||||
actionUrl.searchParams.set('id', courseId);
|
||||
actionUrl.searchParams.set('group', e.target.value);
|
||||
|
||||
location.href = actionUrl.href;
|
||||
|
||||
e.stopPropagation();
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Define how we want to search and filter groups when the user decides to input a search value.
|
||||
*
|
||||
* @method registerListenerEvents
|
||||
* @method searchGroups
|
||||
* @returns {function(): function(*, *): (*)}
|
||||
*/
|
||||
const searchGroups = () => {
|
||||
@ -126,3 +146,20 @@ const searchGroups = () => {
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Define the action to be performed when an item is selected by the search widget.
|
||||
*
|
||||
* @param {String} selected The selected item's value.
|
||||
*/
|
||||
const afterSelect = (selected) => {
|
||||
const menuContainer = document.querySelector(Selectors.elements.getSearchWidgetSelector('group'));
|
||||
const inputElement = menuContainer.querySelector('input[name="group"]');
|
||||
|
||||
$(menuContainer).dropdown('hide'); // Otherwise the dropdown stays open when user choose an option using keyboard.
|
||||
|
||||
if (inputElement.value != selected) {
|
||||
inputElement.value = selected;
|
||||
inputElement.dispatchEvent(new Event('change', {bubbles: true}));
|
||||
}
|
||||
};
|
||||
|
@ -59,6 +59,7 @@ class core_grades_renderer extends plugin_renderer_base {
|
||||
get_string('selectgroupsseparate');
|
||||
|
||||
$data = [
|
||||
'name' => 'group',
|
||||
'label' => $label,
|
||||
'courseid' => $course->id,
|
||||
'groupactionbaseurl' => $groupactionbaseurl
|
||||
@ -73,6 +74,7 @@ class core_grades_renderer extends plugin_renderer_base {
|
||||
}
|
||||
|
||||
$activegroup = groups_get_course_group($course, true, $allowedgroups);
|
||||
$data['group'] = $activegroup;
|
||||
|
||||
if ($activegroup) {
|
||||
$group = groups_get_group($activegroup);
|
||||
|
@ -21,6 +21,8 @@
|
||||
|
||||
Context variables required for this template:
|
||||
* label - The label text fot the group selector element.
|
||||
* name - The name of the group selector element
|
||||
* group - The value of the group selector element (id of the preselected group)
|
||||
* courseid - The course ID.
|
||||
* groupactionbaseurl - The base URL for the group action.
|
||||
* selectedgroup - The text of the selected group option.
|
||||
@ -28,13 +30,26 @@
|
||||
Example context (json):
|
||||
{
|
||||
"label": "Select separate groups",
|
||||
"name": "group",
|
||||
"group": "21",
|
||||
"courseid": "2",
|
||||
"groupactionbaseurl": "index.php?item=test",
|
||||
"selectedgroup": "Group 1"
|
||||
}
|
||||
}}
|
||||
<div class="search-widget d-flex dropdown" data-searchtype="group">
|
||||
<button aria-expanded="false" data-toggle="dropdown" class="btn dropdown-toggle d-flex text-left align-items-center p-0" data-courseid="{{courseid}}" data-action-base-url="{{groupactionbaseurl}}">
|
||||
<div
|
||||
tabindex="0"
|
||||
aria-expanded="false"
|
||||
role="combobox"
|
||||
aria-haspopup="dialog"
|
||||
aria-controls="dialog-{{uniqid}}"
|
||||
data-toggle="dropdown"
|
||||
class="btn dropdown-toggle d-flex text-left align-items-center p-0"
|
||||
data-courseid="{{courseid}}"
|
||||
data-action-base-url="{{groupactionbaseurl}}"
|
||||
data-input-element="input-{{uniqid}}"
|
||||
>
|
||||
<div class="align-items-center d-flex">
|
||||
<div class="d-block pr-3">
|
||||
<span class="d-block small">
|
||||
@ -45,7 +60,8 @@
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</button>
|
||||
<div class="dropdown-menu narrow">
|
||||
</div>
|
||||
<div class="dropdown-menu narrow" id="dialog-{{uniqid}}" role="dialog" aria-modal="true" aria-label="{{#cleanstr}} selectagroup, core {{/cleanstr}}">
|
||||
</div>
|
||||
<input type="hidden" name="{{name}}" value="{{group}}" id="input-{{uniqid}}" />
|
||||
</div>
|
||||
|
@ -29,6 +29,13 @@
|
||||
{{$placeholder}}{{#str}}
|
||||
searchgroups, core_grades
|
||||
{{/str}}{{/placeholder}}
|
||||
{{$additionalattributes}}
|
||||
role="combobox"
|
||||
aria-expanded="true"
|
||||
aria-controls="listbox-{{uniqid}}"
|
||||
aria-autocomplete="list"
|
||||
data-input-element="input-{{uniqid}}"
|
||||
{{/additionalattributes}}
|
||||
{{/ core/search_input_auto }}
|
||||
|
||||
<div class="searchresultscontainer" data-region="search-results-container-widget" aria-live="polite"></div>
|
||||
<input type="hidden" name="search" id="input-{{uniqid}}"/>
|
||||
<div role="listbox" id="listbox-{{uniqid}}" class="searchresultscontainer" data-region="search-results-container-widget"></div>
|
||||
|
Loading…
x
Reference in New Issue
Block a user