Source: control2/button/Button.js

(function() {
    /**
     * @namespace button
     * @memberof Help4.control2
     */
    Help4.control2.button = {};

    /**
     * @enum {string}
     */
    Help4.control2.button.APPEARANCES = {
        widget: 'widget',
        bubble: 'bubble',
        link: 'link',
        rect: 'rect',
        icon: 'icon',
        panel: 'panel',
        rate: 'rate',
        input: 'input',
        tile: 'tile',
        exposed: 'exposed',
        checkbox: 'checkbox',
        radio: 'radio',
        tab: 'tab',
        collapsible: 'collapsible',
        toggle: 'toggle',
        figure: 'figure'
    }

    /**
     * @enum {string}
     */
    Help4.control2.button.TEXT_POSITIONS = {
        top: 'top',
        bottom: 'bottom',
        start: 'start',
        end: 'end'
    }

    /**
     * @typedef {Help4.control2.Control.Params} Help4.control2.button.Button.Params
     * @property {?string} [text = null] - possible button text
     * @property {?string} [icon = null] - possible button icon
     * @property {?string} [image = null] - possible image on button
     * @property {?string} [svg = null] - possible svg on button
     * @property {Help4.control2.button.APPEARANCES} [appearance = 'link'] - button appearance
     * @property {Help4.control2.button.TEXT_POSITIONS} [textPosition = 'end'] - position of text
     * @property {boolean} [toggle = false] - wether the button can be toggled
     * @property {?string} ariaPressed - aria-pressed attribute
     */

    /**
     * A control to handle a button.
     * @augments Help4.control2.Control
     * @property {?string} text - possible button text
     * @property {?string} icon - possible button icon
     * @property {?string} image - possible image on button
     * @property {?string} svg - possible svg on button
     * @property {Help4.control2.button.APPEARANCES} appearance - button appearance
     * @property {Help4.control2.button.TEXT_POSITIONS} textPosition - position of text
     * @property {boolean} toggle - button can be toggled
     * @property {boolean} ariaPressed - DOM aria-pressed
     */
    Help4.control2.button.Button = class extends Help4.control2.Control {
        /**
         * @override
         * @param {Help4.control2.button.Button.Params} [params]
         * @param {Help4.jscore.ControlBase.Params} [derived]
         */
        constructor(params, derived) {
            const {APPEARANCES, TEXT_POSITIONS} = Help4.control2.button;

            params ||= {};
            if (params.svg) params.appearance = params.role = params.tag = APPEARANCES.figure;

            const appearance = params.appearance || derived?.params?.appearance?.init || APPEARANCES.link;

            const {ControlBase} = Help4.jscore;
            const T = ControlBase.TYPES;
            const TT = ControlBase.TEXT_TYPES;

            super(params, {
                params: {
                    tag:          {init: 'button'},
                    role:         {init: 'button'},

                    text:         {type: T.string_null},
                    icon:         {type: T.string_null},
                    image:        {type: T.string_null},
                    svg:          {type: T.string_null},

                    appearance:   {type: T.string, init: appearance, readonly: true},
                    textPosition: {type: T.string, init: TEXT_POSITIONS.end, readonly: true},

                    toggle:       {type: T.boolean, init: false, readonly: true},
                    ariaPressed:  {type: T.boolean, init: false, private: true},

                    autoEvent:    {init: true}
                },
                config: {
                    css: 'control-button ' + appearance
                },
                texts: {
                    text: TT.innerText
                },
                derived
            });
        }

        /**
         * @override
         * @param {Object} event - the received event
         */
        onEvent(event) {
            if (this.disabled) return;

            if (event.type === 'keydown') {
                // XRAY-1096: will otherwise open new tabs for buttons
                if (Help4.KeyMap[event.keyCode] === 'space') {
                    return Help4.Event.cancel(event);
                }
            }

            super.onEvent(event);
        }

        /**
         * @override
         * @param {Help4.jscore.ControlBase.PropertyChangeEvent} event - the change event
         */
        _applyPropertyToDom({name, value, oldValue}) {
            switch (name) {
                case 'active':
                    if (this.toggle) this._applyPropertyToDom({name: 'ariaPressed', value: value, oldValue: oldValue});

                    super._applyPropertyToDom({name, value, oldValue});
                    break;
                case 'text':
                    _setText.call(this, value);
                    break;
                case 'icon':
                    _setIcon.call(this, value);
                    break;
                case 'image':
                    _setImage.call(this, value, oldValue);
                    break;
                case 'svg':
                    _setSVG.call(this, value, oldValue);
                    break;
                case 'ariaPressed':
                    const o = {};
                    o[name] = value;
                    Help4.Element.setAttribute(this.getDom(), o);
                    break;
                default:
                    super._applyPropertyToDom({name, value, oldValue});
                    break;
            }
        }
    }

    /**
     * @memberof Help4.control2.button.Button#
     * @returns {string}
     * @private
     */
    function _getTextPosition() {
        const {TEXT_POSITIONS: TP} = Help4.control2.button;
        const {textPosition: tp} = this;
        return tp === TP.top || tp === TP.start ? TP.start : TP.end;
    }

    /**
     * @memberof Help4.control2.button.Button#
     * @param {string} text
     * @private
     */
    function _setText(text) {
        const dom = this.getDom('-inner');

        if (!text) return void dom?.parentNode.removeChild(dom);

        if (dom) {
            Help4.Element.setText(dom, text);
        } else {
            const {TEXT_POSITIONS: TP} = Help4.control2.button;
            const pos = _getTextPosition.call(this);
            const domParams = {id: '-inner', css: 'inner ' + this.textPosition, text: text};
            if (pos === TP.start) domParams.moveTo = 0;
            const span = this._createElement('span', domParams);
            this._setTextAttribute(span, 'text');
        }
    }

    /**
     * @memberof Help4.control2.button.Button#
     * @param {string} icon
     * @private
     */
    function _setIcon(icon) {
        const dom = this.getDom('-ico');

        if (!icon) return void dom?.parentNode.removeChild(dom);

        icon = Help4.control2.correctIcon(icon);
        icon = Help4.control2.ICONS[icon];

        if (dom) {
            Help4.Element.setText(dom, icon);
        } else {
            const {TEXT_POSITIONS: TP} = Help4.control2.button;
            const textDom = this.getDom('-inner');
            const domParams = {id: '-ico', css: 'icon', ariaHidden: true, role: 'presentation', text: icon};

            textDom && _getTextPosition.call(this) === TP.start
                ? (domParams.insertAfter = textDom)
                : (domParams.moveTo = 0);

            this._createElement('span', domParams);
        }
    }

    /**
     * @memberof Help4.control2.button.Button#
     * @param {string} image
     * @param {string} oldImage
     * @private
     */
    function _setImage(image, oldImage) {
        const dom = this.getDom('-image');

        if (!image) return void dom?.parentNode.removeChild(dom);

        if (dom) {
            Help4.Element.removeClass(dom, oldImage);
            Help4.Element.addClass(dom, image);
        } else {
            const {TEXT_POSITIONS: TP} = Help4.control2.button;
            const iconDom = this.getDom('-ico');
            const textDom = this.getDom('-inner');
            const domParams = {id: '-image', css: 'image ' + image, ariaHidden: true, role: 'presentation'};

            if (iconDom) {
                domParams.insertAfter = iconDom;
            } else if (textDom && _getTextPosition.call(this) === TP.start) {
                domParams.insertAfter = textDom;
            }

            this._createElement('span', domParams);
        }
    }

    /**
     * @memberof Help4.control2.button.Button#
     * @param {string} svg
     * @param {string} oldSvg
     * @private
     */
    function _setSVG(svg, oldSvg) {
        let dom = this.getDom('-svg');

        if (!svg) return void dom?.parentNode.removeChild(dom);

        const controller = Help4.getController();
        const svgUrl = controller.getResourceUrl() + Help4.FILES_PATH + svg + '.svg';

        if (dom) {
            Help4.Element.removeClass(dom, oldSvg);
            Help4.Element.addClass(dom, svg);
            dom.innerHTML = null;
            Help4.downloadSVG(dom, svgUrl);
        } else {
            const {TEXT_POSITIONS: TP} = Help4.control2.button;
            const imageDom = this.getDom('-ico');
            const iconDom = this.getDom('-ico');
            const textDom = this.getDom('-inner');
            const domParams = {id: '-svg', css: 'svg ' + svg};

            if (imageDom) {
                domParams.insertAfter = imageDom;
            } else if (iconDom) {
                domParams.insertAfter = iconDom;
            } else if (textDom && _getTextPosition.call(this) === TP.start) {
                domParams.insertAfter = textDom;
            }

            dom = this._createElement('div', domParams);
            Help4.downloadSVG(dom, svgUrl);
        }
    }
})();