Fix: Unified hover icons overlay and dynamic font loading with previews

This commit is contained in:
mARTin 2026-02-14 09:12:17 +01:00
parent 13c513b314
commit 40e565645a
2 changed files with 65 additions and 60 deletions

View File

@ -683,14 +683,20 @@ h3 {
padding-top: 2px; /* Align with icons */
}
/* Task Actions Visibility */
.weekly-task-item .task-actions {
/* Task Actions Visibility - Unified for Weekly and Time Grid */
.weekly-task-item .task-actions,
.time-slot-task .task-actions {
opacity: 0;
transition: opacity 0.2s ease;
pointer-events: none;
transition: opacity 0.2s ease, visibility 0.2s;
visibility: hidden;
}
.weekly-task-item:hover .task-actions {
.weekly-task-item:hover .task-actions,
.time-slot-task:hover .task-actions {
opacity: 1;
pointer-events: auto;
visibility: visible;
}
.weekly-task-text.completed {
@ -784,6 +790,13 @@ h3 {
display: flex;
align-items: center;
gap: 2px;
position: absolute;
right: 0;
top: 0;
bottom: 0;
z-index: 10;
padding-left: 8px;
padding-right: 4px;
}
.task-action-btn {
@ -791,19 +804,24 @@ h3 {
border: none;
cursor: pointer;
padding: 2px;
width: 20px;
height: 20px;
width: 22px;
height: 22px;
color: var(--weekly-text-light);
border-radius: 4px;
display: flex;
align-items: center;
justify-content: center;
transition: all 0.15s;
transition: all 0.2s ease;
}
.task-action-btn:hover {
background-color: rgba(0,0,0,0.05);
color: var(--weekly-text);
background: rgba(0,0,0,0.08);
}
.dark .task-action-btn:hover {
background-color: rgba(255,255,255,0.1);
color: white;
}
/* Specific button styles */
@ -2061,29 +2079,7 @@ h3 {
animation: slideInFromLeft 0.5s ease-out forwards;
}
/* Fix 2: Task Hover Actions */
.weekly-task-item,
.time-slot-task {
/* Ensure positioning context for absolute actions if needed, though flex is used */
position: relative;
}
.weekly-task-item .task-actions,
.time-slot-task .task-actions {
display: flex;
align-items: center;
gap: 2px;
opacity: 0;
transition: opacity 0.2s ease;
margin-left: auto;
/* Push to far right */
padding-left: 8px;
}
.weekly-task-item:hover .task-actions,
.time-slot-task:hover .task-actions {
opacity: 1;
}
/* Consolidate Task Hover Actions - Removed redundant entries */
.task-action-btn {
background: none;

View File

@ -85,6 +85,8 @@ const AVAILABLE_FONTS = [
{ name: 'Playfair Display', value: 'Playfair Display' },
{ name: 'Merriweather', value: 'Merriweather' },
{ name: 'Nunito', value: 'Nunito' },
{ name: 'Dancing Script', value: 'Dancing Script' },
{ name: 'Pacifico', value: 'Pacifico' },
];
const FONT_WEIGHTS = [
@ -94,6 +96,29 @@ const FONT_WEIGHTS = [
{ name: 'Bold', value: '700' },
];
// Helper to load Google Fonts
const useGoogleFonts = (fonts: string[]) => {
useEffect(() => {
if (typeof window === 'undefined') return;
const fontsToLoad = fonts.filter(f => f && f !== 'Inter');
if (fontsToLoad.length === 0) return;
const linkId = 'google-fonts-link';
let link = document.getElementById(linkId) as HTMLLinkElement;
const fontQuery = fontsToLoad.map(f => f.replace(' ', '+')).join('|');
const href = `https://fonts.googleapis.com/css2?family=${fontsToLoad.map(f => `${f.replace(' ', '+')}:wght@300;400;500;700`).join('&family=')}&display=swap`;
if (!link) {
link = document.createElement('link');
link.id = linkId;
link.rel = 'stylesheet';
document.head.appendChild(link);
}
link.href = href;
}, [fonts]);
};
// Translations
const translations: Record<string, any> = {
en: {
@ -323,7 +348,8 @@ export default function WeeklyView() {
const [somedayLists, setSomedayLists] = useState<SomedayList[]>([]);
const [editingTaskId, setEditingTaskId] = useState<string | null>(null);
const [showSettings, setShowSettings] = useState(false);
const [showDatePicker, setShowDatePicker] = useState(false);
const [showSummary, setShowSummary] = useState(false);
const [isAddingSomedayList, setIsAddingSomedayList] = useState(false);
const [newSomedayListName, setNewSomedayListName] = useState('');
const [language, setLanguage] = useState('de');
@ -365,30 +391,10 @@ export default function WeeklyView() {
const [bodyFont, setBodyFont] = useState('Inter');
const [fontWeight, setFontWeight] = useState('400');
// Load Google Fonts
useEffect(() => {
const fontsToLoad = new Set([headlineFont, bodyFont]);
fontsToLoad.delete('Inter'); // Inter is likely already loaded or default
// Load fonts
useGoogleFonts([headlineFont, bodyFont, 'Dancing Script', 'Pacifico']);
if (fontsToLoad.size === 0) return;
const linkId = 'google-fonts-dynamic';
let link = document.getElementById(linkId) as HTMLLinkElement;
if (!link) {
link = document.createElement('link');
link.id = linkId;
link.rel = 'stylesheet';
document.head.appendChild(link);
}
// Simplification: Load all needed weights for selected fonts
const families = Array.from(fontsToLoad).map(font =>
`${font.replace(/\s+/g, '+')}:wght@300;400;500;700`
).join('&');
link.href = `https://fonts.googleapis.com/css2?family=${families}&display=swap`;
}, [headlineFont, bodyFont]);
// Dynamic font loading is handled by useGoogleFonts hook call above
// Calendar Event Modal State
const [calendarEventModal, setCalendarEventModal] = useState<{
@ -3021,7 +3027,7 @@ function TaskItem({ task, isEditing, onToggle, onEdit, onUpdate, onDelete, onNot
{task.title}
</span>
<div className="task-actions absolute right-0 top-0 bottom-0 flex items-center bg-white/95 dark:bg-gray-800/95 pl-2 pr-1 shadow-[-8px_0_8px_-4px_rgba(255,255,255,0.95)] dark:shadow-[-8px_0_8px_-4px_rgba(31,41,55,0.95)]">
<div className="task-actions absolute right-0 top-0 bottom-0 flex items-center bg-white/95 dark:bg-gray-800/95 pl-2 pr-1 shadow-[-12px_0_12px_-4px_rgba(255,255,255,0.95)] dark:shadow-[-12px_0_12px_-4px_rgba(31,41,55,0.95)] z-20">
{/* Edit */}
<button className="task-action-btn" onClick={(e) => { e.stopPropagation(); onEdit(); }} title="Edit">
<svg viewBox="0 0 24 24" width="12" height="12" stroke="currentColor" strokeWidth="2.5" fill="none" strokeLinecap="round" strokeLinejoin="round"><path d="M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7"></path><path d="M18.5 2.5a2.121 2.121 0 0 1 3 3L12 15l-4 1 1-4 9.5-9.5z"></path></svg>
@ -3349,6 +3355,9 @@ function SettingsSidebar({
const t = translations[profile.language || 'en'] || translations['en'];
// Load fonts for preview
useGoogleFonts([profile.headlineFont || '', profile.bodyFont || '']);
useEffect(() => {
fetchProfile();
// Trigger slide-in after mount
@ -3846,10 +3855,10 @@ function SettingsSidebar({
value={profile.headlineFont || 'Inter'}
onChange={(e) => setProfile({ ...profile, headlineFont: e.target.value })}
className="weekly-input"
style={{ width: '100%', padding: '8px', border: '1px solid #ddd', borderRadius: '4px' }}
style={{ width: '100%', padding: '8px', border: '1px solid #ddd', borderRadius: '4px', fontFamily: profile.headlineFont || 'Inter' }}
>
{AVAILABLE_FONTS.map(font => (
<option key={font.value} value={font.value}>{font.name}</option>
<option key={font.value} value={font.value} style={{ fontFamily: font.value }}>{font.name}</option>
))}
</select>
</div>
@ -3861,10 +3870,10 @@ function SettingsSidebar({
value={profile.bodyFont || 'Inter'}
onChange={(e) => setProfile({ ...profile, bodyFont: e.target.value })}
className="weekly-input"
style={{ width: '100%', padding: '8px', border: '1px solid #ddd', borderRadius: '4px' }}
style={{ width: '100%', padding: '8px', border: '1px solid #ddd', borderRadius: '4px', fontFamily: profile.bodyFont || 'Inter' }}
>
{AVAILABLE_FONTS.map(font => (
<option key={font.value} value={font.value}>{font.name}</option>
<option key={font.value} value={font.value} style={{ fontFamily: font.value }}>{font.name}</option>
))}
</select>
</div>