@naturalcycles/js-lib
Version:
Standard library for universal (browser + Node.js) javascript
96 lines (95 loc) • 3.17 kB
JavaScript
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);