Fix: Unified hover icons overlay and dynamic font loading with previews
This commit is contained in:
parent
13c513b314
commit
40e565645a
@ -683,14 +683,20 @@ h3 {
|
|||||||
padding-top: 2px; /* Align with icons */
|
padding-top: 2px; /* Align with icons */
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Task Actions Visibility */
|
/* Task Actions Visibility - Unified for Weekly and Time Grid */
|
||||||
.weekly-task-item .task-actions {
|
.weekly-task-item .task-actions,
|
||||||
|
.time-slot-task .task-actions {
|
||||||
opacity: 0;
|
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;
|
opacity: 1;
|
||||||
|
pointer-events: auto;
|
||||||
|
visibility: visible;
|
||||||
}
|
}
|
||||||
|
|
||||||
.weekly-task-text.completed {
|
.weekly-task-text.completed {
|
||||||
@ -784,6 +790,13 @@ h3 {
|
|||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 2px;
|
gap: 2px;
|
||||||
|
position: absolute;
|
||||||
|
right: 0;
|
||||||
|
top: 0;
|
||||||
|
bottom: 0;
|
||||||
|
z-index: 10;
|
||||||
|
padding-left: 8px;
|
||||||
|
padding-right: 4px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.task-action-btn {
|
.task-action-btn {
|
||||||
@ -791,19 +804,24 @@ h3 {
|
|||||||
border: none;
|
border: none;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
padding: 2px;
|
padding: 2px;
|
||||||
width: 20px;
|
width: 22px;
|
||||||
height: 20px;
|
height: 22px;
|
||||||
color: var(--weekly-text-light);
|
color: var(--weekly-text-light);
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
transition: all 0.15s;
|
transition: all 0.2s ease;
|
||||||
}
|
}
|
||||||
|
|
||||||
.task-action-btn:hover {
|
.task-action-btn:hover {
|
||||||
|
background-color: rgba(0,0,0,0.05);
|
||||||
color: var(--weekly-text);
|
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 */
|
/* Specific button styles */
|
||||||
@ -2061,29 +2079,7 @@ h3 {
|
|||||||
animation: slideInFromLeft 0.5s ease-out forwards;
|
animation: slideInFromLeft 0.5s ease-out forwards;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Fix 2: Task Hover Actions */
|
/* Consolidate Task Hover Actions - Removed redundant entries */
|
||||||
.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;
|
|
||||||
}
|
|
||||||
|
|
||||||
.task-action-btn {
|
.task-action-btn {
|
||||||
background: none;
|
background: none;
|
||||||
|
|||||||
@ -85,6 +85,8 @@ const AVAILABLE_FONTS = [
|
|||||||
{ name: 'Playfair Display', value: 'Playfair Display' },
|
{ name: 'Playfair Display', value: 'Playfair Display' },
|
||||||
{ name: 'Merriweather', value: 'Merriweather' },
|
{ name: 'Merriweather', value: 'Merriweather' },
|
||||||
{ name: 'Nunito', value: 'Nunito' },
|
{ name: 'Nunito', value: 'Nunito' },
|
||||||
|
{ name: 'Dancing Script', value: 'Dancing Script' },
|
||||||
|
{ name: 'Pacifico', value: 'Pacifico' },
|
||||||
];
|
];
|
||||||
|
|
||||||
const FONT_WEIGHTS = [
|
const FONT_WEIGHTS = [
|
||||||
@ -94,6 +96,29 @@ const FONT_WEIGHTS = [
|
|||||||
{ name: 'Bold', value: '700' },
|
{ 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
|
// Translations
|
||||||
const translations: Record<string, any> = {
|
const translations: Record<string, any> = {
|
||||||
en: {
|
en: {
|
||||||
@ -323,7 +348,8 @@ export default function WeeklyView() {
|
|||||||
const [somedayLists, setSomedayLists] = useState<SomedayList[]>([]);
|
const [somedayLists, setSomedayLists] = useState<SomedayList[]>([]);
|
||||||
const [editingTaskId, setEditingTaskId] = useState<string | null>(null);
|
const [editingTaskId, setEditingTaskId] = useState<string | null>(null);
|
||||||
const [showSettings, setShowSettings] = useState(false);
|
const [showSettings, setShowSettings] = useState(false);
|
||||||
const [showDatePicker, setShowDatePicker] = useState(false);
|
const [showSummary, setShowSummary] = useState(false);
|
||||||
|
|
||||||
const [isAddingSomedayList, setIsAddingSomedayList] = useState(false);
|
const [isAddingSomedayList, setIsAddingSomedayList] = useState(false);
|
||||||
const [newSomedayListName, setNewSomedayListName] = useState('');
|
const [newSomedayListName, setNewSomedayListName] = useState('');
|
||||||
const [language, setLanguage] = useState('de');
|
const [language, setLanguage] = useState('de');
|
||||||
@ -365,30 +391,10 @@ export default function WeeklyView() {
|
|||||||
const [bodyFont, setBodyFont] = useState('Inter');
|
const [bodyFont, setBodyFont] = useState('Inter');
|
||||||
const [fontWeight, setFontWeight] = useState('400');
|
const [fontWeight, setFontWeight] = useState('400');
|
||||||
|
|
||||||
// Load Google Fonts
|
// Load fonts
|
||||||
useEffect(() => {
|
useGoogleFonts([headlineFont, bodyFont, 'Dancing Script', 'Pacifico']);
|
||||||
const fontsToLoad = new Set([headlineFont, bodyFont]);
|
|
||||||
fontsToLoad.delete('Inter'); // Inter is likely already loaded or default
|
|
||||||
|
|
||||||
if (fontsToLoad.size === 0) return;
|
// Dynamic font loading is handled by useGoogleFonts hook call above
|
||||||
|
|
||||||
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]);
|
|
||||||
|
|
||||||
// Calendar Event Modal State
|
// Calendar Event Modal State
|
||||||
const [calendarEventModal, setCalendarEventModal] = useState<{
|
const [calendarEventModal, setCalendarEventModal] = useState<{
|
||||||
@ -3021,7 +3027,7 @@ function TaskItem({ task, isEditing, onToggle, onEdit, onUpdate, onDelete, onNot
|
|||||||
{task.title}
|
{task.title}
|
||||||
</span>
|
</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 */}
|
{/* Edit */}
|
||||||
<button className="task-action-btn" onClick={(e) => { e.stopPropagation(); onEdit(); }} title="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>
|
<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'];
|
const t = translations[profile.language || 'en'] || translations['en'];
|
||||||
|
|
||||||
|
// Load fonts for preview
|
||||||
|
useGoogleFonts([profile.headlineFont || '', profile.bodyFont || '']);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
fetchProfile();
|
fetchProfile();
|
||||||
// Trigger slide-in after mount
|
// Trigger slide-in after mount
|
||||||
@ -3846,10 +3855,10 @@ function SettingsSidebar({
|
|||||||
value={profile.headlineFont || 'Inter'}
|
value={profile.headlineFont || 'Inter'}
|
||||||
onChange={(e) => setProfile({ ...profile, headlineFont: e.target.value })}
|
onChange={(e) => setProfile({ ...profile, headlineFont: e.target.value })}
|
||||||
className="weekly-input"
|
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 => (
|
{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>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
@ -3861,10 +3870,10 @@ function SettingsSidebar({
|
|||||||
value={profile.bodyFont || 'Inter'}
|
value={profile.bodyFont || 'Inter'}
|
||||||
onChange={(e) => setProfile({ ...profile, bodyFont: e.target.value })}
|
onChange={(e) => setProfile({ ...profile, bodyFont: e.target.value })}
|
||||||
className="weekly-input"
|
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 => (
|
{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>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user