feat: forecast swipe nav, note swipe-to-delete, night-shift tint
All checks were successful
Deploy / deploy (push) Successful in 2m45s

- WeatherDayModal now accepts the full forecast array and an onChange
  callback; supports horizontal drag (framer-motion) plus prev/next
  chevrons and a dot-indicator. Drag > 60px switches day; style uses
  semantic tokens (shadow-xl, surface-1).
- NotesTab list items wrap each note in a motion.button with drag=x,
  constrained to -80px. Below it a gradient+trash reveal layer. Drag
  past 60px opens the existing confirmDelete modal.
- HomePageInner adds a night-shift overlay (fixed, mixBlendMode multiply,
  rgba(255,120,40,0.12)) active 22:00-06:00, auto-checked each minute,
  fades in/out over 800ms. No user toggle yet — fully automatic.
This commit is contained in:
Cosmo
2026-04-23 09:17:22 +00:00
parent 0908ad93de
commit 8d32e7ebb0
2 changed files with 155 additions and 29 deletions

View File

@@ -1,5 +1,6 @@
'use client'
import { useState, useEffect, useCallback } from 'react'
import { motion } from 'framer-motion'
import { Plus, X, Trash2, ShoppingCart, FileText, Check, Circle, Calendar as CalendarIcon } from 'lucide-react'
interface NoteItem {
@@ -144,12 +145,37 @@ export default function NotesTab() {
const doneCount = note.items?.filter(i => i.done).length || 0
const totalCount = note.items?.length || 0
return (
<button key={note.id} onClick={() => setActiveNote(note)} style={{
padding: '14px 16px', borderRadius: 16, textAlign: 'left', width: '100%',
background: isActive ? `${note.color}15` : 'rgba(255,255,255,0.025)',
border: `1px solid ${isActive ? note.color + '30' : 'rgba(255,255,255,0.05)'}`,
transition: 'all 0.2s ease',
}}>
<motion.div
key={note.id}
layout
style={{ position: 'relative', borderRadius: 16, overflow: 'hidden' }}
>
{/* Delete reveal layer */}
<div style={{
position: 'absolute', inset: 0,
display: 'flex', alignItems: 'center', justifyContent: 'flex-end',
padding: '0 20px', borderRadius: 16,
background: 'linear-gradient(90deg, transparent 30%, rgba(239,68,68,0.25))',
pointerEvents: 'none',
}}>
<Trash2 size={18} color="#f87171" />
</div>
<motion.button
drag="x"
dragConstraints={{ left: -80, right: 0 }}
dragElastic={0.1}
onDragEnd={(_, info) => {
if (info.offset.x < -60) setConfirmDelete(note)
}}
onClick={() => setActiveNote(note)}
style={{
padding: '14px 16px', borderRadius: 16, textAlign: 'left', width: '100%',
background: isActive ? `${note.color}15` : 'var(--surface-2)',
border: `1px solid ${isActive ? note.color + '30' : 'var(--border-subtle)'}`,
transition: 'background 0.2s ease, border-color 0.2s ease',
position: 'relative', zIndex: 1,
touchAction: 'pan-y',
}}>
<div style={{ display: 'flex', alignItems: 'center', gap: 8, marginBottom: 6 }}>
{note.type === 'shopping'
? <ShoppingCart size={14} color={note.color} />
@@ -185,7 +211,8 @@ export default function NotesTab() {
{note.text}
</div>
)}
</button>
</motion.button>
</motion.div>
)
})}