Reviewed-on: #19 Reviewed-by: Микаэл Оганесян <15lu.akari@unprism.ru> Co-authored-by: fisenko <kkzemeow@gmail.com> Co-committed-by: fisenko <kkzemeow@gmail.com>
This commit is contained in:
@@ -2,7 +2,6 @@ import { FederatedMouseEvent, Graphics } from "pixi.js";
|
||||
import { useCallback, useState, useEffect, useRef, FC, useMemo } from "react";
|
||||
import { observer } from "mobx-react-lite";
|
||||
|
||||
// --- Заглушки для зависимостей (замените на ваши реальные импорты) ---
|
||||
import {
|
||||
BACKGROUND_COLOR,
|
||||
PATH_COLOR,
|
||||
@@ -15,22 +14,16 @@ import { StationData } from "./types";
|
||||
import { useMapData } from "./MapDataContext";
|
||||
import { coordinatesToLocal } from "./utils";
|
||||
import { languageStore } from "@shared";
|
||||
// --- Конец заглушек ---
|
||||
|
||||
// --- Декларации для react-pixi ---
|
||||
// (В реальном проекте типы должны быть предоставлены библиотекой @pixi/react-pixi)
|
||||
declare const pixiContainer: any;
|
||||
declare const pixiGraphics: any;
|
||||
declare const pixiText: any;
|
||||
|
||||
// --- Типы ---
|
||||
type HorizontalAlign = "left" | "center" | "right";
|
||||
type VerticalAlign = "top" | "center" | "bottom";
|
||||
type TextAlign = HorizontalAlign | `${HorizontalAlign} ${VerticalAlign}`;
|
||||
type LabelAlign = "left" | "center" | "right";
|
||||
|
||||
// --- Утилиты ---
|
||||
|
||||
/**
|
||||
* Преобразует текстовое позиционирование в anchor координаты.
|
||||
*/
|
||||
@@ -39,8 +32,6 @@ type LabelAlign = "left" | "center" | "right";
|
||||
* Получает координату anchor.x из типа выравнивания.
|
||||
*/
|
||||
|
||||
// --- Интерфейсы пропсов ---
|
||||
|
||||
interface StationProps {
|
||||
station: StationData;
|
||||
ruLabel: string | null;
|
||||
@@ -83,10 +74,6 @@ const getAnchorFromOffset = (
|
||||
return { x: (1 - nx) / 2, y: (1 - ny) / 2 };
|
||||
};
|
||||
|
||||
// =========================================================================
|
||||
// Компонент: Панель управления выравниванием в стиле УрФУ
|
||||
// =========================================================================
|
||||
|
||||
const LabelAlignmentControl: FC<LabelAlignmentControlProps> = ({
|
||||
scale,
|
||||
currentAlign,
|
||||
@@ -107,7 +94,6 @@ const LabelAlignmentControl: FC<LabelAlignmentControlProps> = ({
|
||||
(g: Graphics) => {
|
||||
g.clear();
|
||||
|
||||
// Основной фон с градиентом
|
||||
g.roundRect(
|
||||
-controlWidth / 2,
|
||||
0,
|
||||
@@ -115,9 +101,8 @@ const LabelAlignmentControl: FC<LabelAlignmentControlProps> = ({
|
||||
controlHeight,
|
||||
borderRadius
|
||||
);
|
||||
g.fill({ color: "#1a1a1a" }); // Темный фон как у УрФУ
|
||||
g.fill({ color: "#1a1a1a" });
|
||||
|
||||
// Тонкая рамка
|
||||
g.roundRect(
|
||||
-controlWidth / 2,
|
||||
0,
|
||||
@@ -127,7 +112,6 @@ const LabelAlignmentControl: FC<LabelAlignmentControlProps> = ({
|
||||
);
|
||||
g.stroke({ color: "#333333", width: strokeWidth });
|
||||
|
||||
// Разделители между кнопками
|
||||
for (let i = 1; i < 3; i++) {
|
||||
const x = -controlWidth / 2 + buttonWidth * i;
|
||||
g.moveTo(x, strokeWidth);
|
||||
@@ -151,7 +135,7 @@ const LabelAlignmentControl: FC<LabelAlignmentControlProps> = ({
|
||||
controlHeight - strokeWidth * 2,
|
||||
borderRadius / 2
|
||||
);
|
||||
g.fill({ color: "#0066cc", alpha: 0.8 }); // Синий акцент УрФУ
|
||||
g.fill({ color: "#0066cc", alpha: 0.8 });
|
||||
}
|
||||
},
|
||||
[controlWidth, controlHeight, buttonWidth, strokeWidth, borderRadius]
|
||||
@@ -230,10 +214,6 @@ const LabelAlignmentControl: FC<LabelAlignmentControlProps> = ({
|
||||
);
|
||||
};
|
||||
|
||||
// =========================================================================
|
||||
// Компонент: Метка Станции (с логикой)
|
||||
// =========================================================================
|
||||
|
||||
const StationLabel = observer(
|
||||
({
|
||||
station,
|
||||
@@ -274,48 +254,45 @@ const StationLabel = observer(
|
||||
hideTimer.current = null;
|
||||
}
|
||||
setIsHovered(true);
|
||||
onTextHover?.(true); // Call the callback to indicate text is hovered
|
||||
onTextHover?.(true);
|
||||
};
|
||||
|
||||
const handleControlPointerEnter = () => {
|
||||
// Дополнительная обработка для панели управления
|
||||
if (hideTimer.current) {
|
||||
clearTimeout(hideTimer.current);
|
||||
hideTimer.current = null;
|
||||
}
|
||||
setIsControlHovered(true);
|
||||
setIsHovered(true);
|
||||
onTextHover?.(true); // Call the callback to indicate text/control is hovered
|
||||
onTextHover?.(true);
|
||||
};
|
||||
|
||||
const handleControlPointerLeave = () => {
|
||||
setIsControlHovered(false);
|
||||
// Если курсор не над основным контейнером, скрываем панель через некоторое время
|
||||
|
||||
if (!isHovered) {
|
||||
hideTimer.current = setTimeout(() => {
|
||||
setIsHovered(false);
|
||||
onTextHover?.(false); // Call the callback to indicate neither text nor control is hovered
|
||||
onTextHover?.(false);
|
||||
}, 0);
|
||||
}
|
||||
};
|
||||
|
||||
const handlePointerLeave = () => {
|
||||
// Увеличиваем время до скрытия панели и добавляем проверку
|
||||
hideTimer.current = setTimeout(() => {
|
||||
setIsHovered(false);
|
||||
// Если курсор не над панелью управления, скрываем и её
|
||||
|
||||
if (!isControlHovered) {
|
||||
setIsControlHovered(false);
|
||||
}
|
||||
onTextHover?.(false); // Call the callback to indicate text is no longer hovered
|
||||
}, 100); // Увеличиваем время до скрытия панели
|
||||
onTextHover?.(false);
|
||||
}, 100);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
setPosition({ x: station.offset_x ?? 0, y: station.offset_y ?? 0 });
|
||||
}, [station.offset_x, station.offset_y, station.id]);
|
||||
|
||||
// Функция для конвертации числового align в строковый
|
||||
const convertNumericAlign = (align: number): LabelAlign => {
|
||||
switch (align) {
|
||||
case 0:
|
||||
@@ -329,7 +306,6 @@ const StationLabel = observer(
|
||||
}
|
||||
};
|
||||
|
||||
// Функция для конвертации строкового align в числовой
|
||||
const convertStringAlign = (align: LabelAlign): number => {
|
||||
switch (align) {
|
||||
case "left":
|
||||
@@ -353,7 +329,6 @@ const StationLabel = observer(
|
||||
const compensatedRuFontSize = (26 * 0.75) / scale;
|
||||
const compensatedNameFontSize = (16 * 0.75) / scale;
|
||||
|
||||
// Измеряем ширину верхнего лейбла
|
||||
useEffect(() => {
|
||||
if (ruLabelRef.current && ruLabel) {
|
||||
setRuLabelWidth(ruLabelRef.current.width);
|
||||
@@ -386,7 +361,6 @@ const StationLabel = observer(
|
||||
y: dragStartPos.current.y + dy_screen,
|
||||
};
|
||||
|
||||
// Проверяем, изменилась ли позиция
|
||||
if (
|
||||
Math.abs(newPosition.x - position.x) > 0.01 ||
|
||||
Math.abs(newPosition.y - position.y) > 0.01
|
||||
@@ -406,7 +380,7 @@ const StationLabel = observer(
|
||||
const handleAlignChange = async (align: LabelAlign) => {
|
||||
setCurrentLabelAlign(align);
|
||||
onLabelAlignChange?.(align);
|
||||
// Сохраняем в стор
|
||||
|
||||
const numericAlign = convertStringAlign(align);
|
||||
setStationAlign(station.id, numericAlign);
|
||||
};
|
||||
@@ -416,34 +390,29 @@ const StationLabel = observer(
|
||||
[position.x, position.y]
|
||||
);
|
||||
|
||||
// Функция для расчета позиции нижнего лейбла относительно ширины верхнего
|
||||
const getSecondLabelPosition = (): number => {
|
||||
if (!ruLabelWidth) return 0;
|
||||
|
||||
switch (currentLabelAlign) {
|
||||
case "left":
|
||||
// Позиционируем относительно левого края верхнего текста
|
||||
return -ruLabelWidth / 2;
|
||||
case "center":
|
||||
// Центрируем относительно центра верхнего текста
|
||||
return 0;
|
||||
case "right":
|
||||
// Позиционируем относительно правого края верхнего текста
|
||||
return ruLabelWidth / 2;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
|
||||
// Функция для расчета anchor нижнего лейбла
|
||||
const getSecondLabelAnchor = (): number => {
|
||||
switch (currentLabelAlign) {
|
||||
case "left":
|
||||
return 0; // anchor.x = 0 (левый край)
|
||||
return 0;
|
||||
case "center":
|
||||
return 0.5; // anchor.x = 0.5 (центр)
|
||||
return 0.5;
|
||||
case "right":
|
||||
return 1; // anchor.x = 1 (правый край)
|
||||
return 1;
|
||||
default:
|
||||
return 0.5;
|
||||
}
|
||||
@@ -522,10 +491,6 @@ const StationLabel = observer(
|
||||
}
|
||||
);
|
||||
|
||||
// =========================================================================
|
||||
// Главный экспортируемый компонент: Станция
|
||||
// =========================================================================
|
||||
|
||||
export const Station = ({
|
||||
station,
|
||||
ruLabel,
|
||||
@@ -548,10 +513,9 @@ export const Station = ({
|
||||
|
||||
g.circle(coordinates.x * UP_SCALE, coordinates.y * UP_SCALE, radius);
|
||||
|
||||
// Change fill color when text is hovered
|
||||
if (isTextHovered) {
|
||||
g.fill({ color: 0x00aaff }); // Highlight color when hovered
|
||||
g.stroke({ color: 0xffffff, width: strokeWidth + 1 }); // Brighter outline when hovered
|
||||
g.fill({ color: 0x00aaff });
|
||||
g.stroke({ color: 0xffffff, width: strokeWidth + 1 });
|
||||
} else {
|
||||
g.fill({ color: PATH_COLOR });
|
||||
g.stroke({ color: BACKGROUND_COLOR, width: strokeWidth });
|
||||
|
||||
Reference in New Issue
Block a user