diff --git a/app/components/p8p_svg.js b/app/components/p8p_svg.js index 1a76633..822ce73 100644 --- a/app/components/p8p_svg.js +++ b/app/components/p8p_svg.js @@ -7,7 +7,8 @@ //Подключение библиотек //--------------------- -import React, { useEffect, useRef } from "react"; //Классы React +import React, { useEffect, useRef, useState } from "react"; //Классы React +import { IconButton, Icon } from "@mui/material"; //Интерфейсные элементы import PropTypes from "prop-types"; //Контроль свойств компонента //--------- @@ -16,7 +17,8 @@ import PropTypes from "prop-types"; //Контроль свойств компо //Стили const STYLES = { - CANVAS: { width: "100%", height: "100%" } + CANVAS: { width: "100%", height: "100%" }, + CONTROLS: { justifyContent: "center", alignItems: "center", display: "flex" } }; //Структура элемента изображения @@ -30,17 +32,29 @@ const P8P_SVG_ITEM_SHAPE = PropTypes.shape({ //----------- //Интерактивные изображения SVG -const P8PSVG = ({ data, items, onClick, style }) => { +const P8PSVG = ({ data, items, onClick, onItemClick, canvasStyle }) => { + //Собственное состояние + const [state, setState] = useState({ + images: [], + currentImage: 0, + imagesCount: 0 + }); + //Ссылки на DOM const svgContainerRef = useRef(null); const svgRef = useRef(null); //Обработка нажатия на элемент изображения const handleClick = e => { - if (e.target.id && items && onClick) { + let itemClickFired = false; + if (e.target.id && items && onItemClick) { const item = items.find(item => item.id == e.target.id); - if (item) onClick({ item }); + if (item) { + onItemClick({ item }); + itemClickFired = true; + } } + if (!itemClickFired && onClick) onClick(e); }; //Формирование интерактивных элементов изображения @@ -48,7 +62,7 @@ const P8PSVG = ({ data, items, onClick, style }) => { items.forEach(item => { const svgE = document.getElementById(item.id); if (svgE) { - svgE.setAttribute("style", `${onClick ? "cursor: pointer" : ""}; ${item.backgroundColor ? `fill: ${item.backgroundColor}` : ""}`); + svgE.setAttribute("style", `${onItemClick ? "cursor: pointer" : ""}; ${item.backgroundColor ? `fill: ${item.backgroundColor}` : ""}`); if (item?.title) { const titleE = document.createElementNS("http://www.w3.org/2000/svg", "title"); titleE.textContent = item.title; @@ -60,22 +74,74 @@ const P8PSVG = ({ data, items, onClick, style }) => { //Загрузка изображения const loadSVG = () => { - const parser = new DOMParser(); - const doc = parser.parseFromString(data, "image/svg+xml"); - svgRef.current = doc.documentElement; - svgRef.current.onclick = handleClick; - svgContainerRef.current.replaceChildren(svgRef.current); - if (items) makeSVGItems(items); + 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, items]); + }, [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
; + return ( +
+
+ {state.imagesCount > 1 ? ( +
+ + arrow_left + + + arrow_right + +
+ ) : null} +
+ ); }; //Контроль свойств - Интерактивные изображения SVG @@ -83,7 +149,8 @@ P8PSVG.propTypes = { data: PropTypes.string.isRequired, items: PropTypes.arrayOf(P8P_SVG_ITEM_SHAPE), onClick: PropTypes.func, - style: PropTypes.object + onItemClick: PropTypes.func, + canvasStyle: PropTypes.object }; //---------------- diff --git a/app/panels/samples/svg.js b/app/panels/samples/svg.js index 142fef5..85455ba 100644 --- a/app/panels/samples/svg.js +++ b/app/panels/samples/svg.js @@ -23,6 +23,7 @@ const SAMPLE_URL = "img/sample.svg"; const STYLES = { CONTAINER: { textAlign: "center", paddingTop: "20px" }, TITLE: { paddingBottom: "15px" }, + FORM: { justifyContent: "center", alignItems: "center" }, SVG: { width: "95vw", height: "30vw", display: "flex", justifyContent: "center" } }; @@ -38,15 +39,20 @@ const Svg = ({ title }) => { data: null, mode: "items1", items1: [ - { id: "1", backgroundColor: "red", desc: "Цифра на флюзеляже" }, - { id: "2", backgroundColor: "magenta", desc: "Ребро флюзеляжа" }, - { id: "3", backgroundColor: "yellow", desc: "Люк" } + { id: "1", backgroundColor: "red", desc: "Цифра на флюзеляже", title: "Цифра на флюзеляже" }, + { id: "2", backgroundColor: "magenta", desc: "Ребро флюзеляжа", title: "Ребро флюзеляжа" }, + { id: "3", backgroundColor: "yellow", desc: "Люк", title: "Люк" } ], items2: [ { id: "4", backgroundColor: "green", desc: "Хвост", title: "Хвост" }, { id: "5", backgroundColor: "blue", desc: "Хвостовой руль", title: "Хвостовой руль" }, { id: "6", backgroundColor: "aquamarine", desc: "Ребро жесткости хвоста", title: "Ребро жесткости хвоста" } ], + items3: [ + { id: "7", backgroundColor: "blueviolet", desc: "Крыло левое", title: "Крыло левое" }, + { id: "8", backgroundColor: "orange", desc: "Двигатель левый", title: "Двигатель левый" }, + { id: "9", backgroundColor: "springgreen", desc: "Крыло правое", title: "Крыло правое" } + ], selectedItemDesc: "" }); @@ -57,6 +63,11 @@ const Svg = ({ title }) => { setSVG(pv => ({ ...pv, loaded: true, data })); }; + //Отработка нажатия на изображение + const handleSVGClick = () => { + setSVG(pv => ({ ...pv, selectedItemDesc: "Выбрано изображение целиком" })); + }; + //Отработка нажатия на элемент изображения const handleSVGItemClick = ({ item }) => { setSVG(pv => ({ ...pv, selectedItemDesc: item?.desc ? `Выбран элемент: ${item.desc}` : "Для выбранного элемента не задано описание" })); @@ -74,17 +85,26 @@ const Svg = ({ title }) => { {title} - + Группа элементов setSVG(pv => ({ ...pv, mode: e.target.value, selectedItemDesc: "" }))}> - } label="Элементы первой группы" /> - } label="Элементы второй группы" /> + } label="Первая" /> + } label="Вторая" /> + } label="Третья" /> {svg.selectedItemDesc ? svg.selectedItemDesc : "Нажмите на элемент изображения для получения его описания"} - {svg.loaded ? : null} + {svg.loaded ? ( + + ) : null} diff --git a/img/sample.svg b/img/sample.svg index 54a7426..cb3c008 100644 --- a/img/sample.svg +++ b/img/sample.svg @@ -103,3 +103,853 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file