Implement custom header formatting (v1.33.0)
This commit is contained in:
parent
889282381f
commit
b827f39722
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "my-weekly-todo-list",
|
"name": "my-weekly-todo-list",
|
||||||
"version": "1.32.0",
|
"version": "1.33.0",
|
||||||
"description": "A web-based weekly task management application that organizes to-dos and calendar events in a single, intuitive weekly view",
|
"description": "A web-based weekly task management application that organizes to-dos and calendar events in a single, intuitive weekly view",
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
|||||||
@ -263,6 +263,8 @@ const translations: Record<string, any> = {
|
|||||||
headerDisplayMonth: "Month Name",
|
headerDisplayMonth: "Month Name",
|
||||||
headerDisplayMonthYear: "Month & Year",
|
headerDisplayMonthYear: "Month & Year",
|
||||||
headerDisplayDate: "Full Date",
|
headerDisplayDate: "Full Date",
|
||||||
|
headerDisplayCustom: "Custom",
|
||||||
|
headerCustomFormatLabel: "Format string (e.g. DD.MM.YYYY)",
|
||||||
language: "Language",
|
language: "Language",
|
||||||
dateFormat: "Date Format",
|
dateFormat: "Date Format",
|
||||||
timeFormat: "Time Format",
|
timeFormat: "Time Format",
|
||||||
@ -471,6 +473,8 @@ const translations: Record<string, any> = {
|
|||||||
headerDisplayMonth: "Monatsname",
|
headerDisplayMonth: "Monatsname",
|
||||||
headerDisplayMonthYear: "Monat & Jahr",
|
headerDisplayMonthYear: "Monat & Jahr",
|
||||||
headerDisplayDate: "Vollständiges Datum",
|
headerDisplayDate: "Vollständiges Datum",
|
||||||
|
headerDisplayCustom: "Benutzerdefiniert",
|
||||||
|
headerCustomFormatLabel: "Format (z.B. DD.MM.YYYY)",
|
||||||
listView: "Liste",
|
listView: "Liste",
|
||||||
notes: "Notizen",
|
notes: "Notizen",
|
||||||
notesSidebar: "Notizen-Seitenleiste",
|
notesSidebar: "Notizen-Seitenleiste",
|
||||||
@ -682,6 +686,8 @@ const translations: Record<string, any> = {
|
|||||||
headerDisplayMonth: "Nom du mois",
|
headerDisplayMonth: "Nom du mois",
|
||||||
headerDisplayMonthYear: "Mois & Année",
|
headerDisplayMonthYear: "Mois & Année",
|
||||||
headerDisplayDate: "Date complète",
|
headerDisplayDate: "Date complète",
|
||||||
|
headerDisplayCustom: "Personnalisé",
|
||||||
|
headerCustomFormatLabel: "Format (ex: DD.MM.YYYY)",
|
||||||
language: "Langue",
|
language: "Langue",
|
||||||
dateFormat: "Format de date",
|
dateFormat: "Format de date",
|
||||||
timeFormat: "Format d'heure",
|
timeFormat: "Format d'heure",
|
||||||
@ -891,6 +897,8 @@ const translations: Record<string, any> = {
|
|||||||
headerDisplayMonth: "Nombre del mes",
|
headerDisplayMonth: "Nombre del mes",
|
||||||
headerDisplayMonthYear: "Mes y Año",
|
headerDisplayMonthYear: "Mes y Año",
|
||||||
headerDisplayDate: "Fecha completa",
|
headerDisplayDate: "Fecha completa",
|
||||||
|
headerDisplayCustom: "Personalizado",
|
||||||
|
headerCustomFormatLabel: "Formato (ej. DD.MM.YYYY)",
|
||||||
language: "Idioma",
|
language: "Idioma",
|
||||||
dateFormat: "Formato de fecha",
|
dateFormat: "Formato de fecha",
|
||||||
timeFormat: "Formato de hora",
|
timeFormat: "Formato de hora",
|
||||||
@ -1100,6 +1108,8 @@ const translations: Record<string, any> = {
|
|||||||
headerDisplayMonth: "Nome del mese",
|
headerDisplayMonth: "Nome del mese",
|
||||||
headerDisplayMonthYear: "Mese e Anno",
|
headerDisplayMonthYear: "Mese e Anno",
|
||||||
headerDisplayDate: "Data completa",
|
headerDisplayDate: "Data completa",
|
||||||
|
headerDisplayCustom: "Personalizzato",
|
||||||
|
headerCustomFormatLabel: "Formato (es. DD.MM.YYYY)",
|
||||||
language: "Lingua",
|
language: "Lingua",
|
||||||
dateFormat: "Formato data",
|
dateFormat: "Formato data",
|
||||||
timeFormat: "Formato ora",
|
timeFormat: "Formato ora",
|
||||||
@ -1424,6 +1434,37 @@ function getCWReferenceDate(days: Date[]): Date {
|
|||||||
return new Date(first.getTime() - distToMonday * 86400000);
|
return new Date(first.getTime() - distToMonday * 86400000);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Format a custom header string using tokens
|
||||||
|
function formatCustomHeader(format: string, days: Date[], language: string, t: any): string {
|
||||||
|
if (!format) return "";
|
||||||
|
const refDate = getCWReferenceDate(days);
|
||||||
|
const today = new Date();
|
||||||
|
|
||||||
|
let result = format;
|
||||||
|
|
||||||
|
// [TODAY] token
|
||||||
|
result = result.replace(/\[TODAY\]/g, today.toLocaleDateString(language, { day: '2-digit', month: '2-digit', year: 'numeric' }));
|
||||||
|
|
||||||
|
// YYYY
|
||||||
|
result = result.replace(/YYYY/g, refDate.getFullYear().toString());
|
||||||
|
|
||||||
|
// WW (ISO Week)
|
||||||
|
result = result.replace(/WW/g, getWeekNumber(refDate).toString().padStart(2, '0'));
|
||||||
|
result = result.replace(/W/g, getWeekNumber(refDate).toString());
|
||||||
|
|
||||||
|
// MMMM (Month Name Long)
|
||||||
|
result = result.replace(/MMMM/g, refDate.toLocaleDateString(language, { month: 'long' }));
|
||||||
|
result = result.replace(/MMM/g, refDate.toLocaleDateString(language, { month: 'short' }));
|
||||||
|
result = result.replace(/MM/g, (refDate.getMonth() + 1).toString().padStart(2, '0'));
|
||||||
|
result = result.replace(/M/g, (refDate.getMonth() + 1).toString());
|
||||||
|
|
||||||
|
// DD (Day)
|
||||||
|
result = result.replace(/DD/g, refDate.getDate().toString().padStart(2, '0'));
|
||||||
|
result = result.replace(/D/g, refDate.getDate().toString());
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
// Check if an event is an all-day event
|
// Check if an event is an all-day event
|
||||||
// Defined outside component to avoid stale closure issues in useCallbacks
|
// Defined outside component to avoid stale closure issues in useCallbacks
|
||||||
const isAllDayEvent = (event: CalendarEvent): boolean => {
|
const isAllDayEvent = (event: CalendarEvent): boolean => {
|
||||||
@ -1803,7 +1844,8 @@ export default function WeeklyView() {
|
|||||||
weekdayCase?: "normal" | "capitalize" | "uppercase";
|
weekdayCase?: "normal" | "capitalize" | "uppercase";
|
||||||
customWeekdayNames?: string;
|
customWeekdayNames?: string;
|
||||||
dateVerticalAlign?: "top" | "middle" | "bottom";
|
dateVerticalAlign?: "top" | "middle" | "bottom";
|
||||||
headerDisplay?: "kw" | "month" | "month_year" | "date";
|
headerDisplay?: "kw" | "month" | "month_year" | "date" | "custom";
|
||||||
|
headerCustomFormat?: string;
|
||||||
lightTheme?: any;
|
lightTheme?: any;
|
||||||
darkTheme?: any;
|
darkTheme?: any;
|
||||||
}>({
|
}>({
|
||||||
@ -5474,6 +5516,8 @@ export default function WeeklyView() {
|
|||||||
<span>{getCWReferenceDate(getVisibleDays()).toLocaleDateString(language, { day: '2-digit', month: '2-digit', year: 'numeric' })}</span>
|
<span>{getCWReferenceDate(getVisibleDays()).toLocaleDateString(language, { day: '2-digit', month: '2-digit', year: 'numeric' })}</span>
|
||||||
) : profile.headerDisplay === "month_year" ? (
|
) : profile.headerDisplay === "month_year" ? (
|
||||||
<span>{getCWReferenceDate(getVisibleDays()).toLocaleDateString(language, { month: 'long', year: 'numeric' })}</span>
|
<span>{getCWReferenceDate(getVisibleDays()).toLocaleDateString(language, { month: 'long', year: 'numeric' })}</span>
|
||||||
|
) : profile.headerDisplay === "custom" ? (
|
||||||
|
<span>{formatCustomHeader(profile.headerCustomFormat || "KW WW | YYYY", getVisibleDays(), language, t)}</span>
|
||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
<span>KW{getWeekNumber(getCWReferenceDate(getVisibleDays())).toString().padStart(2, "0")}</span>
|
<span>KW{getWeekNumber(getCWReferenceDate(getVisibleDays())).toString().padStart(2, "0")}</span>
|
||||||
@ -5731,6 +5775,8 @@ export default function WeeklyView() {
|
|||||||
? getCWReferenceDate(getVisibleDays()).toLocaleDateString(language, { month: "long", year: "numeric" })
|
? getCWReferenceDate(getVisibleDays()).toLocaleDateString(language, { month: "long", year: "numeric" })
|
||||||
: profile.headerDisplay === "date"
|
: profile.headerDisplay === "date"
|
||||||
? getCWReferenceDate(getVisibleDays()).toLocaleDateString(language, { day: '2-digit', month: '2-digit', year: 'numeric' })
|
? getCWReferenceDate(getVisibleDays()).toLocaleDateString(language, { day: '2-digit', month: '2-digit', year: 'numeric' })
|
||||||
|
: profile.headerDisplay === "custom"
|
||||||
|
? formatCustomHeader(profile.headerCustomFormat || "KW WW | YYYY", getVisibleDays(), language, t)
|
||||||
: `KW ${getWeekNumber(getCWReferenceDate(getVisibleDays())).toString().padStart(2, "0")}`
|
: `KW ${getWeekNumber(getCWReferenceDate(getVisibleDays())).toString().padStart(2, "0")}`
|
||||||
}
|
}
|
||||||
</span>
|
</span>
|
||||||
@ -9694,7 +9740,8 @@ function SettingsSidebar({
|
|||||||
weekdayCase?: "normal" | "capitalize" | "uppercase";
|
weekdayCase?: "normal" | "capitalize" | "uppercase";
|
||||||
customWeekdayNames?: string;
|
customWeekdayNames?: string;
|
||||||
dateVerticalAlign?: "top" | "middle" | "bottom";
|
dateVerticalAlign?: "top" | "middle" | "bottom";
|
||||||
headerDisplay?: "kw" | "month" | "month_year" | "date";
|
headerDisplay?: "kw" | "month" | "month_year" | "date" | "custom";
|
||||||
|
headerCustomFormat?: string;
|
||||||
}>({
|
}>({
|
||||||
name: "",
|
name: "",
|
||||||
email: "",
|
email: "",
|
||||||
@ -9709,6 +9756,7 @@ function SettingsSidebar({
|
|||||||
focusTimerDuration: 25,
|
focusTimerDuration: 25,
|
||||||
focusBreakDuration: 5,
|
focusBreakDuration: 5,
|
||||||
headerDisplay: "kw",
|
headerDisplay: "kw",
|
||||||
|
headerCustomFormat: "KW WW | YYYY",
|
||||||
cellDuration: 30,
|
cellDuration: 30,
|
||||||
viewStyle: "list",
|
viewStyle: "list",
|
||||||
fontSize: "M",
|
fontSize: "M",
|
||||||
@ -10852,7 +10900,37 @@ function SettingsSidebar({
|
|||||||
>
|
>
|
||||||
{t.headerDisplayDate}
|
{t.headerDisplayDate}
|
||||||
</button>
|
</button>
|
||||||
|
<button
|
||||||
|
onClick={() => {
|
||||||
|
setProfile({ ...profile, headerDisplay: "custom" });
|
||||||
|
saveSetting("headerDisplay", "custom");
|
||||||
|
}}
|
||||||
|
className={`px-4 py-2 text-sm rounded transition-colors ${profile.headerDisplay === "custom" ? "bg-white shadow-sm font-bold text-black" : "text-gray-500 hover:text-gray-900 hover:bg-gray-200 dark:hover:bg-gray-700"}`}
|
||||||
|
>
|
||||||
|
{t.headerDisplayCustom}
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
{profile.headerDisplay === "custom" && (
|
||||||
|
<div className="mt-2">
|
||||||
|
<label style={{ fontSize: "0.75rem", color: "var(--weekly-settings-label)", display: "block", marginBottom: "4px" }}>
|
||||||
|
{t.headerCustomFormatLabel}
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
value={profile.headerCustomFormat || ""}
|
||||||
|
onChange={(e) => {
|
||||||
|
const val = e.target.value;
|
||||||
|
setProfile({ ...profile, headerCustomFormat: val });
|
||||||
|
saveSetting("headerCustomFormat", val);
|
||||||
|
}}
|
||||||
|
placeholder="KW WW | YYYY or DD.MM.YYYY"
|
||||||
|
className="w-full px-3 py-1.5 text-sm rounded border border-gray-300 dark:border-gray-600 dark:bg-gray-700 dark:text-white"
|
||||||
|
/>
|
||||||
|
<div className="mt-1 text-[10px] text-gray-500 leading-tight">
|
||||||
|
Tokens: WW (Week), YYYY (Year), MMMM (Month Name), MM (Month Num), DD (Day), [TODAY] (Active Date)
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Kanban Stages Settings */}
|
{/* Kanban Stages Settings */}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user