Your IP : 3.145.51.214


Current Path : /var/www/axolotl/data/www/axolotls.ru/www/bitrix/js/main/popup/src/menu/
Upload File :
Current File : /var/www/axolotl/data/www/axolotls.ru/www/bitrix/js/main/popup/src/menu/menu-item.js

import Menu from './menu';
import { Type, Text, Dom, Event, Tag } from 'main.core';
import { EventEmitter } from 'main.core.events';
import { MenuItemOptions } from './menu-types';

const aliases = {
	onSubMenuShow: { namespace: 'BX.Main.Menu.Item', eventName: 'SubMenu:onShow' },
	onSubMenuClose: { namespace: 'BX.Main.Menu.Item', eventName: 'SubMenu:onClose' }
};

EventEmitter.registerAliases(aliases);

export default class MenuItem extends EventEmitter
{
	constructor(options: MenuItemOptions)
	{
		super();
		this.setEventNamespace('BX.Main.Menu.Item');

		options = options || {};
		this.options = options;

		this.id = options.id || Text.getRandom();
		this.text = Type.isStringFilled(options.text) ? options.text : '';
		this.title = Type.isStringFilled(options.title) ? options.title : '';
		this.delimiter = options.delimiter === true;
		this.href = Type.isStringFilled(options.href) ? options.href : null;
		this.target = Type.isStringFilled(options.target) ? options.target : null;
		this.dataset = Type.isPlainObject(options.dataset) ? options.dataset : null;
		this.className = Type.isStringFilled(options.className) ? options.className : null;
		this.menuShowDelay = Type.isNumber(options.menuShowDelay) ? options.menuShowDelay : 300;
		this.subMenuOffsetX = Type.isNumber(options.subMenuOffsetX) ? options.subMenuOffsetX : 4;
		this._items = Type.isArray(options.items) ? options.items : [];
		this.disabled = options.disabled === true;
		this.cacheable = options.cacheable === true;

		/**
		 *
		 * @type {function|string}
		 */
		this.onclick =
			Type.isStringFilled(options.onclick) || Type.isFunction(options.onclick)
				? options.onclick
				: null
		;

		this.subscribeFromOptions(options.events, aliases);

		/**
		 *
		 * @type {Menu}
		 */
		this.menuWindow = null;

		/**
		 *
		 * @type {Menu}
		 */
		this.subMenuWindow = null;

		/**
		 *
		 * @type {{item: Element, text: Element}}
		 */
		this.layout = {
			item: null,
			text: null
		};

		this.getLayout(); //compatibility

		//compatibility
		//now use this.options
		this.events = {};
		this.items = [];
		for (let property in options)
		{
			if (options.hasOwnProperty(property) && typeof (this[property]) === 'undefined')
			{
				this[property] = options[property];
			}
		}
	}

	getLayout(): Element
	{
		if (this.layout.item)
		{
			return this.layout;
		}

		if (this.delimiter)
		{
			if (Type.isStringFilled(this.text))
			{
				this.layout.item = Dom.create('span', {
					props: {
						className: 'popup-window-delimiter-section'
					},
					children: [
						(this.layout.text = Dom.create('span', {
							props: {
								className: 'popup-window-delimiter-text'
							},
							html: this.text
						}))
					]
				});

			}
			else
			{
				this.layout.item = Tag.render`<span class="popup-window-delimiter">`;
			}
		}
		else
		{
			this.layout.item = Dom.create(this.href ? 'a' : 'span', {
				props: {
					className: [
						'menu-popup-item',
						(this.className ? this.className : 'menu-popup-no-icon'),
						(this.hasSubMenu() ? 'menu-popup-item-submenu' : '')
					].join(' ')
				},

				attrs: {
					title: this.title,
					onclick: Type.isString(this.onclick) ? this.onclick : '', // compatibility
					target: this.target ? this.target : ''
				},

				dataset: this.dataset,

				events:
					Type.isFunction(this.onclick)
						? { click: this.onItemClick.bind(this) }
						: null
				,

				children: [
					Dom.create('span', { props: { className: 'menu-popup-item-icon' } }),
					(this.layout.text = Dom.create('span', {
						props: {
							className: 'menu-popup-item-text'
						},
						html: this.text
					}))
				]
			});

			if (this.href)
			{
				this.layout.item.href = this.href;
			}

			if (this.isDisabled())
			{
				this.disable();
			}

			Event.bind(this.layout.item, 'mouseenter', this.onItemMouseEnter.bind(this));
			Event.bind(this.layout.item, 'mouseleave', this.onItemMouseLeave.bind(this));
		}

		return this.layout;
	}

