Forráskód Böngészése

feat: cool new toggle button

Sven 1 éve
szülő
commit
7904918a37

+ 6 - 7
src/components/BytmDialog.css

@@ -1,3 +1,7 @@
+:root {
+  --bytm-dialog-accent-col: #3683d4;
+}
+
 .bytm-dialog-bg {
   --bytm-dialog-bg: #333333;
   --bytm-dialog-bg-highlight: #252525;
@@ -258,14 +262,9 @@
   margin-right: 10px;
 }
 
-.bytm-toggle-label {
-  padding-left: 10px;
-  padding-right: 5px;
-}
-
 .bytm-ftconf-input.bytm-hotkey-input {
   cursor: pointer;
-  min-width: 50px;
+  min-width: 80px;
 }
 
 .bytm-ftconf-input[type=number] {
@@ -396,5 +395,5 @@ hr {
 }
 
 #bytm-ftitem-locale-adornment svg path {
-  fill: #4595c7;
+  fill: var(--bytm-dialog-accent-col, #4595c7);
 }

+ 8 - 6
src/components/BytmDialog.ts

@@ -23,11 +23,11 @@ export interface BytmDialogOptions {
   /** Whether the menu should have a smaller overall appearance - defaults to false */
   smallMenu?: boolean;
   /** Called to render the body of the dialog */
-  renderBody: () => HTMLElement;
+  renderBody: () => HTMLElement | Promise<HTMLElement>;
   /** Called to render the header of the dialog - leave undefined for a blank header */
-  renderHeader?: () => HTMLElement;
+  renderHeader?: () => HTMLElement | Promise<HTMLElement>;
   /** Called to render the footer of the dialog - leave undefined for no footer */
-  renderFooter?: () => HTMLElement;
+  renderFooter?: () => HTMLElement | Promise<HTMLElement>;
 }
 
 /** ID of the last opened (top-most) dialog */
@@ -247,7 +247,7 @@ export class BytmDialog extends NanoEmitter<{
       headerTitleWrapperEl.role = "heading";
       headerTitleWrapperEl.ariaLevel = "1";
 
-      headerTitleWrapperEl.appendChild(header);
+      headerTitleWrapperEl.appendChild(header instanceof Promise ? await header : header);
       headerWrapperEl.appendChild(headerTitleWrapperEl);
     }
     else
@@ -273,7 +273,9 @@ export class BytmDialog extends NanoEmitter<{
     menuBodyElem.classList.add("bytm-dialog-body");
     this.options.smallMenu && menuBodyElem.classList.add("small");
 
-    menuBodyElem.appendChild(this.options.renderBody());
+    const body = this.options.renderBody();
+
+    menuBodyElem.appendChild(body instanceof Promise ? await body : body);
     dialogWrapperEl.appendChild(menuBodyElem);
 
     //#SECTION footer
@@ -282,7 +284,7 @@ export class BytmDialog extends NanoEmitter<{
       const footerWrapper = document.createElement("div");
       footerWrapper.classList.add("bytm-dialog-footer-cont");
       dialogWrapperEl.appendChild(footerWrapper);
-      footerWrapper.appendChild(footer);
+      footerWrapper.appendChild(footer instanceof Promise ? await footer : footer);
     }
 
     return dialogWrapperEl;

+ 1 - 0
src/components/index.ts

@@ -1,2 +1,3 @@
 export * from "./BytmDialog";
 export * from "./hotkeyInput";
+export * from "./toggle";

+ 65 - 0
src/components/toggle.css

@@ -0,0 +1,65 @@
+.bytm-toggle-wrapper {
+  --toggle-height: 24px;
+  --toggle-width: 48px;
+  --toggle-color-on: var(--bytm-dialog-accent-col, #4595c7);
+  --toggle-color-off: #707070;
+
+  display: flex;
+  align-items: center;
+}
+
+.bytm-toggle-wrapper .bytm-toggle-label {
+  cursor: pointer;
+  font-size: 1.5rem;
+  padding: 5px 10px;
+}
+
+/* sauce: https://danklammer.com/articles/simple-css-toggle-switch/ */
+
+.bytm-toggle {
+  appearance: none;
+  width: var(--toggle-width);
+  height: var(--toggle-height);
+  display: inline-block;
+  position: relative;
+  border-radius: 50px;
+  margin: 0px;
+  overflow: hidden;
+  outline: none;
+  border: none;
+  cursor: pointer;
+  background-color: var(--toggle-color-off);
+  transition: background-color ease 0.2s;
+}
+
+.bytm-toggle:before {
+  content: " ";
+  cursor: pointer;
+  display: block;
+  position: absolute;
+  z-index: 2;
+  width: calc(var(--toggle-height) - 4px);
+  height: calc(var(--toggle-height) - 4px);
+  background: #fff;
+  border-radius: 50%;
+  font: 10px/28px Helvetica;
+  text-transform: uppercase;
+  font-weight: bold;
+  text-indent: -22px;
+  word-spacing: 37px;
+  color: #fff;
+  left: 2px;
+  top: 2px;
+  text-shadow: -1px -1px rgba(0,0,0,0.15);
+  white-space: nowrap;
+  box-shadow: 0 1px 2px rgba(0,0,0,0.2);
+  transition: all ease 0.2s;
+}
+
+.bytm-toggle:checked {
+  background-color: var(--toggle-color-on);
+}
+
+.bytm-toggle:checked:before {
+  left: calc(var(--toggle-width) - 22px);
+}

+ 64 - 0
src/components/toggle.ts

@@ -0,0 +1,64 @@
+import { randomId } from "@sv443-network/userutils";
+import { t } from "../utils";
+import "./toggle.css";
+
+export interface ToggleProps {
+  /** Callback function that is called when the toggle is changed */
+  onChange: (value: boolean) => void;
+  /** Initial value of the toggle - defaults to false */
+  initialValue?: boolean;
+  /** If unspecified, a random ID is generated */
+  id?: string;
+  /** Position of the label relative to the toggle */
+  labelPos?: "left" | "right";
+}
+
+/** Creates a simple toggle element */
+export async function createToggle({
+  onChange,
+  initialValue = false,
+  id = randomId(8, 24),
+  labelPos = "left",
+}: ToggleProps) {
+  const wrapperEl = document.createElement("div");
+  wrapperEl.classList.add("bytm-toggle-wrapper", "bytm-no-select");
+  wrapperEl.role = "switch";
+  wrapperEl.tabIndex = 0;
+  wrapperEl.ariaValueText = t(`toggled_${initialValue ? "on" : "off"}`);
+
+  const labelEl = document.createElement("label");
+  labelEl.classList.add("bytm-toggle-label");
+  labelEl.textContent = t(`toggled_${initialValue ? "on" : "off"}`);
+  if(id)
+    labelEl.htmlFor = `bytm-toggle-${id}`;
+
+  const toggleEl = document.createElement("input");
+  toggleEl.classList.add("bytm-toggle");
+  toggleEl.type = "checkbox";
+  toggleEl.checked = initialValue;
+  toggleEl.tabIndex = -1;
+  if(id)
+    toggleEl.id = `bytm-toggle-${id}`;
+
+  const toggleElClicked = (e: Event) => {
+    e.preventDefault();
+    e.stopPropagation();
+
+    onChange(toggleEl.checked);
+    labelEl.textContent = wrapperEl.ariaValueText = t(`toggled_${toggleEl.checked ? "on" : "off"}`);
+  };
+
+  toggleEl.addEventListener("change", toggleElClicked);
+  wrapperEl.addEventListener("keydown", (e) => {
+    if(["Space", " ", "Enter"].includes(e.code)) {
+      toggleEl.checked = !toggleEl.checked;
+      toggleElClicked(e);
+    }
+  });
+
+  labelPos === "left" && wrapperEl.appendChild(labelEl);
+  wrapperEl.appendChild(toggleEl);
+  labelPos === "right" && wrapperEl.appendChild(labelEl);
+
+  return wrapperEl;
+}

+ 1 - 1
src/menu/menu_old.css

@@ -390,5 +390,5 @@ hr {
 }
 
 #bytm-ftitem-locale-adornment svg path {
-  fill: #4595c7;
+  fill: var(--bytm-dialog-accent-col, #4595c7);
 }

+ 12 - 15
src/menu/menu_old.ts

@@ -7,7 +7,7 @@ import { formatVersion } from "../config";
 import { emitSiteEvent, siteEvents } from "../siteEvents";
 import type { FeatureCategory, FeatureKey, FeatureConfig, HotkeyObj, FeatureInfo } from "../types";
 import "./menu_old.css";
-import { createHotkeyInput } from "../components";
+import { createHotkeyInput, createToggle } from "../components";
 import pkg from "../../package.json" assert { type: "json" };
 
 //#MARKER create menu elements
@@ -335,7 +335,8 @@ async function addCfgMenu() {
         switch(type)
         {
         case "toggle":
-          inputType = "checkbox";
+          inputTag = undefined;
+          inputType = undefined;
           break;
         case "slider":
           inputType = "range";
@@ -398,16 +399,6 @@ async function addCfgMenu() {
                 labelElem.textContent = fmtVal(Number(inputElem.value)) + unitTxt;
             });
           }
-          else if(type === "toggle") {
-            labelElem = document.createElement("label");
-            labelElem.classList.add("bytm-ftconf-label", "bytm-toggle-label");
-            labelElem.textContent = toggleLabelText(Boolean(initialVal)) + unitTxt;
-
-            inputElem.addEventListener("input", () => {
-              if(labelElem)
-                labelElem.textContent = toggleLabelText(inputElem.checked) + unitTxt;
-            });
-          }
           else if(type === "select") {
             const ftOpts = typeof ftInfo.options === "function"
               ? ftInfo.options()
@@ -445,9 +436,15 @@ async function addCfgMenu() {
           case "hotkey":
             wrapperElem = createHotkeyInput({
               initialValue: initialVal as HotkeyObj,
-              onChange: (hotkey) => {
-                confChanged(featKey as keyof FeatureConfig, initialVal, hotkey);
-              },
+              onChange: (hotkey) => confChanged(featKey as keyof FeatureConfig, initialVal, hotkey),
+            });
+            break;
+          case "toggle":
+            wrapperElem = await createToggle({
+              onChange: (checked) => confChanged(featKey as keyof FeatureConfig, initialVal, checked),
+              id: `ftconf-${featKey}`,
+              initialValue: Boolean(initialVal),
+              labelPos: "left",
             });
             break;
           }