mirror of
https://github.com/Memo-2023/mana-monorepo.git
synced 2026-05-18 05:49:41 +02:00
feat(calendar): add context menu to CalendarHeader with display settings
Add right-click context menu to the calendar header for customizing: - Compact view toggle - Weekday format (full/short/hidden) - Date visibility toggle - Month display toggle (e.g., "13.12.") 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
78fb495ba2
commit
1f7b93af21
3 changed files with 206 additions and 7 deletions
|
|
@ -1,9 +1,50 @@
|
|||
<script lang="ts">
|
||||
import { viewStore } from '$lib/stores/view.svelte';
|
||||
import { settingsStore } from '$lib/stores/settings.svelte';
|
||||
import { format } from 'date-fns';
|
||||
import { de } from 'date-fns/locale';
|
||||
import CalendarHeaderContextMenu from './CalendarHeaderContextMenu.svelte';
|
||||
|
||||
// Format title based on view type
|
||||
let contextMenu: CalendarHeaderContextMenu;
|
||||
|
||||
// Get weekday format string based on setting
|
||||
function getWeekdayFormat(): string {
|
||||
switch (settingsStore.headerWeekdayFormat) {
|
||||
case 'full':
|
||||
return 'EEEE';
|
||||
case 'short':
|
||||
return 'EEE';
|
||||
case 'hidden':
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
// Get date format string based on settings
|
||||
function getDateFormat(includeYear: boolean = true): string {
|
||||
const parts: string[] = [];
|
||||
|
||||
// Weekday
|
||||
const weekdayFormat = getWeekdayFormat();
|
||||
if (weekdayFormat) {
|
||||
parts.push(weekdayFormat);
|
||||
}
|
||||
|
||||
// Date with optional month
|
||||
if (settingsStore.headerShowDate) {
|
||||
if (settingsStore.headerAlwaysShowMonth) {
|
||||
parts.push(includeYear ? 'd.M. MMMM yyyy' : 'd.M.');
|
||||
} else {
|
||||
parts.push(includeYear ? 'd. MMMM yyyy' : 'd.');
|
||||
}
|
||||
} else if (includeYear) {
|
||||
// Only month and year if date is hidden
|
||||
parts.push('MMMM yyyy');
|
||||
}
|
||||
|
||||
return parts.join(', ');
|
||||
}
|
||||
|
||||
// Format title based on view type and settings
|
||||
let title = $derived.by(() => {
|
||||
const date = viewStore.currentDate;
|
||||
const rangeStart = viewStore.viewRange.start;
|
||||
|
|
@ -11,23 +52,26 @@
|
|||
|
||||
// Helper to format date range
|
||||
const formatRange = () => {
|
||||
const showMonth = settingsStore.headerAlwaysShowMonth;
|
||||
const startFormat = showMonth ? 'd.M.' : 'd.';
|
||||
|
||||
if (rangeStart.getMonth() === rangeEnd.getMonth()) {
|
||||
return (
|
||||
format(rangeStart, 'd.', { locale: de }) +
|
||||
format(rangeStart, startFormat, { locale: de }) +
|
||||
' - ' +
|
||||
format(rangeEnd, 'd. MMMM yyyy', { locale: de })
|
||||
format(rangeEnd, showMonth ? 'd.M. MMMM yyyy' : 'd. MMMM yyyy', { locale: de })
|
||||
);
|
||||
}
|
||||
return (
|
||||
format(rangeStart, 'd. MMM', { locale: de }) +
|
||||
format(rangeStart, showMonth ? 'd.M. MMM' : 'd. MMM', { locale: de }) +
|
||||
' - ' +
|
||||
format(rangeEnd, 'd. MMM yyyy', { locale: de })
|
||||
format(rangeEnd, showMonth ? 'd.M. MMM yyyy' : 'd. MMM yyyy', { locale: de })
|
||||
);
|
||||
};
|
||||
|
||||
switch (viewStore.viewType) {
|
||||
case 'day':
|
||||
return format(date, 'EEEE, d. MMMM yyyy', { locale: de });
|
||||
return format(date, getDateFormat(true), { locale: de });
|
||||
case '5day':
|
||||
case 'week':
|
||||
case '10day':
|
||||
|
|
@ -43,16 +87,29 @@
|
|||
return format(date, 'MMMM yyyy', { locale: de });
|
||||
}
|
||||
});
|
||||
|
||||
function handleContextMenu(e: MouseEvent) {
|
||||
e.preventDefault();
|
||||
contextMenu.show(e.clientX, e.clientY);
|
||||
}
|
||||
</script>
|
||||
|
||||
<header class="calendar-header">
|
||||
<header
|
||||
class="calendar-header"
|
||||
class:compact={settingsStore.headerCompact}
|
||||
oncontextmenu={handleContextMenu}
|
||||
role="banner"
|
||||
>
|
||||
<h1 class="header-title">{title}</h1>
|
||||
</header>
|
||||
|
||||
<CalendarHeaderContextMenu bind:this={contextMenu} />
|
||||
|
||||
<style>
|
||||
.calendar-header {
|
||||
padding: 0.75rem 1rem;
|
||||
background: transparent;
|
||||
cursor: context-menu;
|
||||
}
|
||||
|
||||
.header-title {
|
||||
|
|
@ -67,4 +124,19 @@
|
|||
font-size: 1rem;
|
||||
}
|
||||
}
|
||||
|
||||
/* Compact variant */
|
||||
.calendar-header.compact {
|
||||
padding: 0.5rem 1rem;
|
||||
}
|
||||
|
||||
.calendar-header.compact .header-title {
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
@media (max-width: 640px) {
|
||||
.calendar-header.compact .header-title {
|
||||
font-size: 0.875rem;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,102 @@
|
|||
<script lang="ts">
|
||||
import { ContextMenu, type ContextMenuItem } from '@manacore/shared-ui';
|
||||
import { ArrowsIn, TextAa, Calendar, CalendarBlank } from '@manacore/shared-icons';
|
||||
import { settingsStore, type WeekdayFormat } from '$lib/stores/settings.svelte';
|
||||
|
||||
// Context menu state
|
||||
let visible = $state(false);
|
||||
let x = $state(0);
|
||||
let y = $state(0);
|
||||
|
||||
// Build menu items based on current settings
|
||||
let menuItems = $derived.by((): ContextMenuItem[] => {
|
||||
return [
|
||||
{
|
||||
id: 'compact',
|
||||
label: 'Kompakte Ansicht',
|
||||
icon: ArrowsIn,
|
||||
toggle: true,
|
||||
checked: settingsStore.headerCompact,
|
||||
action: () => toggleSetting('headerCompact'),
|
||||
},
|
||||
{
|
||||
id: 'divider-1',
|
||||
label: '',
|
||||
type: 'divider',
|
||||
},
|
||||
{
|
||||
id: 'weekday-full',
|
||||
label: 'Wochentag ausgeschrieben',
|
||||
icon: TextAa,
|
||||
toggle: true,
|
||||
checked: settingsStore.headerWeekdayFormat === 'full',
|
||||
action: () => setWeekdayFormat('full'),
|
||||
},
|
||||
{
|
||||
id: 'weekday-short',
|
||||
label: 'Wochentag gekürzt',
|
||||
icon: TextAa,
|
||||
toggle: true,
|
||||
checked: settingsStore.headerWeekdayFormat === 'short',
|
||||
action: () => setWeekdayFormat('short'),
|
||||
},
|
||||
{
|
||||
id: 'weekday-hidden',
|
||||
label: 'Wochentag ausblenden',
|
||||
icon: TextAa,
|
||||
toggle: true,
|
||||
checked: settingsStore.headerWeekdayFormat === 'hidden',
|
||||
action: () => setWeekdayFormat('hidden'),
|
||||
},
|
||||
{
|
||||
id: 'divider-2',
|
||||
label: '',
|
||||
type: 'divider',
|
||||
},
|
||||
{
|
||||
id: 'show-date',
|
||||
label: 'Datum anzeigen',
|
||||
icon: Calendar,
|
||||
toggle: true,
|
||||
checked: settingsStore.headerShowDate,
|
||||
action: () => toggleSetting('headerShowDate'),
|
||||
},
|
||||
{
|
||||
id: 'always-show-month',
|
||||
label: 'Monat immer anzeigen',
|
||||
icon: CalendarBlank,
|
||||
toggle: true,
|
||||
checked: settingsStore.headerAlwaysShowMonth,
|
||||
action: () => toggleSetting('headerAlwaysShowMonth'),
|
||||
},
|
||||
];
|
||||
});
|
||||
|
||||
function toggleSetting(key: keyof typeof settingsStore.settings) {
|
||||
const currentValue = settingsStore.settings[key];
|
||||
if (typeof currentValue === 'boolean') {
|
||||
settingsStore.set(key, !currentValue);
|
||||
}
|
||||
}
|
||||
|
||||
function setWeekdayFormat(format: WeekdayFormat) {
|
||||
settingsStore.set('headerWeekdayFormat', format);
|
||||
}
|
||||
|
||||
function handleClose() {
|
||||
visible = false;
|
||||
}
|
||||
|
||||
// Export show function to be called from parent
|
||||
export function show(clientX: number, clientY: number) {
|
||||
x = clientX;
|
||||
y = clientY;
|
||||
visible = true;
|
||||
}
|
||||
|
||||
export function hide() {
|
||||
visible = false;
|
||||
}
|
||||
</script>
|
||||
|
||||
<ContextMenu {visible} {x} {y} items={menuItems} onClose={handleClose} />
|
||||
|
|
@ -13,6 +13,7 @@ import { userSettings } from './user-settings.svelte';
|
|||
export type WeekStartDay = 0 | 1; // 0 = Sunday, 1 = Monday
|
||||
export type TimeFormat = '24h' | '12h';
|
||||
export type AllDayDisplayMode = 'header' | 'block'; // header = separate row, block = full day block in grid
|
||||
export type WeekdayFormat = 'full' | 'short' | 'hidden';
|
||||
|
||||
export interface CalendarAppSettings {
|
||||
// View settings
|
||||
|
|
@ -26,6 +27,12 @@ export interface CalendarAppSettings {
|
|||
dayEndHour: number; // Last visible hour (0-23)
|
||||
allDayDisplayMode: AllDayDisplayMode; // How to display all-day events
|
||||
|
||||
// Header settings
|
||||
headerCompact: boolean; // Compact header display
|
||||
headerWeekdayFormat: WeekdayFormat; // Weekday display format
|
||||
headerShowDate: boolean; // Show date in header
|
||||
headerAlwaysShowMonth: boolean; // Always show month (e.g., "13.12.")
|
||||
|
||||
// DateStrip settings
|
||||
dateStripShowMoonPhases: boolean; // Show moon phase indicators
|
||||
dateStripShowEventIndicators: boolean; // Show event dot indicators
|
||||
|
|
@ -61,6 +68,11 @@ const DEFAULT_SETTINGS: CalendarAppSettings = {
|
|||
dayStartHour: 6,
|
||||
dayEndHour: 20,
|
||||
allDayDisplayMode: 'header',
|
||||
// Header defaults
|
||||
headerCompact: false,
|
||||
headerWeekdayFormat: 'full',
|
||||
headerShowDate: true,
|
||||
headerAlwaysShowMonth: false,
|
||||
// DateStrip defaults
|
||||
dateStripShowMoonPhases: true,
|
||||
dateStripShowEventIndicators: true,
|
||||
|
|
@ -175,6 +187,19 @@ export const settingsStore = {
|
|||
get allDayDisplayMode() {
|
||||
return settings.allDayDisplayMode;
|
||||
},
|
||||
// Header settings
|
||||
get headerCompact() {
|
||||
return settings.headerCompact;
|
||||
},
|
||||
get headerWeekdayFormat() {
|
||||
return settings.headerWeekdayFormat;
|
||||
},
|
||||
get headerShowDate() {
|
||||
return settings.headerShowDate;
|
||||
},
|
||||
get headerAlwaysShowMonth() {
|
||||
return settings.headerAlwaysShowMonth;
|
||||
},
|
||||
// DateStrip settings
|
||||
get dateStripShowMoonPhases() {
|
||||
return settings.dateStripShowMoonPhases;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue