fix deic issues: #61, #62, #63, #64, #65

- keep icons in popup menu but do not allow to click (rather than disapear) #61
- New item: When activating Play Preview #62
- dragable ball on mobile #63
- mobile: dragging the popup menu small issue #64
- mobile vertical direction #65
This commit is contained in:
Yiannis Christodoulou 2025-06-04 15:02:51 +03:00
parent b6a197a00f
commit 58c0247f6b
5 changed files with 243 additions and 95 deletions

View File

@ -1,4 +1,5 @@
import '../styles/EditingTools.css'; import '../styles/EditingTools.css';
import { useEffect, useState } from 'react';
interface EditingToolsProps { interface EditingToolsProps {
onSplit: () => void; onSplit: () => void;
@ -29,6 +30,18 @@ const EditingTools = ({
isPlaying = false, isPlaying = false,
isPlayingSegments = false, isPlayingSegments = false,
}: EditingToolsProps) => { }: EditingToolsProps) => {
const [isSmallScreen, setIsSmallScreen] = useState(false);
useEffect(() => {
const checkScreenSize = () => {
setIsSmallScreen(window.innerWidth <= 640);
};
checkScreenSize();
window.addEventListener('resize', checkScreenSize);
return () => window.removeEventListener('resize', checkScreenSize);
}, []);
// Handle play button click with iOS fix // Handle play button click with iOS fix
const handlePlay = () => { const handlePlay = () => {
// Ensure lastSeekedPosition is used when play is clicked // Ensure lastSeekedPosition is used when play is clicked
@ -47,7 +60,7 @@ const EditingTools = ({
<div className="button-group play-buttons-group"> <div className="button-group play-buttons-group">
{/* Play Segments button */} {/* Play Segments button */}
<button <button
className="button segments-button" className={`button segments-button ${isPlayingSegments && isSmallScreen ? 'highlighted-stop' : ''}`}
onClick={onPlaySegments} onClick={onPlaySegments}
data-tooltip={isPlayingSegments ? "Stop segments playback" : "Play all segments continuously until the end"} data-tooltip={isPlayingSegments ? "Stop segments playback" : "Play all segments continuously until the end"}
style={{ fontSize: '0.875rem' }} style={{ fontSize: '0.875rem' }}
@ -104,12 +117,13 @@ const EditingTools = ({
</button> */} </button> */}
{/* Standard Play button (only shown when not in preview mode or segments playback) */} {/* Standard Play button (only shown when not in preview mode or segments playback) */}
{!isPreviewMode && !isPlayingSegments && ( {!isPreviewMode && (!isPlayingSegments || !isSmallScreen) && (
<button <button
className="button play-button" className={`button play-button ${isPlayingSegments ? 'greyed-out' : ''}`}
onClick={handlePlay} onClick={handlePlay}
data-tooltip={isPlaying ? "Pause video" : "Play full video"} data-tooltip={isPlaying ? "Pause video" : "Play full video"}
style={{ fontSize: '0.875rem' }} style={{ fontSize: '0.875rem' }}
disabled={isPlayingSegments}
> >
{isPlaying ? ( {isPlaying ? (
<> <>
@ -135,7 +149,7 @@ const EditingTools = ({
)} )}
{/* Segments Playback message (replaces play button during segments playback) */} {/* Segments Playback message (replaces play button during segments playback) */}
{isPlayingSegments && ( {isPlayingSegments && !isSmallScreen && (
<div className="segments-playback-message"> <div className="segments-playback-message">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"> <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
<circle cx="12" cy="12" r="10" /> <circle cx="12" cy="12" r="10" />

View File

@ -53,22 +53,47 @@ interface TimelineControlsProps {
} }
// Function to calculate and constrain tooltip position to keep it on screen // Function to calculate and constrain tooltip position to keep it on screen
// Uses smooth transitions instead of hard breakpoints to eliminate jumping
const constrainTooltipPosition = (positionPercent: number) => { const constrainTooltipPosition = (positionPercent: number) => {
// Default position logic (centered) // Smooth transition zones instead of hard breakpoints
let leftValue = `${positionPercent}%`; const leftTransitionStart = 0;
let transform = 'translateX(-50%)'; const leftTransitionEnd = 25;
const rightTransitionStart = 75;
const rightTransitionEnd = 100;
// Near left edge (first 17%) let leftValue: string;
if (positionPercent < 17) { let transform: string;
// Position the left edge of tooltip at 0%, no transform
if (positionPercent <= leftTransitionEnd) {
// Left side: smooth transition from center to left-aligned
if (positionPercent <= leftTransitionStart) {
// Fully left-aligned
leftValue = '0%'; leftValue = '0%';
transform = 'none'; transform = 'none';
} else {
// Smooth transition zone
const transitionProgress = (positionPercent - leftTransitionStart) / (leftTransitionEnd - leftTransitionStart);
const translateAmount = -50 * transitionProgress; // Gradually reduce from 0% to -50%
leftValue = `${positionPercent}%`;
transform = `translateX(${translateAmount}%)`;
} }
// Near right edge (last 17%) } else if (positionPercent >= rightTransitionStart) {
else if (positionPercent > 83) { // Right side: smooth transition from center to right-aligned
// Position the right edge of tooltip at 100% if (positionPercent >= rightTransitionEnd) {
// Fully right-aligned
leftValue = '100%'; leftValue = '100%';
transform = 'translateX(-100%)'; transform = 'translateX(-100%)';
} else {
// Smooth transition zone
const transitionProgress = (positionPercent - rightTransitionStart) / (rightTransitionEnd - rightTransitionStart);
const translateAmount = -50 - (50 * transitionProgress); // Gradually change from -50% to -100%
leftValue = `${positionPercent}%`;
transform = `translateX(${translateAmount}%)`;
}
} else {
// Center zone: normal centered positioning
leftValue = `${positionPercent}%`;
transform = 'translateX(-50%)';
} }
return { left: leftValue, transform }; return { left: leftValue, transform };
@ -1111,8 +1136,8 @@ const TimelineControls = ({
// Handle timeline click to seek and show a tooltip // Handle timeline click to seek and show a tooltip
const handleTimelineClick = (e: React.MouseEvent<HTMLDivElement>) => { const handleTimelineClick = (e: React.MouseEvent<HTMLDivElement>) => {
// Prevent interaction if segments are playing // Remove the check that prevents interaction during preview mode
if (isPlayingSegments) return; // This allows users to click and jump in the timeline while previewing
if (!timelineRef.current || !scrollContainerRef.current) return; if (!timelineRef.current || !scrollContainerRef.current) return;
@ -1243,8 +1268,8 @@ const TimelineControls = ({
// Handle segment resize - works with both mouse and touch events // Handle segment resize - works with both mouse and touch events
const handleSegmentResize = (segmentId: number, isLeft: boolean) => (e: React.MouseEvent | React.TouchEvent) => { const handleSegmentResize = (segmentId: number, isLeft: boolean) => (e: React.MouseEvent | React.TouchEvent) => {
// Prevent interaction if segments are playing // Remove the check that prevents interaction during preview mode
if (isPlayingSegments) return; // This allows users to resize segments while previewing
e.preventDefault(); e.preventDefault();
e.stopPropagation(); // Prevent triggering parent's events e.stopPropagation(); // Prevent triggering parent's events
@ -1579,8 +1604,8 @@ const TimelineControls = ({
// Handle segment click to show the tooltip // Handle segment click to show the tooltip
const handleSegmentClick = (segmentId: number) => (e: React.MouseEvent) => { const handleSegmentClick = (segmentId: number) => (e: React.MouseEvent) => {
// Prevent interaction if segments are playing // Remove the check that prevents interaction during preview mode
if (isPlayingSegments) return; // This allows users to click segments while previewing
// Don't show tooltip if clicked on handle // Don't show tooltip if clicked on handle
if ((e.target as HTMLElement).classList.contains('clip-segment-handle')) { if ((e.target as HTMLElement).classList.contains('clip-segment-handle')) {
@ -2679,24 +2704,23 @@ const TimelineControls = ({
{/* Second row with action buttons similar to segment tooltip */} {/* Second row with action buttons similar to segment tooltip */}
<div className="tooltip-row tooltip-actions"> <div className="tooltip-row tooltip-actions">
{/* New segment button - Moved to first position */} {/* New segment button - Moved to first position */}
{availableSegmentDuration >= 0.5 && (
<button <button
className="tooltip-action-btn new-segment" className={`tooltip-action-btn new-segment ${availableSegmentDuration < 0.5 ? 'disabled' : ''}`}
data-tooltip={`Create new segment`} data-tooltip={availableSegmentDuration < 0.5 ? 'Not enough space for new segment' : 'Create new segment'}
disabled={availableSegmentDuration < 0.5}
onClick={async (e) => { onClick={async (e) => {
e.stopPropagation(); e.stopPropagation();
// Only create if we have at least 0.5 seconds of space
if (availableSegmentDuration < 0.5) {
// Not enough space, do nothing
return;
}
// Create a new segment with the calculated available duration // Create a new segment with the calculated available duration
const segmentStartTime = clickedTime; const segmentStartTime = clickedTime;
const segmentEndTime = segmentStartTime + availableSegmentDuration; const segmentEndTime = segmentStartTime + availableSegmentDuration;
// Only create if we have at least 0.5 seconds of space
if (availableSegmentDuration < 0.5) {
// Not enough space, close tooltip
setShowEmptySpaceTooltip(false);
return;
}
// Create the new segment with a generic name // Create the new segment with a generic name
const newSegment: Segment = { const newSegment: Segment = {
id: Date.now(), id: Date.now(),
@ -2744,7 +2768,6 @@ const TimelineControls = ({
New New
</span> </span>
</button> </button>
)}
{/* Go to start button - play from beginning of cutaway (until next segment) */} {/* Go to start button - play from beginning of cutaway (until next segment) */}
<button <button

View File

@ -158,6 +158,32 @@
font-size: 0.875rem !important; font-size: 0.875rem !important;
} }
/* Greyed out play button when segments are playing */
.play-button.greyed-out {
opacity: 0.5;
cursor: not-allowed;
}
/* Highlighted stop button with blue pulse on small screens */
.segments-button.highlighted-stop {
background-color: rgba(59, 130, 246, 0.1);
color: #3b82f6;
border: 1px solid #3b82f6;
animation: bluePulse 2s infinite;
}
@keyframes bluePulse {
0% {
box-shadow: 0 0 0 0 rgba(59, 130, 246, 0.4);
}
50% {
box-shadow: 0 0 0 8px rgba(59, 130, 246, 0);
}
100% {
box-shadow: 0 0 0 0 rgba(59, 130, 246, 0);
}
}
/* Completely disable ALL hover effects for play buttons */ /* Completely disable ALL hover effects for play buttons */
.play-button:hover:not(:disabled), .preview-button:hover:not(:disabled) { .play-button:hover:not(:disabled), .preview-button:hover:not(:disabled) {
/* Reset everything to prevent any changes */ /* Reset everything to prevent any changes */
@ -237,6 +263,12 @@
} }
@media (max-width: 640px) { @media (max-width: 640px) {
/* Prevent container overflow on mobile */
.editing-tools-container {
padding: 0.75rem;
overflow-x: hidden;
}
/* At this breakpoint, make preview button text shorter */ /* At this breakpoint, make preview button text shorter */
.preview-button { .preview-button {
min-width: auto; min-width: auto;
@ -260,11 +292,25 @@
.button-group.play-buttons-group { .button-group.play-buttons-group {
flex: initial; flex: initial;
justify-content: flex-start; justify-content: flex-start;
flex-shrink: 0;
} }
.button-group.secondary { .button-group.secondary {
flex: initial; flex: initial;
justify-content: flex-end; justify-content: flex-end;
flex-shrink: 0;
}
/* Reduce button sizes on mobile */
.button-group button {
padding: 0.375rem;
min-width: auto;
}
.button-group button svg {
height: 1.125rem;
width: 1.125rem;
margin-right: 0.125rem;
} }
} }
@ -273,6 +319,7 @@
.flex-container.single-row { .flex-container.single-row {
justify-content: space-between; justify-content: space-between;
flex-wrap: nowrap; flex-wrap: nowrap;
gap: 10px;
} }
/* Fix left-align for play buttons */ /* Fix left-align for play buttons */
@ -305,6 +352,14 @@
/* Very small screens - maintain layout but reduce further */ /* Very small screens - maintain layout but reduce further */
@media (max-width: 480px) { @media (max-width: 480px) {
.editing-tools-container {
padding: 0.5rem;
}
.flex-container.single-row {
gap: 8px;
}
.button-group.play-buttons-group, .button-group.play-buttons-group,
.button-group.secondary { .button-group.secondary {
gap: 0.25rem; gap: 0.25rem;
@ -313,5 +368,49 @@
.divider { .divider {
display: none; /* Hide divider on very small screens */ display: none; /* Hide divider on very small screens */
} }
/* Even smaller buttons on very small screens */
.button-group button {
padding: 0.125rem;
}
.button-group button svg {
height: 1rem;
width: 1rem;
margin-right: 0;
}
/* Hide all button text on very small screens */
.button-text,
.reset-text {
display: none;
}
}
/* Portrait orientation specific fixes */
@media (max-width: 640px) and (orientation: portrait) {
.editing-tools-container {
width: 100%;
box-sizing: border-box;
}
.flex-container.single-row {
width: 100%;
padding: 0;
margin: 0;
}
/* Ensure button groups don't overflow */
.button-group {
max-width: 50%;
}
.button-group.play-buttons-group {
max-width: 60%;
}
.button-group.secondary {
max-width: 40%;
}
} }
} }

View File

@ -282,7 +282,7 @@
} }
.timeline-marker { .timeline-marker {
height: 52px; /* Increased height for touch devices */ height: 85px;
} }
.timeline-marker-head { .timeline-marker-head {
@ -294,7 +294,7 @@
.timeline-marker-drag { .timeline-marker-drag {
width: 24px; width: 24px;
height: 24px; height: 24px;
bottom: -18px; /* Adjusted for larger touch target */ bottom: -18px;
} }
.timeline-marker-head.dragging { .timeline-marker-head.dragging {
@ -854,8 +854,6 @@
/* Segments playback mode styles */ /* Segments playback mode styles */
.segments-playback-mode { .segments-playback-mode {
pointer-events: none;
cursor: not-allowed;
} }
.segments-playback-mode .timeline-container, .segments-playback-mode .timeline-container,
@ -864,20 +862,14 @@
.segments-playback-mode .timeline-marker-head, .segments-playback-mode .timeline-marker-head,
.segments-playback-mode .timeline-marker-drag, .segments-playback-mode .timeline-marker-drag,
.segments-playback-mode .trim-handle { .segments-playback-mode .trim-handle {
pointer-events: none;
cursor: not-allowed;
} }
.segments-playback-mode .tooltip-action-btn { .segments-playback-mode .tooltip-action-btn {
opacity: 0.5; opacity: 0.5;
pointer-events: none;
cursor: not-allowed;
} }
.segments-playback-mode .tooltip-time-btn { .segments-playback-mode .tooltip-time-btn {
opacity: 0.5; opacity: 0.5;
pointer-events: none;
cursor: not-allowed;
} }
.segments-playback-mode .clip-segment:hover { .segments-playback-mode .clip-segment:hover {

View File

@ -207,6 +207,26 @@
white-space: nowrap; white-space: nowrap;
} }
/* Disabled state for tooltip action buttons */
.tooltip-action-btn.disabled {
opacity: 0.5;
cursor: not-allowed;
background-color: #f3f4f6;
}
.tooltip-action-btn.disabled:hover {
background-color: #f3f4f6;
color: #9ca3af;
}
.tooltip-action-btn.disabled svg {
color: #9ca3af;
}
.tooltip-action-btn.disabled .tooltip-btn-text {
color: #9ca3af;
}
/* Additional mobile optimizations */ /* Additional mobile optimizations */
@media (max-width: 768px) { @media (max-width: 768px) {
.two-row-tooltip { .two-row-tooltip {