diff --git a/src/v2/V2CompositionRoot.ts b/src/v2/V2CompositionRoot.ts index 7fc9102..24f2518 100644 --- a/src/v2/V2CompositionRoot.ts +++ b/src/v2/V2CompositionRoot.ts @@ -9,6 +9,7 @@ import { TeamRenderer } from './features/team/TeamRenderer'; import { RendererRegistry } from './core/RendererRegistry'; import { CalendarOrchestrator } from './core/CalendarOrchestrator'; import { TimeAxisRenderer } from './features/timeaxis/TimeAxisRenderer'; +import { ScrollManager } from './core/ScrollManager'; import { MockTeamStore, MockResourceStore } from './demo/MockStores'; import { DemoApp } from './demo/DemoApp'; @@ -45,6 +46,7 @@ export function createV2Container(): Container { // CalendarOrchestrator modtager IGroupingStore[] automatisk (array injection) builder.registerType(CalendarOrchestrator).as(); builder.registerType(TimeAxisRenderer).as(); + builder.registerType(ScrollManager).as(); // Demo app builder.registerType(DemoApp).as(); diff --git a/src/v2/core/ScrollManager.ts b/src/v2/core/ScrollManager.ts new file mode 100644 index 0000000..3e4fbd5 --- /dev/null +++ b/src/v2/core/ScrollManager.ts @@ -0,0 +1,23 @@ +export class ScrollManager { + private scrollableContent!: HTMLElement; + private timeAxisContent!: HTMLElement; + private calendarHeader!: HTMLElement; + + init(container: HTMLElement): void { + this.scrollableContent = container.querySelector('swp-scrollable-content')!; + this.timeAxisContent = container.querySelector('swp-time-axis-content')!; + this.calendarHeader = container.querySelector('swp-calendar-header')!; + + this.scrollableContent.addEventListener('scroll', () => this.onScroll()); + } + + private onScroll(): void { + const { scrollTop, scrollLeft } = this.scrollableContent; + + // Synkroniser time-axis vertikalt + this.timeAxisContent.style.transform = `translateY(-${scrollTop}px)`; + + // Synkroniser header horisontalt + this.calendarHeader.style.transform = `translateX(-${scrollLeft}px)`; + } +} diff --git a/src/v2/demo/DemoApp.ts b/src/v2/demo/DemoApp.ts index 5c85f1a..708e0d5 100644 --- a/src/v2/demo/DemoApp.ts +++ b/src/v2/demo/DemoApp.ts @@ -2,6 +2,7 @@ import { CalendarOrchestrator } from '../core/CalendarOrchestrator'; import { TimeAxisRenderer } from '../features/timeaxis/TimeAxisRenderer'; import { NavigationAnimator } from '../core/NavigationAnimator'; import { DateService } from '../core/DateService'; +import { ScrollManager } from '../core/ScrollManager'; import { ViewConfig } from '../core/ViewConfig'; export class DemoApp { @@ -13,7 +14,8 @@ export class DemoApp { constructor( private orchestrator: CalendarOrchestrator, private timeAxisRenderer: TimeAxisRenderer, - private dateService: DateService + private dateService: DateService, + private scrollManager: ScrollManager ) {} init(): void { @@ -49,6 +51,9 @@ export class DemoApp { // Render time axis this.timeAxisRenderer.render(document.getElementById('time-axis') as HTMLElement); + // Init scroll synkronisering + this.scrollManager.init(this.container); + // Setup event handlers this.setupNavigation(); this.setupViewSwitchers(); diff --git a/wwwroot/css/calendar-v2.css b/wwwroot/css/calendar-v2.css index b68a0ab..99aa182 100644 --- a/wwwroot/css/calendar-v2.css +++ b/wwwroot/css/calendar-v2.css @@ -2,6 +2,8 @@ --hour-height: 60px; --time-axis-width: 60px; --grid-columns: 5; + --day-start-hour: 0; + --day-end-hour: 24; --color-border: #e0e0e0; --color-surface: #fff; --color-text-secondary: #666; @@ -68,6 +70,7 @@ swp-calendar-container { grid-template-columns: var(--time-axis-width) 1fr; grid-template-rows: auto 1fr; overflow: hidden; + height: 100%; } /* Time axis */ @@ -78,6 +81,7 @@ swp-time-axis { grid-template-rows: subgrid; border-right: 1px solid var(--color-border); background: var(--color-surface); + overflow: hidden; } swp-header-spacer { @@ -85,8 +89,8 @@ swp-header-spacer { } swp-time-axis-content { - display: flex; - flex-direction: column; + display: grid; + grid-auto-rows: var(--hour-height); overflow: hidden; } @@ -108,19 +112,31 @@ swp-grid-container { } /* Viewport/Track for slide animation */ -swp-header-viewport, -swp-content-viewport { +swp-header-viewport { overflow: hidden; } -swp-header-track, -swp-content-track { +swp-content-viewport { + overflow: hidden; + min-height: 0; /* Tillader at krympe i grid */ +} + +swp-header-track { display: flex; } -swp-header-track > swp-calendar-header, +swp-content-track { + display: flex; + height: 100%; +} + +swp-header-track > swp-calendar-header { + flex: 0 0 100%; +} + swp-content-track > swp-scrollable-content { flex: 0 0 100%; + height: 100%; } /* Header */ @@ -129,6 +145,16 @@ swp-calendar-header { grid-template-columns: repeat(var(--grid-columns), 1fr); grid-auto-rows: auto; background: var(--color-surface); + overflow-y: scroll; + overflow-x: hidden; +} + +swp-calendar-header::-webkit-scrollbar { + background: transparent; +} + +swp-calendar-header::-webkit-scrollbar-thumb { + background: transparent; } swp-calendar-header[data-levels="date"] > swp-day-header { grid-row: 1; } @@ -182,7 +208,7 @@ swp-scrollable-content { swp-time-grid { display: block; position: relative; - min-height: calc(15 * var(--hour-height)); + min-height: calc((var(--day-end-hour) - var(--day-start-hour)) * var(--hour-height)); } swp-grid-lines {