@twobirds/microcomponents
Version:
Micro Components Organization Class
1,635 lines (1,284 loc) • 33.8 kB
text/typescript
import { MC, DC, HTMLMcElement, addListener, removeListener } from './MC.js';
import { LooseObject } from './helpers.js';
/*
helpers
*/
const isObject = ( value: any ): boolean => {
return value != null && typeof value === 'object';
}
const words = ( classes: Array<string> ): Array<string> => {
let classSet: Set<string> = new Set();
// read classes into set
classes.forEach( c => {
c = c.trim();
c.split(' ').forEach( e => {
if ( e !== '' ){
classSet.add(e);
}
});
});
// now we have a sanitized array
classes = Array.from( classSet );
return classes;
}
type inputTypes = HTMLInputElement | HTMLSelectElement | HTMLTextAreaElement;
const getInputName = ( e: HTMLInputElement | HTMLSelectElement | HTMLTextAreaElement ): string => {
let ret = e instanceof HTMLInputElement
? 'input'
: e instanceof HTMLSelectElement
? 'select'
: e instanceof HTMLTextAreaElement
? 'textarea'
: ''
;
return ret;
}
const getInputHandlers = ( e: HTMLElement | ShadowRoot ): LooseObject => {
let
//inputList = ( Array.from( e.querySelectorAll('*') ) as HTMLElement[] ).filter( i => isInputElement(i) ),
inputList = $d('input,select,textarea', e).array,
handlers: LooseObject = {};
inputList.forEach( (i) => {
const
name: string = ( i as inputTypes ).getAttribute('name') || 'undefined';
if ( name ){
let h: InputBase;
switch ( getInputName( i as inputTypes ) ){
case 'input':
//console.log( i, 'is HTMLInputElement');
h = new InputHandler( i as HTMLInputElement, e ); // e = element to search radios in = radioBase
Object.defineProperty( handlers, name, {
enumerable: true,
configurable: true,
get: (): any => { return h.val },
set: ( value: any ) => { h.val = value }
});
break;
case 'select':
//console.log( i, 'is HTMLSelectElement');
h = new SelectHandler( i as HTMLSelectElement );
Object.defineProperty( handlers, name, {
enumerable: true,
configurable: true,
get: (): any => { return h.val },
set: ( value: any ) => { h.val = value }
});
break;
case 'textarea':
//console.log( i, 'is HTMLTextAreaElement');
h = new TextAreaHandler( i as HTMLTextAreaElement );
Object.defineProperty( handlers, name, {
enumerable: true,
configurable: true,
get: (): any => { return h.val },
set: ( value: any ) => { h.val = value }
});
break;
default:
console.warn('handler missing:', name, 'for', i);
break;
}
}
});
return handlers;
}
abstract class InputBase{
constructor( e: any ){
}
abstract get val(): any;
abstract set val( value: any );
}
class InputHandler extends InputBase {
#e: HTMLInputElement;
#type: string;
#radioBase: any;
constructor( e: HTMLInputElement, radioBase?: HTMLElement | ShadowRoot ){
super( e );
this.#e = e;
this.#type = e.type;
this.#radioBase = radioBase;
}
get val(): any{
let that = this,
type = that.#type,
ret: any;
switch (type){
case 'radio':
const check = $d( `input[type="radio"][name="${ this.#e.getAttribute('name') }"]`, this.#radioBase )
.filter( e => e.checked );
if ( !check.length ) return '';
ret = check.array[0];
return ret.value;
case 'checkbox':
return that.#e.checked;
default:
return that.#e.value;
break;
}
return '';
}
set val( value: any ){
let that = this,
type = that.#type,
ret: any;
switch (type){
case 'radio':
const
selection = `input[type="radio"][name="${ this.#e.getAttribute('name') }"]`,
radios = $d( selection, this.#radioBase );
// console.log('set radios', selection, this.#radioBase, radios.array );
if ( !radios.length ) return;
if ( value === '' ){ // uncheck all
radios.forEach( r => { if (r.checked) r.checked = false } );
//console.log('unset all radios', radios.array );
return;
}
const setRadioElement = radios.filter( r => r.getAttribute('value') === value );
if ( !setRadioElement.length ) return;
// console.log('set this radio', setRadioElement.array );
(setRadioElement.array[0] as HTMLInputElement).checked = true;
// console.log('done', setRadioElement.array );
return;
break;
case 'checkbox':
that.#e.checked = value;
break;
default:
that.#e.value = value;
break;
}
return;
}
}
class SelectHandler extends InputBase {
#e: HTMLSelectElement;
#multi: boolean = false;
constructor( e: HTMLSelectElement ){
super( e );
this.#e = e;
this.#multi = e.type === 'select-multiple';
}
get val(): string[] | string{
const
that = this,
ret: string[] = [];
// get all options
const options = $d( that.#e.querySelectorAll('option') )
.filter( o => o.selected )
.forEach( o => { if ( o.selected ) ret.push( o.value ) } );
if ( this.#multi ) return ret;
return ret.length ? ret[0] : '';
}
set val( value: string[] | string ){
const
that = this;
value = typeof value === 'string' ? [ value ] : value;
//console.log('set values', value );
// set all options
const options = $d( that.#e.querySelectorAll('option') )
.forEach( o => {
if ( value.indexOf( o.value ) > -1 ) {
o.setAttribute( 'selected', true )
o.selected = true;
} else {
o.removeAttribute( 'selected' )
o.selected = false;
}
});
/*
if (pOption.selected) {
if (
!pOption.disabled &&
(!pOption.parentNode.disabled ||
pOption.parentNode.nodeName !==
'optgroup')
) {
var value = pOption.value;
ret.push(value);
}
}
*/
return;
}
}
class TextAreaHandler extends InputBase {
#e: HTMLTextAreaElement
constructor( e: HTMLTextAreaElement ){
super(e);
this.#e = e;
}
get val(): string{
return this.#e.value;
}
set val( value: string ){
this.#e.value = value;
return;
}
}
/*
selector classes
*/
class DomSelector{
#set: Set<HTMLElement> = new Set();
public get set(): Set<HTMLElement> {
return this.#set;
}
public get array(): HTMLElement[] {
return Array.from( this.#set );
}
public set set( set: Set<HTMLElement> ) {
this.#set = set;
return;
}
public get length(): number {
return this.#set.size;
}
constructor( selection?: selection, element: any = document.body ){
let
that = this,
undefinedTags: Set<any> = new Set();
// // console.log('DomSelector constructor', ...arguments);
if ( !selection ) {
return that;
}
/* autoloading disabled !
if ( selection instanceof Array ){
selection.forEach( (select) => {
_getElementList( select, element ).forEach( (e) => {
if ( e.localName?.indexOf('-') !== -1 || e.getAttribute?.('is') ) _loadRequirements( e );
that.#set.add( e );
});
});
} else {
_getElementList( selection, element ).forEach( (e) => {
if ( e.localName?.indexOf('-') !== -1 || e.getAttribute?.('is') ) _loadRequirements( e );
that.#set.add( e );
});
}
*/
}
$t( nameSpace?: string ): TSelector {
let
result = new TbSelector()
;
this.#set.forEach( (e: HTMLElement) => {
if ( (e as HTMLMcElement)._mc ){
Object.values( (e as HTMLMcElement)._mc ).forEach( ( mc: MC | DC ) => {
result.set.add( mc );
});
}
});
if ( nameSpace ) {
result.filter( mc => (mc as LooseObject).nameSpace === nameSpace );
}
return result;
}
add( selection: selection, target?: HTMLElement ): DSelector {
let
that = this
;
$d( selection, target ).forEach( element => {
that.#set.add( element );
});
return that;
}
addClass( ...classes: string[] ): DSelector {
// sanitize classes
classes = words( classes );
// now set those classes
this.forEach( element => {
element.classList.add( ...classes );
});
return this;
}
after( selection: selection, target?: HTMLElement ): DSelector {
let
that = this,
after = $d( selection, target );
;
if ( !after.length ) return that;
after
.first()
.forEach( element => {
that.array.reverse().forEach( (e: HTMLElement) => {
e.after( element );
});
});
return that;
}
append( selection: selection, target?: HTMLElement ): DSelector {
let
that = this,
addTo = that.array['0'],
append = $d( selection, target )
;
if ( addTo ){
append.forEach( (e: HTMLElement) => {
addTo.append( e );
});
}
return that;
}
appendTo( selection: selection, target?: HTMLElement ): DSelector {
let
that = this,
appendTo = $d( selection, target ).array['0']
;
if ( appendTo ){
that.forEach( (e: HTMLElement) => {
appendTo.append( e );
});
}
return that;
}
attr(): LooseObject;
attr( obj: LooseObject ): DSelector;
attr( name: string ): string | null;
attr( first?: string | LooseObject, second?: string | object | null ): DSelector;
attr( first?: unknown, second?: unknown ): DSelector | LooseObject | string | null {
let
that = this,
attributes: LooseObject = {}
;
// if no elements in set -> skip
if ( !that.length ) return that;
// if no arguments, return attributes of first in list as hash object
if (!arguments.length) {
Array.from( [ ...that.set ][0].attributes ).forEach( (attribute: LooseObject) => {
attributes[attribute.name] = attribute.value;
});
return attributes;
}
// if first is a string
// if no value (second) is given -> return attribute value of first element in node selection
// if value (second) is null -> remove attribute and return this
// else set attribute values and return this
if ( typeof first === 'string' ) {
// return attributes
if ( second === undefined ){
return [ ...that.set ][0].getAttribute( first );
}
// remove attribute
if ( second === null ){
[ ...that.set ].forEach( (e: HTMLElement) =>{
e.removeAttribute( first );
});
return that;
}
// if attribute value is an object -> convert to json string
if ( typeof second === 'object' ){
second = JSON.stringify( second );
}
// set attribute
[ ...that.set ].forEach( (e: HTMLElement) =>{
e.setAttribute( first, second as string );
});
return that;
}
// if first is an object set attributes to all nodes
if ( typeof first === 'object' ) {
[ ...that.set ].forEach( (e: HTMLElement) =>{
Object.keys( first as LooseObject ).forEach( (key: string) => {
$d(e).attr( key, ( first as LooseObject ).key );
});
});
return that;
}
return that;
}
before( selection: selection, target?: HTMLElement ): DSelector {
let
that = this,
before = $d( selection, target );
;
if ( !before.length ) return that;
before
.first()
.forEach( element => {
that.forEach( (e: HTMLElement) => {
e.before( element );
});
});
return that;
}
children( selection?: selection, rootNode?: HTMLElement ): DSelector{
let
that: DomSelector = this,
set: Set<HTMLElement> = new Set()
;
that.#set.forEach( (e: HTMLElement) => {
Array.from( e.children ).forEach( ( c ) => {
set.add( c as HTMLElement );
});
});
that = $d( [ ...set] );
if ( selection ){
let compare = $d( selection, rootNode );
that.filter( ( e: HTMLElement ) => compare.has( e ) );
}
return that;
}
descendants( selection?: selection, rootNode?: HTMLElement ): DSelector{
let
that: DomSelector = this,
set: Set<HTMLElement> = new Set()
;
that.#set.forEach( (e: HTMLElement) => {
e.querySelectorAll('*').forEach( ( h ) => {
set.add( h as HTMLElement );
});
});
that = $d( [ ...set] );
if ( selection ){
let compare = $d( selection, rootNode );
that.filter( ( e: HTMLElement ) => compare.has( e ) );
}
return that;
}
empty(): DSelector {
return this.filter( (e: any)=>false );
}
first( selection?: selection, rootNode?: HTMLElement ): DSelector{
let
that: DomSelector = this,
set: Set<HTMLElement> = new Set(),
compare: any = false;
;
// get compare selection if specified
if ( selection ){
compare = $d( selection, rootNode );
}
// get compare selection if specified
that.#set.forEach( (e: HTMLElement) => {
if ( e.parentNode ){
set.add( e.parentNode.children[0] as HTMLElement );
}
});
// make result
that = $d( [ ...set ] );
// compare if necessay
if ( selection ){
that.filter( ( e: HTMLElement ) => compare.has( e ) );
}
return that;
}
has( element: HTMLElement ): boolean{
return this.#set.has( element );
}
hasClass( ...classes: string[] ): boolean {
// sanitize classes
classes = words( classes );
// now test those classes
return this.every( e => {
return Array.from( e.classList ).every( c => e.contains( c ) );
});
}
html( html?: string ): string | DSelector{
let that = this;
if ( html ){
that.forEach( (e: HTMLElement) => {
!e.parentNode
? (()=>{
console.warn('Cannot access the .innerHTML of a HTMLElement that hasnt been inserted yet', e );
console.trace( e, this );
console.info( 'If you called this from inside a Web Component constructor, consider using the %c[connected] event callback!', 'color: blue;' )
})()
: e.innerHTML = html;
});
return that;
}
return that.array[0].innerHTML;
}
last( selection?: selection, rootNode?: HTMLElement ): DSelector{
let
that: DomSelector = this,
set: Set<HTMLElement> = new Set()
;
that.#set.forEach( (e: HTMLElement) => {
let
siblings = e.parentNode?.children as HTMLCollection
;
if ( siblings ) {
set.add( Array.from(siblings).at(-1) as HTMLElement );
}
});
that = $d( [ ...set] );
if ( selection ){
let compare = $d( selection, rootNode );
that.filter( ( e: HTMLElement ) => compare.has( e ) );
}
return that;
}
/*
load(): DSelector{
let that = this;
if( !that.length ){
that.add( document.body );
}
that.forEach( e => _loadRequirements( e ) );
return that;
}
*/
next( selection?: selection, rootNode?: HTMLElement ): DSelector{
let
that: DomSelector = this,
set: Set<HTMLElement> = new Set()
;
that.#set.forEach( (e: HTMLElement) => {
let
siblings = e.parentNode?.children as HTMLCollection
;
if ( siblings ) {
let
arr = Array.from(siblings),
pos = arr.indexOf(e) + 1
;
if ( pos < arr.length ){
set.add( arr.at(pos) as HTMLElement );
}
}
});
that = $d( [ ...set] );
if ( selection ){
let compare = $d( selection, rootNode );
that.filter( ( e: HTMLElement ) => compare.has( e ) );
}
return that;
}
normalize(): DSelector {
let that = this;
that.#set.forEach( (e: HTMLElement) => { e.normalize() });
return that;
}
off( eventName: string, cb: Function ): DSelector{
let
that = this,
eventNames =
eventName.indexOf(' ') > -1
? eventName.split(' ')
: [ eventName ]
;
// remove handler
that.array.forEach( (e: HTMLElement) => {
eventNames.forEach(function ( n: string ) {
removeListener(
e,
n,
cb as EventListenerOrEventListenerObject
);
});
});
return that;
}
on( eventName: string, cb: Function, capture: boolean = false, once: boolean = false ): Function{
let
that = this,
eventNames =
eventName.indexOf(' ') > -1
? eventName.split(' ').filter( (s) => s !== '' )
: [ eventName ]
;
function wrapper(){ wrapper.cb.apply(that, Array.from(arguments) ); };
wrapper.cb = cb;
// attach handler
that.forEach( (e: HTMLElement) => {
eventNames.forEach(function ( n: string ) {
addListener(
e,
n,
(wrapper as EventListenerOrEventListenerObject),
capture,
once
);
});
});
return wrapper;
}
one( eventName: string, cb: Function, capture: boolean = false ): Function{
let
that = this,
eventNames =
eventName.indexOf(' ') > -1
? eventName.split(' ').filter( (s) => s !== '' )
: [ eventName ]
;
function wrapper(){ wrapper.cb.apply(that, Array.from(arguments) ); };
wrapper.cb = cb;
// attach handler
that.forEach( (e: HTMLElement) => {
eventNames.forEach(function ( n: string ) {
addListener(
e,
n,
(wrapper as EventListenerOrEventListenerObject),
capture,
true
);
});
});
return wrapper;
}
parent( selection?: selection, rootNode?: HTMLElement ): DSelector {
let
that: DomSelector = this,
set: Set<HTMLElement> = new Set()
;
that.#set.forEach( (e: HTMLElement) => {
if ( e.parentNode && (e.parentNode as HTMLElement).tagName !== 'HTML') {
set.add( e.parentNode as HTMLElement );
}
});
that = $d( [ ...set] );
if ( selection ){
let compare = $d( selection, rootNode );
that.filter( ( e: HTMLElement ) => compare.has( e ) );
}
return that;
}
parents( selection?: selection, rootNode?: HTMLElement ): DSelector {
let
that: DomSelector = this,
set: Set<HTMLElement> = new Set()
;
that.#set.forEach( (e: HTMLElement) => {
let
current = e
;
while ( current.parentNode && (current.parentNode as HTMLElement).tagName !== 'HTML' ){
current = current.parentNode as HTMLElement;
set.add( current );
}
});
that = $d( [ ...set] );
if ( selection ){
let compare = $d( selection, rootNode );
that.filter( ( e: HTMLElement ) => compare.has( e ) );
}
return that;
}
prepend( selection: selection, target?: HTMLElement ): DSelector {
let
that = this,
addTo = that.array['0'],
prepend = $d( selection, target )
;
if ( addTo ){
prepend.forEach( (e: HTMLElement) => {
addTo.prepend( e );
});
}
return that;
}
prev( selection?: selection, rootNode?: HTMLElement ): DSelector{
let
that: DomSelector = this,
set: Set<HTMLElement> = new Set()
;
that.#set.forEach( (e: HTMLElement) => {
let
siblings = e.parentNode?.children as HTMLCollection
;
if ( siblings ) {
let
arr = Array.from(siblings),
pos = arr.indexOf(e) - 1
;
if ( pos > -1 ){
set.add( arr.at(pos) as HTMLElement );
}
}
});
that = $d( [ ...set] );
if ( selection ){
let compare = $d( selection, rootNode );
that.filter( ( e: HTMLElement ) => compare.has( e ) );
}
return that;
}
removeClass( ...classes: string[] ): DSelector {
// sanitize classes
classes = words( classes );
// now set those classes
this.forEach( element => {
element.classList.remove( ...classes );
});
return this;
}
text( text?: string ): string | DSelector{
let that = this;
if ( text ){
that.forEach( (e: HTMLElement) => {
!e.parentNode
? (()=>{
console.warn('Cannot access the .textContent of a HTMLElement that hasnt been inserted yet', e );
console.trace( e, this );
console.info( 'If you called this from inside a Web Component constructor, consider using the %c[connected] event callback!', 'color: blue;' )
})()
: e.innerText = text;
});
return that as DomSelector;
}
let t = that.array[0].innerText;
return t ?? '' as string;
}
toggleClass( ...classes: string[] ): DSelector {
// sanitize classes
classes = words( classes );
// now set those classes
this.forEach( element => {
element.classList.toggle( ...classes );
});
return this;
}
trigger( eventName: string, data: any = {}, bubbles: boolean = false, cancelable: boolean = false, composed: boolean = false ): DSelector{
let
that = this,
ev: Event,
options: LooseObject = { bubbles, cancelable, composed },
eventNames =
eventName.indexOf(' ') > -1
? eventName.split(' ')
: [ eventName ]
;
that.#set.forEach( (e: HTMLElement) => {
eventNames.forEach(function ( n: string ) {
// console.log()
ev = new Event( n, options );
(ev as LooseObject ).data = data;
e.dispatchEvent( ev );
});
});
return that;
}
// INFO:
// val(): LooseObject; // no param: get all as LooseObject
// val( p1: string ): Array<string> | number | string; // get from input name
// val( p1: object ): DomSelector; // set from object
// val( p1: string, p2: any ): DomSelector; // set from name and value
val( p1?: any, p2?: any ): any {
if ( !this.length ) return this;
const
that = this
;
let result: any,
handlers: LooseObject = getInputHandlers( that.array[0] );
// work with it
if ( !p1 ){ // get all inputs as LooseObject
result = Object.assign( {}, handlers );
// Object.keys( handlers ).forEach( ( key ) => result[key] = handlers.hasOwnProperty(key) ? handlers[key].val : undefined );
} else {
if ( isObject(p1) ){ // set inputs from Object
Object.assign( handlers, p1 );
// Object.keys( p1 ).forEach( ( key ) => !!handlers[key] ? handlers[key].val = p1[key] : false );
result = that;
} else if ( typeof p1 === 'string' ){
if ( p2 === undefined ){ // get a single input
result = handlers[p1];
} else { // set a single input
handlers[p1] = p2;
result = that;
}
}
}
return result;
}
values( values?: LooseObject ): LooseObject {
const
that = this,
holder: LooseObject = {};
let
data: LooseObject = {};
//console.log( '$d().values()', values );
if ( !that.length ) {
console.warn('no dom selection to search inputs in');
return {};
}
let handlers = getInputHandlers( that.array[0] );
//console.log( 'handlers', Object.keys(handlers), handlers['cb1'] );
// let values = that.val();
// console.log( 'values', values );
Object.keys( handlers ).forEach( (key: string) => {
//console.log(key);
data[key] = handlers[key];
});
// data = Object.assign( {}, that.val() )
//console.log( 'data', that.array[0], data, Object.keys( data ) );
if ( Object.keys( data ).length === 0 ) {
console.warn('no inputs in this dom selection', that.array[0], data );
}
//console.log( '$d val()', data );
if (values) {
Object
.keys( values )
.forEach( key => {
if ( handlers.hasOwnProperty(key) ) {
console.log( 'key found', key );
handlers[key] = values[key];
}
});
// .forEach( key => handlers[key] = values[key] )
}
return structuredClone( Object.assign( handlers ) );
}
/*
array method mapping
*/
at( index: number ): DSelector {
return $d( this.array.at( index ) as HTMLElement ) as DomSelector;
}
concat( items: DSelector | any[] ): DSelector{
let
that = this,
arr = items instanceof DomSelector
? items.array
: items as HTMLElement[]
;
arr.forEach( (e: HTMLElement) => {
that.#set.add( e );
});
return that;
}
entries(): IterableIterator<[number, any]> {
return this.array.entries();
}
every( predicate: (value: any, index: number, array: any[]) => unknown, thisArg?: any ): boolean {
return this.array.every( predicate, thisArg );
}
filter( predicate: (value: any, index: number, array: any[]) => unknown, thisArg?: any ): DSelector {
this.#set = new Set( this.array.filter( predicate, thisArg ) );
return this;
}
forEach( predicate: (value: any, index: number, array: any[]) => unknown, thisArg?: any ): DSelector {
this.array.forEach( predicate, thisArg );
return this;
}
includes( item: HTMLElement ): boolean{
return this.array.indexOf( item, 0 ) !== -1;
}
indexOf( item: HTMLElement, start?: number ): number{
return this.array.indexOf( item, start );
}
map( predicate: (value: any, index: number, array: any[]) => unknown, thisArg?: any ): Array<any>{
return this.array.map( predicate, thisArg );
}
pop(): HTMLElement {
return this.array.pop() as HTMLElement;
}
push( ...items: HTMLElement[] ): DSelector {
items.forEach( (item: HTMLElement) => {
this.#set.add( item );
});
return this;
}
shift(): HTMLElement | undefined {
let
that = this,
element = that.#set.size ? that.array[0] : undefined
;
if ( element ){
that.#set.delete( element );
}
return element;
}
slice( start: number, end?: number ): DSelector {
return $d( this.array.slice( start, end ) );
}
some( predicate: (value: any, index: number, array: any[]) => unknown, thisArg?: any ): boolean{
return this.array.some( predicate, thisArg );
}
splice( start: number, deleteCount?: number | undefined ): DSelector;
splice( start: number, deleteCount: number, ...items: HTMLElement[] ): DSelector {
let
that = this,
arr = that.array
;
arr.splice( start, deleteCount, ...items );
return $d( arr );
}
}
export interface DSelector extends DomSelector{};
class TbSelector{
#set: Set<any> = new Set();
public get set(): Set<any> {
return this.#set;
}
public get array(): any[] {
return Array.from( this.#set );
}
public set set( set: Set<any> ) {
this.#set = set;
return;
}
public get length(): number {
return this.#set.size;
}
public set length( value: number ){
//ignore
}
constructor( selection?: selection, element: HTMLElement = document.body ){
if ( !selection ) {
return this;
}
$d( selection, element ).forEach( (e: HTMLMcElement) => {
$t( e ).forEach( (t: any ) => {
this.#set.add(t);
});
});
}
$d( selection?: selection, element: HTMLElement = document.body ): DSelector {
let
result = new DomSelector(),
set: Set<HTMLElement> = new Set(),
compare: boolean = !!selection || false,
compareSelection: Set<HTMLElement> = new Set()
;
if ( compare ){
compareSelection = $d( selection, element ).set;
}
this.#set.forEach( (e: any) => {
if ( !compare || compareSelection.has( (e as any)._tg ) ) {
set.add( (e as any)._tg );
}
});
result.set = set;
return result;
}
children( nameSpace?: string ): TSelector{
let
that: TbSelector = this,
set: Set<any> = new Set()
;
that.#set.forEach( (e: any) => {
e.children( nameSpace ).forEach( ( tb: any ) => {
set.add( tb );
});
});
that.#set = set;
return that;
}
descendants( nameSpace?: string ): TSelector{
let
that: TbSelector = this,
set: Set<any> = new Set()
;
that.#set.forEach( (e: any) => {
e.descendants( nameSpace ).forEach( ( tb: any ) => {
set.add( tb );
});
});
that.#set = set;
return that;
}
first( nameSpace?: string ): TSelector{
let
that: TbSelector = this,
set: Set<any> = new Set()
;
that.#set.forEach( (e: any) => {
e
.parent()
.children()
.$d()
.first()
.$t()
.forEach( (tb: any) => {
set.add( tb );
});
});
that.#set = set;
if ( nameSpace ){
that.filter( ( tb: any ) => (tb as any).nameSpace = nameSpace );
}
return that;
}
has( element: any ): boolean{
return this.#set.has( element );
}
last( nameSpace?: string ): TSelector{
let
that: TbSelector = this,
set: Set<any> = new Set()
;
that.#set.forEach( (e: any) => {
e
.parent()
.children()
.$d()
.last()
.$t()
.forEach( (tb: any) => {
set.add( tb );
});
});
that.#set = set;
if ( nameSpace ){
that.filter( ( e: any ) => ( e as LooseObject).nameSpace = nameSpace );
}
return that;
}
next( nameSpace?: string ): TSelector{
let
that: TbSelector = this,
set: Set<any> = new Set()
;
that.#set.forEach( (e: any) => {
let
elements: Set<HTMLElement> = new Set(),
element: HTMLElement
;
element = (e as any)._tg;
while ( element.nextElementSibling !== null ){
element = element.nextElementSibling as HTMLElement;
elements.add( element );
}
$t([...elements])
.$d()
.first()
.$t()
.forEach( (tb: any) => {
set.add( tb );
});
});
that.#set = set;
if ( nameSpace ){
that.filter( ( e: any ) => ( e as LooseObject).nameSpace = nameSpace );
}
return that;
}
ns( nameSpace: string ): TSelector{
return this.filter( (tb: any) => (tb as any).nameSpace === nameSpace );
}
off( name: string, cb: Function ): TSelector{
let
that = this,
eventNames =
name.indexOf(' ') > -1
? name.split(' ').filter( (s) => s !== '' )
: [ name ]
;
// remove handlers
that.$d().array.forEach( (e: HTMLElement) => {
eventNames.forEach(function ( n: string ) {
// console.log('Tb Selector remove event', `[TB]${n}`, e, cb );
removeListener(
e,
`[TB]${n}`,
cb as EventListenerOrEventListenerObject
);
});
});
return that;
}
on( name: string, cb: Function, once: boolean = false ): Function{
let
that = this,
eventNames =
name.indexOf(' ') > -1
? name.split(' ').filter( (s) => s !== '' )
: [ name ]
;
function wrapper(){ wrapper.cb.apply(that, Array.from(arguments) ); };
wrapper.cb = cb;
// attach handler
that.forEach( ( tb: any) => {
eventNames.forEach(function ( n: string ) {
addListener(
(tb as any)._tg,
`[TB]${n}`,
(wrapper as EventListenerOrEventListenerObject),
false,
once
);
});
});
return wrapper;
}
one( name: string, cb: Function ): Function{
let
that = this,
eventNames =
name.indexOf(' ') > -1
? name.split(' ').filter( (s) => s !== '' )
: [ name ]
;
function wrapper(){ wrapper.cb.apply(that, Array.from(arguments) ); };
wrapper.cb = cb;
// attach handler
that.forEach( ( tb: any) => {
eventNames.forEach(function ( n: string ) {
addListener(
(tb as any)._tg,
`[TB]${n}`,
(wrapper as EventListenerOrEventListenerObject),
false,
true
);
});
});
return wrapper;
}
parent( nameSpace?: string ): TSelector {
let
that: TbSelector = this,
set: Set<any> = new Set()
;
that.#set.forEach( (e: any) => {
e.parent( nameSpace ).forEach( ( tb: any ) => {
set.add( tb );
});
});
that.#set = set;
return that;
}
parents( nameSpace?: string ): TSelector {
let
that: TbSelector = this,
set: Set<any> = new Set()
;
that.#set.forEach( (e: any) => {
e.parents( nameSpace ).forEach( ( tb: any ) => {
set.add( tb );
});
});
that.#set = set;
return that;
}
prev( nameSpace?: string ): TSelector{
let
that: TbSelector = this,
set: Set<any> = new Set()
;
that.#set.forEach( (e: any) => {
let
elements: Set<HTMLElement> = new Set(),
element: HTMLElement
;
element = (e as any)._tg;
while ( element.previousElementSibling !== null ){
element = element.previousElementSibling as HTMLElement;
elements.add( element );
}
$t([...elements])
.$d()
.last()
.$t()
.forEach( (tb: any) => {
set.add( tb );
});
});
that.#set = set;
if ( nameSpace ){
that.filter( ( e: any ) => ( e as LooseObject).nameSpace = nameSpace );
}
return that;
}
trigger( ev: string, data: any = {}, bubble: string = 'l' ): TSelector{
let
that = this
;
that.forEach( ( tb: any ) => {
tb.trigger( ev, data, bubble );
});
return that;
}
/*
array method mapping
*/
at( index: number ): TSelector {
return $t( this.array.at( index ) ) as TbSelector;
}
concat( items: TSelector ): TSelector {
let
that = this
;
items.forEach( ( tb: any) => {
that.#set.add( tb );
});
return that;
}
entries(): IterableIterator<[number, any]> {
return this.array.entries();
}
every( predicate: (value: any, index: number, array: any[]) => unknown, thisArg?: any ): boolean {
return this.array.every( predicate, thisArg );
}
filter( predicate: (value: any, index: number, array: any[]) => unknown, thisArg?: any ): TSelector {
return $t( this.array.filter( predicate, thisArg ) );
}
forEach( predicate: (value: any, index: number, array: any[]) => unknown, thisArg?: any ): TSelector {
let
that = this
;
that.array.forEach( predicate, thisArg );
return that;
}
includes( item: any ): boolean{
return this.array.indexOf( item ) !== -1;
}
indexOf( item: any, start?: number ): number{
return this.array.indexOf( item, start );
}
map( predicate: (value: any, index: number, array: any[]) => unknown, thisArg?: any ): Array<any>{
return this.array.map( predicate, thisArg );
}
pop(): any {
let
that = this,
arr = that.array,
result = arr.pop()
;
that.#set = new Set( arr );
return result as any;
}
push( item: any ): TSelector {
let
that = this
;
that.#set.add( item );
return that;
}
shift(): any | undefined {
let
that = this,
first = this.array.shift() as any
;
that.#set.delete( first );
return first;
}
slice( start: number, end?: number ): TSelector {
return $t( this.array.slice( start, end ) );
}
some( predicate: (value: any, index: number, array: any[]) => unknown, thisArg?: any ): boolean{
return this.array.some( predicate, thisArg );
}
splice( start: number, deleteCount?: number | undefined ): TSelector;
splice( start: number, deleteCount: number, ...items: never[] ): TSelector {
let
that = this,
arr = that.array,
result = arr.splice( start, deleteCount, ...items )
;
return $t( result );
}
}
export interface TSelector extends TbSelector{};
export const $d = ( selection: selection, element: any = document ): DSelector => {
let
set = new Set<HTMLElement>()
;
if ( selection instanceof Array ){
selection.forEach( value => {
$d( value, element ).forEach( n => {
set.add( n );
});
});
return new DomSelector( [...set] );
} else {
return new DomSelector( selection, element );
}
}
export const $t = ( selection?: selection, element: any = document ): TSelector => {
return $d( selection, element ).$t();
}
export type singleSelector = string | ShadowRoot | HTMLElement | HTMLElement[] | HTMLCollection | NodeList | DSelector | MC | DC | TSelector | undefined;
export type selection = singleSelector | singleSelector[];