Adding in shortcut and keyboard controls

This commit is contained in:
Jonathan Kingston 2017-05-16 04:28:55 +01:00
parent 41df7a10dc
commit 4a97e07d43
4 changed files with 101 additions and 41 deletions

View file

@ -130,11 +130,13 @@ table {
color: white;
}
.button.primary:hover {
.button.primary:hover,
.button.primary:focus {
background-color: #0675d3;
}
.button.secondary:hover {
.button.secondary:hover,
.button.secondary:focus {
background-color: rgba(0, 0, 0, 0.05);
}
@ -198,7 +200,8 @@ table {
justify-content: center;
}
.panel-back-arrow:hover {
.panel-back-arrow:hover,
.panel-back-arrow:focus {
background: #dedede;
}
@ -249,7 +252,8 @@ table {
transition: background-color 75ms;
}
.onboarding-button:hover {
.onboarding-button:hover,
.onboarding-button:active {
background-color: #0675d3;
}
@ -264,12 +268,14 @@ manage things like container crud */
}
.pop-button:hover,
.pop-button:focus,
.panel-footer-secondary:focus,
.panel-footer-secondary:hover {
background-color: rgba(0, 0, 0, 0.05);
}
.pop-button:active,
.panel-footer-secondary:active {
.pop-button:focus,
.panel-footer-secondary:focus {
background-color: rgba(0, 0, 0, 0.08);
}
@ -350,7 +356,8 @@ span ~ .panel-header-text {
transition: background-color 75ms;
}
.clickable.userContext-wrapper:hover {
.container-panel-row:hover .clickable.userContext-wrapper,
.container-panel-row:focus .clickable.userContext-wrapper {
background: #f2f2f2;
}
@ -360,7 +367,6 @@ span ~ .panel-header-text {
}
/* .userContext-icon is used natively, Bug 1333811 was raised to fix */
.userContext-icon,
.usercontext-icon {
background-image: var(--identity-icon);
background-position: center center;
@ -372,8 +378,8 @@ span ~ .panel-header-text {
flex: 0 0 48px;
}
.clickable:hover .userContext-icon,
.clickable:hover .usercontext-icon {
.container-panel-row:hover .clickable .usercontext-icon,
.container-panel-row:focus .clickable .usercontext-icon {
background-image: url('/img/container-newtab.svg');
fill: 'gray';
filter: url('/img/filters.svg#fill');
@ -482,7 +488,8 @@ span ~ .panel-header-text {
cursor: pointer;
}
.clickable:hover {
.clickable:hover,
.clickable:focus {
background-color: #ebebeb;
}

View file

@ -107,6 +107,15 @@ const Logic = {
browser.storage.local.set({browserActionBadgesClicked: storage.browserActionBadgesClicked});
},
addEnterHandler(element, handler) {
element.addEventListener("click", handler);
element.addEventListener("keydown", (e) => {
if (e.keyCode === 13) {
handler(e);
}
});
},
refreshIdentities() {
return browser.runtime.sendMessage({
method: "queryIdentities"
@ -214,7 +223,7 @@ Logic.registerPanel(P_ONBOARDING_1, {
// This method is called when the object is registered.
initialize() {
// Let's move to the next panel.
document.querySelector("#onboarding-start-button").addEventListener("click", () => {
Logic.addEnterHandler(document.querySelector("#onboarding-start-button"), () => {
localStorage.setItem("onboarded1", true);
Logic.showPanel(P_ONBOARDING_2);
});
@ -235,7 +244,7 @@ Logic.registerPanel(P_ONBOARDING_2, {
// This method is called when the object is registered.
initialize() {
// Let's move to the containers list panel.
document.querySelector("#onboarding-next-button").addEventListener("click", () => {
Logic.addEnterHandler(document.querySelector("#onboarding-next-button"), () => {
localStorage.setItem("onboarded2", true);
Logic.showPanel(P_ONBOARDING_3);
});
@ -256,7 +265,7 @@ Logic.registerPanel(P_ONBOARDING_3, {
// This method is called when the object is registered.
initialize() {
// Let's move to the containers list panel.
document.querySelector("#onboarding-almost-done-button").addEventListener("click", () => {
Logic.addEnterHandler(document.querySelector("#onboarding-almost-done-button"), () => {
localStorage.setItem("onboarded3", true);
Logic.showPanel(P_ONBOARDING_4);
});
@ -297,18 +306,18 @@ Logic.registerPanel(P_CONTAINERS_LIST, {
// This method is called when the object is registered.
initialize() {
document.querySelector("#container-add-link").addEventListener("click", () => {
Logic.addEnterHandler(document.querySelector("#container-add-link"), () => {
Logic.showPanel(P_CONTAINER_EDIT, { name: Logic.generateIdentityName() });
});
document.querySelector("#edit-containers-link").addEventListener("click", () => {
Logic.addEnterHandler(document.querySelector("#edit-containers-link"), () => {
Logic.sendTelemetryPayload({
event: "edit-containers"
});
Logic.showPanel(P_CONTAINERS_EDIT);
});
document.querySelector("#sort-containers-link").addEventListener("click", () => {
Logic.addEnterHandler(document.querySelector("#sort-containers-link"), () => {
browser.runtime.sendMessage({
method: "sortTabs"
}).then(() => {
@ -317,6 +326,30 @@ Logic.registerPanel(P_CONTAINERS_LIST, {
window.close();
});
});
document.addEventListener("keydown", (e) => {
const element = document.activeElement;
function next() {
const nextElement = element.nextElementSibling;
if (nextElement) {
nextElement.focus();
}
}
function previous() {
const previousElement = element.previousElementSibling;
if (previousElement) {
previousElement.focus();
}
}
switch (e.keyCode) {
case 40:
next();
break;
case 38:
previous();
break;
}
});
},
// This method is called when the panel is shown.
@ -330,11 +363,14 @@ Logic.registerPanel(P_CONTAINERS_LIST, {
const manage = document.createElement("td");
tr.classList.add("container-panel-row");
tr.setAttribute("tabindex", "0");
context.classList.add("userContext-wrapper", "open-newtab", "clickable");
manage.classList.add("show-tabs", "pop-button");
context.innerHTML = escaped`
<div class="userContext-icon-wrapper open-newtab">
<div class="userContext-icon"
<div class="usercontext-icon"
data-identity-icon="${identity.image}"
data-identity-color="${identity.color}">
</div>
@ -351,8 +387,10 @@ Logic.registerPanel(P_CONTAINERS_LIST, {
tr.appendChild(manage);
}
tr.addEventListener("click", e => {
if (e.target.matches(".open-newtab") || e.target.parentNode.matches(".open-newtab")) {
Logic.addEnterHandler(tr, e => {
if (e.target.matches(".open-newtab")
|| e.target.parentNode.matches(".open-newtab")
|| e.type === "keydown") {
browser.runtime.sendMessage({
method: "openTab",
userContextId: identity.userContextId,
@ -372,6 +410,12 @@ Logic.registerPanel(P_CONTAINERS_LIST, {
list.innerHTML = "";
list.appendChild(fragment);
/* Not sure why extensions require a focus for the doorhanger,
however it allows us to have a tabindex before the first selected item
*/
document.addEventListener("focus", () => {
list.querySelector("tr").focus();
});
return Promise.resolve();
},
@ -385,11 +429,11 @@ Logic.registerPanel(P_CONTAINER_INFO, {
// This method is called when the object is registered.
initialize() {
document.querySelector("#close-container-info-panel").addEventListener("click", () => {
Logic.addEnterHandler(document.querySelector("#close-container-info-panel"), () => {
Logic.showPreviousPanel();
});
document.querySelector("#container-info-hideorshow").addEventListener("click", () => {
Logic.addEnterHandler(document.querySelector("#container-info-hideorshow"), () => {
const identity = Logic.currentIdentity();
browser.runtime.sendMessage({
method: identity.hasHiddenTabs ? "showTabs" : "hideTabs",
@ -420,7 +464,7 @@ Logic.registerPanel(P_CONTAINER_INFO, {
moveTabsEl.parentNode.insertBefore(fragment, moveTabsEl.nextSibling);
} else {
moveTabsEl.addEventListener("click", () => {
Logic.addEnterHandler(moveTabsEl, () => {
browser.runtime.sendMessage({
method: "moveTabsToWindow",
userContextId: Logic.currentIdentity().userContextId,
@ -483,7 +527,7 @@ Logic.registerPanel(P_CONTAINER_INFO, {
// On click, we activate this tab. But only if this tab is active.
if (tab.active) {
tr.classList.add("clickable");
tr.addEventListener("click", () => {
Logic.addEnterHandler(tr, () => {
browser.runtime.sendMessage({
method: "showTab",
tabId: tab.id,
@ -508,7 +552,7 @@ Logic.registerPanel(P_CONTAINERS_EDIT, {
// This method is called when the object is registered.
initialize() {
document.querySelector("#exit-edit-mode-link").addEventListener("click", () => {
Logic.addEnterHandler(document.querySelector("#exit-edit-mode-link"), () => {
Logic.showPanel(P_CONTAINERS_LIST);
});
},
@ -523,7 +567,7 @@ Logic.registerPanel(P_CONTAINERS_EDIT, {
tr.innerHTML = escaped`
<td class="userContext-wrapper">
<div class="userContext-icon-wrapper">
<div class="userContext-icon"
<div class="usercontext-icon"
data-identity-icon="${identity.image}"
data-identity-color="${identity.color}">
</div>
@ -546,7 +590,7 @@ Logic.registerPanel(P_CONTAINERS_EDIT, {
tr.querySelector(".remove-container .pop-button-image").setAttribute("title", `Edit ${identity.name} container`);
tr.addEventListener("click", e => {
Logic.addEnterHandler(tr, e => {
if (e.target.matches(".edit-container-icon") || e.target.parentNode.matches(".edit-container-icon")) {
Logic.showPanel(P_CONTAINER_EDIT, identity);
} else if (e.target.matches(".delete-container-icon") || e.target.parentNode.matches(".delete-container-icon")) {
@ -574,17 +618,17 @@ Logic.registerPanel(P_CONTAINER_EDIT, {
initialize() {
this.initializeRadioButtons();
document.querySelector("#edit-container-panel-back-arrow").addEventListener("click", () => {
Logic.addEnterHandler(document.querySelector("#edit-container-panel-back-arrow"), () => {
Logic.showPreviousPanel();
});
document.querySelector("#edit-container-cancel-link").addEventListener("click", () => {
Logic.addEnterHandler(document.querySelector("#edit-container-cancel-link"), () => {
Logic.showPreviousPanel();
});
this._editForm = document.getElementById("edit-container-panel-form");
const editLink = document.querySelector("#edit-container-ok-link");
editLink.addEventListener("click", this._submitForm.bind(this));
Logic.addEnterHandler(editLink, this._submitForm.bind(this));
editLink.addEventListener("submit", this._submitForm.bind(this));
this._editForm.addEventListener("submit", this._submitForm.bind(this));
},
@ -663,11 +707,11 @@ Logic.registerPanel(P_CONTAINER_DELETE, {
// This method is called when the object is registered.
initialize() {
document.querySelector("#delete-container-cancel-link").addEventListener("click", () => {
Logic.addEnterHandler(document.querySelector("#delete-container-cancel-link"), () => {
Logic.showPreviousPanel();
});
document.querySelector("#delete-container-ok-link").addEventListener("click", () => {
Logic.addEnterHandler(document.querySelector("#delete-container-ok-link"), () => {
/* This promise wont resolve if the last tab was removed from the window.
as the message async callback stops listening, this isn't an issue for us however it might be in future
if you want to do anything post delete do it in the background script.

View file

@ -33,6 +33,15 @@
"webRequest"
],
"commands": {
"_execute_browser_action": {
"suggested_key": {
"default": "Ctrl+Y"
},
"description": "Open containers panel"
}
},
"browser_action": {
"browser_style": true,
"default_icon": {

View file

@ -40,25 +40,25 @@
<div class="panel container-panel hide" id="container-panel">
<div class="panel-header">
<h3 class="panel-header-text">Containers</h3>
<a class="pop-button" id="sort-containers-link"><img class="pop-button-image" alt="Sort Containers" title="Sort Containers" src="/img/container-sort.svg"></a>
<a href="#" class="pop-button" id="sort-containers-link"><img class="pop-button-image" alt="Sort Containers" title="Sort Containers" src="/img/container-sort.svg"></a>
</div>
<div class="scrollable panel-content">
<div class="scrollable panel-content" tabindex="-1">
<table>
<tbody class="identities-list"></tbody>
</table>
</div>
<div class="panel-footer edit-identities">
<div class="edit-containers-text panel-footer-secondary">
<a id="edit-containers-link">Edit Containers</a>
<a href="#" tabindex="0" id="edit-containers-link">Edit Containers</a>
</div>
<a class="add-container-link pop-button" id="container-add-link">
<a href="#" tabindex="0" class="add-container-link pop-button" id="container-add-link">
<img class="pop-button-image-small icon" alt="Create new container icon" title="Create new container" src="/img/container-add.svg" />
</a>
</div>
</div>
<div class="hide panel container-info-panel" id="container-info-panel">
<div class="hide panel container-info-panel" id="container-info-panel" tabindex="-1">
<div class="columns">
<div class="panel-back-arrow" id="close-container-info-panel">
<img alt="Panel Back Arrow" src="/img/container-arrow.svg" class="back-arrow-img" />
@ -92,7 +92,7 @@
</table>
</div>
<div class="panel-footer edit-containers-panel-footer">
<a id="exit-edit-mode-link" class="exit-edit-mode-link edit-containers-exit-text">Exit Edit Mode</a>
<a href="#" id="exit-edit-mode-link" class="exit-edit-mode-link edit-containers-exit-text">Exit Edit Mode</a>
</div>
</div>
@ -116,7 +116,7 @@
</fieldset>
</form>
<div class="panel-footer">
<a class="button secondary expanded footer-button cancel-button" id="edit-container-cancel-link">Cancel</a>
<a href="#" class="button secondary expanded footer-button cancel-button" id="edit-container-cancel-link">Cancel</a>
<a class="button primary expanded footer-button" id="edit-container-ok-link">OK</a>
</div>
</div>
@ -133,8 +133,8 @@
<p>If you remove this container now, <span id="delete-container-tab-count"></span> container tabs will be closed. Are you sure you want to remove this Container?</p>
</div>
<div class="panel-footer">
<a class="button expanded secondary footer-button cancel-button" id="delete-container-cancel-link">Cancel</a>
<a class="button expanded primary footer-button" id="delete-container-ok-link">OK</a>
<a href="#" class="button expanded secondary footer-button cancel-button" id="delete-container-cancel-link">Cancel</a>
<a href="#" class="button expanded primary footer-button" id="delete-container-ok-link">OK</a>
</div>
</div>