mirror of https://github.com/n2geoff/um
98 lines
2.7 KiB
JavaScript
98 lines
2.7 KiB
JavaScript
/**
|
|
* HTML Tag Scripting Function
|
|
*
|
|
* Generates new DOM element(s) from a tag, attributes
|
|
*
|
|
* @param {String} tag - tag name
|
|
* @param {Object|String|Array} args - attributes, text or array of child elements
|
|
*
|
|
* @returns {HTMLElement} The created DOM element(s)
|
|
*/
|
|
function h(tag, ...args) {
|
|
const el = document.createElement(tag);
|
|
|
|
// support all scalar values as TextNodes
|
|
const isScalar = (value) => ["boolean", "string", "number"].includes(typeof value);
|
|
|
|
args.forEach((arg) => {
|
|
if (isScalar(arg)) {
|
|
el.appendChild(document.createTextNode(arg));
|
|
} else if (Array.isArray(arg)) {
|
|
el.append(...arg);
|
|
} else {
|
|
Object.assign(el, arg);
|
|
}
|
|
});
|
|
|
|
return el;
|
|
}
|
|
|
|
/**
|
|
* App Builder
|
|
*
|
|
* Composes state, actions, view together as
|
|
* mountable ui
|
|
*
|
|
* @param {Object} opts options bag of state, view, actions, and mount
|
|
* @param {Object} opts.state initial app object state
|
|
* @param {Function} opts.view function that returns dom. state and actions are passed in
|
|
* @param {Object} opts.actions object functions includes and return state
|
|
* @param {String} opts.mount querySelector value
|
|
*
|
|
* @returns {Object} state and update() interface
|
|
*/
|
|
function app(opts) {
|
|
// initial setup
|
|
const state = check(opts.state, {});
|
|
const view = check(opts.view, () => null);
|
|
const actions = check(opts.actions, {});
|
|
const mount = opts.mount || "body";
|
|
|
|
/**
|
|
* simple type validation check
|
|
*
|
|
* @param {*} value
|
|
* @param {String} type
|
|
* @returns {*}
|
|
*/
|
|
function check(value, type) {
|
|
return typeof value === typeof type ? value : type;
|
|
}
|
|
|
|
/**
|
|
* Assigns Dispatch-able Actions into App
|
|
*
|
|
* @param {Object} data state used by actions
|
|
* @param {Object} actions functions that update state
|
|
*/
|
|
function dispatch(data, actions) {
|
|
Object.entries(actions).forEach(([name, action]) => {
|
|
if (typeof action === "function") {
|
|
actions[name] = (...args) => {
|
|
// update date from action return
|
|
Object.assign(state, action(data, ...args));
|
|
|
|
// call update
|
|
update();
|
|
};
|
|
}
|
|
});
|
|
|
|
update();
|
|
}
|
|
|
|
/** update dom */
|
|
const update = () => {
|
|
document.querySelector(mount).replaceChildren(view(state, actions));
|
|
};
|
|
|
|
// mount view
|
|
if (opts.view && mount) {
|
|
dispatch(state, actions);
|
|
}
|
|
|
|
return {state,update}
|
|
}
|
|
|
|
export { app, h };
|