diff --git a/.claude/settings.local.json b/.claude/settings.local.json index 5472cb5..9921e42 100644 --- a/.claude/settings.local.json +++ b/.claude/settings.local.json @@ -7,7 +7,10 @@ "WebFetch(domain:caniuse.com)", "WebFetch(domain:blog.rasc.ch)", "WebFetch(domain:developer.chrome.com)", - "Bash(npx tsc:*)" + "Bash(npx tsc:*)", + "WebFetch(domain:github.com)", + "Bash(npm install:*)", + "WebFetch(domain:raw.githubusercontent.com)" ], "deny": [], "ask": [] diff --git a/package-lock.json b/package-lock.json index 1389069..9de71a8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,7 +12,8 @@ "@rollup/rollup-win32-x64-msvc": "^4.52.2", "dayjs": "^1.11.19", "fuse.js": "^7.1.0", - "json-diff-ts": "^4.8.2" + "json-diff-ts": "^4.8.2", + "ts-linq-light": "^1.0.0" }, "devDependencies": { "@fullhuman/postcss-purgecss": "^7.0.2", @@ -4958,6 +4959,31 @@ "node": ">=20" } }, + "node_modules/ts-linq-light": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/ts-linq-light/-/ts-linq-light-1.0.0.tgz", + "integrity": "sha512-8GGyHkHlKuKFTbT/Xz/0y72OTKl1hVRMJ0MqXAaWaergCExKw3bd5M6DR3NSOA7UL0gkTMhWsql9kFYyAjkIdQ==", + "license": "MIT", + "engines": { + "node": ">=18.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-win32-x64-msvc": "^4.53.3" + } + }, + "node_modules/ts-linq-light/node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.53.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.53.3.tgz", + "integrity": "sha512-UhTd8u31dXadv0MopwGgNOBpUVROFKWVQgAg5N1ESyCz8AuBcMqm4AuTjrwgQKGDfoFuz02EuMRHQIw/frmYKQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, "node_modules/tslib": { "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", diff --git a/package.json b/package.json index d2aadc1..2b53162 100644 --- a/package.json +++ b/package.json @@ -43,6 +43,7 @@ "@rollup/rollup-win32-x64-msvc": "^4.52.2", "dayjs": "^1.11.19", "fuse.js": "^7.1.0", - "json-diff-ts": "^4.8.2" + "json-diff-ts": "^4.8.2", + "ts-linq-light": "^1.0.0" } } diff --git a/src/v2/core/CalendarOrchestrator.ts b/src/v2/core/CalendarOrchestrator.ts index a69b934..78964a7 100644 --- a/src/v2/core/CalendarOrchestrator.ts +++ b/src/v2/core/CalendarOrchestrator.ts @@ -1,3 +1,4 @@ +import { from } from 'ts-linq-light'; import { ViewConfig, GroupingConfig } from './ViewConfig'; import { RenderContext } from './RenderContext'; import { IGroupingRenderer } from './IGroupingRenderer'; @@ -25,11 +26,11 @@ export class CalendarOrchestrator { ) {} private getRenderer(type: string): IGroupingRenderer | undefined { - return this.renderers.find(r => r.type === type); + return from(this.renderers).firstOrDefault(r => r.type === type); } private getStore(type: string): IGroupingStore | undefined { - return this.stores.find(s => s.type === type); + return from(this.stores).firstOrDefault(s => s.type === type); } async render(viewConfig: ViewConfig, container: HTMLElement): Promise { @@ -45,7 +46,7 @@ export class CalendarOrchestrator { container.style.setProperty('--grid-columns', String(totalColumns)); - const types = viewConfig.groupings.map(g => g.type); + const types = from(viewConfig.groupings).select(g => g.type).toArray(); headerContainer.dataset.levels = types.join(' '); headerContainer.innerHTML = ''; @@ -58,12 +59,8 @@ export class CalendarOrchestrator { await this.eventRenderer.render(container, visibleDates); } - /** - * Extract visible dates from view config - */ private extractVisibleDates(viewConfig: ViewConfig): string[] { - const dateGrouping = viewConfig.groupings.find(g => g.type === 'date'); - return dateGrouping?.values || []; + return from(viewConfig.groupings).firstOrDefault(g => g.type === 'date')?.values || []; } private fetchAllData(groupings: GroupingConfig[]): Map { @@ -72,7 +69,7 @@ export class CalendarOrchestrator { for (const g of groupings) { if (g.type === 'date') { result.set(g.type, { - items: g.values.map(v => ({ id: v, data: { id: v } })), + items: from(g.values).select(v => ({ id: v, data: { id: v } })).toArray(), byParent: null }); continue; @@ -81,10 +78,17 @@ export class CalendarOrchestrator { const store = this.getStore(g.type); if (!store) continue; const rawItems = store.getByIds(g.values); - const items = rawItems.map((item: any) => ({ id: item.id, data: item })); - const byParent = g.parentKey - ? this.groupBy(items, item => (item.data as any)[g.parentKey!]) - : null; + const items = from(rawItems).select((item: any) => ({ id: item.id, data: item })).toArray(); + let byParent: Map | null = null; + if (g.parentKey) { + byParent = new Map(); + const grouped = from(items) + .where(item => (item.data as any)[g.parentKey!] != null) + .groupBy(item => (item.data as any)[g.parentKey!] as string); + for (const group of grouped) { + byParent.set(group.key, group.toArray()); + } + } result.set(g.type, { items, byParent }); } @@ -108,24 +112,13 @@ export class CalendarOrchestrator { ? gData.byParent.get(parentId) ?? [] : gData.items; - return items.map(item => ({ + return from(items).select(item => ({ type: g.type, id: item.id, data: item.data, parentId, children: this.buildHierarchy(groupings, data, level + 1, item.id) - })); - } - - private groupBy(items: T[], keyFn: (item: T) => string): Map { - const map = new Map(); - for (const item of items) { - const key = keyFn(item); - if (key == null) continue; - if (!map.has(key)) map.set(key, []); - map.get(key)!.push(item); - } - return map; + })).toArray(); } private countLeaves(nodes: HierarchyNode[]): number {