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