diff options
author | Jan-Michael Brummer <jan.brummer@tabos.org> | 2020-08-27 20:43:32 +0200 |
---|---|---|
committer | Michael Catanzaro <mcatanzaro@gnome.org> | 2020-08-31 17:36:17 +0000 |
commit | 87f1033b0fbb785d5a0219e492ebd88b8d380410 (patch) | |
tree | 8b9794eb1587bfa3c8ee03184bd9d85003af6f50 | |
parent | f15614d9a9bb5f9ca011060261263f6e03f545eb (diff) | |
download | epiphany-87f1033b0fbb785d5a0219e492ebd88b8d380410.tar.gz |
ephy.js: Improve multi login experience
Reimplement password fill user menu function, which fixes:
- multi user selection
- keyboard highlight selection
- mouse hovering
- user selection with return
- closing of password menu after leaving content
Fixes: https://gitlab.gnome.org/GNOME/epiphany/-/issues/1143
-rw-r--r-- | embed/web-process-extension/resources/js/ephy.js | 229 |
1 files changed, 108 insertions, 121 deletions
diff --git a/embed/web-process-extension/resources/js/ephy.js b/embed/web-process-extension/resources/js/ephy.js index 028919717..dd9026986 100644 --- a/embed/web-process-extension/resources/js/ephy.js +++ b/embed/web-process-extension/resources/js/ephy.js @@ -132,155 +132,141 @@ Ephy.PreFillUserMenu = class PreFillUserMenu { this._manager = manager; this._formAuth = formAuth; - this._userElement = formAuth.usernameNode; + this._userNode = formAuth.usernameNode; this._users = users; - this._passwordElement = formAuth.passwordNode; - this._selected = null; - this._wasEdited = false; - - this._userElement.addEventListener('input', this._onInput.bind(this), true); - this._userElement.addEventListener('mouseup', this._onMouseUp.bind(this), false); - this._userElement.addEventListener('keydown', this._onKeyDown.bind(this), false); - this._userElement.addEventListener('change', this._removeMenu, false); - this._userElement.addEventListener('blur', this._removeMenu, false); + this._passwordNode = formAuth.passwordNode; + this._currentFocus = -1; + + let style = document.createElement('style'); + style.innerHTML = + '.autocomplete-items {' + + 'position: absolute;' + + 'box-shadow: 5px 5px 5px rgba(0,0,0,0.2);' + + 'z-index: 99;' + + 'top: 100%;' + + 'left: 0;' + + 'right: 0;' + + '}' + + '.autocomplete-items div {' + + 'padding: 10px;' + + 'cursor: pointer;' + + 'background-color: #fff;' + + 'border-bottom: 1px solid #d4d4d4;' + + '}' + + '.autocomplete-items div:hover {' + + 'background-color: #e9e9e9;' + + '}' + + '.autocomplete-active {' + + 'background-color: DodgerBlue !important;' + + 'color: #ffffff;' + + '}'; + + document.getElementsByTagName("head")[0].appendChild(style); + + this._userNode.addEventListener('input', this._onInput.bind(this), true); + this._userNode.addEventListener('keydown', this._onKeyDown.bind(this), true); + this._userNode.addEventListener('mouseup', this._onMouseUp.bind(this), true); + document.addEventListener("click", e => { + this._closeAllLists(e.target); + }); } - // Private - _onInput(event) { - if (this._manager.isAutoFilling(this._userElement)) + if (this._manager.isAutoFilling(this._userNode)) return; - this._wasEdited = true; - this._removeMenu(); - this._showMenu(false); + this._showMenu(false); } - _onMouseUp(event) + _onMouseUp(e) { - if (document.getElementById('ephy-user-choices-container')) - return; - - this._showMenu(!this._wasEdited); + this._showMenu(true); } - _onKeyDown(event) + _showMenu(showAllUsers) { - if (event.key == 'Escape') { - this._removeMenu(); - return; - } - - if (event.key != 'ArrowDown' && event.key != 'ArrowUp') - return; + let a, b, i, val = this._userNode.value; + + this._closeAllLists(); + this._currentFocus = -1; + a = document.createElement("div"); + a.id = this._userNode.id + "-autocomplete-list"; + a.className = "autocomplete-items"; + let elementRect = this._userNode.getBoundingClientRect(); + a.style.width = this._userNode.offsetWidth + 'px'; + a.style.left = elementRect.left + 'px'; + a.style.top = elementRect.top + elementRect.height + 'px'; + document.body.appendChild(a); + this._passwordNode.value = null; + + for (let user of this._users) { + if (showAllUsers || !val || user.substr(0, val.length).toUpperCase() == val.toUpperCase()) { + b = document.createElement("div"); + b.innerHTML = "<strong>" + user.substr(0, val.length) + "</strong>"; + b.innerHTML += user.substr(val.length); + b.innerHTML += "<input type='hidden' value='" + user + "'>"; + + b.addEventListener("click", e => { + this._formAuth.username = e.target.getElementsByTagName("input")[0].value; + this._closeAllLists(); + this._passwordNode.value = null; + this._manager.preFill(this._formAuth); + }); - let container = document.getElementById('ephy-user-choices-container'); - if (!container) { - this._showMenu(!this._wasEdited); - return; + a.appendChild(b); + } } + } - let newSelect = null; - if (this._selected) - newSelect = event.key != 'ArrowUp' ? this._selected.previousSibling : this._selected.nextSibling; - - if (!newSelect) - newSelect = event.key != 'ArrowUp' ? container.firstElementChild.lastElementChild : container.firstElementChild.firstElementChild; - - if (newSelect) { - this._selected = newSelect; - this._userElement.value = this._selected.firstElementChild.textContent; - this._usernameSelected(); - } else { - this._passwordElement.value = ''; + _onKeyDown(e) + { + let autocompleteList = document.getElementById(this._userNode.id + "-autocomplete-list"); + if (autocompleteList) + autocompleteList = autocompleteList.getElementsByTagName("div"); + + if (e.code == 'ArrowDown') { + this._currentFocus++; + this._addActive(autocompleteList); + } else if (e.code == 'ArrowUp') { + this._currentFocus--; + this._addActive(autocompleteList); + } else if (e.code == 'Enter') { + e.preventDefault(); + if (this._currentFocus > -1) + if (autocompleteList) + autocompleteList[this._currentFocus].click(); } - - event.preventDefault(); } - _showMenu(showAll) + _addActive(autocompleteList) { - let mainDiv = document.createElement('div'); - mainDiv.id = 'ephy-user-choices-container'; - - let elementRect = this._userElement.getBoundingClientRect(); - - // 2147483647 is the maximum value browsers will take for z-index. - // See http://stackoverflow.com/questions/8565821/css-max-z-index-value - mainDiv.style.cssText = 'position: absolute;' + - 'z-index: 2147483647;' + - 'cursor: default;' + - 'background-color: white;' + - 'box-shadow: 5px 5px 5px rgba(0,0,0,0.2);' + - 'border-top: 0px;' + - 'border-radius: 8px;' + - 'padding: 12px 0px;' + - '-webkit-user-modify: read-only ! important;'; - mainDiv.style.width = this._userElement.offsetWidth + 'px'; - mainDiv.style.left = elementRect.left + document.body.scrollLeft + 'px'; - mainDiv.style.top = elementRect.top + elementRect.height + document.body.scrollTop + 'px'; - - let ul = document.createElement('ul'); - ul.style.cssText = 'margin: 0; padding: 0;'; - ul.tabindex = -1; - mainDiv.appendChild(ul); - - this._selected = null; - for (let i = 0; i < this._users.length; i++) { - let user = this._users[i]; - if (!showAll && !user.startsWith(this._userElement.value)) - continue; + if (!autocompleteList) + return false; - let li = document.createElement('li'); - li.style.cssText = 'list-style-type: none ! important;' + - 'background-image: none ! important;' + - 'padding: 3px 6px ! important;' + - 'color: black;' + - 'margin: 0px;'; - // FIXME: selection colors. - li.tabindex = -1; - ul.appendChild(li); - - if (user == this._userElement.value) - this._selected = li; - - let anchor = document.createElement('a'); - anchor.style.cssText = 'font-weight: normal ! important;' + - 'font-family: sans ! important;' + - 'text-decoration: none ! important;' + - 'color: black;' + - '-webkit-user-modify: read-only ! important;'; - // FIXME: selection colors. - anchor.textContent = user; - li.appendChild(anchor); - - li.addEventListener('mousedown', event => { - this._userElement.value = user; - this._selected = li; - this._removeMenu(); - this._usernameSelected(); - }, true); - } + this._removeActive(autocompleteList); - document.body.appendChild(mainDiv); + if (this._currentFocus >= autocompleteList.length) + this._currentFocus = 0; + else if (this._currentFocus < 0) + this._currentFocus = (autocompleteList.length - 1); - if (!this._selected) - this._passwordElement.value = ''; + autocompleteList[this._currentFocus].classList.add("autocomplete-active"); } - _removeMenu() + _removeActive(autocompleteList) { - let menu = document.getElementById('ephy-user-choices-container'); - if (menu) - menu.parentNode.removeChild(menu); + for (let autocomplete of autocompleteList) + autocomplete.classList.remove("autocomplete-active"); } - _usernameSelected() + _closeAllLists(elem) { - this._formAuth.username = this._userElement.value; - this._passwordElement.value = ''; - this._manager.preFill(this._formAuth); + let autocompleteItems = document.getElementsByClassName("autocomplete-items"); + + for (let autocomplete of autocompleteItems) + if (elem != autocomplete && elem != this._userNode) + autocomplete.parentNode.removeChild(autocomplete); } } @@ -486,7 +472,8 @@ Ephy.FormManager = class FormManager Ephy.passwordManager.queryUsernames(formAuth.origin).then(users => { if (users.length > 1) { Ephy.log('More than one saved username, hooking menu for choosing which one to select'); - this._preFillUserMenu = new Ephy.PreFillUserMenu(this, formAuth, users); + if (!this._preFillUserMenu) + this._preFillUserMenu = new Ephy.PreFillUserMenu(this, formAuth, users); } this.preFill(formAuth); }); |