Skip to content

⬅️ Back to Table of Contents

📄 ui.js

📊 Analysis Summary

Metric Count
🔧 Functions 71
🧱 Classes 21
📊 Variables & Constants 40

📚 Table of Contents

🛠️ File Location:

📂 editor/js/libs/ui.js

Variables & Constants

Name Type Kind Value Exported
argument any let/var arguments[ i ]
argument any let/var arguments[ i ]
properties string[] let/var [ 'position', 'left', 'top', 'right', 'bottom', 'width', 'height', 'display',...
method string let/var 'set' + property.substring( 0, 1 ).toUpperCase() + property.substring( 1 )
events string[] let/var [ 'KeyUp', 'KeyDown', 'MouseOver', 'MouseOut', 'Click', 'DblClick', 'Change',...
method string let/var 'on' + event
cursor undefined let/var this.selectionStart
selected any let/var this.dom.value
scope this let/var this
changeEvent Event let/var new Event( 'change', { bubbles: true, cancelable: true } )
distance number let/var 0
onMouseDownValue number let/var 0
pointer { x: number; y: number; } let/var { x: 0, y: 0 }
prevPointer { x: number; y: number; } let/var { x: 0, y: 0 }
currentValue number let/var scope.value
value number let/var onMouseDownValue + ( distance / ( event.shiftKey ? 5 : 50 ) ) * scope.step
currentValue number let/var scope.value
value number let/var onMouseDownValue + ( distance / ( event.shiftKey ? 5 : 50 ) ) * scope.step
scope this let/var this
changeEvent Event let/var new Event( 'change', { bubbles: true, cancelable: true } )
distance number let/var 0
onMouseDownValue number let/var 0
pointer { x: number; y: number; } let/var { x: 0, y: 0 }
prevPointer { x: number; y: number; } let/var { x: 0, y: 0 }
currentValue number let/var scope.value
value number let/var onMouseDownValue + ( distance / ( event.shiftKey ? 5 : 50 ) ) * scope.step
tab any let/var *not shown*
panel any let/var *not shown*
scope this let/var this
tabOffsetRight any let/var tab.dom.offsetLeft + tab.dom.offsetWidth
containerWidth any let/var this.tabsDiv.dom.getBoundingClientRect().width
tab UITab let/var new UITab( label, this )
panel UIDiv let/var new UIDiv()
scope this let/var this
item any let/var this.listitems[ 0 ]
item any let/var this.items[ i ]
listitem ListboxItem let/var new ListboxItem( this )
element any let/var this.listitems[ i ]
changeEvent Event let/var new Event( 'change', { bubbles: true, cancelable: true } )
scope this let/var this

Functions

UIElement.add(): this

Returns: this

Calls:

  • this.dom.appendChild
  • console.error
Code
add() {

        for ( let i = 0; i < arguments.length; i ++ ) {

            const argument = arguments[ i ];

            if ( argument instanceof UIElement ) {

                this.dom.appendChild( argument.dom );

            } else {

                console.error( 'UIElement:', argument, 'is not an instance of UIElement.' );

            }

        }

        return this;

    }

UIElement.remove(): this

Returns: this

Calls:

  • this.dom.removeChild
  • console.error
Code
remove() {

        for ( let i = 0; i < arguments.length; i ++ ) {

            const argument = arguments[ i ];

            if ( argument instanceof UIElement ) {

                this.dom.removeChild( argument.dom );

            } else {

                console.error( 'UIElement:', argument, 'is not an instance of UIElement.' );

            }

        }

        return this;

    }

UIElement.clear(): void

Returns: void

Calls:

  • this.dom.removeChild
Code
clear() {

        while ( this.dom.children.length ) {

            this.dom.removeChild( this.dom.lastChild );

        }

    }

UIElement.setId(id: any): this

Parameters:

  • id any

Returns: this

Code
setId( id ) {

        this.dom.id = id;

        return this;

    }

UIElement.getId(): any

Returns: any

Code
getId() {

        return this.dom.id;

    }

UIElement.setClass(name: any): this

Parameters:

  • name any

Returns: this

Code
setClass( name ) {

        this.dom.className = name;

        return this;

    }

UIElement.addClass(name: any): this

Parameters:

  • name any

Returns: this

Calls:

  • this.dom.classList.add
Code
addClass( name ) {

        this.dom.classList.add( name );

        return this;

    }

UIElement.removeClass(name: any): this

Parameters:

  • name any

Returns: this

Calls:

  • this.dom.classList.remove
Code
removeClass( name ) {

        this.dom.classList.remove( name );

        return this;

    }

UIElement.toggleClass(name: any, toggle: any): this

Parameters:

  • name any
  • toggle any

Returns: this

Calls:

  • this.dom.classList.toggle
Code
toggleClass( name, toggle ) {

        this.dom.classList.toggle( name, toggle );

        return this;

    }

UIElement.setStyle(style: any, array: any): this

Parameters:

  • style any
  • array any

Returns: this

Code
setStyle( style, array ) {

        for ( let i = 0; i < array.length; i ++ ) {

            this.dom.style[ style ] = array[ i ];

        }

        return this;

    }

UIElement.setHidden(isHidden: any): this

Parameters:

  • isHidden any

Returns: this

Code
setHidden( isHidden ) {

        this.dom.hidden = isHidden;

        return this;

    }

UIElement.isHidden(): any

Returns: any

Code
isHidden() {

        return this.dom.hidden;

    }

UIElement.setDisabled(value: any): this

Parameters:

  • value any

Returns: this

Code
setDisabled( value ) {

        this.dom.disabled = value;

        return this;

    }

UIElement.setTextContent(value: any): this

Parameters:

  • value any

Returns: this

Code
setTextContent( value ) {

        this.dom.textContent = value;

        return this;

    }

UIElement.setInnerHTML(value: any): void

Parameters:

  • value any

Returns: void

Code
setInnerHTML( value ) {

        this.dom.innerHTML = value;

    }

UIElement.getIndexOfChild(element: any): any

Parameters:

  • element any

Returns: any

Calls:

  • Array.prototype.indexOf.call
Code
getIndexOfChild( element ) {

        return Array.prototype.indexOf.call( this.dom.children, element.dom );

    }

UIText.getValue(): any

Returns: any

Code
getValue() {

        return this.dom.textContent;

    }

UIText.setValue(value: any): this

Parameters:

  • value any

Returns: this

Code
setValue( value ) {

        if ( value !== undefined ) {

            this.dom.textContent = value;

        }

        return this;

    }

UIInput.getValue(): any

Returns: any

Code
getValue() {

        return this.dom.value;

    }

UIInput.setValue(value: any): this

Parameters:

  • value any

Returns: this

Code
setValue( value ) {

        this.dom.value = value;

        return this;

    }

UITextArea.getValue(): any

Returns: any

Code
getValue() {

        return this.dom.value;

    }

UITextArea.setValue(value: any): this

Parameters:

  • value any

Returns: this

Code
setValue( value ) {

        this.dom.value = value;

        return this;

    }

UISelect.setMultiple(boolean: any): this

Parameters:

  • boolean any

Returns: this

Code
setMultiple( boolean ) {

        this.dom.multiple = boolean;

        return this;

    }

UISelect.setOptions(options: any): this

Parameters:

  • options any

Returns: this

Calls:

  • this.dom.removeChild
  • document.createElement
  • this.dom.appendChild
Code
setOptions( options ) {

        const selected = this.dom.value;

        while ( this.dom.children.length > 0 ) {

            this.dom.removeChild( this.dom.firstChild );

        }

        for ( const key in options ) {

            const option = document.createElement( 'option' );
            option.value = key;
            option.innerHTML = options[ key ];
            this.dom.appendChild( option );

        }

        this.dom.value = selected;

        return this;

    }

UISelect.getValue(): any

Returns: any

Code
getValue() {

        return this.dom.value;

    }

UISelect.setValue(value: any): this

Parameters:

  • value any

Returns: this

Calls:

  • String
Code
setValue( value ) {

        value = String( value );

        if ( this.dom.value !== value ) {

            this.dom.value = value;

        }

        return this;

    }

UICheckbox.getValue(): any

Returns: any

Code
getValue() {

        return this.dom.checked;

    }

UICheckbox.setValue(value: any): this

Parameters:

  • value any

Returns: this

Code
setValue( value ) {

        if ( value !== undefined ) {

            this.dom.checked = value;

        }

        return this;

    }

UIColor.getValue(): any

Returns: any

Code
getValue() {

        return this.dom.value;

    }

UIColor.getHexValue(): number

Returns: number

Calls:

  • parseInt
  • this.dom.value.substring
Code
getHexValue() {

        return parseInt( this.dom.value.substring( 1 ), 16 );

    }

UIColor.setValue(value: any): this

Parameters:

  • value any

Returns: this

Code
setValue( value ) {

        this.dom.value = value;

        return this;

    }

UIColor.setHexValue(hex: any): this

Parameters:

  • hex any

Returns: this

Calls:

  • ( '000000' + hex.toString( 16 ) ).slice
  • hex.toString
Code
setHexValue( hex ) {

        this.dom.value = '#' + ( '000000' + hex.toString( 16 ) ).slice( - 6 );

        return this;

    }

UINumber.getValue(): number

Returns: number

Code
getValue() {

        return this.value;

    }

UINumber.setValue(value: any): this

Parameters:

  • value any

Returns: this

Calls:

  • parseFloat
  • value.toFixed
Code
setValue( value ) {

        if ( value !== undefined ) {

            value = parseFloat( value );

            if ( value < this.min ) value = this.min;
            if ( value > this.max ) value = this.max;

            this.value = value;
            this.dom.value = value.toFixed( this.precision );

            if ( this.unit !== '' ) this.dom.value += ' ' + this.unit;

        }

        return this;

    }

UINumber.setPrecision(precision: any): this

Parameters:

  • precision any

Returns: this

Code
setPrecision( precision ) {

        this.precision = precision;

        return this;

    }

UINumber.setStep(step: any): this

Parameters:

  • step any

Returns: this

Code
setStep( step ) {

        this.step = step;

        return this;

    }

UINumber.setNudge(nudge: any): this

Parameters:

  • nudge any

Returns: this

Code
setNudge( nudge ) {

        this.nudge = nudge;

        return this;

    }

UINumber.setRange(min: any, max: any): this

Parameters:

  • min any
  • max any

Returns: this

Code
setRange( min, max ) {

        this.min = min;
        this.max = max;

        return this;

    }

UINumber.setUnit(unit: any): this

Parameters:

  • unit any

Returns: this

Calls:

  • this.setValue
Code
setUnit( unit ) {

        this.unit = unit;

        this.setValue( this.value );

        return this;

    }

onMouseDown(event: any): void

Parameters:

  • event any

Returns: void

Calls:

  • event.preventDefault
  • document.addEventListener
Code
function onMouseDown( event ) {

            if ( document.activeElement === scope.dom ) return;

            event.preventDefault();

            distance = 0;

            onMouseDownValue = scope.value;

            prevPointer.x = event.clientX;
            prevPointer.y = event.clientY;

            document.addEventListener( 'mousemove', onMouseMove );
            document.addEventListener( 'mouseup', onMouseUp );

        }

onMouseMove(event: any): void

Parameters:

  • event any

Returns: void

Calls:

  • Math.min
  • Math.max
  • scope.setValue
  • scope.dom.dispatchEvent
Code
function onMouseMove( event ) {

            const currentValue = scope.value;

            pointer.x = event.clientX;
            pointer.y = event.clientY;

            distance += ( pointer.x - prevPointer.x ) - ( pointer.y - prevPointer.y );

            let value = onMouseDownValue + ( distance / ( event.shiftKey ? 5 : 50 ) ) * scope.step;
            value = Math.min( scope.max, Math.max( scope.min, value ) );

            if ( currentValue !== value ) {

                scope.setValue( value );
                scope.dom.dispatchEvent( changeEvent );

            }

            prevPointer.x = event.clientX;
            prevPointer.y = event.clientY;

        }

onMouseUp(): void

Returns: void

Calls:

  • document.removeEventListener
  • Math.abs
  • scope.dom.focus
  • scope.dom.select
Code
function onMouseUp() {

            document.removeEventListener( 'mousemove', onMouseMove );
            document.removeEventListener( 'mouseup', onMouseUp );

            if ( Math.abs( distance ) < 2 ) {

                scope.dom.focus();
                scope.dom.select();

            }

        }

onTouchStart(event: any): void

Parameters:

  • event any

Returns: void

Calls:

  • document.addEventListener
Code
function onTouchStart( event ) {

            if ( event.touches.length === 1 ) {

                distance = 0;

                onMouseDownValue = scope.value;

                prevPointer.x = event.touches[ 0 ].pageX;
                prevPointer.y = event.touches[ 0 ].pageY;

                document.addEventListener( 'touchmove', onTouchMove, { passive: false } );
                document.addEventListener( 'touchend', onTouchEnd );

            }

        }

onTouchMove(event: any): void

Parameters:

  • event any

Returns: void

Calls:

  • event.preventDefault
  • Math.min
  • Math.max
  • scope.setValue
  • scope.dom.dispatchEvent
Code
function onTouchMove( event ) {

            event.preventDefault();

            const currentValue = scope.value;

            pointer.x = event.touches[ 0 ].pageX;
            pointer.y = event.touches[ 0 ].pageY;

            distance += ( pointer.x - prevPointer.x ) - ( pointer.y - prevPointer.y );

            let value = onMouseDownValue + ( distance / ( event.shiftKey ? 5 : 50 ) ) * scope.step;
            value = Math.min( scope.max, Math.max( scope.min, value ) );

            if ( currentValue !== value ) {

                scope.setValue( value );
                scope.dom.dispatchEvent( changeEvent );

            }

            prevPointer.x = event.touches[ 0 ].pageX;
            prevPointer.y = event.touches[ 0 ].pageY;

        }

onTouchEnd(event: any): void

Parameters:

  • event any

Returns: void

Calls:

  • document.removeEventListener
Code
function onTouchEnd( event ) {

            if ( event.touches.length === 0 ) {

                document.removeEventListener( 'touchmove', onTouchMove );
                document.removeEventListener( 'touchend', onTouchEnd );

            }

        }

onChange(): void

Returns: void

Calls:

  • scope.setValue
Code
function onChange() {

            scope.setValue( scope.dom.value );

        }

onFocus(): void

Returns: void

Code
function onFocus() {

            scope.dom.style.backgroundColor = '';
            scope.dom.style.cursor = '';

        }

onBlur(): void

Returns: void

Code
function onBlur() {

            scope.dom.style.backgroundColor = 'transparent';
            scope.dom.style.cursor = 'ns-resize';

        }

onKeyDown(event: any): void

Parameters:

  • event any

Returns: void

Calls:

  • event.stopPropagation
  • scope.dom.blur
  • event.preventDefault
  • scope.setValue
  • scope.getValue
  • scope.dom.dispatchEvent
Code
function onKeyDown( event ) {

            event.stopPropagation();

            switch ( event.code ) {

                case 'Enter':
                    scope.dom.blur();
                    break;

                case 'ArrowUp':
                    event.preventDefault();
                    scope.setValue( scope.getValue() + scope.nudge );
                    scope.dom.dispatchEvent( changeEvent );
                    break;

                case 'ArrowDown':
                    event.preventDefault();
                    scope.setValue( scope.getValue() - scope.nudge );
                    scope.dom.dispatchEvent( changeEvent );
                    break;

            }

        }

UIInteger.getValue(): number

Returns: number

Code
getValue() {

        return this.value;

    }

UIInteger.setValue(value: any): this

Parameters:

  • value any

Returns: this

Calls:

  • parseInt
Code
setValue( value ) {

        if ( value !== undefined ) {

            value = parseInt( value );

            this.value = value;
            this.dom.value = value;

        }

        return this;

    }

UIInteger.setStep(step: any): this

Parameters:

  • step any

Returns: this

Calls:

  • parseInt
Code
setStep( step ) {

        this.step = parseInt( step );

        return this;

    }

UIInteger.setNudge(nudge: any): this

Parameters:

  • nudge any

Returns: this

Code
setNudge( nudge ) {

        this.nudge = nudge;

        return this;

    }

UIInteger.setRange(min: any, max: any): this

Parameters:

  • min any
  • max any

Returns: this

Code
setRange( min, max ) {

        this.min = min;
        this.max = max;

        return this;

    }

onMouseDown(event: any): void

Parameters:

  • event any

Returns: void

Calls:

  • event.preventDefault
  • document.addEventListener
Code
function onMouseDown( event ) {

            if ( document.activeElement === scope.dom ) return;

            event.preventDefault();

            distance = 0;

            onMouseDownValue = scope.value;

            prevPointer.x = event.clientX;
            prevPointer.y = event.clientY;

            document.addEventListener( 'mousemove', onMouseMove );
            document.addEventListener( 'mouseup', onMouseUp );

        }

onMouseMove(event: any): void

Parameters:

  • event any

Returns: void

Calls:

  • Math.min
  • Math.max
  • scope.setValue
  • scope.dom.dispatchEvent
Code
function onMouseMove( event ) {

            const currentValue = scope.value;

            pointer.x = event.clientX;
            pointer.y = event.clientY;

            distance += ( pointer.x - prevPointer.x ) - ( pointer.y - prevPointer.y );

            let value = onMouseDownValue + ( distance / ( event.shiftKey ? 5 : 50 ) ) * scope.step;
            value = Math.min( scope.max, Math.max( scope.min, value ) ) | 0;

            if ( currentValue !== value ) {

                scope.setValue( value );
                scope.dom.dispatchEvent( changeEvent );

            }

            prevPointer.x = event.clientX;
            prevPointer.y = event.clientY;

        }

onMouseUp(): void

Returns: void

Calls:

  • document.removeEventListener
  • Math.abs
  • scope.dom.focus
  • scope.dom.select
Code
function onMouseUp() {

            document.removeEventListener( 'mousemove', onMouseMove );
            document.removeEventListener( 'mouseup', onMouseUp );

            if ( Math.abs( distance ) < 2 ) {

                scope.dom.focus();
                scope.dom.select();

            }

        }

onChange(): void

Returns: void

Calls:

  • scope.setValue
Code
function onChange() {

            scope.setValue( scope.dom.value );

        }

onFocus(): void

Returns: void

Code
function onFocus() {

            scope.dom.style.backgroundColor = '';
            scope.dom.style.cursor = '';

        }

onBlur(): void

Returns: void

Code
function onBlur() {

            scope.dom.style.backgroundColor = 'transparent';
            scope.dom.style.cursor = 'ns-resize';

        }

onKeyDown(event: any): void

Parameters:

  • event any

Returns: void

Calls:

  • event.stopPropagation
  • scope.dom.blur
  • event.preventDefault
  • scope.setValue
  • scope.getValue
  • scope.dom.dispatchEvent
Code
function onKeyDown( event ) {

            event.stopPropagation();

            switch ( event.code ) {

                case 'Enter':
                    scope.dom.blur();
                    break;

                case 'ArrowUp':
                    event.preventDefault();
                    scope.setValue( scope.getValue() + scope.nudge );
                    scope.dom.dispatchEvent( changeEvent );
                    break;

                case 'ArrowDown':
                    event.preventDefault();
                    scope.setValue( scope.getValue() - scope.nudge );
                    scope.dom.dispatchEvent( changeEvent );
                    break;

            }

        }

UIProgress.setValue(value: any): void

Parameters:

  • value any

Returns: void

Code
setValue( value ) {

        this.dom.value = value;

    }

UITabbedPanel.select(id: any): this

Parameters:

  • id any

Returns: this

Calls:

  • this.tabs.find
  • this.panels.find
  • tab.removeClass
  • panel.setDisplay
  • tab.addClass
  • this.tabsDiv.dom.getBoundingClientRect
  • this.tabsDiv.dom.scrollTo

Internal Comments:

// Deselect current selection
// Scrolls to tab

Code
select( id ) {

        let tab;
        let panel;
        const scope = this;

        // Deselect current selection
        if ( this.selected && this.selected.length ) {

            tab = this.tabs.find( function ( item ) {

                return item.dom.id === scope.selected;

            } );
            panel = this.panels.find( function ( item ) {

                return item.dom.id === scope.selected;

            } );

            if ( tab ) {

                tab.removeClass( 'selected' );

            }

            if ( panel ) {

                panel.setDisplay( 'none' );

            }

        }

        tab = this.tabs.find( function ( item ) {

            return item.dom.id === id;

        } );
        panel = this.panels.find( function ( item ) {

            return item.dom.id === id;

        } );

        if ( tab ) {

            tab.addClass( 'selected' );

        }

        if ( panel ) {

            panel.setDisplay( '' );

        }

        this.selected = id;

        // Scrolls to tab
        if ( tab ) {

            const tabOffsetRight = tab.dom.offsetLeft + tab.dom.offsetWidth;
            const containerWidth = this.tabsDiv.dom.getBoundingClientRect().width;

            if ( tabOffsetRight > containerWidth ) {

                this.tabsDiv.dom.scrollTo( { left: tabOffsetRight - containerWidth, behavior: 'smooth' } );

            }

            if ( tab.dom.offsetLeft < this.tabsDiv.dom.scrollLeft ) {

                this.tabsDiv.dom.scrollTo( { left: 0, behavior: 'smooth' } );

            }

        }

        return this;

    }

UITabbedPanel.addTab(id: any, label: any, items: any): void

Parameters:

  • id any
  • label any
  • items any

Returns: void

Calls:

  • tab.setId
  • this.tabs.push
  • this.tabsDiv.add
  • panel.setId
  • panel.add
  • panel.setDisplay
  • this.panels.push
  • this.panelsDiv.add
  • this.select
Code
addTab( id, label, items ) {

        const tab = new UITab( label, this );
        tab.setId( id );
        this.tabs.push( tab );
        this.tabsDiv.add( tab );

        const panel = new UIDiv();
        panel.setId( id );
        panel.add( items );
        panel.setDisplay( 'none' );
        this.panels.push( panel );
        this.panelsDiv.add( panel );

        this.select( id );

    }

UIListbox.setItems(items: any): void

Parameters:

  • items any

Returns: void

Calls:

  • Array.isArray
  • this.render
Code
setItems( items ) {

        if ( Array.isArray( items ) ) {

            this.items = items;

        }

        this.render();

    }

UIListbox.render(): void

Returns: void

Calls:

  • item.dom.remove
  • this.listitems.splice
  • listitem.setId
  • listitem.setTextContent
  • this.add
Code
render( ) {

        while ( this.listitems.length ) {

            const item = this.listitems[ 0 ];

            item.dom.remove();

            this.listitems.splice( 0, 1 );

        }

        for ( let i = 0; i < this.items.length; i ++ ) {

            const item = this.items[ i ];

            const listitem = new ListboxItem( this );
            listitem.setId( item.id || `Listbox-${i}` );
            listitem.setTextContent( item.name || item.type );
            this.add( listitem );

        }

    }

UIListbox.add(): void

Returns: void

Calls:

  • Array.from
  • this.listitems.concat
  • UIElement.prototype.add.apply
Code
add() {

        const items = Array.from( arguments );

        this.listitems = this.listitems.concat( items );

        UIElement.prototype.add.apply( this, items );

    }

UIListbox.selectIndex(index: any): void

Parameters:

  • index any

Returns: void

Calls:

  • this.setValue
  • this.listitems[ index ].getId
Code
selectIndex( index ) {

        if ( index >= 0 && index < this.items.length ) {

            this.setValue( this.listitems[ index ].getId() );

        }

        this.selectedIndex = index;

    }

UIListbox.getValue(): any

Returns: any

Code
getValue() {

        return this.selectedValue;

    }

UIListbox.setValue(value: any): void

Parameters:

  • value any

Returns: void

Calls:

  • element.getId
  • element.addClass
  • element.removeClass
  • this.dom.dispatchEvent
Code
setValue( value ) {

        for ( let i = 0; i < this.listitems.length; i ++ ) {

            const element = this.listitems[ i ];

            if ( element.getId() === value ) {

                element.addClass( 'active' );

            } else {

                element.removeClass( 'active' );

            }

        }

        this.selectedValue = value;

        const changeEvent = new Event( 'change', { bubbles: true, cancelable: true } );
        this.dom.dispatchEvent( changeEvent );

    }

onClick(): void

Returns: void

Calls:

  • scope.parent.setValue
  • scope.getId
Code
function onClick() {

            if ( scope.parent ) {

                scope.parent.setValue( scope.getId( ) );

            }

        }

Classes

UIElement

Class Code
class UIElement {

    constructor( dom ) {

        this.dom = dom;

    }

    add() {

        for ( let i = 0; i < arguments.length; i ++ ) {

            const argument = arguments[ i ];

            if ( argument instanceof UIElement ) {

                this.dom.appendChild( argument.dom );

            } else {

                console.error( 'UIElement:', argument, 'is not an instance of UIElement.' );

            }

        }

        return this;

    }

    remove() {

        for ( let i = 0; i < arguments.length; i ++ ) {

            const argument = arguments[ i ];

            if ( argument instanceof UIElement ) {

                this.dom.removeChild( argument.dom );

            } else {

                console.error( 'UIElement:', argument, 'is not an instance of UIElement.' );

            }

        }

        return this;

    }

    clear() {

        while ( this.dom.children.length ) {

            this.dom.removeChild( this.dom.lastChild );

        }

    }

    setId( id ) {

        this.dom.id = id;

        return this;

    }

    getId() {

        return this.dom.id;

    }

    setClass( name ) {

        this.dom.className = name;

        return this;

    }

    addClass( name ) {

        this.dom.classList.add( name );

        return this;

    }

    removeClass( name ) {

        this.dom.classList.remove( name );

        return this;

    }

    toggleClass( name, toggle ) {

        this.dom.classList.toggle( name, toggle );

        return this;

    }

    setStyle( style, array ) {

        for ( let i = 0; i < array.length; i ++ ) {

            this.dom.style[ style ] = array[ i ];

        }

        return this;

    }

    setHidden( isHidden ) {

        this.dom.hidden = isHidden;

        return this;

    }

    isHidden() {

        return this.dom.hidden;

    }

    setDisabled( value ) {

        this.dom.disabled = value;

        return this;

    }

    setTextContent( value ) {

        this.dom.textContent = value;

        return this;

    }

    setInnerHTML( value ) {

        this.dom.innerHTML = value;

    }

    getIndexOfChild( element ) {

        return Array.prototype.indexOf.call( this.dom.children, element.dom );

    }

}

Methods

add(): this
Code
add() {

        for ( let i = 0; i < arguments.length; i ++ ) {

            const argument = arguments[ i ];

            if ( argument instanceof UIElement ) {

                this.dom.appendChild( argument.dom );

            } else {

                console.error( 'UIElement:', argument, 'is not an instance of UIElement.' );

            }

        }

        return this;

    }
remove(): this
Code
remove() {

        for ( let i = 0; i < arguments.length; i ++ ) {

            const argument = arguments[ i ];

            if ( argument instanceof UIElement ) {

                this.dom.removeChild( argument.dom );

            } else {

                console.error( 'UIElement:', argument, 'is not an instance of UIElement.' );

            }

        }

        return this;

    }
clear(): void
Code
clear() {

        while ( this.dom.children.length ) {

            this.dom.removeChild( this.dom.lastChild );

        }

    }
setId(id: any): this
Code
setId( id ) {

        this.dom.id = id;

        return this;

    }
getId(): any
Code
getId() {

        return this.dom.id;

    }
setClass(name: any): this
Code
setClass( name ) {

        this.dom.className = name;

        return this;

    }
addClass(name: any): this
Code
addClass( name ) {

        this.dom.classList.add( name );

        return this;

    }
removeClass(name: any): this
Code
removeClass( name ) {

        this.dom.classList.remove( name );

        return this;

    }
toggleClass(name: any, toggle: any): this
Code
toggleClass( name, toggle ) {

        this.dom.classList.toggle( name, toggle );

        return this;

    }
setStyle(style: any, array: any): this
Code
setStyle( style, array ) {

        for ( let i = 0; i < array.length; i ++ ) {

            this.dom.style[ style ] = array[ i ];

        }

        return this;

    }
setHidden(isHidden: any): this
Code
setHidden( isHidden ) {

        this.dom.hidden = isHidden;

        return this;

    }
isHidden(): any
Code
isHidden() {

        return this.dom.hidden;

    }
setDisabled(value: any): this
Code
setDisabled( value ) {

        this.dom.disabled = value;

        return this;

    }
setTextContent(value: any): this
Code
setTextContent( value ) {

        this.dom.textContent = value;

        return this;

    }
setInnerHTML(value: any): void
Code
setInnerHTML( value ) {

        this.dom.innerHTML = value;

    }
getIndexOfChild(element: any): any
Code
getIndexOfChild( element ) {

        return Array.prototype.indexOf.call( this.dom.children, element.dom );

    }

UISpan

Class Code
class UISpan extends UIElement {

    constructor() {

        super( document.createElement( 'span' ) );

    }

}

UIDiv

Class Code
class UIDiv extends UIElement {

    constructor() {

        super( document.createElement( 'div' ) );

    }

}

UIRow

Class Code
class UIRow extends UIDiv {

    constructor() {

        super();

        this.dom.className = 'Row';

    }

}

UIPanel

Class Code
class UIPanel extends UIDiv {

    constructor() {

        super();

        this.dom.className = 'Panel';

    }

}

UIText

Class Code
class UIText extends UISpan {

    constructor( text ) {

        super();

        this.dom.className = 'Text';
        this.dom.style.cursor = 'default';
        this.dom.style.display = 'inline-block';

        this.setValue( text );

    }

    getValue() {

        return this.dom.textContent;

    }

    setValue( value ) {

        if ( value !== undefined ) {

            this.dom.textContent = value;

        }

        return this;

    }

}

Methods

getValue(): any
Code
getValue() {

        return this.dom.textContent;

    }
setValue(value: any): this
Code
setValue( value ) {

        if ( value !== undefined ) {

            this.dom.textContent = value;

        }

        return this;

    }

UIInput

Class Code
class UIInput extends UIElement {

    constructor( text ) {

        super( document.createElement( 'input' ) );

        this.dom.className = 'Input';
        this.dom.style.padding = '2px';
        this.dom.style.border = '1px solid transparent';

        this.dom.setAttribute( 'autocomplete', 'off' );

        this.dom.addEventListener( 'keydown', function ( event ) {

            event.stopPropagation();

        } );

        this.setValue( text );

    }

    getValue() {

        return this.dom.value;

    }

    setValue( value ) {

        this.dom.value = value;

        return this;

    }

}

Methods

getValue(): any
Code
getValue() {

        return this.dom.value;

    }
setValue(value: any): this
Code
setValue( value ) {

        this.dom.value = value;

        return this;

    }

UITextArea

Class Code
class UITextArea extends UIElement {

    constructor() {

        super( document.createElement( 'textarea' ) );

        this.dom.className = 'TextArea';
        this.dom.style.padding = '2px';
        this.dom.spellcheck = false;

        this.dom.setAttribute( 'autocomplete', 'off' );

        this.dom.addEventListener( 'keydown', function ( event ) {

            event.stopPropagation();

            if ( event.code === 'Tab' ) {

                event.preventDefault();

                const cursor = this.selectionStart;

                this.value = this.value.substring( 0, cursor ) + '\t' + this.value.substring( cursor );
                this.selectionStart = cursor + 1;
                this.selectionEnd = this.selectionStart;

            }

        } );

    }

    getValue() {

        return this.dom.value;

    }

    setValue( value ) {

        this.dom.value = value;

        return this;

    }

}

Methods

getValue(): any
Code
getValue() {

        return this.dom.value;

    }
setValue(value: any): this
Code
setValue( value ) {

        this.dom.value = value;

        return this;

    }

UISelect

Class Code
class UISelect extends UIElement {

    constructor() {

        super( document.createElement( 'select' ) );

        this.dom.className = 'Select';
        this.dom.style.padding = '2px';

        this.dom.setAttribute( 'autocomplete', 'off' );

        this.dom.addEventListener( 'pointerdown', function ( event ) {

            event.stopPropagation();

        } );

    }

    setMultiple( boolean ) {

        this.dom.multiple = boolean;

        return this;

    }

    setOptions( options ) {

        const selected = this.dom.value;

        while ( this.dom.children.length > 0 ) {

            this.dom.removeChild( this.dom.firstChild );

        }

        for ( const key in options ) {

            const option = document.createElement( 'option' );
            option.value = key;
            option.innerHTML = options[ key ];
            this.dom.appendChild( option );

        }

        this.dom.value = selected;

        return this;

    }

    getValue() {

        return this.dom.value;

    }

    setValue( value ) {

        value = String( value );

        if ( this.dom.value !== value ) {

            this.dom.value = value;

        }

        return this;

    }

}

Methods

setMultiple(boolean: any): this
Code
setMultiple( boolean ) {

        this.dom.multiple = boolean;

        return this;

    }
setOptions(options: any): this
Code
setOptions( options ) {

        const selected = this.dom.value;

        while ( this.dom.children.length > 0 ) {

            this.dom.removeChild( this.dom.firstChild );

        }

        for ( const key in options ) {

            const option = document.createElement( 'option' );
            option.value = key;
            option.innerHTML = options[ key ];
            this.dom.appendChild( option );

        }

        this.dom.value = selected;

        return this;

    }
getValue(): any
Code
getValue() {

        return this.dom.value;

    }
setValue(value: any): this
Code
setValue( value ) {

        value = String( value );

        if ( this.dom.value !== value ) {

            this.dom.value = value;

        }

        return this;

    }

UICheckbox

Class Code
class UICheckbox extends UIElement {

    constructor( boolean ) {

        super( document.createElement( 'input' ) );

        this.dom.className = 'Checkbox';
        this.dom.type = 'checkbox';

        this.dom.addEventListener( 'pointerdown', function ( event ) {

            // Workaround for TransformControls blocking events in Viewport.Controls checkboxes

            event.stopPropagation();

        } );

        this.setValue( boolean );

    }

    getValue() {

        return this.dom.checked;

    }

    setValue( value ) {

        if ( value !== undefined ) {

            this.dom.checked = value;

        }

        return this;

    }

}

Methods

getValue(): any
Code
getValue() {

        return this.dom.checked;

    }
setValue(value: any): this
Code
setValue( value ) {

        if ( value !== undefined ) {

            this.dom.checked = value;

        }

        return this;

    }

UIColor

Class Code
class UIColor extends UIElement {

    constructor() {

        super( document.createElement( 'input' ) );

        this.dom.className = 'Color';
        this.dom.style.width = '32px';
        this.dom.style.height = '16px';
        this.dom.style.border = '0px';
        this.dom.style.padding = '2px';
        this.dom.style.backgroundColor = 'transparent';

        this.dom.setAttribute( 'autocomplete', 'off' );

        try {

            this.dom.type = 'color';
            this.dom.value = '#ffffff';

        } catch ( exception ) {}

    }

    getValue() {

        return this.dom.value;

    }

    getHexValue() {

        return parseInt( this.dom.value.substring( 1 ), 16 );

    }

    setValue( value ) {

        this.dom.value = value;

        return this;

    }

    setHexValue( hex ) {

        this.dom.value = '#' + ( '000000' + hex.toString( 16 ) ).slice( - 6 );

        return this;

    }

}

Methods

getValue(): any
Code
getValue() {

        return this.dom.value;

    }
getHexValue(): number
Code
getHexValue() {

        return parseInt( this.dom.value.substring( 1 ), 16 );

    }
setValue(value: any): this
Code
setValue( value ) {

        this.dom.value = value;

        return this;

    }
setHexValue(hex: any): this
Code
setHexValue( hex ) {

        this.dom.value = '#' + ( '000000' + hex.toString( 16 ) ).slice( - 6 );

        return this;

    }

UINumber

Class Code
class UINumber extends UIElement {

    constructor( number ) {

        super( document.createElement( 'input' ) );

        this.dom.style.cursor = 'ns-resize';
        this.dom.className = 'Number';
        this.dom.value = '0.00';

        this.dom.setAttribute( 'autocomplete', 'off' );

        this.value = 0;

        this.min = - Infinity;
        this.max = Infinity;

        this.precision = 2;
        this.step = 1;
        this.unit = '';
        this.nudge = 0.01;

        this.setValue( number );

        const scope = this;

        const changeEvent = new Event( 'change', { bubbles: true, cancelable: true } );

        let distance = 0;
        let onMouseDownValue = 0;

        const pointer = { x: 0, y: 0 };
        const prevPointer = { x: 0, y: 0 };

        function onMouseDown( event ) {

            if ( document.activeElement === scope.dom ) return;

            event.preventDefault();

            distance = 0;

            onMouseDownValue = scope.value;

            prevPointer.x = event.clientX;
            prevPointer.y = event.clientY;

            document.addEventListener( 'mousemove', onMouseMove );
            document.addEventListener( 'mouseup', onMouseUp );

        }

        function onMouseMove( event ) {

            const currentValue = scope.value;

            pointer.x = event.clientX;
            pointer.y = event.clientY;

            distance += ( pointer.x - prevPointer.x ) - ( pointer.y - prevPointer.y );

            let value = onMouseDownValue + ( distance / ( event.shiftKey ? 5 : 50 ) ) * scope.step;
            value = Math.min( scope.max, Math.max( scope.min, value ) );

            if ( currentValue !== value ) {

                scope.setValue( value );
                scope.dom.dispatchEvent( changeEvent );

            }

            prevPointer.x = event.clientX;
            prevPointer.y = event.clientY;

        }

        function onMouseUp() {

            document.removeEventListener( 'mousemove', onMouseMove );
            document.removeEventListener( 'mouseup', onMouseUp );

            if ( Math.abs( distance ) < 2 ) {

                scope.dom.focus();
                scope.dom.select();

            }

        }

        function onTouchStart( event ) {

            if ( event.touches.length === 1 ) {

                distance = 0;

                onMouseDownValue = scope.value;

                prevPointer.x = event.touches[ 0 ].pageX;
                prevPointer.y = event.touches[ 0 ].pageY;

                document.addEventListener( 'touchmove', onTouchMove, { passive: false } );
                document.addEventListener( 'touchend', onTouchEnd );

            }

        }

        function onTouchMove( event ) {

            event.preventDefault();

            const currentValue = scope.value;

            pointer.x = event.touches[ 0 ].pageX;
            pointer.y = event.touches[ 0 ].pageY;

            distance += ( pointer.x - prevPointer.x ) - ( pointer.y - prevPointer.y );

            let value = onMouseDownValue + ( distance / ( event.shiftKey ? 5 : 50 ) ) * scope.step;
            value = Math.min( scope.max, Math.max( scope.min, value ) );

            if ( currentValue !== value ) {

                scope.setValue( value );
                scope.dom.dispatchEvent( changeEvent );

            }

            prevPointer.x = event.touches[ 0 ].pageX;
            prevPointer.y = event.touches[ 0 ].pageY;

        }

        function onTouchEnd( event ) {

            if ( event.touches.length === 0 ) {

                document.removeEventListener( 'touchmove', onTouchMove );
                document.removeEventListener( 'touchend', onTouchEnd );

            }

        }

        function onChange() {

            scope.setValue( scope.dom.value );

        }

        function onFocus() {

            scope.dom.style.backgroundColor = '';
            scope.dom.style.cursor = '';

        }

        function onBlur() {

            scope.dom.style.backgroundColor = 'transparent';
            scope.dom.style.cursor = 'ns-resize';

        }

        function onKeyDown( event ) {

            event.stopPropagation();

            switch ( event.code ) {

                case 'Enter':
                    scope.dom.blur();
                    break;

                case 'ArrowUp':
                    event.preventDefault();
                    scope.setValue( scope.getValue() + scope.nudge );
                    scope.dom.dispatchEvent( changeEvent );
                    break;

                case 'ArrowDown':
                    event.preventDefault();
                    scope.setValue( scope.getValue() - scope.nudge );
                    scope.dom.dispatchEvent( changeEvent );
                    break;

            }

        }

        onBlur();

        this.dom.addEventListener( 'keydown', onKeyDown );
        this.dom.addEventListener( 'mousedown', onMouseDown );
        this.dom.addEventListener( 'touchstart', onTouchStart, { passive: false } );
        this.dom.addEventListener( 'change', onChange );
        this.dom.addEventListener( 'focus', onFocus );
        this.dom.addEventListener( 'blur', onBlur );

    }

    getValue() {

        return this.value;

    }

    setValue( value ) {

        if ( value !== undefined ) {

            value = parseFloat( value );

            if ( value < this.min ) value = this.min;
            if ( value > this.max ) value = this.max;

            this.value = value;
            this.dom.value = value.toFixed( this.precision );

            if ( this.unit !== '' ) this.dom.value += ' ' + this.unit;

        }

        return this;

    }

    setPrecision( precision ) {

        this.precision = precision;

        return this;

    }

    setStep( step ) {

        this.step = step;

        return this;

    }

    setNudge( nudge ) {

        this.nudge = nudge;

        return this;

    }

    setRange( min, max ) {

        this.min = min;
        this.max = max;

        return this;

    }

    setUnit( unit ) {

        this.unit = unit;

        this.setValue( this.value );

        return this;

    }

}

Methods

getValue(): number
Code
getValue() {

        return this.value;

    }
setValue(value: any): this
Code
setValue( value ) {

        if ( value !== undefined ) {

            value = parseFloat( value );

            if ( value < this.min ) value = this.min;
            if ( value > this.max ) value = this.max;

            this.value = value;
            this.dom.value = value.toFixed( this.precision );

            if ( this.unit !== '' ) this.dom.value += ' ' + this.unit;

        }

        return this;

    }
setPrecision(precision: any): this
Code
setPrecision( precision ) {

        this.precision = precision;

        return this;

    }
setStep(step: any): this
Code
setStep( step ) {

        this.step = step;

        return this;

    }
setNudge(nudge: any): this
Code
setNudge( nudge ) {

        this.nudge = nudge;

        return this;

    }
setRange(min: any, max: any): this
Code
setRange( min, max ) {

        this.min = min;
        this.max = max;

        return this;

    }
setUnit(unit: any): this
Code
setUnit( unit ) {

        this.unit = unit;

        this.setValue( this.value );

        return this;

    }

UIInteger

Class Code
class UIInteger extends UIElement {

    constructor( number ) {

        super( document.createElement( 'input' ) );

        this.dom.style.cursor = 'ns-resize';
        this.dom.className = 'Number';
        this.dom.value = '0';

        this.dom.setAttribute( 'autocomplete', 'off' );

        this.value = 0;

        this.min = - Infinity;
        this.max = Infinity;

        this.step = 1;
        this.nudge = 1;

        this.setValue( number );

        const scope = this;

        const changeEvent = new Event( 'change', { bubbles: true, cancelable: true } );

        let distance = 0;
        let onMouseDownValue = 0;

        const pointer = { x: 0, y: 0 };
        const prevPointer = { x: 0, y: 0 };

        function onMouseDown( event ) {

            if ( document.activeElement === scope.dom ) return;

            event.preventDefault();

            distance = 0;

            onMouseDownValue = scope.value;

            prevPointer.x = event.clientX;
            prevPointer.y = event.clientY;

            document.addEventListener( 'mousemove', onMouseMove );
            document.addEventListener( 'mouseup', onMouseUp );

        }

        function onMouseMove( event ) {

            const currentValue = scope.value;

            pointer.x = event.clientX;
            pointer.y = event.clientY;

            distance += ( pointer.x - prevPointer.x ) - ( pointer.y - prevPointer.y );

            let value = onMouseDownValue + ( distance / ( event.shiftKey ? 5 : 50 ) ) * scope.step;
            value = Math.min( scope.max, Math.max( scope.min, value ) ) | 0;

            if ( currentValue !== value ) {

                scope.setValue( value );
                scope.dom.dispatchEvent( changeEvent );

            }

            prevPointer.x = event.clientX;
            prevPointer.y = event.clientY;

        }

        function onMouseUp() {

            document.removeEventListener( 'mousemove', onMouseMove );
            document.removeEventListener( 'mouseup', onMouseUp );

            if ( Math.abs( distance ) < 2 ) {

                scope.dom.focus();
                scope.dom.select();

            }

        }

        function onChange() {

            scope.setValue( scope.dom.value );

        }

        function onFocus() {

            scope.dom.style.backgroundColor = '';
            scope.dom.style.cursor = '';

        }

        function onBlur() {

            scope.dom.style.backgroundColor = 'transparent';
            scope.dom.style.cursor = 'ns-resize';

        }

        function onKeyDown( event ) {

            event.stopPropagation();

            switch ( event.code ) {

                case 'Enter':
                    scope.dom.blur();
                    break;

                case 'ArrowUp':
                    event.preventDefault();
                    scope.setValue( scope.getValue() + scope.nudge );
                    scope.dom.dispatchEvent( changeEvent );
                    break;

                case 'ArrowDown':
                    event.preventDefault();
                    scope.setValue( scope.getValue() - scope.nudge );
                    scope.dom.dispatchEvent( changeEvent );
                    break;

            }

        }

        onBlur();

        this.dom.addEventListener( 'keydown', onKeyDown );
        this.dom.addEventListener( 'mousedown', onMouseDown );
        this.dom.addEventListener( 'change', onChange );
        this.dom.addEventListener( 'focus', onFocus );
        this.dom.addEventListener( 'blur', onBlur );

    }

    getValue() {

        return this.value;

    }

    setValue( value ) {

        if ( value !== undefined ) {

            value = parseInt( value );

            this.value = value;
            this.dom.value = value;

        }

        return this;

    }

    setStep( step ) {

        this.step = parseInt( step );

        return this;

    }

    setNudge( nudge ) {

        this.nudge = nudge;

        return this;

    }

    setRange( min, max ) {

        this.min = min;
        this.max = max;

        return this;

    }

}

Methods

getValue(): number
Code
getValue() {

        return this.value;

    }
setValue(value: any): this
Code
setValue( value ) {

        if ( value !== undefined ) {

            value = parseInt( value );

            this.value = value;
            this.dom.value = value;

        }

        return this;

    }
setStep(step: any): this
Code
setStep( step ) {

        this.step = parseInt( step );

        return this;

    }
setNudge(nudge: any): this
Code
setNudge( nudge ) {

        this.nudge = nudge;

        return this;

    }
setRange(min: any, max: any): this
Code
setRange( min, max ) {

        this.min = min;
        this.max = max;

        return this;

    }

UIBreak

Class Code
class UIBreak extends UIElement {

    constructor() {

        super( document.createElement( 'br' ) );

        this.dom.className = 'Break';

    }

}

UIHorizontalRule

Class Code
class UIHorizontalRule extends UIElement {

    constructor() {

        super( document.createElement( 'hr' ) );

        this.dom.className = 'HorizontalRule';

    }

}

UIButton

Class Code
class UIButton extends UIElement {

    constructor( value ) {

        super( document.createElement( 'button' ) );

        this.dom.className = 'Button';
        this.dom.textContent = value;

    }

}

UIProgress

Class Code
class UIProgress extends UIElement {

    constructor( value ) {

        super( document.createElement( 'progress' ) );

        this.dom.value = value;

    }

    setValue( value ) {

        this.dom.value = value;

    }

}

Methods

setValue(value: any): void
Code
setValue( value ) {

        this.dom.value = value;

    }

UITabbedPanel

Class Code
class UITabbedPanel extends UIDiv {

    constructor() {

        super();

        this.dom.className = 'TabbedPanel';

        this.tabs = [];
        this.panels = [];

        this.tabsDiv = new UIDiv();
        this.tabsDiv.setClass( 'Tabs' );

        this.panelsDiv = new UIDiv();
        this.panelsDiv.setClass( 'Panels' );

        this.add( this.tabsDiv );
        this.add( this.panelsDiv );

        this.selected = '';

    }

    select( id ) {

        let tab;
        let panel;
        const scope = this;

        // Deselect current selection
        if ( this.selected && this.selected.length ) {

            tab = this.tabs.find( function ( item ) {

                return item.dom.id === scope.selected;

            } );
            panel = this.panels.find( function ( item ) {

                return item.dom.id === scope.selected;

            } );

            if ( tab ) {

                tab.removeClass( 'selected' );

            }

            if ( panel ) {

                panel.setDisplay( 'none' );

            }

        }

        tab = this.tabs.find( function ( item ) {

            return item.dom.id === id;

        } );
        panel = this.panels.find( function ( item ) {

            return item.dom.id === id;

        } );

        if ( tab ) {

            tab.addClass( 'selected' );

        }

        if ( panel ) {

            panel.setDisplay( '' );

        }

        this.selected = id;

        // Scrolls to tab
        if ( tab ) {

            const tabOffsetRight = tab.dom.offsetLeft + tab.dom.offsetWidth;
            const containerWidth = this.tabsDiv.dom.getBoundingClientRect().width;

            if ( tabOffsetRight > containerWidth ) {

                this.tabsDiv.dom.scrollTo( { left: tabOffsetRight - containerWidth, behavior: 'smooth' } );

            }

            if ( tab.dom.offsetLeft < this.tabsDiv.dom.scrollLeft ) {

                this.tabsDiv.dom.scrollTo( { left: 0, behavior: 'smooth' } );

            }

        }

        return this;

    }

    addTab( id, label, items ) {

        const tab = new UITab( label, this );
        tab.setId( id );
        this.tabs.push( tab );
        this.tabsDiv.add( tab );

        const panel = new UIDiv();
        panel.setId( id );
        panel.add( items );
        panel.setDisplay( 'none' );
        this.panels.push( panel );
        this.panelsDiv.add( panel );

        this.select( id );

    }

}

Methods

select(id: any): this
Code
select( id ) {

        let tab;
        let panel;
        const scope = this;

        // Deselect current selection
        if ( this.selected && this.selected.length ) {

            tab = this.tabs.find( function ( item ) {

                return item.dom.id === scope.selected;

            } );
            panel = this.panels.find( function ( item ) {

                return item.dom.id === scope.selected;

            } );

            if ( tab ) {

                tab.removeClass( 'selected' );

            }

            if ( panel ) {

                panel.setDisplay( 'none' );

            }

        }

        tab = this.tabs.find( function ( item ) {

            return item.dom.id === id;

        } );
        panel = this.panels.find( function ( item ) {

            return item.dom.id === id;

        } );

        if ( tab ) {

            tab.addClass( 'selected' );

        }

        if ( panel ) {

            panel.setDisplay( '' );

        }

        this.selected = id;

        // Scrolls to tab
        if ( tab ) {

            const tabOffsetRight = tab.dom.offsetLeft + tab.dom.offsetWidth;
            const containerWidth = this.tabsDiv.dom.getBoundingClientRect().width;

            if ( tabOffsetRight > containerWidth ) {

                this.tabsDiv.dom.scrollTo( { left: tabOffsetRight - containerWidth, behavior: 'smooth' } );

            }

            if ( tab.dom.offsetLeft < this.tabsDiv.dom.scrollLeft ) {

                this.tabsDiv.dom.scrollTo( { left: 0, behavior: 'smooth' } );

            }

        }

        return this;

    }
addTab(id: any, label: any, items: any): void
Code
addTab( id, label, items ) {

        const tab = new UITab( label, this );
        tab.setId( id );
        this.tabs.push( tab );
        this.tabsDiv.add( tab );

        const panel = new UIDiv();
        panel.setId( id );
        panel.add( items );
        panel.setDisplay( 'none' );
        this.panels.push( panel );
        this.panelsDiv.add( panel );

        this.select( id );

    }

UITab

Class Code
class UITab extends UIText {

    constructor( text, parent ) {

        super( text );

        this.dom.className = 'Tab';

        this.parent = parent;

        const scope = this;

        this.dom.addEventListener( 'click', function () {

            scope.parent.select( scope.dom.id );

        } );

    }

}

UIListbox

Class Code
class UIListbox extends UIDiv {

    constructor() {

        super();

        this.dom.className = 'Listbox';
        this.dom.tabIndex = 0;

        this.items = [];
        this.listitems = [];
        this.selectedIndex = 0;
        this.selectedValue = null;

    }

    setItems( items ) {

        if ( Array.isArray( items ) ) {

            this.items = items;

        }

        this.render();

    }

    render( ) {

        while ( this.listitems.length ) {

            const item = this.listitems[ 0 ];

            item.dom.remove();

            this.listitems.splice( 0, 1 );

        }

        for ( let i = 0; i < this.items.length; i ++ ) {

            const item = this.items[ i ];

            const listitem = new ListboxItem( this );
            listitem.setId( item.id || `Listbox-${i}` );
            listitem.setTextContent( item.name || item.type );
            this.add( listitem );

        }

    }

    add() {

        const items = Array.from( arguments );

        this.listitems = this.listitems.concat( items );

        UIElement.prototype.add.apply( this, items );

    }

    selectIndex( index ) {

        if ( index >= 0 && index < this.items.length ) {

            this.setValue( this.listitems[ index ].getId() );

        }

        this.selectedIndex = index;

    }

    getValue() {

        return this.selectedValue;

    }

    setValue( value ) {

        for ( let i = 0; i < this.listitems.length; i ++ ) {

            const element = this.listitems[ i ];

            if ( element.getId() === value ) {

                element.addClass( 'active' );

            } else {

                element.removeClass( 'active' );

            }

        }

        this.selectedValue = value;

        const changeEvent = new Event( 'change', { bubbles: true, cancelable: true } );
        this.dom.dispatchEvent( changeEvent );

    }

}

Methods

setItems(items: any): void
Code
setItems( items ) {

        if ( Array.isArray( items ) ) {

            this.items = items;

        }

        this.render();

    }
render(): void
Code
render( ) {

        while ( this.listitems.length ) {

            const item = this.listitems[ 0 ];

            item.dom.remove();

            this.listitems.splice( 0, 1 );

        }

        for ( let i = 0; i < this.items.length; i ++ ) {

            const item = this.items[ i ];

            const listitem = new ListboxItem( this );
            listitem.setId( item.id || `Listbox-${i}` );
            listitem.setTextContent( item.name || item.type );
            this.add( listitem );

        }

    }
add(): void
Code
add() {

        const items = Array.from( arguments );

        this.listitems = this.listitems.concat( items );

        UIElement.prototype.add.apply( this, items );

    }
selectIndex(index: any): void
Code
selectIndex( index ) {

        if ( index >= 0 && index < this.items.length ) {

            this.setValue( this.listitems[ index ].getId() );

        }

        this.selectedIndex = index;

    }
getValue(): any
Code
getValue() {

        return this.selectedValue;

    }
setValue(value: any): void
Code
setValue( value ) {

        for ( let i = 0; i < this.listitems.length; i ++ ) {

            const element = this.listitems[ i ];

            if ( element.getId() === value ) {

                element.addClass( 'active' );

            } else {

                element.removeClass( 'active' );

            }

        }

        this.selectedValue = value;

        const changeEvent = new Event( 'change', { bubbles: true, cancelable: true } );
        this.dom.dispatchEvent( changeEvent );

    }

ListboxItem

Class Code
class ListboxItem extends UIDiv {

    constructor( parent ) {

        super();

        this.dom.className = 'ListboxItem';

        this.parent = parent;

        const scope = this;

        function onClick() {

            if ( scope.parent ) {

                scope.parent.setValue( scope.getId( ) );

            }

        }

        this.dom.addEventListener( 'click', onClick );

    }

}