/**
*
* js-create-element <https://github.com/athersharif/js-create-element>
*
* Copyright (c) 2018-Present, Ather Sharif.
* Released under the MIT License.
*
*/
import forEach from 'lodash/forEach';
import assign from 'lodash/assign';
import isPlainObject from 'lodash/isPlainObject';
import isUndefined from 'lodash/isUndefined';
import isNull from 'lodash/isNull';
import toString from 'lodash/toString';
import isEqual from 'lodash/isEqual';
import split from 'lodash/split';
/**
*
* SVG NameSpace URL
*
*/
const svgNameSpace = 'http://www.w3.org/2000/svg';
/**
*
* Filters the argument to be a valid string type or a null object
*
* @param {string} str - The string to be filtered
*
* @return {string} A valid string object or null
*
*/
const filterString = str => (str && !isPlainObject(str) ? toString(str) : null);
/**
*
* Filters the argument to be a valid plain object type or a undefined object
*
* @param {Object} obj - The object to filter
*
* @return {Object} A valid object or undefined
*
*/
const filterObject = obj => (isPlainObject(obj) ? obj : undefined);
/**
*
* Generates a DOM text node with the given text and appends it to the given
* element
*
* @param {string} text - The text to insert
* @param {Element} parent - The element to insert the text in
*
*/
const insertTextElement = (text, parent) => {
if (!isNull(text)) {
const textNode = document.createTextNode(text);
parent.appendChild(textNode);
}
};
/**
*
* Generates a DOM element that replicates the behavior of a CSS pseudo-element
* and appends it to the given element
*
* @param {Object} pseudoElemStyle - The style object for the pseudo element
* @param {Element} parent - The element to insert the pseudo element in
*
*/
const insertPseudoElement = (pseudoElemStyle, parent) => {
if (!isUndefined(pseudoElemStyle) && !isUndefined(pseudoElemStyle.content)) {
let element = createElement('span', { style: pseudoElemStyle });
insertTextElement(pseudoElemStyle.content, element);
parent.appendChild(element);
}
};
/**
*
* Creates the appropriate DOM element based on type
*
* @param {string} type - The type of the element to be created
*
* @return {Element} The DOM element based on supplied type
*
*/
const createDOMElement = type => {
const [elemType, subType] = split(type, ':');
if (isEqual(elemType, 'svg')) {
return document.createElementNS(
svgNameSpace,
isUndefined(subType) ? elemType : subType
);
} else {
return document.createElement(elemType);
}
};
/**
*
* Creates a DOM element using pure JS with the given attributes, style, text,
* and pseudo elements
*
* @param {string} [type] - The HTML tag for the new element
* @param {Object} [options] - Attributes and styles for the new element
* @param {Object} [options.style] - The CSS style for the new element
* @param {string} [options.text] - The text content for the new element
* @param {Object} [options.pseudoBefore] - The CSS for a ::before pseudo-element
* @param {Object} [options.pseudoAfter] - The CSS for a ::after pseudo-element
* @param {...Object} [options.attribute] - The attributes for the new element such as id, etc.
*
* @return {Element} The resulting element
*
*/
const createElement = (type, options) => {
let { style, text, pseudoBefore, pseudoAfter, ...attributes } =
filterObject(options) || {};
type = filterString(type) || 'div';
style = filterObject(style) || {};
text = filterString(text);
pseudoBefore = filterObject(pseudoBefore);
pseudoAfter = filterObject(pseudoAfter);
let element = createDOMElement(type);
forEach(attributes, (value, key) => element.setAttribute(key, value));
forEach(style, (value, key) => assign(element.style, { [key]: value }));
insertPseudoElement(pseudoBefore, element);
insertTextElement(text, element);
insertPseudoElement(pseudoAfter, element);
return element;
};
export { createElement };
// export remaining functions for testing
export const _test = {
filterString,
filterObject,
insertTextElement,
insertPseudoElement,
};