UNPKG

@n8d/htwoo-core

Version:

hTWOo is a Fluent Design Framework purely in HTML and CSS

1,191 lines • 50.5 kB
define(['exports'], (function (exports) { 'use strict';

    var __classPrivateFieldSet = (undefined && undefined.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
        if (kind === "m") throw new TypeError("Private method is not writable");
        if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
        if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
        return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
    };
    var __classPrivateFieldGet = (undefined && undefined.__classPrivateFieldGet) || function (receiver, state, kind, f) {
        if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
        if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
        return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
    };
    var _HOODialog_launcher, _HOODialog_dialog, _HOODialog_dialogType, _HOODialog_closer, _HOODialog_options, _HOODialog_showDialog, _HOODialog_backdropClick, _HOODialog_keyboardClose, _HOODialog_closeDialog;
    /**
     * Dialog type options
     */
    exports.DialogType = void 0;
    (function (DialogType) {
        DialogType["DIALOG"] = "dialog";
        DialogType["MODAL"] = "modal";
    })(exports.DialogType || (exports.DialogType = {}));
    /**
     * HOODialog Class - Handles accessible dialog functionality
     */
    class HOODialog {
        /**
         * Constructor for HOODialog
         * @param launcher - CSS selector for the element that opens the dialog
         * @param dialog - CSS selector for the dialog element
         * @param dialogType - Type of dialog (DIALOG or MODAL)
         * @param options - Configuration options
         */
        constructor(launcher, dialog, dialogType = exports.DialogType.DIALOG, options = HOODialog.options) {
            // Private properties
            _HOODialog_launcher.set(this, void 0);
            _HOODialog_dialog.set(this, void 0);
            _HOODialog_dialogType.set(this, void 0);
            _HOODialog_closer.set(this, void 0);
            _HOODialog_options.set(this, {
                closer: null,
                backdropCloser: true,
                escCloser: true
            });
            /**
             * Open the dialog
             */
            _HOODialog_showDialog.set(this, () => {
                console.debug('-> Fired showDialog -- ', __classPrivateFieldGet(this, _HOODialog_dialog, "f"));
                if (__classPrivateFieldGet(this, _HOODialog_dialogType, "f") === exports.DialogType.DIALOG) {
                    __classPrivateFieldGet(this, _HOODialog_dialog, "f").show();
                }
                else if (__classPrivateFieldGet(this, _HOODialog_dialogType, "f") === exports.DialogType.MODAL) {
                    __classPrivateFieldGet(this, _HOODialog_dialog, "f").showModal();
                    // FIX: Make backdrop click optional
                    // Capture click on backdrop
                    __classPrivateFieldGet(this, _HOODialog_dialog, "f").addEventListener('click', __classPrivateFieldGet(this, _HOODialog_backdropClick, "f"));
                }
                else {
                    throw new Error(`Invalid dialog type specified for ${__classPrivateFieldGet(this, _HOODialog_dialog, "f")}`);
                }
                if (__classPrivateFieldGet(this, _HOODialog_closer, "f")) {
                    __classPrivateFieldGet(this, _HOODialog_closer, "f").addEventListener('click', __classPrivateFieldGet(this, _HOODialog_closeDialog, "f"));
                }
                const autofocus = __classPrivateFieldGet(this, _HOODialog_dialog, "f").querySelector('[autofocus]');
                if (autofocus) {
                    console.debug('No Autofocus');
                    __classPrivateFieldGet(this, _HOODialog_dialog, "f").focus();
                }
                // Capture close on ESC click
                __classPrivateFieldGet(this, _HOODialog_dialog, "f").addEventListener('keydown', __classPrivateFieldGet(this, _HOODialog_keyboardClose, "f"));
            }
            /**
             * Handle clicks on the dialog backdrop
             */
            );
            /**
             * Handle clicks on the dialog backdrop
             */
            _HOODialog_backdropClick.set(this, (event) => {
                const rect = __classPrivateFieldGet(this, _HOODialog_dialog, "f").getBoundingClientRect();
                const isInDialog = (rect.top <= event.clientY &&
                    event.clientY <= rect.top + rect.height &&
                    rect.left <= event.clientX &&
                    event.clientX <= rect.left + rect.width);
                if (!isInDialog && event.target === __classPrivateFieldGet(this, _HOODialog_dialog, "f")) {
                    __classPrivateFieldGet(this, _HOODialog_dialog, "f").close();
                }
            }
            /**
             * Close the dialog on keyCode ESC
             */
            );
            /**
             * Close the dialog on keyCode ESC
             */
            _HOODialog_keyboardClose.set(this, (event) => {
                if (event.keyCode === 27) {
                    __classPrivateFieldGet(this, _HOODialog_dialog, "f").close();
                }
            }
            /**
             * Close the dialog used for custom event or close button
             */
            );
            /**
             * Close the dialog used for custom event or close button
             */
            _HOODialog_closeDialog.set(this, (event) => {
                console.debug('closing dialog');
                __classPrivateFieldGet(this, _HOODialog_dialog, "f").close();
            }
            /**
             * Constructor for HOODialog
             * @param launcher - CSS selector for the element that opens the dialog
             * @param dialog - CSS selector for the dialog element
             * @param dialogType - Type of dialog (DIALOG or MODAL)
             * @param options - Configuration options
             */
            );
            console.debug("Register dialog", dialogType);
            // query DOM elements
            const launchElement = document.querySelector(launcher);
            const dialogElement = document.querySelector(dialog);
            if (!launchElement) {
                throw new Error(`Launcher '${launcher}' Element cannot be found`);
            }
            __classPrivateFieldSet(this, _HOODialog_launcher, launchElement, "f");
            if (!dialogElement) {
                throw new Error(`Dialog '${dialog}' Element not found`);
            }
            __classPrivateFieldSet(this, _HOODialog_dialog, dialogElement, "f");
            __classPrivateFieldGet(this, _HOODialog_launcher, "f").addEventListener('click', __classPrivateFieldGet(this, _HOODialog_showDialog, "f"));
            __classPrivateFieldSet(this, _HOODialog_dialogType, dialogType, "f");
            // Handle options with proper defaults
            __classPrivateFieldSet(this, _HOODialog_options, {
                closer: options.closer === undefined ? null : options.closer,
                backdropCloser: options.backdropCloser === undefined ? true : options.backdropCloser,
                escCloser: options.escCloser === undefined ? true : options.escCloser
            }, "f");
            // in case a close button is defined for the dialog
            if (options.closer !== null) {
                const closerElement = dialogElement.querySelector(options.closer);
                __classPrivateFieldGet(this, _HOODialog_options, "f").closer = options.closer;
                if (closerElement) {
                    __classPrivateFieldSet(this, _HOODialog_closer, closerElement, "f");
                }
            }
            console.debug('AFTER LAUNCH ::: ', __classPrivateFieldGet(this, _HOODialog_dialogType, "f"), __classPrivateFieldGet(this, _HOODialog_launcher, "f"), __classPrivateFieldGet(this, _HOODialog_dialog, "f"), __classPrivateFieldGet(this, _HOODialog_closer, "f"));
        }
    }
    _HOODialog_launcher = new WeakMap(), _HOODialog_dialog = new WeakMap(), _HOODialog_dialogType = new WeakMap(), _HOODialog_closer = new WeakMap(), _HOODialog_options = new WeakMap(), _HOODialog_showDialog = new WeakMap(), _HOODialog_backdropClick = new WeakMap(), _HOODialog_keyboardClose = new WeakMap(), _HOODialog_closeDialog = new WeakMap();
    /** Default options for modal dialog */
    HOODialog.options = {
        closer: null,
        backdropCloser: true,
        escCloser: true
    };
    /**
     * Enum of available dialog types
     */
    HOODialog.dialogType = exports.DialogType;

    // Improved from:
    // Origin 1: https://24ways.org/2019/making-a-better-custom-select-element/
    // Origin 2: https://css-tricks.com/making-a-better-custom-select-element/
    // Code Pen: https://codepen.io/chriscoyier/pen/yLyyZrr
    /**
     * Initialize an accessible custom select element
     * @param listItem - The custom select container element
     */
    const ariaSelect = (listItem) => {
        console.debug('ariaSelect', listItem);
        // Get elements within the select component
        const csSelector = listItem;
        console.log('csSelector', csSelector);
        const csInput = csSelector.querySelector('input');
        if (!csInput) {
            console.error('Input element not found within select component');
            return;
        }
        const csList = csSelector.querySelector('ul');
        if (!csList) {
            console.error('List element not found within select component');
            return;
        }
        const csOptions = csList.querySelectorAll('li.hoo-option');
        if (!csOptions || csOptions.length === 0) {
            console.warn('No options found within select component');
        }
        csSelector.querySelectorAll('svg');
        // Status element to announce to screen readers
        const csStatus = document.querySelector('#custom-select-status');
        if (!csStatus) {
            console.warn('Status element #custom-select-status not found');
        }
        // Convert NodeList to Array for easier manipulation
        const aOptions = Array.from(csOptions);
        // Create and set start point for the state tracker
        let csState = "initial";
        // Set up ARIA attributes for accessibility
        csSelector.setAttribute('role', 'combobox');
        csSelector.setAttribute('aria-haspopup', 'listbox');
        csSelector.setAttribute('aria-owns', 'custom-select-list'); // container owns the list...
        csInput.setAttribute('aria-autocomplete', 'both');
        csInput.setAttribute('aria-controls', 'custom-select-list'); // ...but the input controls it
        csList.setAttribute('role', 'listbox');
        csOptions.forEach((option) => {
            option.setAttribute('role', 'option');
            option.setAttribute('tabindex', "-1"); // make li elements keyboard focusable by script only
        });
        // Set up status message for screen readers
        if (csStatus) {
            csStatus.textContent = csOptions.length + " options available. Arrow down to browse or start typing to filter.";
        }
        // Initial state
        toggleList('Shut');
        /**
         * Toggle the visibility of the dropdown list
         * @param whichWay - 'Open' or 'Shut'
         */
        function toggleList(whichWay) {
            if (whichWay === 'Open') {
                csList.classList.remove('hidden-all');
                csSelector.setAttribute('aria-expanded', 'true');
            }
            else { // === 'Shut'
                csList.classList.add('hidden-all');
                csSelector.setAttribute('aria-expanded', 'false');
            }
        }
        /**
         * Find the currently focused element
         * @returns The element that currently has focus
         */
        function findFocus() {
            return document.activeElement;
        }
        /**
         * Move focus between elements
         * @param fromHere - Current element with focus
         * @param toThere - Direction to move focus ('input', 'forward', or 'back')
         */
        function moveFocus(fromHere, toThere) {
            // Get currently visible options
            const aCurrentOptions = aOptions.filter((option) => {
                return option.style.display === '';
            });
            // Don't move if no options are visible
            if (aCurrentOptions.length === 0) {
                return;
            }
            if (toThere === 'input') {
                csInput.focus();
                return;
            }
            // Handle focus movement based on starting point
            switch (fromHere) {
                case csInput:
                    if (toThere === 'forward') {
                        aCurrentOptions[0].focus();
                    }
                    else if (toThere === 'back') {
                        aCurrentOptions[aCurrentOptions.length - 1].focus();
                    }
                    break;
                case csOptions[0]:
                    if (toThere === 'forward') {
                        aCurrentOptions[1]?.focus();
                    }
                    else if (toThere === 'back') {
                        csInput.focus();
                    }
                    break;
                case csOptions[csOptions.length - 1]:
                    if (toThere === 'forward') {
                        aCurrentOptions[0].focus();
                    }
                    else if (toThere === 'back') {
                        aCurrentOptions[aCurrentOptions.length - 2]?.focus();
                    }
                    break;
                default: // middle list or filtered items 
                    const currentItem = findFocus();
                    const whichOne = aCurrentOptions.indexOf(currentItem);
                    if (toThere === 'forward') {
                        const nextOne = aCurrentOptions[whichOne + 1];
                        if (nextOne)
                            nextOne.focus();
                    }
                    else if (toThere === 'back' && whichOne > 0) {
                        const previousOne = aCurrentOptions[whichOne - 1];
                        if (previousOne)
                            previousOne.focus();
                    }
                    else { // if whichOne = 0
                        csInput.focus();
                    }
                    break;
            }
        }
        /**
         * Filter options based on input value
         */
        function doFilter() {
            const terms = csInput.value;
            const aFilteredOptions = aOptions.filter((option) => {
                return option.innerText.toUpperCase().startsWith(terms.toUpperCase());
            });
            console.debug(aFilteredOptions);
            // Hide all options first
            csOptions.forEach(option => option.style.display = "none");
            // Show only filtered options
            aFilteredOptions.forEach((option) => {
                option.style.display = "";
            });
            setState('filtered');
            updateStatus(aFilteredOptions.length);
        }
        /**
         * Update status message for screen readers
         * @param howMany - Number of options currently available
         */
        function updateStatus(howMany) {
            if (csStatus) {
                csStatus.textContent = howMany + " options available.";
            }
        }
        /**
         * Make a selection from the dropdown
         * @param whichOption - The selected option element
         */
        function makeChoice(whichOption) {
            const optionValue = whichOption.dataset.value;
            if (optionValue) {
                csInput.value = optionValue;
            }
            moveFocus(document.activeElement, 'input');
        }
        /**
         * Update the component state
         * @param newState - The new state to set
         */
        function setState(newState) {
            csState = newState;
        }
        /**
         * Handle keyboard navigation and actions
         * @param whichKey - The key that was pressed
         */
        function doKeyAction(whichKey) {
            const currentFocus = findFocus();
            switch (whichKey) {
                case 'Enter':
                    if (csState === 'initial') {
                        // if state = initial, toggleOpen and set state to opened
                        toggleList('Open');
                        setState('opened');
                    }
                    else if (csState === 'opened' && currentFocus.tagName === 'LI') {
                        // if state = opened and focus on list, makeChoice and set state to closed
                        makeChoice(currentFocus);
                        toggleList('Shut');
                        setState('closed');
                    }
                    else if (csState === 'opened' && currentFocus === csInput) {
                        // if state = opened and focus on input, close it
                        toggleList('Shut');
                        setState('closed');
                    }
                    else if (csState === 'filtered' && currentFocus.tagName === 'LI') {
                        // if state = filtered and focus on list, makeChoice and set state to closed
                        makeChoice(currentFocus);
                        toggleList('Shut');
                        setState('closed');
                    }
                    else if (csState === 'filtered' && currentFocus === csInput) {
                        // if state = filtered and focus on input, set state to opened
                        toggleList('Open');
                        setState('opened');
                    }
                    else { // i.e. csState is closed, or csState is opened/filtered but other focus point?
                        // if state = closed, set state to filtered? i.e. open but keep existing input? 
                        toggleList('Open');
                        setState('filtered');
                    }
                    break;
                case 'Escape':
                    // if state = opened or filtered, set state to initial
                    if (csState === 'opened' || csState === 'filtered') {
                        toggleList('Shut');
                        setState('initial');
                    }
                    break;
                case 'ArrowDown':
                    if (csState === 'initial' || csState === 'closed') {
                        // if state = initial or closed, set state to opened and moveFocus to first
                        toggleList('Open');
                        moveFocus(csInput, 'forward');
                        setState('opened');
                    }
                    else {
                        // if state is opened or filtered, move focus forward
                        toggleList('Open');
                        moveFocus(currentFocus, 'forward');
                    }
                    break;
                case 'ArrowUp':
                    if (csState === 'initial' || csState === 'closed') {
                        // if state = initial or closed, set state to opened and moveFocus to last
                        toggleList('Open');
                        moveFocus(csInput, 'back');
                        setState('opened');
                    }
                    else {
                        // if state is opened or filtered, move focus back
                        moveFocus(currentFocus, 'back');
                    }
                    break;
                default:
                    if (csState === 'initial') {
                        // if state = initial, toggle open, doFilter and set state to filtered
                        toggleList('Open');
                        doFilter();
                        setState('filtered');
                    }
                    else if (csState === 'opened') {
                        // if state = opened, doFilter and set state to filtered
                        doFilter();
                        setState('filtered');
                    }
                    else if (csState === 'closed') {
                        // if state = closed, doFilter and set state to filtered
                        doFilter();
                        setState('filtered');
                    }
                    else { // already filtered
                        doFilter();
                    }
                    break;
            }
        }
        // Event Listeners
        // Handle clicks on the custom select
        csSelector.addEventListener('click', function (e) {
            const currentFocus = findFocus();
            switch (csState) {
                case 'initial': // if state = initial, toggleOpen and set state to opened
                    toggleList('Open');
                    setState('opened');
                    break;
                case 'opened':
                    // if state = opened and focus on input, toggleShut and set state to initial
                    if (currentFocus === csInput) {
                        toggleList('Shut');
                        setState('initial');
                    }
                    else if (currentFocus.tagName === 'LI') {
                        // if state = opened and focus on list, makeChoice, toggleShut and set state to closed
                        makeChoice(currentFocus);
                        toggleList('Shut');
                        setState('closed');
                    }
                    break;
                case 'filtered':
                    // if state = filtered and focus on list, makeChoice and set state to closed
                    if (currentFocus.tagName === 'LI') {
                        makeChoice(currentFocus);
                        toggleList('Shut');
                        setState('closed');
                    } // if state = filtered and focus on input, do nothing (wait for next user input)
                    break;
                case 'closed': // if state = closed, toggleOpen and set state to filtered? or opened?
                    toggleList('Open');
                    setState('filtered');
                    break;
            }
        });
        // Handle keyboard events
        csSelector.addEventListener('keyup', function (e) {
            doKeyAction(e.key);
        });
        // Close the select when clicking outside
        document.addEventListener('click', function (e) {
            const target = e.target;
            if (!target.closest('.hoo-select')) {
                // click outside of the custom group
                toggleList('Shut');
                setState('initial');
            }
        });
    };
    /**
     * Update visibility of optgroups based on their children's visibility
     */
    function updateOptgroupVisibility() {
        const optgroups = document.querySelectorAll('.hoo-optgroup');
        optgroups.forEach(optgroup => {
            const options = optgroup.querySelectorAll('.hoo-option');
            const hasVisibleOption = Array.from(options).some(option => option.style.display !== 'none');
            // Hide or show the optgroup based on visibility of its options
            optgroup.style.display = hasVisibleOption ? '' : 'none';
        });
    }
    // Run initially to set visibility
    updateOptgroupVisibility();
    // Setup mutation observer to handle dynamic changes
    const selectDropdown = document.querySelector('.hoo-select-dropdown');
    if (selectDropdown) {
        const observer = new MutationObserver(() => {
            updateOptgroupVisibility();
        });
        observer.observe(selectDropdown, {
            childList: true,
            subtree: true,
            attributes: true,
            attributeFilter: ['style'] // Monitor only style changes
        });
    }

    /**
     * Position types for sticky tables
     */
    var Position;
    (function (Position) {
        Position["LEFT"] = "left";
        Position["RIGHT"] = "right";
        Position["TOP"] = "top";
        Position["BOTTOM"] = "bottom";
    })(Position || (Position = {}));
    /**
     * Handle collapse and expand actions for collapsible tables
     * @param event - Click event
     */
    const collapseAndExpand = (event) => {
        const target = event.target;
        const parentRow = target.closest("tr");
        if (!parentRow)
            return;
        const parentTable = target.closest("table");
        if (!parentTable)
            return;
        const section = parentRow.dataset.sectionheader;
        if (!section)
            return;
        let query;
        if (section === "all") {
            query = "tr[data-section]";
        }
        else {
            query = `tr[data-section=${section}]`;
        }
        const sectionRows = parentTable.querySelectorAll(query);
        for (let i = 0; i < sectionRows.length; i++) {
            const currentItem = sectionRows[i];
            if (currentItem.classList.contains('is-hidden')) {
                currentItem.classList.remove('is-hidden');
                currentItem.classList.add('is-visible');
                currentItem.setAttribute('aria-hidden', 'false');
                parentRow.setAttribute('aria-expanded', 'true');
            }
            else {
                currentItem.classList.add('is-hidden');
                currentItem.classList.remove('is-visible');
                currentItem.setAttribute('aria-hidden', 'true');
                parentRow.setAttribute('aria-expanded', 'false');
            }
        }
        if (section === "all") {
            const subSection = document.querySelectorAll("tbody tr.collapsable");
            if (parentRow.getAttribute('aria-expanded') === "true") {
                for (let i = 0; i < subSection.length; i++) {
                    const currentItem = subSection[i];
                    currentItem.setAttribute('aria-hidden', 'false');
                    currentItem.setAttribute('aria-expanded', 'true');
                }
            }
            else {
                for (let i = 0; i < subSection.length; i++) {
                    const currentItem = subSection[i];
                    currentItem.setAttribute('aria-hidden', 'true');
                    currentItem.setAttribute('aria-expanded', 'false');
                }
            }
        }
    };
    /**
     * Initialize collapsible functionality for tables
     */
    const initCollapsability = () => {
        const collapseTables = document.querySelectorAll('.hoo-table.is-collapsable');
        collapseTables.forEach(table => {
            const collapseRows = table.querySelectorAll('.collapsable');
            collapseRows.forEach(tableRow => {
                tableRow.addEventListener('click', collapseAndExpand);
            });
        });
    };
    /**
     * The position object for sticky positioning
     */
    ({
        "left": Position.LEFT,
        "right": Position.RIGHT,
        "top": Position.TOP,
        "bottom": Position.BOTTOM
    });
    /**
     * Fix the offset for sticky elements
     * @param parent - Parent element
     * @param selector - Selector for sticky elements
     * @param offset - Position offset type
     */
    const stickyOffsetFixup = (parent, selector, offset) => {
        const innerDefinition = parent.querySelectorAll(selector);
        for (let j = 0; j < innerDefinition.length; j++) {
            const innerElement = innerDefinition[j];
            if (offset === Position.LEFT) {
                innerElement.style[offset] = innerElement.offsetLeft + "px";
            }
            if (offset === Position.RIGHT) {
                innerElement.style[offset] = innerElement.offsetRight + "px";
            }
            if (offset === Position.TOP) {
                innerElement.style[offset] = innerElement.offsetTop + "px";
            }
            if (offset === Position.BOTTOM) {
                innerElement.style[offset] = innerElement.offsetBottom + "px";
            }
        }
    };
    /**
     * Initialize sticky table functionality
     */
    const initSticky = () => {
        const allStickyTables = document.querySelectorAll("table.sticky");
        for (let i = 0; i < allStickyTables.length; i++) {
            const stickyTable = allStickyTables[i];
            stickyOffsetFixup(stickyTable, "tr td.is-sticky.left", Position.LEFT);
            stickyOffsetFixup(stickyTable, "tr th.is-sticky.left", Position.LEFT);
        }
    };
    /**
     * Initialize all table functionality
     */
    const initTables = () => {
        initCollapsability();
        initSticky();
    };

    const elemPivotBar = '.hoo-pivotbar';
    const elemPivotButton = '.hoo-button-pivot';
    const dropDownPivotButton = '.hoo-navitem-text';
    const stateIsActive = 'is-active';
    /**
     * Handle pivot button click events
     * @param event - Click event
     */
    const changePivot = (event) => {
        event.preventDefault();
        const target = event.target;
        console.log(target);
        let currentButton;
        if (target.classList.contains(elemPivotButton.substr(1))) {
            currentButton = target;
        }
        else {
            currentButton = target.closest(elemPivotButton);
        }
        if (!currentButton) {
            if (target.classList.contains(dropDownPivotButton.substr(1))) {
                currentButton = target;
            }
            else {
                currentButton = target.closest(dropDownPivotButton);
            }
        }
        if (!currentButton)
            return;
        const currentPivotBar = target.closest(elemPivotBar);
        if (!currentPivotBar)
            return;
        const allButtons = currentPivotBar.querySelectorAll(elemPivotButton);
        allButtons.forEach(item => {
            item.classList.remove(stateIsActive);
        });
        currentButton.classList.add(stateIsActive);
    };
    /**
     * Initialize pivot bar functionality
     */
    const initPivot = () => {
        // register event on regular buttons
        const pivotBarsButtons = document.querySelectorAll(`${elemPivotBar} ${elemPivotButton}`);
        pivotBarsButtons.forEach(pivotButton => {
            const button = pivotButton;
            button.dataset.ref = button.textContent?.trim() || '';
            button.addEventListener('click', changePivot);
        });
    };

    /**
     * Reset the current page indicator for all nav items
     * @param allNavItems - Collection of navigation items
     */
    const resetCurrentPage = (allNavItems) => {
        allNavItems.forEach((item) => {
            item.removeAttribute('aria-current');
        });
    };
    /**
     * Handle navigation item click events
     * @param event - Click event
     */
    const handleNavItemClick = (event) => {
        const target = event.target;
        let curClickItem = target;
        // Skip handling for button and icon clicks
        if (!curClickItem.classList.contains('hoo-buttonitem') && !curClickItem.classList.contains('hoo-icon')) {
            const curNavItem = curClickItem.closest('.hoo-navitem');
            if (curNavItem) {
                const curNav = curClickItem.closest('.hoo-nav');
                if (!curNav)
                    return;
                const allNavItems = curNav.querySelectorAll('* .hoo-navitem');
                resetCurrentPage(allNavItems);
                curNavItem.setAttribute('aria-current', 'true');
            }
        }
    };
    /**
     * Handle icon click events for expanding/collapsing submenus
     * @param event - Click event
     */
    const handleIconClick = (event) => {
        const target = event.target;
        const curIcon = target;
        const subMenu = curIcon.closest('.hoo-navitem');
        if (!subMenu)
            return;
        if (subMenu.getAttribute('aria-expanded') === 'true') {
            subMenu.setAttribute('aria-expanded', 'false');
        }
        else {
            subMenu.setAttribute('aria-expanded', 'true');
        }
    };
    /**
     * Initialize menu functionality
     */
    const initMenu = () => {
        const navMenus = document.querySelectorAll('.hoo-nav');
        navMenus.forEach(nav => {
            // Add click handlers to navigation items
            const navItems = nav.querySelectorAll('.hoo-navitem');
            navItems.forEach(navItem => {
                navItem.addEventListener('click', handleNavItemClick);
            });
            // Add click handlers to navigation icons
            const navIcons = nav.querySelectorAll('.hoo-buttonicon');
            navIcons.forEach(navIcon => {
                navIcon.addEventListener('click', handleIconClick);
            });
        });
    };

    /**
     * File upload handler class
     * Manages file uploads with drag and drop functionality
     */
    class FileUploadHandler {
        /**
         * Initialize the file upload handler
         * @param htmlElement - The file upload container element
         */
        constructor(htmlElement) {
            this._files = [];
            /**
             * Handle the dragover event
             */
            this.dragOver = (event) => {
                event.preventDefault();
                this._fileLabel.classList.add('drag-over'); // Visual feedback
            };
            /**
             * Handle the dragleave event
             */
            this.dragLeave = (event) => {
                event.preventDefault();
                this._fileLabel.classList.remove('drag-over');
            };
            /**
             * Handle the drop event
             */
            this.dragDrop = (event) => {
                event.preventDefault();
                this._fileLabel.classList.remove('drag-over');
                const files = event.dataTransfer?.files;
                if (files && files.length > 0) {
                    console.log('Files dropped:', files);
                    this.fileChangedEvent({
                        target: { files }
                    });
                }
            };
            /**
             * Handle file selection changes
             */
            this.fileChangedEvent = (event) => {
                let files;
                console.debug('FileUploadHandler.fileChanged', event);
                const target = event.target;
                if (!target.files || target.files.length === 0) {
                    this._infileOutput.innerHTML = "";
                    return;
                }
                files = Array.from(target.files);
                const fileOutput = document.createElement('ul');
                fileOutput.classList.add('hoo-infile-list');
                console.debug('FileUploadHandler', files);
                files.forEach(file => {
                    const fileItem = document.createElement('li');
                    fileItem.innerText = file.name;
                    fileOutput.appendChild(fileItem);
                });
                this._infileOutput.innerHTML = `<div class='hoo-infile-selection'>${this._infileOutput.title || 'Files Selected'}</div>${fileOutput.outerHTML}`;
            };
            this._fileControl = htmlElement;
            console.debug('FileUploadHandler', this._fileControl);
            this._fileLabel = htmlElement.querySelector('.hoo-infile-label');
            if (!this._fileLabel) {
                throw new Error('File label element not found (.hoo-infile-label)');
            }
            this._fileLabel.addEventListener('dragover', this.dragOver);
            this._fileLabel.addEventListener('dragleave', this.dragLeave);
            this._fileLabel.addEventListener('drop', this.dragDrop);
            this._originalFileControl = this._fileControl.querySelector('.hoo-infile-context');
            if (!this._originalFileControl) {
                throw new Error('File input element not found (.hoo-infile-context)');
            }
            console.debug('FileUploadHandler', this._originalFileControl, this._originalFileControl.getAttribute('aria-describedby'));
            this._originalFileControl.addEventListener('change', this.fileChangedEvent);
            const describedBy = this._originalFileControl.getAttribute('aria-describedby');
            if (!describedBy) {
                throw new Error('File input is missing aria-describedby attribute');
            }
            console.debug('FileUploadHandler', describedBy, this._infileOutput, `#${describedBy.trim()}`);
            this._infileOutput = document.querySelector(`#${describedBy.trim()}`);
            if (!this._infileOutput) {
                throw new Error(`Element with id "${describedBy.trim()}" not found`);
            }
            console.debug('Infile Output:', this._infileOutput);
        }
    }

    /**
     * Array to store overflow items for each container
     */
    const overflowItems = [];
    /**
     * Default offset for overflow width calculations
     */
    const defaultOffset = 40;
    /**
     * Handle overflow items based on available container width
     * @param targetWidth - Width of the overflow container
     * @param children - Child elements inside the overflow container
     * @param curContainer - Current overflow container
     * @param itemIndex - Index of the current container
     */
    const getOverflowItems = (targetWidth, children, curContainer, itemIndex) => {
        // Filter items that should go into overflow
        let curOverFlowItems = overflowItems[itemIndex].filter(item => {
            return item.overallWidth > targetWidth - defaultOffset;
        });
        // Filter items that can fit in the container
        let curItems = overflowItems[itemIndex].filter(item => {
            return item.overallWidth < targetWidth - defaultOffset;
        });
        // Flyout Button ellipses
        const overflowControl = curContainer.querySelector('.hoo-buttonicon-overflow .hoo-buttonflyout');
        if (!overflowControl)
            return;
        // Add items to overflow menu
        if (overflowControl && overflowControl.children.length < curOverFlowItems.length) {
            for (let i = 0; i < curOverFlowItems.length; i++) {
                const itemRef = curOverFlowItems[i].ref;
                const element = curContainer.querySelector(`[data-ref="${itemRef}"]`);
                if (element) {
                    let listItem = document.createElement('li');
                    // Moves the Element into a new list item with all Events attached
                    listItem.appendChild(element);
                    // Append list item
                    overflowControl.appendChild(listItem);
                }
            }
        }
        // Toggle overflow button visibility
        if (overflowControl.children.length !== 0) {
            const buttonEnabled = overflowControl.closest('.hoo-buttonicon-overflow');
            if (buttonEnabled) {
                buttonEnabled.classList.add('is-active');
            }
        }
        else {
            const buttonEnabled = overflowControl.closest('.hoo-buttonicon-overflow');
            if (buttonEnabled) {
                buttonEnabled.classList.remove('is-active');
            }
        }
        // Move items back from overflow if there's room
        if (overflowControl && overflowControl.children.length > curOverFlowItems.length) {
            for (let i = 0; i < curItems.length; i++) {
                const itemRef = curItems[i].ref;
                const overflowElement = overflowControl.querySelector(`[data-ref="${itemRef}"]`);
                if (overflowElement) {
                    // Move elements back from overflow menu
                    curContainer.appendChild(overflowElement);
                }
            }
        }
        /**
         * Cleanup empty <li> elements
         */
        for (let i = 0; i < overflowControl.children.length; i++) {
            if (overflowControl.children[i].children.length === 0) {
                overflowControl.children[i].remove();
            }
        }
    };
    /**
     * Handle all entries in the overflow menu
     * @param entry - ResizeObserver entry
     * @param index - Index of the entry
     */
    const entryHandler = (entry, index) => {
        const parent = entry.target.parentElement;
        if (!parent)
            return;
        const childButtons = Array.from(parent.querySelectorAll('.hoo-overflow > *'));
        if (childButtons.length > 0) {
            getOverflowItems(parent.clientWidth, childButtons, entry.target, index);
        }
    };
    /**
     * Handle the overflow behavior for all '.hoo.overflow' containers
     * @param entries - ResizeObserver entries
     */
    const overflow = (entries) => {
        // Handle the overflow behaviour for all '.hoo.overflow' containers
        entries.forEach((item, index) => {
            if (item.target.children) {
                initOverflowElements(item.target.children, index);
                // Handle the resizing
                entryHandler(item, index);
            }
        });
    };
    /**
     * Initialize overflow elements data
     * @param children - Collection of child elements
     * @param index - Container index
     */
    const initOverflowElements = (children, index) => {
        let overallWidth = 0;
        if (overflowItems.length <= index) {
            overflowItems[index] = [];
            for (let i = 0; i < children.length; i++) {
                const child = children[i];
                overallWidth += child.clientWidth;
                if (!child.classList.contains("hoo-buttonicon-overflow")) {
                    const currentItem = {
                        chlld: child,
                        ref: "ref-" + index + "-" + i,
                        width: child.clientWidth,
                        overallWidth: overallWidth
                    };
                    child.dataset.ref = currentItem.ref;
                    overflowItems[index].push(currentItem);
                }
            }
        }
    };
    /**
     * Initialize overflow functionality by setting up ResizeObserver
     * for all overflow containers
     */
    const init = () => {
        const items = document.querySelectorAll('.hoo-overflow');
        if (items.length !== 0) {
            const ofObserver = new ResizeObserver(overflow);
            items.forEach(item => {
                ofObserver.observe(item);
            });
        }
    };

    var overflow$1 = /*#__PURE__*/Object.freeze({
        __proto__: null,
        init: init
    });

    /**
     * Register event handlers for button elements matching a selector
     * @param classSelector - CSS selector for the buttons
     * @param handleWith - Event handler function
     */
    const splitButtonReg = (classSelector, handleWith) => {
        const allSplitButtons = document.querySelectorAll(classSelector);
        allSplitButtons.forEach(element => {
            element.addEventListener('click', ((event) => {
                handleWith(event);
            }));
        });
    };
    /**
     * Handle button flyout toggle
     * @param curSplitButton - The button that was clicked
     * @param parentElement - Parent element of the button
     */
    const _btnFlyOut = (curSplitButton, parentElement) => {
        // Set aria values
        let ariaPressed = curSplitButton.getAttribute('aria-pressed');
        if (ariaPressed === null) {
            curSplitButton.setAttribute('aria-pressed', 'true');
        }
        else {
            curSplitButton.setAttribute('aria-pressed', ariaPressed === 'true' ? 'false' : 'true');
        }
        parentElement.classList.toggle('show-flyout');
    };
    /**
     * Handle button click events
     * @param event - Click event
     */
    const buttonClick = (event) => {
        const curSplitButton = event.target;
        const parentElement = curSplitButton.parentElement;
        _btnFlyOut(curSplitButton, parentElement);
    };
    /**
     * Handle split button click events
     * @param event - Click event
     */
    const splitButtonClick = (event) => {
        console.log("\nEVENT: splitButtonClick");
        const curSplitButton = event.target;
        const parentElement = curSplitButton.parentElement;
        _btnFlyOut(curSplitButton, parentElement);
    };
    /**
     * Handle delete and slide animation
     * @param event - Click event
     */
    const animateDeleteAndSlide = (event) => {
        const eventTarget = event.target;
        if (!eventTarget.parentElement?.dataset.animation)
            return;
        const animationClass = eventTarget.parentElement.dataset.animation;
        // Add class
        eventTarget.classList.add(animationClass);
        const computedStyles = window.getComputedStyle(eventTarget);
        const animationDuration = parseFloat(computedStyles.getPropertyValue('animation-duration')) * 1000;
        console.log(computedStyles, computedStyles.getPropertyValue('animation-duration'), animationDuration);
        setTimeout(() => {
            eventTarget.remove();
        }, animationDuration);
    };
    /**
     * Handle add and slide animation
     * @param event - Click event
     */
    const animateAddAndSlide = (event) => {
        const eventTarget = event.target;
        if (!eventTarget.parentElement?.dataset.animation)
            return;
        const animationClass = eventTarget.parentElement.dataset.animation;
        // Add class
        eventTarget.classList.add(animationClass);
        const newDomElement = document.importNode(eventTarget, true);
        newDomElement.classList.add(animationClass);
        const computedStyles = window.getComputedStyle(newDomElement);
        const animationDuration = parseFloat(computedStyles.getPropertyValue('animation-duration')) * 1000;
        eventTarget.parentElement.appendChild(newDomElement);
        newDomElement.addEventListener('click', animateAddAndSlide);
        console.log(computedStyles, computedStyles.getPropertyValue('animation-duration'), animationDuration);
        setTimeout(() => {
            newDomElement.classList.remove('anim-add-slide');
        }, animationDuration);
    };
    /**
     * Register animation handlers for elements
     * @param classname - CSS class selector
     * @param handleWith - Event handler function
     */
    const registerAnimation = (classname, handleWith) => {
        const animationBlocks = document.querySelectorAll(`${classname} > .sg-anim-block`);
        animationBlocks.forEach(element => {
            element.addEventListener('click', handleWith);
        });
    };
    /**
     * Register file input handlers
     * @param classname - CSS class selector for file inputs
     */
    const registerInputHandler = (classname) => {
        const allFileHandlers = document.querySelectorAll(classname);
        allFileHandlers.forEach(fileHandler => {
            new FileUploadHandler(fileHandler);
        });
    };
    /**
     * Register ARIA select elements
     */
    const registerAriaSelect = () => {
        const selects = document.querySelectorAll('.hoo-select');
        console.debug('ALL SELECTED', selects);
        if (selects) {
            selects.forEach((item) => {
                console.debug(item);
                ariaSelect(item);
            });
        }
    };
    /**
     * Register dialog elements
     */
    const registerDialog = () => {
        try {
            const dialog1 = new HOODialog('#btn-dialog', '#myDialog', exports.DialogType.DIALOG, {
                closer: '#closer-dlg',
                backdropCloser: true,
                escCloser: true
            });
            const dialog2 = new HOODialog('#btn-modal-dialog', '#myDialog-1', exports.DialogType.MODAL, {
                closer: '#closer-mdl',
                backdropCloser: true,
                escCloser: true
            });
        }
        catch (e) {
        }
    };
    /**
     * Main initialization function to run after DOM is loaded
     */
    const afterLoaded = () => {
        registerDialog();
        splitButtonReg('.hoo-buttonsplit > .hoo-buttonsplit-carret', splitButtonClick);
        splitButtonReg('.hoo-buttonsplit-primary > .hoo-buttonsplit-carret', splitButtonClick);
        splitButtonReg('button.hoo-buttonicon-overflow', buttonClick);
        splitButtonReg('button.hoo-buttoncmd', buttonClick);
        splitButtonReg('button.hoo-buttoncmdbar', buttonClick);
        splitButtonReg('button.hoo-buttonicon-flyout', buttonClick);
        splitButtonReg('button.hoo-buttoncontext', buttonClick);
        registerAnimation('.anim-deleteNslide', animateDeleteAndSlide);
        registerAnimation('.anim-addNslide', animateAddAndSlide);
        registerInputHandler('.hoo-input-file');
        registerAriaSelect();
        /** Init Table Helper */
        initTables();
        /** Init Pivot Bars */
        initPivot();
        /** Init Menu Bars */
        initMenu();
        init();
        setTimeout(() => {
            const tmpHidden = document.querySelectorAll('.tmp-hidden');
            console.log(tmpHidden);
            tmpHidden.forEach(item => {
                item.addEventListener("focus", ((event) => {
                    const target = event.target;
                    target.classList.remove('.tmp-hidden');
                    console.log(tmpHidden);
                }));
            });
        }, 1000);
    };
    // Initialize the application when the window loads
    window.onload = afterLoaded;

    exports.FileUploadHandler = FileUploadHandler;
    exports.HOODialog = HOODialog;
    exports.ariaSelect = ariaSelect;
    exports.initMenu = initMenu;
    exports.initPivot = initPivot;
    exports.initTables = initTables;
    exports.overflow = overflow$1;

}));