Refactors project structure and event rendering

Restructures the project for better maintainability and clarity.  Adds a ManagerFactory for dependency injection and reorganizes files.

Updates event rendering logic to correctly handle overlapping events using a stack link system. The EventRendererStrategy now correctly processes and renders event overlaps, ensuring proper display. Introduces processing tracking to avoid double rendering.

Updates documentation to reflect the new structure and build process. Also implements changes to build output and event system for improved clarity.

Fixes #123
This commit is contained in:
Janus Knudsen 2025-09-09 17:15:06 +02:00
parent 72019a3d9a
commit 80ef35c42c
7 changed files with 109 additions and 296 deletions

1
.gitignore vendored
View file

@ -30,3 +30,4 @@ Thumbs.db
*.suo *.suo
*.userosscache *.userosscache
*.sln.docstates *.sln.docstates
js/

View file

@ -40,7 +40,7 @@ eventBus.on('calendar:view-changed', (event) => { /* handle */ });
``` ```
#### Manager Pattern #### Manager Pattern
Each manager is instantiated in `src/index.ts` and handles a specific domain: Each manager is instantiated via ManagerFactory in `src/index.ts` and handles a specific domain:
- **CalendarManager**: Main coordinator, initializes other managers - **CalendarManager**: Main coordinator, initializes other managers
- **ViewManager**: Handles day/week/month view switching - **ViewManager**: Handles day/week/month view switching
- **NavigationManager**: Prev/next/today navigation - **NavigationManager**: Prev/next/today navigation
@ -48,14 +48,18 @@ Each manager is instantiated in `src/index.ts` and handles a specific domain:
- **EventRenderer**: Visual rendering of events in the grid - **EventRenderer**: Visual rendering of events in the grid
- **GridManager**: Creates and maintains the calendar grid structure - **GridManager**: Creates and maintains the calendar grid structure
- **ScrollManager**: Handles scroll position and time indicators - **ScrollManager**: Handles scroll position and time indicators
- **DataManager**: Mock data loading and event data transformation - **DragDropManager**: Drag & drop functionality for events
### Project Structure ### Project Structure
``` ```
src/ src/
├── constants/ # Enums and constants (EventTypes) ├── constants/ # Enums and constants (CoreEvents)
├── core/ # Core functionality (EventBus, CalendarConfig) ├── core/ # Core functionality (EventBus, CalendarConfig)
├── factories/ # ManagerFactory for dependency injection
├── interfaces/ # TypeScript interfaces
├── managers/ # Manager classes (one per domain) ├── managers/ # Manager classes (one per domain)
├── renderers/ # Event rendering services
├── strategies/ # View strategy pattern implementations
├── types/ # TypeScript type definitions ├── types/ # TypeScript type definitions
└── utils/ # Utility functions (DateUtils, PositionUtils) └── utils/ # Utility functions (DateUtils, PositionUtils)
@ -72,18 +76,36 @@ Modular CSS structure without external frameworks:
- `calendar-events-css.css`: Event styling and colors - `calendar-events-css.css`: Event styling and colors
- `calendar-layout-css.css`: Grid and layout - `calendar-layout-css.css`: Grid and layout
- `calendar-popup-css.css`: Popup and modal styles - `calendar-popup-css.css`: Popup and modal styles
- `calendar-month-css.css`: Month view specific styles
### Event Naming Convention ### Event System
Events follow the pattern `category:action`: The application uses CoreEvents enum for type-safe event handling. Events follow the pattern `category:action`:
- `calendar:*` - General calendar events - `calendar:*` - General calendar events
- `grid:*` - Grid-related events - `grid:*` - Grid-related events
- `event:*` - Event data changes - `event:*` - Event data changes
- `navigation:*` - Navigation actions - `navigation:*` - Navigation actions
- `view:*` - View changes - `view:*` - View changes
Core events are centralized in `src/constants/CoreEvents.ts` to maintain consistency across the application.
### Configuration System
CalendarConfig singleton (`src/core/CalendarConfig.ts`) manages:
- Grid settings (hour height, snap intervals, time boundaries)
- View configurations (day/week/month settings)
- Work week presets (standard, compressed, midweek, weekend, fullweek)
- Resource-based calendar mode support
### TypeScript Configuration ### TypeScript Configuration
- Target: ES2020 - Target: ES2020
- Module: ESNext - Module: ESNext
- Strict mode enabled - Strict mode enabled
- Source maps enabled - Source maps enabled
- Output directory: `./js` - Output directory: `wwwroot/js`
### Build System
Uses esbuild for fast TypeScript compilation:
- Entry point: `src/index.ts`
- Output: `wwwroot/js/calendar.js` (single bundled file)
- Platform: Browser
- Format: ESM
- Source maps: Inline for development

