feat: smooth navigation adjustment and time grid actions

- Implemented View Transition API for Week Navigation (simultaneous slide-out/slide-in).
- Added Task Action Icons (Notes, Delete) to Time Grid tasks.
- Refactored navigate function to use startViewTransition.
- Updated globals.css with View Transition keyframes and rules.
This commit is contained in:
mARTin 2026-02-01 14:21:41 +01:00
parent d92a8c7210
commit 3aad7b5371
2 changed files with 92 additions and 9 deletions

View File

@ -1445,12 +1445,14 @@ h3 {
}
/* Fix 2: Task Hover Actions */
.teuxdeux-task-item {
.teuxdeux-task-item,
.time-slot-task {
/* Ensure positioning context for absolute actions if needed, though flex is used */
position: relative;
}
.teuxdeux-task-item .task-actions {
.teuxdeux-task-item .task-actions,
.time-slot-task .task-actions {
display: flex;
align-items: center;
gap: 2px;
@ -1461,7 +1463,8 @@ h3 {
padding-left: 8px;
}
.teuxdeux-task-item:hover .task-actions {
.teuxdeux-task-item:hover .task-actions,
.time-slot-task:hover .task-actions {
opacity: 1;
}
@ -1563,4 +1566,71 @@ h3 {
.teuxdeux-btn-primary:hover {
background: #333;
}
/* --- VIEW TRANSITION ANIMATION (TeuxDeux-like) --- */
.teuxdeux-days-grid {
view-transition-name: week-grid;
}
@keyframes slideOutToLeft {
to {
transform: translateX(-100%);
opacity: 0.8;
}
}
@keyframes slideInFromRight {
from {
transform: translateX(100%);
}
to {
transform: translateX(0);
}
}
@keyframes slideOutToRight {
to {
transform: translateX(100%);
opacity: 0.8;
}
}
@keyframes slideInFromLeft {
from {
transform: translateX(-100%);
}
to {
transform: translateX(0);
}
}
::view-transition-group(week-grid) {
animation-duration: 0.5s;
animation-timing-function: ease-in-out;
}
/* Next Week: Old slides Left, New enters from Right */
[data-transition-direction="next"]::view-transition-old(week-grid) {
animation: slideOutToLeft 0.5s ease-in-out both;
mix-blend-mode: normal;
}
[data-transition-direction="next"]::view-transition-new(week-grid) {
animation: slideInFromRight 0.5s ease-in-out both;
mix-blend-mode: normal;
}
/* Prev Week: Old slides Right, New enters from Left */
[data-transition-direction="prev"]::view-transition-old(week-grid) {
animation: slideOutToRight 0.5s ease-in-out both;
mix-blend-mode: normal;
}
[data-transition-direction="prev"]::view-transition-new(week-grid) {
animation: slideInFromLeft 0.5s ease-in-out both;
mix-blend-mode: normal;
}

View File

@ -203,11 +203,16 @@ export default function TeuxDeuxView() {
// Navigation handlers with proper slide animation
// Simplified: Immediate state update with slide-in animation to prevent blank flash
const navigate = (newDate: Date, direction: 'left' | 'right') => {
setCurrentWeekStart(newDate);
setSlideDirection(direction === 'left' ? 'in-left' : 'in-right');
// Clear animation after it completes
setTimeout(() => setSlideDirection(null), 500);
// Use View Transition API for smooth 'TeuxDeux' slide (simultaneous old/new)
if (typeof document !== 'undefined' && 'startViewTransition' in document) {
document.documentElement.dataset.transitionDirection = direction === 'left' ? 'next' : 'prev';
(document as any).startViewTransition(() => {
setCurrentWeekStart(newDate);
setSlideDirection(null);
});
} else {
setCurrentWeekStart(newDate);
}
};
const goToPrevWeek = () => navigate(new Date(currentWeekStart.getTime() - 7 * 24 * 60 * 60 * 1000), 'right');
@ -565,7 +570,15 @@ export default function TeuxDeuxView() {
toggleTask(task.id);
}}
>
{task.title}
<span style={{ display: 'block', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap', flex: 1 }}>{task.title}</span>
<div className="task-actions">
<button className="task-action-btn" onClick={(e) => { e.stopPropagation(); setSelectedTaskForNotes(task); }} title="Notes">
<svg viewBox="0 0 24 24" width="14" height="14" stroke="currentColor" strokeWidth="2" fill="none" strokeLinecap="round" strokeLinejoin="round"><line x1="3" y1="12" x2="21" y2="12"></line><line x1="3" y1="6" x2="21" y2="6"></line><line x1="3" y1="18" x2="21" y2="18"></line></svg>
</button>
<button className="task-action-btn delete" onClick={(e) => { e.stopPropagation(); deleteTask(task.id); }} title="Delete">
<svg viewBox="0 0 24 24" width="14" height="14" stroke="currentColor" strokeWidth="2" fill="none" strokeLinecap="round" strokeLinejoin="round"><line x1="5" y1="12" x2="19" y2="12"></line></svg>
</button>
</div>
</div>
))}
{isActive && (