deleight
Version:
A library with 9 modules for writing more expressive web applications with traditional HTML, CSS and JavaScript.
140 lines (139 loc) • 4.97 kB
JavaScript
;
/**
* Exports {@link apply} used to apply functions to elements selected with
* a getter from an element tree.
*
*
* @module
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.applyAll = exports.apply = exports.defaultGetter = exports.get = exports.selectFirst = exports.selectAll = void 0;
const apply_js_1 = require("../../object/apply/apply.js");
const operations_js_1 = require("../../object/operations/operations.js");
const selectAll = (element, selectors) => element.querySelectorAll(selectors);
exports.selectAll = selectAll;
const selectFirst = (element, selectors) => element.querySelector(selectors);
exports.selectFirst = selectFirst;
/**
* Get the element pointed to by the key from the target. The value
* returned is determined as follows:
*
* 1. If the key is a number then the value is the child element
* of the target at that index.
*
* 2. If the key is a string, the given selector (or `querySelectorAll` fallback)
* is used to retrieve the value(s).
*
* @example
*
*
* @param target
* @param key
* @param selector
* @returns
*/
function get(target, key, selector = exports.selectFirst) {
if (typeof key === 'number') {
if (key < 0)
key = target.children.length + key;
return target.children[key];
}
else if (typeof key === 'string') {
return selector(target, key);
}
}
exports.get = get;
function getter(selector) {
return (target, key) => get(target, key, selector);
}
exports.defaultGetter = getter();
/**
* Invoke functions with elements within the input element's tree that
* match the provided selection criteria. {@link baseApply} is used
* under the hood so the notes about the behavior of that function also
* apply here.
*
* For this function, the target is an element or document fragment. This
* function uses {@link get} to obtain the 'properties' of the target
* (by supplying a `getter` option to the underlying function ). This function
* also pre-fetches the target's property (also using {@link get}) before
* invoking the component functions by using a mapper option.
*
* @example
* import { apply } from 'deleight/dom/apply';
* import { map, range, forEach, zip } from 'deleight/generators';
* apply({
* main: (main) => {
* const newContent = map(range(101, 120), i => `My index is now ${i}`);
* const lastChildren = map(main.children, c => c.lastElementChild);
* forEach(zip(lastChildren, newContent), ([el, c]) => el.textContent = c);
* // set(lastChildren, {textContent: newContent});
* }
* });
*
* @param components
* @param target
* @param options
* @returns
*/
function apply(components, target, options) {
if (!target)
target = document.body;
const getter = options?.getter || exports.defaultGetter;
const mappedComponents = (0, operations_js_1.mapValues)(components, (options?.mapper || applyMapper)(getter));
return (0, apply_js_1.apply)(mappedComponents, target, { args: options?.args, getter: getter });
}
exports.apply = apply;
/**
* Similar to {@link apply} but uses {@link selectAll} (instead of
* the default {@link selectFirst}) to match elements.
*
* @example
* import { applyAll } from 'deleight/dom/apply';
* import { map, range, forEach, zip } from 'deleight/generators';
* applyAll({
* main: ([main]) => {
* const newContent = map(range(101, 120), i => `My index is now ${i}`);
* const lastChildren = map(main.children, c => c.lastElementChild);
* forEach(zip(lastChildren, newContent), ([el, c]) => el.textContent = c);
* // set(lastChildren, {textContent: newContent});
* }
* });
*
* @param components
* @param target
* @param options
* @returns
*/
function applyAll(components, target, options) {
if (!options)
options = {};
options.getter = getter(exports.selectAll);
return apply(components, target, options);
}
exports.applyAll = applyAll;
function applyFunction(components, getter) {
const innerApplyFunction = (elements, key, ...args) => {
const component = components[key];
if (typeof elements === 'object' && Reflect.has(elements, Symbol.iterator)) {
let result;
for (let element of elements) {
result = component(getter(element, key), key);
if (result !== undefined) {
innerApplyFunction(result, key, ...args);
}
}
}
else if (elements instanceof Element || elements instanceof DocumentFragment) {
return component(getter(elements, key), key);
}
};
return innerApplyFunction;
}
function applyMapper(getter) {
const innerMapper = (comps, key) => {
let comp = comps[key];
return (comp instanceof Function) ? applyFunction(comps, getter) : (0, operations_js_1.mapValues)(comp, innerMapper);
};
return innerMapper;
}