add css files for components

This commit is contained in:
Armen Rohalov
2025-03-26 20:30:27 +02:00
parent e18157648c
commit 9e6596f1e0
15 changed files with 301 additions and 253 deletions
@@ -0,0 +1,54 @@
.content-wrapper {
display: flex;
height: 100vh;
overflow: hidden;
}
.side-menu {
display: flex;
flex-direction: column;
width: 15%;
height: 100%;
}
.left-menu{
border-right: 1px solid #ccc;
}
.right-menu{
overflow-y: auto;
border-left: 1px solid #ccc;
}
.player-wrapper {
width: 70%;
height: 100%;
display: flex;
flex-direction: column;
}
.error-message {
background: #ffdddd;
color: #d8000c;
padding: 6px;
margin: 6px;
border-radius: 4px;
}
.player-container {
display: flex;
flex: 1;
flex-direction: column;
position: relative;
min-height: 0;
}
.player-block {
display: flex;
flex: 1;
position: relative;
background: #000;
justify-content: center;
align-items: center;
overflow: hidden;
}
@@ -0,0 +1,235 @@
import React, { useState, useEffect, useRef } from 'react';
import VideoPlayer from '../VideoPlayer/VideoPlayer';
import AnnotationList from '../AnnotationList';
import MediaList from '../MediaList/MediaList';
import DetectionClassList from '../DetectionClassList/DetectionClassList';
import CanvasEditor from '../CanvasEditor/CanvasEditor';
import * as AnnotationService from '../../services/AnnotationService';
import AnnotationControls from '../AnnotationControls/AnnotationControls';
import saveAnnotation from '../../services/DataHandler';
import './AnnotationMain.css';
function AnnotationMain() {
const [files, setFiles] = useState([]);
const [selectedFile, setSelectedFile] = useState(null);
const [annotations, setAnnotations] = useState({});
const [currentTime, setCurrentTime] = useState(0);
const [selectedClass, setSelectedClass] = useState(null);
const [detections, setDetections] = useState([]);
const [selectedDetectionIndices, setSelectedDetectionIndices] = useState([]);
const [isPlaying, setIsPlaying] = useState(false);
const [videoWidth, setVideoWidth] = useState(640);
const [videoHeight, setVideoHeight] = useState(480);
const [errorMessage, setErrorMessage] = useState("");
const videoRef = useRef(null);
const containerRef = useRef(null);
useEffect(() => {
const initialFiles = [];
setFiles(initialFiles);
}, []);
const handleFileSelect = (file) => {
if (!file) return;
setSelectedFile(file);
setAnnotations({});
setDetections([]);
setSelectedDetectionIndices([]);
setCurrentTime(0);
setIsPlaying(false);
setErrorMessage("");
};
const handleDropNewFiles = (newFiles) => {
if (!newFiles || newFiles.length === 0) return;
const validFiles = [...newFiles];
setFiles(prevFiles => [...prevFiles, ...validFiles]);
if (!selectedFile && validFiles.length > 0) {
setSelectedFile(validFiles[0]);
}
};
const handleAnnotationSave = () => {
if (!videoRef.current) return;
if (!detections || detections.length === 0) {
setErrorMessage("Please create at least one detection before saving");
return;
}
const safeContainerRef = {
current: {
offsetWidth: videoWidth,
offsetHeight: videoHeight
}
};
const imageData = AnnotationService.createAnnotationImage(
videoRef,
detections,
safeContainerRef
);
if (imageData) {
setAnnotations(prevAnnotations => {
const newAnnotations = {
...prevAnnotations,
[currentTime]: { time: currentTime, annotations: detections, imageData }
};
saveAnnotation(currentTime, detections, imageData);
setErrorMessage("");
return newAnnotations;
});
}
};
const handleDelete = () => {
if (selectedDetectionIndices.length === 0) {
setErrorMessage("Please select a detection to delete");
return;
}
const newDetections = detections.filter((_, index) => !selectedDetectionIndices.includes(index));
setDetections(newDetections);
setSelectedDetectionIndices([]);
setErrorMessage("");
};
const handleAnnotationClick = (time) => {
setCurrentTime(time);
const annotation = annotations[time];
if (annotation) {
setDetections(annotation.annotations || []);
setSelectedDetectionIndices([]);
}
if (videoRef.current) {
videoRef.current.currentTime = time;
}
setIsPlaying(false);
};
const handleClassSelect = (cls) => {
setSelectedClass(cls);
};
const handleDetectionsChange = (newDetections) => {
setDetections(newDetections);
};
const handleSelectionChange = (newSelection) => {
setSelectedDetectionIndices(newSelection);
};
const handlePlayPause = () => {
setIsPlaying(prev => !prev);
};
const handleFrameForward = () => {
if (videoRef.current) {
videoRef.current.currentTime += 1 / 30;
setCurrentTime(videoRef.current.currentTime);
}
};
const handleFrameBackward = () => {
if (videoRef.current) {
videoRef.current.currentTime -= 1 / 30;
setCurrentTime(videoRef.current.currentTime);
}
};
const handleSizeChanged = (width, height) => {
setVideoWidth(width);
setVideoHeight(height);
};
const handleSetCurrentTime = (time) => {
setCurrentTime(time);
};
// Toggle debug mode with Ctrl+D
useEffect(() => {
const handleKeyDown = (e) => {
switch (e.key) {
case 'Space':
}
if (e.ctrlKey && e.key === 'd') {
e.preventDefault();
}
};
window.addEventListener('keydown', handleKeyDown);
}, []);
return (
<div className='content-wrapper' >
<div className='side-menu left-menu' >
<MediaList
files={files}
selectedFile={selectedFile}
onFileSelect={handleFileSelect}
onDropNewFiles={handleDropNewFiles}
/>
<DetectionClassList onClassSelect={handleClassSelect} />
</div>
<div className='player-wrapper' >
{errorMessage && (
<div className='error-message' >
{errorMessage}
</div>
)}
<div className='player-container' ref={containerRef}>
<div className='player-block' >
<VideoPlayer
videoFile={selectedFile}
currentTime={currentTime}
videoRef={videoRef}
isPlaying={isPlaying}
onSizeChanged={handleSizeChanged}
onSetCurrentTime={handleSetCurrentTime}
>
<CanvasEditor
width={videoWidth}
height={videoHeight}
detections={detections}
selectedDetectionIndices={selectedDetectionIndices}
onDetectionsChange={handleDetectionsChange}
onSelectionChange={handleSelectionChange}
detectionClass={selectedClass}
/>
</VideoPlayer>
</div>
<AnnotationControls
onFrameBackward={handleFrameBackward}
onPlayPause={handlePlayPause}
isPlaying={isPlaying}
onFrameForward={handleFrameForward}
onSaveAnnotation={handleAnnotationSave}
onDelete={handleDelete}
/>
</div>
</div>
<div className='side-menu right-menu'>
<AnnotationList
annotations={Object.values(annotations)}
onAnnotationClick={handleAnnotationClick}
/>
</div>
</div>
);
}
export default AnnotationMain;