Browse Source

ref: massively improved menu style & UX

Sv443 1 year ago
parent
commit
546715748b
4 changed files with 111 additions and 68 deletions
  1. 4 0
      src/features/layout.css
  2. 55 26
      src/features/menu/menu_old.css
  3. 46 40
      src/features/menu/menu_old.ts
  4. 6 2
      src/tools/post-build.ts

+ 4 - 0
src/features/layout.css

@@ -1,5 +1,9 @@
 /* #MARKER misc */
 
+.bytm-disable-scroll {
+  overflow: hidden !important;
+}
+
 .bytm-generic-btn {
   display: inline-flex;
   align-items: center;

+ 55 - 26
src/features/menu/menu_old.css

@@ -1,14 +1,17 @@
-:root {
-  --bytm-menu-width: calc(min(70vw, 1000px));
-  --bytm-menu-bg: #212121;
-  --bytm-menu-bg-highlight: #111111;
+#bytm-menu-bg {
+  --bytm-menu-height-max: 750px;
+  --bytm-menu-width-max: 1000px;
+  --bytm-menu-bg: #333333;
+  --bytm-menu-bg-highlight: #1e1e1e;
+  --bytm-menu-separator-color: #797979;
+  --bytm-menu-border-radius: 15px;
 }
 
 #bytm-menu-bg {
   display: block;
   position: fixed;
-  width: 100vw;
-  height: 100vh;
+  width: 100%;
+  height: 100%;
   top: 0;
   left: 0;
   z-index: 15;
@@ -16,50 +19,68 @@
 }
 
 #bytm-menu {
-  display: inline-block;
   position: fixed;
-  width: var(--bytm-menu-width);
+  display: flex;
+  flex-direction: column;
+  justify-content: space-between;
+  width: calc(min(100% - 60px, var(--bytm-menu-width-max)));
+  border-radius: var(--bytm-menu-border-radius);
   height: auto;
-  left: calc((100vw - var(--bytm-menu-width)) / 2);
-  top: 10vh;
+  max-height: calc(min(100% - 40px, var(--bytm-menu-height-max)));
+  left: 50%;
+  top: 50%;
+  transform: translate(-50%, -50%);
   z-index: 16;
-  padding: 10px 25px;
   color: #fff;
   background-color: var(--bytm-menu-bg);
 }
 
 #bytm-menu-opts {
   position: relative;
-  max-height: 70vh;
   overflow: auto;
+  padding: 30px 0px;
 }
 
-#bytm-menu-titlecont {
+#bytm-menu-header {
   display: flex;
   justify-content: space-between;
-  padding: 8px 20px 15px 20px;
-  margin-bottom: 5px;
+  margin-bottom: 6px;
+  padding: 15px 20px 15px 20px;
   background-color: var(--bytm-menu-bg);
+  border: 1px solid var(--bytm-menu-separator-color);
+  border-style: none none solid none;
+  border-radius: var(--bytm-menu-border-radius) var(--bytm-menu-border-radius) 0px 0px;
+}
+
+#bytm-menu-titlecont {
+  display: flex;
+  align-items: center;
 }
 
 #bytm-menu-title {
-  font-size: 20px;
-  margin: 5px 0;
+  display: inline-block;
+  font-size: 22px;
 }
 
 #bytm-menu-linkscont {
   display: flex;
   align-items: center;
+  margin-left: 32px;
 }
 
 .bytm-menu-link {
   display: inline-flex;
   align-items: center;
-  margin-left: 10px;
   cursor: pointer;
 }
 
+.bytm-menu-link:not(:last-of-type) {
+  margin-right: 10px;
+}
+
 #bytm-menu-close {
+  width: 32px;
+  height: 32px;
   cursor: pointer;
 }
 
@@ -67,12 +88,12 @@
   display: flex;
   flex-direction: row;
   justify-content: space-between;
-  z-index: 100;
-  padding: 32px 20px 8px 20px;
-  position: sticky;
-  bottom: 0;
+  margin-top: 6px;
+  padding: 20px 20px 8px 20px;
   background: var(--bytm-menu-bg);
   background: linear-gradient(to bottom, rgba(0, 0, 0, 0) 0%, var(--bytm-menu-bg) 30%, var(--bytm-menu-bg) 100%);
+  border: 1px solid var(--bytm-menu-separator-color);
+  border-style: solid none none none;
   pointer-events: none;
 }
 
@@ -80,12 +101,20 @@
   pointer-events: initial;
 }
 
+#bytm-menu-version-cont {
+  display: flex;
+  justify-content: space-around;
+  font-size: 1.15em;
+  padding-bottom: 8px;
+  border-radius: var(--bytm-menu-border-radius) var(--bytm-menu-border-radius) 0px 0px;
+}
+
 #bytm-menu-scroll-indicator {
   --bytm-scroll-indicator-padding: 5px;
   position: sticky;
-  bottom: 60px;
+  bottom: 0;
   left: 50%;
-  margin-top: calc(-32px + var(--bytm-scroll-indicator-padding) * 2);
+  margin-top: calc(-32px - var(--bytm-scroll-indicator-padding) * 2);
   padding: var(--bytm-scroll-indicator-padding);
   transform: translateX(-50%);
   width: 32px;
