UNPKG

@v4fire/client

Version:

V4Fire client core library

177 lines (145 loc) 4 kB
/* eslint-disable @typescript-eslint/no-unused-vars-experimental */ /*! * V4Fire Client Core * https://github.com/V4Fire/Client * * Released under the MIT license * https://github.com/V4Fire/Client/blob/master/LICENSE */ /** * [[include:traits/i-open/README.md]] * @packageDocumentation */ import SyncPromise from 'core/promise/sync'; import type iBlock from 'super/i-block/i-block'; import type { ModsDecl, ModEvent, SetModEvent } from 'super/i-block/i-block'; import type { CloseHelperEvents } from 'traits/i-open/interface'; export * from 'traits/i-open/interface'; export default abstract class iOpen { /** * Trait modifiers */ static readonly mods: ModsDecl = { opened: [ 'true', 'false' ] }; /** @see [[iOpen.open]] */ static open: AddSelf<iOpen['open'], iBlock> = (component) => SyncPromise.resolve(component.setMod('opened', true)); /** @see [[iOpen.close]] */ static close: AddSelf<iOpen['close'], iBlock> = (component) => SyncPromise.resolve(component.setMod('opened', false)); /** @see [[iOpen.onOpenedChange]] */ static onOpenedChange: AddSelf<iOpen['onOpenedChange'], iBlock> = async (component) => { // Loopback }; /** @see [[iOpen.onKeyClose]] */ static onKeyClose: AddSelf<iOpen['onKeyClose'], iBlock & iOpen> = async (component, e) => { if (e.key === 'Escape') { await component.close(); } }; /** @see [[iOpen.onTouchClose]] */ static onTouchClose: AddSelf<iOpen['onTouchClose'], iBlock & iOpen> = async (component, e) => { const target = <CanUndef<Element>>e.target; if (target == null) { return; } if (!target.closest(`.${component.componentId}`)) { await component.close(); } }; /** * Initialize default event listeners to close a component by a keyboard or mouse * * @param component * @param [events] - map with events to listen */ static initCloseHelpers<T extends iBlock>(component: T & iOpen, events: CloseHelperEvents = {}): void { const {async: $a, localEmitter: $e} = component.unsafe; const helpersGroup = {group: 'closeHelpers'}, modsGroup = {group: 'closeHelpers:mods'}; $a.off({group: /closeHelpers/}); $e.on('block.mod.*.opened.*', component.onOpenedChange.bind(component), modsGroup); $e.on('block.mod.set.opened.false', () => $a.off(helpersGroup), modsGroup); const onOpened = () => { $a.setTimeout(() => { const opts = { ...helpersGroup, options: {passive: false} }; try { $a.on(document, events.key ?? 'keyup', (e) => { if (e != null) { return component.onKeyClose(e); } }, opts); $a.on(document, events.touch ?? 'click touchend', (e) => { if (e != null) { return component.onTouchClose(e); } }, opts); } catch {} }, 0, helpersGroup); }; $e.on('block.mod.set.opened.true', onOpened, modsGroup); } /** * Initializes modifier event listeners * * @emits `open()` * @emits `close()` * * @param component */ static initModEvents<T extends iBlock>(component: T): void { const {localEmitter: $e} = component.unsafe; $e.on('block.mod.*.opened.*', (e: ModEvent) => { if (e.type === 'remove' && e.reason !== 'removeMod') { return; } component.emit(e.value === 'false' || e.type === 'remove' ? 'close' : 'open'); }); } /** * Opens the component * @param args */ open(...args: unknown[]): Promise<boolean> { return Object.throw(); } /** * Closes the component * @param args */ close(...args: unknown[]): Promise<boolean> { return Object.throw(); } /** * Handler: the opened modifier has been changed * @param e */ onOpenedChange(e: ModEvent | SetModEvent): Promise<void> { return Object.throw(); } /** * Handler: closing by a keyboard event * @param e */ onKeyClose(e: KeyboardEvent): Promise<void> { return Object.throw(); } /** * Handler: closing by a touch event * @param e */ onTouchClose(e: MouseEvent): Promise<void> { return Object.throw(); } }