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/stageflow/src/

Upload File :
current_dir [ Writeable] document_root [ Writeable]

 

Command :


[ Back ]     

Current File : /home/bitrix/ext_www/rospirotorg.ru/bitrix/js/ui/stageflow/src/chart.js
import { Text, Dom, Tag, Type, Loc } from 'main.core';
import { Button } from 'ui.buttons';
import { Stage } from './stage';
import './css/style.css';

import { MenuManager, Popup, PopupManager } from 'main.popup';

const semanticSelectorPopupId = 'ui-stageflow-select-semantic-popup';
const finalStageSelectorPopupId = 'ui-stageflow-select-final-stage-popup';
const FinalStageDefaultData = {
	id: 'final',
	color: '7BD500',
	isFilled: false,
};

const defaultFinalStageLabels = {
	finalStageName: Loc.getMessage('UI_STAGEFLOW_FINAL_STAGE_NAME'),
	finalStagePopupTitle: Loc.getMessage('UI_STAGEFLOW_FINAL_STAGE_POPUP_TITLE'),
	finalStagePopupFail: Loc.getMessage('UI_STAGEFLOW_FINAL_STAGE_POPUP_FAIL'),
	finalStageSelectorTitle: Loc.getMessage('UI_STAGEFLOW_FINAL_STAGE_SELECTOR_TITLE'),
};

export class Chart
{
	backgroundColor;
	container;
	currentStage = 0;
	stages: Map;
	isActive = false;
	onStageChange;
	labels: {
		finalStageName: string,
		finalStagePopupTitle: string,
		finalStagePopupFail: string,
		finalStageSelectorTitle: string,
	};

	constructor(params: {
		backgroundColor: string,
		currentStage: number,
		isActive: boolean,
		onStageChange: ?Function,
		labels: ?{},
	}, stages = [])
	{
		this.labels = defaultFinalStageLabels;
		if(Type.isPlainObject(params))
		{
			if(Type.isString(params.backgroundColor) && params.backgroundColor.length === 6)
			{
				this.backgroundColor = params.backgroundColor;
			}
			if(params.currentStage)
			{
				this.currentStage = Text.toInteger(params.currentStage);
			}
			if(Type.isBoolean(params.isActive))
			{
				this.isActive = params.isActive;
			}
			if(Type.isFunction(params.onStageChange))
			{
				this.onStageChange = params.onStageChange;
			}
			if(Type.isPlainObject(params.labels))
			{
				this.labels = {...this.labels, ...params.labels};
			}
		}
		FinalStageDefaultData.name = this.labels.finalStageName;
		if(Type.isArray(stages))
		{
			let fillingColor = null;
			if(this.currentStage > 0)
			{
				stages.forEach((data) => {
					if(Text.toInteger(data.id) === Text.toInteger(this.currentStage))
					{
						fillingColor = data.color;
					}
				})
			}
			this.fillStages(stages, fillingColor);
		}
		if(!this.currentStage && this.stages.length > 0)
		{
			this.currentStage = this.stages.keys().next().value;
		}
	}

	setCurrentStageId(stageId: number): Chart
	{
		stageId = Text.toInteger(stageId);
		const currentStage = this.getStageById(stageId);
		if(!currentStage)
		{
			return;
		}
		this.currentStage = stageId;
		const finalStage = this.getFinalStage();
		if(finalStage)
		{
			if(currentStage.isFinal())
			{
				finalStage.setColor(currentStage.getColor()).setName(currentStage.getName());
			}
			else
			{
				finalStage.setColor(FinalStageDefaultData.color).setName(FinalStageDefaultData.name);
			}
		}
		this.stages.forEach((stage: Stage) =>
		{
			if(!stage.isFinal())
			{
				stage.fillingColor = currentStage.getColor();
			}
		});
		this.addBackLightUpToStage();

		return this;
	}

	fillStages(stages: Array, fillingColor: ?string)
	{
		let isFilled = (this.currentStage > 0);
		const finalStageOptions = {};
		this.stages = new Map();
		stages.forEach((data) =>
		{
			data.isFilled = isFilled;
			data.backgroundColor = this.backgroundColor;
			data.fillingColor = fillingColor;
			data.events = {
				onMouseEnter: this.onStageMouseHover.bind(this),
				onMouseLeave: this.onStageMouseLeave.bind(this),
				onClick: this.onStageClick.bind(this),
			};
			const stage = Stage.create(data);
			if(stage)
			{
				this.stages.set(stage.getId(), stage);
			}
			if(stage.isSuccess())
			{
				FinalStageDefaultData.color = stage.getColor();
			}
			if(stage.isFinal())
			{
				finalStageOptions.isFilled = isFilled;
				if(stage.getId() === this.currentStage)
				{
					finalStageOptions.name = stage.getName();
					finalStageOptions.color = stage.getColor();
				}
			}
			else if(isFilled && stage.getId() === this.currentStage)
			{
				isFilled = false;
			}
		});

		if(this.getFailStages().length <= 0)
		{
			FinalStageDefaultData.name = finalStageOptions.name = this.getSuccessStage().getName();
		}

		this.addFinalStage(finalStageOptions);
	}

