UNPKG

@naturalcycles/js-lib

Version:

Standard library for universal (browser + Node.js) javascript

96 lines (95 loc) 3.17 kB
import { __decorate } from "tslib"; import { _Memo } from '../decorators/memo.decorator.js'; import { isServerSide } from '../env.js'; import { _stringify } from '../string/stringify.js'; const RED_DOT_ID = '__red-dot__'; const NOOP = () => { }; /** * @experimental * * Allows to listen for AdminMode keypress combination (Ctrl+Shift+L by default) to toggle AdminMode, * indicated by RedDot DOM element. * * todo: help with Authentication */ export class AdminService { constructor(cfg) { this.cfg = { predicate: e => e.ctrlKey && e.key === 'L', persistToLocalStorage: true, localStorageKey: '__adminMode__', onRedDotClick: NOOP, onChange: NOOP, beforeEnter: () => true, beforeExit: () => true, ...cfg, }; } cfg; adminMode = false; listening = false; /** * Start listening to keyboard events to toggle AdminMode when detected. */ startListening() { if (this.listening || isServerSide()) return; this.adminMode = !!localStorage.getItem(this.cfg.localStorageKey); if (this.adminMode) this.toggleRedDotVisibility(); document.addEventListener('keydown', this.keydownListener.bind(this), { passive: true }); this.listening = true; } stopListening() { if (isServerSide()) return; document.removeEventListener('keydown', this.keydownListener); this.listening = false; } async keydownListener(e) { // console.log(e) if (!this.cfg.predicate(e)) return; await this.toggleRedDot(); } async toggleRedDot() { try { const allow = await this.cfg[this.adminMode ? 'beforeExit' : 'beforeEnter'](); if (!allow) return; // no change } catch (err) { console.error(err); // ok to show alert to Admins, it's not user-facing alert(_stringify(err)); return; // treat as "not allowed" } this.adminMode = !this.adminMode; this.toggleRedDotVisibility(); if (this.cfg.persistToLocalStorage) { const { localStorageKey } = this.cfg; if (this.adminMode) { localStorage.setItem(localStorageKey, '1'); } else { localStorage.removeItem(localStorageKey); } } this.cfg.onChange(this.adminMode); } toggleRedDotVisibility() { this.getRedDotElement().style.display = this.adminMode ? 'block' : 'none'; } getRedDotElement() { const el = document.createElement('div'); el.id = RED_DOT_ID; el.style.cssText = 'position:fixed;width:24px;height:24px;margin-top:-12px;background-color:red;opacity:0.5;top:50%;left:0;z-index:9999999;cursor:pointer;border-radius:0 3px 3px 0'; el.addEventListener('click', () => this.cfg.onRedDotClick()); document.body.append(el); return el; } } __decorate([ _Memo() ], AdminService.prototype, "getRedDotElement", null);