mirror of
https://github.com/Memo-2023/mana-monorepo.git
synced 2026-05-16 06:39:41 +02:00
feat(calendar): improve navigation layout controls and sidebar integration
- Add layout control icons (sidebar/bottom-bar) for better UX - Position FABs on right side for consistent minimization direction - Fix app switcher dropdown direction (opens up when nav at bottom) - Add isToolbarCollapsed store for toolbar state management - Create CalendarToolbarContent component for reusable toolbar elements - Improve sidebar toolbar integration with proper vertical layout - Hide PillViewSwitcher sliding indicator in vertical mode - Add scrollbar to sidebar toolbar content - Ensure all toolbar buttons are full-width and left-aligned in sidebar 🤖 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
287a62a3fb
commit
81f77c424c
5 changed files with 261 additions and 10 deletions
|
|
@ -0,0 +1,195 @@
|
|||
<script lang="ts">
|
||||
import { viewStore } from '$lib/stores/view.svelte';
|
||||
import { settingsStore } from '$lib/stores/settings.svelte';
|
||||
import type { CalendarViewType } from '@calendar/shared';
|
||||
import {
|
||||
PillToolbarButton,
|
||||
PillToolbarDivider,
|
||||
PillTimeRangeSelector,
|
||||
PillViewSwitcher,
|
||||
} from '@manacore/shared-ui';
|
||||
import PillCalendarSelector from './PillCalendarSelector.svelte';
|
||||
|
||||
interface Props {
|
||||
vertical?: boolean;
|
||||
}
|
||||
|
||||
let { vertical = false }: Props = $props();
|
||||
|
||||
// View type labels
|
||||
const viewLabels: Record<CalendarViewType, string> = {
|
||||
day: 'Tag',
|
||||
'5day': '5 Tage',
|
||||
week: 'Woche',
|
||||
'10day': '10 Tage',
|
||||
'14day': '14 Tage',
|
||||
month: 'Monat',
|
||||
year: 'Jahr',
|
||||
agenda: 'Agenda',
|
||||
};
|
||||
|
||||
// Views to show in selector
|
||||
const visibleViews: CalendarViewType[] = [
|
||||
'day',
|
||||
'5day',
|
||||
'week',
|
||||
'10day',
|
||||
'14day',
|
||||
'month',
|
||||
'year',
|
||||
];
|
||||
|
||||
// Convert to ViewOptions for PillViewSwitcher
|
||||
const viewOptions = visibleViews.map((type) => ({
|
||||
id: type,
|
||||
label: viewLabels[type],
|
||||
title: viewLabels[type],
|
||||
}));
|
||||
|
||||
// Hours change handlers
|
||||
function handleStartHourChange(hour: number) {
|
||||
settingsStore.set('dayStartHour', hour);
|
||||
}
|
||||
|
||||
function handleEndHourChange(hour: number) {
|
||||
settingsStore.set('dayEndHour', hour);
|
||||
}
|
||||
|
||||
function handleViewChange(type: string) {
|
||||
viewStore.setViewType(type as CalendarViewType);
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="toolbar-content" class:vertical>
|
||||
<!-- Calendar selector -->
|
||||
<PillCalendarSelector direction={vertical ? 'down' : 'up'} embedded={true} />
|
||||
|
||||
{#if !vertical}
|
||||
<PillToolbarDivider />
|
||||
{/if}
|
||||
|
||||
<!-- Weekdays filter -->
|
||||
<PillToolbarButton
|
||||
onclick={() => settingsStore.set('showOnlyWeekdays', !settingsStore.showOnlyWeekdays)}
|
||||
active={settingsStore.showOnlyWeekdays}
|
||||
title="Nur Wochentage anzeigen (Mo-Fr)"
|
||||
>
|
||||
Mo-Fr
|
||||
</PillToolbarButton>
|
||||
|
||||
<!-- Hours filter with time range selector -->
|
||||
<PillTimeRangeSelector
|
||||
startHour={settingsStore.dayStartHour}
|
||||
endHour={settingsStore.dayEndHour}
|
||||
onStartHourChange={handleStartHourChange}
|
||||
onEndHourChange={handleEndHourChange}
|
||||
direction={vertical ? 'down' : 'up'}
|
||||
embedded={true}
|
||||
toggleMode={true}
|
||||
active={settingsStore.filterHoursEnabled}
|
||||
onToggle={() => settingsStore.set('filterHoursEnabled', !settingsStore.filterHoursEnabled)}
|
||||
labelFormat="range"
|
||||
/>
|
||||
|
||||
{#if !vertical}
|
||||
<PillToolbarDivider />
|
||||
{/if}
|
||||
|
||||
<!-- View selector -->
|
||||
<PillViewSwitcher
|
||||
options={viewOptions}
|
||||
value={viewStore.viewType}
|
||||
onChange={handleViewChange}
|
||||
primaryColor="#3b82f6"
|
||||
embedded={true}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.toolbar-content {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.25rem;
|
||||
}
|
||||
|
||||
.toolbar-content.vertical {
|
||||
flex-direction: column;
|
||||
align-items: stretch;
|
||||
gap: 0.5rem;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
/* All elements in vertical mode - full width, left aligned */
|
||||
.toolbar-content.vertical :global(.pill-toolbar-btn),
|
||||
.toolbar-content.vertical :global(.pill-dropdown .trigger-button),
|
||||
.toolbar-content.vertical :global(button) {
|
||||
width: 100%;
|
||||
justify-content: flex-start;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
/* PillViewSwitcher in vertical mode */
|
||||
.toolbar-content.vertical :global(.pill-view-switcher) {
|
||||
flex-direction: column;
|
||||
gap: 0.25rem;
|
||||
padding: 0;
|
||||
background: transparent;
|
||||
border: none;
|
||||
box-shadow: none;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
/* Hide the sliding indicator in vertical mode */
|
||||
.toolbar-content.vertical :global(.pill-view-switcher .sliding-indicator) {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.toolbar-content.vertical :global(.pill-view-switcher .switcher-btn) {
|
||||
width: 100%;
|
||||
justify-content: flex-start;
|
||||
padding: 0.5rem 0.875rem;
|
||||
border-radius: 9999px;
|
||||
background: transparent;
|
||||
border: 1px solid transparent;
|
||||
}
|
||||
|
||||
.toolbar-content.vertical :global(.pill-view-switcher .switcher-btn:hover) {
|
||||
background: rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
|
||||
:global(.dark) .toolbar-content.vertical :global(.pill-view-switcher .switcher-btn:hover) {
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
}
|
||||
|
||||
.toolbar-content.vertical :global(.pill-view-switcher .switcher-btn.active) {
|
||||
background: color-mix(in srgb, #3b82f6 15%, transparent 85%);
|
||||
border-color: color-mix(in srgb, #3b82f6 25%, transparent 75%);
|
||||
}
|
||||
|
||||
:global(.dark) .toolbar-content.vertical :global(.pill-view-switcher .switcher-btn.active) {
|
||||
background: color-mix(in srgb, #3b82f6 25%, transparent 75%);
|
||||
border-color: color-mix(in srgb, #3b82f6 35%, transparent 65%);
|
||||
}
|
||||
|
||||
/* PillTimeRangeSelector in vertical mode */
|
||||
.toolbar-content.vertical :global(.pill-time-range-selector),
|
||||
.toolbar-content.vertical :global(.pill-dropdown) {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.toolbar-content.vertical :global(.pill-time-range-selector .trigger-button),
|
||||
.toolbar-content.vertical :global(.pill-dropdown .trigger-button) {
|
||||
width: 100%;
|
||||
justify-content: flex-start;
|
||||
}
|
||||
|
||||
/* PillCalendarSelector in vertical mode */
|
||||
.toolbar-content.vertical :global(.calendar-selector) {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.toolbar-content.vertical :global(.calendar-selector .trigger-button) {
|
||||
width: 100%;
|
||||
justify-content: flex-start;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -2,3 +2,4 @@ import { writable } from 'svelte/store';
|
|||
|
||||
export const isSidebarMode = writable(false);
|
||||
export const isNavCollapsed = writable(false);
|
||||
export const isToolbarCollapsed = writable(false);
|
||||
|
|
|
|||
|
|
@ -1191,37 +1191,85 @@
|
|||
flex-direction: column;
|
||||
align-items: stretch;
|
||||
gap: 0.5rem;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
/* All buttons in sidebar toolbar - full width, left aligned */
|
||||
.sidebar-toolbar-content :global(.pill-toolbar-btn),
|
||||
.sidebar-toolbar-content :global(.pill-dropdown .trigger-button) {
|
||||
.sidebar-toolbar-content :global(.pill-dropdown .trigger-button),
|
||||
.sidebar-toolbar-content :global(button) {
|
||||
width: 100%;
|
||||
justify-content: flex-start;
|
||||
text-align: left;
|
||||
background: transparent;
|
||||
border: 1px solid transparent;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
.sidebar-toolbar-content :global(.pill-toolbar-btn:hover),
|
||||
.sidebar-toolbar-content :global(.pill-dropdown .trigger-button:hover) {
|
||||
.sidebar-toolbar-content :global(.pill-dropdown .trigger-button:hover),
|
||||
.sidebar-toolbar-content :global(button:hover) {
|
||||
background: rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
|
||||
:global(.dark) .sidebar-toolbar-content :global(.pill-toolbar-btn:hover),
|
||||
:global(.dark) .sidebar-toolbar-content :global(.pill-dropdown .trigger-button:hover) {
|
||||
:global(.dark) .sidebar-toolbar-content :global(.pill-dropdown .trigger-button:hover),
|
||||
:global(.dark) .sidebar-toolbar-content :global(button:hover) {
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
}
|
||||
|
||||
/* Style for PillViewSwitcher in sidebar */
|
||||
/* Style for PillViewSwitcher in sidebar - vertical layout */
|
||||
.sidebar-toolbar-content :global(.pill-view-switcher) {
|
||||
flex-direction: column;
|
||||
gap: 0.25rem;
|
||||
width: 100%;
|
||||
padding: 0;
|
||||
background: transparent;
|
||||
border: none;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
.sidebar-toolbar-content :global(.pill-view-switcher .view-option) {
|
||||
/* Hide the sliding indicator in vertical mode */
|
||||
.sidebar-toolbar-content :global(.pill-view-switcher .sliding-indicator) {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.sidebar-toolbar-content :global(.pill-view-switcher .switcher-btn) {
|
||||
width: 100%;
|
||||
justify-content: flex-start;
|
||||
padding: 0.5rem 0.875rem;
|
||||
border-radius: 9999px;
|
||||
background: transparent;
|
||||
border: 1px solid transparent;
|
||||
}
|
||||
|
||||
.sidebar-toolbar-content :global(.pill-view-switcher .switcher-btn:hover) {
|
||||
background: rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
|
||||
:global(.dark) .sidebar-toolbar-content :global(.pill-view-switcher .switcher-btn:hover) {
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
}
|
||||
|
||||
.sidebar-toolbar-content :global(.pill-view-switcher .switcher-btn.active) {
|
||||
background: color-mix(in srgb, var(--pill-primary-color, #3b82f6) 15%, transparent 85%);
|
||||
border-color: color-mix(in srgb, var(--pill-primary-color, #3b82f6) 25%, transparent 75%);
|
||||
}
|
||||
|
||||
:global(.dark) .sidebar-toolbar-content :global(.pill-view-switcher .switcher-btn.active) {
|
||||
background: color-mix(in srgb, var(--pill-primary-color, #3b82f6) 25%, transparent 75%);
|
||||
border-color: color-mix(in srgb, var(--pill-primary-color, #3b82f6) 35%, transparent 65%);
|
||||
}
|
||||
|
||||
/* PillTimeRangeSelector in sidebar */
|
||||
.sidebar-toolbar-content :global(.pill-time-range-selector),
|
||||
.sidebar-toolbar-content :global(.pill-dropdown) {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
/* PillCalendarSelector in sidebar */
|
||||
.sidebar-toolbar-content :global(.calendar-selector) {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
/* Mobile: Sidebar container adjustments */
|
||||
|
|
|
|||
|
|
@ -50,6 +50,7 @@
|
|||
.pill-toolbar.position-bottom {
|
||||
top: auto;
|
||||
bottom: var(--toolbar-bottom-offset, 70px);
|
||||
transition: bottom 0.3s ease;
|
||||
}
|
||||
|
||||
/* Mobile: always position above bottom nav */
|
||||
|
|
|
|||
|
|
@ -57,6 +57,8 @@
|
|||
appIcon?: string;
|
||||
primaryColor?: string;
|
||||
autoFocus?: boolean;
|
||||
/** Bottom offset from viewport bottom (default: '70px') */
|
||||
bottomOffset?: string;
|
||||
}
|
||||
|
||||
let {
|
||||
|
|
@ -72,6 +74,7 @@
|
|||
appIcon = 'search',
|
||||
primaryColor = '#8b5cf6',
|
||||
autoFocus = true,
|
||||
bottomOffset = '70px',
|
||||
}: Props = $props();
|
||||
|
||||
let searchQuery = $state('');
|
||||
|
|
@ -239,7 +242,10 @@
|
|||
}
|
||||
</script>
|
||||
|
||||
<div class="quick-input-bar" style="--primary-color: {primaryColor}">
|
||||
<div
|
||||
class="quick-input-bar"
|
||||
style="--primary-color: {primaryColor}; --bottom-offset: {bottomOffset}"
|
||||
>
|
||||
<!-- Results Panel (above input) -->
|
||||
{#if showPanel}
|
||||
<div class="results-panel" transition:slide={{ duration: 150 }}>
|
||||
|
|
@ -411,15 +417,15 @@
|
|||
<style>
|
||||
.quick-input-bar {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
bottom: calc(var(--bottom-offset, 70px) + env(safe-area-inset-bottom, 0px));
|
||||
left: 0;
|
||||
right: 0;
|
||||
z-index: 90;
|
||||
padding: 0.75rem 1rem;
|
||||
padding-top: calc(0.75rem + env(safe-area-inset-top));
|
||||
pointer-events: none;
|
||||
/* Fixed height to prevent layout shift when results appear */
|
||||
height: 72px;
|
||||
transition: bottom 0.3s ease;
|
||||
}
|
||||
|
||||
.input-container,
|
||||
|
|
@ -588,11 +594,11 @@
|
|||
/* Results Panel */
|
||||
.results-panel {
|
||||
position: absolute;
|
||||
top: 100%;
|
||||
bottom: 100%;
|
||||
left: 1rem;
|
||||
right: 1rem;
|
||||
max-width: 700px;
|
||||
margin: 0.5rem auto 0;
|
||||
margin: 0 auto 0.5rem;
|
||||
max-height: 320px;
|
||||
overflow-y: auto;
|
||||
background: rgba(255, 255, 255, 0.95);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue