Refactor CalendarOrchestrator with ts-linq-light
Replaces native array methods with ts-linq-light for improved functional programming Simplifies data transformation and grouping logic Enhances code readability and introduces more declarative data manipulation
This commit is contained in:
parent
23fcaa9985
commit
27561750f8
4 changed files with 52 additions and 29 deletions
|
|
@ -7,7 +7,10 @@
|
||||||
"WebFetch(domain:caniuse.com)",
|
"WebFetch(domain:caniuse.com)",
|
||||||
"WebFetch(domain:blog.rasc.ch)",
|
"WebFetch(domain:blog.rasc.ch)",
|
||||||
"WebFetch(domain:developer.chrome.com)",
|
"WebFetch(domain:developer.chrome.com)",
|
||||||
"Bash(npx tsc:*)"
|
"Bash(npx tsc:*)",
|
||||||
|
"WebFetch(domain:github.com)",
|
||||||
|
"Bash(npm install:*)",
|
||||||
|
"WebFetch(domain:raw.githubusercontent.com)"
|
||||||
],
|
],
|
||||||
"deny": [],
|
"deny": [],
|
||||||
"ask": []
|
"ask": []
|
||||||
|
|
|
||||||
28
package-lock.json
generated
28
package-lock.json
generated
|
|
@ -12,7 +12,8 @@
|
||||||
"@rollup/rollup-win32-x64-msvc": "^4.52.2",
|
"@rollup/rollup-win32-x64-msvc": "^4.52.2",
|
||||||
"dayjs": "^1.11.19",
|
"dayjs": "^1.11.19",
|
||||||
"fuse.js": "^7.1.0",
|
"fuse.js": "^7.1.0",
|
||||||
"json-diff-ts": "^4.8.2"
|
"json-diff-ts": "^4.8.2",
|
||||||
|
"ts-linq-light": "^1.0.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@fullhuman/postcss-purgecss": "^7.0.2",
|
"@fullhuman/postcss-purgecss": "^7.0.2",
|
||||||
|
|
@ -4958,6 +4959,31 @@
|
||||||
"node": ">=20"
|
"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": {
|
"node_modules/tslib": {
|
||||||
"version": "2.8.1",
|
"version": "2.8.1",
|
||||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
|
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
|
||||||
|
|
|
||||||
|
|
@ -43,6 +43,7 @@
|
||||||
"@rollup/rollup-win32-x64-msvc": "^4.52.2",
|
"@rollup/rollup-win32-x64-msvc": "^4.52.2",
|
||||||
"dayjs": "^1.11.19",
|
"dayjs": "^1.11.19",
|
||||||
"fuse.js": "^7.1.0",
|
"fuse.js": "^7.1.0",
|
||||||
"json-diff-ts": "^4.8.2"
|
"json-diff-ts": "^4.8.2",
|
||||||
|
"ts-linq-light": "^1.0.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
import { from } from 'ts-linq-light';
|
||||||
import { ViewConfig, GroupingConfig } from './ViewConfig';
|
import { ViewConfig, GroupingConfig } from './ViewConfig';
|
||||||
import { RenderContext } from './RenderContext';
|
import { RenderContext } from './RenderContext';
|
||||||
import { IGroupingRenderer } from './IGroupingRenderer';
|
import { IGroupingRenderer } from './IGroupingRenderer';
|
||||||
|
|
@ -25,11 +26,11 @@ export class CalendarOrchestrator {
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
private getRenderer(type: string): IGroupingRenderer | undefined {
|
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 {
|
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<void> {
|
async render(viewConfig: ViewConfig, container: HTMLElement): Promise<void> {
|
||||||
|
|
@ -45,7 +46,7 @@ export class CalendarOrchestrator {
|
||||||
|
|
||||||
container.style.setProperty('--grid-columns', String(totalColumns));
|
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.dataset.levels = types.join(' ');
|
||||||
|
|
||||||
headerContainer.innerHTML = '';
|
headerContainer.innerHTML = '';
|
||||||
|
|
@ -58,12 +59,8 @@ export class CalendarOrchestrator {
|
||||||
await this.eventRenderer.render(container, visibleDates);
|
await this.eventRenderer.render(container, visibleDates);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Extract visible dates from view config
|
|
||||||
*/
|
|
||||||
private extractVisibleDates(viewConfig: ViewConfig): string[] {
|
private extractVisibleDates(viewConfig: ViewConfig): string[] {
|
||||||
const dateGrouping = viewConfig.groupings.find(g => g.type === 'date');
|
return from(viewConfig.groupings).firstOrDefault(g => g.type === 'date')?.values || [];
|
||||||
return dateGrouping?.values || [];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fetchAllData(groupings: GroupingConfig[]): Map<string, GroupingData> {
|
private fetchAllData(groupings: GroupingConfig[]): Map<string, GroupingData> {
|
||||||
|
|
@ -72,7 +69,7 @@ export class CalendarOrchestrator {
|
||||||
for (const g of groupings) {
|
for (const g of groupings) {
|
||||||
if (g.type === 'date') {
|
if (g.type === 'date') {
|
||||||
result.set(g.type, {
|
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
|
byParent: null
|
||||||
});
|
});
|
||||||
continue;
|
continue;
|
||||||
|
|
@ -81,10 +78,17 @@ export class CalendarOrchestrator {
|
||||||
const store = this.getStore(g.type);
|
const store = this.getStore(g.type);
|
||||||
if (!store) continue;
|
if (!store) continue;
|
||||||
const rawItems = store.getByIds(g.values);
|
const rawItems = store.getByIds(g.values);
|
||||||
const items = rawItems.map((item: any) => ({ id: item.id, data: item }));
|
const items = from(rawItems).select((item: any) => ({ id: item.id, data: item })).toArray();
|
||||||
const byParent = g.parentKey
|
let byParent: Map<string, { id: string; data: unknown }[]> | null = null;
|
||||||
? this.groupBy(items, item => (item.data as any)[g.parentKey!])
|
if (g.parentKey) {
|
||||||
: null;
|
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 });
|
result.set(g.type, { items, byParent });
|
||||||
}
|
}
|
||||||
|
|
@ -108,24 +112,13 @@ export class CalendarOrchestrator {
|
||||||
? gData.byParent.get(parentId) ?? []
|
? gData.byParent.get(parentId) ?? []
|
||||||
: gData.items;
|
: gData.items;
|
||||||
|
|
||||||
return items.map(item => ({
|
return from(items).select(item => ({
|
||||||
type: g.type,
|
type: g.type,
|
||||||
id: item.id,
|
id: item.id,
|
||||||
data: item.data,
|
data: item.data,
|
||||||
parentId,
|
parentId,
|
||||||
children: this.buildHierarchy(groupings, data, level + 1, item.id)
|
children: this.buildHierarchy(groupings, data, level + 1, item.id)
|
||||||
}));
|
})).toArray();
|
||||||
}
|
|
||||||
|
|
||||||
private groupBy<T>(items: T[], keyFn: (item: T) => string): Map<string, T[]> {
|
|
||||||
const map = new Map<string, T[]>();
|
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private countLeaves(nodes: HierarchyNode[]): number {
|
private countLeaves(nodes: HierarchyNode[]): number {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue