/* *
 *
 *  (c) 2009-2026 Highsoft AS
 *
 *  Integration of this software requires a license.
 *  - For commercial use, see www.highcharts.com/license
 *  - For non-commercial, see www.highcharts.com/license-eula
 *
 *
 *  Authors:
 *  - Sebastian Bochan
 *  - Wojciech Chmiel
 *  - Gøran Slettemark
 *  - Sophie Bremer
 *
 * */
'use strict';
import EditGlobals from './EditGlobals.js';
import { createElement, defined, find, merge } from '../../Shared/Utilities.js';
/* *
 *
 *  Functions
 *
 * */
/**
 * Function to create a context button.
 * @internal
 *
 * @param parentNode
 * The element to which the new element should be appended.
 *
 * @param editMode
 * EditMode instance.
 *
 * @returns
 * Context button element.
 */
function renderContextButton(parentNode, editMode) {
    const contextMenuOptions = editMode.options.contextMenu;
    let contextButton;
    if (contextMenuOptions) {
        contextButton = createElement('button', {
            className: EditGlobals.classNames.contextMenuBtn,
            onclick: function (event) {
                event.stopPropagation();
                editMode.onContextBtnClick();
            }
        }, {}, parentNode);
        // Add the icon if defined.
        if (contextMenuOptions.icon) {
            createElement('img', {
                src: contextMenuOptions.icon,
                className: EditGlobals.classNames.icon
            }, {}, contextButton);
        }
        // Add text next to the icon if defined.
        if (contextMenuOptions.text) {
            createElement('span', {
                className: EditGlobals.classNames.contextMenuBtnText,
                textContent: contextMenuOptions.text
            }, {}, contextButton);
        }
        contextButton.setAttribute('aria-label', editMode.lang.accessibility.contextMenu.button);
        contextButton.setAttribute('aria-expanded', 'false');
    }
    return contextButton;
}
/**
 * Creates the collapsable header element.
 * @internal
 *
 * @param parentElement
 * The HTMLElement to which the element should be rendered to.
 *
 * @param options
 * Nested header options.
 *
 * @returns the outer element and content in the collapsable div.
 */
function renderCollapseHeader(parentElement, options) {
    const { name, showToggle, onchange, isEnabled, isNested, isStandalone, lang } = options;
    const accordion = createElement('div', {
        className: EditGlobals.classNames[(isNested ? 'accordionNestedWrapper' : 'accordionContainer')] + ' ' +
            (isStandalone ?
                EditGlobals.classNames.accordionStandaloneWrapper : '') + ' ' + EditGlobals.classNames.collapsableContentHeader
    }, {}, parentElement);
    const header = createElement('div', {
        className: EditGlobals.classNames.accordionHeader
    }, {}, accordion);
    let headerBtn;
    if (!isStandalone || showToggle) {
        headerBtn = createElement(isStandalone && showToggle ? 'span' : 'button', {
            className: EditGlobals.classNames[isStandalone ?
                'accordionHeaderWrapper' : 'accordionHeaderBtn']
        }, {}, header);
    }
    createElement('span', {
        textContent: lang[name] || name
    }, {}, headerBtn);
    if (showToggle && header) {
        renderToggle(header, {
            enabledOnOffLabels: true,
            id: name,
            name: '',
            onchange: onchange,
            value: isEnabled || false,
            lang
        });
    }
    if (!isStandalone) {
        const headerIcon = createElement('span', {
            className: EditGlobals.classNames.accordionHeaderIcon + ' ' +
                EditGlobals.classNames.collapsedElement
        }, {}, headerBtn);
        headerBtn?.addEventListener('click', function () {
            content.classList.toggle(EditGlobals.classNames.hiddenElement);
            headerIcon?.classList.toggle(EditGlobals.classNames.collapsedElement);
        });
    }
    const content = createElement('div', {
        className: EditGlobals.classNames.accordionContent + ' ' +
            (isStandalone ?
                EditGlobals.classNames.standaloneElement :
                EditGlobals.classNames.hiddenElement)
    }, {}, accordion);
    return { outerElement: accordion, content: content };
}
/**
 * Function to create select element.
 *
 * @param parentElement
 * The element to which the new element should be appended.
 *
 * @param options
 * Select form field options.
 *
 * @returns
 * Select element
 */
function renderSelect(parentElement, options) {
    if (!parentElement) {
        return;
    }
    if (options.name) {
        renderText(parentElement, { title: options.name, isLabel: true });
    }
    const iconsURLPrefix = options.iconsURLPrefix || '';
    const customSelect = createElement('div', {
        className: EditGlobals.classNames.dropdown +
            ' ' +
            EditGlobals.classNames.collapsableContentHeader
    }, {}, parentElement);
    const btn = createElement('button', {
        className: EditGlobals.classNames.dropdownButton
    }, {}, customSelect);
    const btnContent = createElement('div', {
        className: EditGlobals.classNames.dropdownButtonContent
    }, {}, btn);
    const iconURL = (find(options.selectOptions, (item) => item.name === options.value) || {}).iconURL;
    let headerIcon;
    if (iconURL) {
        headerIcon = createElement('img', {
            src: iconsURLPrefix + iconURL,
            className: EditGlobals.classNames.icon
        }, {}, btnContent);
    }
    const placeholder = createElement('span', {
        textContent: options.value,
        id: options.id || ''
    }, {}, btnContent);
    const dropdownPointer = createElement('img', {
        className: EditGlobals.classNames.dropdownIcon +
            ' ' +
            EditGlobals.classNames.collapsedElement,
        src: iconsURLPrefix + 'dropdown-pointer.svg'
    }, {}, btn);
    const dropdown = createElement('ul', {
        className: EditGlobals.classNames.dropdownContent +
            ' ' +
            EditGlobals.classNames.hiddenElement
    }, {}, customSelect);
    btn.addEventListener('click', function () {
        dropdown.classList.toggle(EditGlobals.classNames.hiddenElement);
        dropdownPointer.classList.toggle(EditGlobals.classNames.collapsedElement);
    });
    for (let i = 0, iEnd = options.selectOptions.length; i < iEnd; ++i) {
        renderSelectElement(merge(options.selectOptions[i] || {}, { iconsURLPrefix }), dropdown, placeholder, options.id, dropdownPointer, headerIcon, options.onchange);
    }
    return customSelect;
}
/**
 * @internal
 */
function renderSelectElement(option, dropdown, placeholder, id, dropdownPointer, headerIcon, callback) {
    const iconURL = option.iconsURLPrefix + option.iconURL;
    const selectOption = createElement('li', {}, {}, dropdown);
    const selectOptionBtn = createElement('button', { className: EditGlobals.classNames.customSelectButton }, {}, selectOption);
    if (option.iconURL) {
        createElement('img', {
            src: iconURL
        }, {}, selectOptionBtn);
    }
    createElement('span', { textContent: option.name || '' }, {}, selectOptionBtn);
    selectOptionBtn.addEventListener('click', function () {
        dropdown.classList.add(EditGlobals.classNames.hiddenElement);
        dropdownPointer.classList.toggle(EditGlobals.classNames.collapsedElement);
        placeholder.textContent = option.name || '';
        if (headerIcon && option.iconURL) {
            headerIcon.src = iconURL;
        }
        if (callback) {
            return callback(option.name);
        }
    });
}
/**
 * Function to create toggle element.
 *
 * @param parentElement
 * The element to which the new element should be appended.
 *
 * @param options
 * Form field options.
 *
 * @returns
 * Toggle element.
 */
function renderToggle(parentElement, options) {
    if (!parentElement) {
        return;
    }
    const lang = options.lang, value = options.value, title = options.title || options.name, langKey = options.langKey;
    if (options.isNested) {
        const labeledToggleWrapper = createElement('div', {
            className: EditGlobals.classNames.labeledToggleWrapper
        }, {}, parentElement);
        parentElement = labeledToggleWrapper;
    }
    const toggleContainer = createElement('button', {
        className: EditGlobals.classNames.toggleContainer,
        type: 'button',
        role: 'switch',
        ariaChecked: false,
        ariaLabel: langKey ? lang.accessibility[langKey][options.name] : ''
    }, {}, parentElement);
    if (title) {
        renderText(options.isNested ? parentElement : toggleContainer, { title });
    }
    if (options.enabledOnOffLabels) {
        renderText(toggleContainer, {
            title: lang.off,
            className: EditGlobals.classNames.toggleLabels
        });
    }
    const toggle = createElement('label', {
        className: EditGlobals.classNames.toggleWrapper +
            ' ' + (options.className || '')
    }, {}, toggleContainer);
    const input = renderCheckbox(toggle, value), callbackFn = options.onchange;
    callbackFn && toggleContainer.addEventListener('click', (e) => {
        callbackFn(!input.checked);
        input.checked = !input.checked;
        toggleContainer.setAttribute('aria-checked', input.checked);
        e.stopPropagation();
    });
    const slider = createElement('span', {
        className: EditGlobals.classNames.toggleSlider
    }, {}, toggle);
    callbackFn && slider.addEventListener('click', (e) => {
        e.preventDefault();
    });
    if (options.enabledOnOffLabels) {
        renderText(toggleContainer, {
            title: lang.on,
            className: EditGlobals.classNames.toggleLabels
        });
    }
    return toggleContainer;
}
/**
 * Function to create text element.
 *
 * @param parentElement
 * The element to which the new element should be appended
 *
 * @param options
 * Text options.
 *
 * @returns text Element
 */