@@ -97,14 +126,14 @@
 }
 
 .bytm-hidden {
-  visibility: hidden;
+  visibility: hidden !important;
 }
 
 .bytm-ftconf-category-header {
   font-size: 18px;
   margin-top: 32px;
   margin-bottom: 8px;
-  padding: 0 20px;
+  padding: 0px 20px;
 }
 
 .bytm-ftconf-category-header:first-of-type {

+ 46 - 40
src/features/menu/menu_old.ts

@@ -1,4 +1,4 @@
-import { debounce, isScrollable, pauseFor } from "@sv443-network/userutils";
+import { debounce, isScrollable } from "@sv443-network/userutils";
 import { defaultConfig, getFeatures, saveFeatures, setDefaultFeatures } from "../../config";
 import { scriptInfo } from "../../constants";
 import { FeatureCategory, FeatInfoKey, categoryNames, featInfo } from "../index";
@@ -9,7 +9,10 @@ import "./menu_old.css";
 //#MARKER create menu elements
 
 let isMenuOpen = false;
-let scrollIndicatorShown = true;
+
+/** Threshold in pixels from the top of the options container that dictates for how long the scroll indicator is shown */
+const scrollIndicatorOffsetThreshold = 30;
+let scrollIndicatorEnabled = true;
 
 /**
  * Adds an element to open the BetterYTM menu
@@ -32,22 +35,21 @@ export async function addMenu() {
   });
 
   const menuContainer = document.createElement("div");
-  menuContainer.title = "";
+  menuContainer.title = ""; // prevent bg title from propagating downwards
   menuContainer.id = "bytm-menu";
-  menuContainer.style.borderRadius = "15px";
-  menuContainer.style.display = "flex";
-  menuContainer.style.flexDirection = "column";
-  menuContainer.style.justifyContent = "space-between";
 
 
   //#SECTION title bar
+  const headerElem = document.createElement("div");
+  headerElem.id = "bytm-menu-header";
+
   const titleCont = document.createElement("div");
   titleCont.id = "bytm-menu-titlecont";
+  titleCont.role = "heading";
+  titleCont.ariaLevel = "1";
 
   const titleElem = document.createElement("h2");
   titleElem.id = "bytm-menu-title";
-  titleElem.role = "heading";
-  titleElem.ariaLevel = "1";
   titleElem.innerText = `${scriptInfo.name} - Configuration`;
 
   const linksCont = document.createElement("div");
@@ -79,16 +81,14 @@ export async function addMenu() {
   closeElem.id = "bytm-menu-close";
   closeElem.src = await getResourceUrl("close");
   closeElem.title = "Click to close the menu";
-  closeElem.style.marginLeft = "50px";
-  closeElem.style.width = "32px";
-  closeElem.style.height = "32px";
   closeElem.addEventListener("click", closeMenu);
 
-  linksCont.appendChild(closeElem);
-
   titleCont.appendChild(titleElem);
   titleCont.appendChild(linksCont);
 
+  headerElem.appendChild(titleCont);
+  headerElem.appendChild(closeElem);
+
 
   //#SECTION feature list
   const featuresCont = document.createElement("div");
@@ -258,10 +258,10 @@ export async function addMenu() {
   //#SECTION scroll indicator
   const scrollIndicator = document.createElement("img");
   scrollIndicator.id = "bytm-menu-scroll-indicator";
+  scrollIndicator.src = await getResourceUrl("arrow_down");
   scrollIndicator.role = "button";
   scrollIndicator.title = "Click to scroll to the bottom";
-  scrollIndicator.src = "data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+CjxzdmcgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiBoZWlnaHQ9IjQ4IiB2aWV3Qm94PSIwIC05NjAgOTYwIDk2MCIgd2lkdGg9IjQ4Ij4KICA8cGF0aCBmaWxsPSIjZmZmZmZmIiBkPSJNNDU3LjMwOC03NzkuOTk5djUxMy42OTJMMjEyLjAwMS01MTEuOTk5bC0zMiAzMS45OTlMNDgwLTE4MC4wMDEgNzc5Ljk5OS00ODBsLTMyLTMxLjk5OS0yNDUuMzA3IDI0NS42OTJ2LTUxMy42OTJoLTQ1LjM4NFoiLz4KPC9zdmc+";
-  //#DEBUG scrollIndicatorElem.src = await getResourceUrl("arrow_down");
+
   featuresCont.appendChild(scrollIndicator);
 
   scrollIndicator.addEventListener("click", () => {
@@ -271,20 +271,17 @@ export async function addMenu() {
     });
   });
 
-  featuresCont.addEventListener("scroll", async (evt: Event) => {
+  featuresCont.addEventListener("scroll", (evt: Event) => {
     const scrollPos = (evt.target as HTMLDivElement)?.scrollTop ?? 0;
     const scrollIndicator = document.querySelector<HTMLImageElement>("#bytm-menu-scroll-indicator");
 
     if(!scrollIndicator)
       return;
 
-    if(scrollPos > 10 && scrollIndicatorShown) {
-      scrollIndicatorShown = false;
+    if(scrollIndicatorEnabled && scrollPos > scrollIndicatorOffsetThreshold && !scrollIndicator.classList.contains("bytm-hidden")) {
       scrollIndicator.classList.add("bytm-hidden");
-      await pauseFor(200);
     }
-    else if(scrollPos <= 10 && !scrollIndicatorShown) {
-      scrollIndicatorShown = true;
+    else if(scrollIndicatorEnabled && scrollPos <= scrollIndicatorOffsetThreshold && scrollIndicator.classList.contains("bytm-hidden")) {
       scrollIndicator.classList.remove("bytm-hidden");
     }
   });
@@ -326,35 +323,29 @@ export async function addMenu() {
   footerCont.appendChild(footerElem);
   footerCont.appendChild(resetElem);
 
-  featuresCont.appendChild(footerCont);
-
 
   //#SECTION finalize
-  const menuBody = document.createElement("div");
-  menuBody.id = "bytm-menu-body";
-  menuBody.appendChild(titleCont);
-  menuBody.appendChild(featuresCont);
+  menuContainer.appendChild(headerElem);
+  menuContainer.appendChild(featuresCont);
 
   const versionCont = document.createElement("div");
-  versionCont.style.display = "flex";
-  versionCont.style.justifyContent = "space-around";
-  versionCont.style.fontSize = "1.15em";
-  versionCont.style.marginTop = "5px";
+  versionCont.id = "bytm-menu-version-cont";
 
   const versionElem = document.createElement("span");
   versionElem.id = "bytm-menu-version";
   versionElem.innerText = `v${scriptInfo.version}`;
 
   versionCont.appendChild(versionElem);
-  featuresCont.appendChild(versionCont);
 
-  menuContainer.appendChild(menuBody);
+  menuContainer.appendChild(footerCont);
   menuContainer.appendChild(versionCont);
 
   backgroundElem.appendChild(menuContainer);
 
   document.body.appendChild(backgroundElem);
 
+  window.addEventListener("resize", debounce(checkToggleScrollIndicator, 150));
+
   log("Added menu element");
 }
 
@@ -367,7 +358,7 @@ export function closeMenu(evt?: MouseEvent | KeyboardEvent) {
   isMenuOpen = false;
   evt?.bubbles && evt.stopPropagation();
 
-  document.body.removeAttribute("no-y-overflow");
+  document.body.classList.remove("bytm-disable-scroll");
   const menuBg = document.querySelector("#bytm-menu-bg") as HTMLElement;
 
   menuBg.style.visibility = "hidden";
@@ -380,19 +371,34 @@ export function openMenu() {
     return;
   isMenuOpen = true;
 
-  document.body.setAttribute("no-y-overflow", "");
+  document.body.classList.add("bytm-disable-scroll");
   const menuBg = document.querySelector("#bytm-menu-bg") as HTMLElement;
 
   menuBg.style.visibility = "visible";
   menuBg.style.display = "block";
 
+  checkToggleScrollIndicator();
+}
+
+/** Checks if the features container is scrollable and toggles the scroll indicator accordingly */
+function checkToggleScrollIndicator() {
   const featuresCont = document.querySelector<HTMLElement>("#bytm-menu-opts");
   const scrollIndicator = document.querySelector<HTMLElement>("#bytm-menu-scroll-indicator");
 
+  
   // disable scroll indicator if container doesn't scroll
-  if(featuresCont && scrollIndicator && !isScrollable(featuresCont).vertical) {
-    scrollIndicatorShown = false;
-    scrollIndicator.classList.add("hidden");
-    scrollIndicator.style.visibility = "hidden";
+  if(featuresCont && scrollIndicator) {
+    const verticalScroll = isScrollable(featuresCont).vertical;
+    /** If true, the indicator's threshold is under the available scrollable space and so it should be disabled */
+    const underThreshold = featuresCont.scrollHeight - featuresCont.clientHeight <= scrollIndicatorOffsetThreshold;
+
+    if(!underThreshold && verticalScroll && !scrollIndicatorEnabled) {
+      scrollIndicatorEnabled = true;
+      scrollIndicator.classList.remove("bytm-hidden");
+    }
+    if((!verticalScroll && scrollIndicatorEnabled) || underThreshold) {
+      scrollIndicatorEnabled = false;
+      scrollIndicator.classList.add("bytm-hidden");
+    }
   }
 }

+ 6 - 2
src/tools/post-build.ts

@@ -113,8 +113,12 @@ I welcome every contribution on GitHub!
     const sizeKiB = Number((Buffer.byteLength(finalUserscript, "utf8") / 1024).toFixed(2));
 
     let buildStats: Partial<BuildStats> = {};
-    if(await exists(".build.json"))
-      buildStats = JSON.parse(String(await readFile(".build.json"))) as BuildStats;
+    if(await exists(".build.json")) {
+      try {
+        buildStats = JSON.parse(String(await readFile(".build.json"))) as BuildStats;
+      }
+      catch(e) { void e; }
+    }
 
     let sizeIndicator = "";
     if(buildStats.sizeKiB) {