UNPKG

deleight

Version:

A library with 9 modules for writing more expressive web applications with traditional HTML, CSS and JavaScript.

400 lines (399 loc) 11.7 kB
"use strict"; /** * A few important functions for expressively and efficiently * working with elements on a web page. * * The functions here include: * * 5. {@link listener} an {@link apply} component (function) used to declaratively * attach listeners to specified elements within a tree. * * 6. {@link setter} an {@link apply} component (function) used to declaratively * set the values of element properties within a tree. * * 7. {@link assigner} an {@link apply} component (function) used to declaratively * set element sub-properties within a tree. * * 8. {@link attrSetter} an {@link apply} component (function) used to declaratively * set the values of element attributes within a tree. * * 9. Many more including {@link addTo}, {@link attr}, {@link prop} and * {@link selectorSetter}... * * The functions are used to build components which can be used with {@link apply}, * {@link process} or {@link Dom}. * * Pending tests. Please report bugs. * * @module */ Object.defineProperty(exports, "__esModule", { value: true }); exports.text = exports.all = exports.attr = exports.bind = exports.addTo = exports.selectMembers = exports.selectorSetter = exports.attrsSetter = exports.attrSetter = exports.assigner = exports.setters = exports.setter = exports.listener = void 0; const operations_js_1 = require("../../object/operations/operations.js"); /** * Creates a function to be called with listener functions to return `apply * * @example * import { listener } from 'deleight/dom/components' * import { apply } from 'deleight/dom/apply' * * document.body.innerHTML = ` * <div>I am a div</div> * <p>I am a paragraph</p> * <section>I am a section <button>Btn1</button></section> * <article>I am an article <button>Btn2</button></article> * `; * * const componentFactory = listener('click', { once: true }) * * const btns = []; * const component = componentFactory(e => btns.push(e.target.textContent)); * * const subComponents = { button: component } * apply({ section: subComponents, article: subComponents }); * * @param event * @param options * @returns */ function listener(event, options) { return (listener) => (elements) => { if (elements instanceof Element) elements = [elements]; for (let element of elements) element.addEventListener(event, listener, options); }; } exports.listener = listener; /** * Sets a property on 1 or more elements. * * @example * import { setter } from 'deleight/dom/components' * import { apply } from 'deleight/dom/apply' * * document.body.innerHTML = ` * <div>I am a div</div> * <p>I am a paragraph</p> * <section>I am a section <button>Btn1</button></section> * <article>I am an article <button>Btn2</button></article> * `; * * const val = setter('propKey'); * apply({ section: { button: val(20) }, article: { button: val(33) } }); * * @param key * @returns */ function setter(key) { return (value) => (elements) => { if (elements instanceof Element) elements = [elements]; for (let element of elements) element[key] = value; }; } exports.setter = setter; /** * Returns a component for setting multiple element properties or * nested properties (such as properties within Element.style. * * Pending tests. Please report bugs. * * @example * import { setters } from 'deleight/dom/components' * import { apply } from 'deleight/dom/apply' * * document.body.innerHTML = ` * <div>I am a div</div> * <p>I am a paragraph</p> * <section>I am a section <button>Btn1</button></section> * <article>I am an article <button>Btn2</button></article> * `; * * apply({ * section: { button: setters({ a: 1, b: 2 }) }, * article: { button: setters({ a: 5, b: 6 }) } * }); * * @param value * @returns */ function setters(value) { return (elements) => { if (elements instanceof Element) elements = [elements]; for (let element of elements) (0, operations_js_1.assign)(element, [value]); }; } exports.setters = setters; /** * Alias (older name) for {@link setters} */ exports.assigner = setters; /** * Returns a component for setting single element attributes. * * Pending tests. Please report bugs. * * @example * import { attrSetter } from 'deleight/dom/components' * import { apply } from 'deleight/dom/apply' * * document.body.innerHTML = ` * <div>I am a div</div> * <p>I am a paragraph</p> * <section>I am a section <button>Btn1</button></section> * <article>I am an article <button>Btn2</button></article> * `; * * const val = attrSetter('attr') * apply({ section: { button: val(20) }, article: { button: val(33) } }); * * @param name * @returns */ function attrSetter(name) { return (value) => (elements) => { if (elements instanceof Element) elements = [elements]; for (let element of elements) element.setAttribute(name, value); }; } exports.attrSetter = attrSetter; /** * Returns a component for setting multiple element attributes. * * Pending tests. Please report bugs. * * @example * import { attrsSetter } from 'deleight/dom/components' * import { apply } from 'deleight/dom/apply' * * document.body.innerHTML = ` * <div>I am a div</div> * <p>I am a paragraph</p> * <section>I am a section <button>Btn1</button></section> * <article>I am an article <button>Btn2</button></article> * `; * * apply({ * section: { button: attrsSetter({ a: '1', b: '2' }) }, * article: { button: attrsSetter({ a: '5', b: '6' }) } * }); * * @param values * @returns */ function attrsSetter(values) { return (elements) => { if (elements instanceof Element) elements = [elements]; for (let element of elements) for (let [k, v] of Object.entries(values)) element.setAttribute(k, v); }; } exports.attrsSetter = attrsSetter; /** * Returns a component that sets the selected members as properties on the element using the keys * associated with the `selectorAttr`. * * @example * import { listener } from 'deleight/dom/components' * import { apply } from 'deleight/dom/apply' * * document.body.innerHTML = ` * <div>I am a div</div> * <p m-ember="about">I am a paragraph</p> * <section>I am a section <button m-ember="b1">Btn1</button></section> * <article>I am an article <button m-ember="b2">Btn2</button></article> * `; * * const comp = selectorSetter() * comp(body); * document.body.about; * document.body.b1; * document.body.b2; * * @param selectorAttr * @returns */ function selectorSetter(selectorAttr = 'm-ember') { return (element) => { selectMembers(element, selectorAttr); }; } exports.selectorSetter = selectorSetter; /** * Sets the selected members as properties on the element using the keys * associated with the `selectorAttr`. * * @example * import { selectMembers } from 'deleight/dom/components' * import { apply } from 'deleight/dom/apply' * * document.body.innerHTML = ` * <div>I am a div</div> * <p>I am a paragraph</p> * <section>I am a section <button m-ember="b1">Btn1</button></section> * <article>I am an article <button m-ember="b2">Btn2</button></article> * `; * * selectMembers(); * document.body.b1; * document.body.b2; * * * @param selectorAttr * @returns */ function selectMembers(element, selectorAttr = 'm-ember') { if (!element) element = document.body; const selected = element.querySelectorAll(`*[${selectorAttr}]`); for (let item of selected) element[item.getAttribute(selectorAttr)] = item; return element; } exports.selectMembers = selectMembers; /** * Components for setting up reactivity */ /** * A component that adds the element (or a value derived from it) * to an object used with primitives from the object/shared module. * * The optional wrapper is a function or key used to derive another * value from the element. If a function, it takes the element, * optional matcher and optional extra args as arg * and returns the derived value. If a key, fetches the element's * property with the key. * * @example * import { addTo } from 'deleight/dom/components' * import { apply } from 'deleight/dom/apply' * * document.body.innerHTML = ` * <div>I am a div</div> * <p>I am a paragraph</p> * <section>I am a section <button>Btn1</button></section> * <article>I am an article <button>Btn2</button></article> * `; * * const obj = { }; // a reactive object * apply({ * section: { button: addTo(obj, text) }, * article: { button: addTo(obj, text) } * }); * * @param object * @param key * @param wrapper * @returns */ function addTo(object, key, wrapper) { if (!key) key = 'textContent'; return (elements) => { if (elements instanceof Element) elements = [elements]; if (wrapper && !(wrapper instanceof Function)) { const key = wrapper; wrapper = (el) => el[key]; } if (wrapper) elements = Array.prototype.map.call(elements, wrapper); if (!Reflect.has(object, key)) object[key] = []; object[key].push(...elements); }; } exports.addTo = addTo; /** * Alias (older name) for {@link addTo} */ exports.bind = addTo; const attrHandler = { set(target, key, value) { if (typeof key === 'string') target.setAttribute(key, value); return true; } }; /** * A wrapper used with {@link addTo} to make the binding set attributes * instead of properties on the bound element. * * @example * import { addTo, attr } from 'deleight/dom/components' * import { apply } from 'deleight/dom/apply' * import { sets } from 'deleight/object/shared' * * document.body.innerHTML = ` * <div>I am a div</div> * <p>I am a paragraph</p> * <section>I am a section <button>Btn1</button></section> * <article>I am an article <button>Btn2</button></article> * `; * * const obj = { }; // a reactive object * * apply({ * section: { button: addTo(obj, 'a', attr) }, * article: { button: addTo(obj, 'color', 'styles') } * }); * * sets(obj, 'yellow'); * // document.querySelector('button').getAttribute('a'); // yellow * // document.body.lastElementChild.lastElementChild.style.color; // yellow * * sets(obj, 'green'); * // document.querySelector('button').getAttribute('a'); // green * // document.body.lastElementChild.lastElementChild.style.color; // green * * @param element */ function attr(element) { return new Proxy(element, attrHandler); } exports.attr = attr; /** * Returns a component that applies all the specified components * to the matched element(s): * * @example * import { listener } from 'deleight/dom/components' * import { apply } from 'deleight/dom/apply' * * document.body.innerHTML = ` * <div>I am a div</div> * <p>I am a paragraph</p> * <section>I am a section <button>Btn1</button></section> * <article>I am an article <button>Btn2</button></article> * `; * * const a = setter('a'); * const b = setter('b'); * apply({ section: { button: all(a(2), b(3)) }, article: { button: all(a(62), b(53)) } }); * * @param components * @returns */ function all(...components) { return (elements, matcher, ...args) => { if (elements instanceof Element) elements = [elements]; for (let element of elements) for (let comp of components) comp(element, matcher, ...args); }; } exports.all = all; /** * Just an alias for the longish 'textContent' string. * */ exports.text = 'textContent';