mirror of
https://github.com/moodle/moodle.git
synced 2025-04-22 00:42:54 +02:00
MDL-70075 core: Autocomplete selection should always have an active item
Ensure that there is always one active element in the list of selected autocomplete elements. Without this we have issues beacuse clicking on the link makes the first one active if one is not already active, and this turns a click event into a drag event, which means that it is not deleted.
This commit is contained in:
parent
a88838c25f
commit
7d786c7968
2
lib/amd/build/form-autocomplete.min.js
vendored
2
lib/amd/build/form-autocomplete.min.js
vendored
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -76,10 +76,58 @@ function($, log, str, templates, notification, LoadingIcon, Aria) {
|
||||
|
||||
// Tell the input field it has a new active descendant so the item is announced.
|
||||
selectionElement.attr('aria-activedescendant', itemId);
|
||||
selectionElement.attr('data-active-value', element.attr('data-value'));
|
||||
|
||||
return $.Deferred().resolve();
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the actively selected element from the state object.
|
||||
*
|
||||
* @param {Object} state
|
||||
* @returns {jQuery}
|
||||
*/
|
||||
var getActiveElementFromState = function(state) {
|
||||
var selectionRegion = $(document.getElementById(state.selectionId));
|
||||
var activeId = selectionRegion.attr('aria-activedescendant');
|
||||
|
||||
if (activeId) {
|
||||
var activeElement = $(document.getElementById(activeId));
|
||||
if (activeElement.length) {
|
||||
// The active descendent still exists.
|
||||
return activeElement;
|
||||
}
|
||||
}
|
||||
|
||||
var activeValue = selectionRegion.attr('data-active-value');
|
||||
return selectionRegion.find('[data-value="' + activeValue + '"]');
|
||||
};
|
||||
|
||||
/**
|
||||
* Update the active selection from the given state object.
|
||||
*
|
||||
* @param {Object} state
|
||||
*/
|
||||
var updateActiveSelectionFromState = function(state) {
|
||||
var activeElement = getActiveElementFromState(state);
|
||||
var activeValue = activeElement.attr('data-value');
|
||||
|
||||
var selectionRegion = $(document.getElementById(state.selectionId));
|
||||
if (activeValue) {
|
||||
// Find the index of the currently selected index.
|
||||
var activeIndex = selectionRegion.find('[aria-selected=true]').index(activeElement);
|
||||
|
||||
if (activeIndex !== -1) {
|
||||
activateSelection(activeIndex, state);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Either the active index was not set, or it could not be found.
|
||||
// Select the first value instead.
|
||||
activateSelection(0, state);
|
||||
};
|
||||
|
||||
/**
|
||||
* Update the element that shows the currently selected items.
|
||||
*
|
||||
@ -97,12 +145,6 @@ function($, log, str, templates, notification, LoadingIcon, Aria) {
|
||||
// Build up a valid context to re-render the template.
|
||||
var items = [];
|
||||
var newSelection = $(document.getElementById(state.selectionId));
|
||||
var activeId = newSelection.attr('aria-activedescendant');
|
||||
var activeValue = false;
|
||||
|
||||
if (activeId) {
|
||||
activeValue = $(document.getElementById(activeId)).attr('data-value');
|
||||
}
|
||||
originalSelect.children('option').each(function(index, ele) {
|
||||
if ($(ele).prop('selected')) {
|
||||
var label;
|
||||
@ -116,23 +158,24 @@ function($, log, str, templates, notification, LoadingIcon, Aria) {
|
||||
}
|
||||
}
|
||||
});
|
||||
var context = $.extend({items: items}, options, state);
|
||||
|
||||
if (!hasItemListChanged(state, items)) {
|
||||
M.util.js_complete(pendingKey);
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
state.items = items;
|
||||
|
||||
var context = $.extend(options, state);
|
||||
// Render the template.
|
||||
return templates.render(options.templates.items, context)
|
||||
.then(function(html, js) {
|
||||
// Add it to the page.
|
||||
templates.replaceNodeContents(newSelection, html, js);
|
||||
|
||||
if (activeValue !== false) {
|
||||
// Reselect any previously selected item.
|
||||
newSelection.children('[aria-selected=true]').each(function(index, ele) {
|
||||
if ($(ele).attr('data-value') === activeValue) {
|
||||
activateSelection(index, state);
|
||||
}
|
||||
});
|
||||
}
|
||||
updateActiveSelectionFromState(state);
|
||||
|
||||
return activeValue;
|
||||
return;
|
||||
})
|
||||
.then(function() {
|
||||
return M.util.js_complete(pendingKey);
|
||||
@ -140,6 +183,21 @@ function($, log, str, templates, notification, LoadingIcon, Aria) {
|
||||
.catch(notification.exception);
|
||||
};
|
||||
|
||||
/**
|
||||
* Check whether the list of items stored in the state has changed.
|
||||
*
|
||||
* @param {Object} state
|
||||
* @param {Array} items
|
||||
*/
|
||||
var hasItemListChanged = function(state, items) {
|
||||
if (state.items.length !== items.length) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Check for any items in the state items which are not present in the new items list.
|
||||
return state.items.filter(item => items.indexOf(item) === -1).length > 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* Notify of a change in the selection.
|
||||
*
|
||||
@ -807,6 +865,7 @@ function($, log, str, templates, notification, LoadingIcon, Aria) {
|
||||
.catch();
|
||||
});
|
||||
var selectionElement = $(document.getElementById(state.selectionId));
|
||||
|
||||
// Handle clicks on the selected items (will unselect an item).
|
||||
selectionElement.on('click', '[role=option]', function(e) {
|
||||
var pendingPromise = addPendingJSPromise('form-autocomplete-clicks');
|
||||
@ -814,17 +873,12 @@ function($, log, str, templates, notification, LoadingIcon, Aria) {
|
||||
// Remove it from the selection.
|
||||
pendingPromise.resolve(deselectItem(options, state, $(e.currentTarget), originalSelect));
|
||||
});
|
||||
|
||||
// When listbox is focused, focus on the first option if there is no focused option.
|
||||
selectionElement.on('focus', function() {
|
||||
// Find the list of selections.
|
||||
var selectionsElement = $(document.getElementById(state.selectionId));
|
||||
// Find the active one.
|
||||
var element = selectionsElement.children('[data-active-selection]');
|
||||
if (!element.length) {
|
||||
activateSelection(0, state);
|
||||
return;
|
||||
}
|
||||
updateActiveSelectionFromState(state);
|
||||
});
|
||||
|
||||
// Keyboard navigation for the selection list.
|
||||
selectionElement.on('keydown', function(e) {
|
||||
var pendingPromise = addPendingJSPromise('form-autocomplete-keydown-' + e.keyCode);
|
||||
@ -1057,7 +1111,8 @@ function($, log, str, templates, notification, LoadingIcon, Aria) {
|
||||
inputId: 'form_autocomplete_input-' + uniqueId,
|
||||
suggestionsId: 'form_autocomplete_suggestions-' + uniqueId,
|
||||
selectionId: 'form_autocomplete_selection-' + uniqueId,
|
||||
downArrowId: 'form_autocomplete_downarrow-' + uniqueId
|
||||
downArrowId: 'form_autocomplete_downarrow-' + uniqueId,
|
||||
items: [],
|
||||
};
|
||||
|
||||
// Increment the unique counter so we don't get duplicates ever.
|
||||
|
Loading…
x
Reference in New Issue
Block a user