/**
 * @since Last modified: 2024-08-09 19:57:01
 * @class Message
 * @description Delayed enter and leave events.
 * @param {getEl} elem - The target node.The writing is the same as getEl.
 * @param {Object} [options] - The parameters
 * @see {@link https://codepen.io/axui/pen/wvZBbqP|demo @ codepen}
 * @example
 * ax.Message({content:''});
 */
import pipe from "../utils/pipe";
import PubBase from "./PubBase";
import PubCommon from "./PubCommon";
import extend from "../utils/extend";
import renderTpl from "../utils/renderTpl";
import tplToEl from "../utils/tplToEl";
import createEl from "../utils/createEl";
import Hover from "./Hover";
import elState from "../utils/elState";
import slideDown from "../utils/slideDown";
import slideUp from "../utils/slideUp";
import ax from "../ax";
import classes from "../utils/classes";
import PubListen from "./PubListen";
import optMessage from "../options/optMessage";
import prefix from "../global/prefix";
class Message extends pipe(PubListen, PubCommon)(PubBase) {
    constructor(options, inital = true) {
        super();
        this.options = {};
        this.createParent = (placement) => {
            let parent = ax.messages.find((k) => k.placement === placement);
            if (!parent) {
                this.parentEl = createEl('div', { class: `${prefix}message`, placement });
                this.options.classes ? classes.add(this.parentEl, this.options.classes) : null;
                ax.messages.push({ placement, el: this.parentEl });
            }
            else {
                this.parentEl = parent.el;
            }
            this.options.zIndex ? this.parentEl.style.zIndex = this.options.zIndex : null;
        };
        this.ready({
            options,
            maps: optMessage,
        });
        /**
        * @name Message.targetEl
        * @description Target element.
        * @type getEl
        */
        /**
        * @name Message.rawOpts
        * @description Raw Parameters of the instance from map.
        * @type object
        */
        /**
         * @name Message.dftOpts
         * @description Default Parameters of the instance from merging.The reset method requires this variable.
         * @type object
         */
        /**
         * @name Message.options
         * @description Parameters of the instance.
         * @type object
         */
        /**
         * @name Message.destroyed
         * @description Destruction state.
         * @type boolean
         */
        /**
        * @name Message.plans
        * @description Plans for listening.
        * @type object
        */
        /**
        * @name Message.timestamp
        * @description Instance creation time.
        * @type number
        */
        /**
        * @name Message.parentEl
        * @description Parent node of message.
        * @type HTMLElement
        */
        this.parentEl = null;
        /**
        * @name Message.closeEl
        * @description Button node to close message.
        * @type HTMLElement
        */
        this.closeEl = null;
        /**
        * @name Message.contEl
        * @description Content node of message.
        * @type HTMLElement
        */
        this.contEl = null;
        /**
        * @name Message.progressEl
        * @description Progress node of message.
        * @type HTMLElement
        */
        this.progressEl = null;
        this.timeout = null;
        this.slideOut = () => {
            this.targetEl.setAttribute('progress', 'play');
            this.timeout = setTimeout(() => {
                this.hide();
            }, this.options.delay);
        };
        this.clearOut = () => {
            this.targetEl.setAttribute('progress', 'stop');
            this.timeout && clearTimeout(this.timeout);
        };
        super.listen({ name: 'ready' });
        inital ? this.init() : null;
    }
    /**
    * @description Initialization instance.
    * @param {fn_cb_none} [cb] - Callback execution after initialization.
    * @returns {*} Return the instance itself.
    */
    init(cb) {
        super.listen({ name: 'start' });
        this.createParent(this.options.placement || 'center-top');
        this.render();
        this.setAttrs();
        /**
        * @name Message.shown
        * @description Message display status.
        * @type boolean
        */
        this.shown = false;
        this.closeEl ? this.closeEl.onclick = () => this.hide() : null;
        this.contEl.querySelectorAll('[role="closebubble"]').forEach((k) => {
            k.onclick = () => this.hide();
        });
        if (!this.options.manual) {
            this.hoverIns = new Hover(this.targetEl, {
                onEnter: () => {
                    this.clearOut();
                },
                onLeave: () => {
                    this.slideOut();
                }
            });
        }
        elState(this.parentEl).isVirtual ? document.body.appendChild(this.parentEl) : null;
        super.listen({ name: 'init', cb });
        return this;
    }
    render() {
        let progressTpl = `{{ if(this.progress && !this.manual){ /}}<div role="progress" style="transition-duration:{{ this.delay }}ms;"></div>{{ } /}}`, closeTpl = `{{ if(this.manual){ /}}<i role="close" class="${prefix}icon-close"></i> {{ } /}}`, alltpl = ``;
        if (this.options.notable) {
            alltpl = `
            <section {{this.notable ? 'notable' : ''}} progress="stop">
            <div role="wrapper">
                {{ if(this.iconShow){ /}}<ax-result type="svg" to="{{this.status}}" role="icon"></ax-result>{{ } /}}
                    <div role="body">
                        <div role="heading">{{this.heading || this.lang.heading[this.status]}}</div>
                        <div role="content">{{ this.content || this.lang.content[this.status] }}</div>
                    </div>
                ${closeTpl}
                </div>
                ${progressTpl}
            </section>
            `;
        }
        else {
            alltpl = `
            <section progress="stop">
            <div role="wrapper">
                {{ if(this.iconShow){ /}}<ax-result type="font" to="{{this.status}}" role="icon" shape="fill"></ax-result>{{ } /}}
                    <div role="body">
                        <div role="content">{{ this.content || this.lang.content[this.status]}}</div>
                    </div>
                    ${closeTpl}
                </div>
                ${progressTpl}
            </section>
            `;
        }
        this.template = renderTpl(alltpl, this.options);
        this.targetEl = tplToEl(this.template);
        this.contEl = this.targetEl.querySelector('[role="content"]');
        this.closeEl = this.targetEl.querySelector('[role="close"]');
        this.progressEl = this.targetEl.querySelector('[role="progress"]');
    }
    setAttrs() {
        this.targetEl.setAttribute('status', this.options.status);
    }
    /**
    * @description Show message.
    * @param {fn_cb_none} [cb] - Callback execution after displaying message.
    * @returns {*} Return the instance itself
    */
    show(cb) {
        if (this.destroyed || this.shown) {
            return this;
        }
        this.parentEl.appendChild(this.targetEl);
        slideDown({
            el: this.targetEl,
            done: () => {
                this.shown = true;
                super.listen({ name: 'show', cb });
                if (!this.options.manual) {
                    this.targetEl.setAttribute('progress', 'play');
                    this.slideOut();
                }
            }
        });
        return this;
    }
    /**
    * @description Hide message.
    * @param {fn_cb_none} [cb] - Callback execution after hiding message.
    * @returns {*} Return the instance itself
    */
    hide(cb) {
        if (this.destroyed || !this.shown) {
            return this;
        }
        slideUp({
            el: this.targetEl,
            done: () => {
                this.targetEl.remove();
                this.shown = false;
                this.targetEl.setAttribute('progress', 'stop');
                super.listen({ name: 'hide', cb });
            }
        });
        return this;
    }
    updateCont(content, cb) {
        if (this.destroyed) {
            return this;
        }
        this.contEl.innerHTML = content;
        super.listen({ name: 'updateCont', cb, params: [content] });
        return this;
    }
    /**
    * @description Update settings.
    * @param {object} settings - The parameters to be updated.Format : settings={name:'',age:''}
    * @param {fn_cb_options} [cb] - Callback execution after update settings.
    * @returns {*} Return the instance itself
    */
    update(settings, cb) {
        if (this.destroyed) {
            return this;
        }
        this.targetEl.remove();
        this.options = extend({ target: this.options, source: settings });
        this.init();
        super.listen({ name: 'update', cb, params: [settings] });
        return this;
    }
    /**
     * @description Destroy instance and prohibit use.
     * @param {fn_cb_none} [cb] - Callback execution after destruction.
     * @returns {any} - Return the instance itself.
     */
    destroy(cb) {
        if (this.destroyed) {
            return this;
        }
        this.hide();
        this.closeEl ? this.closeEl.onclick = null : null;
        this.contEl.querySelectorAll('[role="closebubble"]').forEach((k) => {
            k.onclick = () => null;
        });
        this.hoverIns ? this.hoverIns.destroy() : null;
        this.targetEl.remove();
        this.destroyed = true;
        super.listen({ name: 'destroy', cb });
        return this;
    }
}
Message.hostType = 'none';
export default Message;
