feat: localization (FR/ES/IT), portal date picker, font matching, quote cleanup
- Add French, Spanish, and Italian translations for full UI localization - Add Italian to language selector; show native language names - Fix Jump to Date calendar: use React portal with fixed positioning to prevent clipping by overflow:hidden ancestors - Fix font matching: time grid tasks inherit from task font settings when at default values (0.75rem/500), matching someday list fonts - CSS fallback chain: time-slot-task vars fall through to task vars - Remove broken recite.vercel.app default quote URL - Add date-fns locales (fr, es, it) for SimpleDatePicker v1.21.0 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
4c18d37bec
commit
9ac7198b24
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "my-weekly-todo-list",
|
||||
"version": "1.20.0",
|
||||
"version": "1.21.0",
|
||||
"description": "A web-based weekly task management application that organizes to-dos and calendar events in a single, intuitive weekly view",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
|
||||
@ -2051,9 +2051,9 @@ h3 {
|
||||
gap: 0.25rem;
|
||||
padding: 2px 0;
|
||||
background: transparent;
|
||||
font-family: var(--weekly-time-task-font, var(--weekly-font));
|
||||
font-size: var(--weekly-time-task-size, 0.75rem);
|
||||
font-weight: var(--weekly-time-task-weight, 500);
|
||||
font-family: var(--weekly-time-task-font, var(--weekly-task-font, var(--weekly-font)));
|
||||
font-size: var(--weekly-time-task-size, var(--weekly-task-size, 0.9rem));
|
||||
font-weight: var(--weekly-time-task-weight, var(--weekly-task-weight, 400));
|
||||
cursor: pointer;
|
||||
white-space: normal;
|
||||
word-break: break-word;
|
||||
|
||||
@ -1,28 +1,54 @@
|
||||
import React, { useState, useEffect, useRef } from 'react';
|
||||
import React, { useState, useEffect, useRef, useCallback } from 'react';
|
||||
import { createPortal } from 'react-dom';
|
||||
import { format, addMonths, subMonths, startOfMonth, endOfMonth, startOfWeek, endOfWeek, addDays, isSameMonth, isSameDay } from 'date-fns';
|
||||
import { enUS, de } from 'date-fns/locale';
|
||||
import { enUS, de, fr, es, it } from 'date-fns/locale';
|
||||
|
||||
interface SimpleDatePickerProps {
|
||||
selected: Date;
|
||||
onSelect: (date: Date) => void;
|
||||
onClose: () => void;
|
||||
language?: string;
|
||||
anchorRef?: React.RefObject<HTMLElement | null>;
|
||||
}
|
||||
|
||||
export default function SimpleDatePicker({ selected, onSelect, onClose, language = 'en' }: SimpleDatePickerProps) {
|
||||
export default function SimpleDatePicker({ selected, onSelect, onClose, language = 'en', anchorRef }: SimpleDatePickerProps) {
|
||||
const [currentMonth, setCurrentMonth] = useState(new Date(selected));
|
||||
const modalRef = useRef<HTMLDivElement>(null);
|
||||
const locale = language === 'de' ? de : enUS;
|
||||
const localeMap: Record<string, typeof enUS> = { de, fr, es, it };
|
||||
const locale = localeMap[language] || enUS;
|
||||
const [position, setPosition] = useState<{ top: number; left: number } | null>(null);
|
||||
|
||||
const updatePosition = useCallback(() => {
|
||||
if (anchorRef?.current) {
|
||||
const rect = anchorRef.current.getBoundingClientRect();
|
||||
setPosition({
|
||||
top: rect.bottom + 8,
|
||||
left: rect.left + rect.width / 2 - 144, // 144 = half of 18rem (288px)
|
||||
});
|
||||
}
|
||||
}, [anchorRef]);
|
||||
|
||||
useEffect(() => {
|
||||
updatePosition();
|
||||
window.addEventListener('resize', updatePosition);
|
||||
window.addEventListener('scroll', updatePosition, true);
|
||||
return () => {
|
||||
window.removeEventListener('resize', updatePosition);
|
||||
window.removeEventListener('scroll', updatePosition, true);
|
||||
};
|
||||
}, [updatePosition]);
|
||||
|
||||
useEffect(() => {
|
||||
const handleClickOutside = (event: MouseEvent) => {
|
||||
if (modalRef.current && !modalRef.current.contains(event.target as Node)) {
|
||||
// Also check if click is on the anchor/trigger button
|
||||
if (anchorRef?.current && anchorRef.current.contains(event.target as Node)) return;
|
||||
onClose();
|
||||
}
|
||||
};
|
||||
document.addEventListener('mousedown', handleClickOutside);
|
||||
return () => document.removeEventListener('mousedown', handleClickOutside);
|
||||
}, [onClose]);
|
||||
}, [onClose, anchorRef]);
|
||||
|
||||
const renderHeader = () => (
|
||||
<div className="datepicker-header" style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', marginBottom: '1rem', padding: '0 0.5rem' }}>
|
||||
@ -116,44 +142,40 @@ export default function SimpleDatePicker({ selected, onSelect, onClose, language
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
// Use fixed positioning via portal if we have an anchor, otherwise fallback to absolute
|
||||
const usePortal = !!anchorRef?.current && !!position;
|
||||
|
||||
const pickerContent = (
|
||||
<div ref={modalRef} style={{
|
||||
position: 'absolute',
|
||||
top: '100%',
|
||||
left: '50%',
|
||||
transform: 'translateX(-50%)',
|
||||
marginTop: '0.5rem',
|
||||
position: usePortal ? 'fixed' : 'absolute',
|
||||
top: usePortal ? position!.top : '100%',
|
||||
left: usePortal ? position!.left : '50%',
|
||||
transform: usePortal ? 'none' : 'translateX(-50%)',
|
||||
marginTop: usePortal ? 0 : '0.5rem',
|
||||
backgroundColor: 'var(--weekly-bg, white)',
|
||||
borderRadius: '0.5rem',
|
||||
boxShadow: '0 20px 25px -5px rgba(0, 0, 0, 0.15), 0 10px 10px -5px rgba(0, 0, 0, 0.08)',
|
||||
padding: '1rem',
|
||||
zIndex: 2000,
|
||||
zIndex: 9999,
|
||||
width: '18rem',
|
||||
border: '1px solid var(--weekly-border, #e5e7eb)',
|
||||
animation: 'fadeIn 0.15s ease-out'
|
||||
animation: 'simpleDatePickerFadeIn 0.15s ease-out'
|
||||
}}>
|
||||
{/* Decorative triangle */}
|
||||
<div style={{
|
||||
position: 'absolute',
|
||||
top: '-0.3rem',
|
||||
left: '50%',
|
||||
marginLeft: '-0.375rem',
|
||||
width: '0.75rem',
|
||||
height: '0.75rem',
|
||||
backgroundColor: 'var(--weekly-bg, white)',
|
||||
transform: 'rotate(45deg)',
|
||||
borderTop: '1px solid var(--weekly-border, #e5e7eb)',
|
||||
borderLeft: '1px solid var(--weekly-border, #e5e7eb)'
|
||||
}}></div>
|
||||
{renderHeader()}
|
||||
{renderDays()}
|
||||
{renderCells()}
|
||||
<style jsx>{`
|
||||
@keyframes fadeIn {
|
||||
<style>{`
|
||||
@keyframes simpleDatePickerFadeIn {
|
||||
from { opacity: 0; transform: translateY(-5px); }
|
||||
to { opacity: 1; transform: translateY(0); }
|
||||
}
|
||||
`}</style>
|
||||
</div>
|
||||
);
|
||||
|
||||
if (usePortal) {
|
||||
return createPortal(pickerContent, document.body);
|
||||
}
|
||||
|
||||
return pickerContent;
|
||||
}
|
||||
|
||||
@ -426,6 +426,315 @@ const translations: Record<string, any> = {
|
||||
weekdayCaseCapitalize: "Großbuchstabe (Montag)",
|
||||
weekdayCaseUppercase: "Großbuchstaben (MONTAG)",
|
||||
},
|
||||
fr: {
|
||||
settings: "Paramètres",
|
||||
general: "Général",
|
||||
calendar: "Connexions",
|
||||
localisation: "Localisation",
|
||||
account: "Compte",
|
||||
runningList: "Liste continue (reporter les tâches à aujourd'hui)",
|
||||
protectEventTimes: "Protéger les horaires des événements",
|
||||
showTimeGrid: "Afficher la grille horaire",
|
||||
timeSlotDuration: "Durée des créneaux horaires",
|
||||
viewStyle: "Style d'affichage",
|
||||
simpleView: "Simple",
|
||||
calendarView: "Calendrier",
|
||||
listView: "Liste",
|
||||
language: "Langue",
|
||||
dateFormat: "Format de date",
|
||||
timeFormat: "Format d'heure",
|
||||
saveChanges: "Enregistrer",
|
||||
connectedCalendars: "Calendriers connectés",
|
||||
connectMore: "En connecter d'autres",
|
||||
connectGoogle: "Connecter Google Agenda",
|
||||
connectApple: "Connecter le calendrier Apple",
|
||||
connectSynology: "Connecter Synology",
|
||||
noCalendars: "Aucun calendrier connecté.",
|
||||
dataPrivacy: "Données et confidentialité",
|
||||
downloadData: "Télécharger mes données",
|
||||
deleteAccount: "Supprimer le compte",
|
||||
name: "Nom",
|
||||
email: "E-mail",
|
||||
timezone: "Fuseau horaire",
|
||||
changePassword: "Changer le mot de passe",
|
||||
newPassword: "Nouveau mot de passe",
|
||||
confirmPassword: "Confirmer le mot de passe",
|
||||
someday: "UN JOUR",
|
||||
lists: "Listes",
|
||||
loading: "Chargement de vos tâches…",
|
||||
sycing: "Synchronisation…",
|
||||
synced: "Synchronisé",
|
||||
localization: "Localisation",
|
||||
allDayEvents: "ÉVÉNEMENTS JOURNÉE ENTIÈRE",
|
||||
syncCalendar: "Synchroniser le calendrier",
|
||||
toggleDarkMode: "Basculer le mode sombre",
|
||||
signOut: "Se déconnecter",
|
||||
startHour: "Début de journée",
|
||||
endHour: "Fin de journée",
|
||||
weekAbbr: "S",
|
||||
goalOfWeek: "Objectif de la semaine",
|
||||
goalScope: "Portée de l'objectif",
|
||||
goalScopeWeek: "Par semaine",
|
||||
goalScopeDay: "Par jour",
|
||||
goalFallback: "Type d'objectif par défaut",
|
||||
defaultGoal: "Objectif par défaut personnalisé",
|
||||
showTaskCheckboxes: "Afficher les cases à cocher",
|
||||
showSomeday: "Afficher la section Un jour",
|
||||
showAllDay: "Afficher la section Journée entière",
|
||||
allDayPosition: "Position des événements journée entière",
|
||||
allDayAbove: "Au-dessus",
|
||||
allDayBelow: "En dessous",
|
||||
newPasswordDesc: "Laisser vide pour conserver le mot de passe actuel.",
|
||||
dateAlignment: "Alignement de la date",
|
||||
dateVerticalAlign: "Alignement vertical de la date",
|
||||
alignTop: "Haut",
|
||||
alignMiddle: "Milieu",
|
||||
alignBottom: "Bas",
|
||||
dateLayout: "Disposition de la date",
|
||||
alignmentLeft: "Gauche",
|
||||
alignmentCenter: "Centre",
|
||||
alignmentRight: "Droite",
|
||||
alignmentTight: "Compact",
|
||||
backupRestore: "Sauvegarde et restauration",
|
||||
backupRestoreDesc: "Exportez toutes vos tâches, listes et projets au format JSON. Vous pouvez modifier le fichier et le réimporter.",
|
||||
exportAllData: "Exporter toutes les données (JSON)",
|
||||
importData: "Importer des données",
|
||||
importMode: "Mode d'importation",
|
||||
importModeMerge: "Fusionner",
|
||||
importModeMergeDesc: "Ajouter les données importées aux tâches existantes",
|
||||
importModeReplace: "Remplacer",
|
||||
importModeReplaceDesc: "Supprimer toutes les données existantes et les remplacer par les données importées",
|
||||
importReplaceWarning: "Attention : toutes vos tâches, listes et projets actuels seront définitivement supprimés !",
|
||||
importSelectFile: "Sélectionner un fichier JSON…",
|
||||
importButton: "Importer",
|
||||
importing: "Importation…",
|
||||
exporting: "Exportation…",
|
||||
projects: "Projets",
|
||||
projectsDesc: "Organisez vos tâches avec des projets colorés",
|
||||
addProject: "Ajouter un projet",
|
||||
projectName: "Nom",
|
||||
projectColor: "Couleur",
|
||||
noProjects: "Aucun projet",
|
||||
assignProject: "Attribuer un projet",
|
||||
removeProject: "Retirer le projet",
|
||||
weekdayFormat: "Format des jours",
|
||||
weekdayFormatFull: "Nom complet (lundi)",
|
||||
weekdayFormatShort: "Abrégé (lun.)",
|
||||
weekdayFormatNarrow: "Étroit (L)",
|
||||
weekdayFormatCustom: "Personnalisé",
|
||||
customWeekdayNamesMon: "Lu; Ma; Me; Je; Ve; Sa; Di",
|
||||
customWeekdayNamesSun: "Di; Lu; Ma; Me; Je; Ve; Sa",
|
||||
weekdayCase: "Casse des jours",
|
||||
weekdayCaseNormal: "Normal (lundi)",
|
||||
weekdayCaseCapitalize: "Majuscule (Lundi)",
|
||||
weekdayCaseUppercase: "Majuscules (LUNDI)",
|
||||
},
|
||||
es: {
|
||||
settings: "Ajustes",
|
||||
general: "General",
|
||||
calendar: "Conexiones",
|
||||
localisation: "Localización",
|
||||
account: "Cuenta",
|
||||
runningList: "Lista continua (pasar tareas a hoy)",
|
||||
protectEventTimes: "Proteger horarios de eventos",
|
||||
showTimeGrid: "Mostrar cuadrícula horaria",
|
||||
timeSlotDuration: "Duración de los intervalos",
|
||||
viewStyle: "Estilo de vista",
|
||||
simpleView: "Simple",
|
||||
calendarView: "Calendario",
|
||||
listView: "Lista",
|
||||
language: "Idioma",
|
||||
dateFormat: "Formato de fecha",
|
||||
timeFormat: "Formato de hora",
|
||||
saveChanges: "Guardar cambios",
|
||||
connectedCalendars: "Calendarios conectados",
|
||||
connectMore: "Conectar más",
|
||||
connectGoogle: "Conectar Google Calendar",
|
||||
connectApple: "Conectar calendario de Apple",
|
||||
connectSynology: "Conectar Synology",
|
||||
noCalendars: "No hay calendarios conectados.",
|
||||
dataPrivacy: "Datos y privacidad",
|
||||
downloadData: "Descargar mis datos",
|
||||
deleteAccount: "Eliminar cuenta",
|
||||
name: "Nombre",
|
||||
email: "Correo electrónico",
|
||||
timezone: "Zona horaria",
|
||||
changePassword: "Cambiar contraseña",
|
||||
newPassword: "Nueva contraseña",
|
||||
confirmPassword: "Confirmar contraseña",
|
||||
someday: "ALGÚN DÍA",
|
||||
lists: "Listas",
|
||||
loading: "Cargando tus tareas…",
|
||||
sycing: "Sincronizando…",
|
||||
synced: "Sincronizado",
|
||||
localization: "Localización",
|
||||
allDayEvents: "EVENTOS DE TODO EL DÍA",
|
||||
syncCalendar: "Sincronizar calendario",
|
||||
toggleDarkMode: "Alternar modo oscuro",
|
||||
signOut: "Cerrar sesión",
|
||||
startHour: "Inicio del día",
|
||||
endHour: "Fin del día",
|
||||
weekAbbr: "S",
|
||||
goalOfWeek: "Objetivo de la semana",
|
||||
goalScope: "Alcance del objetivo",
|
||||
goalScopeWeek: "Por semana",
|
||||
goalScopeDay: "Por día",
|
||||
goalFallback: "Tipo de objetivo por defecto",
|
||||
defaultGoal: "Objetivo predeterminado personalizado",
|
||||
showTaskCheckboxes: "Mostrar casillas en las tareas",
|
||||
showSomeday: "Mostrar sección Algún día",
|
||||
showAllDay: "Mostrar sección Todo el día",
|
||||
allDayPosition: "Posición de eventos de todo el día",
|
||||
allDayAbove: "Arriba",
|
||||
allDayBelow: "Abajo",
|
||||
newPasswordDesc: "Dejar en blanco para conservar la contraseña actual.",
|
||||
dateAlignment: "Alineación de la fecha",
|
||||
dateVerticalAlign: "Alineación vertical de la fecha",
|
||||
alignTop: "Arriba",
|
||||
alignMiddle: "Centro",
|
||||
alignBottom: "Abajo",
|
||||
dateLayout: "Disposición de la fecha",
|
||||
alignmentLeft: "Izquierda",
|
||||
alignmentCenter: "Centro",
|
||||
alignmentRight: "Derecha",
|
||||
alignmentTight: "Compacto",
|
||||
backupRestore: "Copia de seguridad y restauración",
|
||||
backupRestoreDesc: "Exporta todas tus tareas, listas y proyectos como archivo JSON. Puedes editar el archivo y volver a importarlo.",
|
||||
exportAllData: "Exportar todos los datos (JSON)",
|
||||
importData: "Importar datos",
|
||||
importMode: "Modo de importación",
|
||||
importModeMerge: "Combinar",
|
||||
importModeMergeDesc: "Añadir los datos importados junto a las tareas existentes",
|
||||
importModeReplace: "Reemplazar",
|
||||
importModeReplaceDesc: "Eliminar todos los datos existentes y reemplazarlos con los datos importados",
|
||||
importReplaceWarning: "Advertencia: ¡Se eliminarán permanentemente todas tus tareas, listas y proyectos actuales!",
|
||||
importSelectFile: "Seleccionar archivo JSON…",
|
||||
importButton: "Importar",
|
||||
importing: "Importando…",
|
||||
exporting: "Exportando…",
|
||||
projects: "Proyectos",
|
||||
projectsDesc: "Organiza las tareas con proyectos de colores",
|
||||
addProject: "Añadir proyecto",
|
||||
projectName: "Nombre",
|
||||
projectColor: "Color",
|
||||
noProjects: "Aún no hay proyectos",
|
||||
assignProject: "Asignar proyecto",
|
||||
removeProject: "Quitar proyecto",
|
||||
weekdayFormat: "Formato de los días",
|
||||
weekdayFormatFull: "Nombre completo (lunes)",
|
||||
weekdayFormatShort: "Abreviado (lun.)",
|
||||
weekdayFormatNarrow: "Estrecho (L)",
|
||||
weekdayFormatCustom: "Personalizado",
|
||||
customWeekdayNamesMon: "Lu; Ma; Mi; Ju; Vi; Sá; Do",
|
||||
customWeekdayNamesSun: "Do; Lu; Ma; Mi; Ju; Vi; Sá",
|
||||
weekdayCase: "Mayúsculas de los días",
|
||||
weekdayCaseNormal: "Normal (lunes)",
|
||||
weekdayCaseCapitalize: "Mayúscula inicial (Lunes)",
|
||||
weekdayCaseUppercase: "Mayúsculas (LUNES)",
|
||||
},
|
||||
it: {
|
||||
settings: "Impostazioni",
|
||||
general: "Generali",
|
||||
calendar: "Connessioni",
|
||||
localisation: "Localizzazione",
|
||||
account: "Account",
|
||||
runningList: "Lista continua (sposta le attività a oggi)",
|
||||
protectEventTimes: "Proteggi gli orari degli eventi",
|
||||
showTimeGrid: "Mostra griglia oraria",
|
||||
timeSlotDuration: "Durata degli intervalli",
|
||||
viewStyle: "Stile di visualizzazione",
|
||||
simpleView: "Semplice",
|
||||
calendarView: "Calendario",
|
||||
listView: "Lista",
|
||||
language: "Lingua",
|
||||
dateFormat: "Formato data",
|
||||
timeFormat: "Formato ora",
|
||||
saveChanges: "Salva modifiche",
|
||||
connectedCalendars: "Calendari collegati",
|
||||
connectMore: "Collega altri",
|
||||
connectGoogle: "Collega Google Calendar",
|
||||
connectApple: "Collega il calendario Apple",
|
||||
connectSynology: "Collega Synology",
|
||||
noCalendars: "Nessun calendario collegato.",
|
||||
dataPrivacy: "Dati e privacy",
|
||||
downloadData: "Scarica i miei dati",
|
||||
deleteAccount: "Elimina account",
|
||||
name: "Nome",
|
||||
email: "E-mail",
|
||||
timezone: "Fuso orario",
|
||||
changePassword: "Cambia password",
|
||||
newPassword: "Nuova password",
|
||||
confirmPassword: "Conferma password",
|
||||
someday: "UN GIORNO",
|
||||
lists: "Liste",
|
||||
loading: "Caricamento delle attività…",
|
||||
sycing: "Sincronizzazione…",
|
||||
synced: "Sincronizzato",
|
||||
localization: "Localizzazione",
|
||||
allDayEvents: "EVENTI GIORNATA INTERA",
|
||||
syncCalendar: "Sincronizza calendario",
|
||||
toggleDarkMode: "Attiva/disattiva modalità scura",
|
||||
signOut: "Esci",
|
||||
startHour: "Inizio giornata",
|
||||
endHour: "Fine giornata",
|
||||
weekAbbr: "S",
|
||||
goalOfWeek: "Obiettivo della settimana",
|
||||
goalScope: "Ambito dell'obiettivo",
|
||||
goalScopeWeek: "Per settimana",
|
||||
goalScopeDay: "Per giorno",
|
||||
goalFallback: "Tipo di obiettivo predefinito",
|
||||
defaultGoal: "Obiettivo predefinito personalizzato",
|
||||
showTaskCheckboxes: "Mostra caselle di spunta sulle attività",
|
||||
showSomeday: "Mostra sezione Un giorno",
|
||||
showAllDay: "Mostra sezione Giornata intera",
|
||||
allDayPosition: "Posizione eventi giornata intera",
|
||||
allDayAbove: "Sopra",
|
||||
allDayBelow: "Sotto",
|
||||
newPasswordDesc: "Lascia vuoto per mantenere la password attuale.",
|
||||
dateAlignment: "Allineamento della data",
|
||||
dateVerticalAlign: "Allineamento verticale della data",
|
||||
alignTop: "In alto",
|
||||
alignMiddle: "Al centro",
|
||||
alignBottom: "In basso",
|
||||
dateLayout: "Disposizione della data",
|
||||
alignmentLeft: "Sinistra",
|
||||
alignmentCenter: "Centro",
|
||||
alignmentRight: "Destra",
|
||||
alignmentTight: "Compatto",
|
||||
backupRestore: "Backup e ripristino",
|
||||
backupRestoreDesc: "Esporta tutte le attività, le liste e i progetti come file JSON. Puoi modificare il file e reimportarlo.",
|
||||
exportAllData: "Esporta tutti i dati (JSON)",
|
||||
importData: "Importa dati",
|
||||
importMode: "Modalità di importazione",
|
||||
importModeMerge: "Unisci",
|
||||
importModeMergeDesc: "Aggiungere i dati importati alle attività esistenti",
|
||||
importModeReplace: "Sostituisci",
|
||||
importModeReplaceDesc: "Elimina tutti i dati esistenti e sostituiscili con i dati importati",
|
||||
importReplaceWarning: "Attenzione: tutte le attività, le liste e i progetti attuali verranno eliminati definitivamente!",
|
||||
importSelectFile: "Seleziona file JSON…",
|
||||
importButton: "Importa",
|
||||
importing: "Importazione…",
|
||||
exporting: "Esportazione…",
|
||||
projects: "Progetti",
|
||||
projectsDesc: "Organizza le attività con progetti colorati",
|
||||
addProject: "Aggiungi progetto",
|
||||
projectName: "Nome",
|
||||
projectColor: "Colore",
|
||||
noProjects: "Nessun progetto",
|
||||
assignProject: "Assegna progetto",
|
||||
removeProject: "Rimuovi progetto",
|
||||
weekdayFormat: "Formato dei giorni",
|
||||
weekdayFormatFull: "Nome completo (lunedì)",
|
||||
weekdayFormatShort: "Abbreviato (lun)",
|
||||
weekdayFormatNarrow: "Stretto (L)",
|
||||
weekdayFormatCustom: "Personalizzato",
|
||||
customWeekdayNamesMon: "Lu; Ma; Me; Gi; Ve; Sa; Do",
|
||||
customWeekdayNamesSun: "Do; Lu; Ma; Me; Gi; Ve; Sa",
|
||||
weekdayCase: "Maiuscole dei giorni",
|
||||
weekdayCaseNormal: "Normale (lunedì)",
|
||||
weekdayCaseCapitalize: "Iniziale maiuscola (Lunedì)",
|
||||
weekdayCaseUppercase: "Maiuscolo (LUNEDÌ)",
|
||||
},
|
||||
};
|
||||
|
||||
// Date utilities
|
||||
@ -884,8 +1193,8 @@ export default function WeeklyView() {
|
||||
yearFontFamily: "Oswald",
|
||||
yearFontSize: "1.5rem",
|
||||
yearFontWeight: "700",
|
||||
quoteSourceUrl: "https://recite.vercel.app/api/random",
|
||||
quoteSourceUrls: ["https://recite.vercel.app/api/random"],
|
||||
quoteSourceUrl: "",
|
||||
quoteSourceUrls: [],
|
||||
});
|
||||
const [motivationalQuote, setMotivationalQuote] = useState("");
|
||||
const [showSummary, setShowSummary] = useState(false);
|
||||
@ -946,6 +1255,7 @@ export default function WeeklyView() {
|
||||
const [isSearchOpen, setIsSearchOpen] = useState(false);
|
||||
const [isRecurringTasksOpen, setIsRecurringTasksOpen] = useState(false);
|
||||
const [showDatePicker, setShowDatePicker] = useState(false);
|
||||
const datePickerBtnRef = useRef<HTMLButtonElement>(null);
|
||||
const [showQuickSettings, setShowQuickSettings] = useState(false);
|
||||
|
||||
const [focusTimerDuration, setFocusTimerDuration] = useState(25);
|
||||
@ -3802,13 +4112,27 @@ export default function WeeklyView() {
|
||||
: "var(--weekly-font-headline)",
|
||||
"--weekly-date-size": scaleRem(profile.dateFontSize || "0.65rem"),
|
||||
"--weekly-date-weight": profile.dateFontWeight || "400",
|
||||
"--weekly-time-task-font": fontVal(profile.timeTaskFontFamily)
|
||||
? `"${fontVal(profile.timeTaskFontFamily)}", sans-serif`
|
||||
: fontVal(profile.taskFontFamily)
|
||||
? `"${fontVal(profile.taskFontFamily)}", sans-serif`
|
||||
: "var(--weekly-font)",
|
||||
"--weekly-time-task-size": scaleRem(profile.timeTaskFontSize || profile.taskFontSize || "0.9rem"),
|
||||
"--weekly-time-task-weight": profile.timeTaskFontWeight || profile.taskFontWeight || "400",
|
||||
"--weekly-time-task-font": (() => {
|
||||
// If time task font is explicitly customized (not default "Inter"), use it
|
||||
// Otherwise inherit from task font
|
||||
const ttf = fontVal(profile.timeTaskFontFamily);
|
||||
const tf = fontVal(profile.taskFontFamily);
|
||||
const isDefault = !ttf || ttf === "Inter";
|
||||
if (!isDefault) return `"${ttf}", sans-serif`;
|
||||
if (tf) return `"${tf}", sans-serif`;
|
||||
return "var(--weekly-font)";
|
||||
})(),
|
||||
"--weekly-time-task-size": scaleRem(
|
||||
// If time task size is the old default 0.75rem, use task size instead
|
||||
profile.timeTaskFontSize && profile.timeTaskFontSize !== "0.75rem"
|
||||
? profile.timeTaskFontSize
|
||||
: profile.taskFontSize || "0.9rem"
|
||||
),
|
||||
"--weekly-time-task-weight":
|
||||
// If time task weight is the old default 500, use task weight instead
|
||||
profile.timeTaskFontWeight && profile.timeTaskFontWeight !== "500"
|
||||
? profile.timeTaskFontWeight
|
||||
: profile.taskFontWeight || "400",
|
||||
"--weekly-font":
|
||||
"var(--font-body)" /* Force default body font as requested */,
|
||||
"--weekly-task-font": fontVal(profile.taskFontFamily)
|
||||
@ -4758,8 +5082,9 @@ export default function WeeklyView() {
|
||||
</div>
|
||||
|
||||
{/* Date Picker Toggle */}
|
||||
<div className="relative" style={{ zIndex: showDatePicker ? 2001 : "auto" }}>
|
||||
<div>
|
||||
<button
|
||||
ref={datePickerBtnRef}
|
||||
className={`p-1.5 hover:bg-gray-100 rounded-md transition-colors ${showDatePicker ? "text-teal-600 bg-teal-50" : "text-gray-500 hover:text-black"}`}
|
||||
onClick={() => setShowDatePicker(!showDatePicker)}
|
||||
title="Jump to date"
|
||||
@ -4775,6 +5100,7 @@ export default function WeeklyView() {
|
||||
}}
|
||||
onClose={() => setShowDatePicker(false)}
|
||||
language={language}
|
||||
anchorRef={datePickerBtnRef}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
@ -8141,7 +8467,7 @@ function SettingsSidebar({
|
||||
showSubHourSlots: true,
|
||||
allDayPosition: "below",
|
||||
goalFallbackType: "quote",
|
||||
quoteSourceUrl: "https://recite.vercel.app/api/random",
|
||||
quoteSourceUrl: "",
|
||||
headlineFont: "Inter",
|
||||
headlineFontSize: "1.25rem",
|
||||
headlineFontWeight: "900",
|
||||
@ -9553,9 +9879,10 @@ function SettingsSidebar({
|
||||
}}
|
||||
>
|
||||
<option value="en">English</option>
|
||||
<option value="de">German</option>
|
||||
<option value="fr">French</option>
|
||||
<option value="es">Spanish</option>
|
||||
<option value="de">Deutsch</option>
|
||||
<option value="fr">Français</option>
|
||||
<option value="es">Español</option>
|
||||
<option value="it">Italiano</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
@ -11378,13 +11705,13 @@ function SettingsSidebar({
|
||||
API-Datenquellen (URLs)
|
||||
</label>
|
||||
<div style={{ display: "flex", flexDirection: "column", gap: "8px" }}>
|
||||
{(profile.quoteSourceUrls || [profile.quoteSourceUrl || "https://recite.vercel.app/api/random"]).map((url: string, idx: number) => (
|
||||
{(profile.quoteSourceUrls || [profile.quoteSourceUrl || ""]).map((url: string, idx: number) => (
|
||||
<div key={idx} style={{ display: "flex", gap: "8px" }}>
|
||||
<input
|
||||
type="text"
|
||||
value={url}
|
||||
onChange={(e) => {
|
||||
const newUrls = [...(profile.quoteSourceUrls || [profile.quoteSourceUrl || "https://recite.vercel.app/api/random"])];
|
||||
const newUrls = [...(profile.quoteSourceUrls || [profile.quoteSourceUrl || ""])];
|
||||
newUrls[idx] = e.target.value;
|
||||
setProfile((p) => ({ ...p, quoteSourceUrls: newUrls }));
|
||||
}}
|
||||
@ -11402,7 +11729,7 @@ function SettingsSidebar({
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => {
|
||||
const newUrls = (profile.quoteSourceUrls || [profile.quoteSourceUrl || "https://recite.vercel.app/api/random"]).filter((_val: string, i: number) => i !== idx);
|
||||
const newUrls = (profile.quoteSourceUrls || [profile.quoteSourceUrl || ""]).filter((_val: string, i: number) => i !== idx);
|
||||
setProfile((p) => ({ ...p, quoteSourceUrls: newUrls }));
|
||||
}}
|
||||
style={{
|
||||
@ -11421,7 +11748,7 @@ function SettingsSidebar({
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => {
|
||||
const newUrls = [...(profile.quoteSourceUrls || [profile.quoteSourceUrl || "https://recite.vercel.app/api/random"]), ""];
|
||||
const newUrls = [...(profile.quoteSourceUrls || [profile.quoteSourceUrl || ""]), ""];
|
||||
setProfile((p) => ({ ...p, quoteSourceUrls: newUrls }));
|
||||
}}
|
||||
style={{
|
||||
|
||||
Loading…
Reference in New Issue
Block a user