tom-select
Version:
Tom Select is a versatile and dynamic <select> UI control. Forked from Selectize.js to provide a framework agnostic autocomplete widget with native-feeling keyboard navigation, it's useful for tagging, contact lists, country selectors, etc.
211 lines (168 loc) • 4.19 kB
text/typescript
import { iterate } from './utils.ts';
/**
* Return a dom element from either a dom query string, jQuery object, a dom element or html string
* https://stackoverflow.com/questions/494143/creating-a-new-dom-element-from-an-html-string-using-built-in-dom-methods-or-pro/35385518#35385518
*
* param query should be {}
*/
export const getDom = ( query:any ):HTMLElement => {
if( query.jquery ){
return query[0];
}
if( query instanceof HTMLElement ){
return query;
}
if( isHtmlString(query) ){
var tpl = document.createElement('template');
tpl.innerHTML = query.trim(); // Never return a text node of whitespace as the result
return tpl.content.firstChild as HTMLElement;
}
return document.querySelector(query);
};
export const isHtmlString = (arg:any): boolean => {
if( typeof arg === 'string' && arg.indexOf('<') > -1 ){
return true;
}
return false;
}
export const escapeQuery = (query:string):string => {
return query.replace(/['"\\]/g, '\\$&');
}
/**
* Dispatch an event
*
*/
export const triggerEvent = ( dom_el:HTMLElement, event_name:string ):void => {
var event = document.createEvent('HTMLEvents');
event.initEvent(event_name, true, false);
dom_el.dispatchEvent(event)
};
/**
* Apply CSS rules to a dom element
*
*/
export const applyCSS = ( dom_el:HTMLElement, css:{ [key: string]: string|number }):void => {
Object.assign(dom_el.style, css);
}
/**
* Add css classes
*
*/
export const addClasses = ( elmts:HTMLElement|HTMLElement[], ...classes:string[]|string[][] ) => {
var norm_classes = classesArray(classes);
elmts = castAsArray(elmts);
elmts.map( el => {
norm_classes.map( cls => {
el.classList.add( cls );
});
});
}
/**
* Remove css classes
*
*/
export const removeClasses = ( elmts:HTMLElement|HTMLElement[], ...classes:string[]|string[][] ) => {
var norm_classes = classesArray(classes);
elmts = castAsArray(elmts);
elmts.map( el => {
norm_classes.map(cls => {
el.classList.remove( cls );
});
});
}
/**
* Return arguments
*
*/
export const classesArray = (args:string[]|string[][]):string[] => {
var classes:string[] = [];
iterate( args, (_classes) =>{
if( typeof _classes === 'string' ){
_classes = _classes.trim().split(/[\t\n\f\r\s]/);
}
if( Array.isArray(_classes) ){
classes = classes.concat(_classes);
}
});
return classes.filter(Boolean);
}
/**
* Create an array from arg if it's not already an array
*
*/
export const castAsArray = (arg:any):Array<any> => {
if( !Array.isArray(arg) ){
arg = [arg];
}
return arg;
}
/**
* Get the closest node to the evt.target matching the selector
* Stops at wrapper
*
*/
export const parentMatch = ( target:null|HTMLElement, selector:string, wrapper?:HTMLElement ):HTMLElement|void => {
if( wrapper && !wrapper.contains(target) ){
return;
}
while( target && target.matches ){
if( target.matches(selector) ){
return target;
}
target = target.parentNode as HTMLElement;
}
}
/**
* Get the first or last item from an array
*
* > 0 - right (last)
* <= 0 - left (first)
*
*/
export const getTail = ( list:Array<any>|NodeList, direction:number=0 ):any => {
if( direction > 0 ){
return list[list.length-1];
}
return list[0];
}
/**
* Return true if an object is empty
*
*/
export const isEmptyObject = (obj:object):boolean => {
return (Object.keys(obj).length === 0);
}
/**
* Get the index of an element amongst sibling nodes of the same type
*
*/
export const nodeIndex = ( el:null|Element, amongst?:string ):number => {
if (!el) return -1;
amongst = amongst || el.nodeName;
var i = 0;
while( el = el.previousElementSibling ){
if( el.matches(amongst) ){
i++;
}
}
return i;
}
/**
* Set attributes of an element
*
*/
export const setAttr = (el:Element,attrs:{ [key: string]: null|string|number }) => {
iterate( attrs,(val,attr) => {
if( val == null ){
el.removeAttribute(attr as string);
}else{
el.setAttribute(attr as string, ''+val);
}
});
}
/**
* Replace a node
*/
export const replaceNode = ( existing:Node, replacement:Node ) => {
if( existing.parentNode ) existing.parentNode.replaceChild(replacement, existing);
}