	addFinalStage(data: {
		isFilled: ?boolean,
		name: ?string,
		color: ?string,
	})
	{
		this.stages.set(FinalStageDefaultData.id, new Stage({...{
			backgroundColor: this.backgroundColor,
			events: {
				onMouseEnter: this.onStageMouseHover.bind(this),
				onMouseLeave: this.onStageMouseLeave.bind(this),
				onClick: this.onFinalStageClick.bind(this),
			},
		}, ...FinalStageDefaultData, ...data}));
	}

	getFinalStage(): ?Stage
	{
		return this.getStageById(FinalStageDefaultData.id);
	}

	getStages(): Map
	{
		return this.stages;
	}

	getFirstFailStage(): ?Stage
	{
		let failStage = null;
		this.stages.forEach((stage: Stage) =>
		{
			if(stage.isFail() && !failStage)
			{
				failStage = stage;
			}
		});

		return failStage;
	}

	getFailStages(): Array
	{
		const failStages = [];
		this.stages.forEach((stage: Stage) =>
		{
			if(stage.isFail())
			{
				failStages.push(stage);
			}
		});

		return failStages;
	}

	getSuccessStage(): ?Stage
	{
		let finalStage = null;
		this.stages.forEach((stage: Stage) =>
		{
			if(stage.isSuccess())
			{
				finalStage = stage;
			}
		});

		return finalStage;
	}

	getStageById(id: number): ?Stage
	{
		return this.stages.get(id);
	}

	render(): Element
	{
		const container = this.renderContainer();

		this.getStages().forEach((stage: Stage) =>
		{
			if(stage.isFinal())
			{
				return;
			}
			container.appendChild(stage.render());
		});

		this.addBackLightUpToStage();

		return container;
	}

	renderContainer(): Element
	{
		if(this.container)
		{
			Dom.clean(this.container);
			return this.container;
		}

		this.container = Tag.render`<div class="ui-stageflow-container"></div>`;

		return this.container;
	}

	onStageMouseHover(stage: Stage)
	{
		if(!this.isActive)
		{
			return;
		}

		this.hoverStageId = stage.getId();

		for(let [id, currentStage] of this.stages)
		{
			currentStage.addBackLight(stage.getColor());
			if(id === stage.getId())
			{
				break;
			}
		}

		this.increaseStageWidthForNameVisibility(stage);
	}

	onStageMouseLeave(stage: Stage)
	{
		if(!this.isActive)
		{
			return;
		}

		Dom.style(stage.node, {
			flexBasis: null,
			flexGrow: null,
		})

		for(let [id, currentStage] of this.stages)
		{
			currentStage.removeBackLight();
			if(id === stage.getId())
			{
				break;
			}
		}
	}

	onStageClick(stage: Stage)
	{
		if(!this.isActive)
		{
			return;
		}
		if(stage.getId() !== this.currentStage && Type.isFunction(this.onStageChange))
		{
			this.onStageChange(stage);
		}
		const popup = this.getSemanticSelectorPopup();
		if(popup.isShown())
		{
			popup.close();
		}
	}

	onFinalStageClick(stage: Stage)
	{
		if(!this.isActive)
		{
			return;
		}

		if(this.getFailStages().length <= 0)
		{
			this.onStageClick(this.getSuccessStage());
		}
		else
		{
			const popup = this.getSemanticSelectorPopup();
			popup.show();
			const currentStage = this.getStageById(this.currentStage);
			this.isActive = false;
			if (!currentStage.isFinal()) {
				const finalStage = this.getStageById(FinalStageDefaultData.id);
				if (finalStage) {
					this.addBackLightUpToStage(finalStage.getId(), finalStage.getColor());
				}
			}
		}
	}

	addBackLightUpToStage(stageId: number|string = null, color: string = null)
	{
		if(!stageId)
		{
			stageId = this.currentStage;
		}
		const currentStage = this.getStageById(stageId);
		if(currentStage && !color)
		{
			color = currentStage.getColor();
		}

		let isFilled = !!stageId;
		this.stages.forEach((stage: Stage) =>
		{
			stage.isFilled = isFilled;
			if(stage.isFilled)
			{
				stage.addBackLight(color ? color : stage.getColor());
			}
			else
			{
				stage.removeBackLight();
			}
			if(!stage.isFinal() && isFilled && stage.getId() === stageId)
			{
				isFilled = false;
			}
		});
	}

	getSemanticSelectorPopup(): Popup
	{
		let popup = PopupManager.getPopupById(semanticSelectorPopupId);

		if (!popup)
		{
			popup = PopupManager.create({
				id: semanticSelectorPopupId,
				autoHide: true,
				closeByEsc: true,
				closeIcon: true,
				maxWidth: 420,
				content: Tag.render`<div class="ui-stageflow-popup-title">${this.labels.finalStagePopupTitle}</div>`,
				buttons: [
					this.getSemanticPopupSuccessButton(),
					this.getSemanticPopupFailureButton(),
				],
				events: {
					onClose: () => {
						this.setCurrentStageId(this.currentStage);
						this.isActive = true;
					},
				},
			});
		}

		return popup;
	}

	getSemanticPopupSuccessButton(): Button
	{
		return new BX.UI.Button({
			color: BX.UI.Button.Color.SUCCESS,
			text: this.getSuccessStage().getName(),
			onclick: () => {
				this.isActive = true;
				this.onStageClick(this.getSuccessStage());
			},
		});
	}

	getSemanticPopupFailureButton(): ?Button
	{
		const failureSemanticText = this.getFailStageName();
		if (!failureSemanticText)
		{
			return null;
		}

		return new BX.UI.Button({
			color: BX.UI.Button.Color.DANGER,
			text: failureSemanticText,
			onclick: () => {
				PopupManager.getPopupById(semanticSelectorPopupId)?.close();
				const finalStagePopup = this.getFinalStageSelectorPopup();
				finalStagePopup.show();
				this.isActive = false;
			},
		});
	}

	getFinalStageSemanticSelector(isSuccess: boolean = null): Element
	{
		if(!this.finalStageSemanticSelector)
		{
			this.finalStageSemanticSelector = Tag.render`<div class="ui-stageflow-stage-selector-option ui-stageflow-stage-selector-option-fail" onclick="${this.onSemanticSelectorClick.bind(this)}"></div>`;
		}

		if(Type.isBoolean(isSuccess))
		{
			let realFinalStage = null;
			let failStageName = this.getFailStageName();
			if(isSuccess || !failStageName)
			{
				this.finalStageSemanticSelector.classList.add('ui-stageflow-stage-selector-option-success');
				this.finalStageSemanticSelector.classList.remove('ui-stageflow-stage-selector-option-fail');
				this.finalStageSemanticSelector.innerText = this.getSuccessStage().getName();
				realFinalStage = this.getSuccessStage();
			}
			else
			{
				this.finalStageSemanticSelector.classList.add('ui-stageflow-stage-selector-option-fail');
				this.finalStageSemanticSelector.classList.remove('ui-stageflow-stage-selector-option-success');
				this.finalStageSemanticSelector.innerText = failStageName;
				realFinalStage = this.getFirstFailStage();
			}
			const finalStage = this.getFinalStage();
			if(finalStage && realFinalStage)
			{
				finalStage.setColor(realFinalStage.getColor()).setName(realFinalStage.getName());
			}
			this.addBackLightUpToStage(finalStage.getId(), finalStage.getColor());
		}

		return this.finalStageSemanticSelector;
	}

	getFinalStageSelectorPopup(isSuccess: boolean = false): Popup
	{
		let popup = PopupManager.getPopupById(finalStageSelectorPopupId);
		if (!popup)
		{
			popup = PopupManager.create({
				id: finalStageSelectorPopupId,
				autoHide: false,
				closeByEsc: true,
				closeIcon: true,
				width: 420,
				titleBar: true,
				buttons: [
					new BX.UI.SaveButton({
						onclick: () =>
						{
							popup.close();
							const stage = this.getSelectedFinalStage();
							if(stage)
							{
								this.onStageClick(stage);
							}
						}
					}),
					new BX.UI.CancelButton({
						onclick: () =>
						{
							popup.close();
						}
					}),
				],
				events: {
					onClose: () =>
					{
						this.setCurrentStageId(this.currentStage);
						this.isActive = true;
					},
				}
			});
		}

		popup.setContent(this.getFinalStagePopupFailStagesWrapper(isSuccess));
		popup.setTitleBar(this.getFinalStagePopupTitleBar(isSuccess));

		return popup;
	}

