mirror of
https://github.com/nagaoo0/HabbitGrid.git
synced 2026-01-12 07:54:53 +00:00
Update for Sally
Added Freeze Day
This commit is contained in:
@@ -1,9 +1,10 @@
|
||||
import React, { useMemo, useEffect } from 'react';
|
||||
import { motion } from 'framer-motion';
|
||||
import { getColorIntensity, isToday, formatDate, getWeekdayLabel } from '../lib/utils-habit';
|
||||
import { getColorIntensity, isToday, formatDate, getWeekdayLabel, getFrozenDays } from '../lib/utils-habit';
|
||||
import { toggleCompletion } from '../lib/storage';
|
||||
|
||||
const HabitGrid = ({ habit, onUpdate, fullView = false }) => {
|
||||
const frozenDays = getFrozenDays(habit.completions);
|
||||
const weeks = useMemo(() => {
|
||||
const today = new Date();
|
||||
// Find the Monday of the current week
|
||||
@@ -67,13 +68,14 @@ const HabitGrid = ({ habit, onUpdate, fullView = false }) => {
|
||||
const intensity = isCompleted ? getColorIntensity(habit.completions, dateStr) : 0;
|
||||
const isTodayCell = isToday(date);
|
||||
const isFuture = date > new Date();
|
||||
const isFrozen = frozenDays.includes(dateStr);
|
||||
return (
|
||||
<motion.button
|
||||
key={dayIndex}
|
||||
whileHover={{ scale: 1.15 }}
|
||||
whileTap={{ scale: 0.9 }}
|
||||
onClick={() => handleCellClick(date)}
|
||||
className="habit-cell w-3 h-3 rounded-sm"
|
||||
className="habit-cell w-3 h-3 rounded-sm flex items-center justify-center"
|
||||
style={{
|
||||
backgroundColor: isCompleted ? habit.color : 'transparent',
|
||||
opacity: isFuture ? 0 : (isCompleted ? 0.3 + (intensity * 0.7) : 1),
|
||||
@@ -81,8 +83,12 @@ const HabitGrid = ({ habit, onUpdate, fullView = false }) => {
|
||||
pointerEvents: isFuture ? 'none' : 'auto',
|
||||
visibility: isFuture ? 'hidden' : 'visible',
|
||||
}}
|
||||
title={`${dateStr}${isCompleted ? ' ✓' : ''}`}
|
||||
/>
|
||||
title={`${dateStr}${isCompleted ? ' ✓' : ''}${isFrozen ? ' (Frozen)' : ''}`}
|
||||
>
|
||||
{isFrozen && (
|
||||
<span role="img" aria-label="Frozen" style={{ fontSize: '0.7em' }}>❄️</span>
|
||||
)}
|
||||
</motion.button>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
|
||||
@@ -1,6 +1,21 @@
|
||||
import React from 'react';
|
||||
// Utility to lighten a hex color
|
||||
function lightenColor(hex, percent) {
|
||||
hex = hex.replace(/^#/, '');
|
||||
if (hex.length === 3) hex = hex.split('').map(x => x + x).join('');
|
||||
const num = parseInt(hex, 16);
|
||||
let r = (num >> 16) + Math.round(255 * percent);
|
||||
let g = ((num >> 8) & 0x00FF) + Math.round(255 * percent);
|
||||
let b = (num & 0x0000FF) + Math.round(255 * percent);
|
||||
r = Math.min(255, r);
|
||||
g = Math.min(255, g);
|
||||
b = Math.min(255, b);
|
||||
return `rgb(${r},${g},${b})`;
|
||||
}
|
||||
import { Flame } from 'lucide-react';
|
||||
import { motion } from 'framer-motion';
|
||||
import { getColorIntensity, isToday, formatDate } from '../lib/utils-habit';
|
||||
import { getFrozenDays } from '../lib/utils-habit';
|
||||
import { toggleCompletion } from '../lib/storage';
|
||||
|
||||
const MiniGrid = ({ habit, onUpdate }) => {
|
||||
@@ -31,35 +46,46 @@ const MiniGrid = ({ habit, onUpdate }) => {
|
||||
|
||||
return (
|
||||
<div ref={scrollRef} className="flex gap-1 overflow-x-auto grid-scroll pb-2">
|
||||
{days.map((date, index) => {
|
||||
const dateStr = formatDate(date);
|
||||
const isCompleted = habit.completions.includes(dateStr);
|
||||
const intensity = isCompleted ? getColorIntensity(habit.completions, dateStr) : 0;
|
||||
const isTodayCell = isToday(date);
|
||||
const dayLetter = date.toLocaleDateString('en-US', { weekday: 'short' })[0];
|
||||
|
||||
return (
|
||||
<div key={index} className="flex flex-col items-center">
|
||||
<motion.button
|
||||
whileHover={{ scale: 0.9 }}
|
||||
whileTap={{ scale: 0.5 }}
|
||||
onClick={(e) => handleCellClick(e, date)}
|
||||
className={`habit-cell flex w-8 h-8 transition-all ${isTodayCell ? 'rounded-md' : 'rounded-2xl'}`}
|
||||
style={{
|
||||
backgroundColor: isCompleted
|
||||
? habit.color
|
||||
: 'transparent',
|
||||
opacity: isCompleted ? 0.3 + (intensity * 0.7) : 1,
|
||||
border: isTodayCell
|
||||
? `2px solid ${habit.color}`
|
||||
: `1px solid ${habit.color}20`,
|
||||
}}
|
||||
title={dateStr}
|
||||
/>
|
||||
<span className="text-[10px] text-muted-foreground mt-1">{dayLetter}</span>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
{(() => {
|
||||
const frozenDays = getFrozenDays(habit.completions);
|
||||
return days.map((date, index) => {
|
||||
const dateStr = formatDate(date);
|
||||
const isCompleted = habit.completions.includes(dateStr);
|
||||
const intensity = isCompleted ? getColorIntensity(habit.completions, dateStr) : 0;
|
||||
const isTodayCell = isToday(date);
|
||||
const dayLetter = date.toLocaleDateString('en-US', { weekday: 'short' })[0];
|
||||
const isFrozen = frozenDays.includes(dateStr);
|
||||
return (
|
||||
<div key={index} className="flex flex-col items-center">
|
||||
<motion.button
|
||||
whileHover={{ scale: 0.9 }}
|
||||
whileTap={{ scale: 0.5 }}
|
||||
onClick={(e) => handleCellClick(e, date)}
|
||||
className={`habit-cell flex w-8 h-8 transition-all items-center justify-center ${isTodayCell ? 'rounded-md' : 'rounded-2xl'}`}
|
||||
style={{
|
||||
backgroundColor: isCompleted
|
||||
? habit.color
|
||||
: 'transparent',
|
||||
opacity: isCompleted ? 0.3 + (intensity * 0.7) : 1,
|
||||
border: isTodayCell
|
||||
? `2px solid ${habit.color}`
|
||||
: `1px solid ${habit.color}20`,
|
||||
}}
|
||||
title={dateStr}
|
||||
>
|
||||
{isFrozen && (
|
||||
<span role="img" aria-label="Frozen" style={{ fontSize: '1.2em' }}>❄️</span>
|
||||
)}
|
||||
{/* Flame icon for full streak days */}
|
||||
{isCompleted && intensity >= 1 && (
|
||||
<Flame className="w-5 h-5 absolute" style={{ color: lightenColor(habit.color, 0.4) }} />
|
||||
)}
|
||||
</motion.button>
|
||||
<span className="text-[10px] text-muted-foreground mt-1">{dayLetter}</span>
|
||||
</div>
|
||||
);
|
||||
});
|
||||
})()}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user