mirror of
https://github.com/azaion/ui.git
synced 2026-04-22 09:16:35 +00:00
add css files for components
This commit is contained in:
+1
-1
@@ -1,5 +1,5 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import AnnotationMain from './components/AnnotationMain';
|
import AnnotationMain from './components/AnnotationMain/AnnotationMain';
|
||||||
|
|
||||||
function App() {
|
function App() {
|
||||||
|
|
||||||
|
|||||||
@@ -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;
|
||||||
@@ -5,7 +5,7 @@ import React from 'react';
|
|||||||
function AnnotationList({ annotations, onAnnotationClick }) {
|
function AnnotationList({ annotations, onAnnotationClick }) {
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<h3>Annotations</h3>
|
<h3 className='menu-title'>Annotations</h3>
|
||||||
<ul>
|
<ul>
|
||||||
{annotations.map((annotation, index) => (
|
{annotations.map((annotation, index) => (
|
||||||
<li key={index} onClick={() => onAnnotationClick(annotation.time)}>
|
<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;
|
||||||
|
}
|
||||||
+17
-54
@@ -1,12 +1,13 @@
|
|||||||
import React, { useState, useEffect, useRef } from 'react';
|
import React, { useState, useEffect, useRef } from 'react';
|
||||||
import VideoPlayer from './VideoPlayer';
|
import VideoPlayer from '../VideoPlayer/VideoPlayer';
|
||||||
import AnnotationList from './AnnotationList';
|
import AnnotationList from '../AnnotationList';
|
||||||
import MediaList from './MediaList';
|
import MediaList from '../MediaList/MediaList';
|
||||||
import DetectionClassList from './DetectionClassList';
|
import DetectionClassList from '../DetectionClassList/DetectionClassList';
|
||||||
import CanvasEditor from './CanvasEditor';
|
import CanvasEditor from '../CanvasEditor/CanvasEditor';
|
||||||
import * as AnnotationService from '../services/AnnotationService';
|
import * as AnnotationService from '../../services/AnnotationService';
|
||||||
import AnnotationControls from './AnnotationControls';
|
import AnnotationControls from '../AnnotationControls/AnnotationControls';
|
||||||
import saveAnnotation from '../services/DataHandler';
|
import saveAnnotation from '../../services/DataHandler';
|
||||||
|
import './AnnotationMain.css';
|
||||||
|
|
||||||
function AnnotationMain() {
|
function AnnotationMain() {
|
||||||
const [files, setFiles] = useState([]);
|
const [files, setFiles] = useState([]);
|
||||||
@@ -169,60 +170,27 @@ function AnnotationMain() {
|
|||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div style={{ display: 'flex', height: '100vh', overflow: 'hidden' }}>
|
<div className='content-wrapper' >
|
||||||
<div style={{
|
<div className='side-menu left-menu' >
|
||||||
width: '15%',
|
|
||||||
height: '100%',
|
|
||||||
overflowY: 'auto',
|
|
||||||
borderRight: '1px solid #ccc',
|
|
||||||
display: 'flex',
|
|
||||||
flexDirection: 'column'
|
|
||||||
}}>
|
|
||||||
<MediaList
|
<MediaList
|
||||||
files={files}
|
files={files}
|
||||||
selectedFile={selectedFile}
|
selectedFile={selectedFile}
|
||||||
onFileSelect={handleFileSelect}
|
onFileSelect={handleFileSelect}
|
||||||
onDropNewFiles={handleDropNewFiles}
|
onDropNewFiles={handleDropNewFiles}
|
||||||
/>
|
/>
|
||||||
<div style={{ flexGrow: 1 }}>
|
|
||||||
<DetectionClassList onClassSelect={handleClassSelect} />
|
<DetectionClassList onClassSelect={handleClassSelect} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
|
|
||||||
<div style={{
|
<div className='player-wrapper' >
|
||||||
width: '70%',
|
|
||||||
height: '100%',
|
|
||||||
display: 'flex',
|
|
||||||
flexDirection: 'column'
|
|
||||||
}}>
|
|
||||||
{errorMessage && (
|
{errorMessage && (
|
||||||
<div style={{
|
<div className='error-message' >
|
||||||
backgroundColor: '#ffdddd',
|
|
||||||
color: '#d8000c',
|
|
||||||
padding: '6px',
|
|
||||||
margin: '6px',
|
|
||||||
borderRadius: '4px'
|
|
||||||
}}>
|
|
||||||
{errorMessage}
|
{errorMessage}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<div style={{
|
<div className='player-container' ref={containerRef}>
|
||||||
flex: 1,
|
<div className='player-block' >
|
||||||
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'
|
|
||||||
}}>
|
|
||||||
<VideoPlayer
|
<VideoPlayer
|
||||||
videoFile={selectedFile}
|
videoFile={selectedFile}
|
||||||
currentTime={currentTime}
|
currentTime={currentTime}
|
||||||
@@ -254,12 +222,7 @@ function AnnotationMain() {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div style={{
|
<div className='side-menu right-menu'>
|
||||||
width: '15%',
|
|
||||||
height: '100%',
|
|
||||||
overflowY: 'auto',
|
|
||||||
borderLeft: '1px solid #ccc'
|
|
||||||
}}>
|
|
||||||
<AnnotationList
|
<AnnotationList
|
||||||
annotations={Object.values(annotations)}
|
annotations={Object.values(annotations)}
|
||||||
onAnnotationClick={handleAnnotationClick}
|
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 React, { useRef, useState, useEffect } from 'react';
|
||||||
import * as AnnotationService from '../services/AnnotationService';
|
import * as AnnotationService from '../../services/AnnotationService';
|
||||||
import DetectionContainer from './DetectionContainer';
|
import DetectionContainer from '../DetectionContainer';
|
||||||
|
import './CanvasEditor.css';
|
||||||
|
|
||||||
function CanvasEditor({
|
function CanvasEditor({
|
||||||
width,
|
width,
|
||||||
@@ -240,23 +241,9 @@ function CanvasEditor({
|
|||||||
}, [isDragging, resizeData, mouseDownPos]);
|
}, [isDragging, resizeData, mouseDownPos]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div className='editor-container' >
|
||||||
style={{
|
<div className='canvas-editor'
|
||||||
position: 'absolute',
|
ref={containerRef}
|
||||||
width: '100%',
|
|
||||||
height: '100%',
|
|
||||||
top: 0,
|
|
||||||
left: 0,
|
|
||||||
pointerEvents: 'auto',
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<div ref={containerRef}
|
|
||||||
style={{
|
|
||||||
position: 'absolute',
|
|
||||||
width: '100%',
|
|
||||||
height: '100%',
|
|
||||||
pointerEvents: 'auto',
|
|
||||||
}}
|
|
||||||
onMouseDown={handleMouseDown}
|
onMouseDown={handleMouseDown}
|
||||||
onMouseMove={handleMouseMove}
|
onMouseMove={handleMouseMove}
|
||||||
onMouseUp={handleMouseUp}
|
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;
|
||||||
|
}
|
||||||
+6
-11
@@ -1,5 +1,6 @@
|
|||||||
import React, { useEffect, useState } from 'react';
|
import React, { useEffect, useState } from 'react';
|
||||||
import DetectionClass from '../models/DetectionClass';
|
import DetectionClass from '../../models/DetectionClass';
|
||||||
|
import './DetectionClassList.css';
|
||||||
|
|
||||||
function DetectionClassList({ onClassSelect }) {
|
function DetectionClassList({ onClassSelect }) {
|
||||||
const [detectionClasses, setDetectionClasses] = useState([]);
|
const [detectionClasses, setDetectionClasses] = useState([]);
|
||||||
@@ -83,9 +84,9 @@ function DetectionClassList({ onClassSelect }) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div className='class-list'>
|
||||||
<h3 style={{ marginTop: '15px', fontSize: '14px' }}>Classes</h3>
|
<h3 className='menu-title'>Classes</h3>
|
||||||
<ul style={{ listStyleType: 'none', padding: 0, margin: 0 }}>
|
<ul className='class-list-group' >
|
||||||
{detectionClasses.map((cls) => {
|
{detectionClasses.map((cls) => {
|
||||||
const backgroundColor = calculateColor(cls.Id);
|
const backgroundColor = calculateColor(cls.Id);
|
||||||
const darkBg = calculateColor(cls.Id, '0.8');
|
const darkBg = calculateColor(cls.Id, '0.8');
|
||||||
@@ -94,16 +95,10 @@ function DetectionClassList({ onClassSelect }) {
|
|||||||
return (
|
return (
|
||||||
<li
|
<li
|
||||||
key={cls.Id}
|
key={cls.Id}
|
||||||
|
className='class-list-item'
|
||||||
style={{
|
style={{
|
||||||
cursor: 'pointer',
|
|
||||||
padding: '8px',
|
|
||||||
border: `1px solid ${isSelected ? '#000' : '#eee'}`,
|
border: `1px solid ${isSelected ? '#000' : '#eee'}`,
|
||||||
backgroundColor: isSelected ? darkBg : backgroundColor,
|
backgroundColor: isSelected ? darkBg : backgroundColor,
|
||||||
fontSize: '14px',
|
|
||||||
marginBottom: '2px',
|
|
||||||
display: 'flex',
|
|
||||||
alignItems: 'center',
|
|
||||||
borderRadius: '4px',
|
|
||||||
}}
|
}}
|
||||||
onClick={() => handleClassClick(cls)}
|
onClick={() => handleClassClick(cls)}
|
||||||
>
|
>
|
||||||
@@ -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 React from 'react';
|
||||||
import { useDropzone } from 'react-dropzone';
|
import { useDropzone } from 'react-dropzone';
|
||||||
|
import './MediaList.css'
|
||||||
|
|
||||||
function MediaList({ files, selectedFile, onFileSelect, onDropNewFiles }) {
|
function MediaList({ files, selectedFile, onFileSelect, onDropNewFiles }) {
|
||||||
const { getRootProps, getInputProps, isDragActive } = useDropzone({
|
const { getRootProps, getInputProps, isDragActive } = useDropzone({
|
||||||
@@ -7,18 +8,15 @@ function MediaList({ files, selectedFile, onFileSelect, onDropNewFiles }) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div className='explorer'>
|
||||||
<h3 style={{ fontSize: '14px' }}>Files</h3>
|
<h3 className='menu-title' >Files</h3>
|
||||||
<ul style={{ listStyleType: 'none', padding: 0 }}>
|
<ul className='file-list-group' >
|
||||||
{files.map((file) => (
|
{files.map((file) => (
|
||||||
<li
|
<li
|
||||||
|
className='file-list-item'
|
||||||
key={file.name}
|
key={file.name}
|
||||||
style={{
|
style={{
|
||||||
cursor: 'pointer',
|
backgroundColor: selectedFile === file ? '#f0f0f0' : 'transparent'
|
||||||
padding: '6px',
|
|
||||||
borderBottom: '1px solid #eee',
|
|
||||||
backgroundColor: selectedFile === file ? '#f0f0f0' : 'transparent',
|
|
||||||
fontSize: '12px',
|
|
||||||
}}
|
}}
|
||||||
onClick={() => onFileSelect(file)}
|
onClick={() => onFileSelect(file)}
|
||||||
>
|
>
|
||||||
@@ -26,12 +24,12 @@ function MediaList({ files, selectedFile, onFileSelect, onDropNewFiles }) {
|
|||||||
</li>
|
</li>
|
||||||
))}
|
))}
|
||||||
</ul>
|
</ul>
|
||||||
<div {...getRootProps()} style={{ border: '2px dashed #ccc', padding: '8px', textAlign: 'center', marginTop: '10px', cursor: 'pointer' }}>
|
<div className='file-input-block' {...getRootProps()} >
|
||||||
<input {...getInputProps()} />
|
<input {...getInputProps()} />
|
||||||
{isDragActive ? (
|
{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>
|
||||||
</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 React, { useEffect, useRef, useState } from 'react';
|
||||||
|
import './VideoPlayer.css';
|
||||||
|
|
||||||
function VideoPlayer({ children, videoFile, currentTime, videoRef, isPlaying, onSizeChanged, onSetCurrentTime }) {
|
function VideoPlayer({ children, videoFile, currentTime, videoRef, isPlaying, onSizeChanged, onSetCurrentTime }) {
|
||||||
const containerRef = useRef(null);
|
const containerRef = useRef(null);
|
||||||
@@ -131,58 +132,15 @@ function VideoPlayer({ children, videoFile, currentTime, videoRef, isPlaying, on
|
|||||||
}, [onSetCurrentTime, isPlaying]);
|
}, [onSetCurrentTime, isPlaying]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div className='player' ref={containerRef} >
|
||||||
ref={containerRef}
|
<video className='video' ref={videoRef} preload="auto" playsInline muted />
|
||||||
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
|
|
||||||
/>
|
|
||||||
{playbackError && (
|
{playbackError && (
|
||||||
<div style={{
|
<div className='player-error' >
|
||||||
position: 'absolute',
|
|
||||||
top: '10px',
|
|
||||||
left: '10px',
|
|
||||||
background: 'rgba(255,0,0,0.7)',
|
|
||||||
color: 'white',
|
|
||||||
padding: '5px',
|
|
||||||
borderRadius: '3px',
|
|
||||||
fontSize: '12px',
|
|
||||||
zIndex: 10
|
|
||||||
}}>
|
|
||||||
{playbackError}
|
{playbackError}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
<div
|
<div className='player-item'>
|
||||||
style={{
|
|
||||||
position: 'absolute',
|
|
||||||
top: 0,
|
|
||||||
left: 0,
|
|
||||||
width: '100%',
|
|
||||||
height: '100%',
|
|
||||||
pointerEvents: 'auto'
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{children}
|
{children}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
Reference in New Issue
Block a user