	getFinalStagePopupFailStagesWrapper(isSuccess: boolean = false): HTMLElement
	{
		const failStageListWrapper = Tag.render`<div class="ui-stageflow-final-fail-stage-list-wrapper"></div>`;
		if (isSuccess)
		{
			return failStageListWrapper;
		}

		const failStages = this.getFailStages();
		if (failStages.length > 1)
		{
			failStages.forEach((stage: Stage) => {
				Dom.append(this.getFinalStagePopupFailStage(stage), failStageListWrapper);
			});

			this.setCheckedStageInFailStagesWrapper(failStageListWrapper);
		}

		return failStageListWrapper;
	}

	setCheckedStageInFailStagesWrapper(failStageListWrapper: HTMLElement): void
	{
		const failStagesNodeList = this.extractFinalStagePopupFailStages(failStageListWrapper);
		if (!Type.isArrayFilled(failStagesNodeList))
		{
			return;
		}

		const firstFailStageInput = failStagesNodeList[0].querySelector('input');
		if (firstFailStageInput)
		{
			firstFailStageInput.checked = true;
		}
	}

	extractFinalStagePopupFailStages(failStageListWrapper: HTMLElement): NodeList
	{
		return failStageListWrapper.querySelectorAll('.ui-stageflow-final-fail-stage-list-section') ?? [];
	}

	getFinalStagePopupFailStage(stage: Stage): HTMLElement
	{
		return Tag.render`
			<div class="ui-stageflow-final-fail-stage-list-section">
				<input
					data-stage-id="${stage.getId()}"
					id="ui-stageflow-final-fail-stage-${stage.getId()}"
					name="ui-stageflow-final-fail-stage-input"
					class="crm-list-fail-deal-button"
					type="radio"
				>
				<label for="ui-stageflow-final-fail-stage-${stage.getId()}">${stage.getName()}</label>
			</div>
		`;
	}

	getFinalStagePopupTitleBar(isSuccess: boolean = false): Object
	{
		const titleBar = {};

		titleBar.content = Tag.render`
			<div class="ui-stageflow-stage-selector-block">
				<span>${this.labels.finalStageSelectorTitle}</span>
				${this.getFinalStageSemanticSelector(isSuccess)}
			</div>
		`;

		return titleBar;
	}

	onSemanticSelectorClick()
	{
		const failStageName = this.getFailStageName();
		const menu = MenuManager.create({
			id: 'ui-stageflow-final-stage-semantic-selector',
			bindElement: this.getFinalStageSemanticSelector(),
			items: [
				{
					text: this.getSuccessStage().getName(),
					onclick: () =>
					{
						this.getFinalStageSelectorPopup(true);
						menu.close();
					},
				},
				(failStageName ? {
					text: failStageName,
					onclick: () =>
					{
						this.getFinalStageSelectorPopup(false);
						menu.close();
					},
				} : null),
			]
		});

		menu.show();
	}

	getSelectedFinalStage(): ?Stage
	{
		const finalStageSemanticSelector = this.getFinalStageSemanticSelector();
		if(finalStageSemanticSelector.classList.contains('ui-stageflow-stage-selector-option-success'))
		{
			return this.getSuccessStage();
		}
		else
		{
			const failStages = this.getFailStages();
			if(failStages.length > 1)
			{
				const finalStageSelectorPopupContainer = document.getElementById(finalStageSelectorPopupId);
				if(finalStageSelectorPopupContainer)
				{
					const selectedInput = finalStageSelectorPopupContainer.querySelector('input:checked');
					if(selectedInput)
					{
						const failStage = this.getStageById(Text.toInteger(selectedInput.dataset.stageId));
						if(failStage)
						{
							return failStage;
						}
					}
				}
			}

			return this.getFirstFailStage();
		}
	}

	getFailStageName(): ?string
	{
		const failStagesLength = this.getFailStages().length;

		if(failStagesLength <= 0)
		{
			return null;
		}
		else if(failStagesLength === 1)
		{
			return this.getFirstFailStageName();
		}
		else
		{
			return this.labels.finalStagePopupFail;
		}
	}

	getFirstFailStageName(): ?string
	{
		return this.getFirstFailStage()?.getName();
	}

	increaseStageWidthForNameVisibility(stage: Stage): void
	{
		if (!stage.isNameCropped())
		{
			return
		}

		Dom.style(stage.node, {
			flexGrow: 0,
			flexBasis: `${stage.getMinWidthForFullNameVisibility()}px`,
		})
	}
}

Youez - 2016 - github.com/yon3zu
LinuXploit