Moving away from Azure Devops #1
5 changed files with 121 additions and 6 deletions
|
|
@ -10,6 +10,7 @@ import { RendererRegistry } from './core/RendererRegistry';
|
||||||
import { CalendarOrchestrator } from './core/CalendarOrchestrator';
|
import { CalendarOrchestrator } from './core/CalendarOrchestrator';
|
||||||
import { TimeAxisRenderer } from './features/timeaxis/TimeAxisRenderer';
|
import { TimeAxisRenderer } from './features/timeaxis/TimeAxisRenderer';
|
||||||
import { ScrollManager } from './core/ScrollManager';
|
import { ScrollManager } from './core/ScrollManager';
|
||||||
|
import { HeaderDrawerManager } from './core/HeaderDrawerManager';
|
||||||
import { MockTeamStore, MockResourceStore } from './demo/MockStores';
|
import { MockTeamStore, MockResourceStore } from './demo/MockStores';
|
||||||
import { DemoApp } from './demo/DemoApp';
|
import { DemoApp } from './demo/DemoApp';
|
||||||
|
|
||||||
|
|
@ -47,6 +48,7 @@ export function createV2Container(): Container {
|
||||||
builder.registerType(CalendarOrchestrator).as<CalendarOrchestrator>();
|
builder.registerType(CalendarOrchestrator).as<CalendarOrchestrator>();
|
||||||
builder.registerType(TimeAxisRenderer).as<TimeAxisRenderer>();
|
builder.registerType(TimeAxisRenderer).as<TimeAxisRenderer>();
|
||||||
builder.registerType(ScrollManager).as<ScrollManager>();
|
builder.registerType(ScrollManager).as<ScrollManager>();
|
||||||
|
builder.registerType(HeaderDrawerManager).as<HeaderDrawerManager>();
|
||||||
|
|
||||||
// Demo app
|
// Demo app
|
||||||
builder.registerType(DemoApp).as<DemoApp>();
|
builder.registerType(DemoApp).as<DemoApp>();
|
||||||
|
|
|
||||||
47
src/v2/core/HeaderDrawerManager.ts
Normal file
47
src/v2/core/HeaderDrawerManager.ts
Normal file
|
|
@ -0,0 +1,47 @@
|
||||||
|
export class HeaderDrawerManager {
|
||||||
|
private drawer!: HTMLElement;
|
||||||
|
private spacer!: HTMLElement;
|
||||||
|
private expanded = false;
|
||||||
|
private readonly expandedHeight = 24;
|
||||||
|
private readonly duration = 200;
|
||||||
|
|
||||||
|
init(container: HTMLElement): void {
|
||||||
|
this.drawer = container.querySelector('swp-header-drawer')!;
|
||||||
|
this.spacer = container.querySelector('swp-header-spacer')!;
|
||||||
|
}
|
||||||
|
|
||||||
|
toggle(): void {
|
||||||
|
this.expanded ? this.collapse() : this.expand();
|
||||||
|
}
|
||||||
|
|
||||||
|
expand(): void {
|
||||||
|
if (this.expanded) return;
|
||||||
|
this.expanded = true;
|
||||||
|
this.animate(0, this.expandedHeight);
|
||||||
|
}
|
||||||
|
|
||||||
|
collapse(): void {
|
||||||
|
if (!this.expanded) return;
|
||||||
|
this.expanded = false;
|
||||||
|
this.animate(this.expandedHeight, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
private animate(from: number, to: number): void {
|
||||||
|
const keyframes = [
|
||||||
|
{ height: `${from}px` },
|
||||||
|
{ height: `${to}px` }
|
||||||
|
];
|
||||||
|
const options: KeyframeAnimationOptions = {
|
||||||
|
duration: this.duration,
|
||||||
|
easing: 'ease',
|
||||||
|
fill: 'forwards'
|
||||||
|
};
|
||||||
|
|
||||||
|
this.drawer.animate(keyframes, options);
|
||||||
|
this.spacer.animate(keyframes, options);
|
||||||
|
}
|
||||||
|
|
||||||
|
isExpanded(): boolean {
|
||||||
|
return this.expanded;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -3,6 +3,7 @@ import { TimeAxisRenderer } from '../features/timeaxis/TimeAxisRenderer';
|
||||||
import { NavigationAnimator } from '../core/NavigationAnimator';
|
import { NavigationAnimator } from '../core/NavigationAnimator';
|
||||||
import { DateService } from '../core/DateService';
|
import { DateService } from '../core/DateService';
|
||||||
import { ScrollManager } from '../core/ScrollManager';
|
import { ScrollManager } from '../core/ScrollManager';
|
||||||
|
import { HeaderDrawerManager } from '../core/HeaderDrawerManager';
|
||||||
import { ViewConfig } from '../core/ViewConfig';
|
import { ViewConfig } from '../core/ViewConfig';
|
||||||
|
|
||||||
export class DemoApp {
|
export class DemoApp {
|
||||||
|
|
@ -15,7 +16,8 @@ export class DemoApp {
|
||||||
private orchestrator: CalendarOrchestrator,
|
private orchestrator: CalendarOrchestrator,
|
||||||
private timeAxisRenderer: TimeAxisRenderer,
|
private timeAxisRenderer: TimeAxisRenderer,
|
||||||
private dateService: DateService,
|
private dateService: DateService,
|
||||||
private scrollManager: ScrollManager
|
private scrollManager: ScrollManager,
|
||||||
|
private headerDrawerManager: HeaderDrawerManager
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
init(): void {
|
init(): void {
|
||||||
|
|
@ -54,9 +56,13 @@ export class DemoApp {
|
||||||
// Init scroll synkronisering
|
// Init scroll synkronisering
|
||||||
this.scrollManager.init(this.container);
|
this.scrollManager.init(this.container);
|
||||||
|
|
||||||
|
// Init header drawer
|
||||||
|
this.headerDrawerManager.init(this.container);
|
||||||
|
|
||||||
// Setup event handlers
|
// Setup event handlers
|
||||||
this.setupNavigation();
|
this.setupNavigation();
|
||||||
this.setupViewSwitchers();
|
this.setupViewSwitchers();
|
||||||
|
this.setupDrawerToggle();
|
||||||
|
|
||||||
// Initial render
|
// Initial render
|
||||||
this.orchestrator.render(this.views.simple, this.container);
|
this.orchestrator.render(this.views.simple, this.container);
|
||||||
|
|
@ -86,4 +92,10 @@ export class DemoApp {
|
||||||
document.getElementById('btn-team')!.onclick = () =>
|
document.getElementById('btn-team')!.onclick = () =>
|
||||||
this.animator.slide('left', () => this.orchestrator.render(this.views.team, this.container));
|
this.animator.slide('left', () => this.orchestrator.render(this.views.team, this.container));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private setupDrawerToggle(): void {
|
||||||
|
document.getElementById('btn-drawer')!.onclick = () => {
|
||||||
|
this.headerDrawerManager.toggle();
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
:root {
|
:root {
|
||||||
--hour-height: 60px;
|
--hour-height: 64px;
|
||||||
--time-axis-width: 60px;
|
--time-axis-width: 60px;
|
||||||
--grid-columns: 5;
|
--grid-columns: 5;
|
||||||
--day-start-hour: 0;
|
--day-start-hour: 0;
|
||||||
|
|
@ -8,6 +8,8 @@
|
||||||
--color-surface: #fff;
|
--color-surface: #fff;
|
||||||
--color-text-secondary: #666;
|
--color-text-secondary: #666;
|
||||||
--color-primary: #1976d2;
|
--color-primary: #1976d2;
|
||||||
|
--color-hour-line: rgba(0, 0, 0, 0.2);
|
||||||
|
--color-grid-line-light: rgba(0, 0, 0, 0.05);
|
||||||
}
|
}
|
||||||
|
|
||||||
* { box-sizing: border-box; margin: 0; padding: 0; }
|
* { box-sizing: border-box; margin: 0; padding: 0; }
|
||||||
|
|
@ -85,6 +87,16 @@ swp-time-axis {
|
||||||
}
|
}
|
||||||
|
|
||||||
swp-header-spacer {
|
swp-header-spacer {
|
||||||
|
height: 0;
|
||||||
|
overflow: hidden;
|
||||||
|
border-bottom: 1px solid var(--color-border);
|
||||||
|
}
|
||||||
|
|
||||||
|
swp-header-drawer {
|
||||||
|
display: block;
|
||||||
|
height: 0;
|
||||||
|
overflow: hidden;
|
||||||
|
background: #fafafa;
|
||||||
border-bottom: 1px solid var(--color-border);
|
border-bottom: 1px solid var(--color-border);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -100,6 +112,17 @@ swp-hour-marker {
|
||||||
font-size: 11px;
|
font-size: 11px;
|
||||||
color: var(--color-text-secondary);
|
color: var(--color-text-secondary);
|
||||||
text-align: right;
|
text-align: right;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
swp-hour-marker::after {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
top: -1px;
|
||||||
|
right: 0;
|
||||||
|
width: 5px;
|
||||||
|
height: 1px;
|
||||||
|
background: var(--color-hour-line);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Grid container */
|
/* Grid container */
|
||||||
|
|
@ -211,15 +234,44 @@ swp-time-grid {
|
||||||
min-height: calc((var(--day-end-hour) - var(--day-start-hour)) * var(--hour-height));
|
min-height: calc((var(--day-end-hour) - var(--day-start-hour)) * var(--hour-height));
|
||||||
}
|
}
|
||||||
|
|
||||||
swp-grid-lines {
|
/* Timelinjer via ::after */
|
||||||
|
swp-time-grid::after {
|
||||||
|
content: '';
|
||||||
position: absolute;
|
position: absolute;
|
||||||
inset: 0;
|
inset: 0;
|
||||||
background: repeating-linear-gradient(
|
z-index: 2;
|
||||||
|
background-image: repeating-linear-gradient(
|
||||||
to bottom,
|
to bottom,
|
||||||
transparent,
|
transparent,
|
||||||
transparent calc(var(--hour-height) - 1px),
|
transparent calc(var(--hour-height) - 1px),
|
||||||
var(--color-border) calc(var(--hour-height) - 1px),
|
var(--color-hour-line) calc(var(--hour-height) - 1px),
|
||||||
var(--color-border) var(--hour-height)
|
var(--color-hour-line) var(--hour-height)
|
||||||
|
);
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Kvarterlinjer - 3 linjer per time (15, 30, 45 min), ikke ved timegrænsen */
|
||||||
|
swp-grid-lines {
|
||||||
|
display: block;
|
||||||
|
position: absolute;
|
||||||
|
inset: 0;
|
||||||
|
z-index: 1;
|
||||||
|
background-image: repeating-linear-gradient(
|
||||||
|
to bottom,
|
||||||
|
transparent 0,
|
||||||
|
transparent calc(var(--hour-height) / 4 - 1px),
|
||||||
|
var(--color-grid-line-light) calc(var(--hour-height) / 4 - 1px),
|
||||||
|
var(--color-grid-line-light) calc(var(--hour-height) / 4),
|
||||||
|
transparent calc(var(--hour-height) / 4),
|
||||||
|
transparent calc(var(--hour-height) / 2 - 1px),
|
||||||
|
var(--color-grid-line-light) calc(var(--hour-height) / 2 - 1px),
|
||||||
|
var(--color-grid-line-light) calc(var(--hour-height) / 2),
|
||||||
|
transparent calc(var(--hour-height) / 2),
|
||||||
|
transparent calc(var(--hour-height) * 3 / 4 - 1px),
|
||||||
|
var(--color-grid-line-light) calc(var(--hour-height) * 3 / 4 - 1px),
|
||||||
|
var(--color-grid-line-light) calc(var(--hour-height) * 3 / 4),
|
||||||
|
transparent calc(var(--hour-height) * 3 / 4),
|
||||||
|
transparent var(--hour-height)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,7 @@
|
||||||
</swp-week-info>
|
</swp-week-info>
|
||||||
<swp-nav-button id="btn-prev">←</swp-nav-button>
|
<swp-nav-button id="btn-prev">←</swp-nav-button>
|
||||||
<swp-nav-button id="btn-next">→</swp-nav-button>
|
<swp-nav-button id="btn-next">→</swp-nav-button>
|
||||||
|
<swp-nav-button id="btn-drawer">Toggle</swp-nav-button>
|
||||||
</swp-calendar-nav>
|
</swp-calendar-nav>
|
||||||
|
|
||||||
<swp-calendar-container>
|
<swp-calendar-container>
|
||||||
|
|
@ -31,6 +32,7 @@
|
||||||
<swp-header-track>
|
<swp-header-track>
|
||||||
<swp-calendar-header></swp-calendar-header>
|
<swp-calendar-header></swp-calendar-header>
|
||||||
</swp-header-track>
|
</swp-header-track>
|
||||||
|
<swp-header-drawer></swp-header-drawer>
|
||||||
</swp-header-viewport>
|
</swp-header-viewport>
|
||||||
<swp-content-viewport>
|
<swp-content-viewport>
|
||||||
<swp-content-track>
|
<swp-content-track>
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue