From 736010799c438525e76654117ae06c4cff87ba3c Mon Sep 17 00:00:00 2001 From: GooseOb Date: Mon, 6 Jan 2025 18:09:07 +0100 Subject: [PATCH] turn menu into a module --- package.json | 2 +- src/listeners/video-page.ts | 6 +-- src/menu/close.ts | 29 +++++++++++++ src/menu/init.ts | 43 ++++++++++--------- src/menu/section.ts | 4 +- src/menu/value.ts | 85 ++++++++++++++++--------------------- 6 files changed, 94 insertions(+), 75 deletions(-) create mode 100644 src/menu/close.ts diff --git a/package.json b/package.json index 5a1e850..bbf6845 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "yt-defaulter", "author": "GooseOb", - "version": "1.11.15", + "version": "1.11.16", "repository": { "type": "git", "url": "git+https://github.com/GooseOb/YT-Defaulter.git" diff --git a/src/listeners/video-page.ts b/src/listeners/video-page.ts index 4f3f2f2..1ebb4ed 100644 --- a/src/listeners/video-page.ts +++ b/src/listeners/video-page.ts @@ -21,12 +21,12 @@ export const onVideoPage = async () => { applySettings(computeSettings(doNotChangeSpeed)); - if (!menu.value.element) { + if (!menu.element) { menu.init(); } }); - if (menu.value.element) { - menu.updateThisChannel(config.channel.get()); + if (menu.element) { + menu.updateThisChannel(); } }; diff --git a/src/menu/close.ts b/src/menu/close.ts new file mode 100644 index 0000000..ae04958 --- /dev/null +++ b/src/menu/close.ts @@ -0,0 +1,29 @@ +import { isDescendantOrTheSame } from '../utils'; +import { element, btn } from './value'; + +export const close = () => { + element.style.visibility = 'hidden'; + stopListening(); +}; + +export const listenForClose = () => { + document.addEventListener('click', onClick); + document.addEventListener('keyup', onKeyUp); +}; + +const stopListening = () => { + document.removeEventListener('click', onClick); + document.removeEventListener('keyup', onKeyUp); +}; + +const onClick = (e: Event) => { + const el = e.target as HTMLElement; + if (!isDescendantOrTheSame(el, [element, btn])) close(); +}; + +const onKeyUp = (e: KeyboardEvent) => { + if (e.code === 'Escape') { + close(); + btn.focus(); + } +}; diff --git a/src/menu/init.ts b/src/menu/init.ts index 1a6b091..641c7d8 100644 --- a/src/menu/init.ts +++ b/src/menu/init.ts @@ -11,7 +11,7 @@ import { section } from './section'; import { settingsIcon } from './settings-icon'; import * as config from '../config'; import { withOnClick } from '../utils/with'; -import { value } from './value'; +import * as menu from './value'; import { untilAppear } from '../utils'; import * as get from '../element-getters'; @@ -72,24 +72,25 @@ export const init = () => { }) ); - value.btn = withOnClick( - button('', { - id: BTN_ID, - ariaLabel: text.OPEN_SETTINGS, - tabIndex: 0, + menu.set( + div({ + id: MENU_ID, }), - () => { - value.toggle(); - } + withOnClick( + button('', { + id: BTN_ID, + ariaLabel: text.OPEN_SETTINGS, + tabIndex: 0, + }), + menu.toggle + ) ); - value.btn.setAttribute('aria-controls', MENU_ID); - value.btn.classList.add(btnClass + '--icon-button'); - value.btn.append(settingsIcon()); - value.element = div({ - id: MENU_ID, - }); - value.element.append( + menu.btn.setAttribute('aria-controls', MENU_ID); + menu.btn.classList.add(btnClass + '--icon-button'); + menu.btn.append(settingsIcon()); + + menu.element.append( sections, controlCheckboxDiv('shorts', 'shortsToUsual', text.SHORTS), controlCheckboxDiv('new-tab', 'newTab', text.NEW_TAB), @@ -107,20 +108,20 @@ export const init = () => { controlDiv, controlStatus ); - value.element.addEventListener('keyup', (e) => { + menu.element.addEventListener('keyup', (e) => { const el = e.target as HTMLInputElement; if (e.code === 'Enter' && el.type === 'checkbox') el.checked = !el.checked; }); untilAppear(get.actionsBar).then((actionsBar) => { - actionsBar.insertBefore(value.btn, actionsBar.lastChild); - get.popupContainer().append(value.element); - value.width = value.element.getBoundingClientRect().width; + actionsBar.insertBefore(menu.btn, actionsBar.lastChild); + get.popupContainer().append(menu.element); + menu.adjustWidth(); sections.style.maxWidth = sections.offsetWidth + 'px'; }); const listener = () => { - if (value.isOpen) value.fixPosition(); + if (menu.isOpen) menu.fixPosition(); }; window.addEventListener('scroll', listener); window.addEventListener('resize', listener); diff --git a/src/menu/section.ts b/src/menu/section.ts index 32a67b6..19a0012 100644 --- a/src/menu/section.ts +++ b/src/menu/section.ts @@ -4,7 +4,7 @@ import { withControlListeners, withHint } from '../utils/with'; import { getControlCreators } from './get-controls-creators'; import { validateVolume } from './validate-volume'; import { Hint } from '../hint'; -import { value } from './value'; +import { setFirstFocusable } from './value'; import { getElCreator } from '../utils'; import { speedNormal } from '../player'; import * as controls from './controls'; @@ -52,7 +52,7 @@ export const section = ( values: ['2', '1.75', '1.5', '1.25', speedNormal, '0.75', '0.5', '0.25'], getText: (val) => val, }); - if (sectionId === SECTION_GLOBAL) value.firstFocusable = speedSelect.elem; + if (sectionId === SECTION_GLOBAL) setFirstFocusable(speedSelect.elem); const sectionElement = div({ role: 'group' }); sectionElement.setAttribute('aria-labelledby', sectionId); diff --git a/src/menu/value.ts b/src/menu/value.ts index d729222..fbdbf6a 100644 --- a/src/menu/value.ts +++ b/src/menu/value.ts @@ -1,52 +1,41 @@ -import { debounce, isDescendantOrTheSame } from '../utils'; +import { debounce } from '../utils'; +import { close, listenForClose } from './close'; + +export const set = (el: HTMLDivElement, btnEl: HTMLButtonElement) => { + element = el; + btn = btnEl; +}; + +export let element = null as HTMLDivElement; +export let btn = null as HTMLButtonElement; +export let isOpen = false; + +let menuWidth = 0; +export const adjustWidth = () => { + menuWidth = element.getBoundingClientRect().width; +}; type Focusable = { focus(): void }; +let firstFocusable = null as Focusable; +export const setFirstFocusable = (el: Focusable) => { + firstFocusable = el; +}; + +export const toggle = debounce(() => { + isOpen = !isOpen; + if (isOpen) { + fixPosition(); + element.style.visibility = 'visible'; + listenForClose(); + firstFocusable.focus(); + } else { + close(); + } +}, 100); -export const value = { - element: null as HTMLDivElement, - btn: null as HTMLButtonElement, - isOpen: false, - width: 0, - _closeListener: { - onClick(e: Event) { - const el = e.target as HTMLElement; - if (!isDescendantOrTheSame(el, [value.element, value.btn])) - value.toggle(); - }, - onKeyUp(e: KeyboardEvent) { - if (e.code === 'Escape') { - value._setOpen(false); - value.btn.focus(); - } - }, - add() { - document.addEventListener('click', this.onClick); - document.addEventListener('keyup', this.onKeyUp); - }, - remove() { - document.removeEventListener('click', this.onClick); - document.removeEventListener('keyup', this.onKeyUp); - }, - }, - firstFocusable: null as Focusable, - _setOpen(bool: boolean) { - if (bool) { - this.fixPosition(); - this.element.style.visibility = 'visible'; - this._closeListener.add(); - this.firstFocusable.focus(); - } else { - this.element.style.visibility = 'hidden'; - this._closeListener.remove(); - } - this.isOpen = bool; - }, - toggle: debounce(function () { - this._setOpen(!this.isOpen); - }, 100), - fixPosition() { - const { y, height, width, left } = this.btn.getBoundingClientRect(); - this.element.style.top = y + height + 8 + 'px'; - this.element.style.left = left + width - this.width + 'px'; - }, +export const fixPosition = () => { + const { y, height, width, left } = btn.getBoundingClientRect(); + element.style.top = y + height + 8 + 'px'; + element.style.left = left + width - menuWidth + 'px'; + console.log(element.style.left); };