/* Парус 8 - Панели мониторинга Компонент: Интерактивные изображения SVG */ //--------------------- //Подключение библиотек //--------------------- import React, { useEffect, useRef, useState } from "react"; //Классы React import { IconButton, Icon, Container, Grid } from "@mui/material"; //Интерфейсные элементы import PropTypes from "prop-types"; //Контроль свойств компонента //--------- //Константы //--------- //Стили const STYLES = { GRID_ITEM_CANVAS: { width: "100%", height: "100%" }, CONTROLS: { justifyContent: "center", alignItems: "center", display: "flex" } }; //Структура элемента изображения const P8P_SVG_ITEM_SHAPE = PropTypes.shape({ id: PropTypes.oneOfType([PropTypes.string, PropTypes.number]).isRequired, title: PropTypes.any, backgroundColor: PropTypes.oneOfType([PropTypes.string, PropTypes.arrayOf(PropTypes.string)]) }); //----------- //Тело модуля //----------- //Интерактивные изображения SVG const P8PSVG = ({ data, items, onClick, onItemClick, canvasStyle, fillOpacity }) => { //Собственное состояние const [state, setState] = useState({ images: [], currentImage: 0, imagesCount: 0 }); //Ссылки на DOM const svgContainerRef = useRef(null); const svgRef = useRef(null); //Обработка нажатия на элемент изображения const handleClick = e => { let itemClickFired = false; if (items && onItemClick) { const item = items.find(item => item.id == e.target?.id || item.id == e.target?.parentElement?.id); if (item) { onItemClick({ item }); itemClickFired = true; } } if (!itemClickFired && onClick) onClick(e); }; //Формирование интерактивных элементов изображения const makeSVGItems = () => { items.forEach(item => { const svgE = document.getElementById(item.id); if (svgE) { //Запомним старый стиль элемента let styleOld = svgE.getAttribute("style") || ""; if (styleOld && !styleOld.endsWith(";")) styleOld = `${styleOld};`; //Сформируем стиль для заливки let fillStyle = ""; if (item.backgroundColor) fillStyle = `fill: ${item.backgroundColor}; ${fillOpacity ? `opacity: ${fillOpacity};` : ""}`; //Сформируем стиль для курсора let cursorStyle = ""; if (onItemClick) cursorStyle = "cursor: pointer;"; //Добавим элемент для всплывающей подсказки let titleE = null; if (item?.title) { titleE = document.createElementNS("http://www.w3.org/2000/svg", "title"); titleE.textContent = item.title; svgE.appendChild(titleE); } //Если нем попалась группа if (svgE.tagName == "g") { //Установим ей новые стили svgE.setAttribute("style", `${styleOld}${cursorStyle}`); //И заливку всем дочерним элементам if (fillStyle) for (const child of svgE.children) { let childStyleOld = child.getAttribute("style") || ""; if (childStyleOld && !childStyleOld.endsWith(";")) childStyleOld = `${childStyleOld};`; child.setAttribute("style", `${childStyleOld}${fillStyle}`); } } else { //Это простой элемент, не группа - просто выставляем стили svgE.setAttribute("style", `${styleOld}${cursorStyle}${fillStyle}`); } } }); }; //Загрузка изображения const loadSVG = () => { const images = data .split("") .filter(i => i) .map(i => i + ""); setState(pv => ({ ...pv, images, imagesCount: images.length, currentImage: 0 })); }; //Отображение текущего изображения const showSVG = () => { if (state.imagesCount > 0) { const parser = new DOMParser(); const doc = parser.parseFromString(state.images[state.currentImage], "image/svg+xml"); svgRef.current = doc.documentElement; svgRef.current.onclick = handleClick; svgContainerRef.current.replaceChildren(svgRef.current); if (items) makeSVGItems(items); } }; //Переключение текущего изображения const switchImage = direction => { setState(pv => ({ ...pv, currentImage: direction > 0 ? pv.currentImage + 1 >= pv.imagesCount ? 0 : pv.currentImage + 1 : pv.currentImage - 1 < 0 ? pv.imagesCount - 1 : pv.currentImage - 1 })); }; //При обновлении данных useEffect(() => { loadSVG(); // eslint-disable-next-line react-hooks/exhaustive-deps }, [data]); //При загрузке изображения useEffect(() => { showSVG(); // eslint-disable-next-line react-hooks/exhaustive-deps }, [state.images, state.currentImage, items]); //При прокрутке изображений назад const handlePrevClick = () => switchImage(1); //При прокрутке изображений вперёд const handleNextClick = () => switchImage(-1); //Генерация содержимого return (
{state.imagesCount > 1 ? (
arrow_left arrow_right
) : null}
); }; //Контроль свойств - Интерактивные изображения SVG P8PSVG.propTypes = { data: PropTypes.string.isRequired, items: PropTypes.arrayOf(P8P_SVG_ITEM_SHAPE), onClick: PropTypes.func, onItemClick: PropTypes.func, canvasStyle: PropTypes.object, fillOpacity: PropTypes.string }; //---------------- //Интерфейс модуля //---------------- export { P8PSVG };