403Webshell
Server IP : 80.87.202.40  /  Your IP : 216.73.216.169
Web Server : Apache
System : Linux rospirotorg.ru 5.14.0-539.el9.x86_64 #1 SMP PREEMPT_DYNAMIC Thu Dec 5 22:26:13 UTC 2024 x86_64
User : bitrix ( 600)
PHP Version : 8.2.27
Disable Function : NONE
MySQL : OFF |  cURL : ON |  WGET : ON |  Perl : ON |  Python : OFF |  Sudo : ON |  Pkexec : ON
Directory :  /home/bitrix/ext_www/rospirotorg.ru/bitrix/js/ui/system/menu/src/

Upload File :
current_dir [ Writeable] document_root [ Writeable]

 

Command :


[ Back ]     

Current File : /home/bitrix/ext_www/rospirotorg.ru/bitrix/js/ui/system/menu/src/menu-item.js
import { Dom, Event, Tag, Text, Type } from 'main.core';
import { Counter, CounterColor } from 'ui.cnt';
import { Outline } from 'ui.icon-set.api.core';
import 'ui.icon-set.outline';

import { Menu } from './menu';
import { mouse } from './mouse';
import { MenuItemDesign } from './const';
import type { MenuItemOptions, MenuItemCallbacks } from './types';

import './menu-item.css';

export class MenuItem
{
	#options: MenuItemOptions;
	#callbacks: MenuItemCallbacks;

	#subMenu: Menu;
	#element: HTMLElement;
	#showTimeout: number;
	#closeTimeout: number;
	#subMenuHovered: boolean;

	constructor(options: MenuItemOptions, callbacks: MenuItemCallbacks)
	{
		const defaultItemOptions: MenuItemOptions = {
			closeOnSubItemClick: true,
		};

		this.#options = { ...defaultItemOptions, ...options };
		this.#callbacks = callbacks;
	}

	getOptions(): MenuItemOptions
	{
		return this.#options;
	}

	getSubMenu(): Menu
	{
		return this.#subMenu;
	}

	render(): HTMLElement
	{
		if (this.#element)
		{
			return this.#element;
		}

		this.#element = Tag.render`
			<div class="ui-popup-menu-item --${this.#options.design ?? MenuItemDesign.Default}">
				${this.#renderHeader()}
				${this.#renderButtons()}
			</div>
		`;

		Event.bind(this.#element, 'click', this.#options.onClick);
		Event.bind(this.#element, 'mouseenter', this.#onMouseEnter);
		Event.bind(this.#element, 'mouseleave', this.#onMouseLeave);

		return this.#element;
	}

	showSubMenu = (): void => {
		this.#subMenuHovered = false;
		this.#subMenu ??= new Menu({
			...this.#options.subMenu,
			targetContainer: this.#callbacks.getTargetContainer(),
			autoHide: false,
			items: this.#options.subMenu.items.map((itemOptions: MenuItemOptions): MenuItemOptions => {
				if (!itemOptions)
				{
					return null;
				}

				return {
					...itemOptions,
					onClick: () => this.#onSubMenuItemClick(itemOptions),
				};
			}),
			offsetLeft: this.#element.offsetWidth,
			offsetTop: -this.#element.offsetHeight,
			bindOptions: {
				forceBindPosition: true,
				forceTop: true,
				forceLeft: true,
			},
			events: {
				onFirstShow: this.#onFirstShow,
				onShow: this.#onShow,
				onClose: this.#onClose,
				onDestroy: this.#onClose,
			},
		});

		this.#subMenu.show(this.#element);
	};

	adjustSubMenu = (): void => {
		if (!this.#subMenu)
		{
			return;
		}

		let offsetLeft = this.#element.offsetWidth;
		let offsetTop = -this.#element.offsetHeight;
		this.#subMenu.getPopup().setOffset({ offsetLeft, offsetTop });
		this.#subMenu.getPopup().adjustPosition();

		const targetContainer = this.#callbacks.getTargetContainer();
		const targetIsBody = targetContainer === document.body;
		const targetRect = {
			...targetContainer.getBoundingClientRect().toJSON(),
			...(targetIsBody ? { top: 0 } : null),
			...(targetIsBody ? { right: window.innerWidth } : null),
			...(targetIsBody ? { bottom: window.innerHeight } : null),
			...(targetIsBody ? { left: 0 } : null),
		};

		let popupRect = this.#subMenu.getPopupContainer().getBoundingClientRect();
		if (popupRect.right >= targetRect.right)
		{
			offsetLeft = -popupRect.width;
		}

		if (popupRect.bottom >= targetRect.bottom)
		{
			offsetTop = -popupRect.height;
		}

		this.#subMenu.getPopup().setOffset({ offsetLeft, offsetTop });
		this.#subMenu.getPopup().adjustPosition();

		popupRect = this.#subMenu.getPopupContainer().getBoundingClientRect();
		if (popupRect.left <= targetRect.left)
		{
			offsetLeft = this.#element.offsetWidth;
		}

		if (popupRect.top <= targetRect.top)
		{
			offsetTop = -this.#element.offsetHeight;
		}

		this.#subMenu.getPopup().setOffset({ offsetLeft, offsetTop });
		this.#subMenu.getPopup().adjustPosition();
	};

	closeSubMenu = (): void => {
		clearTimeout(this.#showTimeout);
		this.#subMenu?.close();
	};

	closeSubMenuWithTimeout(): void
	{
		clearTimeout(this.#closeTimeout);
		this.#closeTimeout = setTimeout(this.closeSubMenu, 200);
	}

	destroy(): void
	{
		this.#subMenu?.destroy();
	}

	#onMouseEnter = (): void => {
		if (this.#options.design === MenuItemDesign.Disabled)
		{
			return;
		}

		this.#subMenuHovered = false;
		this.#callbacks.onMouseEnter?.();
		if (this.#options.subMenu)
		{
			clearTimeout(this.#closeTimeout);
			this.#showTimeout = setTimeout(this.showSubMenu, 200);
		}
	};

	#onMouseLeave = (event: MouseEvent): void => {
		clearTimeout(this.#showTimeout);

		const subMenuContainer = this.#subMenu?.getPopupContainer();
		if (!this.#subMenuHovered && subMenuContainer && !subMenuContainer.contains(event.relatedTarget))
		{
			const distance = mouse.getPosition().left - subMenuContainer.getBoundingClientRect().left;
			const distanceDelta = Math.abs(distance) - Math.abs(distance + mouse.getDelta().left);
			if (distanceDelta <= 1)
			{
				this.closeSubMenu();
			}
			else
			{
				this.closeSubMenuWithTimeout();
			}
		}
	};

	#onSubMenuItemClick(item: MenuItemOptions): void
	{
		item.onClick?.();

		if (!item.subMenu && this.#options.closeOnSubItemClick)
		{
			this.#callbacks.onSubMenuItemClick?.();
		}
	}

	#onFirstShow = (): void => {
		Event.bind(this.#subMenu.getPopupContainer(), 'mouseenter', (): void => {
			clearTimeout(this.#closeTimeout);
			this.#subMenuHovered = true;
		});
	};

	#onShow = (): void => {
		this.adjustSubMenu();
		Dom.addClass(this.#element, '--hovered');
	};

	#onClose = (): void => {
		Dom.removeClass(this.#element, '--hovered');
	};

	#renderHeader(): HTMLElement
	{
		return Tag.render`
			<div class="ui-popup-menu-item-header">
				${this.#renderTitle()}
				${this.#renderSubtitle()}
			</div>
		`;
	}

	#renderTitle(): HTMLElement
	{
		return Tag.render`
			<div class="ui-popup-menu-item-title">
				${this.#renderLock()}
				<div class="ui-popup-menu-item-title-text">${Text.encode(this.#options.title)}</div>
				${this.#renderBadgeText()}
			</div>
		`;
	}

	#renderLock(): HTMLElement
	{
		if (!this.#options.isLocked)
		{
			return '';
		}

		return Tag.render`
			<div class="ui-popup-menu-item-lock">
				<div class="ui-icon-set --${Outline.LOCK_L}"></div>
			</div>
		`;
	}

	#renderBadgeText(): HTMLElement
	{
		if (!Type.isStringFilled(this.#options.badgeText?.title))
		{
			return '';
		}

		const color = this.#options.badgeText.color;
		const style = color ? `--badge-color: ${color};` : '';

		return Tag.render`
			<div class="ui-popup-menu-item-badge-text" style="${style}">
				${Text.encode(this.#options.badgeText.title)}
			</div>
		`;
	}

	#renderSubtitle(): HTMLElement
	{
		if (!Type.isStringFilled(this.#options.subtitle))
		{
			return '';
		}

		return Tag.render`
			<div class="ui-popup-menu-item-subtitle">${Text.encode(this.#options.subtitle)}</div>
		`;
	}

	#renderButtons(): HTMLElement
	{
		return Tag.render`
			<div class="ui-popup-menu-item-buttons">
				${this.#renderCheck()}
				${this.#renderExtra()}
				${this.#renderCounter()}
				${this.#renderIcon()}
				${this.#renderArrow()}
			</div>
		`;
	}

	#renderCheck(): HTMLElement
	{
		if (!Type.isBoolean(this.#options.isSelected))
		{
			return '';
		}

		if (!this.#options.isSelected)
		{
			return Tag.render`
				<div class="ui-popup-menu-item-check"></div>
			`;
		}

		return Tag.render`
			<div class="ui-popup-menu-item-check">
				<div class="ui-icon-set --${Outline.CHECK_L}"></div>
			</div>
		`;
	}

	#renderExtra(): HTMLElement
	{
		if (!this.#options.extraIcon)
		{
			return '';
		}

		const extra = Tag.render`
			<div class="ui-popup-menu-item-extra ${this.#options.extraIcon.isSelected ? '--selected' : ''}">
				<div class="ui-icon-set --${this.#options.extraIcon.icon}"></div>
			</div>
		`;

		Event.bind(extra, 'click', (event: MouseEvent): void => {
			this.#options.extraIcon.onClick();
			event.stopPropagation();
		}, true);

		return extra;
	}

	#renderCounter(): HTMLElement
	{
		if (!this.#options.counter)
		{
			return '';
		}

		if (!this.#options.counter.value)
		{
			return Tag.render`
				<div class="ui-popup-menu-item-counter"></div>
			`;
		}

		return Tag.render`
			<div class="ui-popup-menu-item-counter">
				${new Counter({ color: CounterColor.DANGER, ...this.#options.counter }).render()}
			</div>
		`;
	}

	#renderIcon(): HTMLElement
	{
		if (this.#options.icon)
		{
			return Tag.render`
				<div class="ui-popup-menu-item-icon">
					<div class="ui-icon-set --${this.#options.icon}"></div>
				</div>
			`;
		}

		if (this.#options.svg)
		{
			return Tag.render`
				<div class="ui-popup-menu-item-svg">
					${this.#options.svg}
				</div>
			`;
		}

		return '';
	}

	#renderArrow(): HTMLElement
	{
		if (!this.#options.subMenu)
		{
			return '';
		}

		return Tag.render`
			<div class="ui-popup-menu-item-arrow">
				<div class="ui-icon-set --${Outline.CHEVRON_RIGHT_L}"></div>
			</div>
		`;
	}
}

Youez - 2016 - github.com/yon3zu
LinuXploit