diff --git a/css/options.css b/css/options.css index a20b109..37fd954 100644 --- a/css/options.css +++ b/css/options.css @@ -59,12 +59,12 @@ h2 { .setting { display: flex; - justify-content: space-between; - padding: 1px; font-size: 12px; font-weight: 400; position: relative; flex-wrap: wrap; + justify-content: flex-end; + min-height: 44px; } .setting-description { @@ -83,8 +83,8 @@ h2 { } hr { - margin-top: 1rem; - margin-bottom: 1rem; + margin-top: 2px; + margin-bottom: 2px; border: 0; border-top: 1px solid #3e4852; box-sizing: content-box; @@ -120,6 +120,10 @@ hr { margin: 6px 10px; } +.hotkeys { + margin: 7px 4px; +} + .hotkeys .btn-text { margin-right: 0; } @@ -130,6 +134,7 @@ hr { .btn-enter { background-color: #00b1e2; + margin: 7px 4px; } .reset-area { @@ -202,3 +207,51 @@ hr { border-radius: 8px; box-shadow: 0 0 0 2px rgba(255, 255, 255, 0.1); } + +.addHotkey { + opacity: 0; + margin: 7px 4px; + transition: opacity 0.4s ease-out; +} + +.setting:hover .addHotkey { + opacity: 1; + transition: opacity 0.2s linear; +} + +.setting > div:first-of-type { + margin: auto 4px auto auto; +} + +/* For Notification Messages */ + +#notify { + --notificationBGColor: rgba(0, 137, 255, 0.6); + --notificationBorder: rgb(0, 137, 255); + --notificationTextColor: white; + position: sticky; + top: 0px; + width: 400px; + height: 0; + color: var(--notificationTextColor); + font-size: 15px; + font-weight: 500; + text-align: center; + background: var(--notificationBGColor); + overflow: hidden; + box-sizing: border-box; + transition: height .2s, border .2s, margin .2s; + left: 50%; + transform: translateX(-50%); +} + +#notifyText { + line-height: 50px; + vertical-align: middle; +} + +.active { + height:50px !important; + border-bottom: 2px var(--notificationBorder) solid !important; + margin-top: -50px; +} diff --git a/js/background.js b/js/background.js index 9cd6c80..e2a120a 100644 --- a/js/background.js +++ b/js/background.js @@ -2,17 +2,17 @@ chrome.runtime.onInstalled.addListener(function () { let settings = { hotkeys: { codes: { - navUp: ["w"], - navLeft: ["a"], - navDown: ["s"], - navRight: ["d"], - expand: ["e"], - contract: ["shift", "e"], - quit: ["q"], - click: ["enter"], - shiftClick: ["shift", "enter"], - controlClick: ["control", "enter"], - disableKeys: ["control", "shift", "x"], + navUp: [["w"]], + navLeft: [["a"]], + navDown: [["s"]], + navRight: [["d"]], + expand: [["e"]], + contract: [["shift", "e"]], + quit: [["q"]], + click: [["f"], ["enter"]], + shiftClick: [["shift", "enter"]], + controlClick: [["control", "enter"]], + disableKeys: [["control", "shift", "x"]], }, }, }; diff --git a/js/options.js b/js/options.js index fd80170..d13baea 100644 --- a/js/options.js +++ b/js/options.js @@ -5,7 +5,6 @@ function getSettings(callback) { chrome.storage.local.get(["extension-settings"], function (result) { SETTINGS_FULL = result["extension-settings"]; HOTKEY_CODES = SETTINGS_FULL.hotkeys.codes; - console.log(HOTKEY_CODES); if (callback instanceof Function) { callback(); } @@ -44,7 +43,6 @@ function enterNewHotkey(event) { setHotkeyBtn(element.parentNode); } keysDown.add(e.key.toLowerCase(), keysDown.size); - console.log(keysDown); textArea.innerHTML = formateHotkeys(keysDown); keysFinal = new Set(keysDown); } @@ -56,18 +54,70 @@ function enterNewHotkey(event) { window.removeEventListener("keydown", keyPress); window.removeEventListener("keyup", keyRelease); // setHotkeyBtn(element.parentNode); - updateHotkey(element, [...keysFinal]); + let newHotkey = [...keysFinal]; + if (isNotDuplicateHotkey(newHotkey, HOTKEY_CODES)) { + updateHotkey(element, newHotkey); + alertMessage("success", "Hotkey successfully added!", 2000); + } else { + updateHotkey(element, null); + alertMessage("failure", "That hotkey is already in use...", 2000); + } } } } -function updateHotkey(element, newVal) { +function alertMessage(type, text, timeOut) { + let notification = document.getElementById("notify"); + let notificationText = document.getElementById("notifyText"); + + // color the notification + let bgColor, textColor, borderColor; + switch (type) { + case "success": + bgColor = "rgb(67 255 125 / 70%)"; + textColor = "white"; + borderColor = "lime"; + break; + case "failure": + bgColor = "rgb(255 70 104 / 70%)"; + textColor = "white"; + borderColor = "red"; + break; + default: + bgColor = "rgb(0 137 255 / 60%)"; + textColor = "white"; + borderColor = "rgb(0 137 255)"; + break; + } + notification.style.setProperty("--notificationBGColor", bgColor); + notification.style.setProperty("--notificationBorder", borderColor); + notification.style.setProperty("--notificationTextColor", textColor); + + // Set the text + notificationText.innerHTML = text; + + // Show the notification for the specified time + notification.classList.add("active"); + setTimeout(function () { + notification.classList.remove("active"); + }, timeOut); +} + +function updateHotkey(element, newVal, index) { let parentSection = element.parentNode; parentSection.removeChild(element); let newSettings = SETTINGS_FULL; - newSettings.hotkeys.codes[parentSection.id] = newVal; + + if (newVal === null && typeof index !== "undefined") { + // remove the hotkey at the index provided + newSettings.hotkeys.codes[parentSection.id].splice(index, 1); + } else if (newVal !== null) { + // add the new hotkey to the beginning of the stored hotkeys + newSettings.hotkeys.codes[parentSection.id].unshift(newVal); + } + chrome.storage.local.set({ "extension-settings": newSettings }, function () { - setHotkeyBtn(parentSection); + getSettings(setHotkeyBtn(parentSection)); // TODO: Send message to content scripts that settings updated }); } @@ -97,23 +147,37 @@ function formateHotkeys(set1) { return keyString; } -function setHotkeyBtn(element) { - let el = document.createElement("div"); +function setHotkeyBtn(btnParent) { + let hotkeys = HOTKEY_CODES[btnParent.id]; + + // remove old buttons + while (btnParent.childNodes.length > 2) { + btnParent.removeChild(btnParent.lastChild); + } + // TODO: Check if hotkey combo in storage + let createNewBtn = document.createElement("div"); + createNewBtn.setAttribute("id", btnParent.id + "-btn"); + createNewBtn.setAttribute("class", "btn btn-hov addHotkey"); + createNewBtn.innerHTML = ` + + Click to type a new shortcut + + `; + // Add listner for new hotkey input + createNewBtn.addEventListener("click", enterNewHotkey); + btnParent.appendChild(createNewBtn); + // if in storage print stored combo - if (HOTKEY_CODES[element.id].length !== 0) { - console.log(element.id); - // delete previous btn - let old = document.getElementById(element.id + "-hotkey"); - if (old !== null && element.parentNode) { - old.parentNode.removeChild(old); - } - el.setAttribute("id", element.id + "-hotkey"); - el.setAttribute("class", "btn hotkeys"); - el.innerHTML = ` + if (hotkeys.length !== 0) { + hotkeys.forEach((hotkey, index) => { + let el = document.createElement("div"); + el.setAttribute("id", btnParent.id + "-hotkey-" + index); + el.setAttribute("class", "btn hotkeys"); + el.innerHTML = ` - ${formateHotkeys(new Set(HOTKEY_CODES[element.id]))} + ${formateHotkeys(new Set(hotkey))} `; - el.getElementsByClassName("close")[0].addEventListener( - "click", - (deleteHotkey = function () { - updateHotkey(el, []); - }) - ); - element.appendChild(el); - } else { - // delete any previous btn - let old = document.getElementById(element.id + "-btn"); - if (old !== null && element.parentNode) { - old.parentNode.removeChild(old); - } - - el.setAttribute("id", element.id + "-btn"); - el.setAttribute("class", "btn btn-hov"); - el.innerHTML = ` - - Click to type a new shortcut - - `; - // Add listner for new hotkey input - el.addEventListener("click", enterNewHotkey); - element.appendChild(el); + el.getElementsByClassName("close")[0].addEventListener( + "click", + (deleteHotkey = function () { + updateHotkey(el, null, index); + alertMessage("success", "Hotkey removed!", 2000); + }) + ); + btnParent.appendChild(el); + }); } } +function isNotDuplicateHotkey(newHotkey, allHotkeys) { + let arrayHotkeys = Object.values(allHotkeys); + for (command of arrayHotkeys) { + for (hotkey of command) { + if (areArraysEqual(newHotkey, hotkey)) { + return false; + } + } + } + // no duplicates found + return true; +} + +function areArraysEqual(a, b) { + if (a === b) return true; + if (a == null || b == null) return false; + if (a.length !== b.length) return false; + + for (var i = 0; i < a.length; ++i) { + if (a[i] !== b[i]) return false; + } + return true; +} + getSettings(function () { // once the DOM is ready file in all settings info let stateCheck = setInterval(() => { diff --git a/js/spatial-navigation.js b/js/spatial-navigation.js index 31e6342..eec338b 100644 --- a/js/spatial-navigation.js +++ b/js/spatial-navigation.js @@ -1819,7 +1819,9 @@ function getSettings(callback) { exports.getSettings = getSettings; function isHotkeyPressed(hotkeyID) { - return setArrayMatch(KEYS_DOWN, HOTKEY_CODES[hotkeyID]); + return HOTKEY_CODES[hotkeyID].some((combo) => { + return setArrayMatch(KEYS_DOWN, combo); + }) } exports.isHotkeyPressed = isHotkeyPressed; diff --git a/options.html b/options.html index bccbfd5..3dcfb4e 100644 --- a/options.html +++ b/options.html @@ -9,6 +9,11 @@ + +
+ +
+

Spatial Navigation Options