Refactor calendar V2 core with DI and new features

Introduces dependency injection container and composition root
Adds core services like DateService and NavigationAnimator
Simplifies CalendarOrchestrator with improved store handling
Implements mock stores and demo application for V2 calendar
This commit is contained in:
Janus C. H. Knudsen 2025-12-07 14:31:16 +01:00
parent 1ad7d10266
commit a0c0ef9e8d
17 changed files with 331 additions and 134 deletions

View file

@ -107,6 +107,22 @@ swp-grid-container {
overflow: hidden;
}
/* Viewport/Track for slide animation */
swp-header-viewport,
swp-content-viewport {
overflow: hidden;
}
swp-header-track,
swp-content-track {
display: flex;
}
swp-header-track > swp-calendar-header,
swp-content-track > swp-scrollable-content {
flex: 0 0 100%;
}
/* Header */
swp-calendar-header {
display: grid;

View file

@ -17,6 +17,8 @@
<swp-week-number>V2</swp-week-number>
<swp-date-range id="view-info"></swp-date-range>
</swp-week-info>
<swp-nav-button id="btn-prev"></swp-nav-button>
<swp-nav-button id="btn-next"></swp-nav-button>
</swp-calendar-nav>
<swp-calendar-container>
@ -25,107 +27,26 @@
<swp-time-axis-content id="time-axis"></swp-time-axis-content>
</swp-time-axis>
<swp-grid-container>
<swp-calendar-header></swp-calendar-header>
<swp-scrollable-content>
<swp-time-grid>
<swp-grid-lines></swp-grid-lines>
<swp-day-columns></swp-day-columns>
</swp-time-grid>
</swp-scrollable-content>
<swp-header-viewport>
<swp-header-track>
<swp-calendar-header></swp-calendar-header>
</swp-header-track>
</swp-header-viewport>
<swp-content-viewport>
<swp-content-track>
<swp-scrollable-content>
<swp-time-grid>
<swp-grid-lines></swp-grid-lines>
<swp-day-columns></swp-day-columns>
</swp-time-grid>
</swp-scrollable-content>
</swp-content-track>
</swp-content-viewport>
</swp-grid-container>
</swp-calendar-container>
</swp-calendar>
</div>
<script type="module">
import {
CalendarOrchestrator,
RendererRegistry,
StoreRegistry,
DateRenderer,
ResourceRenderer,
TeamRenderer
} from './js/calendar-v2.js';
const rendererRegistry = new RendererRegistry([
new DateRenderer(),
new ResourceRenderer(),
new TeamRenderer()
]);
const mockTeams = [
{ id: 'alpha', name: 'Team Alpha' },
{ id: 'beta', name: 'Team Beta' }
];
const mockResources = [
{ id: 'alice', name: 'Alice', teamId: 'alpha' },
{ id: 'bob', name: 'Bob', teamId: 'alpha' },
{ id: 'carol', name: 'Carol', teamId: 'beta' },
{ id: 'dave', name: 'Dave', teamId: 'beta' }
];
const storeRegistry = new StoreRegistry();
storeRegistry.register('team', { getByIds: ids => mockTeams.filter(t => ids.includes(t.id)) });
storeRegistry.register('resource', { getByIds: ids => mockResources.filter(r => ids.includes(r.id)) });
const orchestrator = new CalendarOrchestrator(rendererRegistry, storeRegistry);
const container = document.querySelector('swp-calendar-container');
const viewInfo = document.getElementById('view-info');
function getWeekDates() {
const today = new Date();
const mon = new Date(today);
mon.setDate(today.getDate() - today.getDay() + 1);
return Array.from({ length: 5 }, (_, i) => {
const d = new Date(mon);
d.setDate(mon.getDate() + i);
return d.toISOString().split('T')[0];
});
}
const dates = getWeekDates();
const views = {
simple: {
templateId: 'simple',
groupings: [{ type: 'date', values: dates }]
},
resource: {
templateId: 'resource',
groupings: [
{ type: 'resource', values: ['alice', 'bob', 'carol'] },
{ type: 'date', values: dates.slice(0, 3) }
]
},
team: {
templateId: 'team',
groupings: [
{ type: 'team', values: ['alpha', 'beta'] },
{ type: 'resource', values: ['alice', 'bob', 'carol', 'dave'], parentKey: 'teamId' },
{ type: 'date', values: dates.slice(0, 3) }
]
}
};
function generateTimeAxis() {
const el = document.getElementById('time-axis');
el.innerHTML = Array.from({ length: 15 }, (_, i) =>
`<swp-hour-marker>${(6 + i).toString().padStart(2, '0')}:00</swp-hour-marker>`
).join('');
}
async function render(view, label) {
viewInfo.textContent = label;
await orchestrator.render(view, container);
}
document.getElementById('btn-simple').onclick = () => render(views.simple, '5 datoer');
document.getElementById('btn-resource').onclick = () => render(views.resource, '3 resources × 3 datoer');
document.getElementById('btn-team').onclick = () => render(views.team, '2 teams × 2 resources × 3 datoer');
generateTimeAxis();
render(views.simple, '5 datoer');
</script>
<script type="module" src="js/v2-demo.js"></script>
</body>
</html>