shrooms-vb-web/toolkit/Toolkit.js

134 lines
3.9 KiB
JavaScript
Raw Normal View History

2025-02-18 22:39:36 +00:00
import App from /**/"./App.js";
import Component from /**/"./Component.js";
import Group from /**/"./Group.js";
import MenuBar from /**/"./MenuBar.js";
import MenuItem from /**/"./MenuItem.js";
// Pseudo environment context
let _package = {};
// GUI widget toolkit root
class Toolkit {
//////////////////////////////// Constants ////////////////////////////////
// Event keys
static group = Symbol(); // Events emitted by Toolkit.Group
static target = Symbol(); // Event target as a Toolkit.Component
///////////////////////// Initialization Methods //////////////////////////
Toolkit() { throw new Error("Cannot be instantiated."); }
static {
// Environment members
Object.assign(_package, {
componentKey: Symbol("Toolkit component"),
darkQuery : window.matchMedia("(prefers-color-scheme:dark)"),
nextId : 0n,
override : this.#override,
underride : this.#underride
});
// Register package classes with the Toolkit namespace
_package.register = ()=>{
this.Component = Component(this, _package);
this.App = App (this, _package);
this.Group = Group (this, _package);
this.MenuBar = MenuBar (this, _package);
this.MenuItem = MenuItem (this, _package);
Object.freeze(this);
Object.seal (this);
};
}
///////////////////////////// Static Methods //////////////////////////////
// Resolve the Toolkit.Component for an HTML element
static component(element) {
return element[_package.componentKey] ?? null;
}
// Terminate an event
static consume(event) {
event.preventDefault();
event.stopPropagation();
}
// Generate a unique element ID
static id() {
return "tk-" + _package.nextId++;
}
// Determine whether the user dark mode preference is active
static isDark() {
return _package.darkQuery.matches;
}
// Determine whether an element is fully visible
static isVisible(element, cache = null) {
cache ??= new Map();
for (let e = element; e instanceof Element; e = e.parentNode) {
let style;
if (!cache.has(e))
cache.set(e, style = getComputedStyle(e));
else style = cache.get(e);
if (style.display == "none" || style.visibility == "hidden")
return false;
}
return true;
}
// Generate a list of focusable descendant elements
static listFocusable(element) {
let cache = new Map();
return Array.from(element.querySelectorAll(
"*:is(a[href],area,button,details,input,textarea,select," +
"[tabindex='0']):not([disabled])"
)).filter(e=>this.isVisible(e, cache));
}
static stylesheet(url) {
let style = document.createElement("link");
style.rel = "stylesheet";
style.href = url;
return style;
}
///////////////////////////// Package Methods /////////////////////////////
// Process overrides for Toolkit.Component initialization
static #override(fromCaller, fromSelf) {
fromCaller = Object.assign({}, fromCaller ?? {});
fromSelf = Object.assign({}, fromSelf ?? {});
fromSelf.style = Object.assign(
fromSelf.style ?? {}, fromCaller.style ?? {});
delete fromCaller.style;
Object.assign(fromSelf, fromCaller);
return fromSelf;
}
// Extract override properties for later processing
static #underride(overrides, underrides) {
let ret = {};
for (let entry of Object.entries(underrides)) {
ret[entry[0]] = overrides[entry[0]] ?? underrides[entry[1]];
delete overrides[entry[0]];
}
return ret;
}
}
_package.register();
export default Toolkit;