From 570a6f26abebcafcdf02eae696d3fe1435ae01d9 Mon Sep 17 00:00:00 2001
From: dragonwocky <thedragonring.bod@gmail.com>
Date: Sun, 15 Jan 2023 15:57:15 +1100
Subject: [PATCH] fix(menu): close select popups w/ esc, toggle menu w/ hotkeys
 while menu focused

---
 src/core/client.mjs          |  6 ++++++
 src/core/menu/components.mjs |  7 +++++++
 src/core/menu/menu.mjs       | 22 +++++++++++++++++++++-
 3 files changed, 34 insertions(+), 1 deletion(-)

diff --git a/src/core/client.mjs b/src/core/client.mjs
index b8f5d32..88461c9 100644
--- a/src/core/client.mjs
+++ b/src/core/client.mjs
@@ -54,6 +54,7 @@ export default async (api, db) => {
       _notionTheme = notionTheme;
       const msg = {
         namespace: "notion-enhancer",
+        hotkey: openMenuHotkey,
         theme: notionTheme,
         icon: menuButtonIconStyle,
       };
@@ -129,6 +130,11 @@ export default async (api, db) => {
   document.querySelector(notionSidebar)?.append($menuButton);
 
   window.addEventListener("focus", () => updateTheme(true));
+  window.addEventListener("message", (event) => {
+    if (event.data?.namespace !== "notion-enhancer") return;
+    if (event.data?.action === "close-menu") closeMenu();
+    if (event.data?.action === "open-menu") openMenu();
+  });
   addMutationListener("body", () => {
     if ($menuModal?.hasAttribute("open")) updateTheme();
   });
diff --git a/src/core/menu/components.mjs b/src/core/menu/components.mjs
index 77133f6..e8720a6 100644
--- a/src/core/menu/components.mjs
+++ b/src/core/menu/components.mjs
@@ -492,6 +492,7 @@ function Select({ values, _get, _set, ...props }) {
   $select.onclick = (event) => {
     onclick?.(event);
     $popup.setAttribute("open", true);
+    setState({ popupOpen: true });
   };
   useState(["rerender"], () => {
     _get?.().then((value) => {
@@ -503,10 +504,16 @@ function Select({ values, _get, _set, ...props }) {
         setTimeout(() => {
           $select.style.width = "";
           $select.style.background = "";
+          setState({ popupOpen: false });
         }, 200);
       } else $select.innerText = value;
     });
   });
+  document.addEventListener("click", (event) => {
+    if (!$popup.hasAttribute("open")) return;
+    if ($popup.contains(event.target) || event.target === $select) return;
+    _set?.($select.innerText);
+  });
 
   return html`<div class="notion-enhancer--menu-select relative">
     ${$select}${$popup}
diff --git a/src/core/menu/menu.mjs b/src/core/menu/menu.mjs
index 3fefcd5..5b6c052 100644
--- a/src/core/menu/menu.mjs
+++ b/src/core/menu/menu.mjs
@@ -165,13 +165,33 @@ const render = async () => {
 window.addEventListener("focus", () => setState({ rerender: true }));
 window.addEventListener("message", (event) => {
   if (event.data?.namespace !== "notion-enhancer") return;
-  const [theme, icon] = getState(["theme", "icon"]);
+  const [hotkey, theme, icon] = getState(["hotkey", "theme", "icon"]);
   setState({
     rerender: true,
+    hotkey: event.data?.hotkey ?? hotkey,
     theme: event.data?.theme ?? theme,
     icon: event.data?.icon ?? icon,
   });
 });
+useState(["hotkey"], ([hotkey]) => {
+  const { addKeyListener } = globalThis.__enhancerApi ?? {},
+    [hotkeyRegistered] = getState(["hotkeyRegistered"]);
+  if (!hotkey || !addKeyListener || hotkeyRegistered) return;
+  setState({ hotkeyRegistered: true });
+  addKeyListener(hotkey, (event) => {
+    event.preventDefault();
+    const msg = { namespace: "notion-enhancer", action: "open-menu" };
+    parent?.postMessage(msg, "*");
+  });
+  addKeyListener("Escape", () => {
+    const [popupOpen] = getState(["popupOpen"]);
+    if (!popupOpen) {
+      const msg = { namespace: "notion-enhancer", action: "close-menu" };
+      parent?.postMessage(msg, "*");
+    } else setState({ rerender: true });
+  });
+});
+
 useState(["theme"], ([theme]) => {
   if (theme === "dark") document.body.classList.add("dark");
   if (theme === "light") document.body.classList.remove("dark");