344
package-lock.json generated
View file

@ -1,398 +1,176 @@
{ {
"name": "calendar-plantempus", "name": "calendar-plantempus",
"version": "1.0.0", "version": "1.0.0",
"lockfileVersion": 3, "lockfileVersion": 1,
"requires": true, "requires": true,
"packages": { "dependencies": {
"": { "@esbuild/aix-ppc64": {
"name": "calendar-plantempus",
"version": "1.0.0",
"devDependencies": {
"esbuild": "^0.19.0",
"typescript": "^5.0.0"
}
},
"node_modules/@esbuild/aix-ppc64": {
"version": "0.19.12", "version": "0.19.12",
"resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.19.12.tgz", "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.19.12.tgz",
"integrity": "sha512-bmoCYyWdEL3wDQIVbcyzRyeKLgk2WtWLTWz1ZIAZF/EGbNOwSA6ew3PftJ1PqMiOOGu0OyFMzG53L0zqIpPeNA==", "integrity": "sha512-bmoCYyWdEL3wDQIVbcyzRyeKLgk2WtWLTWz1ZIAZF/EGbNOwSA6ew3PftJ1PqMiOOGu0OyFMzG53L0zqIpPeNA==",
"cpu": [
"ppc64"
],
"dev": true, "dev": true,
"optional": true, "optional": true
"os": [
"aix"
],
"engines": {
"node": ">=12"
}
}, },
"node_modules/@esbuild/android-arm": { "@esbuild/android-arm": {
"version": "0.19.12", "version": "0.19.12",
"resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.19.12.tgz", "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.19.12.tgz",
"integrity": "sha512-qg/Lj1mu3CdQlDEEiWrlC4eaPZ1KztwGJ9B6J+/6G+/4ewxJg7gqj8eVYWvao1bXrqGiW2rsBZFSX3q2lcW05w==", "integrity": "sha512-qg/Lj1mu3CdQlDEEiWrlC4eaPZ1KztwGJ9B6J+/6G+/4ewxJg7gqj8eVYWvao1bXrqGiW2rsBZFSX3q2lcW05w==",
"cpu": [
"arm"
],
"dev": true, "dev": true,
"optional": true, "optional": true
"os": [
"android"
],
"engines": {
"node": ">=12"
}
}, },
"node_modules/@esbuild/android-arm64": { "@esbuild/android-arm64": {
"version": "0.19.12", "version": "0.19.12",
"resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.19.12.tgz", "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.19.12.tgz",
"integrity": "sha512-P0UVNGIienjZv3f5zq0DP3Nt2IE/3plFzuaS96vihvD0Hd6H/q4WXUGpCxD/E8YrSXfNyRPbpTq+T8ZQioSuPA==", "integrity": "sha512-P0UVNGIienjZv3f5zq0DP3Nt2IE/3plFzuaS96vihvD0Hd6H/q4WXUGpCxD/E8YrSXfNyRPbpTq+T8ZQioSuPA==",
"cpu": [
"arm64"
],
"dev": true, "dev": true,
"optional": true, "optional": true
"os": [
"android"
],
"engines": {
"node": ">=12"
}
}, },
"node_modules/@esbuild/android-x64": { "@esbuild/android-x64": {
"version": "0.19.12", "version": "0.19.12",
"resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.19.12.tgz", "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.19.12.tgz",
"integrity": "sha512-3k7ZoUW6Q6YqhdhIaq/WZ7HwBpnFBlW905Fa4s4qWJyiNOgT1dOqDiVAQFwBH7gBRZr17gLrlFCRzF6jFh7Kew==", "integrity": "sha512-3k7ZoUW6Q6YqhdhIaq/WZ7HwBpnFBlW905Fa4s4qWJyiNOgT1dOqDiVAQFwBH7gBRZr17gLrlFCRzF6jFh7Kew==",
"cpu": [
"x64"
],
"dev": true, "dev": true,
"optional": true, "optional": true
"os": [
"android"
],
"engines": {
"node": ">=12"
}
}, },
"node_modules/@esbuild/darwin-arm64": { "@esbuild/darwin-arm64": {
"version": "0.19.12", "version": "0.19.12",
"resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.19.12.tgz", "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.19.12.tgz",
"integrity": "sha512-B6IeSgZgtEzGC42jsI+YYu9Z3HKRxp8ZT3cqhvliEHovq8HSX2YX8lNocDn79gCKJXOSaEot9MVYky7AKjCs8g==", "integrity": "sha512-B6IeSgZgtEzGC42jsI+YYu9Z3HKRxp8ZT3cqhvliEHovq8HSX2YX8lNocDn79gCKJXOSaEot9MVYky7AKjCs8g==",
"cpu": [
"arm64"
],
"dev": true, "dev": true,
"optional": true, "optional": true
"os": [
"darwin"
],
"engines": {
"node": ">=12"
}
}, },
"node_modules/@esbuild/darwin-x64": { "@esbuild/darwin-x64": {
"version": "0.19.12", "version": "0.19.12",
"resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.19.12.tgz", "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.19.12.tgz",
"integrity": "sha512-hKoVkKzFiToTgn+41qGhsUJXFlIjxI/jSYeZf3ugemDYZldIXIxhvwN6erJGlX4t5h417iFuheZ7l+YVn05N3A==", "integrity": "sha512-hKoVkKzFiToTgn+41qGhsUJXFlIjxI/jSYeZf3ugemDYZldIXIxhvwN6erJGlX4t5h417iFuheZ7l+YVn05N3A==",
"cpu": [
"x64"
],
"dev": true, "dev": true,
"optional": true, "optional": true
"os": [
"darwin"
],
"engines": {
"node": ">=12"
}
}, },
"node_modules/@esbuild/freebsd-arm64": { "@esbuild/freebsd-arm64": {
"version": "0.19.12", "version": "0.19.12",
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.19.12.tgz", "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.19.12.tgz",
"integrity": "sha512-4aRvFIXmwAcDBw9AueDQ2YnGmz5L6obe5kmPT8Vd+/+x/JMVKCgdcRwH6APrbpNXsPz+K653Qg8HB/oXvXVukA==", "integrity": "sha512-4aRvFIXmwAcDBw9AueDQ2YnGmz5L6obe5kmPT8Vd+/+x/JMVKCgdcRwH6APrbpNXsPz+K653Qg8HB/oXvXVukA==",
"cpu": [
"arm64"
],
"dev": true, "dev": true,
"optional": true, "optional": true
"os": [
"freebsd"
],
"engines": {
"node": ">=12"
}
}, },
"node_modules/@esbuild/freebsd-x64": { "@esbuild/freebsd-x64": {
"version": "0.19.12", "version": "0.19.12",
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.19.12.tgz", "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.19.12.tgz",
"integrity": "sha512-EYoXZ4d8xtBoVN7CEwWY2IN4ho76xjYXqSXMNccFSx2lgqOG/1TBPW0yPx1bJZk94qu3tX0fycJeeQsKovA8gg==", "integrity": "sha512-EYoXZ4d8xtBoVN7CEwWY2IN4ho76xjYXqSXMNccFSx2lgqOG/1TBPW0yPx1bJZk94qu3tX0fycJeeQsKovA8gg==",
"cpu": [
"x64"
],
"dev": true, "dev": true,
"optional": true, "optional": true
"os": [
"freebsd"
],
"engines": {
"node": ">=12"
}
}, },
"node_modules/@esbuild/linux-arm": { "@esbuild/linux-arm": {
"version": "0.19.12", "version": "0.19.12",
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.19.12.tgz", "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.19.12.tgz",
"integrity": "sha512-J5jPms//KhSNv+LO1S1TX1UWp1ucM6N6XuL6ITdKWElCu8wXP72l9MM0zDTzzeikVyqFE6U8YAV9/tFyj0ti+w==", "integrity": "sha512-J5jPms//KhSNv+LO1S1TX1UWp1ucM6N6XuL6ITdKWElCu8wXP72l9MM0zDTzzeikVyqFE6U8YAV9/tFyj0ti+w==",
"cpu": [
"arm"
],
"dev": true, "dev": true,
"optional": true, "optional": true
"os": [
"linux"
],
"engines": {
"node": ">=12"
}
}, },
"node_modules/@esbuild/linux-arm64": { "@esbuild/linux-arm64": {
"version": "0.19.12", "version": "0.19.12",
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.19.12.tgz", "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.19.12.tgz",
"integrity": "sha512-EoTjyYyLuVPfdPLsGVVVC8a0p1BFFvtpQDB/YLEhaXyf/5bczaGeN15QkR+O4S5LeJ92Tqotve7i1jn35qwvdA==", "integrity": "sha512-EoTjyYyLuVPfdPLsGVVVC8a0p1BFFvtpQDB/YLEhaXyf/5bczaGeN15QkR+O4S5LeJ92Tqotve7i1jn35qwvdA==",
"cpu": [
"arm64"
],
"dev": true, "dev": true,
"optional": true, "optional": true
"os": [
"linux"
],
"engines": {
"node": ">=12"
}
}, },
"node_modules/@esbuild/linux-ia32": { "@esbuild/linux-ia32": {
"version": "0.19.12", "version": "0.19.12",
"resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.19.12.tgz", "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.19.12.tgz",
"integrity": "sha512-Thsa42rrP1+UIGaWz47uydHSBOgTUnwBwNq59khgIwktK6x60Hivfbux9iNR0eHCHzOLjLMLfUMLCypBkZXMHA==", "integrity": "sha512-Thsa42rrP1+UIGaWz47uydHSBOgTUnwBwNq59khgIwktK6x60Hivfbux9iNR0eHCHzOLjLMLfUMLCypBkZXMHA==",
"cpu": [
"ia32"
],
"dev": true, "dev": true,
"optional": true, "optional": true
"os": [
"linux"
],
"engines": {
"node": ">=12"
}
}, },
"node_modules/@esbuild/linux-loong64": { "@esbuild/linux-loong64": {
"version": "0.19.12", "version": "0.19.12",
"resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.19.12.tgz", "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.19.12.tgz",
"integrity": "sha512-LiXdXA0s3IqRRjm6rV6XaWATScKAXjI4R4LoDlvO7+yQqFdlr1Bax62sRwkVvRIrwXxvtYEHHI4dm50jAXkuAA==", "integrity": "sha512-LiXdXA0s3IqRRjm6rV6XaWATScKAXjI4R4LoDlvO7+yQqFdlr1Bax62sRwkVvRIrwXxvtYEHHI4dm50jAXkuAA==",
"cpu": [
"loong64"
],
"dev": true, "dev": true,
"optional": true, "optional": true
"os": [
"linux"
],
"engines": {
"node": ">=12"
}
}, },
"node_modules/@esbuild/linux-mips64el": { "@esbuild/linux-mips64el": {
"version": "0.19.12", "version": "0.19.12",
"resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.19.12.tgz", "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.19.12.tgz",
"integrity": "sha512-fEnAuj5VGTanfJ07ff0gOA6IPsvrVHLVb6Lyd1g2/ed67oU1eFzL0r9WL7ZzscD+/N6i3dWumGE1Un4f7Amf+w==", "integrity": "sha512-fEnAuj5VGTanfJ07ff0gOA6IPsvrVHLVb6Lyd1g2/ed67oU1eFzL0r9WL7ZzscD+/N6i3dWumGE1Un4f7Amf+w==",
"cpu": [
"mips64el"
],
"dev": true, "dev": true,
"optional": true, "optional": true
"os": [
"linux"
],
"engines": {
"node": ">=12"
}
}, },
"node_modules/@esbuild/linux-ppc64": { "@esbuild/linux-ppc64": {
"version": "0.19.12", "version": "0.19.12",
"resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.19.12.tgz", "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.19.12.tgz",
"integrity": "sha512-nYJA2/QPimDQOh1rKWedNOe3Gfc8PabU7HT3iXWtNUbRzXS9+vgB0Fjaqr//XNbd82mCxHzik2qotuI89cfixg==", "integrity": "sha512-nYJA2/QPimDQOh1rKWedNOe3Gfc8PabU7HT3iXWtNUbRzXS9+vgB0Fjaqr//XNbd82mCxHzik2qotuI89cfixg==",
"cpu": [
"ppc64"
],
"dev": true, "dev": true,
"optional": true, "optional": true
"os": [
"linux"
],
"engines": {
"node": ">=12"
}
}, },
"node_modules/@esbuild/linux-riscv64": { "@esbuild/linux-riscv64": {
"version": "0.19.12", "version": "0.19.12",
"resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.19.12.tgz", "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.19.12.tgz",
"integrity": "sha512-2MueBrlPQCw5dVJJpQdUYgeqIzDQgw3QtiAHUC4RBz9FXPrskyyU3VI1hw7C0BSKB9OduwSJ79FTCqtGMWqJHg==", "integrity": "sha512-2MueBrlPQCw5dVJJpQdUYgeqIzDQgw3QtiAHUC4RBz9FXPrskyyU3VI1hw7C0BSKB9OduwSJ79FTCqtGMWqJHg==",
"cpu": [
"riscv64"
],
"dev": true, "dev": true,
"optional": true, "optional": true
"os": [
"linux"
],
"engines": {
"node": ">=12"
}
}, },
"node_modules/@esbuild/linux-s390x": { "@esbuild/linux-s390x": {
"version": "0.19.12", "version": "0.19.12",
"resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.19.12.tgz", "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.19.12.tgz",
"integrity": "sha512-+Pil1Nv3Umes4m3AZKqA2anfhJiVmNCYkPchwFJNEJN5QxmTs1uzyy4TvmDrCRNT2ApwSari7ZIgrPeUx4UZDg==", "integrity": "sha512-+Pil1Nv3Umes4m3AZKqA2anfhJiVmNCYkPchwFJNEJN5QxmTs1uzyy4TvmDrCRNT2ApwSari7ZIgrPeUx4UZDg==",
"cpu": [
"s390x"
],
"dev": true, "dev": true,
"optional": true, "optional": true
"os": [
"linux"
],
"engines": {
"node": ">=12"
}
}, },
"node_modules/@esbuild/linux-x64": { "@esbuild/linux-x64": {
"version": "0.19.12", "version": "0.19.12",
"resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.19.12.tgz", "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.19.12.tgz",
"integrity": "sha512-B71g1QpxfwBvNrfyJdVDexenDIt1CiDN1TIXLbhOw0KhJzE78KIFGX6OJ9MrtC0oOqMWf+0xop4qEU8JrJTwCg==", "integrity": "sha512-B71g1QpxfwBvNrfyJdVDexenDIt1CiDN1TIXLbhOw0KhJzE78KIFGX6OJ9MrtC0oOqMWf+0xop4qEU8JrJTwCg==",
"cpu": [
"x64"
],
"dev": true, "dev": true,
"optional": true, "optional": true
"os": [
"linux"
],
"engines": {
"node": ">=12"
}
}, },
"node_modules/@esbuild/netbsd-x64": { "@esbuild/netbsd-x64": {
"version": "0.19.12", "version": "0.19.12",
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.19.12.tgz", "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.19.12.tgz",
"integrity": "sha512-3ltjQ7n1owJgFbuC61Oj++XhtzmymoCihNFgT84UAmJnxJfm4sYCiSLTXZtE00VWYpPMYc+ZQmB6xbSdVh0JWA==", "integrity": "sha512-3ltjQ7n1owJgFbuC61Oj++XhtzmymoCihNFgT84UAmJnxJfm4sYCiSLTXZtE00VWYpPMYc+ZQmB6xbSdVh0JWA==",
"cpu": [
"x64"
],
"dev": true, "dev": true,
"optional": true, "optional": true
"os": [
"netbsd"
],
"engines": {
"node": ">=12"
}
}, },
"node_modules/@esbuild/openbsd-x64": { "@esbuild/openbsd-x64": {
"version": "0.19.12", "version": "0.19.12",
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.19.12.tgz", "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.19.12.tgz",
"integrity": "sha512-RbrfTB9SWsr0kWmb9srfF+L933uMDdu9BIzdA7os2t0TXhCRjrQyCeOt6wVxr79CKD4c+p+YhCj31HBkYcXebw==", "integrity": "sha512-RbrfTB9SWsr0kWmb9srfF+L933uMDdu9BIzdA7os2t0TXhCRjrQyCeOt6wVxr79CKD4c+p+YhCj31HBkYcXebw==",
"cpu": [
"x64"
],
"dev": true, "dev": true,
"optional": true, "optional": true
"os": [
"openbsd"
],
"engines": {
"node": ">=12"
}
}, },
"node_modules/@esbuild/sunos-x64": { "@esbuild/sunos-x64": {
"version": "0.19.12", "version": "0.19.12",
"resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.19.12.tgz", "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.19.12.tgz",
"integrity": "sha512-HKjJwRrW8uWtCQnQOz9qcU3mUZhTUQvi56Q8DPTLLB+DawoiQdjsYq+j+D3s9I8VFtDr+F9CjgXKKC4ss89IeA==", "integrity": "sha512-HKjJwRrW8uWtCQnQOz9qcU3mUZhTUQvi56Q8DPTLLB+DawoiQdjsYq+j+D3s9I8VFtDr+F9CjgXKKC4ss89IeA==",
"cpu": [
"x64"
],
"dev": true, "dev": true,
"optional": true, "optional": true
"os": [
"sunos"
],
"engines": {
"node": ">=12"
}
}, },
"node_modules/@esbuild/win32-arm64": { "@esbuild/win32-arm64": {
"version": "0.19.12", "version": "0.19.12",
"resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.19.12.tgz", "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.19.12.tgz",
"integrity": "sha512-URgtR1dJnmGvX864pn1B2YUYNzjmXkuJOIqG2HdU62MVS4EHpU2946OZoTMnRUHklGtJdJZ33QfzdjGACXhn1A==", "integrity": "sha512-URgtR1dJnmGvX864pn1B2YUYNzjmXkuJOIqG2HdU62MVS4EHpU2946OZoTMnRUHklGtJdJZ33QfzdjGACXhn1A==",
"cpu": [
"arm64"
],
"dev": true, "dev": true,
"optional": true, "optional": true
"os": [
"win32"
],
"engines": {
"node": ">=12"
}
}, },
"node_modules/@esbuild/win32-ia32": { "@esbuild/win32-ia32": {
"version": "0.19.12", "version": "0.19.12",
"resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.19.12.tgz", "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.19.12.tgz",
"integrity": "sha512-+ZOE6pUkMOJfmxmBZElNOx72NKpIa/HFOMGzu8fqzQJ5kgf6aTGrcJaFsNiVMH4JKpMipyK+7k0n2UXN7a8YKQ==", "integrity": "sha512-+ZOE6pUkMOJfmxmBZElNOx72NKpIa/HFOMGzu8fqzQJ5kgf6aTGrcJaFsNiVMH4JKpMipyK+7k0n2UXN7a8YKQ==",
"cpu": [
"ia32"
],
"dev": true, "dev": true,
"optional": true, "optional": true
"os": [
"win32"
],
"engines": {
"node": ">=12"
}
}, },
"node_modules/@esbuild/win32-x64": { "@esbuild/win32-x64": {
"version": "0.19.12", "version": "0.19.12",
"resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.19.12.tgz", "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.19.12.tgz",
"integrity": "sha512-T1QyPSDCyMXaO3pzBkF96E8xMkiRYbUEZADd29SyPGabqxMViNoii+NcK7eWJAEoU6RZyEm5lVSIjTmcdoB9HA==", "integrity": "sha512-T1QyPSDCyMXaO3pzBkF96E8xMkiRYbUEZADd29SyPGabqxMViNoii+NcK7eWJAEoU6RZyEm5lVSIjTmcdoB9HA==",
"cpu": [
"x64"
],
"dev": true, "dev": true,
"optional": true, "optional": true
"os": [
"win32"
],
"engines": {
"node": ">=12"
}
}, },
"node_modules/esbuild": { "esbuild": {
"version": "0.19.12", "version": "0.19.12",
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.19.12.tgz", "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.19.12.tgz",
"integrity": "sha512-aARqgq8roFBj054KvQr5f1sFu0D65G+miZRCuJyJ0G13Zwx7vRar5Zhn2tkQNzIXcBrNVsv/8stehpj+GAjgbg==", "integrity": "sha512-aARqgq8roFBj054KvQr5f1sFu0D65G+miZRCuJyJ0G13Zwx7vRar5Zhn2tkQNzIXcBrNVsv/8stehpj+GAjgbg==",
"dev": true, "dev": true,
"hasInstallScript": true, "requires": {
"bin": {
"esbuild": "bin/esbuild"
},
"engines": {
"node": ">=12"
},
"optionalDependencies": {
"@esbuild/aix-ppc64": "0.19.12", "@esbuild/aix-ppc64": "0.19.12",
"@esbuild/android-arm": "0.19.12", "@esbuild/android-arm": "0.19.12",
"@esbuild/android-arm64": "0.19.12", "@esbuild/android-arm64": "0.19.12",
@ -418,18 +196,16 @@
"@esbuild/win32-x64": "0.19.12" "@esbuild/win32-x64": "0.19.12"
} }
}, },
"node_modules/typescript": { "fuse.js": {
"version": "5.8.3", "version": "7.1.0",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz", "resolved": "https://registry.npmjs.org/fuse.js/-/fuse.js-7.1.0.tgz",
"integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==", "integrity": "sha512-trLf4SzuuUxfusZADLINj+dE8clK1frKdmqiJNb1Es75fmI5oY6X2mxLVUciLLjxqw/xr72Dhy+lER6dGd02FQ=="
"dev": true, },
"bin": { "typescript": {
"tsc": "bin/tsc", "version": "5.9.2",
"tsserver": "bin/tsserver" "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.2.tgz",
}, "integrity": "sha512-CWBzXQrc/qOkhidw1OzBTQuYRbfyxDXJMVJ1XNwUHGROVmuaeiEm3OslpZ1RV96d7SKKjZKrSJu3+t/xlw3R9A==",
"engines": { "dev": true
"node": ">=14.17"
}
} }
} }
} }

