153be98780
Легенда и цифры на материале масштабируются от экрана игроков и не обрезаются жёлтой рамкой. Переключение сцен без записи project.json на диск. Co-authored-by: Cursor <cursoragent@cursor.com>
81 lines
2.5 KiB
TypeScript
81 lines
2.5 KiB
TypeScript
import React, { useRef } from 'react';
|
||
|
||
import type { MaterialLegend, MaterialsOverlayLayout } from '../../../shared/types';
|
||
import { DEFAULT_MATERIALS_OVERLAY_LAYOUT } from '../../../shared/types';
|
||
import { legendPanelVisualLayout } from '../../../shared/types/materialLegend';
|
||
import { useSceneOverlayView } from '../sceneOverlay/SceneOverlayViewContext';
|
||
|
||
import styles from './MaterialLegendPanel.module.css';
|
||
|
||
type Props = {
|
||
legend: MaterialLegend;
|
||
layout: MaterialsOverlayLayout;
|
||
editable?: boolean;
|
||
onLayoutChange?: (layout: MaterialsOverlayLayout) => void;
|
||
};
|
||
|
||
export function MaterialLegendPanel({
|
||
legend,
|
||
layout,
|
||
editable = false,
|
||
onLayoutChange,
|
||
}: Props) {
|
||
const host = useSceneOverlayView();
|
||
const view = host?.view ?? { w: 1, h: 1 };
|
||
const viewport = host?.viewport ?? { x: 0, y: 0, w: view.w, h: view.h };
|
||
const dragRef = useRef<{ startX: number; startY: number; origin: MaterialsOverlayLayout } | null>(
|
||
null,
|
||
);
|
||
const effective = layout ?? DEFAULT_MATERIALS_OVERLAY_LAYOUT;
|
||
const vis = legendPanelVisualLayout(effective, viewport);
|
||
|
||
if (!legend.enabled || legend.items.length === 0) return null;
|
||
|
||
return (
|
||
<div
|
||
className={styles.panel}
|
||
style={{
|
||
left: vis.left,
|
||
top: vis.top,
|
||
width: vis.width,
|
||
transform: `translate(-50%, -50%) scale(${String(vis.scale)})`,
|
||
}}
|
||
onPointerDown={(e) => {
|
||
if (!editable || !onLayoutChange) return;
|
||
e.stopPropagation();
|
||
(e.currentTarget as HTMLDivElement).setPointerCapture(e.pointerId);
|
||
dragRef.current = {
|
||
startX: e.clientX,
|
||
startY: e.clientY,
|
||
origin: { ...effective },
|
||
};
|
||
}}
|
||
onPointerMove={(e) => {
|
||
const d = dragRef.current;
|
||
if (!d || !onLayoutChange) return;
|
||
const dx = (e.clientX - d.startX) / Math.max(1, view.w);
|
||
const dy = (e.clientY - d.startY) / Math.max(1, view.h);
|
||
onLayoutChange({
|
||
...d.origin,
|
||
cx: d.origin.cx + dx,
|
||
cy: d.origin.cy + dy,
|
||
});
|
||
}}
|
||
onPointerUp={() => {
|
||
dragRef.current = null;
|
||
}}
|
||
>
|
||
<div className={styles.title}>Легенда</div>
|
||
{legend.items
|
||
.slice()
|
||
.sort((a, b) => a.number - b.number)
|
||
.map((item) => (
|
||
<div key={item.id} className={styles.item}>
|
||
<div className={styles.num}>{item.number}</div>
|
||
<div className={styles.text}>{item.text || '—'}</div>
|
||
</div>
|
||
))}
|
||
</div>
|
||
);
|
||
}
|