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
+1 -1
View File
@@ -1,5 +1,5 @@
import React from 'react';
import AnnotationMain from './components/AnnotationMain';
import AnnotationMain from './components/AnnotationMain/AnnotationMain';
function App() {
-91
View File
@@ -1,91 +0,0 @@
import React from 'react';
function AnnotationControls({ onFrameBackward, onPlayPause, isPlaying, onFrameForward, onSaveAnnotation, onDelete }) {
return (
<div style={{
display: 'flex',
justifyContent: 'center',
position: 'relative',
zIndex: 1,
padding: '10px',
background: '#f5f5f5',
borderRadius: '4px',
marginTop: '10px'
}}>
<button
style={{
padding: '10px 20px',
fontSize: '16px',
margin: '0 5px',
background: '#e0e0e0',
border: '1px solid #ccc',
borderRadius: '4px',
cursor: 'pointer'
}}
onClick={onFrameBackward}
title="Previous Frame"
>
</button>
<button
style={{
padding: '10px 20px',
fontSize: '16px',
margin: '0 5px',
background: isPlaying ? '#ff8a8a' : '#8aff8a',
border: '1px solid #ccc',
borderRadius: '4px',
cursor: 'pointer'
}}
onClick={onPlayPause}
>
{isPlaying ? '⏸️ Pause' : '▶️ Play'}
</button>
<button
style={{
padding: '10px 20px',
fontSize: '16px',
margin: '0 5px',
background: '#e0e0e0',
border: '1px solid #ccc',
borderRadius: '4px',
cursor: 'pointer'
}}
onClick={onFrameForward}
title="Next Frame"
>
</button>
<button
style={{
padding: '10px 20px',
fontSize: '16px',
margin: '0 5px',
background: '#8ad4ff',
border: '1px solid #ccc',
borderRadius: '4px',
cursor: 'pointer'
}}
onClick={onSaveAnnotation}
>
💾 Save
</button>
<button
style={{
padding: '10px 20px',
fontSize: '16px',
margin: '0 5px',
background: '#ffb38a',
border: '1px solid #ccc',
borderRadius: '4px',
cursor: 'pointer'
}}
onClick={onDelete}
>
🗑 Delete
</button>
</div>
);
}
export default AnnotationControls;
@@ -0,0 +1,39 @@
.buttons-group {
display: flex;
justify-content: center;
position: relative;
z-index: 1;
padding: 10px;
background: #f5f5f5;
border-radius: 4px;
margin-top: 10px;
}
.control-btn {
padding: 10px 20px;
font-size: 16px;
margin: 0 5px;
border: 1px solid #ccc;
border-radius: 4px;
cursor: pointer;
}
.play-btn{
background: #8aff8a;
}
.pause-btn{
background: #ff8a8a;
}
.arrow-btn{
background: #e0e0e0;
}
.save-btn{
background: #8ad4ff;
}
.delete-btn{
background: #ffb38a;
}
@@ -0,0 +1,43 @@
import React from 'react';
import './AnnotationControls.css';
function AnnotationControls({ onFrameBackward, onPlayPause, isPlaying, onFrameForward, onSaveAnnotation, onDelete }) {
return (
<div className='buttons-group' >
<button
className='control-btn arrow-btn'
onClick={onFrameBackward}
title="Previous Frame"
>
</button>
<button
className={isPlaying ? 'control-btn pause-btn': 'control-btn play-btn'}
onClick={onPlayPause}
>
{isPlaying ? '⏸️ Pause' : '▶️ Play'}
</button>
<button
className='control-btn arrow-btn'
onClick={onFrameForward}
title="Next Frame"
>
</button>
<button
className='control-btn save-btn'
onClick={onSaveAnnotation}
>
💾 Save
</button>
<button
className='control-btn delete-btn'
onClick={onDelete}
>
🗑 Delete
</button>
</div>
);
}
export default AnnotationControls;
+1 -1
View File
@@ -5,7 +5,7 @@ import React from 'react';
function AnnotationList({ annotations, onAnnotationClick }) {
return (
<div>
<h3>Annotations</h3>
<h3 className='menu-title'>Annotations</h3>
<ul>
{annotations.map((annotation, index) => (
<li key={index} onClick={() => onAnnotationClick(annotation.time)}>
@@ -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;
}
@@ -1,12 +1,13 @@
import React, { useState, useEffect, useRef } from 'react';
import VideoPlayer from './VideoPlayer';
import AnnotationList from './AnnotationList';
import MediaList from './MediaList';
import DetectionClassList from './DetectionClassList';
import CanvasEditor from './CanvasEditor';
import * as AnnotationService from '../services/AnnotationService';
import AnnotationControls from './AnnotationControls';
import saveAnnotation from '../services/DataHandler';
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([]);
@@ -169,60 +170,27 @@ function AnnotationMain() {
}, []);
return (
<div style={{ display: 'flex', height: '100vh', overflow: 'hidden' }}>
<div style={{
width: '15%',
height: '100%',
overflowY: 'auto',
borderRight: '1px solid #ccc',
display: 'flex',
flexDirection: 'column'
}}>
<div className='content-wrapper' >
<div className='side-menu left-menu' >
<MediaList
files={files}
selectedFile={selectedFile}
onFileSelect={handleFileSelect}
onDropNewFiles={handleDropNewFiles}
/>
<div style={{ flexGrow: 1 }}>
<DetectionClassList onClassSelect={handleClassSelect} />
</div>
</div>
<div style={{
width: '70%',
height: '100%',
display: 'flex',
flexDirection: 'column'
}}>
<div className='player-wrapper' >
{errorMessage && (
<div style={{
backgroundColor: '#ffdddd',
color: '#d8000c',
padding: '6px',
margin: '6px',
borderRadius: '4px'
}}>
<div className='error-message' >
{errorMessage}
</div>
)}
<div style={{
flex: 1,
display: 'flex',
flexDirection: 'column',
position: 'relative',
minHeight: 0
}} ref={containerRef}>
<div style={{
flex: 1,
position: 'relative',
backgroundColor: '#000',
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
overflow: 'hidden'
}}>
<div className='player-container' ref={containerRef}>
<div className='player-block' >
<VideoPlayer
videoFile={selectedFile}
currentTime={currentTime}
@@ -254,12 +222,7 @@ function AnnotationMain() {
</div>
</div>
<div style={{
width: '15%',
height: '100%',
overflowY: 'auto',
borderLeft: '1px solid #ccc'
}}>
<div className='side-menu right-menu'>
<AnnotationList
annotations={Object.values(annotations)}
onAnnotationClick={handleAnnotationClick}
@@ -0,0 +1,15 @@
.editor-container {
position: absolute;
width: 100%;
height: 100%;
top: 0;
left: 0;
pointer-events: auto;
}
.canvas-editor {
position: absolute;
width: 100%;
height: 100%;
pointer-events: auto;
}
@@ -1,6 +1,7 @@
import React, { useRef, useState, useEffect } from 'react';
import * as AnnotationService from '../services/AnnotationService';
import DetectionContainer from './DetectionContainer';
import * as AnnotationService from '../../services/AnnotationService';
import DetectionContainer from '../DetectionContainer';
import './CanvasEditor.css';
function CanvasEditor({
width,
@@ -240,23 +241,9 @@ function CanvasEditor({
}, [isDragging, resizeData, mouseDownPos]);
return (
<div
style={{
position: 'absolute',
width: '100%',
height: '100%',
top: 0,
left: 0,
pointerEvents: 'auto',
}}
>
<div ref={containerRef}
style={{
position: 'absolute',
width: '100%',
height: '100%',
pointerEvents: 'auto',
}}
<div className='editor-container' >
<div className='canvas-editor'
ref={containerRef}
onMouseDown={handleMouseDown}
onMouseMove={handleMouseMove}
onMouseUp={handleMouseUp}
@@ -0,0 +1,19 @@
.class-list {
flex-grow: 1;
}
.class-list-group {
list-style-type: none;
padding: 0;
margin: 0;
}
.class-list-item {
display: flex;
align-items: center;
cursor: pointer;
padding: 8px;
font-size: 14px;
margin-bottom: 2px;
border-radius: 4px;
}
@@ -1,5 +1,6 @@
import React, { useEffect, useState } from 'react';
import DetectionClass from '../models/DetectionClass';
import DetectionClass from '../../models/DetectionClass';
import './DetectionClassList.css';
function DetectionClassList({ onClassSelect }) {
const [detectionClasses, setDetectionClasses] = useState([]);
@@ -83,9 +84,9 @@ function DetectionClassList({ onClassSelect }) {
};
return (
<div>
<h3 style={{ marginTop: '15px', fontSize: '14px' }}>Classes</h3>
<ul style={{ listStyleType: 'none', padding: 0, margin: 0 }}>
<div className='class-list'>
<h3 className='menu-title'>Classes</h3>
<ul className='class-list-group' >
{detectionClasses.map((cls) => {
const backgroundColor = calculateColor(cls.Id);
const darkBg = calculateColor(cls.Id, '0.8');
@@ -94,16 +95,10 @@ function DetectionClassList({ onClassSelect }) {
return (
<li
key={cls.Id}
className='class-list-item'
style={{
cursor: 'pointer',
padding: '8px',
border: `1px solid ${isSelected ? '#000' : '#eee'}`,
backgroundColor: isSelected ? darkBg : backgroundColor,
fontSize: '14px',
marginBottom: '2px',
display: 'flex',
alignItems: 'center',
borderRadius: '4px',
}}
onClick={() => handleClassClick(cls)}
>
+28
View File
@@ -0,0 +1,28 @@
.menu-title {
font-size: 14px;
margin: 15px 0;
}
.file-list-group {
padding: 0;
list-style-type: none;
}
.file-list-item {
cursor: pointer;
padding: 6px;
border-bottom: 1px solid #eee;
font-size: 12px;
}
.label {
font-size: 12px;
}
.file-input-block {
border: 2px dashed #ccc;
padding: 8px;
text-align: center;
margin-top: 10px;
cursor: pointer;
}
@@ -1,5 +1,6 @@
import React from 'react';
import { useDropzone } from 'react-dropzone';
import './MediaList.css'
function MediaList({ files, selectedFile, onFileSelect, onDropNewFiles }) {
const { getRootProps, getInputProps, isDragActive } = useDropzone({
@@ -7,18 +8,15 @@ function MediaList({ files, selectedFile, onFileSelect, onDropNewFiles }) {
});
return (
<div>
<h3 style={{ fontSize: '14px' }}>Files</h3>
<ul style={{ listStyleType: 'none', padding: 0 }}>
<div className='explorer'>
<h3 className='menu-title' >Files</h3>
<ul className='file-list-group' >
{files.map((file) => (
<li
className='file-list-item'
key={file.name}
style={{
cursor: 'pointer',
padding: '6px',
borderBottom: '1px solid #eee',
backgroundColor: selectedFile === file ? '#f0f0f0' : 'transparent',
fontSize: '12px',
backgroundColor: selectedFile === file ? '#f0f0f0' : 'transparent'
}}
onClick={() => onFileSelect(file)}
>
@@ -26,12 +24,12 @@ function MediaList({ files, selectedFile, onFileSelect, onDropNewFiles }) {
</li>
))}
</ul>
<div {...getRootProps()} style={{ border: '2px dashed #ccc', padding: '8px', textAlign: 'center', marginTop: '10px', cursor: 'pointer' }}>
<div className='file-input-block' {...getRootProps()} >
<input {...getInputProps()} />
{isDragActive ? (
<p style={{ fontSize: '12px' }}>Drop here</p>
<p className='label' >Drop here</p>
) : (
<p style={{ fontSize: '12px' }}>Drag new files</p>
<p className='label' >Drag new files</p>
)}
</div>
</div>
@@ -0,0 +1,40 @@
.player {
position: relative;
width: 100%;
height: 100%;
display: flex;
align-items: center;
justify-content: center;
background: #000;
overflow: hidden;
}
.video {
width: 100%;
height: auto;
max-height: 100%;
display: block;
object-fit: contain;
pointer-events: none;
}
.player-error {
position: absolute;
top: 10px;
left: 10px;
background: rgba(255, 0, 0, 0.7);
color: white;
padding: 5px;
border-radius: 3px;
font-size: 12px;
z-index: 10;
}
.player-item{
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
pointer-events: auto;
}
@@ -1,4 +1,5 @@
import React, { useEffect, useRef, useState } from 'react';
import './VideoPlayer.css';
function VideoPlayer({ children, videoFile, currentTime, videoRef, isPlaying, onSizeChanged, onSetCurrentTime }) {
const containerRef = useRef(null);
@@ -131,58 +132,15 @@ function VideoPlayer({ children, videoFile, currentTime, videoRef, isPlaying, on
}, [onSetCurrentTime, isPlaying]);
return (
<div
ref={containerRef}
style={{
position: 'relative',
width: '100%',
height: '100%',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
backgroundColor: '#000',
overflow: 'hidden'
}}
>
<video
ref={videoRef}
style={{
width: '100%',
height: 'auto',
maxHeight: '100%',
display: 'block',
objectFit: 'contain',
pointerEvents: 'none'
}}
preload="auto"
playsInline
muted
/>
<div className='player' ref={containerRef} >
<video className='video' ref={videoRef} preload="auto" playsInline muted />
{playbackError && (
<div style={{
position: 'absolute',
top: '10px',
left: '10px',
background: 'rgba(255,0,0,0.7)',
color: 'white',
padding: '5px',
borderRadius: '3px',
fontSize: '12px',
zIndex: 10
}}>
<div className='player-error' >
{playbackError}
</div>
)}
<div
style={{
position: 'absolute',
top: 0,
left: 0,
width: '100%',
height: '100%',
pointerEvents: 'auto'
}}
>
<div className='player-item'>
{children}
</div>
</div>