	getContainer(): Element
	{
		return this.getLayout().item;
	}

	getTextContainer(): Element
	{
		return this.getLayout().text;
	}

	getText(): string
	{
		return this.text;
	}

	setText(text: string)
	{
		if (Type.isString(text))
		{
			this.text = text;
			this.getTextContainer().innerHTML = text;
		}
	}

	hasSubMenu(): boolean
	{
		return this.subMenuWindow !== null || this._items.length;
	}

	showSubMenu(): void
	{
		this.addSubMenu(this._items);

		if (this.subMenuWindow)
		{
			Dom.addClass(this.layout.item, 'menu-popup-item-open');

			this.closeSiblings();
			this.closeChildren();

			const popupWindow = this.subMenuWindow.getPopupWindow();
			if (!popupWindow.isShown())
			{
				this.emit('SubMenu:onShow');
				popupWindow.show();
			}

			this.adjustSubMenu();
		}
	}

	addSubMenu(items: []): Menu
	{
		if (this.subMenuWindow !== null || !Type.isArray(items) || !items.length)
		{
			return;
		}

		const rootMenuWindow = this.getMenuWindow().getRootMenuWindow() || this.getMenuWindow();
		const options = rootMenuWindow.params;

		//Override root menu options
		options.autoHide = false;
		options.menuShowDelay = this.menuShowDelay;
		options.cacheable = this.isCacheable();
		options.zIndexAbsolute = this.getMenuWindow().getPopupWindow().getZindex() + 2;
		options.bindOptions = {
			forceTop: true,
			forceLeft: true,
			forceBindPosition: true
		};

		delete options.zIndex;
		delete options.events;
		delete options.angle;
		delete options.overlay;

		this.subMenuWindow = new Menu('popup-submenu-' + this.id, this.layout.item, items, options);
		this.subMenuWindow.setParentMenuWindow(this.getMenuWindow());
		this.subMenuWindow.setParentMenuItem(this);

		this.subMenuWindow.getPopupWindow().subscribe('onDestroy', this.handleSubMenuDestroy.bind(this));
		Dom.addClass(this.layout.item, 'menu-popup-item-submenu');

		return this.subMenuWindow;
	}

	closeSubMenu(): void
	{
		this.clearSubMenuTimeout();

		if (this.subMenuWindow)
		{
			Dom.removeClass(this.layout.item, 'menu-popup-item-open');

			this.closeChildren();

			const popup = this.subMenuWindow.getPopupWindow();
			if (popup.isShown())
			{
				this.emit('SubMenu:onClose');
			}

			this.subMenuWindow.close();
		}
	}

	closeSiblings(): void
	{
		const siblings = this.menuWindow.getMenuItems();
		for (let i = 0; i < siblings.length; i++)
		{
			if (siblings[i] !== this)
			{
				siblings[i].closeSubMenu();
			}
		}
	}

	closeChildren(): void
	{
		if (this.subMenuWindow)
		{
			const children = this.subMenuWindow.getMenuItems();
			for (let i = 0; i < children.length; i++)
			{
				children[i].closeSubMenu();
			}
		}
	}

	destroySubMenu(): void
	{
		if (this.subMenuWindow)
		{
			Dom.removeClass(this.layout.item, 'menu-popup-item-open menu-popup-item-submenu');
			this.destroyChildren();
			this.subMenuWindow.destroy();

			this.subMenuWindow = null;
			this._items = [];
		}
	}

	destroyChildren(): void
	{
		if (this.subMenuWindow)
		{
			const children = this.subMenuWindow.getMenuItems();
			for (let i = 0; i < children.length; i++)
			{
				children[i].destroySubMenu();
			}
		}
	}

