@protorians/widgets
Version:
Create your web user interfaces with widgets
511 lines (510 loc) • 16.4 kB
JavaScript
import { Environment, MetricRandom, Signal, TextUtility, TreatmentQueueStatus, } from "@protorians/core";
import { ObjectElevation, WidgetsNativeProperty } from "./enums.js";
import { Widgets } from "./widgets.js";
import { StyleWidget } from "./style.js";
import { SpectraElement } from "@protorians/spectra";
export const WidgetNativeProperties = Object.values(WidgetsNativeProperty);
export class ContextWidget {
widget;
root;
props;
state;
engine;
constructor(widget, props, state) {
this.widget = widget;
this.props = props || {};
this.state = state || {};
}
}
export class WidgetNode {
element;
_fingerprint;
_isConnected = false;
_reference;
_tag = 'div';
_attributes = {};
_props = {};
_signal;
_locked = false;
_context = undefined;
_stylesheet = undefined;
_mounted = false;
constructor(declaration) {
this.extractProperties(declaration);
this.element = Environment.Client
? document.createElement(this.tag)
: new SpectraElement(this.tag);
this._fingerprint = `${MetricRandom.CreateAlpha(6).join('')}-${MetricRandom.Create(10).join('')}`;
this._signal = new Signal.Stack;
this.mount(() => this._isConnected = true);
}
static get style() {
return undefined;
}
static get attributes() {
return undefined;
}
static get children() {
return undefined;
}
static mount(widget) {
return widget;
}
static unmount(widget) {
return widget;
}
get tag() {
return this._tag;
}
get kind() {
return 'view';
}
get fingerprint() {
return this._fingerprint;
}
get isConnected() {
return this._isConnected || ((Environment.Client && this.clientElement) ? this.clientElement.isConnected : false);
}
get clientElement() {
return Environment.Client ? this.element || undefined : undefined;
}
get serverElement() {
return Environment.Client ? undefined : this.element || undefined;
}
get children() {
return this._props.children;
}
get attributes() {
return this._attributes;
}
get props() {
return this._props;
}
get datasets() {
const dataset = {};
const entries = Environment.Client
? Object.entries(this.clientElement?.dataset || {})
: [...this.serverElement?.blueprint.attributes.entries() || []]
.filter(x => x.toString().startsWith('data-'));
for (const [key, value] of entries)
dataset[TextUtility.camelCase(key)] = value;
return dataset;
}
get reference() {
return this._reference || undefined;
}
get locked() {
return this._locked;
}
set locked(value) {
this._locked = value;
if (this._locked)
this.lock();
else
this.unlock();
}
get signal() {
return this._signal;
}
get measure() {
return {
x: 0,
y: 0,
top: 0,
bottom: 0,
left: 0,
right: 0,
width: 0,
height: 0,
...(Environment.Client ? this.clientElement?.getBoundingClientRect().toJSON() : {}),
};
}
get stylesheet() {
this._stylesheet = this._stylesheet || (new StyleWidget()).bind(this);
return this._stylesheet;
}
get context() {
return this._context;
}
useContext(context) {
this._context = context;
return this;
}
construct(callback) {
this._signal.listen('construct', callback);
return this;
}
mount(callback) {
this._signal.listen('mount', payload => {
if (!this._mounted) {
this._mounted = true;
return callback(payload);
}
});
return this;
}
unmount(callback) {
this._signal.listen('unmount', payload => {
if (this._mounted) {
this._mounted = false;
return callback(payload);
}
});
return this;
}
ready(callback) {
if (!this._isConnected && this.context?.root) {
callback({
root: this.context?.root,
widget: this,
payload: this,
});
}
else
this.signal.listen('mount', (payload) => {
callback(payload);
return TreatmentQueueStatus.SnapOut;
});
return this;
}
before(callback) {
this._signal.listen('before', callback);
return this;
}
after(callback) {
this._signal.listen('after', callback);
return this;
}
get(state) {
return Environment.Client ? (this.clientElement && typeof this.clientElement[state] !== "undefined" ? this.clientElement[state] : undefined) : undefined;
}
set(state) {
if (Environment.Client && this.clientElement)
this.clientElement[state] = state;
return this;
}
clear() {
if (this._context) {
this._context?.engine?.clear(this);
}
if (!this._context)
this._signal.listen('mount', () => {
this._context?.engine?.clear(this);
return TreatmentQueueStatus.SnapOut;
});
return this;
}
remove() {
if (this._context)
this._context?.engine?.remove(this);
if (!this._context)
this._signal.listen('mount', () => {
this._context?.engine?.remove(this);
return TreatmentQueueStatus.SnapOut;
});
return this;
}
enable() {
if (this._context)
this._context.engine?.enable(this);
else if (!this._context)
this._signal.listen('mount', () => {
this._context?.engine?.enable(this);
return TreatmentQueueStatus.SnapOut;
});
return this;
}
disable() {
if (this._context)
this._context.engine?.disable(this);
else if (!this._context)
this._signal.listen('mount', () => {
this._context?.engine?.disable(this);
return TreatmentQueueStatus.SnapOut;
});
return this;
}
lock() {
if (this._context)
this._context.engine?.lock(this);
else if (!this._context)
this._signal.listen('mount', () => {
this._context?.engine?.lock(this);
return TreatmentQueueStatus.SnapOut;
});
return this;
}
focus() {
if (this._context)
this._context.engine?.focus(this);
else if (!this._context)
this._signal.listen('mount', () => {
this._context?.engine?.focus(this);
return TreatmentQueueStatus.SnapOut;
});
return this;
}
blur() {
if (this._context)
this._context.engine?.blur(this);
else if (!this._context)
this._signal.listen('mount', () => {
this._context?.engine?.blur(this);
return TreatmentQueueStatus.SnapOut;
});
return this;
}
unlock() {
if (this._context)
this._context.engine?.unlock(this);
else if (!this._context)
this._signal.listen('mount', () => {
this._context?.engine?.unlock(this);
return TreatmentQueueStatus.SnapOut;
});
return this;
}
trigger(type) {
if (this._context)
this._context.engine?.trigger(this, type);
else if (!this._context)
this._signal.listen('mount', () => {
this._context?.engine?.trigger(this, type);
return TreatmentQueueStatus.SnapOut;
});
return this;
}
stase(state) {
if (this._context)
this._context.engine?.stase(this, state);
else if (!this._context)
this._signal.listen('mount', () => {
this._context?.engine?.stase(this, state);
return TreatmentQueueStatus.SnapOut;
});
return this;
}
computedStyle(token) {
return this.context?.engine?.computedStyle(this, token);
}
hide() {
if (this._context)
this._context.engine?.hide(this);
else if (!this._context)
this._signal.listen('mount', () => {
this._context?.engine?.hide(this);
return TreatmentQueueStatus.SnapOut;
});
return this;
}
show(display) {
if (this._context)
this._context.engine?.show(this, display);
else if (!this._context)
this._signal.listen('mount', () => {
this._context?.engine?.show(this, display);
return TreatmentQueueStatus.SnapOut;
});
return this;
}
toggle(option) {
if (this._context)
this._context.engine?.toggle(this, option);
else if (!this._context)
this._signal.listen('mount', () => {
this._context?.engine?.toggle(this, option);
return TreatmentQueueStatus.SnapOut;
});
return this;
}
elevate(elevation) {
this
.stylesheet
.merge({ zIndex: elevation?.toString() || ObjectElevation.None })
.sync();
return this;
}
data(dataset) {
if (this._context)
this._context.engine?.data(this, dataset);
else if (!this._context)
this._signal.listen('mount', () => {
this._context?.engine?.data(this, dataset);
return TreatmentQueueStatus.SnapOut;
});
return this;
}
attribute(attributes) {
if (this._context)
this._context.engine?.attribute(this, attributes);
else if (!this._context)
this._signal.listen('mount', () => {
this._context?.engine?.attribute(this, attributes);
return TreatmentQueueStatus.SnapOut;
});
return this;
}
attributeLess(attributes) {
if (this._context)
this._context.engine?.attributeLess(this, attributes);
else if (!this._context)
this._signal.listen('mount', () => {
this._context?.engine?.attributeLess(this, attributes);
return TreatmentQueueStatus.SnapOut;
});
return this;
}
style(declaration) {
this.stylesheet.merge(declaration).sync();
return this;
}
className(token) {
if (this._context)
this._context.engine?.className(this, token);
else if (!this._context)
this._signal.listen('mount', () => {
this._context?.engine?.className(this, token);
return TreatmentQueueStatus.SnapOut;
});
return this;
}
removeClassName(token) {
if (this._context)
this._context.engine?.removeClassName(this, token);
else if (!this._context)
this._signal.listen('mount', () => {
this._context?.engine?.removeClassName(this, token);
return TreatmentQueueStatus.SnapOut;
});
return this;
}
replaceClassName(oldToken, token) {
if (this._context)
this._context.engine?.replaceClassName(this, oldToken, token);
else if (!this._context)
this._signal.listen('mount', () => {
this._context?.engine?.replaceClassName(this, oldToken, token);
return TreatmentQueueStatus.SnapOut;
});
return this;
}
clearClassName() {
if (this._context)
this._context.engine?.clearClassName(this);
else if (!this._context)
this._signal.listen('mount', () => {
this._context?.engine?.clearClassName(this);
return TreatmentQueueStatus.SnapOut;
});
return this;
}
value(data) {
if (this._context)
this._context.engine?.value(this, data);
else if (!this._context)
this._signal.listen('mount', () => {
this._context?.engine?.value(this, data);
return TreatmentQueueStatus.SnapOut;
});
return this;
}
html(code) {
if (this._context)
this._context.engine?.html(this, code);
else if (!this._context)
this._signal.listen('mount', () => {
this._context?.engine?.html(this, code);
return TreatmentQueueStatus.SnapOut;
});
return this;
}
content(children) {
if (this._context)
this._context.engine?.content(this, children);
else if (!this._context)
this._signal.listen('mount', () => {
this._context?.engine?.content(this, children);
return TreatmentQueueStatus.SnapOut;
});
return this;
}
listen(type, callback, options = false) {
if (this._context)
this._context.engine?.listen(this, type, callback, options);
else if (!this._context)
this._signal.listen('mount', () => {
this._context?.engine?.listen(this, type, callback, options);
return TreatmentQueueStatus.SnapOut;
});
return this;
}
on(type, callback) {
if (this._context)
this._context.engine?.on(this, type, callback);
else if (!this._context)
this._signal.listen('mount', () => {
this._context?.engine?.on(this, type, callback);
return TreatmentQueueStatus.SnapOut;
});
return this;
}
detachEvent(type) {
if (this._context)
this._context.engine?.detachEvent(this, type);
else if (!this._context)
this._signal.listen('mount', () => {
this._context?.engine?.detachEvent(this, type);
return TreatmentQueueStatus.SnapOut;
});
return this;
}
clone() {
return new this.constructor(structuredClone({ ...this.props, ...this.attributes }));
}
append(children) {
if (Array.isArray(children))
children.forEach(child => this.element?.append(child));
else if (children instanceof WidgetNode) {
this.element?.append(children.element);
children.useContext(this._context);
}
return this;
}
prepend(children) {
if (Array.isArray(children))
children.forEach(child => this.element?.prepend(child));
else if (children instanceof WidgetNode) {
this.element?.prepend(children.element);
children.useContext(this._context);
}
return this;
}
callable(callable) {
if (Environment.Client && this.clientElement && callable.client)
callable.client(this.clientElement);
if (!Environment.Client && this.serverElement && callable.server)
callable.server(this.serverElement);
return this;
}
extractProperties(properties) {
properties = properties || this._props || {};
const _attributes = {};
const _props = {};
properties.children = properties.children || this.constructor.children;
Object.keys(properties)
.forEach((key) => {
if (!WidgetNativeProperties.includes(key))
_attributes[key] = properties[key];
else
_props[key] = properties[key];
});
this._attributes = { ...(this.constructor.attributes || {}), ..._attributes };
this._props = _props;
return this;
}
}
export function WidgetBuilder(widget, context) {
const engine = ((Environment.Client) ? Widgets.Engine.client(widget) : Widgets.Engine.server(widget));
context.engine = engine;
return engine.render(widget, context);
}