Добавлена иконка на боковой тултип для отображения/скрытия подсветки датчиков на модели
This commit is contained in:
207
frontend/components/model/SceneToolbar.tsx — копия
Normal file
207
frontend/components/model/SceneToolbar.tsx — копия
Normal file
@@ -0,0 +1,207 @@
|
||||
import React, { useState } from 'react';
|
||||
import Image from 'next/image';
|
||||
import useNavigationStore from '@/app/store/navigationStore';
|
||||
import type { Zone } from '@/app/types';
|
||||
|
||||
interface ToolbarButton {
|
||||
icon: string;
|
||||
label: string;
|
||||
onClick: () => void;
|
||||
onMouseDown?: () => void;
|
||||
onMouseUp?: () => void;
|
||||
active?: boolean;
|
||||
children?: ToolbarButton[];
|
||||
}
|
||||
|
||||
interface SceneToolbarProps {
|
||||
onZoomIn?: () => void;
|
||||
onZoomOut?: () => void;
|
||||
onTopView?: () => void;
|
||||
onPan?: () => void;
|
||||
onSelectModel?: (modelPath: string) => void;
|
||||
panActive?: boolean;
|
||||
navMenuActive?: boolean;
|
||||
}
|
||||
|
||||
const SceneToolbar: React.FC<SceneToolbarProps> = ({
|
||||
onZoomIn,
|
||||
onZoomOut,
|
||||
onTopView,
|
||||
onPan,
|
||||
onSelectModel,
|
||||
panActive = false,
|
||||
navMenuActive = false,
|
||||
}) => {
|
||||
const [isZoomOpen, setIsZoomOpen] = useState(false);
|
||||
const { showMonitoring, openMonitoring, closeMonitoring, currentZones, loadZones, currentObject } = useNavigationStore();
|
||||
|
||||
const handleToggleNavMenu = () => {
|
||||
if (showMonitoring) {
|
||||
closeMonitoring();
|
||||
} else {
|
||||
openMonitoring();
|
||||
}
|
||||
};
|
||||
|
||||
const handleHomeClick = async () => {
|
||||
if (!onSelectModel) return;
|
||||
|
||||
try {
|
||||
let zones: Zone[] = Array.isArray(currentZones) ? currentZones : [];
|
||||
|
||||
// Если зоны ещё не загружены, откройте Monitoring и загрузите зоны для текущего объекта
|
||||
if ((!zones || zones.length === 0) && currentObject?.id) {
|
||||
if (!showMonitoring) {
|
||||
openMonitoring();
|
||||
}
|
||||
await loadZones(currentObject.id);
|
||||
zones = useNavigationStore.getState().currentZones || [];
|
||||
}
|
||||
|
||||
if (!Array.isArray(zones) || zones.length === 0) {
|
||||
console.warn('No zones available to select a model from.');
|
||||
return;
|
||||
}
|
||||
|
||||
const sorted = zones.slice().sort((a: Zone, b: Zone) => {
|
||||
const oa = typeof a.order === 'number' ? a.order : 0;
|
||||
const ob = typeof b.order === 'number' ? b.order : 0;
|
||||
if (oa !== ob) return oa - ob;
|
||||
return (a.name || '').localeCompare(b.name || '');
|
||||
});
|
||||
|
||||
const top = sorted[0];
|
||||
let chosenPath: string | null = top?.model_path && String(top.model_path).trim() ? top.model_path! : null;
|
||||
if (!chosenPath) {
|
||||
const nextWithModel = sorted.find((z) => z.model_path && String(z.model_path).trim());
|
||||
chosenPath = nextWithModel?.model_path ?? null;
|
||||
}
|
||||
|
||||
if (chosenPath) {
|
||||
onSelectModel(chosenPath);
|
||||
} else {
|
||||
console.warn('No zone has a valid model_path to open.');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error selecting top zone model:', error);
|
||||
}
|
||||
};
|
||||
|
||||
const defaultButtons: ToolbarButton[] = [
|
||||
{
|
||||
icon: '/icons/Zoom.png',
|
||||
label: 'Zoom',
|
||||
onClick: () => setIsZoomOpen(!isZoomOpen),
|
||||
active: isZoomOpen,
|
||||
children: [
|
||||
{
|
||||
icon: '/icons/plus.svg',
|
||||
label: 'Zoom In',
|
||||
onClick: onZoomIn || (() => {}),
|
||||
},
|
||||
{
|
||||
icon: '/icons/minus.svg',
|
||||
label: 'Zoom Out',
|
||||
onClick: onZoomOut || (() => {}),
|
||||
},
|
||||
]
|
||||
},
|
||||
{
|
||||
icon: '/icons/Video.png',
|
||||
label: "Top View",
|
||||
onClick: onTopView || (() => console.log('Top View')),
|
||||
},
|
||||
{
|
||||
icon: '/icons/Pointer.png',
|
||||
label: 'Pan',
|
||||
onClick: onPan || (() => console.log('Pan')),
|
||||
active: panActive,
|
||||
},
|
||||
{
|
||||
icon: '/icons/Warehouse.png',
|
||||
label: 'Home',
|
||||
onClick: handleHomeClick,
|
||||
},
|
||||
{
|
||||
icon: '/icons/Layers.png',
|
||||
label: 'Levels',
|
||||
onClick: handleToggleNavMenu,
|
||||
active: navMenuActive,
|
||||
},
|
||||
];
|
||||
|
||||
|
||||
return (
|
||||
<div className="fixed right-5 top-1/2 transform -translate-y-1/2 z-50">
|
||||
<div className="flex flex-col gap-0">
|
||||
<div
|
||||
className="flex flex-col items-center gap-2 py-4 bg-[#161824] rounded-[15px] border border-white/10 shadow-[0_8px_32px_rgba(0,0,0,0.3)]"
|
||||
style={{ minHeight: '320px' }}
|
||||
>
|
||||
{defaultButtons.map((button, index) => (
|
||||
<div key={index} className="flex flex-col items-center gap-2">
|
||||
<button
|
||||
onClick={button.onClick}
|
||||
className={`
|
||||
relative group flex items-center justify-center w-16 h-12 rounded-lg transition-all duration-200
|
||||
hover:bg-blue-600/20 hover:scale-110 hover:shadow-lg
|
||||
focus:outline-none focus:ring-2 focus:ring-blue-500/50
|
||||
${button.active
|
||||
? 'bg-blue-600/30 text-blue-400 shadow-md'
|
||||
: 'bg-transparent text-gray-300 hover:text-blue-400'
|
||||
}
|
||||
`}
|
||||
title={button.label}
|
||||
>
|
||||
<Image
|
||||
src={button.icon}
|
||||
alt={button.label}
|
||||
width={20}
|
||||
height={20}
|
||||
className="w-5 h-5 transition-transform duration-200 group-hover:scale-110"
|
||||
/>
|
||||
<div className="absolute right-full mr-3 top-1/2 transform -translate-y-1/2
|
||||
opacity-0 group-hover:opacity-100 transition-opacity duration-200
|
||||
pointer-events-none z-60">
|
||||
<div className="bg-gray-900 text-white text-xs px-2 py-1 rounded
|
||||
whitespace-nowrap shadow-lg border border-gray-700">
|
||||
{button.label}
|
||||
</div>
|
||||
<div className="absolute left-full top-1/2 transform -translate-y-1/2
|
||||
w-0 h-0 border-t-4 border-t-transparent
|
||||
border-b-4 border-b-transparent
|
||||
border-l-4 border-l-gray-900">
|
||||
</div>
|
||||
</div>
|
||||
</button>
|
||||
{button.active && button.children && (
|
||||
<div className="flex flex-col gap-2 mt-2">
|
||||
{button.children.map((childButton, childIndex) => (
|
||||
<button
|
||||
key={childIndex}
|
||||
onClick={childButton.onClick}
|
||||
onMouseDown={childButton.onMouseDown}
|
||||
onMouseUp={childButton.onMouseUp}
|
||||
className="relative group flex items-center justify-center w-12 h-10 bg-gray-800/50 rounded-md transition-all duration-200 hover:bg-blue-600/30"
|
||||
title={childButton.label}
|
||||
>
|
||||
<Image
|
||||
src={childButton.icon}
|
||||
alt={childButton.label}
|
||||
width={16}
|
||||
height={16}
|
||||
className="w-4 h-4"
|
||||
/>
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default SceneToolbar;
|
||||
Reference in New Issue
Block a user