	adjustSubMenu(): void
	{
		if (!this.subMenuWindow || !this.layout.item)
		{
			return;
		}

		const popupWindow = this.subMenuWindow.getPopupWindow();
		const itemRect = this.layout.item.getBoundingClientRect();

		let offsetLeft = itemRect.width + this.subMenuOffsetX;
		let offsetTop = itemRect.height + this.getPopupPadding();
		let angleOffset = itemRect.height / 2 - this.getPopupPadding();
		let anglePosition = 'left';

		const popupWidth = popupWindow.getPopupContainer().offsetWidth;
		const popupHeight = popupWindow.getPopupContainer().offsetHeight;
		const popupBottom = itemRect.top + popupHeight;

		const clientWidth = document.documentElement.clientWidth;
		const clientHeight = document.documentElement.clientHeight;

		// let's try to fit a submenu to the browser viewport
		const exceeded = popupBottom - clientHeight;
		if (exceeded > 0)
		{
			let roundOffset = Math.ceil(exceeded / itemRect.height) * itemRect.height;
			if (roundOffset > itemRect.top)
			{
				// it cannot be higher than the browser viewport.
				roundOffset -= Math.ceil((roundOffset - itemRect.top) / itemRect.height) * itemRect.height;
			}

			if (itemRect.bottom > (popupBottom - roundOffset))
			{
				// let's sync bottom boundaries.
				roundOffset -= itemRect.bottom - (popupBottom - roundOffset) + this.getPopupPadding();
			}

			offsetTop += roundOffset;
			angleOffset += roundOffset;
		}

		if ((itemRect.left + offsetLeft + popupWidth) > clientWidth)
		{
			const left = itemRect.left - popupWidth - this.subMenuOffsetX;
			if (left > 0)
			{
				offsetLeft = -popupWidth - this.subMenuOffsetX;
				anglePosition = 'right';
			}
		}

		popupWindow.setBindElement(this.layout.item);
		popupWindow.setOffset({ offsetLeft: offsetLeft, offsetTop: -offsetTop });
		popupWindow.setAngle({ position: anglePosition, offset: angleOffset });
		popupWindow.adjustPosition();
	}

	getPopupPadding(): number
	{
		if (!Type.isNumber(this.popupPadding))
		{
			if (this.subMenuWindow)
			{
				const menuContainer = this.subMenuWindow.layout.menuContainer;
				this.popupPadding = parseInt(Dom.style(menuContainer, 'paddingTop'), 10);
			}
			else
			{
				this.popupPadding = 0;
			}
		}

		return this.popupPadding;
	}

	getSubMenu(): Menu | null
	{
		return this.subMenuWindow;
	}

	getId(): string
	{
		return this.id;
	}

	setMenuWindow(menu: Menu): string
	{
		this.menuWindow = menu;
	}

	getMenuWindow(): Menu | null
	{
		return this.menuWindow;
	}

	getMenuShowDelay(): number
	{
		return this.menuShowDelay;
	}

	enable(): void
	{
		this.disabled = false;
		this.getContainer().classList.remove('menu-popup-item-disabled');
	}

	disable(): void
	{
		this.disabled = true;
		this.closeSubMenu();
		this.getContainer().classList.add('menu-popup-item-disabled');
	}

	isDisabled(): boolean
	{
		return this.disabled;
	}

	setCacheable(cacheable): void
	{
		this.cacheable = cacheable !== false;
	}

	isCacheable(): boolean
	{
		return this.cacheable;
	}

	/**
	 * @private
	 */
	onItemClick(event): void
	{
		this.onclick.call(this.menuWindow, event, this); //compatibility
	}

	/**
	 * @private
	 */
	onItemMouseEnter(event): void
	{
		if (this.isDisabled())
		{
			return;
		}

		EventEmitter.emit(this, 'onMouseEnter', undefined, { thisArg: this });

		this.clearSubMenuTimeout();

		if (this.hasSubMenu())
		{
			this.subMenuTimeout = setTimeout(function() {
				this.showSubMenu();
			}.bind(this), this.menuShowDelay);
		}
		else
		{
			this.subMenuTimeout = setTimeout(function() {
				this.closeSiblings();
			}.bind(this), this.menuShowDelay);
		}
	}

	/**
	 * @private
	 */
	onItemMouseLeave(event): void
	{
		if (this.isDisabled())
		{
			return;
		}

		EventEmitter.emit(this, 'onMouseLeave', undefined, { thisArg: this });

		this.clearSubMenuTimeout();
	}

	/**
	 * @private
	 */
	clearSubMenuTimeout(): void
	{
		if (this.subMenuTimeout)
		{
			clearTimeout(this.subMenuTimeout);
		}

		this.subMenuTimeout = null;
	}

	/**
	 * @private
	 */
	handleSubMenuDestroy(): void
	{
		this.subMenuWindow = null;
	}
}