View file

@ -12,5 +12,8 @@
"devDependencies": { "devDependencies": {
"esbuild": "^0.19.0", "esbuild": "^0.19.0",
"typescript": "^5.0.0" "typescript": "^5.0.0"
},
"dependencies": {
"fuse.js": "^7.1.0"
} }
} }

View file

@ -7,9 +7,8 @@ import { eventBus } from '../core/EventBus';
import { CoreEvents } from '../constants/CoreEvents'; import { CoreEvents } from '../constants/CoreEvents';
import { CalendarEvent } from '../types/CalendarTypes'; import { CalendarEvent } from '../types/CalendarTypes';
// Import Fuse.js ES module // Import Fuse.js from npm
// @ts-ignore - Fuse.js types not available for local file import Fuse from 'fuse.js';
import Fuse from '../../wwwroot/js/lib/fuse.min.mjs';
export class EventFilterManager { export class EventFilterManager {
private searchInput: HTMLInputElement | null = null; private searchInput: HTMLInputElement | null = null;

View file

@ -72,8 +72,16 @@ export abstract class BaseEventRenderer implements EventRendererStrategy {
return; return;
} }
// Track hvilke events der allerede er blevet processeret
const processedEvents = new Set<string>();
// Gå gennem hvert event og find overlaps // Gå gennem hvert event og find overlaps
events.forEach((currentEvent, index) => { events.forEach((currentEvent, index) => {
// Skip events der allerede er processeret som del af en overlap gruppe
if (processedEvents.has(currentEvent.id)) {
return;
}
const remainingEvents = events.slice(index + 1); const remainingEvents = events.slice(index + 1);
const overlappingEvents = this.overlapDetector.resolveOverlap(currentEvent, remainingEvents); const overlappingEvents = this.overlapDetector.resolveOverlap(currentEvent, remainingEvents);
@ -81,10 +89,14 @@ export abstract class BaseEventRenderer implements EventRendererStrategy {
// Der er overlaps - opret stack links // Der er overlaps - opret stack links
const result = this.overlapDetector.decorateWithStackLinks(currentEvent, overlappingEvents); const result = this.overlapDetector.decorateWithStackLinks(currentEvent, overlappingEvents);
this.new_renderOverlappingEvents(result, container); this.new_renderOverlappingEvents(result, container);
// Marker alle events i overlap gruppen som processeret
overlappingEvents.forEach(event => processedEvents.add(event.id));
} else { } else {
// Intet overlap - render normalt // Intet overlap - render normalt
const element = this.renderEvent(currentEvent); const element = this.renderEvent(currentEvent);
container.appendChild(element); container.appendChild(element);
processedEvents.add(currentEvent.id);
} }
}); });
} }

View file

@ -66,7 +66,7 @@ export class OverlapDetector {
}; };
stackLinks.set(event.id as EventId, stackLink); stackLinks.set(event.id as EventId, stackLink);
}); });
overlappingEvents.push(newEvent);
return { return {
overlappingEvents, overlappingEvents,
stackLinks stackLinks