Current Path : /var/www/www-root/data/www.catalog.monolith-realty.ru/bitrix/js/ui/buttons/src/ |
Current File : /var/www/www-root/data/www.catalog.monolith-realty.ru/bitrix/js/ui/buttons/src/button-manager.js |
import { Type, Reflection, Dom, Runtime, Tag } from 'main.core'; import { EventEmitter, BaseEvent } from 'main.core.events'; import { MenuItem } from 'main.popup'; import BaseButton from './base-button'; import Button from './button/button'; import SplitButton from './split-button/split-button'; import ButtonTag from './button/button-tag'; import ButtonColor from './button/button-color'; import ButtonSize from './button/button-size'; import ButtonState from './button/button-state'; import ButtonStyle from './button/button-style'; import ButtonIcon from './button/button-icon'; import SplitButtonState from './split-button/split-button-state'; import type { SplitButtonOptions } from './split-button/split-button-options'; import type { ButtonOptions } from './button/button-options'; import type { SplitSubButtonOptions } from './split-button/split-sub-button-options'; import SplitSubButtonType from './split-button/split-sub-button-type'; export default class ButtonManager { /** * @public * @param {HTMLButtonElement | HTMLAnchorElement | HTMLInputElement} node * @return {Button | SplitButton} */ static createFromNode( node: HTMLButtonElement | HTMLAnchorElement | HTMLInputElement | HTMLDivElement ): Button | SplitButton { if (!Type.isDomNode(node)) { throw new Error('BX.UI.ButtonManager.createFromNode: "node" must be a DOM node.'); } if (!Dom.hasClass(node, Button.BASE_CLASS) && !Dom.hasClass(node, SplitButton.BASE_CLASS)) { throw new Error('BX.UI.ButtonManager.createFromNode: "node" is not a button.'); } const isSplitButton = Dom.hasClass(node, SplitButton.BASE_CLASS); let tag = null; let text = null; let textNode = null; let counterNode = null; let disabled = false; let mainButtonOptions: SplitSubButtonOptions = {}; let menuButtonOptions: SplitSubButtonOptions = {}; if (isSplitButton) { const mainButton = node.querySelector(`.${SplitSubButtonType.MAIN}`); const menuButton = node.querySelector(`.${SplitSubButtonType.MENU}`); if (!mainButton) { throw new Error('BX.UI.ButtonManager.createFromNode: a split button doesn\'t have a main button.'); } if (!menuButton) { throw new Error('BX.UI.ButtonManager.createFromNode: a split button doesn\'t have a menu button.'); } const mainButtonTag = this.#getTag(mainButton); if (mainButtonTag === ButtonTag.INPUT || mainButtonTag === ButtonTag.SUBMIT) { text = mainButton.value; } else { [textNode, counterNode] = this.#getTextNode(mainButton); text = textNode.textContent; } disabled = Dom.hasClass(node, SplitButtonState.DISABLED); mainButtonOptions = { tag: mainButtonTag, textNode, counterNode, buttonNode: mainButton, disabled: Dom.hasClass(node, SplitButtonState.MAIN_DISABLED) }; menuButtonOptions = { tag: this.#getTag(menuButton), buttonNode: menuButton, textNode: null, counterNode: null, disabled: Dom.hasClass(node, SplitButtonState.MENU_DISABLED) }; } else { tag = this.#getTag(node); if (tag === null) { throw new Error('BX.UI.ButtonManager.createFromNode: "node" must be a button, link or input.'); } disabled = Dom.hasClass(node, ButtonState.DISABLED); if (tag === ButtonTag.INPUT || tag === ButtonTag.SUBMIT) { text = node.value; } else { [textNode, counterNode] = this.#getTextNode(node); text = textNode.textContent; } } const options: ButtonOptions & SplitButtonOptions = { id: node.dataset.btnUniqid, buttonNode: node, textNode: isSplitButton ? null : textNode, counterNode: isSplitButton ? null : counterNode, counter: this.#getCounter(counterNode), tag, text, disabled, mainButton: mainButtonOptions, menuButton: menuButtonOptions, size: this.#getEnumProp(node, ButtonSize), color: this.#getEnumProp(node, ButtonColor), icon: this.#getEnumProp(node, ButtonIcon), state: this.#getEnumProp(node, isSplitButton ? SplitButtonState : ButtonState), noCaps: Dom.hasClass(node, ButtonStyle.NO_CAPS), round: Dom.hasClass(node, ButtonStyle.ROUND) }; const nodeOptions = Dom.attr(node, 'data-json-options') || {}; if (Dom.hasClass(node, ButtonStyle.DROPDOWN)) { options.dropdown = true; } else if (nodeOptions.dropdown === false) { options.dropdown = false; } if (nodeOptions.onclick) { options.onclick = this.#convertEventHandler(nodeOptions.onclick); } if (Type.isPlainObject(nodeOptions.events)) { options.events = nodeOptions.events; this.#convertEvents(options.events); } if (Type.isPlainObject(nodeOptions.menu)) { options.menu = nodeOptions.menu; this.#convertMenuEvents(options.menu.items); } ['mainButton', 'menuButton'].forEach(button => { if (!Type.isPlainObject(nodeOptions[button])) { return; } options[button] = Runtime.merge(options[button], nodeOptions[button]); if (options[button].onclick) { options[button].onclick = this.#convertEventHandler(options[button].onclick); } this.#convertEvents(options[button].events); }); if (Type.isStringFilled(nodeOptions.menuTarget)) { options.menuTarget = nodeOptions.menuTarget; } return isSplitButton ? new SplitButton(options) : new Button(options); } static createByUniqId(id): Button | SplitButton | null { if (!Type.isStringFilled(id)) { return null; } const node = document.querySelector(`[data-btn-uniqid="${id}"]`); return node ? this.createFromNode(node) : null; } /** * @private * @param {HTMLElement} node * @return {null|number} */ static #getTag(node: HTMLElement | HTMLInputElement): ButtonTag | null { if (node.nodeName === 'A') { return ButtonTag.LINK; } else if (node.nodeName === 'BUTTON') { return ButtonTag.BUTTON; } else if (node.nodeName === 'INPUT' && node.type === 'button') { return ButtonTag.INPUT; } else if (node.nodeName === 'INPUT' && node.type === 'submit') { return ButtonTag.SUBMIT; } return null; } /** * @private * @param {HTMLElement} node */ static #getTextNode(node: HTMLElement): [HTMLElement, HTMLElement] { let textNode = node.querySelector('.ui-btn-text'); const counterNode = node.querySelector('.ui-btn-counter'); if (!textNode) { if (counterNode) { Dom.remove(counterNode); } textNode = Tag.render`<span class="ui-btn-text">${node.innerHTML.trim()}</span>`; Dom.clean(node); Dom.append(textNode, node); if (counterNode) { Dom.append(counterNode, node); } } return [textNode, counterNode]; } /** * @private * @param counterNode * @return {null|any} */ static #getCounter(counterNode: HTMLElement): number | string | null { if (Type.isDomNode(counterNode)) { const textContent = counterNode.textContent; const counter = Number(textContent); return Type.isNumber(counter) ? counter : textContent; } return null; } /** * @private * @param {HTMLElement} node * @param {object} enumeration * @return {null|*} */ static #getEnumProp(node: HTMLElement, enumeration: Object) { for (let key in enumeration) { if (!enumeration.hasOwnProperty(key)) { continue; } if (Dom.hasClass(node, enumeration[key])) { return enumeration[key]; } } return null; } /** * @private * @param handler * @return {Function} */ static #convertEventHandler(handler): Function { if (Type.isFunction(handler)) { return handler; } if (!Type.isObject(handler)) { throw new Error('BX.UI.ButtonManager.createFromNode: Event handler must be described as object or function.'); } if (Type.isStringFilled(handler.code)) { return function() { // handle code can use callback arguments eval(handler.code); }; } else if (Type.isStringFilled(handler.event)) { return function(...args) { let event; if (args[0] instanceof BaseEvent) { event = args[0]; } else { if (args[0] instanceof BaseButton) { event = new BaseEvent({ data: { button: args[0], event: args[1] } }); } else if (args[1] instanceof MenuItem) { event = new BaseEvent({ data: { item: args[1], event: args[0] } }); } else { event = new BaseEvent({ data: args }); } } EventEmitter.emit(handler.event, event); }; } else if (Type.isStringFilled(handler.handler)) { return function(...args) { const fn = Reflection.getClass(handler.handler); if (Type.isFunction(fn)) { let context = this; if (Type.isStringFilled(handler.context)) { context = Reflection.getClass(handler.context); } return fn.apply(context, args); } else { console.warn( `BX.UI.ButtonManager.createFromNode: be aware, the handler ${handler.handler} is not a function.` ); } return null; }; } return null; } /** * @private * @param events */ static #convertEvents(events) { if (Type.isPlainObject(events)) { for (let [eventName, eventFn] of Object.entries(events)) { events[eventName] = this.#convertEventHandler(eventFn); } } } /** * @private * @param items */ static #convertMenuEvents(items) { if (!Type.isArray(items)) { return; } items.forEach(item => { if (item.onclick) { item.onclick = this.#convertEventHandler(item.onclick); } if (item.events) { this.#convertEvents(item.events); } if (Type.isArray(item.items)) { this.#convertMenuEvents(item.items); } }); } /** * @deprecated * @param uniqId * @return {null|*} */ static getByUniqid(uniqId) { const toolbar = BX.UI.ToolbarManager.getDefaultToolbar(); return toolbar ? toolbar.getButton(uniqId) : null; } }