wip
This commit is contained in:
parent
c16e432b29
commit
2ec4b93fa5
9 changed files with 187 additions and 249 deletions
|
|
@ -1,25 +1,14 @@
|
|||
import { from } from 'ts-linq-light';
|
||||
import { ViewConfig } from './ViewConfig';
|
||||
import { IGroupingRenderer, RenderContext } from './IGroupingRenderer';
|
||||
import { IGroupingStore } from './IGroupingStore';
|
||||
import { RenderBuilder } from './RenderBuilder';
|
||||
import { Renderer, RenderContext } from './IGroupingRenderer';
|
||||
import { buildPipeline } from './RenderBuilder';
|
||||
import { EventRenderer } from '../features/event/EventRenderer';
|
||||
import { ViewConfig } from './ViewConfig';
|
||||
|
||||
export class CalendarOrchestrator {
|
||||
constructor(
|
||||
private renderers: IGroupingRenderer[],
|
||||
private stores: IGroupingStore[],
|
||||
private allRenderers: Renderer[],
|
||||
private eventRenderer: EventRenderer
|
||||
) {}
|
||||
|
||||
private getRenderer(type: string): IGroupingRenderer | undefined {
|
||||
return from(this.renderers).firstOrDefault(r => r.type === type);
|
||||
}
|
||||
|
||||
private getStore(type: string): IGroupingStore | undefined {
|
||||
return from(this.stores).firstOrDefault(s => s.type === type);
|
||||
}
|
||||
|
||||
async render(viewConfig: ViewConfig, container: HTMLElement): Promise<void> {
|
||||
const headerContainer = container.querySelector('swp-calendar-header') as HTMLElement;
|
||||
const columnContainer = container.querySelector('swp-day-columns') as HTMLElement;
|
||||
|
|
@ -27,53 +16,42 @@ export class CalendarOrchestrator {
|
|||
throw new Error('Missing swp-calendar-header or swp-day-columns');
|
||||
}
|
||||
|
||||
const context: RenderContext = { headerContainer, columnContainer };
|
||||
// Byg filter fra viewConfig
|
||||
const filter: Record<string, string[]> = {};
|
||||
for (const grouping of viewConfig.groupings) {
|
||||
filter[grouping.type] = grouping.values;
|
||||
}
|
||||
|
||||
// Clear containers
|
||||
const context: RenderContext = { headerContainer, columnContainer, filter };
|
||||
|
||||
// Clear
|
||||
headerContainer.innerHTML = '';
|
||||
columnContainer.innerHTML = '';
|
||||
|
||||
// Set header levels
|
||||
const types = from(viewConfig.groupings).select(g => g.type).toArray();
|
||||
headerContainer.dataset.levels = types.join(' ');
|
||||
// Vælg renderers baseret på groupings types
|
||||
const activeRenderers = this.selectRenderers(viewConfig);
|
||||
|
||||
// Byg renderer chain
|
||||
const builder = new RenderBuilder(context);
|
||||
|
||||
for (const grouping of viewConfig.groupings) {
|
||||
const renderer = this.getRenderer(grouping.type);
|
||||
if (renderer) {
|
||||
const items = this.getItems(grouping.type, viewConfig);
|
||||
builder.add(renderer, items);
|
||||
}
|
||||
}
|
||||
|
||||
// Beregn total columns og render
|
||||
const totalColumns = builder.getTotalCount();
|
||||
// Beregn total kolonner dynamisk
|
||||
const totalColumns = this.calculateTotalColumns(viewConfig);
|
||||
container.style.setProperty('--grid-columns', String(totalColumns));
|
||||
|
||||
builder.build();
|
||||
// Byg og kør pipeline
|
||||
const pipeline = buildPipeline(activeRenderers);
|
||||
pipeline.run(context);
|
||||
|
||||
// Render events
|
||||
const visibleDates = this.extractVisibleDates(viewConfig);
|
||||
await this.eventRenderer.render(container, visibleDates);
|
||||
// Events
|
||||
const dates = filter['date'] || [];
|
||||
await this.eventRenderer.render(container, dates);
|
||||
}
|
||||
|
||||
private getItems(type: string, viewConfig: ViewConfig): ReturnType<typeof from> {
|
||||
const grouping = from(viewConfig.groupings).firstOrDefault(g => g.type === type);
|
||||
if (!grouping) return from([]);
|
||||
|
||||
if (type === 'date') {
|
||||
return from(grouping.values);
|
||||
}
|
||||
|
||||
const store = this.getStore(type);
|
||||
if (!store) return from([]);
|
||||
|
||||
return from(store.getByIds(grouping.values));
|
||||
private selectRenderers(viewConfig: ViewConfig): Renderer[] {
|
||||
const types = viewConfig.groupings.map(g => g.type);
|
||||
return this.allRenderers.filter(r => types.includes(r.type));
|
||||
}
|
||||
|
||||
private extractVisibleDates(viewConfig: ViewConfig): string[] {
|
||||
return from(viewConfig.groupings).firstOrDefault(g => g.type === 'date')?.values || [];
|
||||
private calculateTotalColumns(viewConfig: ViewConfig): number {
|
||||
const dateCount = viewConfig.groupings.find(g => g.type === 'date')?.values.length || 1;
|
||||
const resourceCount = viewConfig.groupings.find(g => g.type === 'resource')?.values.length || 1;
|
||||
return dateCount * resourceCount;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,16 +1,11 @@
|
|||
import { IEnumerable } from 'ts-linq-light';
|
||||
import { NextFunction } from './RenderBuilder';
|
||||
|
||||
export interface RenderContext {
|
||||
headerContainer: HTMLElement;
|
||||
columnContainer: HTMLElement;
|
||||
filter: Record<string, string[]>; // { team: ['alpha'], resource: ['alice', 'bob'], date: [...] }
|
||||
}
|
||||
|
||||
export interface IGroupingRenderer<T = unknown> {
|
||||
export interface Renderer {
|
||||
readonly type: string;
|
||||
render(
|
||||
items: IEnumerable<T>,
|
||||
next: NextFunction,
|
||||
context: RenderContext
|
||||
): void;
|
||||
next: Renderer | null;
|
||||
render(context: RenderContext): void;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,73 +1,20 @@
|
|||
import { from, IEnumerable } from 'ts-linq-light';
|
||||
import { IGroupingRenderer, RenderContext } from './IGroupingRenderer';
|
||||
import { Renderer, RenderContext } from './IGroupingRenderer';
|
||||
|
||||
export interface NextFunction {
|
||||
count(items: IEnumerable<unknown>): number;
|
||||
render(items: IEnumerable<unknown>): void;
|
||||
export interface Pipeline {
|
||||
run(context: RenderContext): void;
|
||||
}
|
||||
|
||||
interface RenderLevel {
|
||||
renderer: IGroupingRenderer;
|
||||
items: IEnumerable<unknown>;
|
||||
}
|
||||
|
||||
export class RenderBuilder {
|
||||
private levels: RenderLevel[] = [];
|
||||
|
||||
constructor(private context: RenderContext) {}
|
||||
|
||||
add(renderer: IGroupingRenderer, items: IEnumerable<unknown>): this {
|
||||
this.levels.push({ renderer, items });
|
||||
return this;
|
||||
export function buildPipeline(renderers: Renderer[]): Pipeline {
|
||||
// Link renderers
|
||||
for (let i = 0; i < renderers.length - 1; i++) {
|
||||
renderers[i].next = renderers[i + 1];
|
||||
}
|
||||
|
||||
getTotalCount(): number {
|
||||
if (this.levels.length === 0) return 0;
|
||||
const first = renderers[0] ?? null;
|
||||
|
||||
const chain = this.buildChain(0);
|
||||
return chain.count(this.levels[0].items);
|
||||
}
|
||||
|
||||
build(): void {
|
||||
if (this.levels.length === 0) return;
|
||||
|
||||
const chain = this.buildChain(0);
|
||||
chain.render(this.levels[0].items);
|
||||
}
|
||||
|
||||
private buildChain(index: number): NextFunction {
|
||||
if (index >= this.levels.length) {
|
||||
// Leaf - ingen flere levels
|
||||
return {
|
||||
count: (items) => from(items).count(),
|
||||
render: () => {}
|
||||
};
|
||||
return {
|
||||
run(context: RenderContext) {
|
||||
if (first) first.render(context);
|
||||
}
|
||||
|
||||
const level = this.levels[index];
|
||||
const nextChain = this.buildChain(index + 1);
|
||||
|
||||
return {
|
||||
count: (items) => {
|
||||
let total = 0;
|
||||
for (const item of items) {
|
||||
const childItems = this.getChildItems(index, item);
|
||||
total += nextChain.count(childItems);
|
||||
}
|
||||
return total || from(items).count();
|
||||
},
|
||||
render: (items) => {
|
||||
level.renderer.render(items, nextChain, this.context);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private getChildItems(levelIndex: number, _parentItem: unknown): IEnumerable<unknown> {
|
||||
// Returnerer næste levels items - rendereren selv filtrerer baseret på parent
|
||||
const nextLevel = this.levels[levelIndex + 1];
|
||||
if (!nextLevel) {
|
||||
return from([]);
|
||||
}
|
||||
return nextLevel.items;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue