WEB APP: P8PSVG - поддержка групп, прозрачности заливки, верстка на Gridе

This commit is contained in:
Mikhail Chechnev 2024-05-18 00:41:42 +03:00
parent c61b8cc9c2
commit 46d078219f
2 changed files with 58 additions and 26 deletions

View File

@ -8,7 +8,7 @@
//--------------------- //---------------------
import React, { useEffect, useRef, useState } from "react"; //Классы React import React, { useEffect, useRef, useState } from "react"; //Классы React
import { IconButton, Icon } from "@mui/material"; //Интерфейсные элементы import { IconButton, Icon, Container, Grid } from "@mui/material"; //Интерфейсные элементы
import PropTypes from "prop-types"; //Контроль свойств компонента import PropTypes from "prop-types"; //Контроль свойств компонента
//--------- //---------
@ -17,13 +17,13 @@ import PropTypes from "prop-types"; //Контроль свойств компо
//Стили //Стили
const STYLES = { const STYLES = {
CANVAS: { width: "100%", height: "100%" }, GRID_ITEM_CANVAS: { width: "100%", height: "100%" },
CONTROLS: { justifyContent: "center", alignItems: "center", display: "flex" } CONTROLS: { justifyContent: "center", alignItems: "center", display: "flex" }
}; };
//Структура элемента изображения //Структура элемента изображения
const P8P_SVG_ITEM_SHAPE = PropTypes.shape({ const P8P_SVG_ITEM_SHAPE = PropTypes.shape({
id: PropTypes.string.isRequired, id: PropTypes.oneOfType([PropTypes.string, PropTypes.number]).isRequired,
backgroundColor: PropTypes.oneOfType([PropTypes.string, PropTypes.arrayOf(PropTypes.string)]) backgroundColor: PropTypes.oneOfType([PropTypes.string, PropTypes.arrayOf(PropTypes.string)])
}); });
@ -32,7 +32,7 @@ const P8P_SVG_ITEM_SHAPE = PropTypes.shape({
//----------- //-----------
//Интерактивные изображения SVG //Интерактивные изображения SVG
const P8PSVG = ({ data, items, onClick, onItemClick, canvasStyle }) => { const P8PSVG = ({ data, items, onClick, onItemClick, canvasStyle, fillOpacity }) => {
//Собственное состояние //Собственное состояние
const [state, setState] = useState({ const [state, setState] = useState({
images: [], images: [],
@ -47,8 +47,8 @@ const P8PSVG = ({ data, items, onClick, onItemClick, canvasStyle }) => {
//Обработка нажатия на элемент изображения //Обработка нажатия на элемент изображения
const handleClick = e => { const handleClick = e => {
let itemClickFired = false; let itemClickFired = false;
if (e.target.id && items && onItemClick) { if (items && onItemClick) {
const item = items.find(item => item.id == e.target.id); const item = items.find(item => item.id == e.target?.id || item.id == e.target?.parentElement?.id);
if (item) { if (item) {
onItemClick({ item }); onItemClick({ item });
itemClickFired = true; itemClickFired = true;
@ -62,11 +62,36 @@ const P8PSVG = ({ data, items, onClick, onItemClick, canvasStyle }) => {
items.forEach(item => { items.forEach(item => {
const svgE = document.getElementById(item.id); const svgE = document.getElementById(item.id);
if (svgE) { if (svgE) {
svgE.setAttribute("style", `${onItemClick ? "cursor: pointer" : ""}; ${item.backgroundColor ? `fill: ${item.backgroundColor}` : ""}`); //Запомним старый стиль элемента
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) { if (item?.title) {
const titleE = document.createElementNS("http://www.w3.org/2000/svg", "title"); titleE = document.createElementNS("http://www.w3.org/2000/svg", "title");
titleE.textContent = item.title; titleE.textContent = item.title;
svgE.replaceChildren(titleE); 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}`);
} }
} }
}); });
@ -121,16 +146,20 @@ const P8PSVG = ({ data, items, onClick, onItemClick, canvasStyle }) => {
}, [state.images, state.currentImage, items]); }, [state.images, state.currentImage, items]);
//При прокрутке изображений назад //При прокрутке изображений назад
const handlePrevClick = () => switchImage(-1); const handlePrevClick = () => switchImage(1);
//При прокрутке изображений вперёд //При прокрутке изображений вперёд
const handleNextClick = () => switchImage(1); const handleNextClick = () => switchImage(-1);
//Генерация содержимого //Генерация содержимого
return ( return (
<div> <Container>
<div ref={svgContainerRef} style={{ ...STYLES.CANVAS, ...(canvasStyle ? canvasStyle : {}) }}></div> <Grid container direction="column" justifyContent="center" alignItems="center" spacing={0}>
<Grid item xs={12} sx={STYLES.GRID_ITEM_CANVAS}>
<div ref={svgContainerRef} style={{ ...(canvasStyle ? canvasStyle : {}) }}></div>
</Grid>
{state.imagesCount > 1 ? ( {state.imagesCount > 1 ? (
<Grid item xs={12}>
<div style={STYLES.CONTROLS}> <div style={STYLES.CONTROLS}>
<IconButton onClick={handlePrevClick}> <IconButton onClick={handlePrevClick}>
<Icon>arrow_left</Icon> <Icon>arrow_left</Icon>
@ -139,8 +168,10 @@ const P8PSVG = ({ data, items, onClick, onItemClick, canvasStyle }) => {
<Icon>arrow_right</Icon> <Icon>arrow_right</Icon>
</IconButton> </IconButton>
</div> </div>
</Grid>
) : null} ) : null}
</div> </Grid>
</Container>
); );
}; };
@ -150,7 +181,8 @@ P8PSVG.propTypes = {
items: PropTypes.arrayOf(P8P_SVG_ITEM_SHAPE), items: PropTypes.arrayOf(P8P_SVG_ITEM_SHAPE),
onClick: PropTypes.func, onClick: PropTypes.func,
onItemClick: PropTypes.func, onItemClick: PropTypes.func,
canvasStyle: PropTypes.object canvasStyle: PropTypes.object,
fillOpacity: PropTypes.string
}; };
//---------------- //----------------

View File

@ -24,7 +24,7 @@ const STYLES = {
CONTAINER: { textAlign: "center", paddingTop: "20px" }, CONTAINER: { textAlign: "center", paddingTop: "20px" },
TITLE: { paddingBottom: "15px" }, TITLE: { paddingBottom: "15px" },
FORM: { justifyContent: "center", alignItems: "center" }, FORM: { justifyContent: "center", alignItems: "center" },
SVG: { width: "95vw", height: "30vw", display: "flex", justifyContent: "center" } SVG: { height: "30vw", display: "flex", justifyContent: "center" }
}; };
//----------- //-----------