feat(calendar): combine calendar/tasks pill with sidebar toggle and mobile splitscreen

- Combine separate Kalender and Aufgaben pills into single tab group
- Add prependElements prop to PillNavigation for tab groups at start
- Clicking Aufgaben tab toggles todo sidebar instead of navigating
- Remove separate /tasks route (no longer needed)
- Implement 50/50 splitscreen layout on mobile (calendar top, todos bottom)
- Add proper flex container hierarchy for mobile layout
- Make TodoSidebarSection fill container on mobile with clean edges
- Add calendar and check-square icons to PillTabGroup
- Export PillTabGroupConfig type from shared-ui

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Till-JS 2025-12-15 02:35:35 +01:00
parent c712cc7995
commit 1395291b49
9 changed files with 420 additions and 511 deletions

View file

@ -101,6 +101,7 @@ export type {
PillNavElement,
PillNavigationProps,
PillTabOption,
PillTabGroupConfig,
ExpandableToolbarProps,
} from './navigation';

View file

@ -200,6 +200,8 @@
showThemeToggle?: boolean;
/** Primary color for active state (CSS custom property or hex) */
primaryColor?: string;
/** Elements to prepend before nav items (tab groups, dividers, nav items) */
prependElements?: PillNavElement[];
/** Additional elements (tab groups, dividers) to show after nav items */
elements?: PillNavElement[];
/** Show logout button */
@ -269,6 +271,7 @@
showLanguageSwitcher = false,
showThemeToggle = true,
primaryColor,
prependElements = [],
elements = [],
showLogout = true,
themeVariantItems = [],
@ -495,6 +498,42 @@
</a>
{/if}
<!-- Prepended Elements (Tab Groups, Dividers, Nav Items) -->
{#each prependElements as element}
{#if isTabGroup(element)}
<PillTabGroup
options={element.options}
value={element.value}
onChange={element.onChange}
sectionLabel={element.sectionLabel}
onContextMenu={element.onContextMenu}
{isSidebarMode}
{primaryColor}
/>
{:else if isDivider(element)}
<div class="pill-divider" class:sidebar-divider={isSidebarMode}></div>
{:else if isNavItem(element)}
<a href={element.href} class="pill glass-pill" class:active={isActive(element.href)}>
{#if element.icon}
{#if phosphorIcons[element.icon]}
{@const IconComponent = phosphorIcons[element.icon]}
<IconComponent size={18} class="pill-icon" />
{:else}
<svg class="pill-icon" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d={getIconPath(element.icon)}
/>
</svg>
{/if}
{/if}
<span class="pill-label">{element.label}</span>
</a>
{/if}
{/each}
<!-- Navigation Items -->
{#each items as item}
<a href={item.href} class="pill glass-pill" class:active={isActive(item.href)}>
@ -533,6 +572,7 @@
value={element.value}
onChange={element.onChange}
sectionLabel={element.sectionLabel}
onContextMenu={element.onContextMenu}
{isSidebarMode}
{primaryColor}
/>

View file

@ -14,6 +14,8 @@
isSidebarMode?: boolean;
/** Primary color for active state */
primaryColor?: string;
/** Called on right-click (context menu) - receives click coordinates */
onContextMenu?: (x: number, y: number) => void;
}
let {
@ -23,8 +25,16 @@
sectionLabel,
isSidebarMode = false,
primaryColor,
onContextMenu,
}: Props = $props();
function handleContextMenu(event: MouseEvent) {
if (onContextMenu) {
event.preventDefault();
onContextMenu(event.clientX, event.clientY);
}
}
// Icon SVG paths (same as PillNavigation)
const icons: Record<string, string> = {
list: 'M4 6h16M4 10h16M4 14h16M4 18h16',
@ -38,6 +48,10 @@
fire: 'M17.657 18.657A8 8 0 016.343 7.343S7 9 9 10c0-2 .5-5 2.986-7C14 5 16.09 5.777 17.656 7.343A7.975 7.975 0 0120 13a7.975 7.975 0 01-2.343 5.657z',
trending: 'M13 7h8m0 0v8m0-8l-8 8-4-4-6 6',
single: 'M4 6h16M4 12h16M4 18h16',
calendar:
'M8 7V3m8 4V3m-9 8h10M5 21h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z',
'check-square':
'M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4',
};
function getIconPath(name: string): string {
@ -51,7 +65,8 @@
}
</script>
<div class="pill-tab-group" class:sidebar-mode={isSidebarMode}>
<!-- svelte-ignore a11y_no_static_element_interactions -->
<div class="pill-tab-group" class:sidebar-mode={isSidebarMode} oncontextmenu={handleContextMenu}>
{#if sectionLabel && isSidebarMode}
<p class="section-label">{sectionLabel}</p>
{/if}

View file

@ -92,6 +92,8 @@ export interface PillTabGroupConfig {
onChange: (id: string) => void;
/** Optional section label (shown above in sidebar mode) */
sectionLabel?: string;
/** Called on right-click (context menu) - receives click coordinates */
onContextMenu?: (x: number, y: number) => void;
}
export interface PillDivider {
@ -137,6 +139,10 @@ export interface PillNavigationProps {
showThemeToggle?: boolean;
/** Primary color for active state */
primaryColor?: string;
/** Elements to prepend before nav items (tab groups, dividers, nav items) */
prependElements?: PillNavElement[];
/** Additional elements to show after nav items (tab groups, dividers) */
elements?: PillNavElement[];
}
export interface NavItem {