function renderText(parentElement, options) {
    let textElem;
    const { title: text, className, isLabel } = options;
    if (parentElement) {
        const labelFor = isLabel ? { htmlFor: text } : {};
        textElem = createElement(isLabel ? 'label' : 'div', {
            className: EditGlobals.classNames.labelText + ' ' + (className || ''),
            textContent: text,
            ...labelFor
        }, {}, parentElement);
    }
    return textElem;
}
/**
 * Function to create Icon element.
 *
 * @param parentElement
 * The element to which the new element should be appended.
 *
 * @param options
 * Icon options.
 *
 * @returns
 * Icon Element
 */
function renderIcon(parentElement, options) {
    const { icon, callback } = options;
    if (!parentElement) {
        return;
    }
    const iconElem = createElement('div', {
        onclick: callback,
        className: options.className || ''
    }, {}, parentElement);
    iconElem.style['background-image'] = 'url(' + icon + ')';
    const mousedown = options.mousedown;
    const click = options.click;
    if (mousedown) {
        iconElem.onmousedown = function () {
            mousedown.apply(this, arguments);
        };
    }
    if (click) {
        iconElem.addEventListener('click', function () {
            click.apply(this, arguments);
        });
    }
    return iconElem;
}
/**
 * Function to create input element.
 *
 * @param parentElement
 * the element to which the new element should be appended
 *
 * @param options
 * Form field options
 *
 * @returns
 * Input Element
 */
function renderInput(parentElement, options) {
    if (!parentElement) {
        return;
    }
    if (options.name) {
        renderText(parentElement, { title: options.name, isLabel: true });
    }
    const input = createElement('input', {
        type: 'text',
        onclick: options.callback,
        id: options.id || '',
        name: options.name || '',
        value: ((defined(options.value) &&
            options.value.toString().replace(/\"/g, '')) || '')
    }, {}, parentElement);
    const onchange = options.onchange;
    if (onchange) {
        input.addEventListener('change', function (e) {
            onchange(e.target.value);
        });
    }
    return input;
}
/**
 * Function to create textarea element.
 *
 * @param parentElement
 * The element to which the new element should be appended
 *
 * @param options
 * Form field options
 *
 * @returns
 * textarea Element
 */
function renderTextarea(parentElement, options) {
    if (!parentElement) {
        return;
    }
    if (options.name) {
        renderText(parentElement, { title: options.name, isLabel: true });
    }
    const textarea = createElement('textarea', {
        id: options.id,
        name: options.name,
        value: options.value || ''
    }, {}, parentElement);
    const onchange = options.onchange;
    if (onchange) {
        textarea.addEventListener('change', function (e) {
            onchange(e.target.value);
        });
    }
    return textarea;
}
/**
 * Function to render the input of type checkbox.
 *
 * @param parentElement
 * An element to which render the checkbox to
 *
 * @param checked
 * Whether the checkbox should be checked or not.
 *
 * @returns
 * The checkbox element
 */
function renderCheckbox(parentElement, checked) {
    let input;
    if (parentElement) {
        input = createElement('input', {
            type: 'checkbox',
            checked: !!checked
        }, {}, parentElement);
    }
    return input;
}
/**
 * Function to create button element.
 *
 * @param parentElement
 * the element to which the new element should be appended
 *
 * @param options
 * Button field options
 *
 * @returns
 * Button Element
 */
function renderButton(parentElement, options) {
    if (!parentElement) {
        return;
    }
    const button = createElement('button', {
        className: (EditGlobals.classNames.button + ' ' +
            (options.className || '')),
        onclick: options.callback,
        textContent: options.text
    }, options.style || {}, parentElement);
    if (options.icon) {
        button.style['background-image'] =
            'url(' + options.icon + ')';
    }
    return button;
}
/**
 * Get the renderer function based on the type of the element to render.
 *
 * @param type
 * Type of the element to render
 *
 * @returns
 * function to render a specific element
 */
function getRendererFunction(type) {
    return {
        select: renderSelect,
        toggle: renderToggle,
        text: renderText,
        collapse: renderCollapseHeader,
        icon: renderIcon,
        contextButton: renderContextButton,
        input: renderInput,
        textarea: renderTextarea,
        checkbox: renderCheckbox,
        button: renderButton
    }[type];
}
const EditRenderer = {
    renderSelect,
    renderToggle,
    renderText,
    renderCollapseHeader,
    renderIcon,
    renderContextButton,
    renderInput,
    renderTextarea,
    renderCheckbox,
    renderButton,
    getRendererFunction
};
export default EditRenderer;
