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 { useEffect, useState } from 'react';
interface EditingToolsProps {
onSplit: () => void;
@ -29,6 +30,18 @@ const EditingTools = ({
isPlaying = false,
isPlayingSegments = false,
}: 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
const handlePlay = () => {
// Ensure lastSeekedPosition is used when play is clicked
@ -47,7 +60,7 @@ const EditingTools = ({
<div className="button-group play-buttons-group">
{/* Play Segments button */}
<button
className="button segments-button"
className={`button segments-button ${isPlayingSegments && isSmallScreen ? 'highlighted-stop' : ''}`}
onClick={onPlaySegments}
data-tooltip={isPlayingSegments ? "Stop segments playback" : "Play all segments continuously until the end"}
style={{ fontSize: '0.875rem' }}
@ -104,12 +117,13 @@ const EditingTools = ({
</button> */}
{/* Standard Play button (only shown when not in preview mode or segments playback) */}
{!isPreviewMode && !isPlayingSegments && (
{!isPreviewMode && (!isPlayingSegments || !isSmallScreen) && (
<button
className="button play-button"
className={`button play-button ${isPlayingSegments ? 'greyed-out' : ''}`}
onClick={handlePlay}
data-tooltip={isPlaying ? "Pause video" : "Play full video"}
style={{ fontSize: '0.875rem' }}
disabled={isPlayingSegments}
>
{isPlaying ? (
<>
@ -135,7 +149,7 @@ const EditingTools = ({
)}
{/* Segments Playback message (replaces play button during segments playback) */}
{isPlayingSegments && (
{isPlayingSegments && !isSmallScreen && (
<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">
<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
// Uses smooth transitions instead of hard breakpoints to eliminate jumping
const constrainTooltipPosition = (positionPercent: number) => {
// Default position logic (centered)
let leftValue = `${positionPercent}%`;
let transform = 'translateX(-50%)';
// Near left edge (first 17%)
if (positionPercent < 17) {
// Position the left edge of tooltip at 0%, no transform
leftValue = '0%';
transform = 'none';
}
// Near right edge (last 17%)
else if (positionPercent > 83) {
// Position the right edge of tooltip at 100%
leftValue = '100%';
transform = 'translateX(-100%)';
// Smooth transition zones instead of hard breakpoints
const leftTransitionStart = 0;
const leftTransitionEnd = 25;
const rightTransitionStart = 75;
const rightTransitionEnd = 100;
let leftValue: string;
let transform: string;
if (positionPercent <= leftTransitionEnd) {
// Left side: smooth transition from center to left-aligned
if (positionPercent <= leftTransitionStart) {
// Fully left-aligned
leftValue = '0%';
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}%)`;
}
} else if (positionPercent >= rightTransitionStart) {
// Right side: smooth transition from center to right-aligned
if (positionPercent >= rightTransitionEnd) {
// Fully right-aligned
leftValue = '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 };
@ -1111,8 +1136,8 @@ const TimelineControls = ({
// Handle timeline click to seek and show a tooltip
const handleTimelineClick = (e: React.MouseEvent<HTMLDivElement>) => {
// Prevent interaction if segments are playing
if (isPlayingSegments) return;
// Remove the check that prevents interaction during preview mode
// This allows users to click and jump in the timeline while previewing
if (!timelineRef.current || !scrollContainerRef.current) return;
@ -1243,8 +1268,8 @@ const TimelineControls = ({
// Handle segment resize - works with both mouse and touch events
const handleSegmentResize = (segmentId: number, isLeft: boolean) => (e: React.MouseEvent | React.TouchEvent) => {
// Prevent interaction if segments are playing
if (isPlayingSegments) return;
// Remove the check that prevents interaction during preview mode
// This allows users to resize segments while previewing
e.preventDefault();
e.stopPropagation(); // Prevent triggering parent's events
@ -1579,8 +1604,8 @@ const TimelineControls = ({
// Handle segment click to show the tooltip
const handleSegmentClick = (segmentId: number) => (e: React.MouseEvent) => {
// Prevent interaction if segments are playing
if (isPlayingSegments) return;
// Remove the check that prevents interaction during preview mode
// This allows users to click segments while previewing
// Don't show tooltip if clicked on handle
if ((e.target as HTMLElement).classList.contains('clip-segment-handle')) {
@ -2679,72 +2704,70 @@ const TimelineControls = ({
{/* Second row with action buttons similar to segment tooltip */}
<div className="tooltip-row tooltip-actions">
{/* New segment button - Moved to first position */}
{availableSegmentDuration >= 0.5 && (
<button
className="tooltip-action-btn new-segment"
data-tooltip={`Create new segment`}
onClick={async (e) => {
e.stopPropagation();
<button
className={`tooltip-action-btn new-segment ${availableSegmentDuration < 0.5 ? 'disabled' : ''}`}
data-tooltip={availableSegmentDuration < 0.5 ? 'Not enough space for new segment' : 'Create new segment'}
disabled={availableSegmentDuration < 0.5}
onClick={async (e) => {
e.stopPropagation();
// Create a new segment with the calculated available duration
const segmentStartTime = clickedTime;
const segmentEndTime = segmentStartTime + availableSegmentDuration;
// Only create if we have at least 0.5 seconds of space
if (availableSegmentDuration < 0.5) {
// Not enough space, do nothing
return;
}
// 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 a new segment with the calculated available duration
const segmentStartTime = clickedTime;
const segmentEndTime = segmentStartTime + availableSegmentDuration;
// Create the new segment with a generic name
const newSegment: Segment = {
id: Date.now(),
name: `segment`,
startTime: segmentStartTime,
endTime: segmentEndTime,
thumbnail: '' // Empty placeholder - we'll use dynamic colors instead
};
// Add the new segment to existing segments
const updatedSegments = [...clipSegments, newSegment];
// Create and dispatch the update event
const updateEvent = new CustomEvent('update-segments', {
detail: {
segments: updatedSegments,
recordHistory: true, // Explicitly record this action in history
action: 'create_segment'
}
});
document.dispatchEvent(updateEvent);
// Create the new segment with a generic name
const newSegment: Segment = {
id: Date.now(),
name: `segment`,
startTime: segmentStartTime,
endTime: segmentEndTime,
thumbnail: '' // Empty placeholder - we'll use dynamic colors instead
};
// Close empty space tooltip
setShowEmptySpaceTooltip(false);
// Add the new segment to existing segments
const updatedSegments = [...clipSegments, newSegment];
// After creating the segment, wait a short time for the state to update
setTimeout(() => {
// The newly created segment is the last one in the array with the ID we just assigned
const createdSegment = updatedSegments[updatedSegments.length - 1];
// Create and dispatch the update event
const updateEvent = new CustomEvent('update-segments', {
detail: {
segments: updatedSegments,
recordHistory: true, // Explicitly record this action in history
action: 'create_segment'
}
});
document.dispatchEvent(updateEvent);
// Close empty space tooltip
setShowEmptySpaceTooltip(false);
// After creating the segment, wait a short time for the state to update
setTimeout(() => {
// The newly created segment is the last one in the array with the ID we just assigned
const createdSegment = updatedSegments[updatedSegments.length - 1];
if (createdSegment) {
// Set this segment as selected to show its tooltip
setSelectedSegmentId(createdSegment.id);
logger.debug("Created and selected new segment:", createdSegment.id);
}
}, 100); // Small delay to ensure state is updated
}}
>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
<rect x="3" y="3" width="18" height="18" rx="2" ry="2"></rect>
<line x1="12" y1="8" x2="12" y2="16"></line>
<line x1="8" y1="12" x2="16" y2="12"></line>
</svg>
<span className="tooltip-btn-text">
New
</span>
</button>
)}
if (createdSegment) {
// Set this segment as selected to show its tooltip
setSelectedSegmentId(createdSegment.id);
logger.debug("Created and selected new segment:", createdSegment.id);
}
}, 100); // Small delay to ensure state is updated
}}
>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
<rect x="3" y="3" width="18" height="18" rx="2" ry="2"></rect>
<line x1="12" y1="8" x2="12" y2="16"></line>
<line x1="8" y1="12" x2="16" y2="12"></line>
</svg>
<span className="tooltip-btn-text">
New
</span>
</button>
{/* Go to start button - play from beginning of cutaway (until next segment) */}
<button

View File

@ -158,6 +158,32 @@
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 */
.play-button:hover:not(:disabled), .preview-button:hover:not(:disabled) {
/* Reset everything to prevent any changes */
@ -237,6 +263,12 @@
}
@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 */
.preview-button {
min-width: auto;
@ -260,11 +292,25 @@
.button-group.play-buttons-group {
flex: initial;
justify-content: flex-start;
flex-shrink: 0;
}
.button-group.secondary {
flex: initial;
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 {
justify-content: space-between;
flex-wrap: nowrap;
gap: 10px;
}
/* Fix left-align for play buttons */
@ -305,6 +352,14 @@
/* Very small screens - maintain layout but reduce further */
@media (max-width: 480px) {
.editing-tools-container {
padding: 0.5rem;
}
.flex-container.single-row {
gap: 8px;
}
.button-group.play-buttons-group,
.button-group.secondary {
gap: 0.25rem;
@ -313,5 +368,49 @@
.divider {
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 {
height: 52px; /* Increased height for touch devices */
height: 85px;
}
.timeline-marker-head {
@ -294,7 +294,7 @@
.timeline-marker-drag {
width: 24px;
height: 24px;
bottom: -18px; /* Adjusted for larger touch target */
bottom: -18px;
}
.timeline-marker-head.dragging {
@ -854,8 +854,6 @@
/* Segments playback mode styles */
.segments-playback-mode {
pointer-events: none;
cursor: not-allowed;
}
.segments-playback-mode .timeline-container,
@ -864,20 +862,14 @@
.segments-playback-mode .timeline-marker-head,
.segments-playback-mode .timeline-marker-drag,
.segments-playback-mode .trim-handle {
pointer-events: none;
cursor: not-allowed;
}
.segments-playback-mode .tooltip-action-btn {
opacity: 0.5;
pointer-events: none;
cursor: not-allowed;
}
.segments-playback-mode .tooltip-time-btn {
opacity: 0.5;
pointer-events: none;
cursor: not-allowed;
}
.segments-playback-mode .clip-segment:hover {

View File

@ -207,6 +207,26 @@
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 */
@media (max-width: 768px) {
.two-row-tooltip {